Have JIT execution take ownership of the LLVMContextRef

Also stop leaking the ExecutionEngine created for jit code by forcibly disposing
of it after the JIT code has finished executing
This commit is contained in:
Alex Crichton 2013-05-17 19:44:50 -04:00
parent 779191cd4b
commit 5c5095d25e
8 changed files with 94 additions and 60 deletions

View File

@ -103,7 +103,7 @@ pub mod jit {
use back::link::llvm_err; use back::link::llvm_err;
use driver::session::Session; use driver::session::Session;
use lib::llvm::llvm; use lib::llvm::llvm;
use lib::llvm::{ModuleRef, PassManagerRef}; use lib::llvm::{ModuleRef, PassManagerRef, ContextRef};
use metadata::cstore; use metadata::cstore;
use core::cast; use core::cast;
@ -126,6 +126,7 @@ pub mod jit {
pub fn exec(sess: Session, pub fn exec(sess: Session,
pm: PassManagerRef, pm: PassManagerRef,
c: ContextRef,
m: ModuleRef, m: ModuleRef,
opt: c_int, opt: c_int,
stacks: bool) { stacks: bool) {
@ -154,26 +155,43 @@ pub mod jit {
}); });
} }
// The execute function will return a void pointer // We custom-build a JIT execution engine via some rust wrappers
// to the _rust_main function. We can do closure // first. This wrappers takes ownership of the module passed in.
// magic here to turn it straight into a callable rust let ee = llvm::LLVMRustBuildJIT(manager, pm, m, opt, stacks);
// closure. It will also cleanup the memory manager if ee.is_null() {
// for us. llvm::LLVMContextDispose(c);
llvm_err(sess, ~"Could not create the JIT");
let entry = llvm::LLVMRustExecuteJIT(manager,
pm, m, opt, stacks);
if ptr::is_null(entry) {
llvm_err(sess, ~"Could not JIT");
} else {
let closure = Closure {
code: entry,
env: ptr::null()
};
let func: &fn() = cast::transmute(closure);
func();
} }
// 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| {
llvm::LLVMGetNamedFunction(m, entry)
};
if fun.is_null() {
llvm::LLVMDisposeExecutionEngine(ee);
llvm::LLVMContextDispose(c);
llvm_err(sess, ~"Could not find _rust_main in the JIT");
}
// Finally, once we have the pointer to the code, we can do some
// closure magic here to turn it straight into a callable rust
// closure
let code = llvm::LLVMGetPointerToGlobal(ee, fun);
assert!(!code.is_null());
let closure = Closure {
code: code,
env: ptr::null()
};
let func: &fn() = cast::transmute(closure);
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);
} }
} }
} }
@ -190,6 +208,7 @@ pub mod write {
use driver::session; use driver::session;
use lib::llvm::llvm; use lib::llvm::llvm;
use lib::llvm::{ModuleRef, mk_pass_manager, mk_target_data}; use lib::llvm::{ModuleRef, mk_pass_manager, mk_target_data};
use lib::llvm::{False, ContextRef};
use lib; use lib;
use back::passes; use back::passes;
@ -208,6 +227,7 @@ pub mod write {
} }
pub fn run_passes(sess: Session, pub fn run_passes(sess: Session,
llcx: ContextRef,
llmod: ModuleRef, llmod: ModuleRef,
output_type: output_type, output_type: output_type,
output: &Path) { output: &Path) {
@ -282,7 +302,7 @@ pub mod write {
// JIT execution takes ownership of the module, // JIT execution takes ownership of the module,
// so don't dispose and return. // so don't dispose and return.
jit::exec(sess, pm.llpm, llmod, CodeGenOptLevel, true); jit::exec(sess, pm.llpm, llcx, llmod, CodeGenOptLevel, true);
if sess.time_llvm_passes() { if sess.time_llvm_passes() {
llvm::LLVMRustPrintPassTimings(); llvm::LLVMRustPrintPassTimings();
@ -350,6 +370,7 @@ pub mod write {
// Clean up and return // Clean up and return
llvm::LLVMDisposeModule(llmod); llvm::LLVMDisposeModule(llmod);
llvm::LLVMContextDispose(llcx);
if sess.time_llvm_passes() { if sess.time_llvm_passes() {
llvm::LLVMRustPrintPassTimings(); llvm::LLVMRustPrintPassTimings();
} }
@ -368,6 +389,7 @@ pub mod write {
} }
llvm::LLVMDisposeModule(llmod); llvm::LLVMDisposeModule(llmod);
llvm::LLVMContextDispose(llcx);
if sess.time_llvm_passes() { llvm::LLVMRustPrintPassTimings(); } if sess.time_llvm_passes() { llvm::LLVMRustPrintPassTimings(); }
} }
} }

View File

@ -217,7 +217,7 @@ pub fn compile_rest(sess: Session,
let mut crate = crate_opt.unwrap(); let mut crate = crate_opt.unwrap();
let (llmod, link_meta) = { let (llcx, llmod, link_meta) = {
crate = time(time_passes, ~"intrinsic injection", || crate = time(time_passes, ~"intrinsic injection", ||
front::intrinsic_inject::inject_intrinsic(sess, crate)); front::intrinsic_inject::inject_intrinsic(sess, crate));
@ -340,14 +340,14 @@ pub fn compile_rest(sess: Session,
let obj_filename = outputs.obj_filename.with_filetype("s"); let obj_filename = outputs.obj_filename.with_filetype("s");
time(time_passes, ~"LLVM passes", || time(time_passes, ~"LLVM passes", ||
link::write::run_passes(sess, llmod, output_type, link::write::run_passes(sess, llcx, llmod, output_type,
&obj_filename)); &obj_filename));
link::write::run_ndk(sess, &obj_filename, &outputs.obj_filename); link::write::run_ndk(sess, &obj_filename, &outputs.obj_filename);
} else { } else {
time(time_passes, ~"LLVM passes", || time(time_passes, ~"LLVM passes", ||
link::write::run_passes(sess, llmod, sess.opts.output_type, link::write::run_passes(sess, llcx, llmod, sess.opts.output_type,
&outputs.obj_filename)); &outputs.obj_filename));
} }
let stop_after_codegen = let stop_after_codegen =

View File

@ -205,6 +205,8 @@ pub enum BasicBlock_opaque {}
pub type BasicBlockRef = *BasicBlock_opaque; pub type BasicBlockRef = *BasicBlock_opaque;
pub enum Builder_opaque {} pub enum Builder_opaque {}
pub type BuilderRef = *Builder_opaque; pub type BuilderRef = *Builder_opaque;
pub enum ExecutionEngine_opaque {}
pub type ExecutionEngineRef = *ExecutionEngine_opaque;
pub enum MemoryBuffer_opaque {} pub enum MemoryBuffer_opaque {}
pub type MemoryBufferRef = *MemoryBuffer_opaque; pub type MemoryBufferRef = *MemoryBuffer_opaque;
pub enum PassManager_opaque {} pub enum PassManager_opaque {}
@ -223,7 +225,7 @@ pub enum Pass_opaque {}
pub type PassRef = *Pass_opaque; pub type PassRef = *Pass_opaque;
pub mod llvm { pub mod llvm {
use super::{AtomicBinOp, AtomicOrdering, BasicBlockRef}; use super::{AtomicBinOp, AtomicOrdering, BasicBlockRef, ExecutionEngineRef};
use super::{Bool, BuilderRef, ContextRef, MemoryBufferRef, ModuleRef}; use super::{Bool, BuilderRef, ContextRef, MemoryBufferRef, ModuleRef};
use super::{ObjectFileRef, Opcode, PassManagerRef, PassManagerBuilderRef}; use super::{ObjectFileRef, Opcode, PassManagerRef, PassManagerBuilderRef};
use super::{SectionIteratorRef, TargetDataRef, TypeKind, TypeRef, UseRef}; use super::{SectionIteratorRef, TargetDataRef, TypeKind, TypeRef, UseRef};
@ -363,6 +365,10 @@ pub mod llvm {
pub unsafe fn LLVMGetPointerAddressSpace(PointerTy: TypeRef) pub unsafe fn LLVMGetPointerAddressSpace(PointerTy: TypeRef)
-> c_uint; -> c_uint;
#[fast_ffi] #[fast_ffi]
pub unsafe fn LLVMGetPointerToGlobal(EE: ExecutionEngineRef,
V: ValueRef)
-> *();
#[fast_ffi]
pub unsafe fn LLVMGetVectorSize(VectorTy: TypeRef) -> c_uint; pub unsafe fn LLVMGetVectorSize(VectorTy: TypeRef) -> c_uint;
/* Operations on other types */ /* Operations on other types */
@ -1003,6 +1009,8 @@ pub mod llvm {
Name: *c_char); Name: *c_char);
#[fast_ffi] #[fast_ffi]
pub unsafe fn LLVMDisposeBuilder(Builder: BuilderRef); pub unsafe fn LLVMDisposeBuilder(Builder: BuilderRef);
#[fast_ffi]
pub unsafe fn LLVMDisposeExecutionEngine(EE: ExecutionEngineRef);
/* Metadata */ /* Metadata */
#[fast_ffi] #[fast_ffi]
@ -1819,11 +1827,11 @@ pub mod llvm {
/** Execute the JIT engine. */ /** Execute the JIT engine. */
#[fast_ffi] #[fast_ffi]
pub unsafe fn LLVMRustExecuteJIT(MM: *(), pub unsafe fn LLVMRustBuildJIT(MM: *(),
PM: PassManagerRef, PM: PassManagerRef,
M: ModuleRef, M: ModuleRef,
OptLevel: c_int, OptLevel: c_int,
EnableSegmentedStacks: bool) -> *(); EnableSegmentedStacks: bool) -> ExecutionEngineRef;
/** Parses the bitcode in the given memory buffer. */ /** Parses the bitcode in the given memory buffer. */
#[fast_ffi] #[fast_ffi]

View File

@ -3023,7 +3023,7 @@ pub fn trans_crate(sess: session::Session,
tcx: ty::ctxt, tcx: ty::ctxt,
output: &Path, output: &Path,
emap2: resolve::ExportMap2, emap2: resolve::ExportMap2,
maps: astencode::Maps) -> (ModuleRef, LinkMeta) { maps: astencode::Maps) -> (ContextRef, ModuleRef, LinkMeta) {
let symbol_hasher = @mut hash::default_state(); let symbol_hasher = @mut hash::default_state();
let link_meta = link::build_link_meta(sess, crate, output, symbol_hasher); let link_meta = link::build_link_meta(sess, crate, output, symbol_hasher);
@ -3045,9 +3045,11 @@ pub fn trans_crate(sess: session::Session,
let llmod_id = link_meta.name.to_owned() + ".rc"; let llmod_id = link_meta.name.to_owned() + ".rc";
unsafe { unsafe {
if !llvm::LLVMRustStartMultithreading() { // FIXME(#6511): get LLVM building with --enable-threads so this
sess.bug("couldn't enable multi-threaded LLVM"); // function can be called
} // if !llvm::LLVMRustStartMultithreading() {
// sess.bug("couldn't enable multi-threaded LLVM");
// }
let llcx = llvm::LLVMContextCreate(); let llcx = llvm::LLVMContextCreate();
set_task_llcx(llcx); set_task_llcx(llcx);
let llmod = str::as_c_str(llmod_id, |buf| { let llmod = str::as_c_str(llmod_id, |buf| {
@ -3187,7 +3189,8 @@ pub fn trans_crate(sess: session::Session,
io::println(fmt!("%-7u %s", v, k)); io::println(fmt!("%-7u %s", v, k));
} }
} }
return (llmod, link_meta); unset_task_llcx();
return (llcx, llmod, link_meta);
} }
} }
@ -3198,8 +3201,10 @@ pub fn task_llcx() -> ContextRef {
*opt.expect("task-local LLVMContextRef wasn't ever set!") *opt.expect("task-local LLVMContextRef wasn't ever set!")
} }
fn set_task_llcx(c: ContextRef) { unsafe fn set_task_llcx(c: ContextRef) {
unsafe { local_data::local_data_set(task_local_llcx_key, @c);
local_data::local_data_set(task_local_llcx_key, @c); }
}
unsafe fn unset_task_llcx() {
local_data::local_data_pop(task_local_llcx_key);
} }

View File

@ -476,8 +476,10 @@ mod tests {
debug!("regression test for #5784"); debug!("regression test for #5784");
run_cmds(["let a = 1;"]); run_cmds(["let a = 1;"]);
debug!("regression test for #5803"); // XXX: can't spawn new tasks because the JIT code is cleaned up
run_cmds(["spawn( || println(\"Please don't segfault\") );", // after the main function is done.
"do spawn { println(\"Please?\"); }"]); // debug!("regression test for #5803");
// run_cmds(["spawn( || println(\"Please don't segfault\") );",
// "do spawn { println(\"Please?\"); }"]);
} }
} }

View File

@ -329,12 +329,12 @@ LLVMRustLoadCrate(void* mem, const char* crate) {
return true; return true;
} }
extern "C" void* extern "C" LLVMExecutionEngineRef
LLVMRustExecuteJIT(void* mem, LLVMRustBuildJIT(void* mem,
LLVMPassManagerRef PMR, LLVMPassManagerRef PMR,
LLVMModuleRef M, LLVMModuleRef M,
CodeGenOpt::Level OptLevel, CodeGenOpt::Level OptLevel,
bool EnableSegmentedStacks) { bool EnableSegmentedStacks) {
InitializeNativeTarget(); InitializeNativeTarget();
InitializeNativeTargetAsmPrinter(); InitializeNativeTargetAsmPrinter();
@ -371,21 +371,15 @@ LLVMRustExecuteJIT(void* mem,
if(!EE || Err != "") { if(!EE || Err != "") {
LLVMRustError = Err.c_str(); LLVMRustError = Err.c_str();
return 0; // The EngineBuilder only takes ownership of these two structures if the
// create() call is successful, but here it wasn't successful.
LLVMDisposeModule(M);
delete MM;
return NULL;
} }
MM->invalidateInstructionCache(); MM->invalidateInstructionCache();
Function* func = EE->FindFunctionNamed("_rust_main"); return wrap(EE);
if(!func || Err != "") {
LLVMRustError = Err.c_str();
return 0;
}
void* entry = EE->getPointerToFunction(func);
assert(entry);
return entry;
} }
extern "C" bool extern "C" bool

View File

@ -6,13 +6,14 @@ LLVMRustConstSmallInt
LLVMRustConstInt LLVMRustConstInt
LLVMRustLoadCrate LLVMRustLoadCrate
LLVMRustPrepareJIT LLVMRustPrepareJIT
LLVMRustExecuteJIT LLVMRustBuildJIT
LLVMRustParseBitcode LLVMRustParseBitcode
LLVMRustParseAssemblyFile LLVMRustParseAssemblyFile
LLVMRustPrintPassTimings LLVMRustPrintPassTimings
LLVMRustStartMultithreading LLVMRustStartMultithreading
LLVMCreateObjectFile LLVMCreateObjectFile
LLVMDisposeObjectFile LLVMDisposeObjectFile
LLVMDisposeExecutionEngine
LLVMGetSections LLVMGetSections
LLVMDisposeSectionIterator LLVMDisposeSectionIterator
LLVMIsSectionIteratorAtEnd LLVMIsSectionIteratorAtEnd
@ -356,6 +357,7 @@ LLVMGetParamParent
LLVMGetParamTypes LLVMGetParamTypes
LLVMGetParams LLVMGetParams
LLVMGetPointerAddressSpace LLVMGetPointerAddressSpace
LLVMGetPointerToGlobal
LLVMGetPreviousBasicBlock LLVMGetPreviousBasicBlock
LLVMGetPreviousFunction LLVMGetPreviousFunction
LLVMGetPreviousGlobal LLVMGetPreviousGlobal

View File

@ -45,6 +45,7 @@
#include "llvm/Transforms/Vectorize.h" #include "llvm/Transforms/Vectorize.h"
#include "llvm-c/Core.h" #include "llvm-c/Core.h"
#include "llvm-c/BitReader.h" #include "llvm-c/BitReader.h"
#include "llvm-c/ExecutionEngine.h"
#include "llvm-c/Object.h" #include "llvm-c/Object.h"
// Used by RustMCJITMemoryManager::getPointerToNamedFunction() // Used by RustMCJITMemoryManager::getPointerToNamedFunction()