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 driver::session::Session;
use lib::llvm::llvm;
use lib::llvm::{ModuleRef, PassManagerRef};
use lib::llvm::{ModuleRef, PassManagerRef, ContextRef};
use metadata::cstore;
use core::cast;
@ -126,6 +126,7 @@ pub mod jit {
pub fn exec(sess: Session,
pm: PassManagerRef,
c: ContextRef,
m: ModuleRef,
opt: c_int,
stacks: bool) {
@ -154,26 +155,43 @@ pub mod jit {
});
}
// The execute function will return a void pointer
// to the _rust_main function. We can do closure
// magic here to turn it straight into a callable rust
// closure. It will also cleanup the memory manager
// for us.
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();
// We custom-build a JIT execution engine via some rust wrappers
// first. This wrappers takes ownership of the module passed in.
let ee = llvm::LLVMRustBuildJIT(manager, pm, m, opt, stacks);
if ee.is_null() {
llvm::LLVMContextDispose(c);
llvm_err(sess, ~"Could not create the 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| {
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 lib::llvm::llvm;
use lib::llvm::{ModuleRef, mk_pass_manager, mk_target_data};
use lib::llvm::{False, ContextRef};
use lib;
use back::passes;
@ -208,6 +227,7 @@ pub mod write {
}
pub fn run_passes(sess: Session,
llcx: ContextRef,
llmod: ModuleRef,
output_type: output_type,
output: &Path) {
@ -282,7 +302,7 @@ pub mod write {
// JIT execution takes ownership of the module,
// 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() {
llvm::LLVMRustPrintPassTimings();
@ -350,6 +370,7 @@ pub mod write {
// Clean up and return
llvm::LLVMDisposeModule(llmod);
llvm::LLVMContextDispose(llcx);
if sess.time_llvm_passes() {
llvm::LLVMRustPrintPassTimings();
}
@ -368,6 +389,7 @@ pub mod write {
}
llvm::LLVMDisposeModule(llmod);
llvm::LLVMContextDispose(llcx);
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 (llmod, link_meta) = {
let (llcx, llmod, link_meta) = {
crate = time(time_passes, ~"intrinsic injection", ||
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");
time(time_passes, ~"LLVM passes", ||
link::write::run_passes(sess, llmod, output_type,
&obj_filename));
link::write::run_passes(sess, llcx, llmod, output_type,
&obj_filename));
link::write::run_ndk(sess, &obj_filename, &outputs.obj_filename);
} else {
time(time_passes, ~"LLVM passes", ||
link::write::run_passes(sess, llmod, sess.opts.output_type,
&outputs.obj_filename));
link::write::run_passes(sess, llcx, llmod, sess.opts.output_type,
&outputs.obj_filename));
}
let stop_after_codegen =

View File

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

View File

@ -3023,7 +3023,7 @@ pub fn trans_crate(sess: session::Session,
tcx: ty::ctxt,
output: &Path,
emap2: resolve::ExportMap2,
maps: astencode::Maps) -> (ModuleRef, LinkMeta) {
maps: astencode::Maps) -> (ContextRef, ModuleRef, LinkMeta) {
let symbol_hasher = @mut hash::default_state();
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";
unsafe {
if !llvm::LLVMRustStartMultithreading() {
sess.bug("couldn't enable multi-threaded LLVM");
}
// FIXME(#6511): get LLVM building with --enable-threads so this
// function can be called
// if !llvm::LLVMRustStartMultithreading() {
// sess.bug("couldn't enable multi-threaded LLVM");
// }
let llcx = llvm::LLVMContextCreate();
set_task_llcx(llcx);
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));
}
}
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!")
}
fn set_task_llcx(c: ContextRef) {
unsafe {
local_data::local_data_set(task_local_llcx_key, @c);
}
unsafe fn set_task_llcx(c: ContextRef) {
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");
run_cmds(["let a = 1;"]);
debug!("regression test for #5803");
run_cmds(["spawn( || println(\"Please don't segfault\") );",
"do spawn { println(\"Please?\"); }"]);
// XXX: can't spawn new tasks because the JIT code is cleaned up
// after the main function is done.
// 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;
}
extern "C" void*
LLVMRustExecuteJIT(void* mem,
LLVMPassManagerRef PMR,
LLVMModuleRef M,
CodeGenOpt::Level OptLevel,
bool EnableSegmentedStacks) {
extern "C" LLVMExecutionEngineRef
LLVMRustBuildJIT(void* mem,
LLVMPassManagerRef PMR,
LLVMModuleRef M,
CodeGenOpt::Level OptLevel,
bool EnableSegmentedStacks) {
InitializeNativeTarget();
InitializeNativeTargetAsmPrinter();
@ -371,21 +371,15 @@ LLVMRustExecuteJIT(void* mem,
if(!EE || Err != "") {
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();
Function* func = EE->FindFunctionNamed("_rust_main");
if(!func || Err != "") {
LLVMRustError = Err.c_str();
return 0;
}
void* entry = EE->getPointerToFunction(func);
assert(entry);
return entry;
return wrap(EE);
}
extern "C" bool

View File

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

View File

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