Skip to content

Commit 36a575b

Browse files
Driver provided "intrinsics".
Allows drivers to define functions which are only expanded by the plugin after all type checking, inference, etc etc, ie only once during mono item collection. The form of the interface with the plugins is slightly different than what was proposed in rust-lang#51623. Additionally, signature checking is added.
1 parent 3f0e164 commit 36a575b

File tree

21 files changed

+505
-19
lines changed

21 files changed

+505
-19
lines changed

src/librustc/mir/mod.rs

+39-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ pub mod traversal;
4949
pub mod visit;
5050

5151
/// Types for locals
52-
type LocalDecls<'tcx> = IndexVec<Local, LocalDecl<'tcx>>;
52+
pub type LocalDecls<'tcx> = IndexVec<Local, LocalDecl<'tcx>>;
5353

5454
pub trait HasLocalDecls<'tcx> {
5555
fn local_decls(&self) -> &LocalDecls<'tcx>;
@@ -3350,3 +3350,41 @@ impl<'tcx> TypeFoldable<'tcx> for Constant<'tcx> {
33503350
self.literal.visit_with(visitor)
33513351
}
33523352
}
3353+
3354+
pub trait CustomIntrinsicMirGen: Sync + Send {
3355+
/// Codegen a plugin-defined intrinsic. This is intended to be used to
3356+
/// "return" values based on the monomorphized and erased types of the
3357+
/// function call. Codegen will codegen the `extra_stmts` and then insert
3358+
/// an unconditional branch to the exit block.
3359+
///
3360+
/// Consider this to be highly unstable; it will likely change without
3361+
/// warning. There is also no spec for this, it is 100% implementation
3362+
/// defined, and may not be implemented at all for some codegen backends.
3363+
///
3364+
/// If the codegen backend is multithreaded, this will be called from
3365+
/// any number of threads, hence `Sync + Send`.
3366+
///
3367+
/// YOU ARE RESPONSIBLE FOR THE SAFETY OF THE EXTRA STATEMENTS.
3368+
/// You have been warned. Good luck, have fun.
3369+
fn mirgen_simple_intrinsic<'tcx>(&self,
3370+
tcx: TyCtxt<'tcx>,
3371+
instance: ty::Instance<'tcx>,
3372+
mir: &mut Body<'tcx>);
3373+
3374+
/// The following are used for typeck-ing:
3375+
3376+
/// The number of generic parameters expected.
3377+
fn generic_parameter_count<'tcx>(&self, tcx: TyCtxt<'tcx>) -> usize;
3378+
/// The types of the input args.
3379+
fn inputs<'tcx>(&self, tcx: TyCtxt<'tcx>) -> &'tcx List<Ty<'tcx>>;
3380+
/// The return type.
3381+
fn output<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx>;
3382+
}
3383+
3384+
/*impl<'a> HashStable<StableHashingContext<'a>> for dyn CustomIntrinsicMirGen {
3385+
fn hash_stable<W: StableHasherResult>(&self,
3386+
_ctx: &mut StableHashingContext<'_>,
3387+
_hasher: &mut StableHasher<W>) {
3388+
// TO DO
3389+
}
3390+
}*/

src/librustc/query/mod.rs

+18
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,24 @@ rustc_queries! {
129129
}
130130
}
131131

