diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index cd4b32735e57a..d63ff148ccece 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -29,6 +29,7 @@ use rustc_data_structures::small_vec::SmallVec; use rustc_data_structures::sync::Lrc; use rustc_data_structures::sync::ReadGuard; use rustc_serialize as serialize; +use std::collections::HashMap; use std::borrow::Cow; use std::fmt::{self, Debug, Formatter, Write}; use std::ops::{Index, IndexMut}; @@ -2923,3 +2924,39 @@ impl<'tcx> TypeFoldable<'tcx> for Literal<'tcx> { } } } +pub type PluginIntrinsics = HashMap>; +pub trait PluginIntrinsicCodegen: Sync + Send { + /// Codegen a plugin-defined intrinsic. This is intended to be used to + /// "return" values based on the monomorphized and erased types of the + /// function call. Codegen will codegen the `extra_stmts` and then insert + /// an unconditional branch to the exit block. + /// + /// Consider this to be highly unstable; it will likely change without + /// warning. There is also no spec for this, it is 100% implementation + /// defined, and may not be implemented at all for some codegen backends. + /// + /// If the codegen backend is multithreaded, this will be called from + /// any number of threads, hence `Sync + Send`. + /// + /// YOU ARE RESPONSIBLE FOR THE SAFETY OF THE EXTRA STATEMENTS. + /// You have been warned. Good luck, have fun. + fn codegen_simple_intrinsic<'a, 'tcx>(&self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + source_info: SourceInfo, + sig: &ty::FnSig<'tcx>, + parent_mir: &Mir<'tcx>, + parent_param_substs: &'tcx Substs<'tcx>, + args: &Vec>, + dest: Place<'tcx>, + extra_stmts: &mut Vec>) + where 'tcx: 'a; + + /// The following are used for during typeck: + + /// The number of generic parameters expected. + fn generic_parameter_count<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> usize; + /// The types of the input args. + fn inputs<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Vec>; + /// The return type. + fn output<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Ty<'tcx>; +} diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 076d56fb80842..86f84c4c2c6e8 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -19,6 +19,7 @@ use lint; use lint::builtin::BuiltinLintDiagnostics; use middle::allocator::AllocatorKind; use middle::dependency_format; +use mir::PluginIntrinsics; use session::search_paths::PathKind; use session::config::{OutputType}; use ty::tls; @@ -26,7 +27,8 @@ use util::nodemap::{FxHashSet}; use util::common::{duration_to_secs_str, ErrorReported}; use util::common::ProfileQueriesMsg; -use rustc_data_structures::sync::{self, Lrc, Lock, LockCell, OneThread, Once, RwLock}; +use rustc_data_structures::sync::{self, Lrc, Lock, LockCell, OneThread, Once, + RwLock, ArcCell, }; use syntax::ast::NodeId; use errors::{self, DiagnosticBuilder, DiagnosticId}; @@ -54,6 +56,7 @@ use std::fmt; use std::io::Write; use std::path::{Path, PathBuf}; use std::time::Duration; +use std::sync::Arc; use std::sync::mpsc; use std::sync::atomic::{AtomicUsize, Ordering}; @@ -92,6 +95,7 @@ pub struct Session { pub one_time_diagnostics: Lock, String)>>, pub plugin_llvm_passes: OneThread>>, pub plugin_attributes: OneThread>>, + pub plugin_intrinsics: ArcCell, pub crate_types: Once>, pub dependency_formats: Once, /// The crate_disambiguator is constructed out of all the `-C metadata` @@ -1109,6 +1113,7 @@ pub fn build_session_( one_time_diagnostics: Lock::new(FxHashSet()), plugin_llvm_passes: OneThread::new(RefCell::new(Vec::new())), plugin_attributes: OneThread::new(RefCell::new(Vec::new())), + plugin_intrinsics: ArcCell::new(Arc::new(HashMap::new())), crate_types: Once::new(), dependency_formats: Once::new(), crate_disambiguator: Once::new(), diff --git a/src/librustc_codegen_llvm/context.rs b/src/librustc_codegen_llvm/context.rs index b774d7c5def21..7180f5b8d88db 100644 --- a/src/librustc_codegen_llvm/context.rs +++ b/src/librustc_codegen_llvm/context.rs @@ -26,6 +26,7 @@ use type_of::PointeeInfo; use rustc_data_structures::base_n; use rustc::mir::mono::Stats; +use rustc::mir::PluginIntrinsics; use rustc::session::config::{self, NoDebugInfo}; use rustc::session::Session; use rustc::ty::layout::{LayoutError, LayoutOf, Size, TyLayout}; @@ -102,6 +103,7 @@ pub struct CodegenCx<'a, 'tcx: 'a> { pub rust_try_fn: Cell>, intrinsics: RefCell>, + pub plugin_intrinsics: Arc, /// A counter that is used for generating local symbol names local_gen_sym_counter: Cell, @@ -306,6 +308,7 @@ impl<'a, 'tcx> CodegenCx<'a, 'tcx> { eh_unwind_resume: Cell::new(None), rust_try_fn: Cell::new(None), intrinsics: RefCell::new(FxHashMap()), + plugin_intrinsics: tcx.sess.plugin_intrinsics.get(), local_gen_sym_counter: Cell::new(0), }; cx.isize_ty = Type::isize(&cx); diff --git a/src/librustc_codegen_llvm/mir/block.rs b/src/librustc_codegen_llvm/mir/block.rs index 14d20b6dbe297..d074868654576 100644 --- a/src/librustc_codegen_llvm/mir/block.rs +++ b/src/librustc_codegen_llvm/mir/block.rs @@ -454,6 +454,38 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { return; } + if let Some(intrinsic) = intrinsic { + if let Some(trans) = bx.cx.plugin_intrinsics.get(intrinsic) { + // Get the extra statements from the plugin: + let &(ref dest, target) = destination + .as_ref() + .unwrap(); + + let mut extra_stmts = Vec::new(); + trans.codegen_simple_intrinsic(bx.tcx(), + terminator.source_info, + &sig, + self.mir, + self.param_substs, + args, + dest.clone(), + &mut extra_stmts); + + // Now, codegen: + for stmt_kind in extra_stmts.into_iter() { + let stmt = mir::Statement { + source_info: terminator.source_info, + kind: stmt_kind, + }; + bx = self.codegen_statement(bx, &stmt); + } + + // Lastly, jump to the target block: + funclet_br(self, bx, target); + return; + } + } + let extra_args = &args[sig.inputs().len()..]; let extra_args = extra_args.iter().map(|op_arg| { let op_ty = op_arg.ty(self.mir, bx.tcx()); diff --git a/src/librustc_data_structures/sync.rs b/src/librustc_data_structures/sync.rs index b82fe3ec60c3b..e3879c1372ddc 100644 --- a/src/librustc_data_structures/sync.rs +++ b/src/librustc_data_structures/sync.rs @@ -39,6 +39,7 @@ use std::fmt::Debug; use std::fmt::Formatter; use std::fmt; use std::ops::{Deref, DerefMut}; +use std::sync::{Arc, atomic::AtomicPtr, atomic, }; use owning_ref::{Erased, OwningRef}; pub fn serial_join(oper_a: A, oper_b: B) -> (RA, RB) @@ -751,3 +752,55 @@ impl DerefMut for OneThread { &mut self.inner } } + +/// Provides atomic mutability by replacing the value inside this `ArcCell`. +/// Similar to the `crossbeam` structure of the same name. +#[derive(Debug)] +pub struct ArcCell(AtomicPtr); +impl ArcCell { + pub fn new(v: Arc) -> Self { + ArcCell(AtomicPtr::new(Arc::into_raw(v) as *mut _)) + } + pub fn get(&self) -> Arc { + let ptr = self.0.load(atomic::Ordering::Acquire); + let arc = unsafe { Arc::from_raw(ptr as *const T) }; + let ret = arc.clone(); + // don't drop our copy: + ::std::mem::forget(arc); + ret + } + /// Update the value, returning the previous value. + pub fn set(&self, v: Arc) -> Arc { + let new = Arc::into_raw(v) as *mut _; + let mut expected = self.0.load(atomic::Ordering::Acquire); + loop { + match self.0.compare_exchange_weak(expected, new, + atomic::Ordering::SeqCst, + atomic::Ordering::Acquire) { + Ok(old) => { + return unsafe { Arc::from_raw(old as *const T) }; + }, + Err(v) => { + expected = v; + }, + } + } + } +} +impl Drop for ArcCell { + fn drop(&mut self) { + let ptr = self.0.load(atomic::Ordering::Acquire); + // drop our copy of the arc: + unsafe { Arc::from_raw(ptr as *const _) }; + } +} +impl Clone for ArcCell { + fn clone(&self) -> Self { + ArcCell::new(self.get()) + } +} +impl From> for ArcCell { + fn from(v: Arc) -> Self { + Self::new(v) + } +} diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 96e9616699d37..5fcc48f063ac4 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -50,7 +50,7 @@ use std::io::{self, Write}; use std::iter; use std::path::{Path, PathBuf}; use rustc_data_structures::sync::{self, Lrc, Lock}; -use std::sync::mpsc; +use std::sync::{mpsc, Arc, }; use syntax::{self, ast, attr, diagnostics, visit}; use syntax::ext::base::ExtCtxt; use syntax::fold::Folder; @@ -896,6 +896,7 @@ where lint_groups, llvm_passes, attributes, + intrinsics, .. } = registry; @@ -914,6 +915,7 @@ where *sess.plugin_llvm_passes.borrow_mut() = llvm_passes; *sess.plugin_attributes.borrow_mut() = attributes.clone(); + sess.plugin_intrinsics.set(Arc::new(intrinsics)); })?; // Lint plugins are registered; now we can process command line flags. diff --git a/src/librustc_plugin/registry.rs b/src/librustc_plugin/registry.rs index b1ab86674cf90..879aa658b5840 100644 --- a/src/librustc_plugin/registry.rs +++ b/src/librustc_plugin/registry.rs @@ -11,6 +11,7 @@ //! Used by plugin crates to tell `rustc` about the plugins they provide. use rustc::lint::{EarlyLintPassObject, LateLintPassObject, LintId, Lint}; +use rustc::mir::{PluginIntrinsics, PluginIntrinsicCodegen}; use rustc::session::Session; use syntax::ext::base::{SyntaxExtension, NamedSyntaxExtension, NormalTT, IdentTT}; @@ -61,6 +62,9 @@ pub struct Registry<'a> { #[doc(hidden)] pub attributes: Vec<(String, AttributeType)>, + #[doc(hidden)] + pub intrinsics: PluginIntrinsics, + whitelisted_custom_derives: Vec, } @@ -77,6 +81,7 @@ impl<'a> Registry<'a> { lint_groups: HashMap::new(), llvm_passes: vec![], attributes: vec![], + intrinsics: HashMap::new(), whitelisted_custom_derives: Vec::new(), } } @@ -95,6 +100,17 @@ impl<'a> Registry<'a> { self.args_hidden.as_ref().map(|v| &v[..]).unwrap_or(&[]) } + /// Register a plugin intrinsic. Ignored if `name` is a normal Rust intrinsic. + /// + /// When a function call to the named intrinsic is made, codegen (only LLVM, currently) + /// will replace the usual function call with the extra statements provided by the passed + /// trait object. It will then branch directly to the exit block. It is highly unsafe. Do not + /// use lightly. + pub fn register_intrinsic(&mut self, name: String, + codegen: Box) { + self.intrinsics.insert(name, codegen); + } + /// Register a syntax extension of any kind. /// /// This is the most general hook into `libsyntax`'s expansion behavior. diff --git a/src/librustc_typeck/check/intrinsic.rs b/src/librustc_typeck/check/intrinsic.rs index c93023edcea08..639d85f49d6d0 100644 --- a/src/librustc_typeck/check/intrinsic.rs +++ b/src/librustc_typeck/check/intrinsic.rs @@ -319,12 +319,20 @@ pub fn check_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } ref other => { - struct_span_err!(tcx.sess, it.span, E0093, + let plugin_intrinsics = tcx.sess.plugin_intrinsics.get(); + if let Some(plugin) = plugin_intrinsics.get(*other) { + (plugin.generic_parameter_count(tcx), + plugin.inputs(tcx), + plugin.output(tcx)) + } else { + struct_span_err!(tcx.sess, it.span, E0093, "unrecognized intrinsic function: `{}`", *other) .span_label(it.span, "unrecognized intrinsic") .emit(); - return; + return; + + } } }; (n_tps, inputs, output) diff --git a/src/test/compile-fail-fulldeps/auxiliary/plugin_intrinsic_codegen.rs b/src/test/compile-fail-fulldeps/auxiliary/plugin_intrinsic_codegen.rs new file mode 100644 index 0000000000000..ad435326779b3 --- /dev/null +++ b/src/test/compile-fail-fulldeps/auxiliary/plugin_intrinsic_codegen.rs @@ -0,0 +1,84 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// force-host + +#![feature(plugin_registrar, rustc_private)] +#![deny(plugin_as_library)] // should have no effect in a plugin crate + +extern crate rustc; +extern crate rustc_plugin; + + +use rustc::mir::*; +use rustc::ty::{Ty, TyCtxt, FnSig, subst::Substs, }; +use rustc_plugin::Registry; + +#[plugin_registrar] +pub fn plugin_registrar(reg: &mut Registry) { + let codegen = Box::new(GenericCountMismatch) as Box<_>; + reg.register_intrinsic("generic_count_mismatch".into(), codegen); + let codegen = Box::new(InputOutputMismatch) as Box<_>; + reg.register_intrinsic("type_mismatch".into(), codegen); +} + +struct GenericCountMismatch; +impl PluginIntrinsicCodegen for GenericCountMismatch { + fn codegen_simple_intrinsic<'a, 'tcx>(&self, + _tcx: TyCtxt<'a, 'tcx, 'tcx>, + _source_info: SourceInfo, + _sig: &FnSig<'tcx>, + _parent_mir: &Mir<'tcx>, + _parent_param_substs: &'tcx Substs<'tcx>, + _args: &Vec>, + _dest: Place<'tcx>, + _extra_stmts: &mut Vec>) + where 'tcx: 'a, + { + unreachable!() + } + + /// The number of generic parameters expected. + fn generic_parameter_count<'a, 'tcx>(&self, _tcx: TyCtxt<'a, 'tcx, 'tcx>) -> usize { 5 } + /// The types of the input args. + fn inputs<'a, 'tcx>(&self, _tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Vec> { vec![] } + /// The return type. + fn output<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Ty<'tcx> { + tcx.mk_nil() + } +} + +struct InputOutputMismatch; +impl PluginIntrinsicCodegen for InputOutputMismatch { + fn codegen_simple_intrinsic<'a, 'tcx>(&self, + _tcx: TyCtxt<'a, 'tcx, 'tcx>, + _source_info: SourceInfo, + _sig: &FnSig<'tcx>, + _parent_mir: &Mir<'tcx>, + _parent_param_substs: &'tcx Substs<'tcx>, + _args: &Vec>, + _dest: Place<'tcx>, + _extra_stmts: &mut Vec>) + where 'tcx: 'a, + { + unreachable!() + } + + /// The number of generic parameters expected. + fn generic_parameter_count<'a, 'tcx>(&self, _tcx: TyCtxt<'a, 'tcx, 'tcx>) -> usize { 0 } + /// The types of the input args. + fn inputs<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Vec> { + vec![tcx.types.u64] + } + /// The return type. + fn output<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Ty<'tcx> { + tcx.types.u64 + } +} diff --git a/src/test/compile-fail-fulldeps/plugin-intrinsic-arg.rs b/src/test/compile-fail-fulldeps/plugin-intrinsic-arg.rs new file mode 100644 index 0000000000000..4368f55c65e69 --- /dev/null +++ b/src/test/compile-fail-fulldeps/plugin-intrinsic-arg.rs @@ -0,0 +1,25 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:plugin_intrinsic_codegen.rs +// ignore-stage1 + +#![feature(plugin, intrinsics)] +#![plugin(plugin_intrinsic_codegen)] + +extern "rust-intrinsic" { + /// The plugin expects `arg1` to be `u64`. + fn type_mismatch(arg1: i64) -> u64; + //~^ ERROR intrinsic has wrong type +} + +fn main() { + unreachable!(); +} diff --git a/src/test/compile-fail-fulldeps/plugin-intrinsic-generic.rs b/src/test/compile-fail-fulldeps/plugin-intrinsic-generic.rs new file mode 100644 index 0000000000000..308a050dbc61c --- /dev/null +++ b/src/test/compile-fail-fulldeps/plugin-intrinsic-generic.rs @@ -0,0 +1,25 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:plugin_intrinsic_codegen.rs +// ignore-stage1 + +#![feature(plugin, intrinsics)] +#![plugin(plugin_intrinsic_codegen)] + +extern "rust-intrinsic" { + /// The plugin expects 5 generic params + fn generic_count_mismatch(); + //~^ ERROR intrinsic has wrong number of type parameters: found 1, expected 5 +} + +fn main() { + unreachable!(); +} diff --git a/src/test/compile-fail-fulldeps/plugin-intrinsic-ret.rs b/src/test/compile-fail-fulldeps/plugin-intrinsic-ret.rs new file mode 100644 index 0000000000000..9cfa1ab93c24e --- /dev/null +++ b/src/test/compile-fail-fulldeps/plugin-intrinsic-ret.rs @@ -0,0 +1,25 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:plugin_intrinsic_codegen.rs +// ignore-stage1 + +#![feature(plugin, intrinsics)] +#![plugin(plugin_intrinsic_codegen)] + +extern "rust-intrinsic" { + /// The plugin expects the return to be `u64`. + fn type_mismatch(arg1: u64) -> i64; + //~^ ERROR intrinsic has wrong type +} + +fn main() { + unreachable!(); +} diff --git a/src/test/run-pass-fulldeps/auxiliary/plugin_intrinsic_codegen.rs b/src/test/run-pass-fulldeps/auxiliary/plugin_intrinsic_codegen.rs new file mode 100644 index 0000000000000..794ead31040f2 --- /dev/null +++ b/src/test/run-pass-fulldeps/auxiliary/plugin_intrinsic_codegen.rs @@ -0,0 +1,73 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// force-host + +#![feature(plugin_registrar, rustc_private)] +#![deny(plugin_as_library)] // should have no effect in a plugin crate + +extern crate rustc; +extern crate rustc_plugin; + +use rustc::mir::*; +use rustc::ty::{Ty, TyCtxt, FnSig, Const, subst::Substs, ParamEnv, }; +use rustc_plugin::Registry; + +#[plugin_registrar] +pub fn plugin_registrar(reg: &mut Registry) { + let codegen = Box::new(GetSecretValueCodegen) as Box<_>; + reg.register_intrinsic("get_secret_value".into(), codegen); +} + +struct GetSecretValueCodegen; +impl PluginIntrinsicCodegen for GetSecretValueCodegen { + fn codegen_simple_intrinsic<'a, 'tcx>(&self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + source_info: SourceInfo, + _sig: &FnSig<'tcx>, + _parent_mir: &Mir<'tcx>, + _parent_param_substs: &'tcx Substs<'tcx>, + _args: &Vec>, + dest: Place<'tcx>, + extra_stmts: &mut Vec>) + where 'tcx: 'a, + { + // chosen by fair dice roll. + // guaranteed to be random. + const SECRET_VALUE: u64 = 4; + + let v = Const::from_bits(tcx, SECRET_VALUE as u128, + ParamEnv::empty().and(tcx.types.u64)); + let v = tcx.mk_const(*v); + let v = Literal::Value { + value: v, + }; + let v = Constant { + span: source_info.span, + ty: tcx.types.u64, + literal: v, + }; + let v = Box::new(v); + let v = Operand::Constant(v); + let ret = Rvalue::Use(v); + + let stmt = StatementKind::Assign(dest, ret); + extra_stmts.push(stmt); + } + + /// The number of generic parameters expected. + fn generic_parameter_count<'a, 'tcx>(&self, _tcx: TyCtxt<'a, 'tcx, 'tcx>) -> usize { 0 } + /// The types of the input args. + fn inputs<'a, 'tcx>(&self, _tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Vec> { vec![] } + /// The return type. + fn output<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Ty<'tcx> { + tcx.types.u64 + } +} diff --git a/src/test/run-pass-fulldeps/plugin-intrinsic.rs b/src/test/run-pass-fulldeps/plugin-intrinsic.rs new file mode 100644 index 0000000000000..a977bb0acf4ea --- /dev/null +++ b/src/test/run-pass-fulldeps/plugin-intrinsic.rs @@ -0,0 +1,25 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:plugin_intrinsic_codegen.rs +// ignore-stage1 + +#![feature(plugin, intrinsics)] +#![plugin(plugin_intrinsic_codegen)] + +extern "rust-intrinsic" { + /// Returns the secret value. + fn get_secret_value() -> u64; +} + +fn main() { + const SECRET_VALUE: u64 = 4; + assert_eq!(unsafe { get_secret_value() }, SECRET_VALUE); +}