Skip to content

Commit b0ff73f

Browse files
Plugin provided "intrinsics".
Allows plugins to define functions which are only expanded by the plugin after all type checking, inference, etc etc, ie only once codegen encounters a call to the intrinsic. In this initial version, the codegen crate is reponsible for calling the plugin intrinsic codgen trait and codegening the resulting extra MIR statements. Plugins are limited to `mir::StatementKind` and those contained therein, so they can't insert extra basic blocks, or insert calls to arbitraty functions. This means the compiler can still reason about what is reachable. 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 4f0ca92 commit b0ff73f

File tree

14 files changed

+417
-4
lines changed

14 files changed

+417
-4
lines changed

src/librustc/mir/mod.rs

+37
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use rustc_data_structures::small_vec::SmallVec;
2929
use rustc_data_structures::sync::Lrc;
3030
use rustc_data_structures::sync::ReadGuard;
3131
use rustc_serialize as serialize;
32+
use std::collections::HashMap;
3233
use std::borrow::Cow;
3334
use std::fmt::{self, Debug, Formatter, Write};
3435
use std::ops::{Index, IndexMut};
@@ -2923,3 +2924,39 @@ impl<'tcx> TypeFoldable<'tcx> for Literal<'tcx> {
29232924
}
29242925
}
29252926
}
2927+
pub type PluginIntrinsics = HashMap<String, Box<PluginIntrinsicCodegen>>;
2928+
pub trait PluginIntrinsicCodegen: Sync + Send {
2929+
/// Codegen a plugin-defined intrinsic. This is intended to be used to
2930+
/// "return" values based on the monomorphized and erased types of the
2931+
/// function call. Codegen will codegen the `extra_stmts` and then insert
2932+
/// an unconditional branch to the exit block.
2933+
///
2934+
/// Consider this to be highly unstable; it will likely change without
2935+
/// warning. There is also no spec for this, it is 100% implementation
2936+
/// defined, and may not be implemented at all for some codegen backends.
2937+
///
2938+
/// If the codegen backend is multithreaded, this will be called from
2939+
/// any number of threads, hence `Sync + Send`.
2940+
///
2941+
/// YOU ARE RESPONSIBLE FOR THE SAFETY OF THE EXTRA STATEMENTS.
2942+
/// You have been warned. Good luck, have fun.
2943+
fn codegen_simple_intrinsic<'a, 'tcx>(&self,
2944+
tcx: TyCtxt<'a, 'tcx, 'tcx>,
2945+
source_info: SourceInfo,
2946+
sig: &ty::FnSig<'tcx>,
2947+
parent_mir: &Mir<'tcx>,
2948+
parent_param_substs: &'tcx Substs<'tcx>,
2949+
args: &Vec<Operand<'tcx>>,
2950+
dest: Place<'tcx>,
2951+
extra_stmts: &mut Vec<StatementKind<'tcx>>)
2952+
where 'tcx: 'a;
2953+
2954+
/// The following are used for during typeck:
2955+
2956+
/// The number of generic parameters expected.
2957+
fn generic_parameter_count<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> usize;
2958+
/// The types of the input args.
2959+
fn inputs<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Vec<Ty<'tcx>>;
2960+
/// The return type.
2961+
fn output<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Ty<'tcx>;
2962+
}

src/librustc/session/mod.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,16 @@ use lint;
1919
use lint::builtin::BuiltinLintDiagnostics;
2020
use middle::allocator::AllocatorKind;
2121
use middle::dependency_format;
22+
use mir::PluginIntrinsics;
2223
use session::search_paths::PathKind;
2324
use session::config::{OutputType};
2425
use ty::tls;
2526
use util::nodemap::{FxHashSet};
2627
use util::common::{duration_to_secs_str, ErrorReported};
2728
use util::common::ProfileQueriesMsg;
2829

29-
use rustc_data_structures::sync::{self, Lrc, Lock, LockCell, OneThread, Once, RwLock};
30+
use rustc_data_structures::sync::{self, Lrc, Lock, LockCell, OneThread, Once,
31+
RwLock, ArcCell, };
3032

3133
use syntax::ast::NodeId;
3234
use errors::{self, DiagnosticBuilder, DiagnosticId};
@@ -54,6 +56,7 @@ use std::fmt;
5456
use std::io::Write;
5557
use std::path::{Path, PathBuf};
5658
use std::time::Duration;
59+
use std::sync::Arc;
5760
use std::sync::mpsc;
5861
use std::sync::atomic::{AtomicUsize, Ordering};
5962

@@ -92,6 +95,7 @@ pub struct Session {
9295
pub one_time_diagnostics: Lock<FxHashSet<(DiagnosticMessageId, Option<Span>, String)>>,
9396
pub plugin_llvm_passes: OneThread<RefCell<Vec<String>>>,
9497
pub plugin_attributes: OneThread<RefCell<Vec<(String, AttributeType)>>>,
98+
pub plugin_intrinsics: ArcCell<PluginIntrinsics>,
9599
pub crate_types: Once<Vec<config::CrateType>>,
96100
pub dependency_formats: Once<dependency_format::Dependencies>,
97101
/// The crate_disambiguator is constructed out of all the `-C metadata`
@@ -1109,6 +1113,7 @@ pub fn build_session_(
11091113
one_time_diagnostics: Lock::new(FxHashSet()),
11101114
plugin_llvm_passes: OneThread::new(RefCell::new(Vec::new())),
11111115
plugin_attributes: OneThread::new(RefCell::new(Vec::new())),
1116+
plugin_intrinsics: ArcCell::new(Arc::new(HashMap::new())),
11121117
crate_types: Once::new(),
11131118
dependency_formats: Once::new(),
11141119
crate_disambiguator: Once::new(),

src/librustc_codegen_llvm/context.rs

+3
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use type_of::PointeeInfo;
2626

2727
use rustc_data_structures::base_n;
2828
use rustc::mir::mono::Stats;
29+
use rustc::mir::PluginIntrinsics;
2930
use rustc::session::config::{self, NoDebugInfo};
3031
use rustc::session::Session;
3132
use rustc::ty::layout::{LayoutError, LayoutOf, Size, TyLayout};
@@ -102,6 +103,7 @@ pub struct CodegenCx<'a, 'tcx: 'a> {
102103
pub rust_try_fn: Cell<Option<ValueRef>>,
103104

104105
intrinsics: RefCell<FxHashMap<&'static str, ValueRef>>,
106+
pub plugin_intrinsics: Arc<PluginIntrinsics>,
105107

106108
/// A counter that is used for generating local symbol names
107109
local_gen_sym_counter: Cell<usize>,
@@ -306,6 +308,7 @@ impl<'a, 'tcx> CodegenCx<'a, 'tcx> {
306308
eh_unwind_resume: Cell::new(None),
307309
rust_try_fn: Cell::new(None),
308310
intrinsics: RefCell::new(FxHashMap()),
311+
plugin_intrinsics: tcx.sess.plugin_intrinsics.get(),
309312
local_gen_sym_counter: Cell::new(0),
310313
};
311314
cx.isize_ty = Type::isize(&cx);

src/librustc_codegen_llvm/mir/block.rs

+32
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,38 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> {
454454
return;
455455
}
456456

457+
if let Some(intrinsic) = intrinsic {
458+
if let Some(trans) = bx.cx.plugin_intrinsics.get(intrinsic) {
459+
// Get the extra statements from the plugin:
460+
let &(ref dest, target) = destination
461+
.as_ref()
462+
.unwrap();
463+
464+
let mut extra_stmts = Vec::new();
465+
trans.codegen_simple_intrinsic(bx.tcx(),
466+
terminator.source_info,
467+
&sig,
468+
self.mir,
469+
self.param_substs,
470+
args,
471+
dest.clone(),
472+
&mut extra_stmts);
473+
474+
// Now, codegen:
475+
for stmt_kind in extra_stmts.into_iter() {
476+
let stmt = mir::Statement {
477+
source_info: terminator.source_info,
478+
kind: stmt_kind,
479+
};
480+
bx = self.codegen_statement(bx, &stmt);
481+
}
482+
483+
// Lastly, jump to the target block:
484+
funclet_br(self, bx, target);
485+
return;
486+
}
487+
}
488+
457489
let extra_args = &args[sig.inputs().len()..];
458490
let extra_args = extra_args.iter().map(|op_arg| {
459491
let op_ty = op_arg.ty(self.mir, bx.tcx());

src/librustc_data_structures/sync.rs

+53
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ use std::fmt::Debug;
3939
use std::fmt::Formatter;
4040
use std::fmt;
4141
use std::ops::{Deref, DerefMut};
42+
use std::sync::{Arc, atomic::AtomicPtr, atomic, };
4243
use owning_ref::{Erased, OwningRef};
4344

4445
pub fn serial_join<A, B, RA, RB>(oper_a: A, oper_b: B) -> (RA, RB)
@@ -751,3 +752,55 @@ impl<T> DerefMut for OneThread<T> {
751752
&mut self.inner
752753
}
753754
}
755+
756+
/// Provides atomic mutability by replacing the value inside this `ArcCell`.
757+
/// Similar to the `crossbeam` structure of the same name.
758+
#[derive(Debug)]
759+
pub struct ArcCell<T>(AtomicPtr<T>);
760+
impl<T> ArcCell<T> {
761+
pub fn new(v: Arc<T>) -> Self {
762+
ArcCell(AtomicPtr::new(Arc::into_raw(v) as *mut _))
763+
}
764+
pub fn get(&self) -> Arc<T> {
765+
let ptr = self.0.load(atomic::Ordering::Acquire);
766+
let arc = unsafe { Arc::from_raw(ptr as *const T) };
767+
let ret = arc.clone();
768+
// don't drop our copy:
769+
::std::mem::forget(arc);
770+
ret
771+
}
772+
/// Update the value, returning the previous value.
773+
pub fn set(&self, v: Arc<T>) -> Arc<T> {
774+
let new = Arc::into_raw(v) as *mut _;
775+
let mut expected = self.0.load(atomic::Ordering::Acquire);
776+
loop {
777+
match self.0.compare_exchange_weak(expected, new,
778+
atomic::Ordering::SeqCst,
779+
atomic::Ordering::Acquire) {
780+
Ok(old) => {
781+
return unsafe { Arc::from_raw(old as *const T) };
782+
},
783+
Err(v) => {
784+
expected = v;
785+
},
786+
}
787+
}
788+
}
789+
}
790+
impl<T> Drop for ArcCell<T> {
791+
fn drop(&mut self) {
792+
let ptr = self.0.load(atomic::Ordering::Acquire);
793+
// drop our copy of the arc:
794+
unsafe { Arc::from_raw(ptr as *const _) };
795+
}
796+
}
797+
impl<T> Clone for ArcCell<T> {
798+
fn clone(&self) -> Self {
799+
ArcCell::new(self.get())
800+
}
801+
}
802+
impl<T> From<Arc<T>> for ArcCell<T> {
803+
fn from(v: Arc<T>) -> Self {
804+
Self::new(v)
805+
}
806+
}

src/librustc_driver/driver.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ use std::io::{self, Write};
5050
use std::iter;
5151
use std::path::{Path, PathBuf};
5252
use rustc_data_structures::sync::{self, Lrc, Lock};
53-
use std::sync::mpsc;
53+
use std::sync::{mpsc, Arc, };
5454
use syntax::{self, ast, attr, diagnostics, visit};
5555
use syntax::ext::base::ExtCtxt;
5656
use syntax::fold::Folder;
@@ -896,6 +896,7 @@ where
896896
lint_groups,
897897
llvm_passes,
898898
attributes,
899+
intrinsics,
899900
..
900901
} = registry;
901902

@@ -914,6 +915,7 @@ where
914915

915916
*sess.plugin_llvm_passes.borrow_mut() = llvm_passes;
916917
*sess.plugin_attributes.borrow_mut() = attributes.clone();
918+
sess.plugin_intrinsics.set(Arc::new(intrinsics));
917919
})?;
918920

919921
// Lint plugins are registered; now we can process command line flags.

src/librustc_plugin/registry.rs

+16
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//! Used by plugin crates to tell `rustc` about the plugins they provide.
1212
1313
use rustc::lint::{EarlyLintPassObject, LateLintPassObject, LintId, Lint};
14+
use rustc::mir::{PluginIntrinsics, PluginIntrinsicCodegen};
1415
use rustc::session::Session;
1516

1617
use syntax::ext::base::{SyntaxExtension, NamedSyntaxExtension, NormalTT, IdentTT};
@@ -61,6 +62,9 @@ pub struct Registry<'a> {
6162
#[doc(hidden)]
6263
pub attributes: Vec<(String, AttributeType)>,
6364

65+
#[doc(hidden)]
66+
pub intrinsics: PluginIntrinsics,
67+
6468
whitelisted_custom_derives: Vec<ast::Name>,
6569
}
6670

