Skip to content

Commit 2621a36

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 b53cd5c commit 2621a36

File tree

23 files changed

+473
-19
lines changed

23 files changed

+473
-19
lines changed

compiler/rustc_codegen_ssa/src/mir/block.rs

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

627+
// Custom intrinsics are treated as-if they were normal functions here.
628+
let is_custom_intrinsic = intrinsic.and_then(|_| instance )
629+
.map(|instance| bx.tcx().custom_intrinsic_mir(instance).is_some() )
630+
.unwrap_or_default();
631+
627632
// Prepare the return value destination
628633
let ret_dest = if let Some((dest, _)) = *destination {
629-
let is_intrinsic = intrinsic.is_some();
634+
let is_intrinsic = intrinsic.is_some() && !is_custom_intrinsic;
630635
self.make_return_dest(&mut bx, dest, &fn_abi.ret, &mut llargs, is_intrinsic)
631636
} else {
632637
ReturnDest::Nothing
@@ -647,7 +652,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
647652
return;
648653
}
649654

650-
if intrinsic.is_some() && intrinsic != Some(sym::drop_in_place) {
655+
if intrinsic.is_some() && intrinsic != Some(sym::drop_in_place) &&
656+
!is_custom_intrinsic {
651657
let intrinsic = intrinsic.unwrap();
652658
let dest = match ret_dest {
653659
_ if fn_abi.ret.is_indirect() => llargs[0],

compiler/rustc_codegen_ssa/src/mir/mod.rs

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

141141
let llfn = cx.get_fn(instance);
142142

143-
let mir = cx.tcx().instance_mir(instance.def);
143+
let mir = cx.tcx().instance_mir(instance);
144144

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

compiler/rustc_data_structures/src/sync.rs

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

2526
pub use std::sync::atomic::Ordering;
2627
pub use std::sync::atomic::Ordering::SeqCst;
@@ -656,3 +657,55 @@ impl<T> DerefMut for OneThread<T> {
656657
&mut self.inner
657658
}
658659
}
660+
661+
/// Provides atomic mutability by replacing the value inside this `ArcCell`.
662+
/// Similar to the `crossbeam` structure of the same name.
663+
#[derive(Debug)]
664+
pub struct ArcCell<T>(AtomicPtr<T>);
665+
impl<T> ArcCell<T> {
666+
pub fn new(v: Arc<T>) -> Self {
667+
ArcCell(AtomicPtr::new(Arc::into_raw(v) as *mut _))
668+
}
669+
pub fn get(&self) -> Arc<T> {
670+
let ptr = self.0.load(atomic::Ordering::Acquire);
671+
let arc = unsafe { Arc::from_raw(ptr as *const T) };
672+
let ret = arc.clone();
673+
// don't drop our copy:
674+
::std::mem::forget(arc);
675+
ret
676+
}
677+
/// Update the value, returning the previous value.
678+
pub fn set(&self, v: Arc<T>) -> Arc<T> {
679+
let new = Arc::into_raw(v) as *mut _;
680+
let mut expected = self.0.load(atomic::Ordering::Acquire);
681+
loop {
682+
match self.0.compare_exchange_weak(expected, new,
683+
atomic::Ordering::SeqCst,
684+
atomic::Ordering::Acquire) {
685+
Ok(old) => {
686+
return unsafe { Arc::from_raw(old as *const T) };
687+
},
688+
Err(v) => {
689+
expected = v;
690+
},
691+
}
692+
}
693+
}
694+
}
695+
impl<T> Drop for ArcCell<T> {
696+
fn drop(&mut self) {
697+
let ptr = self.0.load(atomic::Ordering::Acquire);
698+
// drop our copy of the arc:
699+
unsafe { Arc::from_raw(ptr as *const _) };
700+
}
701+
}
702+
impl<T> Clone for ArcCell<T> {
703+
fn clone(&self) -> Self {
704+
ArcCell::new(self.get())
705+
}
706+
}
707+
impl<T> From<Arc<T>> for ArcCell<T> {
708+
fn from(v: Arc<T>) -> Self {
709+
Self::new(v)
710+
}
711+
}

compiler/rustc_middle/src/mir/mod.rs

+31-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ mod type_foldable;
5454
pub mod visit;
5555

5656
/// Types for locals
57-
type LocalDecls<'tcx> = IndexVec<Local, LocalDecl<'tcx>>;
57+
pub type LocalDecls<'tcx> = IndexVec<Local, LocalDecl<'tcx>>;
5858

5959
pub trait HasLocalDecls<'tcx> {
6060
fn local_decls(&self) -> &LocalDecls<'tcx>;
@@ -2674,3 +2674,33 @@ impl Location {
26742674
}
26752675
}
26762676
}
2677+
2678+
pub trait CustomIntrinsicMirGen: Sync + Send {
2679+
/// Codegen a plugin-defined intrinsic. This is intended to be used to
2680+
/// "return" values based on the monomorphized and erased types of the
2681+
/// function call. Codegen will codegen the `extra_stmts` and then insert
2682+
/// an unconditional branch to the exit block.
2683+
///
2684+
/// Consider this to be highly unstable; it will likely change without
2685+
/// warning. There is also no spec for this, it is 100% implementation
2686+
/// defined, and may not be implemented at all for some codegen backends.
2687+
///
2688+
/// If the codegen backend is multithreaded, this will be called from
2689+
/// any number of threads, hence `Sync + Send`.
2690+
///
2691+
/// YOU ARE RESPONSIBLE FOR THE SAFETY OF THE EXTRA STATEMENTS.
2692+
/// You have been warned. Good luck, have fun.
2693+
fn mirgen_simple_intrinsic<'tcx>(&self,
2694+
tcx: TyCtxt<'tcx>,
2695+
instance: ty::Instance<'tcx>,
2696+
mir: &mut Body<'tcx>);
2697+
2698+
/// The following are used for typeck-ing:
2699+
2700+
/// The number of generic parameters expected.
2701+
fn generic_parameter_count<'tcx>(&self, tcx: TyCtxt<'tcx>) -> usize;
2702+
/// The types of the input args.
2703+
fn inputs<'tcx>(&self, tcx: TyCtxt<'tcx>) -> &'tcx List<Ty<'tcx>>;
2704+
/// The return type.
2705+
fn output<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx>;
2706+
}

