Skip to content

Commit d09730e

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 b043380 commit d09730e

File tree

22 files changed

+507
-20
lines changed

22 files changed

+507
-20
lines changed

src/librustc/mir/mod.rs

+40-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use crate::ty::print::{FmtPrinter, Printer};
1616
use crate::ty::subst::{Subst, SubstsRef};
1717
use crate::ty::{
1818
self, AdtDef, CanonicalUserTypeAnnotations, Region, Ty, TyCtxt,
19-
UserTypeAnnotationIndex,
19+
UserTypeAnnotationIndex, List,
2020
};
2121

2222
use polonius_engine::Atom;
@@ -50,7 +50,7 @@ pub mod traversal;
5050
pub mod visit;
5151

5252
/// Types for locals
53-
type LocalDecls<'tcx> = IndexVec<Local, LocalDecl<'tcx>>;
53+
pub type LocalDecls<'tcx> = IndexVec<Local, LocalDecl<'tcx>>;
5454

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

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
@@ -3028,20 +3028,26 @@ impl<'tcx> TyCtxt<'tcx> {
30283028
}
30293029

30303030
/// Returns the possibly-auto-generated MIR of a `(DefId, Subst)` pair.
3031-
pub fn instance_mir(self, instance: ty::InstanceDef<'tcx>) -> &'tcx Body<'tcx> {
3032-
match instance {
3031+
pub fn instance_mir(self, instance: ty::Instance<'tcx>) -> &'tcx Body<'tcx> {
3032+
match instance.def {
30333033
ty::InstanceDef::Item(did) => {
30343034
self.optimized_mir(did)
30353035
}
3036+
ty::InstanceDef::Intrinsic(..) => {
3037+
if let Some(mir) = self.custom_intrinsic_mir(instance) {
3038+
mir
3039+
} else {
3040+
self.mir_shims(instance.def)
3041+
}
3042+
},
30363043
ty::InstanceDef::VtableShim(..) |
30373044
ty::InstanceDef::ReifyShim(..) |
3038-
ty::InstanceDef::Intrinsic(..) |
30393045
ty::InstanceDef::FnPtrShim(..) |
30403046
ty::InstanceDef::Virtual(..) |
30413047
ty::InstanceDef::ClosureOnceShim { .. } |
30423048
ty::InstanceDef::DropGlue(..) |
30433049
ty::InstanceDef::CloneShim(..) => {
3044-
self.mir_shims(instance)
3050+
self.mir_shims(instance.def)
30453051
}
30463052
}
30473053
}
@@ -3324,7 +3330,12 @@ fn instance_def_size_estimate<'tcx>(tcx: TyCtxt<'tcx>, instance_def: InstanceDef
33243330
match instance_def {
33253331
InstanceDef::Item(..) |
33263332
InstanceDef::DropGlue(..) => {
3327-
let mir = tcx.instance_mir(instance_def);
3333+
let instance = Instance {
3334+
def: instance_def,
3335+
// this field can be whatever because it won't be used in this case.
3336+
substs: tcx.intern_substs(&[]),
3337+
};
3338+
let mir = tcx.instance_mir(instance);
33283339
mir.basic_blocks().iter().map(|bb| bb.statements.len()).sum()
33293340
},
33303341
// 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
@@ -378,7 +378,7 @@ pub fn codegen_instance<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>(
378378

379379
let lldecl = cx.get_fn(instance);
380380

381-
let mir = cx.tcx().instance_mir(instance.def);
381+
let mir = cx.tcx().instance_mir(instance);
382382
mir::codegen_mir::<Bx>(cx, lldecl, &mir, instance, sig);
383383
}
384384

src/librustc_codegen_ssa/mir/block.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -604,16 +604,22 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
604604
let arg_count = fn_ty.args.len() + fn_ty.ret.is_indirect() as usize;
605605
let mut llargs = Vec::with_capacity(arg_count);
606606

607+
// Custom intrinsics are treated as-if they were normal functions here.
608+
let is_custom_intrinsic = intrinsic.and_then(|_| instance )
609+
.map(|instance| bx.tcx().custom_intrinsic_mir(instance).is_some() )
610+
.unwrap_or_default();
611+
607612
// Prepare the return value destination
608613
let ret_dest = if let Some((ref dest, _)) = *destination {
609-
let is_intrinsic = intrinsic.is_some();
614+
let is_intrinsic = intrinsic.is_some() && !is_custom_intrinsic;
610615
self.make_return_dest(&mut bx, dest, &fn_ty.ret, &mut llargs,
611616
is_intrinsic)
612617
} else {
613618
ReturnDest::Nothing
614619
};
615620

616-
if intrinsic.is_some() && intrinsic != Some("drop_in_place") {
621+
if intrinsic.is_some() && intrinsic != Some("drop_in_place") &&
622+
!is_custom_intrinsic {
617623
let dest = match ret_dest {
618624
_ if fn_ty.ret.is_indirect() => llargs[0],
619625
ReturnDest::Nothing =>

src/librustc_codegen_utils/symbol_names.rs

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

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

src/librustc_mir/const_eval.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -330,8 +330,13 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
330330
ret: Option<mir::BasicBlock>,
331331
) -> InterpResult<'tcx, Option<&'mir mir::Body<'tcx>>> {
332332
debug!("eval_fn_call: {:?}", instance);
333+
// WIP: We assume all custom intrinsics are const. This is okay for now
334+
// as these are generated in the provider/query system, which caches the
335+
// resulting MIR. Effectively, this means for any specific set of generic
336+
// params, the MIR is the same for each call (ie in user code).
337+
let custom = ecx.tcx.custom_intrinsic_mir(instance);
333338
// Only check non-glue functions
334-
if let ty::InstanceDef::Item(def_id) = instance.def {
339+
if let (None, ty::InstanceDef::Item(def_id)) = (custom, instance.def) {
335340
// Execution might have wandered off into other crates, so we cannot do a stability-
336341
// sensitive check here. But we can at least rule out functions that are not const
337342
// at all.
@@ -348,7 +353,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
348353
}
349354
}
350355
// This is a const fn. Call it.
351-
Ok(Some(match ecx.load_mir(instance.def, None) {
356+
Ok(Some(match ecx.load_mir(instance, None) {
352357
Ok(body) => body,
353358
Err(err) => {
354359
if let err_unsup!(NoMirFor(ref path)) = err.kind {
@@ -672,7 +677,7 @@ pub fn const_eval_raw_provider<'tcx>(
672677
Default::default()
673678
);
674679

675-
let res = ecx.load_mir(cid.instance.def, cid.promoted);
680+
let res = ecx.load_mir(cid.instance, cid.promoted);
676681
res.and_then(
677682
|body| eval_body_using_ecx(&mut ecx, cid, body)
678683
).and_then(|place| {

src/librustc_mir/interpret/eval_context.rs

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

293293
pub fn load_mir(
294294
&self,
295-
instance: ty::InstanceDef<'tcx>,
295+
instance: ty::Instance<'tcx>,
296296
promoted: Option<mir::Promoted>,
297297
) -> InterpResult<'tcx, &'tcx mir::Body<'tcx>> {
298298
// do not continue if typeck errors occurred (can only occur in local crate)
@@ -307,7 +307,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
307307
if let Some(promoted) = promoted {
308308
return Ok(&self.tcx.promoted_mir(did)[promoted]);
309309
}
310-
match instance {
310+
match instance.def {
311311
ty::InstanceDef::Item(def_id) => if self.tcx.is_mir_available(did) {
312312
Ok(self.tcx.optimized_mir(did))
313313
} 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
@@ -57,6 +57,7 @@ pub fn provide(providers: &mut Providers<'_>) {
5757
providers.const_eval = const_eval::const_eval_provider;
5858
providers.const_eval_raw = const_eval::const_eval_raw_provider;
5959
providers.check_match = hair::pattern::check_match;
60+
monomorphize::provide(providers);
6061
providers.const_field = |tcx, param_env_and_value| {
6162
let (param_env, (value, field)) = param_env_and_value.into_parts();
6263
const_eval::const_field(tcx, param_env, None, field, value)

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)