132+
/// If defined by the driver, returns the extra mir statements to codegen,
133+
/// else returns `None`.
134+
query custom_intrinsic_mirgen(key: DefId) -> Option<Lrc<dyn mir::CustomIntrinsicMirGen>> {
135+
anon
136+
fatal_cycle
137+
no_hash
138+
139+
desc { |tcx| "asking for the custom MIR generator of `{}`", tcx.def_path_str(key) }
140+
}
141+
/// The monomorphized MIR for a custom intrinsic instance.
142+
query custom_intrinsic_mir(inst: ty::Instance<'tcx>) -> Option<&'tcx mir::Body<'tcx>> {
143+
anon
144+
fatal_cycle
145+
no_force
146+
147+
desc { |tcx| "asking for the custom MIR of `{}`", tcx.def_path_str(inst.def_id()) }
148+
}
149+
132150
query promoted_mir(key: DefId) -> &'tcx IndexVec<mir::Promoted, mir::Body<'tcx>> {
133151
cache_on_disk_if { key.is_local() }
134152
load_cached(tcx, id) {

src/librustc/ty/mod.rs

+16-5
Original file line numberDiff line numberDiff line change
@@ -3032,20 +3032,26 @@ impl<'tcx> TyCtxt<'tcx> {
30323032
}
30333033

30343034
/// Returns the possibly-auto-generated MIR of a `(DefId, Subst)` pair.
3035-
pub fn instance_mir(self, instance: ty::InstanceDef<'tcx>) -> &'tcx Body<'tcx> {
3036-
match instance {
3035+
pub fn instance_mir(self, instance: ty::Instance<'tcx>) -> &'tcx Body<'tcx> {
3036+
match instance.def {
30373037
ty::InstanceDef::Item(did) => {
30383038
self.optimized_mir(did)
30393039
}
3040+
ty::InstanceDef::Intrinsic(..) => {
3041+
if let Some(mir) = self.custom_intrinsic_mir(instance) {
3042+
mir
3043+
} else {
3044+
self.mir_shims(instance.def)
3045+
}
3046+
},
30403047
ty::InstanceDef::VtableShim(..) |
30413048
ty::InstanceDef::ReifyShim(..) |
3042-
ty::InstanceDef::Intrinsic(..) |
30433049
ty::InstanceDef::FnPtrShim(..) |
30443050
ty::InstanceDef::Virtual(..) |
30453051
ty::InstanceDef::ClosureOnceShim { .. } |
30463052
ty::InstanceDef::DropGlue(..) |
30473053
ty::InstanceDef::CloneShim(..) => {
3048-
self.mir_shims(instance)
3054+
self.mir_shims(instance.def)
30493055
}
30503056
}
30513057
}
@@ -3329,7 +3335,12 @@ fn instance_def_size_estimate<'tcx>(tcx: TyCtxt<'tcx>, instance_def: InstanceDef
33293335
match instance_def {
33303336
InstanceDef::Item(..) |
33313337
InstanceDef::DropGlue(..) => {
3332-
let mir = tcx.instance_mir(instance_def);
3338+
let instance = Instance {
3339+
def: instance_def,
3340+
// this field can be whatever because it won't be used in this case.
3341+
substs: tcx.intern_substs(&[]),
3342+
};
3343+
let mir = tcx.instance_mir(instance);
33333344
mir.basic_blocks().iter().map(|bb| bb.statements.len()).sum()
33343345
},
33353346
// Estimate the size of other compiler-generated shims to be 1.

src/librustc_codegen_ssa/base.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,7 @@ pub fn codegen_instance<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>(
370370

371371
let lldecl = cx.get_fn(instance);
372372

373-
let mir = cx.tcx().instance_mir(instance.def);
373+
let mir = cx.tcx().instance_mir(instance);
374374
mir::codegen_mir::<Bx>(cx, lldecl, &mir, instance, sig);
375375
}
376376

src/librustc_codegen_ssa/mir/block.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -570,9 +570,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
570570
let arg_count = fn_abi.args.len() + fn_abi.ret.is_indirect() as usize;
571571
let mut llargs = Vec::with_capacity(arg_count);
572572

573+
// Custom intrinsics are treated as-if they were normal functions here.
574+
let is_custom_intrinsic = intrinsic.and_then(|_| instance )
575+
.map(|instance| bx.tcx().custom_intrinsic_mir(instance).is_some() )
576+
.unwrap_or_default();
577+
573578
// Prepare the return value destination
574579
let ret_dest = if let Some((ref dest, _)) = *destination {
575-
let is_intrinsic = intrinsic.is_some();
580+
let is_intrinsic = intrinsic.is_some() && !is_custom_intrinsic;
576581
self.make_return_dest(&mut bx, dest, &fn_abi.ret, &mut llargs,
577582
is_intrinsic)
578583
} else {
@@ -594,7 +599,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
594599
return;
595600
}
596601

597-
if intrinsic.is_some() && intrinsic != Some("drop_in_place") {
602+
if intrinsic.is_some() && intrinsic != Some("drop_in_place") &&
603+
!is_custom_intrinsic {
598604
let dest = match ret_dest {
599605
_ if fn_abi.ret.is_indirect() => llargs[0],
600606
ReturnDest::Nothing =>

src/librustc_codegen_utils/symbol_names.rs

+4
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,10 @@ fn symbol_name(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Symbol {
137137
Node::ForeignItem(_) => true,
138138
_ => false,
139139
}
140+
} else if let ty::InstanceDef::Intrinsic(_) = instance.def {
141+
// custom intrinsics should never be foreign, otherwise
142+
// generic parameters will cause duplicate symbols names.
143+
tcx.custom_intrinsic_mir(instance).is_none()
140144
} else {
141145
tcx.is_foreign_item(def_id)
142146
};

src/librustc_data_structures/sync.rs

+53
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use std::collections::HashMap;
2121
use std::hash::{Hash, BuildHasher};
2222
use std::marker::PhantomData;
2323
use std::ops::{Deref, DerefMut};
24+
use std::sync::{Arc, atomic::AtomicPtr, atomic, };
2425
use crate::owning_ref::{Erased, OwningRef};
2526

2627
pub use std::sync::atomic::Ordering::SeqCst;
@@ -790,3 +791,55 @@ impl<T> DerefMut for OneThread<T> {
790791
&mut self.inner
791792
}
792793
}
794+
795+
/// Provides atomic mutability by replacing the value inside this `ArcCell`.
796+
/// Similar to the `crossbeam` structure of the same name.
797+
#[derive(Debug)]
798+
pub struct ArcCell<T>(AtomicPtr<T>);
799+
impl<T> ArcCell<T> {
800+
pub fn new(v: Arc<T>) -> Self {
801+
ArcCell(AtomicPtr::new(Arc::into_raw(v) as *mut _))
802+
}
803+
pub fn get(&self) -> Arc<T> {
804+
let ptr = self.0.load(atomic::Ordering::Acquire);
805+
let arc = unsafe { Arc::from_raw(ptr as *const T) };
806+
let ret = arc.clone();
807+
// don't drop our copy:
808+
::std::mem::forget(arc);
809+
ret
810+
}
811+
/// Update the value, returning the previous value.
812+
pub fn set(&self, v: Arc<T>) -> Arc<T> {
813+
let new = Arc::into_raw(v) as *mut _;
814+
let mut expected = self.0.load(atomic::Ordering::Acquire);
815+
loop {
816+
match self.0.compare_exchange_weak(expected, new,
817+
atomic::Ordering::SeqCst,
818+
atomic::Ordering::Acquire) {
819+
Ok(old) => {
820+
return unsafe { Arc::from_raw(old as *const T) };
821+
},
822+
Err(v) => {
823+
expected = v;
824+
},
825+
}
826+
}
827+
}
828+
}
829+
impl<T> Drop for ArcCell<T> {
830+
fn drop(&mut self) {
831+
let ptr = self.0.load(atomic::Ordering::Acquire);
832+
// drop our copy of the arc:
833+
unsafe { Arc::from_raw(ptr as *const _) };
834+
}
835+
}
836+
impl<T> Clone for ArcCell<T> {
837+
fn clone(&self) -> Self {
838+
ArcCell::new(self.get())
839+
}
840+
}
841+
impl<T> From<Arc<T>> for ArcCell<T> {
842+
fn from(v: Arc<T>) -> Self {
843+
Self::new(v)
844+
}
845+
}

src/librustc_mir/const_eval.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -327,8 +327,13 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
327327
ret: Option<mir::BasicBlock>,
328328
) -> InterpResult<'tcx, Option<&'mir mir::Body<'tcx>>> {
329329
debug!("eval_fn_call: {:?}", instance);
330+
// WIP: We assume all custom intrinsics are const. This is okay for now
331+
// as these are generated in the provider/query system, which caches the
332+
// resulting MIR. Effectively, this means for any specific set of generic
333+
// params, the MIR is the same for each call (ie in user code).
334+
let custom = ecx.tcx.custom_intrinsic_mir(instance);
330335
// Only check non-glue functions
331-
if let ty::InstanceDef::Item(def_id) = instance.def {
336+
if let (None, ty::InstanceDef::Item(def_id)) = (custom, instance.def) {
332337
// Execution might have wandered off into other crates, so we cannot do a stability-
333338
// sensitive check here. But we can at least rule out functions that are not const
334339
// at all.
@@ -345,7 +350,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
345350
}
346351
}
347352
// This is a const fn. Call it.
348-
Ok(Some(match ecx.load_mir(instance.def, None) {
353+
Ok(Some(match ecx.load_mir(instance, None) {
349354
Ok(body) => body,
350355
Err(err) => {
351356
if let err_unsup!(NoMirFor(ref path)) = err.kind {
@@ -692,7 +697,7 @@ pub fn const_eval_raw_provider<'tcx>(
692697
Default::default()
693698
);
694699

695-
let res = ecx.load_mir(cid.instance.def, cid.promoted);
700+
let res = ecx.load_mir(cid.instance, cid.promoted);
696701
res.and_then(
697702
|body| eval_body_using_ecx(&mut ecx, cid, body)
698703
).and_then(|place| {

src/librustc_mir/interpret/eval_context.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
282282

283283
pub fn load_mir(
284284
&self,
285-
instance: ty::InstanceDef<'tcx>,
285+
instance: ty::Instance<'tcx>,
286286
promoted: Option<mir::Promoted>,
287287
) -> InterpResult<'tcx, &'tcx mir::Body<'tcx>> {
288288
// do not continue if typeck errors occurred (can only occur in local crate)
@@ -297,7 +297,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
297297
if let Some(promoted) = promoted {
298298
return Ok(&self.tcx.promoted_mir(did)[promoted]);
299299
}
300-
match instance {
300+
match instance.def {
301301
ty::InstanceDef::Item(def_id) => if self.tcx.is_mir_available(did) {
302302
Ok(self.tcx.optimized_mir(did))
303303
} else {

src/librustc_mir/interpret/terminator.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -247,8 +247,10 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
247247
}
248248
};
249249

250+
let custom = self.tcx.custom_intrinsic_mir(instance);
251+
250252
match instance.def {
251-
ty::InstanceDef::Intrinsic(..) => {
253+
ty::InstanceDef::Intrinsic(..) if custom.is_none() => {
252254
// The intrinsic itself cannot diverge, so if we got here without a return
253255
// place... (can happen e.g., for transmute returning `!`)
254256
let dest = match dest {
@@ -262,6 +264,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
262264
self.dump_place(*dest);
263265
Ok(())
264266
}
267+
ty::InstanceDef::Intrinsic(..) |
265268
ty::InstanceDef::VtableShim(..) |
266269
ty::InstanceDef::ReifyShim(..) |
267270
ty::InstanceDef::ClosureOnceShim { .. } |
@@ -332,6 +335,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
332335
// The Rust ABI is special: ZST get skipped.
333336
let rust_abi = match caller_abi {
334337
Abi::Rust | Abi::RustCall => true,
338+
Abi::RustIntrinsic if custom.is_some() => true,
335339
_ => false
336340
};
337341
// We have two iterators: Where the arguments come from,

src/librustc_mir/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ pub fn provide(providers: &mut Providers<'_>) {
5454
borrow_check::provide(providers);
5555
shim::provide(providers);
5656
transform::provide(providers);
57+
monomorphize::provide(providers);
5758
monomorphize::partitioning::provide(providers);
5859
providers.const_eval = const_eval::const_eval_provider;
5960
providers.const_eval_raw = const_eval::const_eval_raw_provider;

src/librustc_mir/monomorphize/collector.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -747,6 +747,10 @@ fn visit_instance_use<'tcx>(
747747
if !is_direct_call {
748748
bug!("intrinsic {:?} being reified", def_id);
749749
}
750+
751+
if let Some(_mir) = tcx.custom_intrinsic_mir(instance) {
752+
output.push(create_fn_mono_item(instance));
753+
}
750754
}
751755
ty::InstanceDef::VtableShim(..) |
752756
ty::InstanceDef::ReifyShim(..) |
@@ -1251,7 +1255,7 @@ fn collect_neighbours<'tcx>(
12511255
output: &mut Vec<MonoItem<'tcx>>,
12521256
) {
12531257
debug!("collect_neighbours: {:?}", instance.def_id());
1254-
let body = tcx.instance_mir(instance.def);
1258+
let body = tcx.instance_mir(instance);
12551259

12561260
MirNeighborCollector {
12571261
tcx,

0 commit comments

Comments
 (0)