From bfb32d6cb7b7b57213270efa2bc6ef8e0a6e63b9 Mon Sep 17 00:00:00 2001 From: "Nicolas B. Pierron" Date: Fri, 9 Feb 2018 23:25:33 +0000 Subject: [PATCH] Rebase HolyJIT on top of nightly 2018-02-19 without plugin. - Replace the dependency on a custom rustc with the plugin interface by a rustc driver. - Add Nix expression to wrap all commands under the proper build environment with the proper dependency. (set RUSTC_WRAPPER) - Create a rustc.sh script to either call rust or holyjit depending whether we are calling rustc for building the sources or building the examples/tests. - Rebase on top of Mir changes: - replace Local by Places. - handle cloning of promoted Mir. - Copy arguments at function entry. --- Cargo.toml | 13 +- bin/Cargo.toml | 7 + bin/src/main.rs | 224 ++++++++++++++++++++++++ default.nix | 11 ++ examples/brainfuck.rs | 11 +- lib/Cargo.toml | 4 +- lib/src/compile.rs | 132 ++++++++------- lib/src/lir.rs | 4 +- plugin/src/lib.rs | 105 ++++++------ plugin/src/trans.rs | 385 +++++++++++++++++++++++++++++------------- release.nix | 20 +++ rustc.sh | 38 +++++ 12 files changed, 716 insertions(+), 238 deletions(-) create mode 100644 bin/Cargo.toml create mode 100644 bin/src/main.rs create mode 100644 default.nix create mode 100644 release.nix create mode 100755 rustc.sh diff --git a/Cargo.toml b/Cargo.toml index 4d3580e..35c6273 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,17 +1,26 @@ [package] -name = "holyjit" +name = "holyjit_pkg" version = "0.0.0" authors = [ "Nicolas B. Pierron " ] [workspace] members = [ "lib", - "plugin" + "plugin", + "bin" ] [dependencies] holyjit_lib = { path = "./lib" } holyjit_plugin = { path = "./plugin" } +holyjit = { path = "./bin" } +# Before running any tests or examples, make sure to set the RUSTC_WRAPPER as +# follow: +# +# export RUSTC_WRAPPER=$PWD/rustc.sh +# +# This wrapper will choose whether to start holyjit as a compiler instead of +# starting the default rustc compiler. [[example]] name = "brainfuck" diff --git a/bin/Cargo.toml b/bin/Cargo.toml new file mode 100644 index 0000000..d26e474 --- /dev/null +++ b/bin/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "holyjit" +version = "0.0.0" +authors = ["Nicolas B. Pierron "] + +[dependencies] +holyjit_plugin = { path = "../plugin" } diff --git a/bin/src/main.rs b/bin/src/main.rs new file mode 100644 index 0000000..e090958 --- /dev/null +++ b/bin/src/main.rs @@ -0,0 +1,224 @@ +// error-pattern:yummy +#![feature(box_syntax)] +#![feature(rustc_private)] +#![feature(decl_macro)] + +//extern crate holyjit_plugin; +extern crate getopts; +extern crate rustc; +extern crate rustc_driver; +extern crate rustc_errors; +extern crate rustc_plugin; +extern crate rustc_trans_utils; +extern crate rustc_mir; +extern crate syntax; + +extern crate holyjit_plugin; + +use rustc_mir::transform::dump_mir; +use rustc::hir::def_id::DefId; +use rustc::mir::Mir; +use rustc::ty::TyCtxt; +use rustc::ty::maps::Providers; +use rustc_driver::{driver, Compilation, CompilerCalls, RustcDefaultCalls}; +use rustc_trans_utils::trans_crate::TransCrate; +use rustc::session::{config, Session}; +use rustc::session::config::{ErrorOutputType, Input}; +use std::path::PathBuf; +use syntax::ast; +use std::env; +use std::process::Command; +use rustc_mir::transform::{MirPass, MirSource}; + +struct HolyJitCompilerCalls { + default: RustcDefaultCalls, + + // Whether we should instrument or not the code with HolyJIT. + add_jit: bool, +} + +impl HolyJitCompilerCalls { + fn new(add_jit: bool) -> Self { + Self { + default: RustcDefaultCalls, + add_jit, + } + } +} + +impl<'a> CompilerCalls<'a> for HolyJitCompilerCalls { + fn early_callback( + &mut self, + matches: &getopts::Matches, + sopts: &config::Options, + cfg: &ast::CrateConfig, + descriptions: &rustc_errors::registry::Registry, + output: ErrorOutputType, + ) -> Compilation { + self.default + .early_callback(matches, sopts, cfg, descriptions, output) + } + + fn no_input( + &mut self, + matches: &getopts::Matches, + sopts: &config::Options, + cfg: &ast::CrateConfig, + odir: &Option, + ofile: &Option, + descriptions: &rustc_errors::registry::Registry, + ) -> Option<(Input, Option)> { + self.default + .no_input(matches, sopts, cfg, odir, ofile, descriptions) + } + + fn late_callback( + &mut self, + trans_crate: &TransCrate, + matches: &getopts::Matches, + sess: &Session, + crate_stores: &rustc::middle::cstore::CrateStore, + input: &Input, + odir: &Option, + ofile: &Option, + ) -> Compilation { + self.default + .late_callback(trans_crate, matches, sess, crate_stores, input, odir, ofile) + } + + fn build_controller(&mut self, sess: &Session, matches: &getopts::Matches) -> driver::CompileController<'a> { + let mut controller = self.default.build_controller(sess, matches); + + // Extend rustc default driver, with the extra pass provided by the + // holyjit driver. This extra pass is registered at the end of the + // optimization made on the Mir, such that we have a desugared version + // of Rust before it goes into the trans phase. + if self.add_jit { + // nbp-note: controller.provide_extern = ? + // https://github.com/rust-lang/rust/blob/1670a532dd769763f1d6ad9e5d624ec31361a098/src/librustc_driver/driver.rs#L1003 + + // https://github.com/rust-lang/rust/blob/1670a532dd769763f1d6ad9e5d624ec31361a098/src/librustc_driver/driver.rs#L328 + // + // controller.provide = box |providers| providers.optimized_mir = |tcx, def_id| { let mut mir = transform::optimized_mir(tcx, def_id).clone(); /* mutate mir */ tcx.alloc_mir(mir) }; + + + // instead of transform::optimized_mir... + // let mut p = ty::maps::Providers::default(); driver::default_provide(&mut p); (p.optimized_mir)(tcx, def_id) + let old_provide = std::mem::replace(&mut controller.provide, box |_| {}); + controller.provide = box (move |providers| { + old_provide(providers); + // Note: this erases the default one, but we re-create the + // default Provider within optimized_mir function in order to + // execute all other optimization passes. + *providers = Providers { + optimized_mir, + ..*providers + }; + }); + } + + controller + } +} + +pub macro run_passes($tcx:ident, $mir:ident, $def_id:ident, $suite_index:expr; $($pass:expr,)*) {{ + let suite_index: usize = $suite_index; + let run_passes = |mir: &mut _, promoted| { + let source = MirSource { + def_id: $def_id, + promoted + }; + let mut index = 0; + let mut run_pass = |pass: &MirPass| { + let run_hooks = |mir: &_, index, is_after| { + dump_mir::on_mir_pass($tcx, &format_args!("{:03}-{:03}", suite_index, index), + &pass.name(), source, mir, is_after); + }; + run_hooks(mir, index, false); + pass.run_pass($tcx, source, mir); + run_hooks(mir, index, true); + + index += 1; + }; + $(run_pass(&$pass);)* + }; + + run_passes(&mut $mir, None); + + for (index, promoted_mir) in $mir.promoted.iter_enumerated_mut() { + run_passes(promoted_mir, Some(index)); + + // Let's make sure we don't miss any nested instances + assert!(promoted_mir.promoted.is_empty()); + } +}} + +fn optimized_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx Mir<'tcx> { + let mut p = Providers::default(); + driver::default_provide(&mut p); + let optmir = p.optimized_mir; + let mut mir = optmir(tcx, def_id).clone(); + // TODO + run_passes![tcx, mir, def_id, 3; + holyjit_plugin::AttachFnGraphOnCst, + ]; + tcx.alloc_mir(mir) +} + +fn extract_sysroot() -> String { + // If we attempt to compile without a sysroot, then the core library would + // not be found which would prevent the compilation of some crates. This + // code is a work-around which either relies on the environment variables, + // or on the original rustc compiler to extra the sysroot path. + option_env!("SYSROOT") + .map(String::from) + .or_else(|| env::var("SYSROOT").ok()) + .or_else(|| { + let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME")); + let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN")); + home.and_then(|home| toolchain.map(|toolchain| format!("{}/toolchains/{}", home, toolchain))) + }) + .or_else(|| { + Command::new("rustc") + .arg("--print") + .arg("sysroot") + .output() + .ok() + .and_then(|out| String::from_utf8(out.stdout).ok()) + .map(|s| s.trim().to_owned()) + }) + .expect("need to specify SYSROOT env var during holyjit compilation, or use rustup or multirust") +} + +pub fn main() { + let mut args: Vec = env::args().collect(); + + + // Setting RUSTC_WRAPPER causes Cargo to pass 'rustc' as the first argument. + // We're invoking the compiler programmatically, so we ignore this. + if args.len() <= 1 { + std::process::exit(1); + } + if args[1] == "rustc" { + // we still want to be able to invoke it normally though + args.remove(1); + } + + let args = if args.iter().any(|s| s == "--sysroot") { + // User provides the --sysroot on the command line. + args + } else { + // HolyJit provide the --sysroot from the environment or extracted from + // rustc. + args.into_iter() + .chain(Some("--sysroot".to_owned())) + .chain(Some(extract_sysroot())) + .collect() + }; + + let holyjit_enabled = true; + let mut holyjit_cc = HolyJitCompilerCalls::new(holyjit_enabled); + rustc_driver::run(move || { + rustc_driver::run_compiler(&args, &mut holyjit_cc, None, None) + }); +} diff --git a/default.nix b/default.nix new file mode 100644 index 0000000..197395e --- /dev/null +++ b/default.nix @@ -0,0 +1,11 @@ +{ stdenv, rust }: + +stdenv.mkDerivation rec { + version = "0.0.0"; + name = "holyjit-${version}"; + buildInputs = [ rust ]; + shellHook = " + export RUSTC_WRAPPER=$PWD/rustc.sh + export RUST_BACKTRACE=1 + "; +} \ No newline at end of file diff --git a/examples/brainfuck.rs b/examples/brainfuck.rs index a035ac9..4865123 100644 --- a/examples/brainfuck.rs +++ b/examples/brainfuck.rs @@ -1,5 +1,4 @@ -#![feature(plugin, custom_attribute)] -#![plugin(holyjit_plugin)] +#![feature(custom_attribute)] #![feature(unboxed_closures)] #[macro_use] extern crate holyjit_lib as hj; @@ -65,7 +64,11 @@ fn eval_impl(_jc: hj::JitContext, program: String) -> Result<(), ()> { } fn main() { + let prog = "++"; + // Run without the Jit. let jc : hj::JitContext = Default::default(); - let res = eval(jc, "++".into()); - res.unwrap(); + eval_impl(jc, prog.into()).unwrap(); + // Run with the Jit. + let jc : hj::JitContext = Default::default(); + eval(jc, prog.into()).unwrap(); } diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 9ad4eef..7252906 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -6,8 +6,8 @@ authors = [ "Nicolas B. Pierron " ] # Lock dynasm and serde_derive in order to build against old versions of # rustc. [dependencies] -dynasm = "=0.1.1" -dynasmrt = "=0.1.1" +dynasm = "=0.1.3" +dynasmrt = "=0.1.3" serde = "1.0" serde_derive = "=1.0.12" bincode = "0.8" diff --git a/lib/src/compile.rs b/lib/src/compile.rs index c82b150..104ebb5 100644 --- a/lib/src/compile.rs +++ b/lib/src/compile.rs @@ -30,9 +30,7 @@ impl JitCode { /// Cast the current code into the proper fn-type given as parameter of /// this generic function. pub unsafe fn get_fn(&self) -> *const () { - unsafe { - mem::transmute(self.code.ptr(self.start)) - } + mem::transmute(self.code.ptr(self.start)) } } @@ -1256,70 +1254,23 @@ impl Compiler { return Err(Error::NYI("Reading arguments from the stack.")); }; - // Consume all the registers which are used for arguments. - for (&(r, sz), reg) in cu.args_defs.iter().zip(self::asm::ABI_ARGS.iter()) { - let reg = *reg; - if sz <= 8 { - self.register(r, vec![AllocInfo{ reg, off: 0, sz }]) - } else { - self.register(r, vec![AllocInfo{ reg, off: 0, sz: 8 }]) - } - self.take(reg); - } - - /* - // Generate code for loading arguments values into registers, if - // they are larger than a single register, then they are assumed to - // be pointers to the value. - for (&(r, sz), ar) in cu.args_defs.iter().zip(self::asm::ABI_ARGS.iter()) { - if sz > 8 { - let ar = *ar; - let mut off = 0; - let mut alloc = vec![]; - while off < sz { - match sz - off { - 1 => { - let reg = self.allocate(1)?; - self.asm.movb_mr(ar, off as i32, reg); - alloc.push(AllocInfo{ reg, sz: 1, off }); - off += 1; - } - 2 | 3 => { - let reg = self.allocate(2)?; - self.asm.movw_mr(ar, off as i32, reg); - alloc.push(AllocInfo{ reg, sz: 2, off }); - off += 2; - } - 4 | 5 | 6 | 7 => { - let reg = self.allocate(4)?; - self.asm.movl_mr(ar, off as i32, reg); - alloc.push(AllocInfo{ reg, sz: 4, off }); - off += 4; - } - _ => { - let reg = self.allocate(4)?; - self.asm.movq_mr(ar, off as i32, reg); - alloc.push(AllocInfo{ reg, sz: 8, off }); - off += 8; - } - } - } - self.unregister(r); - self.register(r, alloc); - } + // Consume all the registers which are by arguments. The arguments would + // be pushed on the stack as soon as we see SetFramePtr. + for (&_, reg) in cu.args_defs.iter().zip(self::asm::ABI_ARGS.iter()) { + // TODO: Hum ... do we have to support argument given over 2 registers here? + self.take(*reg); } - */ // Compile each basic block. for (id, block) in cu.blocks.iter().enumerate() { println!("Compile Block {}:", id); - self.compile_block(id, block)? + self.compile_block(id, block, &cu.args_defs)? } self.finalize() } - fn compile_block(&mut self, id: usize, block: &lir::BasicBlockData) -> Result<(), Error> { + fn compile_block(&mut self, id: usize, block: &lir::BasicBlockData, args_defs: &Vec) -> Result<(), Error> { let label = self.bb_labels[id]; self.asm.backend.dynamic_label(label); @@ -1353,7 +1304,7 @@ impl Compiler { for inst in block.insts.iter() { println!(" Compile Inst: {:?}", inst); - self.compile_inst(inst)? + self.compile_inst(inst, args_defs)? } self.compile_terminator(&block.end)?; @@ -1513,7 +1464,7 @@ impl Compiler { val_ptr.as_ref::<'static>().unwrap() } - fn compile_inst(&mut self, inst: &lir::Inst) -> Result<(), Error> { + fn compile_inst(&mut self, inst: &lir::Inst, args_defs: &Vec) -> Result<(), Error> { use lir::Inst::*; match inst { &SetFramePtr(fp, _sz, stack_size) => { @@ -1525,6 +1476,63 @@ impl Compiler { ; sub rsp, stack_size as _ ); self.register(fp, vec![AllocInfo{ reg: Register::Rbp, off: 0, sz: 8 }]); + + // Generate code which copy the arguments from their argument register + // to the stack, this might involve copying larger structures as well + // from the pointer if not given by value. + for (&(off, sz, by_value), ar) in args_defs.iter().zip(self::asm::ABI_ARGS.iter()) { + let off = 0 - (off as isize) - (sz as isize); + let ar = *ar; + println!(" Copy {:?} to offset {:?} with size {:?}", ar, off, sz); + if by_value { + // ar is a value which needs to be copied to the stack. + match sz { + 1 => self.asm.movb_rm(ar, Register::Rbp, off as i32), + 2 | 3 => self.asm.movw_rm(ar, Register::Rbp, off as i32), + 4 | 5 | 6 | 7 => + self.asm.movl_rm(ar, Register::Rbp, off as i32), + _ => self.asm.movq_rm(ar, Register::Rbp, off as i32), + } + } else { + // ar is a pointer to the data which need to be copied + // to the stack. We generate sequences of load and store + // instructions. + let mut arg_off = 0; + while arg_off < sz { + match sz - arg_off { + 1 => { + let tmp = self.allocate(1)?; + self.asm.movb_mr(ar, arg_off as i32, tmp); + self.asm.movb_rm(tmp, Register::Rbp, (arg_off as isize + off) as i32); + arg_off += 1; + self.free(tmp); + } + 2 | 3 => { + let tmp = self.allocate(2)?; + self.asm.movw_mr(ar, arg_off as i32, tmp); + self.asm.movw_rm(tmp, Register::Rbp, (arg_off as isize + off) as i32); + arg_off += 2; + self.free(tmp); + } + 4 | 5 | 6 | 7 => { + let tmp = self.allocate(4)?; + self.asm.movl_mr(ar, arg_off as i32, tmp); + self.asm.movl_rm(tmp, Register::Rbp, (arg_off as isize + off) as i32); + arg_off += 4; + self.free(tmp); + } + _ => { + let tmp = self.allocate(4)?; + self.asm.movq_mr(ar, arg_off as i32, tmp); + self.asm.movq_rm(tmp, Register::Rbp, (arg_off as isize + off) as i32); + arg_off += 8; + self.free(tmp); + } + } + } + } + self.free(ar); + } }, &Static(out, static_offset, static_size) => { // Load the content from the static pointer, and convert it @@ -2144,10 +2152,12 @@ impl Compiler { } fn finalize(self) -> Result { - match self.asm.backend.finalize() { + let res = match self.asm.backend.finalize() { // TODO: transmute and return a class which implements Fn types. Ok(buf) => Ok(JitCode { code: buf, start: self.start }), Err(_) => Err(Error::Finalize), - } + }; + println!("Compilation finished"); + res } } diff --git a/lib/src/lir.rs b/lib/src/lir.rs index 7956347..c687b74 100644 --- a/lib/src/lir.rs +++ b/lib/src/lir.rs @@ -11,7 +11,7 @@ pub struct CompilationUnit { pub stack_size: usize, /// Ordered list of arguments, with their associated registers. - pub args_defs: Vec, + pub args_defs: Vec, /// List of basic blocks of a given function. pub blocks: Vec, @@ -90,6 +90,8 @@ pub enum Terminator { pub type Sz = usize; pub type Reg = usize; pub type Imm = isize; +pub type ByValue = bool; +pub type ArgDef = (Imm, Sz, ByValue); pub type RegDef = (Reg, Sz); pub type RangeInclusive = (Imm, Imm); diff --git a/plugin/src/lib.rs b/plugin/src/lib.rs index 7ccb21c..13850a2 100644 --- a/plugin/src/lib.rs +++ b/plugin/src/lib.rs @@ -3,20 +3,20 @@ // extern crate syntax; extern crate rustc; +extern crate rustc_mir; +// extern crate rustc_typeck; extern crate rustc_data_structures; extern crate rustc_const_math; -extern crate rustc_plugin; +extern crate syntax_pos; -use rustc::mir::transform::{MirPass, MirSource}; +use rustc_mir::transform::{MirPass, MirSource}; use rustc::mir::{Mir, self}; // use rustc::mir::visit::MutVisitor; use rustc::ty::{self, TyCtxt}; use rustc::middle::const_val::ConstVal; use rustc_const_math::ConstInt; use rustc::hir::def_id::DefId; - -use rustc_plugin::Registry; -use std::rc::Rc; +use rustc::hir; // Used to express the Mir into the Lir used for the Jit compilation. extern crate holyjit_lib; @@ -93,8 +93,9 @@ impl<'a, 'tcx> AttachJitGraph<'a, 'tcx> { /// HolyJitFnWrapper. fn is_placeholder(&self, mir: &mut Mir<'tcx>) -> bool { // Filter out any mir which does not define a constant. - match self.source { - MirSource::Const(_) => (), + let id = self.tcx.hir.as_local_node_id(self.source.def_id).unwrap(); + match self.tcx.hir.body_owner_kind(id) { + hir::BodyOwnerKind ::Const => (), _ => return false }; @@ -103,12 +104,12 @@ impl<'a, 'tcx> AttachJitGraph<'a, 'tcx> { // jit! macro. // // mir.return_ty == "holyjit::HolyJitFnWrapper<...>" - match mir.return_ty.ty_adt_def() { + match mir.return_ty().ty_adt_def() { Some(adt) => { let wrpr = self.tcx.def_key(adt.did).disambiguated_data.data; - let wrpr = wrpr.get_opt_name().unwrap().as_str(); + let wrpr = wrpr.get_opt_name().unwrap(); // println!("CollectFunctionsIndex: {}", wrpr); - wrpr == "HolyJitFnWrapper" + "HolyJitFnWrapper" == wrpr }, None => false } @@ -143,7 +144,7 @@ impl<'a, 'tcx> AttachJitGraph<'a, 'tcx> { for statement in &data.statements { match *statement { mir::Statement { kind: mir::StatementKind::Assign( - mir::Lvalue::Local(assign_local), + mir::Place::Local(assign_local), ref rvalue ), ref source_info} if assign_local == fn_local => { @@ -167,7 +168,9 @@ impl<'a, 'tcx> AttachJitGraph<'a, 'tcx> { // Deconstruct the operand of the cast operator to find the DefId of // the function. let fn_id = match fn_ref.literal { - mir::Literal::Value { value: ConstVal::Function(did, _) } => did, + mir::Literal::Value { + value: &ty::Const { val: ConstVal::Function(did, _), .. } + } => did, mir::Literal::Value { .. } => return Err(Error::NoFnConstLiteral), _ => return Err(Error::NoLiteralOperand), }; @@ -181,7 +184,7 @@ impl<'a, 'tcx> AttachJitGraph<'a, 'tcx> { /// This function convert the Mir of the wrapped function into a vector /// of serialized graph, which would be deserialized by the Jit library. - fn serialize_mir(&self, fn_id: DefId, src_info: mir::SourceInfo) -> Result<(Vec, Vec>), Error> { + fn serialize_mir(&self, fn_id: DefId, src_info: mir::SourceInfo, nb_promoted: usize) -> Result<(Vec, Vec>, Vec>), Error> { let fn_mir = ty::queries::optimized_mir::try_get(self.tcx, src_info.span, fn_id); let fn_mir = match fn_mir { @@ -195,7 +198,7 @@ impl<'a, 'tcx> AttachJitGraph<'a, 'tcx> { _ => return Err(Error::FunctionIsNotAvailable), }; - let trans = trans::Transpiler::new(self.tcx, fn_id); + let trans = trans::Transpiler::new(self.tcx, fn_id, nb_promoted); Ok(trans.convert(fn_mir)?) } @@ -219,12 +222,14 @@ impl<'a, 'tcx> AttachJitGraph<'a, 'tcx> { ty::TypeVariants::TyRef(ref region, ref tam) => { match tam.ty.sty { ty::TypeVariants::TyArray(t, l) if t == self.tcx.types.u8 => { - if l != 1 { + if l.val.to_const_int().unwrap().to_u64().unwrap() != 1 { return Err(Error::UnexpectedArrayLen) } - let arr_ty = ty::TypeVariants::TyArray(t, bytes.len()); - let ref_ty = ty::TypeVariants::TyRef(*region, ty::TypeAndMut{ - ty: self.tcx.mk_ty(arr_ty), ..*tam + // TyArray(t, ..) + let arr_ty = self.tcx.mk_array(t, bytes.len() as u64); + let ref_ty = self.tcx.mk_ref(*region, ty::TypeAndMut{ + ty: arr_ty, + ..*tam }); Some(ref_ty) } @@ -232,21 +237,21 @@ impl<'a, 'tcx> AttachJitGraph<'a, 'tcx> { } }, ty::TypeVariants::TyArray(t, l) if t == self.tcx.types.u8 => { - if l != 1 { + if l.val.to_const_int().unwrap().to_u64().unwrap() != 1 { return Err(Error::UnexpectedArrayLen) } if arr_local != None { return Err(Error::MultipleArrayDefinitions) } arr_local = Some(idx); - let arr_ty = ty::TypeVariants::TyArray(t, bytes.len()); + let arr_ty = self.tcx.mk_array(t, bytes.len() as u64); Some(arr_ty) }, _ => None, }; if let Some(arr_ty) = arr_ty { - mir.local_decls[idx].ty = self.tcx.mk_ty(arr_ty); + mir.local_decls[idx].ty = arr_ty; } } @@ -256,7 +261,12 @@ impl<'a, 'tcx> AttachJitGraph<'a, 'tcx> { }; // Create operands for each element of the vector. - let mk_literal = |i| mir::Literal::Value{ value: ConstVal::Integral(ConstInt::U8(i)) }; + let mk_literal = |i| mir::Literal::Value{ + value: self.tcx.mk_const(ty::Const { + val: ConstVal::Integral(ConstInt::U8(i)), + ty: self.tcx.types.u8 + }) + }; let mk_operand = |cst: &mir::Constant<'tcx>, i| mir::Operand::Constant(Box::new( mir::Constant{ literal: mk_literal(i), ..cst.clone() })); @@ -269,7 +279,7 @@ impl<'a, 'tcx> AttachJitGraph<'a, 'tcx> { let cst = { let op = match *statement { mir::Statement { kind: mir::StatementKind::Assign( - mir::Lvalue::Local(assign_local), + mir::Place::Local(assign_local), mir::Rvalue::Aggregate(_, ref op) ), ..} if assign_local == arr_local && op.len() == 1 => { @@ -285,7 +295,7 @@ impl<'a, 'tcx> AttachJitGraph<'a, 'tcx> { }; statement.kind = mir::StatementKind::Assign( - mir::Lvalue::Local(arr_local), + mir::Place::Local(arr_local), mir::Rvalue::Aggregate(Box::new(mir::AggregateKind::Array(self.tcx.types.u8)), bytes.into_iter().map(|i| mk_operand(&cst, i)).collect()) ); @@ -311,14 +321,20 @@ impl<'a, 'tcx> AttachJitGraph<'a, 'tcx> { /// of the referenced content. /// /// All rvalues are supposed to be static: - /// Rvalue::Ref(region, mut, Lvalue::Static(box Static{ def_id, ty })) - fn replace_defs(&self, mir: &mut Mir<'tcx>, statics: Vec>) -> Result<(), Error> { + /// Rvalue::Ref(region, mut, Place::Static(box Static{ def_id, ty })) + fn replace_defs(&self, mir: &mut Mir<'tcx>, statics: Vec>, promoted: Vec>) -> Result<(), Error> { + // Addcopied promoted Mir, copied from the function body, and references + // by the rvalue defs. + for m in promoted.into_iter() { + mir.promoted.push(m); + } + // Create references to the extracted static definitions' types. let tys : Vec<_> = statics.iter() // TODO: We need to create a reference to the statics types. .map(|rv| match rv { - &mir::Rvalue::Ref(ref region, _, mir::Lvalue::Static(ref st)) => + &mir::Rvalue::Ref(ref region, _, mir::Place::Static(ref st)) => // region == tcx.types.re_erased self.tcx.mk_imm_ref(region.clone(), st.ty), &mir::Rvalue::Use(mir::Operand::Constant(ref constant)) => @@ -424,7 +440,7 @@ impl<'a, 'tcx> AttachJitGraph<'a, 'tcx> { match *statement { // replace the constant tuple. mir::Statement { kind: mir::StatementKind::Assign( - mir::Lvalue::Local(assign_local), + mir::Place::Local(assign_local), mir::Rvalue::Aggregate(_, _) ), ..} if assign_local == tup_local => { @@ -432,13 +448,13 @@ impl<'a, 'tcx> AttachJitGraph<'a, 'tcx> { }, // Replace the cast type. mir::Statement { kind: mir::StatementKind::Assign( - mir::Lvalue::Local(assign_local), + mir::Place::Local(assign_local), mir::Rvalue::Cast(ref ck, ref op, _) ), ..} if coerce_locals.contains(&assign_local) => { cast_idx = Some(st_idx); cast_rep = Some(mir::StatementKind::Assign( - mir::Lvalue::Local(assign_local), + mir::Place::Local(assign_local), mir::Rvalue::Cast(ck.clone(), op.clone(), coerce_ty.unwrap()) )); }, @@ -463,18 +479,18 @@ impl<'a, 'tcx> AttachJitGraph<'a, 'tcx> { .collect(); // Create a list of local operands of the tuple. + // We move the operands computed from the locals into the array. let local_ops : Vec<_> = local_ids.iter() - .map(|idx| mir::Operand::Consume(mir::Lvalue::Local(idx.clone()))) + .map(|idx| mir::Operand::Move(mir::Place::Local(idx.clone()))) .collect(); + // Create locals and initialize them. let mut stmt_before : Vec<_> = local_ids.iter().zip(statics.iter()) .fold(vec![],|mut stmts, (idx, rv)| { stmts.push(mir::Statement { - kind: mir::StatementKind::StorageLive( - mir::Lvalue::Local(idx.clone()) - ), + kind: mir::StatementKind::StorageLive(idx.clone()), source_info: mir::SourceInfo { span: Default::default(), scope: mir::ARGUMENT_VISIBILITY_SCOPE, @@ -482,7 +498,7 @@ impl<'a, 'tcx> AttachJitGraph<'a, 'tcx> { }); stmts.push(mir::Statement { kind: mir::StatementKind::Assign( - mir::Lvalue::Local(idx.clone()), + mir::Place::Local(idx.clone()), rv.clone() ), source_info: mir::SourceInfo { @@ -493,12 +509,11 @@ impl<'a, 'tcx> AttachJitGraph<'a, 'tcx> { stmts }); + // Remove the storage associate to each local. let mut stmt_after : Vec<_> = local_ids.iter().fold(vec![], |mut stmts, idx| { stmts.push(mir::Statement { - kind: mir::StatementKind::StorageDead( - mir::Lvalue::Local(idx.clone()) - ), + kind: mir::StatementKind::StorageDead(idx.clone()), source_info: mir::SourceInfo { span: Default::default(), scope: mir::ARGUMENT_VISIBILITY_SCOPE, @@ -518,7 +533,7 @@ impl<'a, 'tcx> AttachJitGraph<'a, 'tcx> { let data = &mut mir[insert_block]; data.statements[tuple_idx].kind = mir::StatementKind::Assign( - mir::Lvalue::Local(tup_local), + mir::Place::Local(tup_local), mir::Rvalue::Aggregate(Box::new(mir::AggregateKind::Tuple), local_ops) ); @@ -552,7 +567,8 @@ impl<'a, 'tcx> AttachJitGraph<'a, 'tcx> { // Step 3: Convert the Mir of function into HolyJit representation // and serialize it. - let (bytes, defs) = self.serialize_mir(fn_id, src_info).unwrap_or_else(|e| + let nb_promoted = mir.promoted.len(); + let (bytes, defs, promoted) = self.serialize_mir(fn_id, src_info, nb_promoted).unwrap_or_else(|e| panic!("Fail to convert the wrapped function: {:?}", e), ); @@ -561,7 +577,7 @@ impl<'a, 'tcx> AttachJitGraph<'a, 'tcx> { self.replace_bytes(mir, bytes).unwrap_or_else(|e| panic!("Fail to attach the serialized function: {:?}", e), ); - self.replace_defs(mir, defs).unwrap_or_else(|e| + self.replace_defs(mir, defs, promoted).unwrap_or_else(|e| panic!("Fail to attach static references: {:?}", e), ); } @@ -569,7 +585,7 @@ impl<'a, 'tcx> AttachJitGraph<'a, 'tcx> { /// This structure is registered as a MirPass and is used to deletage to /// AttachJitGraph implementation. -struct AttachFnGraphOnCst; +pub struct AttachFnGraphOnCst; impl MirPass for AttachFnGraphOnCst { fn run_pass<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource, mir: &mut Mir<'tcx>) @@ -582,8 +598,3 @@ impl MirPass for AttachFnGraphOnCst { attach_jit.run_pass(mir); } } - -#[plugin_registrar] -pub fn plugin_registrar(reg: &mut Registry) { - reg.register_opt_mir_pass(Rc::new(AttachFnGraphOnCst)); -} diff --git a/plugin/src/trans.rs b/plugin/src/trans.rs index 0edfcba..fadaf07 100644 --- a/plugin/src/trans.rs +++ b/plugin/src/trans.rs @@ -13,6 +13,8 @@ use rustc::middle::lang_items::DropInPlaceFnLangItem; use rustc_const_math::ConstUsize; use rustc_data_structures::indexed_vec::Idx; +use syntax_pos::DUMMY_SP; + use holyjit_lib::lir; use bincode; @@ -47,8 +49,16 @@ pub struct Transpiler<'a, 'tcx: 'a> { // represented by a number. This number is used for allocating new // registers in the MIR. last_reg: lir::Reg, + + /// Map indexes of the function promoted MIR, to indexes of the of the + /// contant promoted MIR. + promoted_map: HashMap, + // Number of promoted blocks which are already listed in the constant MIR + // before attaching any new promoted MIR ot it. + promoted_idx: usize, } +#[derive(Debug)] struct Local<'tcx> { _idx: mir::Local, ty: ty::Ty<'tcx>, @@ -59,7 +69,7 @@ struct Local<'tcx> { /// Determine if the address or the value should be returned to the caller. #[derive(Debug, Copy, Clone)] -enum LvalueCtx { +enum PlaceCtx { Value, Address, RefSlice, @@ -70,6 +80,11 @@ pub enum Error { UnknownType, TooLargeType, + // Validate statement is used by Miri to ensure that the content of a given + // place contains data which correspond to the type assumption. Maybe we + // could later generate proper assertions as well. + ValidateStmt, + /// Inline Assembly code is not supported for the moment, and probably /// would not be in the future versions of HolyJit. InlineAssembly, @@ -77,14 +92,10 @@ pub enum Error { /// Serialization error while converting the Lir into a vector of bytes. Serialize, - /// LvalueCtx::RefSlice is used to indicate that a Deref projection is + /// PlaceCtx::RefSlice is used to indicate that a Deref projection is /// not supposed to do anything because it would depend on the consumer. UnexpectedRefSliceCtx, - /// LvalueCtx::RefSlice is used to indicate that a Deref projection is - /// not supposed to do anything because it would depend on the consumer. - UnexpectedIndexBase, - /// Not Yet Implemented. NYI, } @@ -116,8 +127,9 @@ impl<'tcx> From> for Error { } } +// TODO: Have a look at Rust's src/librustc_trans/context.rs , and how CodegenCx is built. impl<'a, 'tcx> Transpiler<'a, 'tcx> { - pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, fn_id: DefId) -> Self { + pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, fn_id: DefId, nb_promoted: usize) -> Self { Self { tcx: tcx, param_env: tcx.param_env(fn_id), @@ -130,10 +142,39 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { statics: vec![], statics_size: 0, last_reg: 1, + promoted_map: HashMap::new(), + promoted_idx: nb_promoted, } } } +/* +impl<'a, 'tcx> ty::layout::HasDataLayout for &'a Transpiler<'a, 'tcx> { + fn data_layout(&self) -> &ty::layout::TargetDataLayout { + &self.tcx.data_layout + } +} + +impl<'a, 'tcx> ty::layout::HasTyCtxt<'tcx> for &'a Transpiler<'a, 'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'b, 'tcx, 'tcx> { + self.tcx + } +} +*/ +/* +impl<'a, 'tcx> LayoutOf> for &'a Transpiler<'a, 'tcx> { + type TyLayout = TyLayout<'tcx>; + + fn layout_of(self, ty: Ty<'tcx>) -> Self::TyLayout { + self.tcx.layout_of(ty::ParamEnv::empty(traits::Reveal::All).and(ty)) + .unwrap_or_else(|e| match e { + LayoutError::SizeOverflow(_) => panic!("LayoutError::SizeOverflow"), + _ => panic!("failed to get layout for `{}`: {}", ty, e) + }) + } +} +*/ + /// This type correspons to the sequences of instructions build by the /// statements content. Each have a return type and a return register. #[derive(Debug)] @@ -141,9 +182,12 @@ struct InstSeq<'tcx>(Vec, lir::Reg, ty::Ty<'tcx>); struct TermSeq(Vec, lir::Terminator); impl<'a, 'tcx> Transpiler<'a, 'tcx> { - pub fn convert(mut self, mir: &mir::Mir<'tcx>) -> Result<(Vec, Vec>), Error> { + // This function output a vector of bytes which corresponds to the LIR, a + // vector of rvalue which corresponds to all statics & promoted indexes, and + // a vector of promoted blocks. + pub fn convert(mut self, mir: &mir::Mir<'tcx>) -> Result<(Vec, Vec>, Vec>), Error> { let (args_defs, stack_size) = self.locals(mir)?; - self.graph(mir, stack_size, &args_defs)?; + self.graph(mir, stack_size)?; let blocks = mem::replace(&mut self.blocks, Vec::new()); @@ -155,9 +199,18 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { println!("lir:\n{}\n", lir); let bytes : Vec = bincode::serialize(&lir, bincode::Infinite)?; + // Copy promoted block to the constant, such that we can reference the + // same value (which are not supposed to be mutable.) + let mut promoted: Vec<_> = self.promoted_map.into_iter().map(|(tgt_idx, src_idx)| + (tgt_idx, mir.promoted[src_idx].clone()) + ).collect(); + promoted.sort_by(|a, b| a.0.cmp(&b.0)); + let promoted : Vec<_> = promoted.into_iter().map(|a| a.1).collect(); + Ok(( bytes, - self.statics + self.statics, + promoted )) } @@ -167,7 +220,7 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { } */ - fn locals(&mut self, mir: &mir::Mir<'tcx>) -> Result<(Vec, usize), Error> { + fn locals(&mut self, mir: &mir::Mir<'tcx>) -> Result<(Vec, usize), Error> { // Loop over arguments and locals to find out about their sizes // (librustc/ty/layout.rs), and where they should be allocated. // @@ -178,12 +231,11 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { // see librustc_mir/transform/inline.rs (type_size_of) // let mut stack : usize = 0; - let args_size : usize = 0; // TODO: Cumulate the stack size. - let mut args_defs : Vec = Vec::with_capacity(mir.arg_count); + let mut args_defs : Vec = Vec::with_capacity(mir.arg_count); for (local, decl) in mir.local_decls.iter_enumerated() { - let layout = decl.ty.layout(self.tcx, self.param_env)?; - let size = layout.size(&self.tcx.data_layout).bytes() as usize; - let align = layout.align(&self.tcx.data_layout).pref() as usize; + let layout = self.tcx.layout_of(self.param_env.and(decl.ty))?; // decl.ty.layout(self.tcx, self.param_env)?; + let size = layout.size.bytes() as usize; + let align = layout.align.pref() as usize; // (&self.tcx.data_layout) let local_kind = mir.local_kind(local); println!("local {:?} : {} =>: size: {} ; align: {:?} ; kind: {:?}", local, mir.local_decls[local].ty, size, align, local_kind); @@ -199,25 +251,20 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { match local_kind { mir::LocalKind::Arg => { // Bump the stack pointer with the size of the local. - let (off, args_size) = self.add_type(decl.ty, args_size)?; - let reg = self.get_new_reg(); - let size = args_size - off; - - // TODO: use proper pointer size. - if size > 8 { - // Arguments are pass by pointer, if larger than - // a pointer size. (unless it is a fat_pointer, - // or is this only for returned values?) - args_defs.push((reg, 8)); - } else { - args_defs.push((reg, size)); - } + let (off, bump) = self.add_type(decl.ty, stack)?; + stack = bump; + let size = bump - off; + + // Add the size of the content which is being copied, + // the JIT-compiler will have to infer if this is a + // pointer type or the content of it :/ + args_defs.push((off as lir::Imm, size, (size <= 8) as lir::ByValue)); Local { _idx: local, ty: decl.ty, off, size, - reg: Some(reg), + reg: None, } }, mir::LocalKind::ReturnPointer | @@ -267,7 +314,7 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { lir } - fn graph(&mut self, mir: &mir::Mir<'tcx>, stack_size: usize, args_defs: &Vec) -> Result<(), Error> { + fn graph(&mut self, mir: &mir::Mir<'tcx>, stack_size: usize) -> Result<(), Error> { for (bb, bb_data) in mir.basic_blocks().iter_enumerated() { let lbb = self.get_block(bb); let mut lblock_insts : Vec = vec![]; @@ -280,7 +327,7 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { // the stack, and these arguments should only be used once // in the entire graph, and marked as dead after the first // assignment. - args_defs.clone() + vec![] } else { vec![ (self.fp, 8 /* size of frame ptr */ ) ] }; @@ -288,7 +335,11 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { // Append the instructions needed for the execution of every // staements. for statement in &bb_data.statements { - let mut insts = self.statement(statement)?; + let mut insts = match self.statement(statement) { + Ok(res) => res, + Err(Error::ValidateStmt) => continue, + e => { e?; panic!("The impossible happened") } + }; lblock_insts.append(&mut insts); }; @@ -313,6 +364,10 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { fn statement(&mut self, statement: &mir::Statement<'tcx>) -> Result, Error> { match statement.kind { + mir::StatementKind::Validate(_, _) => { + // Used by Miri as an assertion mechanism. + Err(Error::ValidateStmt) + } mir::StatementKind::Assign(ref lvalue, ref rvalue) => { // Collect the sequence of instruction to fetch the value. let InstSeq(rv_insts, rv_reg, rv_ty) = @@ -322,10 +377,10 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { // Collect the sequence of instruction which fetch the // memory location. let InstSeq(mut lv_insts, lv_reg, lv_ty) = - self.lvalue(lvalue, LvalueCtx::Address).or_else( + self.lvalue(lvalue, PlaceCtx::Address).or_else( report_nyi!("StatementKind::Assign({:?}, _)", lvalue))?; - let lv_ty = lv_ty.builtin_deref(true, ty::LvaluePreference::NoPreference).unwrap().ty; + let lv_ty = lv_ty.builtin_deref(true).unwrap().ty; let (_, lv_size) = self.add_type(lv_ty, 0)?; let (_, rv_size) = self.add_type(rv_ty, 0)?; @@ -364,6 +419,7 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { } fn reg_of_local(&self, index: mir::Local) -> Option { + // println!("reg_of_local({:?}) == {:?}", index, self.locals_map[&index]); self.locals_map[&index].reg } fn type_of_local(&self, index: mir::Local) -> ty::Ty<'tcx> { @@ -377,8 +433,8 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { } fn offset_of(&self, ty: ty::Ty<'tcx>, field: &mir::Field) -> Result { - let layout = ty.layout(self.tcx, self.param_env)?; - let off = layout.field_offset(&self.tcx.data_layout, field.index(), None).bytes(); + let layout = self.tcx.layout_of(self.param_env.and(ty))?; + let off = layout.fields.offset(field.index()).bytes(); println!("offset_of({:?}, {:?}) -> offset: {:?} {{ layout: {:?} }}", ty, field, off, layout); Ok(off as usize) } @@ -388,9 +444,9 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { /// contains the offset of the given type, and the new bump-offset for /// futher allocations. fn add_type(&self, ty: ty::Ty<'tcx>, bump: usize) -> Result<(usize, usize), Error> { - let layout = ty.layout(self.tcx, self.param_env)?; - let size = layout.size(&self.tcx.data_layout).bytes() as usize; - let align = layout.align(&self.tcx.data_layout).pref() as usize; + let layout = self.tcx.layout_of(self.param_env.and(ty))?; + let size = layout.size.bytes() as usize; + let align = layout.align.pref() as usize; // align the bump offset. let miss_align = bump & (align - 1); @@ -416,15 +472,61 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { let bump = self.statics_size; let (off, bump) = self.add_type(ty, bump)?; self.statics_size = bump; - let layout = ty.layout(self.tcx, self.param_env)?; + let layout = self.tcx.layout_of(self.param_env.and(ty))?; println!("register_static:\n: {:?}\n: {:?}\n: {:?}\n: {:?}", rvalue, ty, bump - off, layout); Ok((off as lir::Imm, bump - off as lir::Sz)) } - fn lvalue<'b>(&'b mut self, lvalue: &'b mir::Lvalue<'tcx>, lvctx: LvalueCtx) -> Result, Error> + fn register_promoted(&mut self, promoted: mir::Promoted) -> mir::Promoted { + // This function does not clone the promoted MIR, but only keep the + // index of which MIR needs to be cloned, such that when we reach back + // the top of the function MIR, we can clone all the necessary promoted + // MIR. + let idx = mir::Promoted::new(self.promoted_idx); + // (assume no duplicated?) + self.promoted_map.insert(idx, promoted); + self.promoted_idx += 1; + idx + } + + fn local(&mut self, index: mir::Local) -> Result, Error> + { + if let Some(reg) = self.reg_of_local(index) { + let ty = self.type_of_local(index); + let ptr_size = 8; // TODO: use proper pointer size. + if self.size_of_local(index) > ptr_size { + let val = self.get_new_reg(); + return Ok(InstSeq(vec![ + lir::Inst::Live(val), + lir::Inst::Load(val, reg, self.size_of_local(index) as lir::Sz), + lir::Inst::Dead(reg), + ], val, ty)) + } + return Ok(InstSeq(vec![], reg, ty)); + } + let off = self.get_new_reg(); + let ptr = self.get_new_reg(); + let reg = self.get_new_reg(); + Ok(InstSeq(vec![ + lir::Inst::Live(off), + // Compute the offset within the stack frame. + lir::Inst::CopyImm(off, self.offset_of_local(index) as lir::Imm, + 8 as lir::Sz /* size of pointer */), + lir::Inst::Live(ptr), + // Add the offset to the stack frame pointer. + lir::Inst::Add(ptr, self.fp, off), + lir::Inst::Dead(off), + lir::Inst::Live(reg), + // Add the offset to the stack frame pointer. + lir::Inst::Load(reg, ptr, self.size_of_local(index) as lir::Sz), + lir::Inst::Dead(ptr), + ], reg, self.type_of_local(index))) + } + + fn lvalue<'b>(&'b mut self, lvalue: &'b mir::Place<'tcx>, lvctx: PlaceCtx) -> Result, Error> { match (lvalue, lvctx) { - (&mir::Lvalue::Local(index), LvalueCtx::Address) => { + (&mir::Place::Local(index), PlaceCtx::Address) => { if let Some(_reg) = self.reg_of_local(index) { return Err(Error::NYI); } @@ -445,7 +547,7 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { lir::Inst::Dead(off), ], reg, ty)) }, - (&mir::Lvalue::Local(index), LvalueCtx::Value) => { + (&mir::Place::Local(index), PlaceCtx::Value) => { if let Some(reg) = self.reg_of_local(index) { let ty = self.type_of_local(index); let ptr_size = 8; // TODO: use proper pointer size. @@ -477,42 +579,42 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { lir::Inst::Dead(ptr), ], reg, self.type_of_local(index))) }, - (&mir::Lvalue::Static(ref def), _) => { - // Lvalue::Static seems to be always wrapped under a + (&mir::Place::Static(ref def), _) => { + // Place::Static seems to be always wrapped under a // Rvalue::Ref, so forbid any lvalue of this type. - println!("Lvalue::Static(def_id: {:?}, ty: {:?}) within {:?}", def.def_id, def.ty, lvctx); - unreachable!("Unexpected Lvalue::Static") + println!("Place::Static(def_id: {:?}, ty: {:?}) within {:?}", def.def_id, def.ty, lvctx); + unreachable!("Unexpected Place::Static") } - (&mir::Lvalue::Projection(ref proj), ref lvctx) => { + (&mir::Place::Projection(ref proj), ref lvctx) => { self.lvalue_projection(&proj, *lvctx).or_else( - report_nyi!("mir::Lvalue::Projection(({:?})", proj)) + report_nyi!("mir::Place::Projection(({:?})", proj)) } - (_, LvalueCtx::RefSlice) => Err(Error::UnexpectedRefSliceCtx), + (_, PlaceCtx::RefSlice) => Err(Error::UnexpectedRefSliceCtx), } } - fn lvalue_projection<'b>(&'b mut self, proj: &'b mir::LvalueProjection<'tcx>, lvctx: LvalueCtx) -> Result, Error> + fn lvalue_projection<'b>(&'b mut self, proj: &'b mir::PlaceProjection<'tcx>, lvctx: PlaceCtx) -> Result, Error> { - // Projection<'tcx, Lvalue<'tcx>, Operand<'tcx>, Ty<'tcx>> + // Projection<'tcx, Place<'tcx>, Operand<'tcx>, Ty<'tcx>> let mir::Projection { ref base, ref elem } = *proj; let InstSeq(mut base_insts, base_reg, base_ty) = match *elem { mir::ProjectionElem::Index(_) => - self.lvalue(base, LvalueCtx::RefSlice)?, + self.lvalue(base, PlaceCtx::RefSlice)?, mir::ProjectionElem::Field(_, _) => - self.lvalue(base, LvalueCtx::Address)?, - _ => self.lvalue(base, LvalueCtx::Value)? + self.lvalue(base, PlaceCtx::Address)?, + _ => self.lvalue(base, PlaceCtx::Value)? }; match *elem { mir::ProjectionElem::Deref => { let InstSeq(addr_insts, addr_reg, addr_ty) = InstSeq(base_insts, base_reg, base_ty); match lvctx { - LvalueCtx::Value => { + PlaceCtx::Value => { let mut insts = addr_insts; let reg = self.get_new_reg(); - let res_ty = addr_ty.builtin_deref(true, ty::LvaluePreference::NoPreference).unwrap().ty; + let res_ty = addr_ty.builtin_deref(true).unwrap().ty; let (_, size) = self.add_type(res_ty, 0)?; insts.append(&mut vec![ lir::Inst::Live(reg), @@ -521,23 +623,23 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { ]); Ok(InstSeq(insts, reg, res_ty)) } - LvalueCtx::RefSlice => { + PlaceCtx::RefSlice => { // We are asking to dereference a fat-pointer, but // we will either request the pointer of it or the // length of it. assert!(addr_ty.is_slice()); Ok(InstSeq(addr_insts, addr_reg, addr_ty)) } - LvalueCtx::Address => { + PlaceCtx::Address => { Ok(InstSeq(addr_insts, addr_reg, addr_ty)) } } } mir::ProjectionElem::Field(ref field, ref ty) => { // Given a type, generate a field access for this type. - let struct_ty = base_ty.builtin_deref(true, ty::LvaluePreference::NoPreference).unwrap().ty; + let struct_ty = base_ty.builtin_deref(true).unwrap().ty; match lvctx { - LvalueCtx::Value => { + PlaceCtx::Value => { let mut insts = base_insts; let imm = self.get_new_reg(); let addr = self.get_new_reg(); @@ -560,7 +662,7 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { ]); Ok(InstSeq(insts, reg, ty)) } - LvalueCtx::Address => { + PlaceCtx::Address => { let mut insts = base_insts; let imm = self.get_new_reg(); let reg = self.get_new_reg(); @@ -580,26 +682,26 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { ]); Ok(InstSeq(insts, reg, res_ty)) }, - LvalueCtx::RefSlice => Err(Error::UnexpectedRefSliceCtx), + PlaceCtx::RefSlice => Err(Error::UnexpectedRefSliceCtx), } } - mir::ProjectionElem::Index(ref operand) => { + mir::ProjectionElem::Index(ref index) => { // base [ operand ] let InstSeq(idx_insts, idx_reg, _idx_ty) = - self.operand(operand).or_else( - report_nyi!("mir::ProjectionElem::Index({:?})", operand))?; + self.local(*index).or_else( + report_nyi!("mir::ProjectionElem::Index({:?})", index))?; - let (InstSeq(mut base_insts, base_reg, base_ty), layout) = { + let InstSeq(mut base_insts, base_reg, base_ty) = { // Assert that base is FatPointer given by value. Note // we use the RefSlice type, to skip the Defer operation // made on the lvalue. assert!(base_ty.is_slice()); // TODO: add TyArray support? - let slice_ty = base_ty.builtin_deref(true, ty::LvaluePreference::NoPreference).unwrap().ty; + let slice_ty = base_ty.builtin_deref(true).unwrap().ty; let elem_ty = slice_ty.builtin_index().unwrap(); let res_ty = self.tcx.mk_mut_ptr(elem_ty); - let layout = base_ty.layout(self.tcx, self.param_env)?; - let offset = layout.field_offset(&self.tcx.data_layout, 0, None); + let layout = self.tcx.layout_of(self.param_env.and(base_ty))?; + let offset = layout.fields.offset(ty::layout::FAT_PTR_ADDR); let (_, size) = self.add_type(res_ty, 0)?; let reg = self.get_new_reg(); @@ -609,26 +711,19 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { lir::Inst::Dead(base_reg), ]); - (InstSeq(base_insts, reg, res_ty), slice_ty.layout(self.tcx, self.param_env)?) + InstSeq(base_insts, reg, res_ty) }; // Compute the re-aligned size of the element. - let res_ty = base_ty.builtin_deref(true, ty::LvaluePreference::NoPreference).unwrap().ty; - - let elem_sz = match layout { - &ty::layout::Layout::Array { element_size, .. } => - element_size.bytes() as lir::Sz, - _ => { - return Err(Error::UnexpectedIndexBase); - } - }; + let res_ty = base_ty.builtin_deref(true).unwrap().ty; + let elem_sz = self.tcx.layout_of(self.param_env.and(res_ty))?.size.bytes() as lir::Sz; // We are expecting a pointer, in both cases. self.check_same_size(base_ty, self.tcx.types.usize)?; let (_, idx_size) = self.add_type(self.tcx.types.usize, 0)?; match lvctx { - LvalueCtx::Value => { + PlaceCtx::Value => { let imm = self.get_new_reg(); let mul = self.get_new_reg(); let reg = self.get_new_reg(); @@ -653,8 +748,8 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { Ok(InstSeq(insts, val, res_ty)) } - LvalueCtx::RefSlice => Err(Error::UnexpectedRefSliceCtx), - LvalueCtx::Address => Err(Error::NYI), + PlaceCtx::RefSlice => Err(Error::UnexpectedRefSliceCtx), + PlaceCtx::Address => Err(Error::NYI), } } _ => Err(Error::NYI) @@ -678,7 +773,7 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { // Thus we have to fake a content of the same type, and // in the place-holder structure added by the jit! // macro, add a new reference to static variables. - &mir::Lvalue::Static(ref def) => { + &mir::Place::Static(ref def) => { let ty = self.tcx.mk_imm_ref(region.clone(), def.ty); let reg = self.get_new_reg(); let (off, sz) = self.register_static(rvalue, ty)?; @@ -693,8 +788,8 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { // location in which the data is stored. So when a // rvalue request the location of the lvalue, then we // just return the unmodified lvalue. - &mir::Lvalue::Local(_) => - self.lvalue(lvalue, LvalueCtx::Address).or_else( + &mir::Place::Local(_) => + self.lvalue(lvalue, PlaceCtx::Address).or_else( report_nyi!("mir::Rvalue::Ref(_, _, {:?})", lvalue)), _ => Err(Error::NYI) @@ -703,10 +798,10 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { mir::Rvalue::Len(ref lvalue) => { let InstSeq(lv_insts, lv_reg, lv_ty) = - self.lvalue(lvalue, LvalueCtx::RefSlice).or_else( + self.lvalue(lvalue, PlaceCtx::RefSlice).or_else( report_nyi!("mir::Rvalue::Len({:?})", lvalue))?; - let layout = lv_ty.layout(self.tcx, self.param_env)?; + let layout = self.tcx.layout_of(self.param_env.and(lv_ty))?; println!("mir::Rvalue::Len({:?})", lvalue); println!("ty: {:?} / layout: {:?}", lv_ty, layout); @@ -717,7 +812,7 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { //assert_eq!(layout.field_count(), 2); //assert_eq!(layout.field_type(&self.lcx, 1), self.tcx.types.usize); - let offset = layout.field_offset(&self.tcx.data_layout, 1, None); + let offset = layout.fields.offset(ty::layout::FAT_PTR_EXTRA); let (_, size) = self.add_type(self.tcx.types.usize, 0)?; // We are expecting a pointer, in both cases. //self.check_same_size(lv_ty, self.tcx.types.usize)?; @@ -834,20 +929,20 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { let (ty, variant) = match **kind { mir::AggregateKind::Array(ty) => - (self.tcx.mk_array(ty, operands.len()), 0), + (self.tcx.mk_array(ty, operands.len() as u64), 0), mir::AggregateKind::Tuple => { let tys : Vec<_> = operands.iter().map(|op| { let &InstSeq(_, _, ty) = op; ty }).collect(); - (self.tcx.intern_tup(&tys, true), 0) + (self.tcx.intern_tup(&tys, false), 0) } mir::AggregateKind::Adt(def, variant, substs, _) => (self.tcx.mk_adt(def, substs), variant), _ => return Err(Error::NYI) }; - let layout = ty.layout(self.tcx, self.param_env)?; + let layout = self.tcx.layout_of(self.param_env.and(ty))?; let (_, size) = self.add_type(ty, 0)?; // Initialize the space for the type to 0. @@ -857,12 +952,13 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { lir::Inst::CopyImm(reg, 0 as lir::Imm, size as lir::Sz), ]; - match *layout { - ty::layout::Layout::Univariant { .. } => (), - ty::layout::Layout::General { discr, .. } => { + let fields = match &layout.variants { + &ty::layout::Variants::Single { .. } => &layout.fields, + // Layout::Univariant -> Variants::Tagged { .. smae thing .. } + &ty::layout::Variants::Tagged { ref discr, ref variants, .. } => { // Initialize the discriminant - let (_dty, sz) = match discr { - ty::layout::Integer::I8 => (self.tcx.types.i8, 1), + let (_dty, sz) = match discr.value { + ty::layout::Primitive::Int(ty::layout::Integer::I8, _) => (self.tcx.types.i8, 1), _ => { unreachable!("ty: {:?} / layout: {:?}", ty, layout); } @@ -875,17 +971,18 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { lir::Inst::StoreInto(reg, discr_reg, 0, sz as lir::Sz), lir::Inst::Dead(discr_reg), ]); + + &variants[variant].fields } - _ => { + _ => { // Variants::NicheFilling return Err(Error::NYI); } - } + }; // Add each operand for (i, InstSeq(mut op_insts, op_reg, op_ty)) in operands.into_iter().enumerate() { let (_, size) = self.add_type(op_ty, 0)?; - let off = layout.field_offset(&self.tcx.data_layout, - i, Some(variant)).bytes(); + let off = fields.offset(i).bytes(); insts.append(&mut op_insts); insts.append(&mut vec![ lir::Inst::StoreInto(reg, op_reg, @@ -897,22 +994,51 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { Ok(InstSeq(insts, reg, ty)) } - _ => Err(Error::NYI) + mir::Rvalue::Discriminant(ref place) => { + // Get the place as a value. + let InstSeq(mut insts, reg, ty) = self.lvalue(place, PlaceCtx::Value)?; + // Extract the field which corresponds to the discriminant. + let layout = self.tcx.layout_of(self.param_env.and(ty))?; + // Read the discriminant of an ADT. + match &layout.variants { + // What?! Rustc produces a discriminant rvalue on a type + // which is neither a tagged place nor a niche, we should + // not generate anything for such assignment. + &ty::layout::Variants::Single { ref index } => { + let ret = self.get_new_reg(); + insts.append(&mut vec![ + lir::Inst::Dead(reg), + lir::Inst::CopyImm(ret, *index as lir::Imm, 1 as lir::Sz), + ]); + Ok(InstSeq(insts, ret, self.tcx.types.u8)) + } + _ => { + println!("rvalue: {:?}", rvalue); + println!("Layout = {:?}", layout); + Err(Error::NYI) + } + } + } + _ => { + println!("rvalue: {:?}", rvalue); + Err(Error::NYI) + } } } fn operand(&mut self, operand: &mir::Operand<'tcx>) -> Result, Error> { match *operand { - mir::Operand::Consume(ref lvalue) => { - Ok(self.lvalue(lvalue, LvalueCtx::Value).or_else( - report_nyi!("mir::Operand::Consume({:?})", lvalue))?) + mir::Operand::Copy(ref place) => { + Ok(self.lvalue(place, PlaceCtx::Value).or_else( + report_nyi!("mir::Operand::Copy({:?})", place))?) + } + mir::Operand::Move(ref place) => { + Ok(self.lvalue(place, PlaceCtx::Value).or_else( + report_nyi!("mir::Operand::Move({:?})", place))?) } mir::Operand::Constant(ref constant) => { let res = match constant.literal.clone() { - mir::Literal::Item { .. /*def_id, substs*/ } => { - Err(Error::NYI) - }, - mir::Literal::Value { ref value } if self.constval_use_static(value) => { + mir::Literal::Value { ref value } if self.constval_use_static(&value.val) => { // Load the static in a register. let reg = self.get_new_reg(); @@ -948,11 +1074,27 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { ], reg, ty)) }, mir::Literal::Value { value } => { - Ok(self.constval(value, constant.ty).or_else( + Ok(self.constval(value.val, constant.ty).or_else( report_nyi!("mir::Literal::Value: {:?} ", constant.literal))?) }, - mir::Literal::Promoted { .. } => { - Err(Error::NYI) + mir::Literal::Promoted { index } => { + // Duplicate the operand with the promoted value and + // register it to be known at runtime. + + let ty = constant.ty; + let index = self.register_promoted(index); + let operand = mir::Operand::Constant(Box::new(mir::Constant { + literal: mir::Literal::Promoted { index }, + ty: ty, + span: constant.span, + })); + let rv = mir::Rvalue::Use(operand); + let (off, sz) = self.register_static(&rv, ty)?; + let reg = self.get_new_reg(); + Ok(InstSeq(vec![ + lir::Inst::Live(reg), + lir::Inst::Static(reg, off, sz), + ], reg, ty)) }, }; @@ -1011,9 +1153,9 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { let (insts, value) = { match self.ret_local { Some(local) => { - let fake_rv = mir::Lvalue::Local(local); + let fake_rv = mir::Place::Local(local); let InstSeq(insts, reg, _ty) = - self.lvalue(&fake_rv, LvalueCtx::Value).or_else( + self.lvalue(&fake_rv, PlaceCtx::Value).or_else( report_nyi!("mir::TerminatorKind::Return"))?; (insts, Some(reg)) } @@ -1102,17 +1244,18 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { let resume = self.new_block(); let target = self.get_block(*bb); let InstSeq(mut ret_insts, ret_reg, ret_ty) = - self.lvalue(lvalue, LvalueCtx::Address).or_else( + self.lvalue(lvalue, PlaceCtx::Address).or_else( report_nyi!("Call(destination: {:?}, _)", lvalue))?; - let ret_ty = ret_ty.builtin_deref(true, ty::LvaluePreference::NoPreference).unwrap().ty; + let ret_ty = ret_ty.builtin_deref(true).unwrap().ty; // TODO: check that the returned value is the same // size as the lvalue storage space. let (_, size) = self.add_type(ret_ty, 0)?; - let layout = ret_ty.layout(self.tcx, self.param_env)?; - let is_fat_ptr = match layout { - &ty::layout::Layout::FatPointer{ .. } => true, + let is_fat_ptr = match ret_ty.sty { + ty::TyRawPtr(ref elem) | + ty::TyRef(_, ref elem) => !elem.ty.is_sized(self.tcx, self.param_env, DUMMY_SP), + ty::TyAdt(def, _) if def.is_box() => !ret_ty.boxed_ty().is_sized(self.tcx, self.param_env, DUMMY_SP), _ => false, }; @@ -1173,7 +1316,7 @@ impl<'a, 'tcx> Transpiler<'a, 'tcx> { } &mir::TerminatorKind::Drop { ref location, target, unwind } => { let InstSeq(lv_insts, lv_reg, lv_ty) = - self.lvalue(location, LvalueCtx::Address).or_else( + self.lvalue(location, PlaceCtx::Address).or_else( report_nyi!("Drop(location: {:?}, _)", location))?; // see resolve_drop_in_place diff --git a/release.nix b/release.nix new file mode 100644 index 0000000..181336b --- /dev/null +++ b/release.nix @@ -0,0 +1,20 @@ +{ nixpkgs ? +, rust_overlay ? ~/.config/nixpkgs/overlays/nixpkgs-mozilla/rust-overlay.nix +}: + +let + rustChannel = { channel = "nightly"; date = "2018-02-19"; }; + hj_overlay = self: super: { + hj_rust = (super.rustChannelOf rustChannel).rust.override { + extensions = [ "rust-src" ]; + }; + holyjit = super.callPackage ./default.nix { + rust =self.hj_rust; + }; + }; + pkgs = import nixpkgs { overlays = [ (import rust_overlay) hj_overlay ]; }; + jobs = { + inherit (pkgs) holyjit; + }; +in + jobs diff --git a/rustc.sh b/rustc.sh new file mode 100755 index 0000000..ec5ca94 --- /dev/null +++ b/rustc.sh @@ -0,0 +1,38 @@ +#!/bin/sh + +debug=true + +# CLI: rustc --crate name path/name.rs +wrap=false; +case $4 in + tests/*) wrap=true;; + examples/*) wrap=true;; + benches/*) wrap=true;; +esac + +if $wrap; then + for arg; do + case $arg in + # .../target/debug/deps/libholyjit_lib-abcdef.rlib + holyjit_lib=*) holyjit_lib=${arg#*=};; + esac + done + + test -z "$holyjit_lib" && \ + echo 1>&2 "Unable to find holyjit_lib on the command line." + + holyjit=$(dirname $(dirname $holyjit_lib))/holyjit + test \! -x $holyjit && \ + echo 1>&2 "Unable to find $holyjit binary." + + if $debug; then + log_dir=$(dirname $holyjit)/log + mkdir -p $log_dir + exec $holyjit "$@" -Z dump-mir=all -Z dump-mir-dir=$log_dir + else + exec $holyjit "$@" + fi +fi + +exec "$@" +