* - 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).
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;
binary: ~str,
running: bool,
lib_search_paths: ~[~str],
+ engines: ~[~jit::Engine],
- program: Program,
+ program: ~Program,
}
// Action to do after reading a :command
}
/// 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()
};
};
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); }
}
}
// 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); }
}
}
}
// return fast for empty inputs
if to_run.len() == 0 && result.is_none() {
- return repl;
+ return (program, None);
}
//
// 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);
// 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);
// 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 {
/// 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());
}
}
}
- 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() {
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;
}
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);
}
}
}
binary: ~"rusti",
running: true,
lib_search_paths: ~[],
- program: Program::new(),
+ engines: ~[],
+ program: ~Program::new(),
}
}
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) {}
assert!(r.running);
let result = run_line(&mut r, io::stdin(), io::stdout(),
~":exit", false);
- assert!(result.is_none());
+ assert!(result);
assert!(!r.running);
}
}