Skip to content

Fix rusti basic usage #7769

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 14, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 56 additions & 16 deletions src/librustc/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,15 +101,30 @@ pub mod jit {
use back::link::llvm_err;
use driver::session::Session;
use lib::llvm::llvm;
use lib::llvm::{ModuleRef, ContextRef};
use lib::llvm::{ModuleRef, ContextRef, ExecutionEngineRef};
use metadata::cstore;

use std::cast;
use std::ptr;
use std::str;
use std::sys;
use std::local_data;
use std::unstable::intrinsics;

struct LLVMJITData {
ee: ExecutionEngineRef,
llcx: ContextRef
}

pub trait Engine {}
impl Engine for LLVMJITData {}

impl Drop for LLVMJITData {
fn drop(&self) {
unsafe {
llvm::LLVMDisposeExecutionEngine(self.ee);
llvm::LLVMContextDispose(self.llcx);
}
}
}

pub fn exec(sess: Session,
c: ContextRef,
m: ModuleRef,
Expand All @@ -130,7 +145,7 @@ pub mod jit {

debug!("linking: %s", path);

do str::as_c_str(path) |buf_t| {
do path.as_c_str |buf_t| {
if !llvm::LLVMRustLoadCrate(manager, buf_t) {
llvm_err(sess, ~"Could not link");
}
Expand All @@ -149,7 +164,7 @@ pub mod jit {
// Next, we need to get a handle on the _rust_main function by
// looking up it's corresponding ValueRef and then requesting that
// the execution engine compiles the function.
let fun = do str::as_c_str("_rust_main") |entry| {
let fun = do "_rust_main".as_c_str |entry| {
llvm::LLVMGetNamedFunction(m, entry)
};
if fun.is_null() {
Expand All @@ -163,20 +178,45 @@ pub mod jit {
// closure
let code = llvm::LLVMGetPointerToGlobal(ee, fun);
assert!(!code.is_null());
let closure = sys::Closure {
code: code,
env: ptr::null()
};
let func: &fn() = cast::transmute(closure);
let func: extern "Rust" fn() = cast::transmute(code);
func();

// Sadly, there currently is no interface to re-use this execution
// engine, so it's disposed of here along with the context to
// prevent leaks.
llvm::LLVMDisposeExecutionEngine(ee);
llvm::LLVMContextDispose(c);
// Currently there is no method of re-using the executing engine
// from LLVM in another call to the JIT. While this kinda defeats
// the purpose of having a JIT in the first place, there isn't
// actually much code currently which would re-use data between
// different invocations of this. Additionally, the compilation
// model currently isn't designed to support this scenario.
//
// We can't destroy the engine/context immediately here, however,
// because of annihilation. The JIT code contains drop glue for any
// types defined in the crate we just ran, and if any of those boxes
// are going to be dropped during annihilation, the drop glue must
// be run. Hence, we need to transfer ownership of this jit engine
// to the caller of this function. To be convenient for now, we
// shove it into TLS and have someone else remove it later on.
let data = ~LLVMJITData { ee: ee, llcx: c };
set_engine(data as ~Engine);
}
}

// The stage1 compiler won't work, but that doesn't really matter. TLS
// changed only very recently to allow storage of owned values.
fn engine_key(_: ~Engine) {}

#[cfg(not(stage0))]
fn set_engine(engine: ~Engine) {
unsafe { local_data::set(engine_key, engine) }
}
#[cfg(stage0)]
fn set_engine(_: ~Engine) {}

#[cfg(not(stage0))]
pub fn consume_engine() -> Option<~Engine> {
unsafe { local_data::pop(engine_key) }
}
#[cfg(stage0)]
pub fn consume_engine() -> Option<~Engine> { None }
}

pub mod write {
Expand Down
6 changes: 5 additions & 1 deletion src/librustc/rustc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,11 @@ pub fn monitor(f: ~fn(diagnostic::Emitter)) {

let _finally = finally { ch: ch };

f(demitter)
f(demitter);

// Due reasons explain in #7732, if there was a jit execution context it
// must be consumed and passed along to our parent task.
back::link::jit::consume_engine()
} {
result::Ok(_) => { /* fallthrough */ }
result::Err(_) => {
Expand Down
103 changes: 68 additions & 35 deletions src/librusti/rusti.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,23 @@
* - Pass #3
* Finally, a program is generated to deserialize the local variable state,
* run the code input, and then reserialize all bindings back into a local
* hash map. Once this code runs, the input has fully been run and the REPL
* waits for new input.
* hash map. This code is then run in the JIT engine provided by the rust
* compiler.
*
* - Pass #4
* Once this code runs, the input has fully been run and the hash map of local
* variables from TLS is read back into the local store of variables. This is
* then used later to pass back along to the parent rusti task and then begin
* waiting for input again.
*
* - Pass #5
* When running rusti code, it's important to consume ownership of the LLVM
* jit contextual information to prevent code from being deallocated too soon
* (before drop glue runs, see #7732). For this reason, the jit context is
* consumed and also passed along to the parent task. The parent task then
* keeps around all contexts while rusti is running. This must be done because
* tasks could in theory be spawned off and running in the background (still
* using the code).
*
* Encoding/decoding is done with EBML, and there is simply a map of ~str ->
* ~[u8] maintaining the values of each local binding (by name).
Expand All @@ -60,6 +75,7 @@ use std::cell::Cell;
use extra::rl;

use rustc::driver::{driver, session};
use rustc::back::link::jit;
use syntax::{ast, diagnostic};
use syntax::ast_util::*;
use syntax::parse::token;
Expand All @@ -80,8 +96,9 @@ pub struct Repl {
binary: ~str,
running: bool,
lib_search_paths: ~[~str],
engines: ~[~jit::Engine],

program: Program,
program: ~Program,
}

// Action to do after reading a :command
Expand All @@ -91,13 +108,15 @@ enum CmdAction {
}

/// Run an input string in a Repl, returning the new Repl.
fn run(mut repl: Repl, input: ~str) -> Repl {
fn run(mut program: ~Program, binary: ~str, lib_search_paths: ~[~str],
input: ~str) -> (~Program, Option<~jit::Engine>)
{
// Build some necessary rustc boilerplate for compiling things
let binary = repl.binary.to_managed();
let binary = binary.to_managed();
let options = @session::options {
crate_type: session::unknown_crate,
binary: binary,
addl_lib_search_paths: @mut repl.lib_search_paths.map(|p| Path(*p)),
addl_lib_search_paths: @mut lib_search_paths.map(|p| Path(*p)),
jit: true,
.. copy *session::basic_options()
};
Expand Down Expand Up @@ -136,9 +155,9 @@ fn run(mut repl: Repl, input: ~str) -> Repl {
};
match vi.node {
ast::view_item_extern_mod(*) => {
repl.program.record_extern(s);
program.record_extern(s);
}
ast::view_item_use(*) => { repl.program.record_view_item(s); }
ast::view_item_use(*) => { program.record_view_item(s); }
}
}

Expand All @@ -156,10 +175,10 @@ fn run(mut repl: Repl, input: ~str) -> Repl {
// them at all usable they need to be decorated
// with #[deriving(Encoable, Decodable)]
ast::item_struct(*) => {
repl.program.record_struct(name, s);
program.record_struct(name, s);
}
// Item declarations are hoisted out of main()
_ => { repl.program.record_item(name, s); }
_ => { program.record_item(name, s); }
}
}

Expand Down Expand Up @@ -190,17 +209,17 @@ fn run(mut repl: Repl, input: ~str) -> Repl {
}
// return fast for empty inputs
if to_run.len() == 0 && result.is_none() {
return repl;
return (program, None);
}

//
// Stage 2: run everything up to typeck to learn the types of the new
// variables introduced into the program
//
info!("Learning about the new types in the program");
repl.program.set_cache(); // before register_new_vars (which changes them)
program.set_cache(); // before register_new_vars (which changes them)
let input = to_run.connect("\n");
let test = repl.program.test_code(input, &result, *new_locals);
let test = program.test_code(input, &result, *new_locals);
debug!("testing with ^^^^^^ %?", (||{ println(test) })());
let dinput = driver::str_input(test.to_managed());
let cfg = driver::build_configuration(sess, binary, &dinput);
Expand All @@ -210,14 +229,14 @@ fn run(mut repl: Repl, input: ~str) -> Repl {
// Once we're typechecked, record the types of all local variables defined
// in this input
do find_main(crate.expect("crate after cu_typeck"), sess) |blk| {
repl.program.register_new_vars(blk, tcx.expect("tcx after cu_typeck"));
program.register_new_vars(blk, tcx.expect("tcx after cu_typeck"));
}

//
// Stage 3: Actually run the code in the JIT
//
info!("actually running code");
let code = repl.program.code(input, &result);
let code = program.code(input, &result);
debug!("actually running ^^^^^^ %?", (||{ println(code) })());
let input = driver::str_input(code.to_managed());
let cfg = driver::build_configuration(sess, binary, &input);
Expand All @@ -231,9 +250,15 @@ fn run(mut repl: Repl, input: ~str) -> Repl {
// local variable bindings.
//
info!("cleaning up after code");
repl.program.consume_cache();
program.consume_cache();

return repl;
//
// Stage 5: Extract the LLVM execution engine to take ownership of the
// generated JIT code. This means that rusti can spawn parallel
// tasks and we won't deallocate the code emitted until rusti
// itself is destroyed.
//
return (program, jit::consume_engine());

fn parse_input(sess: session::Session, binary: @str,
input: &str) -> @ast::crate {
Expand Down Expand Up @@ -418,8 +443,8 @@ fn run_cmd(repl: &mut Repl, _in: @io::Reader, _out: @io::Writer,
/// Executes a line of input, which may either be rust code or a
/// :command. Returns a new Repl if it has changed.
pub fn run_line(repl: &mut Repl, in: @io::Reader, out: @io::Writer, line: ~str,
use_rl: bool)
-> Option<Repl> {
use_rl: bool) -> bool
{
if line.starts_with(":") {
// drop the : and the \n (one byte each)
let full = line.slice(1, line.len());
Expand All @@ -442,21 +467,30 @@ pub fn run_line(repl: &mut Repl, in: @io::Reader, out: @io::Writer, line: ~str,
}
}
}
return None;
return true;
}
}
}

let line = Cell::new(line);
let r = Cell::new(copy *repl);
let program = Cell::new(copy repl.program);
let lib_search_paths = Cell::new(copy repl.lib_search_paths);
let binary = Cell::new(copy repl.binary);
let result = do task::try {
run(r.take(), line.take())
run(program.take(), binary.take(), lib_search_paths.take(), line.take())
};

if result.is_ok() {
return Some(result.get());
match result {
Ok((program, engine)) => {
repl.program = program;
match engine {
Some(e) => { repl.engines.push(e); }
None => {}
}
return true;
}
Err(*) => { return false; }
}
return None;
}

pub fn main() {
Expand All @@ -468,8 +502,9 @@ pub fn main() {
binary: copy args[0],
running: true,
lib_search_paths: ~[],
engines: ~[],

program: Program::new(),
program: ~Program::new(),
};

let istty = unsafe { libc::isatty(libc::STDIN_FILENO as i32) } != 0;
Expand Down Expand Up @@ -502,10 +537,7 @@ pub fn main() {
}
loop;
}
match run_line(&mut repl, in, out, line, istty) {
Some(new_repl) => repl = new_repl,
None => { }
}
run_line(&mut repl, in, out, line, istty);
}
}
}
Expand All @@ -524,7 +556,8 @@ mod tests {
binary: ~"rusti",
running: true,
lib_search_paths: ~[],
program: Program::new(),
engines: ~[],
program: ~Program::new(),
}
}

Expand All @@ -535,9 +568,9 @@ mod tests {
fn run_program(prog: &str) {
let mut r = repl();
for prog.split_iter('\n').advance |cmd| {
let result = run_line(&mut r, io::stdin(), io::stdout(),
cmd.to_owned(), false);
r = result.expect(fmt!("the command '%s' failed", cmd));
assert!(run_line(&mut r, io::stdin(), io::stdout(),
cmd.to_owned(), false),
"the command '%s' failed", cmd);
}
}
fn run_program(_: &str) {}
Expand Down Expand Up @@ -682,7 +715,7 @@ mod tests {
assert!(r.running);
let result = run_line(&mut r, io::stdin(), io::stdout(),
~":exit", false);
assert!(result.is_none());
assert!(result);
assert!(!r.running);
}
}