Skip to content

Commit 889755f

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 1b521f5 commit 889755f

File tree

23 files changed

+487
-26
lines changed

23 files changed

+487
-26
lines changed

src/librustc_codegen_ssa/mir/block.rs

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

622+
// Custom intrinsics are treated as-if they were normal functions here.
623+
let is_custom_intrinsic = intrinsic.and_then(|_| instance )
624+
.map(|instance| bx.tcx().custom_intrinsic_mir(instance).is_some() )
625+
.unwrap_or_default();
626+
622627
// Prepare the return value destination
623628
let ret_dest = if let Some((dest, _)) = *destination {
624-
let is_intrinsic = intrinsic.is_some();
629+
let is_intrinsic = intrinsic.is_some() && !is_custom_intrinsic;
625630
self.make_return_dest(&mut bx, dest, &fn_abi.ret, &mut llargs, is_intrinsic)
626631
} else {
627632
ReturnDest::Nothing
@@ -642,7 +647,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
642647
return;
643648
}
644649

645-
if intrinsic.is_some() && intrinsic != Some("drop_in_place") {
650+
if intrinsic.is_some() && intrinsic != Some("drop_in_place") &&
651+
!is_custom_intrinsic {
646652
let dest = match ret_dest {
647653
_ if fn_abi.ret.is_indirect() => llargs[0],
648654
ReturnDest::Nothing => {

src/librustc_codegen_ssa/mir/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
137137

138138
let llfn = cx.get_fn(instance);
139139

140-
let mir = cx.tcx().instance_mir(instance.def);
140+
let mir = cx.tcx().instance_mir(instance);
141141

142142
let fn_abi = FnAbi::of_instance(cx, instance, &[]);
143143
debug!("fn_abi: {:?}", fn_abi);

src/librustc_data_structures/sync.rs

+53
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use std::collections::HashMap;
2222
use std::hash::{BuildHasher, Hash};
2323
use std::marker::PhantomData;
2424
use std::ops::{Deref, DerefMut};
25+
use std::sync::{Arc, atomic::AtomicPtr, atomic, };
2526

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

src/librustc_middle/mir/mod.rs

+31-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ mod type_foldable;
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>;
@@ -2664,3 +2664,33 @@ impl Location {
26642664
}
26652665
}
26662666
}
2667+
2668+
pub trait CustomIntrinsicMirGen: Sync + Send {
2669+
/// Codegen a plugin-defined intrinsic. This is intended to be used to
2670+
/// "return" values based on the monomorphized and erased types of the
2671+
/// function call. Codegen will codegen the `extra_stmts` and then insert
2672+
/// an unconditional branch to the exit block.
2673+
///
2674+
/// Consider this to be highly unstable; it will likely change without
2675+
/// warning. There is also no spec for this, it is 100% implementation
2676+
/// defined, and may not be implemented at all for some codegen backends.
2677+
///
2678+
/// If the codegen backend is multithreaded, this will be called from
2679+
/// any number of threads, hence `Sync + Send`.
2680+
///
2681+
/// YOU ARE RESPONSIBLE FOR THE SAFETY OF THE EXTRA STATEMENTS.
2682+
/// You have been warned. Good luck, have fun.
2683+
fn mirgen_simple_intrinsic<'tcx>(&self,
2684+
tcx: TyCtxt<'tcx>,
2685+
instance: ty::Instance<'tcx>,
2686+
mir: &mut BodyAndCache<'tcx>);
2687+
2688+
/// The following are used for typeck-ing:
2689+
2690+
/// The number of generic parameters expected.
2691+
fn generic_parameter_count<'tcx>(&self, tcx: TyCtxt<'tcx>) -> usize;
2692+
/// The types of the input args.
2693+
fn inputs<'tcx>(&self, tcx: TyCtxt<'tcx>) -> &'tcx List<Ty<'tcx>>;
2694+
/// The return type.
2695+
fn output<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx>;
2696+
}

