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:
parent
779191cd4b
commit
5c5095d25e
|
@ -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(); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 =
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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?\"); }"]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
|
|
Loading…
Reference in New Issue