Skip to content

Commit

Permalink
Rebase HolyJIT on top of nightly 2018-02-19 without plugin.
Browse files Browse the repository at this point in the history
 - 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.
  • Loading branch information
nbp committed Mar 27, 2018
1 parent 64388f9 commit bfb32d6
Show file tree
Hide file tree
Showing 12 changed files with 716 additions and 238 deletions.
13 changes: 11 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
[package]
name = "holyjit"
name = "holyjit_pkg"
version = "0.0.0"
authors = [ "Nicolas B. Pierron <nicolas.b.pierron@nbp.name>" ]

[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"
7 changes: 7 additions & 0 deletions bin/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "holyjit"
version = "0.0.0"
authors = ["Nicolas B. Pierron <nicolas.b.pierron@gmail.com>"]

[dependencies]
holyjit_plugin = { path = "../plugin" }
224 changes: 224 additions & 0 deletions bin/src/main.rs
Original file line number Diff line number Diff line change
@@ -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<PathBuf>,
ofile: &Option<PathBuf>,
descriptions: &rustc_errors::registry::Registry,
) -> Option<(Input, Option<PathBuf>)> {
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<PathBuf>,
ofile: &Option<PathBuf>,
) -> 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<String> = 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)
});
}
11 changes: 11 additions & 0 deletions default.nix
Original file line number Diff line number Diff line change
@@ -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
";
}
11 changes: 7 additions & 4 deletions examples/brainfuck.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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();
}
4 changes: 2 additions & 2 deletions lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ authors = [ "Nicolas B. Pierron <nicolas.b.pierron@nbp.name>" ]
# 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"
Expand Down
Loading

0 comments on commit bfb32d6

Please sign in to comment.