@@ -77,6 +81,7 @@ impl<'a> Registry<'a> {
7781
lint_groups: HashMap::new(),
7882
llvm_passes: vec![],
7983
attributes: vec![],
84+
intrinsics: HashMap::new(),
8085
whitelisted_custom_derives: Vec::new(),
8186
}
8287
}
@@ -95,6 +100,17 @@ impl<'a> Registry<'a> {
95100
self.args_hidden.as_ref().map(|v| &v[..]).unwrap_or(&[])
96101
}
97102

103+
/// Register a plugin intrinsic. Ignored if `name` is a normal Rust intrinsic.
104+
///
105+
/// When a function call to the named intrinsic is made, codegen (only LLVM, currently)
106+
/// will replace the usual function call with the extra statements provided by the passed
107+
/// trait object. It will then branch directly to the exit block. It is highly unsafe. Do not
108+
/// use lightly.
109+
pub fn register_intrinsic(&mut self, name: String,
110+
codegen: Box<PluginIntrinsicCodegen>) {
111+
self.intrinsics.insert(name, codegen);
112+
}
113+
98114
/// Register a syntax extension of any kind.
99115
///
100116
/// This is the most general hook into `libsyntax`'s expansion behavior.

src/librustc_typeck/check/intrinsic.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -319,12 +319,20 @@ pub fn check_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
319319
}
320320

