Skip to content

Commit 247ad45

Browse files
committed
auto merge of #7769 : alexcrichton/rust/issue-7732-fix-rusti-again, r=cmr
Turns out this was a more subtle bug than I originally thought. My analysis can be found in #7732, but I also tried to put descriptive info into the comments. Closes #7732
2 parents b937af1 + 21d7098 commit 247ad45

File tree

3 files changed

+129
-52
lines changed

3 files changed

+129
-52
lines changed

src/librustc/back/link.rs

+56-16
Original file line numberDiff line numberDiff line change
@@ -101,15 +101,30 @@ pub mod jit {
101101
use back::link::llvm_err;
102102
use driver::session::Session;
103103
use lib::llvm::llvm;
104-
use lib::llvm::{ModuleRef, ContextRef};
104+
use lib::llvm::{ModuleRef, ContextRef, ExecutionEngineRef};
105105
use metadata::cstore;
106106

107107
use std::cast;
108-
use std::ptr;
109-
use std::str;
110-
use std::sys;
108+
use std::local_data;
111109
use std::unstable::intrinsics;
112110

111+
struct LLVMJITData {
112+
ee: ExecutionEngineRef,
113+
llcx: ContextRef
114+
}
115+
116+
pub trait Engine {}
117+
impl Engine for LLVMJITData {}
118+
119+
impl Drop for LLVMJITData {
120+
fn drop(&self) {
121+
unsafe {
122+
llvm::LLVMDisposeExecutionEngine(self.ee);
123+
llvm::LLVMContextDispose(self.llcx);
124+
}
125+
}
126+
}
127+
113128
pub fn exec(sess: Session,
114129
c: ContextRef,
115130
m: ModuleRef,
@@ -130,7 +145,7 @@ pub mod jit {
130145

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

133-
do str::as_c_str(path) |buf_t| {
148+
do path.as_c_str |buf_t| {
134149
if !llvm::LLVMRustLoadCrate(manager, buf_t) {
135150
llvm_err(sess, ~"Could not link");
136151
}
@@ -149,7 +164,7 @@ pub mod jit {
149164
// Next, we need to get a handle on the _rust_main function by
150165
// looking up it's corresponding ValueRef and then requesting that
151166
// the execution engine compiles the function.
152-
let fun = do str::as_c_str("_rust_main") |entry| {
167+
let fun = do "_rust_main".as_c_str |entry| {
153168
llvm::LLVMGetNamedFunction(m, entry)
154169
};
155170
if fun.is_null() {
@@ -163,20 +178,45 @@ pub mod jit {
163178
// closure
164179
let code = llvm::LLVMGetPointerToGlobal(ee, fun);
165180
assert!(!code.is_null());
166-
let closure = sys::Closure {
167-
code: code,
168-
env: ptr::null()
169-
};
170-
let func: &fn() = cast::transmute(closure);
181+
let func: extern "Rust" fn() = cast::transmute(code);
171182
func();
172183

173-
// Sadly, there currently is no interface to re-use this execution
174-
// engine, so it's disposed of here along with the context to
175-
// prevent leaks.
176-
llvm::LLVMDisposeExecutionEngine(ee);
177-
llvm::LLVMContextDispose(c);
184+
// Currently there is no method of re-using the executing engine
185+
// from LLVM in another call to the JIT. While this kinda defeats
186+
// the purpose of having a JIT in the first place, there isn't
187+
// actually much code currently which would re-use data between
188+
// different invocations of this. Additionally, the compilation
189+
// model currently isn't designed to support this scenario.
190+
//
191+
// We can't destroy the engine/context immediately here, however,
192+
// because of annihilation. The JIT code contains drop glue for any
193+
// types defined in the crate we just ran, and if any of those boxes
194+
// are going to be dropped during annihilation, the drop glue must
195+
// be run. Hence, we need to transfer ownership of this jit engine
196+
// to the caller of this function. To be convenient for now, we
197+
// shove it into TLS and have someone else remove it later on.
198+
let data = ~LLVMJITData { ee: ee, llcx: c };
199+
set_engine(data as ~Engine);
178200
}
179201
}
202+
203+
// The stage1 compiler won't work, but that doesn't really matter. TLS
204+
// changed only very recently to allow storage of owned values.
205+
fn engine_key(_: ~Engine) {}
206+
207+
#[cfg(not(stage0))]
208+
fn set_engine(engine: ~Engine) {
209+
unsafe { local_data::set(engine_key, engine) }
210+
}
211+
#[cfg(stage0)]
212+
fn set_engine(_: ~Engine) {}
213+
214+
#[cfg(not(stage0))]
215+
pub fn consume_engine() -> Option<~Engine> {
216+
unsafe { local_data::pop(engine_key) }
217+
}
218+
#[cfg(stage0)]
219+
pub fn consume_engine() -> Option<~Engine> { None }
180220
}
181221

182222
pub mod write {

src/librustc/rustc.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,11 @@ pub fn monitor(f: ~fn(diagnostic::Emitter)) {
332332

333333
let _finally = finally { ch: ch };
334334

335-
f(demitter)
335+
f(demitter);
336+
337+
// Due reasons explain in #7732, if there was a jit execution context it
338+
// must be consumed and passed along to our parent task.
339+
back::link::jit::consume_engine()
336340
} {
337341
result::Ok(_) => { /* fallthrough */ }
338342
result::Err(_) => {

src/librusti/rusti.rs

+68-35
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,23 @@
3636
* - Pass #3
3737
* Finally, a program is generated to deserialize the local variable state,
3838
* run the code input, and then reserialize all bindings back into a local
39-
* hash map. Once this code runs, the input has fully been run and the REPL
40-
* waits for new input.
39+
* hash map. This code is then run in the JIT engine provided by the rust
40+
* compiler.
41+
*
42+
* - Pass #4
43+
* Once this code runs, the input has fully been run and the hash map of local
44+
* variables from TLS is read back into the local store of variables. This is
45+
* then used later to pass back along to the parent rusti task and then begin
46+
* waiting for input again.
47+
*
48+
* - Pass #5
49+
* When running rusti code, it's important to consume ownership of the LLVM
50+
* jit contextual information to prevent code from being deallocated too soon
51+
* (before drop glue runs, see #7732). For this reason, the jit context is
52+
* consumed and also passed along to the parent task. The parent task then
53+
* keeps around all contexts while rusti is running. This must be done because
54+
* tasks could in theory be spawned off and running in the background (still
55+
* using the code).
4156
*
4257
* Encoding/decoding is done with EBML, and there is simply a map of ~str ->
4358
* ~[u8] maintaining the values of each local binding (by name).
@@ -60,6 +75,7 @@ use std::cell::Cell;
6075
use extra::rl;
6176

6277
use rustc::driver::{driver, session};
78+
use rustc::back::link::jit;
6379
use syntax::{ast, diagnostic};
6480
use syntax::ast_util::*;
6581
use syntax::parse::token;
@@ -80,8 +96,9 @@ pub struct Repl {
8096
binary: ~str,
8197
running: bool,
8298
lib_search_paths: ~[~str],
99+
engines: ~[~jit::Engine],
83100

84-
program: Program,
101+
program: ~Program,
85102
}
86103

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

93110
/// Run an input string in a Repl, returning the new Repl.
94-
fn run(mut repl: Repl, input: ~str) -> Repl {
111+
fn run(mut program: ~Program, binary: ~str, lib_search_paths: ~[~str],
112+
input: ~str) -> (~Program, Option<~jit::Engine>)
113+
{
95114
// Build some necessary rustc boilerplate for compiling things
96-
let binary = repl.binary.to_managed();
115+
let binary = binary.to_managed();
97116
let options = @session::options {
98117
crate_type: session::unknown_crate,
99118
binary: binary,
100-
addl_lib_search_paths: @mut repl.lib_search_paths.map(|p| Path(*p)),
119+
addl_lib_search_paths: @mut lib_search_paths.map(|p| Path(*p)),
101120
jit: true,
102121
.. copy *session::basic_options()
103122
};
@@ -136,9 +155,9 @@ fn run(mut repl: Repl, input: ~str) -> Repl {
136155
};
137156
match vi.node {
138157
ast::view_item_extern_mod(*) => {
139-
repl.program.record_extern(s);
158+
program.record_extern(s);
140159
}
141-
ast::view_item_use(*) => { repl.program.record_view_item(s); }
160+
ast::view_item_use(*) => { program.record_view_item(s); }
142161
}
143162
}
144163

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

@@ -190,17 +209,17 @@ fn run(mut repl: Repl, input: ~str) -> Repl {
190209
}
191210
// return fast for empty inputs
192211
if to_run.len() == 0 && result.is_none() {
193-
return repl;
212+
return (program, None);
194213
}
195214

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

216235
//
217236
// Stage 3: Actually run the code in the JIT
218237
//
219238
info!("actually running code");
220-
let code = repl.program.code(input, &result);
239+
let code = program.code(input, &result);
221240
debug!("actually running ^^^^^^ %?", (||{ println(code) })());
222241
let input = driver::str_input(code.to_managed());
223242
let cfg = driver::build_configuration(sess, binary, &input);
@@ -231,9 +250,15 @@ fn run(mut repl: Repl, input: ~str) -> Repl {
231250
// local variable bindings.
232251
//
233252
info!("cleaning up after code");
234-
repl.program.consume_cache();
253+
program.consume_cache();
235254

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

238263
fn parse_input(sess: session::Session, binary: @str,
239264
input: &str) -> @ast::crate {
@@ -418,8 +443,8 @@ fn run_cmd(repl: &mut Repl, _in: @io::Reader, _out: @io::Writer,
418443
/// Executes a line of input, which may either be rust code or a
419444
/// :command. Returns a new Repl if it has changed.
420445
pub fn run_line(repl: &mut Repl, in: @io::Reader, out: @io::Writer, line: ~str,
421-
use_rl: bool)
422-
-> Option<Repl> {
446+
use_rl: bool) -> bool
447+
{
423448
if line.starts_with(":") {
424449
// drop the : and the \n (one byte each)
425450
let full = line.slice(1, line.len());
@@ -442,21 +467,30 @@ pub fn run_line(repl: &mut Repl, in: @io::Reader, out: @io::Writer, line: ~str,
442467
}
443468
}
444469
}
445-
return None;
470+
return true;
446471
}
447472
}
448473
}
449474

450475
let line = Cell::new(line);
451-
let r = Cell::new(copy *repl);
476+
let program = Cell::new(copy repl.program);
477+
let lib_search_paths = Cell::new(copy repl.lib_search_paths);
478+
let binary = Cell::new(copy repl.binary);
452479
let result = do task::try {
453-
run(r.take(), line.take())
480+
run(program.take(), binary.take(), lib_search_paths.take(), line.take())
454481
};
455482

456-
if result.is_ok() {
457-
return Some(result.get());
483+
match result {
484+
Ok((program, engine)) => {
485+
repl.program = program;
486+
match engine {
487+
Some(e) => { repl.engines.push(e); }
488+
None => {}
489+
}
490+
return true;
491+
}
492+
Err(*) => { return false; }
458493
}
459-
return None;
460494
}
461495

462496
pub fn main() {
@@ -468,8 +502,9 @@ pub fn main() {
468502
binary: copy args[0],
469503
running: true,
470504
lib_search_paths: ~[],
505+
engines: ~[],
471506
472-
program: Program::new(),
507+
program: ~Program::new(),
473508
};
474509
475510
let istty = unsafe { libc::isatty(libc::STDIN_FILENO as i32) } != 0;
@@ -502,10 +537,7 @@ pub fn main() {
502537
}
503538
loop;
504539
}
505-
match run_line(&mut repl, in, out, line, istty) {
506-
Some(new_repl) => repl = new_repl,
507-
None => { }
508-
}
540+
run_line(&mut repl, in, out, line, istty);
509541
}
510542
}
511543
}
@@ -524,7 +556,8 @@ mod tests {
524556
binary: ~"rusti",
525557
running: true,
526558
lib_search_paths: ~[],
527-
program: Program::new(),
559+
engines: ~[],
560+
program: ~Program::new(),
528561
}
529562
}
530563

@@ -535,9 +568,9 @@ mod tests {
535568
fn run_program(prog: &str) {
536569
let mut r = repl();
537570
for prog.split_iter('\n').advance |cmd| {
538-
let result = run_line(&mut r, io::stdin(), io::stdout(),
539-
cmd.to_owned(), false);
540-
r = result.expect(fmt!("the command '%s' failed", cmd));
571+
assert!(run_line(&mut r, io::stdin(), io::stdout(),
572+
cmd.to_owned(), false),
573+
"the command '%s' failed", cmd);
541574
}
542575
}
543576
fn run_program(_: &str) {}
@@ -682,7 +715,7 @@ mod tests {
682715
assert!(r.running);
683716
let result = run_line(&mut r, io::stdin(), io::stdout(),
684717
~":exit", false);
685-
assert!(result.is_none());
718+
assert!(result);
686719
assert!(!r.running);
687720
}
688721
}

0 commit comments

Comments
 (0)