compiler/rustc_middle/src/query/mod.rs

+15
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,21 @@ rustc_queries! {
306306
}
307307
}
308308

309+
/// If defined by the driver, returns the extra mir statements to codegen,
310+
/// else returns `None`.
311+
query custom_intrinsic_mirgen(key: DefId) -> Option<Lrc<dyn mir::CustomIntrinsicMirGen>> {
312+
anon
313+
no_hash
314+
315+
desc { |tcx| "asking for the custom MIR generator of `{}`", tcx.def_path_str(key) }
316+
}
317+
/// The monomorphized MIR for a custom intrinsic instance.
318+
query custom_intrinsic_mir(inst: ty::Instance<'tcx>) -> Option<&'tcx mir::Body<'tcx>> {
319+
anon
320+
321+
desc { |tcx| "asking for the custom MIR of `{}`", tcx.def_path_str(inst.def_id()) }
322+
}
323+
309324
/// Returns coverage summary info for a function, after executing the `InstrumentCoverage`
310325
/// MIR pass (assuming the -Zinstrument-coverage option is enabled).
311326
query coverageinfo(key: DefId) -> mir::CoverageInfo {

compiler/rustc_middle/src/ty/mod.rs

+10-4
Original file line numberDiff line numberDiff line change
@@ -2951,23 +2951,29 @@ impl<'tcx> TyCtxt<'tcx> {
29512951
}
29522952

29532953
/// Returns the possibly-auto-generated MIR of a `(DefId, Subst)` pair.
2954-
pub fn instance_mir(self, instance: ty::InstanceDef<'tcx>) -> &'tcx Body<'tcx> {
2955-
match instance {
2954+
pub fn instance_mir(self, instance: ty::Instance<'tcx>) -> &'tcx Body<'tcx> {
2955+
match instance.def {
29562956
ty::InstanceDef::Item(def) => {
29572957
if let Some((did, param_did)) = def.as_const_arg() {
29582958
self.optimized_mir_of_const_arg((did, param_did))
29592959
} else {
29602960
self.optimized_mir(def.did)
29612961
}
29622962
}
2963+
ty::InstanceDef::Intrinsic(..) => {
2964+
if let Some(mir) = self.custom_intrinsic_mir(instance) {
2965+
mir
2966+
} else {
2967+
self.mir_shims(instance.def)
2968+
}
2969+
}
29632970
ty::InstanceDef::VtableShim(..)
29642971
| ty::InstanceDef::ReifyShim(..)
2965-
| ty::InstanceDef::Intrinsic(..)
29662972
| ty::InstanceDef::FnPtrShim(..)
29672973
| ty::InstanceDef::Virtual(..)
29682974
| ty::InstanceDef::ClosureOnceShim { .. }
29692975
| ty::InstanceDef::DropGlue(..)
2970-
| ty::InstanceDef::CloneShim(..) => self.mir_shims(instance),
2976+
| ty::InstanceDef::CloneShim(..) => self.mir_shims(instance.def),
29712977
}
29722978
}
29732979

compiler/rustc_mir/src/const_eval/eval_queries.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
293293
MemoryExtra { can_access_statics: is_static },
294294
);
295295

296-
let res = ecx.load_mir(cid.instance.def, cid.promoted);
296+
let res = ecx.load_mir(cid.instance, cid.promoted);
297297
match res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, &body)) {
298298
Err(error) => {
299299
let err = ConstEvalErr::new(&ecx, error, None);

compiler/rustc_mir/src/const_eval/machine.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
243243
}
244244
}
245245
// This is a const fn. Call it.
246-
Ok(Some(match ecx.load_mir(instance.def, None) {
246+
Ok(Some(match ecx.load_mir(instance, None) {
247247
Ok(body) => body,
248248
Err(err) => {
249249
if let err_unsup!(NoMirFor(did)) = err.kind {

compiler/rustc_mir/src/interpret/eval_context.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -424,11 +424,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
424424

425425
pub fn load_mir(
426426
&self,
427-
instance: ty::InstanceDef<'tcx>,
427+
instance: ty::Instance<'tcx>,
428428
promoted: Option<mir::Promoted>,
429429
) -> InterpResult<'tcx, &'tcx mir::Body<'tcx>> {
430430
// do not continue if typeck errors occurred (can only occur in local crate)
431-
let def = instance.with_opt_param();
431+
let def = instance.def.with_opt_param();
432432
if let Some(def) = def.as_local() {
433433
if self.tcx.has_typeck_results(def.did) {
434434
if let Some(error_reported) = self.tcx.typeck_opt_const_arg(def).tainted_by_errors {
@@ -440,7 +440,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
440440
if let Some(promoted) = promoted {
441441
return Ok(&self.tcx.promoted_mir_of_opt_const_arg(def)[promoted]);
442442
}
443-
match instance {
443+
match instance.def {
444444
ty::InstanceDef::Item(def) => {
445445
if self.tcx.is_mir_available(def.did) {
446446
if let Some((did, param_did)) = def.as_const_arg() {

compiler/rustc_mir/src/interpret/terminator.rs

+3
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
223223
}
224224
};
225225

226+
let custom = self.tcx.custom_intrinsic_mir(instance);
227+
226228
// ABI check
227229
{
228230
let callee_abi = {
@@ -301,6 +303,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
301303
// The Rust ABI is special: ZST get skipped.
302304
let rust_abi = match caller_abi {
303305
Abi::Rust | Abi::RustCall => true,
306+
Abi::RustIntrinsic if custom.is_some() => true,
304307
_ => false,
305308
};
306309
// We have two iterators: Where the arguments come from,

compiler/rustc_mir/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ pub fn provide(providers: &mut Providers) {
5050
const_eval::provide(providers);
5151
shim::provide(providers);
5252
transform::provide(providers);
53+
monomorphize::provide(providers);
5354
monomorphize::partitioning::provide(providers);
5455
monomorphize::polymorphize::provide(providers);
5556
providers.eval_to_const_value_raw = const_eval::eval_to_const_value_raw_provider;

compiler/rustc_mir/src/monomorphize/collector.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -769,6 +769,10 @@ fn visit_instance_use<'tcx>(
769769
if !is_direct_call {
770770
bug!("{:?} being reified", instance);
771771
}
772+
773+
if let Some(_mir) = tcx.custom_intrinsic_mir(instance) {
774+
output.push(create_fn_mono_item(tcx, instance, source));
775+
}
772776
}
773777
ty::InstanceDef::DropGlue(_, None) => {
774778
// Don't need to emit noop drop glue if we are calling directly.
@@ -1237,7 +1241,7 @@ fn collect_neighbours<'tcx>(
12371241
output: &mut Vec<Spanned<MonoItem<'tcx>>>,
12381242
) {
12391243
debug!("collect_neighbours: {:?}", instance.def_id());
1240-
let body = tcx.instance_mir(instance.def);
1244+
let body = tcx.instance_mir(instance);
12411245

12421246
MirNeighborCollector { tcx, body: &body, output, instance }.visit_body(&body);
12431247
}

compiler/rustc_mir/src/monomorphize/mod.rs

+68-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +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
use rustc_hir::lang_items::LangItem;
69

710
pub mod collector;
811
pub mod partitioning;
912
pub mod polymorphize;
1013

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

compiler/rustc_symbol_mangling/src/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,10 @@ fn compute_symbol_name(
178178
Node::ForeignItem(_) => true,
179179
_ => false,
180180
}
181+
} else if let ty::InstanceDef::Intrinsic(_) = instance.def {
182+
// custom intrinsics should never be foreign, otherwise
183+
// generic parameters will cause duplicate symbols names.
184+
tcx.custom_intrinsic_mir(instance).is_none()
181185
} else {
182186
tcx.is_foreign_item(def_id)
183187
};

compiler/rustc_ty/src/ty.rs

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

424424
match instance_def {
425425
InstanceDef::Item(..) | InstanceDef::DropGlue(..) => {
426-
let mir = tcx.instance_mir(instance_def);
426+
let mir = tcx.instance_mir(ty::Instance {
427+
def: instance_def,
428+
substs: tcx.intern_substs(&[]),
429+
});
427430
mir.basic_blocks().iter().map(|bb| bb.statements.len()).sum()
428431
}
429432
// Estimate the size of other compiler-generated shims to be 1.

0 commit comments

Comments
 (0)