321321
ref other => {
322-
struct_span_err!(tcx.sess, it.span, E0093,
322+
let plugin_intrinsics = tcx.sess.plugin_intrinsics.get();
323+
if let Some(plugin) = plugin_intrinsics.get(*other) {
324+
(plugin.generic_parameter_count(tcx),
325+
plugin.inputs(tcx),
326+
plugin.output(tcx))
327+
} else {
328+
struct_span_err!(tcx.sess, it.span, E0093,
323329
"unrecognized intrinsic function: `{}`",
324330
*other)
325331
.span_label(it.span, "unrecognized intrinsic")
326332
.emit();
327-
return;
333+
return;
334+
335+
}
328336
}
329337
};
330338
(n_tps, inputs, output)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// force-host
12+
13+
#![feature(plugin_registrar, rustc_private)]
14+
#![deny(plugin_as_library)] // should have no effect in a plugin crate
15+
16+
extern crate rustc;
17+
extern crate rustc_plugin;
18+
19+
20+
use rustc::mir::*;
21+
use rustc::ty::{Ty, TyCtxt, FnSig, subst::Substs, };
22+
use rustc_plugin::Registry;
23+
24+
#[plugin_registrar]
25+
pub fn plugin_registrar(reg: &mut Registry) {
26+
let codegen = Box::new(GenericCountMismatch) as Box<_>;
27+
reg.register_intrinsic("generic_count_mismatch".into(), codegen);
28+
let codegen = Box::new(InputOutputMismatch) as Box<_>;
29+
reg.register_intrinsic("type_mismatch".into(), codegen);
30+
}
31+
32+
struct GenericCountMismatch;
33+
impl PluginIntrinsicCodegen for GenericCountMismatch {
34+
fn codegen_simple_intrinsic<'a, 'tcx>(&self,
35+
_tcx: TyCtxt<'a, 'tcx, 'tcx>,
36+
_source_info: SourceInfo,
37+
_sig: &FnSig<'tcx>,
38+
_parent_mir: &Mir<'tcx>,
39+
_parent_param_substs: &'tcx Substs<'tcx>,
40+
_args: &Vec<Operand<'tcx>>,
41+
_dest: Place<'tcx>,
42+
_extra_stmts: &mut Vec<StatementKind<'tcx>>)
43+
where 'tcx: 'a,
44+
{
45+
unreachable!()
46+
}
47+
48+
/// The number of generic parameters expected.
49+
fn generic_parameter_count<'a, 'tcx>(&self, _tcx: TyCtxt<'a, 'tcx, 'tcx>) -> usize { 5 }
50+
/// The types of the input args.
51+
fn inputs<'a, 'tcx>(&self, _tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Vec<Ty<'tcx>> { vec![] }
52+
/// The return type.
53+
fn output<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Ty<'tcx> {
54+
tcx.mk_nil()
55+
}
56+
}
57+
58+
struct InputOutputMismatch;
59+
impl PluginIntrinsicCodegen for InputOutputMismatch {
60+
fn codegen_simple_intrinsic<'a, 'tcx>(&self,
61+
_tcx: TyCtxt<'a, 'tcx, 'tcx>,
62+
_source_info: SourceInfo,
63+
_sig: &FnSig<'tcx>,
64+
_parent_mir: &Mir<'tcx>,
65+
_parent_param_substs: &'tcx Substs<'tcx>,
66+
_args: &Vec<Operand<'tcx>>,
67+
_dest: Place<'tcx>,
68+
_extra_stmts: &mut Vec<StatementKind<'tcx>>)
69+
where 'tcx: 'a,
70+
{
71+
unreachable!()
72+
}
73+
74+
/// The number of generic parameters expected.
75+
fn generic_parameter_count<'a, 'tcx>(&self, _tcx: TyCtxt<'a, 'tcx, 'tcx>) -> usize { 0 }
76+
/// The types of the input args.
77+
fn inputs<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Vec<Ty<'tcx>> {
78+
vec![tcx.types.u64]
79+
}
80+
/// The return type.
81+
fn output<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Ty<'tcx> {
82+
tcx.types.u64
83+
}
84+
}

0 commit comments

Comments
 (0)