src/librustc_middle/query/mod.rs

+15
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,21 @@ rustc_queries! {
205205
}
206206
}
207207

208+
/// If defined by the driver, returns the extra mir statements to codegen,
209+
/// else returns `None`.
210+
query custom_intrinsic_mirgen(key: DefId) -> Option<Lrc<dyn mir::CustomIntrinsicMirGen>> {
211+
anon
212+
no_hash
213+
214+
desc { |tcx| "asking for the custom MIR generator of `{}`", tcx.def_path_str(key) }
215+
}
216+
/// The monomorphized MIR for a custom intrinsic instance.
217+
query custom_intrinsic_mir(inst: ty::Instance<'tcx>) -> Option<&'tcx mir::BodyAndCache<'tcx>> {
218+
anon
219+
220+
desc { |tcx| "asking for the custom MIR of `{}`", tcx.def_path_str(inst.def_id()) }
221+
}
222+
208223
query promoted_mir(key: DefId) -> &'tcx IndexVec<mir::Promoted, mir::BodyAndCache<'tcx>> {
209224
cache_on_disk_if { key.is_local() }
210225
load_cached(tcx, id) {

src/librustc_middle/ty/mod.rs

+10-4
Original file line numberDiff line numberDiff line change
@@ -3000,17 +3000,23 @@ impl<'tcx> TyCtxt<'tcx> {
30003000
}
30013001

30023002
/// Returns the possibly-auto-generated MIR of a `(DefId, Subst)` pair.
3003-
pub fn instance_mir(self, instance: ty::InstanceDef<'tcx>) -> ReadOnlyBodyAndCache<'tcx, 'tcx> {
3004-
match instance {
3003+
pub fn instance_mir(self, instance: ty::Instance<'tcx>) -> ReadOnlyBodyAndCache<'tcx, 'tcx> {
3004+
match instance.def {
30053005
ty::InstanceDef::Item(did) => self.optimized_mir(did).unwrap_read_only(),
3006+
ty::InstanceDef::Intrinsic(..) => {
3007+
if let Some(mir) = self.custom_intrinsic_mir(instance) {
3008+
mir.unwrap_read_only()
3009+
} else {
3010+
self.mir_shims(instance.def).unwrap_read_only()
3011+
}
3012+
}
30063013
ty::InstanceDef::VtableShim(..)
30073014
| ty::InstanceDef::ReifyShim(..)
3008-
| ty::InstanceDef::Intrinsic(..)
30093015
| ty::InstanceDef::FnPtrShim(..)
30103016
| ty::InstanceDef::Virtual(..)
30113017
| ty::InstanceDef::ClosureOnceShim { .. }
30123018
| ty::InstanceDef::DropGlue(..)
3013-
| ty::InstanceDef::CloneShim(..) => self.mir_shims(instance).unwrap_read_only(),
3019+
| ty::InstanceDef::CloneShim(..) => self.mir_shims(instance.def).unwrap_read_only(),
30143020
}
30153021
}
30163022

src/librustc_mir/const_eval/eval_queries.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ pub fn const_eval_raw_provider<'tcx>(
306306
MemoryExtra { can_access_statics: is_static },
307307
);
308308

309-
let res = ecx.load_mir(cid.instance.def, cid.promoted);
309+
let res = ecx.load_mir(cid.instance, cid.promoted);
310310
res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, &body))
311311
.and_then(|place| {
312312
Ok(RawConst { alloc_id: place.ptr.assert_ptr().alloc_id, ty: place.layout.ty })

src/librustc_mir/const_eval/fn_queries.rs

+3
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,9 @@ fn is_const_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> Option<bool> {
125125

126126
match tcx.fn_sig(def_id).abi() {
127127
Abi::RustIntrinsic | Abi::PlatformIntrinsic => {
128+
if tcx.custom_intrinsic_mirgen(def_id).is_some() {
129+
return Some(true);
130+
}
128131
Some(tcx.lookup_const_stability(def_id).is_some())
129132
}
130133
_ => None,

src/librustc_mir/const_eval/machine.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter {
218218
}
219219
}
220220
// This is a const fn. Call it.
221-
Ok(Some(match ecx.load_mir(instance.def, None) {
221+
Ok(Some(match ecx.load_mir(instance, None) {
222222
Ok(body) => *body,
223223
Err(err) => {
224224
if let err_unsup!(NoMirFor(did)) = err.kind {

src/librustc_mir/interpret/eval_context.rs

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

352352
pub fn load_mir(
353353
&self,
354-
instance: ty::InstanceDef<'tcx>,
354+
instance: ty::Instance<'tcx>,
355355
promoted: Option<mir::Promoted>,
356356
) -> InterpResult<'tcx, mir::ReadOnlyBodyAndCache<'tcx, 'tcx>> {
357357
// do not continue if typeck errors occurred (can only occur in local crate)
@@ -366,7 +366,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
366366
if let Some(promoted) = promoted {
367367
return Ok(self.tcx.promoted_mir(did)[promoted].unwrap_read_only());
368368
}
369-
match instance {
369+
match instance.def {
370370
ty::InstanceDef::Item(def_id) => {
371371
if self.tcx.is_mir_available(did) {
372372
Ok(self.tcx.optimized_mir(did).unwrap_read_only())

src/librustc_mir/interpret/terminator.rs

+3
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
203203
}
204204
};
205205

206+
let custom = self.tcx.custom_intrinsic_mir(instance);
207+
206208
// ABI check
207209
{
208210
let callee_abi = {
@@ -281,6 +283,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
281283
// The Rust ABI is special: ZST get skipped.
282284
let rust_abi = match caller_abi {
283285
Abi::Rust | Abi::RustCall => true,
286+
Abi::RustIntrinsic if custom.is_some() => true,
284287
_ => false,
285288
};
286289
// We have two iterators: Where the arguments come from,

src/librustc_mir/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ pub fn provide(providers: &mut Providers<'_>) {
4848
const_eval::provide(providers);
4949
shim::provide(providers);
5050
transform::provide(providers);
51+
monomorphize::provide(providers);
5152
monomorphize::partitioning::provide(providers);
5253
providers.const_eval_validated = const_eval::const_eval_validated_provider;
5354
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
@@ -693,6 +693,10 @@ fn visit_instance_use<'tcx>(
693693
if !is_direct_call {
694694
bug!("{:?} being reified", instance);
695695
}
696+
697+
if let Some(_mir) = tcx.custom_intrinsic_mir(instance) {
698+
output.push(create_fn_mono_item(instance));
699+
}
696700
}
697701
ty::InstanceDef::DropGlue(_, None) => {
698702
// Don't need to emit noop drop glue if we are calling directly.
@@ -1157,7 +1161,7 @@ fn collect_neighbours<'tcx>(
11571161
output: &mut Vec<MonoItem<'tcx>>,
11581162
) {
11591163
debug!("collect_neighbours: {:?}", instance.def_id());
1160-
let body = tcx.instance_mir(instance.def);
1164+
let body = tcx.instance_mir(instance);
11611165

11621166
MirNeighborCollector { tcx, body: &body, output, instance }.visit_body(&body);
11631167
}

src/librustc_mir/monomorphize/mod.rs

+71-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,79 @@
1-
use rustc_middle::traits;
1+
use rustc_index::vec::IndexVec;
2+
use rustc_middle::{traits, mir};
23
use rustc_middle::ty::adjustment::CustomCoerceUnsized;
3-
use rustc_middle::ty::{self, Ty, TyCtxt};
4+
use rustc_middle::ty::{self, Ty, TyCtxt, Instance};
5+
use rustc_middle::ty::query::Providers;
6+
use rustc_span::DUMMY_SP;
47

58
pub mod collector;
69
pub mod partitioning;
710

11+
pub fn provide(providers: &mut Providers<'_>) {
12+
providers.custom_intrinsic_mirgen = |_, _| { None };
13+
providers.custom_intrinsic_mir = custom_intrinsic_mir;
14+
}
15+
16+
fn custom_intrinsic_mir<'tcx>(tcx: TyCtxt<'tcx>,
17+
instance: Instance<'tcx>)
18+
-> Option<&'tcx mir::BodyAndCache<'tcx>>
19+
{
20+
let mirgen = tcx.custom_intrinsic_mirgen(instance.def_id())?;
21+
22+
let ty = instance.monomorphic_ty(tcx);
23+
let sig = ty.fn_sig(tcx);
24+
let sig = tcx.normalize_erasing_late_bound_regions(
25+
ty::ParamEnv::reveal_all(),
26+
&sig,
27+
);
28+
29+
// no var arg calls, so we can skip monomorphizing extra arguments.
30+
assert!(!sig.c_variadic);
31+
32+
let source_scope_local_data = mir::ClearCrossCrate::Clear;
33+
let source_scope = mir::SourceScopeData {
34+
span: DUMMY_SP,
35+
parent_scope: None,
36+
local_data: source_scope_local_data,
37+
};
38+
let source_info = mir::SourceInfo {
39+
span: DUMMY_SP,
40+
scope: mir::OUTERMOST_SOURCE_SCOPE,
41+
};
42+
43+
let mut source_scopes = IndexVec::new();
44+
source_scopes.push(source_scope.clone());
45+
46+
let ret_decl = mir::LocalDecl::new_return_place(sig.output(), DUMMY_SP);
47+
let mut local_decls = IndexVec::from_elem_n(ret_decl, 1);
48+
for &arg in sig.inputs().iter() {
49+
local_decls.push(mir::LocalDecl {
50+
mutability: mir::Mutability::Mut,
51+
local_info: mir::LocalInfo::Other,
52+
ty: arg,
53+
source_info,
54+
internal: false,
55+
user_ty: mir::UserTypeProjections::none(),
56+
is_block_tail: None,
57+
});
58+
}
59+
60+
let gen = mir::Body::new(IndexVec::new(),
61+
source_scopes,
62+
local_decls,
63+
Default::default(),
64+
sig.inputs().len(),
65+
Vec::new(),
66+
source_scope.span,
67+
Vec::new(),
68+
None);
69+
let mut gen = mir::BodyAndCache::new(gen);
70+
71+
mirgen.mirgen_simple_intrinsic(tcx, instance, &mut gen);
72+
gen.ensure_predecessors();
73+
74+
Some(tcx.arena.alloc(gen))
75+
}
76+
877
pub fn custom_coerce_unsize_info<'tcx>(
978
tcx: TyCtxt<'tcx>,
1079
source_ty: Ty<'tcx>,

src/librustc_symbol_mangling/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,10 @@ fn compute_symbol_name(
183183
Node::ForeignItem(_) => true,
184184
_ => false,
185185
}
186+
} else if let ty::InstanceDef::Intrinsic(_) = instance.def {
187+
// custom intrinsics should never be foreign, otherwise
188+
// generic parameters will cause duplicate symbols names.
189+
tcx.custom_intrinsic_mir(instance).is_none()
186190
} else {
187191
tcx.is_foreign_item(def_id)
188192
};

src/librustc_ty/ty.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,10 @@ fn instance_def_size_estimate<'tcx>(
294294

295295
match instance_def {
296296
InstanceDef::Item(..) | InstanceDef::DropGlue(..) => {
297-
let mir = tcx.instance_mir(instance_def);
297+
let mir = tcx.instance_mir(ty::Instance {
298+
def: instance_def,
299+
substs: tcx.intern_substs(&[]),
300+
});
298301
mir.basic_blocks().iter().map(|bb| bb.statements.len()).sum()
299302
}
300303
// Estimate the size of other compiler-generated shims to be 1.

0 commit comments

Comments
 (0)