From 7d8f00f158602fd7ace5b24c747a4449024b3488 Mon Sep 17 00:00:00 2001 From: Zack Corr Date: Sat, 25 Aug 2012 14:54:30 +1000 Subject: [PATCH 01/10] Add experimental JIT compiler --- Makefile.in | 3 +- configure | 2 +- src/rustc/back/link.rs | 26 ++++++++++++- src/rustc/driver/driver.rs | 6 ++- src/rustc/driver/rustc.rs | 1 + src/rustc/driver/session.rs | 2 + src/rustc/lib/llvm.rs | 6 +++ src/rustllvm/RustWrapper.cpp | 71 +++++++++++++++++++++++++++++++----- src/rustllvm/rustllvm.def.in | 2 + 9 files changed, 105 insertions(+), 14 deletions(-) diff --git a/Makefile.in b/Makefile.in index 70da145182b4e..1d575dc3191fe 100644 --- a/Makefile.in +++ b/Makefile.in @@ -216,7 +216,8 @@ RUSTC_INPUTS := $(S)src/rustc/driver/rustc.rs ###################################################################### # FIXME: x86-ism -LLVM_COMPONENTS=x86 ipo bitreader bitwriter linker asmparser +LLVM_COMPONENTS=x86 ipo bitreader bitwriter linker asmparser jit mcjit \ + interpreter define DEF_LLVM_VARS # The configure script defines these variables with the target triples diff --git a/configure b/configure index 040bae9fe60a3..8cc6f02c7dbb1 100755 --- a/configure +++ b/configure @@ -595,7 +595,7 @@ do LLVM_TARGET="--target=$t" # Disable unused LLVM features - LLVM_OPTS="$LLVM_DBG_OPTS --disable-docs --disable-jit \ + LLVM_OPTS="$LLVM_DBG_OPTS --disable-docs \ --enable-bindings=none --disable-threads \ --disable-pthreads" diff --git a/src/rustc/back/link.rs b/src/rustc/back/link.rs index 671962e168cf6..4b98c62ea3ac0 100644 --- a/src/rustc/back/link.rs +++ b/src/rustc/back/link.rs @@ -70,6 +70,7 @@ mod write { // Generate a pre-optimization intermediate file if -save-temps was // specified. + if opts.save_temps { match opts.output_type { output_type_bitcode => { @@ -129,7 +130,7 @@ mod write { llvm::LLVMPassManagerBuilderDispose(MPMB); } if !sess.no_verify() { llvm::LLVMAddVerifierPass(pm.llpm); } - if is_object_or_assembly_or_exe(opts.output_type) { + if is_object_or_assembly_or_exe(opts.output_type) || opts.jit { let LLVMOptNone = 0 as c_int; // -O0 let LLVMOptLess = 1 as c_int; // -O1 let LLVMOptDefault = 2 as c_int; // -O2, -Os @@ -142,6 +143,29 @@ mod write { session::Aggressive => LLVMOptAggressive }; + if opts.jit { + // If we are using JIT, go ahead and create and + // execute the engine now. + + /*llvm::LLVMAddBasicAliasAnalysisPass(pm.llpm); + llvm::LLVMAddInstructionCombiningPass(pm.llpm); + llvm::LLVMAddReassociatePass(pm.llpm); + llvm::LLVMAddGVNPass(pm.llpm); + llvm::LLVMAddCFGSimplificationPass(pm.llpm);*/ + + // JIT execution takes ownership of the module, + // so don't dispose and return. + + if !llvm::LLVMRustJIT(pm.llpm, + llmod, + CodeGenOptLevel, + true) { + llvm_err(sess, ~"Could not JIT"); + } + if sess.time_llvm_passes() { llvm::LLVMRustPrintPassTimings(); } + return; + } + let mut FileType; if opts.output_type == output_type_object || opts.output_type == output_type_exe { diff --git a/src/rustc/driver/driver.rs b/src/rustc/driver/driver.rs index 8023194f3d99b..c9fdb8d4ccb99 100644 --- a/src/rustc/driver/driver.rs +++ b/src/rustc/driver/driver.rs @@ -246,7 +246,8 @@ fn compile_upto(sess: session, cfg: ast::crate_cfg, let stop_after_codegen = sess.opts.output_type != link::output_type_exe || - sess.opts.static && sess.building_library; + (sess.opts.static && sess.building_library) || + sess.opts.jit; if stop_after_codegen { return {crate: crate, tcx: Some(ty_cx)}; } @@ -470,6 +471,7 @@ fn build_session_options(matches: getopts::matches, llvm::LLVMSetDebug(1); } + let jit = opt_present(matches, ~"jit"); let output_type = if parse_only || no_trans { link::output_type_none @@ -532,6 +534,7 @@ fn build_session_options(matches: getopts::matches, extra_debuginfo: extra_debuginfo, lint_opts: lint_opts, save_temps: save_temps, + jit: jit, output_type: output_type, addl_lib_search_paths: addl_lib_search_paths, maybe_sysroot: sysroot_opt, @@ -607,6 +610,7 @@ fn opts() -> ~[getopts::opt] { optopt(~"o"), optopt(~"out-dir"), optflag(~"xg"), optflag(~"c"), optflag(~"g"), optflag(~"save-temps"), optopt(~"sysroot"), optopt(~"target"), + optflag(~"jit"), optmulti(~"W"), optmulti(~"warn"), optmulti(~"A"), optmulti(~"allow"), diff --git a/src/rustc/driver/rustc.rs b/src/rustc/driver/rustc.rs index 927b6b3da2b75..db4a129befba8 100644 --- a/src/rustc/driver/rustc.rs +++ b/src/rustc/driver/rustc.rs @@ -42,6 +42,7 @@ Options: -L Add a directory to the library search path --lib Compile a library crate --ls List the symbols defined by a compiled library crate + --jit Execute using JIT (experimental) --no-trans Run all passes except translation; no output -O Equivalent to --opt-level=2 -o Write output to diff --git a/src/rustc/driver/session.rs b/src/rustc/driver/session.rs index 56d2cc86aa763..97636e1489ed0 100644 --- a/src/rustc/driver/session.rs +++ b/src/rustc/driver/session.rs @@ -90,6 +90,7 @@ type options = extra_debuginfo: bool, lint_opts: ~[(lint::lint, lint::level)], save_temps: bool, + jit: bool, output_type: back::link::output_type, addl_lib_search_paths: ~[Path], maybe_sysroot: Option, @@ -231,6 +232,7 @@ fn basic_options() -> @options { extra_debuginfo: false, lint_opts: ~[], save_temps: false, + jit: false, output_type: link::output_type_exe, addl_lib_search_paths: ~[], maybe_sysroot: None, diff --git a/src/rustc/lib/llvm.rs b/src/rustc/lib/llvm.rs index d441869457805..392d08ea53653 100644 --- a/src/rustc/lib/llvm.rs +++ b/src/rustc/lib/llvm.rs @@ -947,6 +947,12 @@ extern mod llvm { call. */ fn LLVMRustGetLastError() -> *c_char; + /** JIT the module. **/ + fn LLVMRustJIT(PM: PassManagerRef, + M: ModuleRef, + OptLevel: c_int, + EnableSegmentedStacks: bool) -> bool; + /** Parses the bitcode in the given memory buffer. */ fn LLVMRustParseBitcode(MemBuf: MemoryBufferRef) -> ModuleRef; diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index 4a927744f07a0..8f8503a74d497 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -28,6 +28,11 @@ #include "llvm/Target/TargetOptions.h" #include "llvm/Support/Host.h" #include "llvm/Support/Debug.h" +#include "llvm/Support/DynamicLibrary.h" +#include "llvm/ExecutionEngine/ExecutionEngine.h" +#include "llvm/ExecutionEngine/JIT.h" +#include "llvm/ExecutionEngine/Interpreter.h" +#include "llvm/ExecutionEngine/GenericValue.h" #include "llvm-c/Core.h" #include "llvm-c/BitReader.h" #include "llvm-c/Object.h" @@ -68,6 +73,61 @@ void LLVMInitializeX86TargetMC(); void LLVMInitializeX86AsmPrinter(); void LLVMInitializeX86AsmParser(); +// Only initialize the platforms supported by Rust here, +// because using --llvm-root will have multiple platforms +// that rustllvm doesn't actually link to and it's pointless to put target info +// into the registry that Rust can not generate machine code for. + +#define INITIALIZE_TARGETS() LLVMInitializeX86TargetInfo(); \ + LLVMInitializeX86Target(); \ + LLVMInitializeX86TargetMC(); \ + LLVMInitializeX86AsmPrinter(); \ + LLVMInitializeX86AsmParser(); + +extern "C" bool +LLVMRustJIT(LLVMPassManagerRef PMR, + LLVMModuleRef M, + CodeGenOpt::Level OptLevel, + bool EnableSegmentedStacks) { + + INITIALIZE_TARGETS(); + InitializeNativeTarget(); + InitializeNativeTargetAsmPrinter(); + + std::string Err; + TargetOptions Options; + Options.NoFramePointerElim = true; + Options.EnableSegmentedStacks = EnableSegmentedStacks; + + PassManager *PM = unwrap(PMR); + + PM->run(*unwrap(M)); + + ExecutionEngine* EE = EngineBuilder(unwrap(M)) + .setTargetOptions(Options) + .setOptLevel(OptLevel) + .setUseMCJIT(true) + .create(); + + if(!EE || Err != "") { + LLVMRustError = Err.c_str(); + return false; + } + + Function* func = EE->FindFunctionNamed("main"); + + if(!func || Err != "") { + LLVMRustError = Err.c_str(); + return false; + } + + std::vector args; + + EE->runFunction(func, args); + + return true; +} + extern "C" bool LLVMRustWriteOutputFile(LLVMPassManagerRef PMR, LLVMModuleRef M, @@ -77,16 +137,7 @@ LLVMRustWriteOutputFile(LLVMPassManagerRef PMR, CodeGenOpt::Level OptLevel, bool EnableSegmentedStacks) { - // Only initialize the platforms supported by Rust here, - // because using --llvm-root will have multiple platforms - // that rustllvm doesn't actually link to and it's pointless to put target info - // into the registry that Rust can not generate machine code for. - - LLVMInitializeX86TargetInfo(); - LLVMInitializeX86Target(); - LLVMInitializeX86TargetMC(); - LLVMInitializeX86AsmPrinter(); - LLVMInitializeX86AsmParser(); + INITIALIZE_TARGETS(); TargetOptions Options; Options.NoFramePointerElim = true; diff --git a/src/rustllvm/rustllvm.def.in b/src/rustllvm/rustllvm.def.in index 64b560999bec5..2fd4b3fa6f359 100644 --- a/src/rustllvm/rustllvm.def.in +++ b/src/rustllvm/rustllvm.def.in @@ -4,6 +4,7 @@ LLVMRustWriteOutputFile LLVMRustGetLastError LLVMRustConstSmallInt LLVMRustConstInt +LLVMRustJIT LLVMRustParseBitcode LLVMRustParseAssemblyFile LLVMRustPrintPassTimings @@ -485,6 +486,7 @@ LLVMIsThreadLocal LLVMIsUndef LLVMLabelType LLVMLabelTypeInContext +LLVMLinkInInterpreter LLVMMDNode LLVMMDNodeInContext LLVMMDString From a4901b14024042f9e17f6653e934b2d0c01c120b Mon Sep 17 00:00:00 2001 From: Zack Corr Date: Sun, 26 Aug 2012 22:40:21 +1000 Subject: [PATCH 02/10] jit: Add custom memory manager (still segfaulting) --- mk/libuv/ia32/win/src/libuv/uv.target.mk.bak | 167 ------------- src/rustc/back/link.rs | 33 ++- src/rustc/lib/llvm.rs | 10 +- src/rustllvm/RustWrapper.cpp | 245 ++++++++++++++++++- src/rustllvm/rustllvm.def.in | 4 +- 5 files changed, 275 insertions(+), 184 deletions(-) delete mode 100644 mk/libuv/ia32/win/src/libuv/uv.target.mk.bak diff --git a/mk/libuv/ia32/win/src/libuv/uv.target.mk.bak b/mk/libuv/ia32/win/src/libuv/uv.target.mk.bak deleted file mode 100644 index 00b34e4bae5d9..0000000000000 --- a/mk/libuv/ia32/win/src/libuv/uv.target.mk.bak +++ /dev/null @@ -1,167 +0,0 @@ -# This file is generated by gyp; do not edit. - -TOOLSET := target -TARGET := uv -DEFS_Debug := '-DWIN32' \ - '-D_CRT_SECURE_NO_DEPRECATE' \ - '-D_CRT_NONSTDC_NO_DEPRECATE' \ - '-DHAVE_CONFIG_H' \ - '-D_WIN32_WINNT=0x0600' \ - '-DEIO_STACKSIZE=262144' \ - '-D_GNU_SOURCE' \ - '-DDEBUG' \ - '-D_DEBUG' - -# Flags passed to all source files. -CFLAGS_Debug := -g \ - -O0 - -# Flags passed to only C files. -CFLAGS_C_Debug := - -# Flags passed to only C++ files. -CFLAGS_CC_Debug := - -INCS_Debug := -I$(srcdir)\src\libuv\include \ - -I$(srcdir)\src\libuv\include\uv-private \ - -I$(srcdir)\src\libuv\src \ - -I$(srcdir)\src\libuv\src\ares\config_win32 - -DEFS_Release := '-DWIN32' \ - '-D_CRT_SECURE_NO_DEPRECATE' \ - '-D_CRT_NONSTDC_NO_DEPRECATE' \ - '-DHAVE_CONFIG_H' \ - '-D_WIN32_WINNT=0x0600' \ - '-DEIO_STACKSIZE=262144' \ - '-D_GNU_SOURCE' \ - '-DNDEBUG' - -# Flags passed to all source files. -CFLAGS_Release := -O3 \ - -fomit-frame-pointer \ - -fdata-sections \ - -ffunction-sections - -# Flags passed to only C files. -CFLAGS_C_Release := - -# Flags passed to only C++ files. -CFLAGS_CC_Release := - -INCS_Release := -I$(srcdir)\src\libuv\include \ - -I$(srcdir)\src\libuv\include\uv-private \ - -I$(srcdir)\src\libuv\src \ - -I$(srcdir)\src\libuv\src\ares\config_win32 - -OBJS := $(obj).target\$(TARGET)\src\libuv\src\uv-common.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares_cancel.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares__close_sockets.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares_data.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares_destroy.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares_expand_name.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares_expand_string.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares_fds.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares_free_hostent.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares_free_string.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares_gethostbyaddr.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares_gethostbyname.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares__get_hostent.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares_getnameinfo.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares_getopt.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares_getsock.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares_init.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares_library_init.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares_llist.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares_mkquery.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares_nowarn.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares_options.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares_parse_aaaa_reply.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares_parse_a_reply.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares_parse_mx_reply.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares_parse_ns_reply.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares_parse_ptr_reply.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares_parse_srv_reply.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares_parse_txt_reply.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares_process.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares_query.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares__read_line.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares_search.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares_send.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares_strcasecmp.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares_strdup.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares_strerror.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares_timeout.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares__timeval.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares_version.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares_writev.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\bitncmp.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\inet_net_pton.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\inet_ntop.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\windows_port.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares_getenv.o \ - $(obj).target\$(TARGET)\src\libuv\src\ares\ares_platform.o \ - $(obj).target\$(TARGET)\src\libuv\src\win\async.o \ - $(obj).target\$(TARGET)\src\libuv\src\win\cares.o \ - $(obj).target\$(TARGET)\src\libuv\src\win\core.o \ - $(obj).target\$(TARGET)\src\libuv\src\win\dl.o \ - $(obj).target\$(TARGET)\src\libuv\src\win\error.o \ - $(obj).target\$(TARGET)\src\libuv\src\win\fs.o \ - $(obj).target\$(TARGET)\src\libuv\src\win\fs-event.o \ - $(obj).target\$(TARGET)\src\libuv\src\win\getaddrinfo.o \ - $(obj).target\$(TARGET)\src\libuv\src\win\handle.o \ - $(obj).target\$(TARGET)\src\libuv\src\win\loop-watcher.o \ - $(obj).target\$(TARGET)\src\libuv\src\win\pipe.o \ - $(obj).target\$(TARGET)\src\libuv\src\win\thread.o \ - $(obj).target\$(TARGET)\src\libuv\src\win\process.o \ - $(obj).target\$(TARGET)\src\libuv\src\win\req.o \ - $(obj).target\$(TARGET)\src\libuv\src\win\stream.o \ - $(obj).target\$(TARGET)\src\libuv\src\win\tcp.o \ - $(obj).target\$(TARGET)\src\libuv\src\win\tty.o \ - $(obj).target\$(TARGET)\src\libuv\src\win\threadpool.o \ - $(obj).target\$(TARGET)\src\libuv\src\win\timer.o \ - $(obj).target\$(TARGET)\src\libuv\src\win\udp.o \ - $(obj).target\$(TARGET)\src\libuv\src\win\util.o \ - $(obj).target\$(TARGET)\src\libuv\src\win\winapi.o \ - $(obj).target\$(TARGET)\src\libuv\src\win\winsock.o - -# Add to the list of files we specially track dependencies for. -all_deps += $(OBJS) - -# CFLAGS et al overrides must be target-local. -# See "Target-specific Variable Values" in the GNU Make manual. -$(OBJS): TOOLSET := $(TOOLSET) -$(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) -$(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) - -# Suffix rules, putting all outputs into $(obj). - -$(obj).$(TOOLSET)\$(TARGET)\%.o: $(srcdir)\%.c FORCE_DO_CMD - @$(call do_cmd,cc,1) - -# Try building from generated source, too. - -$(obj).$(TOOLSET)\$(TARGET)\%.o: $(obj).$(TOOLSET)\%.c FORCE_DO_CMD - @$(call do_cmd,cc,1) - -$(obj).$(TOOLSET)\$(TARGET)\%.o: $(obj)\%.c FORCE_DO_CMD - @$(call do_cmd,cc,1) - -# End of this set of suffix rules -### Rules for final target. -LDFLAGS_Debug := - -LDFLAGS_Release := - -LIBS := - -$(obj).target\src\libuv\libuv.a: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE)) -$(obj).target\src\libuv\libuv.a: LIBS := $(LIBS) -$(obj).target\src\libuv\libuv.a: TOOLSET := $(TOOLSET) -$(obj).target\src\libuv\libuv.a: $(OBJS) FORCE_DO_CMD - $(call do_cmd,alink) - -all_deps += $(obj).target\src\libuv\libuv.a -# Add target alias -.PHONY: uv -uv: $(obj).target\src\libuv\libuv.a - diff --git a/src/rustc/back/link.rs b/src/rustc/back/link.rs index 4b98c62ea3ac0..d9e5ce3cf92a9 100644 --- a/src/rustc/back/link.rs +++ b/src/rustc/back/link.rs @@ -154,14 +154,37 @@ mod write { llvm::LLVMAddCFGSimplificationPass(pm.llpm);*/ // JIT execution takes ownership of the module, - // so don't dispose and return. + // so don't dispose and return. Due to a weird bug + // with dynamic libraries, we need to separate jitting + // into two functions and load crates inbetween. + + if !llvm::LLVMRustPrepareJIT(pm.llpm, + llmod, + CodeGenOptLevel, + true) { + llvm_err(sess, ~"Could not JIT"); + } + + // We need to tell LLVM where to resolve all linked + // symbols from. The equivalent of -lstd, -lcore, etc. + /*let cstore = sess.cstore; + for cstore::get_used_crate_files(cstore).each |cratepath| { + debug!{"linking: %s", cratepath}; + + let _: () = str::as_c_str( + cratepath, + |buf_t| { + if !llvm::LLVMRustLoadLibrary(buf_t) { + llvm_err(sess, ~"Could not link"); + } + debug!{"linked: %s", cratepath}; + }); + }*/ - if !llvm::LLVMRustJIT(pm.llpm, - llmod, - CodeGenOptLevel, - true) { + if !llvm::LLVMRustExecuteJIT() { llvm_err(sess, ~"Could not JIT"); } + if sess.time_llvm_passes() { llvm::LLVMRustPrintPassTimings(); } return; } diff --git a/src/rustc/lib/llvm.rs b/src/rustc/lib/llvm.rs index 392d08ea53653..5b8bdb89f9205 100644 --- a/src/rustc/lib/llvm.rs +++ b/src/rustc/lib/llvm.rs @@ -947,12 +947,18 @@ extern mod llvm { call. */ fn LLVMRustGetLastError() -> *c_char; - /** JIT the module. **/ - fn LLVMRustJIT(PM: PassManagerRef, + /** Load a shared library to resolve symbols against. */ + fn LLVMRustLoadLibrary(Filename: *c_char) -> bool; + + /** Create the JIT engine. */ + fn LLVMRustPrepareJIT(PM: PassManagerRef, M: ModuleRef, OptLevel: c_int, EnableSegmentedStacks: bool) -> bool; + /** Execute the JIT engine. */ + fn LLVMRustExecuteJIT() -> bool; + /** Parses the bitcode in the given memory buffer. */ fn LLVMRustParseBitcode(MemBuf: MemoryBufferRef) -> ModuleRef; diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index 8f8503a74d497..290f9f135e964 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -29,8 +29,11 @@ #include "llvm/Support/Host.h" #include "llvm/Support/Debug.h" #include "llvm/Support/DynamicLibrary.h" +#include "llvm/Support/Memory.h" #include "llvm/ExecutionEngine/ExecutionEngine.h" #include "llvm/ExecutionEngine/JIT.h" +#include "llvm/ExecutionEngine/JITMemoryManager.h" +#include "llvm/ExecutionEngine/MCJIT.h" #include "llvm/ExecutionEngine/Interpreter.h" #include "llvm/ExecutionEngine/GenericValue.h" #include "llvm-c/Core.h" @@ -38,6 +41,14 @@ #include "llvm-c/Object.h" #include +// Used by RustMCJITMemoryManager::getPointerToNamedFunction() +// to get around glibc issues. See the function for more information. +#ifdef __linux__ +#include +#include +#include +#endif + using namespace llvm; static const char *LLVMRustError; @@ -85,7 +96,208 @@ void LLVMInitializeX86AsmParser(); LLVMInitializeX86AsmParser(); extern "C" bool -LLVMRustJIT(LLVMPassManagerRef PMR, +LLVMRustLoadLibrary(const char* file) { + std::string err; + + if(llvm::sys::DynamicLibrary::LoadLibraryPermanently(file, &err)) { + LLVMRustError = err.c_str(); + return false; + } + + return true; +} + +ExecutionEngine* EE; + +// Custom memory manager for MCJITting. It needs special features +// that the generic JIT memory manager doesn't entail. Based on +// code from LLI, change where needed for Rust. +class RustMCJITMemoryManager : public JITMemoryManager { +public: + SmallVector AllocatedDataMem; + SmallVector AllocatedCodeMem; + SmallVector FreeCodeMem; + + RustMCJITMemoryManager() { } + ~RustMCJITMemoryManager(); + + virtual uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment, + unsigned SectionID); + + virtual uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment, + unsigned SectionID); + + virtual void *getPointerToNamedFunction(const std::string &Name, + bool AbortOnFailure = true); + + // Invalidate instruction cache for code sections. Some platforms with + // separate data cache and instruction cache require explicit cache flush, + // otherwise JIT code manipulations (like resolved relocations) will get to + // the data cache but not to the instruction cache. + virtual void invalidateInstructionCache(); + + // The MCJITMemoryManager doesn't use the following functions, so we don't + // need implement them. + virtual void setMemoryWritable() { + llvm_unreachable("Unimplemented call"); + } + virtual void setMemoryExecutable() { + llvm_unreachable("Unimplemented call"); + } + virtual void setPoisonMemory(bool poison) { + llvm_unreachable("Unimplemented call"); + } + virtual void AllocateGOT() { + llvm_unreachable("Unimplemented call"); + } + virtual uint8_t *getGOTBase() const { + llvm_unreachable("Unimplemented call"); + return 0; + } + virtual uint8_t *startFunctionBody(const Function *F, + uintptr_t &ActualSize){ + llvm_unreachable("Unimplemented call"); + return 0; + } + virtual uint8_t *allocateStub(const GlobalValue* F, unsigned StubSize, + unsigned Alignment) { + llvm_unreachable("Unimplemented call"); + return 0; + } + virtual void endFunctionBody(const Function *F, uint8_t *FunctionStart, + uint8_t *FunctionEnd) { + llvm_unreachable("Unimplemented call"); + } + virtual uint8_t *allocateSpace(intptr_t Size, unsigned Alignment) { + llvm_unreachable("Unimplemented call"); + return 0; + } + virtual uint8_t *allocateGlobal(uintptr_t Size, unsigned Alignment) { + llvm_unreachable("Unimplemented call"); + return 0; + } + virtual void deallocateFunctionBody(void *Body) { + llvm_unreachable("Unimplemented call"); + } + virtual uint8_t* startExceptionTable(const Function* F, + uintptr_t &ActualSize) { + llvm_unreachable("Unimplemented call"); + return 0; + } + virtual void endExceptionTable(const Function *F, uint8_t *TableStart, + uint8_t *TableEnd, uint8_t* FrameRegister) { + llvm_unreachable("Unimplemented call"); + } + virtual void deallocateExceptionTable(void *ET) { + llvm_unreachable("Unimplemented call"); + } +}; + +uint8_t *RustMCJITMemoryManager::allocateDataSection(uintptr_t Size, + unsigned Alignment, + unsigned SectionID) { + if (!Alignment) + Alignment = 16; + uint8_t *Addr = (uint8_t*)calloc((Size + Alignment - 1)/Alignment, Alignment); + AllocatedDataMem.push_back(sys::MemoryBlock(Addr, Size)); + return Addr; +} + +uint8_t *RustMCJITMemoryManager::allocateCodeSection(uintptr_t Size, + unsigned Alignment, + unsigned SectionID) { + if (!Alignment) + Alignment = 16; + unsigned NeedAllocate = Alignment * ((Size + Alignment - 1)/Alignment + 1); + uintptr_t Addr = 0; + // Look in the list of free code memory regions and use a block there if one + // is available. + for (int i = 0, e = FreeCodeMem.size(); i != e; ++i) { + sys::MemoryBlock &MB = FreeCodeMem[i]; + if (MB.size() >= NeedAllocate) { + Addr = (uintptr_t)MB.base(); + uintptr_t EndOfBlock = Addr + MB.size(); + // Align the address. + Addr = (Addr + Alignment - 1) & ~(uintptr_t)(Alignment - 1); + // Store cutted free memory block. + FreeCodeMem[i] = sys::MemoryBlock((void*)(Addr + Size), + EndOfBlock - Addr - Size); + return (uint8_t*)Addr; + } + } + + // No pre-allocated free block was large enough. Allocate a new memory region. + sys::MemoryBlock MB = sys::Memory::AllocateRWX(NeedAllocate, 0, 0); + + AllocatedCodeMem.push_back(MB); + Addr = (uintptr_t)MB.base(); + uintptr_t EndOfBlock = Addr + MB.size(); + // Align the address. + Addr = (Addr + Alignment - 1) & ~(uintptr_t)(Alignment - 1); + // The AllocateRWX may allocate much more memory than we need. In this case, + // we store the unused memory as a free memory block. + unsigned FreeSize = EndOfBlock-Addr-Size; + if (FreeSize > 16) + FreeCodeMem.push_back(sys::MemoryBlock((void*)(Addr + Size), FreeSize)); + + // Return aligned address + return (uint8_t*)Addr; +} + +void RustMCJITMemoryManager::invalidateInstructionCache() { + for (int i = 0, e = AllocatedCodeMem.size(); i != e; ++i) + sys::Memory::InvalidateInstructionCache(AllocatedCodeMem[i].base(), + AllocatedCodeMem[i].size()); +} + +void *RustMCJITMemoryManager::getPointerToNamedFunction(const std::string &Name, + bool AbortOnFailure) { +#ifdef __linux__ + // Force the following functions to be linked in to anything that uses the + // JIT. This is a hack designed to work around the all-too-clever Glibc + // strategy of making these functions work differently when inlined vs. when + // not inlined, and hiding their real definitions in a separate archive file + // that the dynamic linker can't see. For more info, search for + // 'libc_nonshared.a' on Google, or read http://llvm.org/PR274. + if (Name == "stat") return (void*)(intptr_t)&stat; + if (Name == "fstat") return (void*)(intptr_t)&fstat; + if (Name == "lstat") return (void*)(intptr_t)&lstat; + if (Name == "stat64") return (void*)(intptr_t)&stat64; + if (Name == "fstat64") return (void*)(intptr_t)&fstat64; + if (Name == "lstat64") return (void*)(intptr_t)&lstat64; + if (Name == "atexit") return (void*)(intptr_t)&atexit; + if (Name == "mknod") return (void*)(intptr_t)&mknod; +#endif + + const char *NameStr = Name.c_str(); + void *Ptr = sys::DynamicLibrary::SearchForAddressOfSymbol(NameStr); + if (Ptr) return Ptr; + + // If it wasn't found and if it starts with an underscore ('_') character, + // try again without the underscore. + if (NameStr[0] == '_') { + Ptr = sys::DynamicLibrary::SearchForAddressOfSymbol(NameStr+1); + if (Ptr) return Ptr; + } + + if (AbortOnFailure) + report_fatal_error("Program used external function '" + Name + + "' which could not be resolved!"); + return 0; +} + +RustMCJITMemoryManager::~RustMCJITMemoryManager() { + for (unsigned i = 0, e = AllocatedCodeMem.size(); i != e; ++i) + sys::Memory::ReleaseRWX(AllocatedCodeMem[i]); + for (unsigned i = 0, e = AllocatedDataMem.size(); i != e; ++i) + free(AllocatedDataMem[i].base()); +} + +// Separated functions because loading libraries before creating +// an execution engine seems to break stuff. + +extern "C" bool +LLVMRustPrepareJIT(LLVMPassManagerRef PMR, LLVMModuleRef M, CodeGenOpt::Level OptLevel, bool EnableSegmentedStacks) { @@ -96,15 +308,16 @@ LLVMRustJIT(LLVMPassManagerRef PMR, std::string Err; TargetOptions Options; - Options.NoFramePointerElim = true; - Options.EnableSegmentedStacks = EnableSegmentedStacks; + Options.JITEmitDebugInfo = true; + //Options.NoFramePointerElim = true; + //Options.EnableSegmentedStacks = EnableSegmentedStacks; - PassManager *PM = unwrap(PMR); - - PM->run(*unwrap(M)); + unwrap(PMR)->run(*unwrap(M)); - ExecutionEngine* EE = EngineBuilder(unwrap(M)) + RustMCJITMemoryManager* MM = new RustMCJITMemoryManager(); + EE = EngineBuilder(unwrap(M)) .setTargetOptions(Options) + .setJITMemoryManager(MM) .setOptLevel(OptLevel) .setUseMCJIT(true) .create(); @@ -114,6 +327,16 @@ LLVMRustJIT(LLVMPassManagerRef PMR, return false; } + MM->invalidateInstructionCache(); + + return true; +} + +extern "C" bool +LLVMRustExecuteJIT() { + assert(EE); + + std::string Err; Function* func = EE->FindFunctionNamed("main"); if(!func || Err != "") { @@ -121,9 +344,13 @@ LLVMRustJIT(LLVMPassManagerRef PMR, return false; } - std::vector args; + //std::vector args; + typedef int (*entry_t)(int, int); + entry_t entry = (entry_t) EE->getPointerToFunction(func); - EE->runFunction(func, args); + assert(entry); + entry(0, 0); + //EE->runFunction(func, args); return true; } diff --git a/src/rustllvm/rustllvm.def.in b/src/rustllvm/rustllvm.def.in index 2fd4b3fa6f359..0cb047ad5d03c 100644 --- a/src/rustllvm/rustllvm.def.in +++ b/src/rustllvm/rustllvm.def.in @@ -4,7 +4,9 @@ LLVMRustWriteOutputFile LLVMRustGetLastError LLVMRustConstSmallInt LLVMRustConstInt -LLVMRustJIT +LLVMRustLoadLibrary +LLVMRustPrepareJIT +LLVMRustExecuteJIT LLVMRustParseBitcode LLVMRustParseAssemblyFile LLVMRustPrintPassTimings From b9a3bf60b263939111ca5aa643e1a6ad38bab191 Mon Sep 17 00:00:00 2001 From: Zack Corr Date: Tue, 28 Aug 2012 09:51:30 +1000 Subject: [PATCH 03/10] jit: Link in __morestack and make it resolvable by JIT --- mk/rustllvm.mk | 3 ++- src/rustllvm/RustWrapper.cpp | 16 +++++++--------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/mk/rustllvm.mk b/mk/rustllvm.mk index 622f7d4fa090a..804a2cec5f2b1 100644 --- a/mk/rustllvm.mk +++ b/mk/rustllvm.mk @@ -23,10 +23,11 @@ RUSTLLVM_OBJS_OBJS_$(1) := $$(RUSTLLVM_OBJS_CS_$(1):rustllvm/%.cpp=rustllvm/$(1) ALL_OBJ_FILES += $$(RUSTLLVM_OBJS_OBJS_$(1)) rustllvm/$(1)/$(CFG_RUSTLLVM): $$(RUSTLLVM_OBJS_OBJS_$(1)) \ + rt/$(1)/arch/$$(HOST_$(1))/libmorestack.a \ $$(MKFILE_DEPS) $$(RUSTLLVM_DEF_$(1)) @$$(call E, link: $$@) $$(Q)$$(call CFG_LINK_C_$(1),$$@,$$(RUSTLLVM_OBJS_OBJS_$(1)) \ - $$(CFG_GCCISH_PRE_LIB_FLAGS) $$(LLVM_LIBS_$(1)) \ + $$(CFG_GCCISH_PRE_LIB_FLAGS) $$(LLVM_LIBS_$(1)) rt/$(1)/arch/$$(HOST_$(1))/libmorestack.a \ $$(CFG_GCCISH_POST_LIB_FLAGS) \ $$(LLVM_LDFLAGS_$(1)),$$(RUSTLLVM_DEF_$(1)),$$(CFG_RUSTLLVM)) diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index 290f9f135e964..796c054e0781c 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -49,6 +49,9 @@ #include #endif +// Does this need to be done, or can it be made to resolve from the main program? +extern "C" void __morestack(void *args, void *fn_ptr, uintptr_t stack_ptr); + using namespace llvm; static const char *LLVMRustError; @@ -269,17 +272,12 @@ void *RustMCJITMemoryManager::getPointerToNamedFunction(const std::string &Name, if (Name == "mknod") return (void*)(intptr_t)&mknod; #endif + if (Name == "__morestack") return (void*)(intptr_t)&__morestack; + const char *NameStr = Name.c_str(); void *Ptr = sys::DynamicLibrary::SearchForAddressOfSymbol(NameStr); if (Ptr) return Ptr; - // If it wasn't found and if it starts with an underscore ('_') character, - // try again without the underscore. - if (NameStr[0] == '_') { - Ptr = sys::DynamicLibrary::SearchForAddressOfSymbol(NameStr+1); - if (Ptr) return Ptr; - } - if (AbortOnFailure) report_fatal_error("Program used external function '" + Name + "' which could not be resolved!"); @@ -309,8 +307,8 @@ LLVMRustPrepareJIT(LLVMPassManagerRef PMR, std::string Err; TargetOptions Options; Options.JITEmitDebugInfo = true; - //Options.NoFramePointerElim = true; - //Options.EnableSegmentedStacks = EnableSegmentedStacks; + Options.NoFramePointerElim = true; + Options.EnableSegmentedStacks = EnableSegmentedStacks; unwrap(PMR)->run(*unwrap(M)); From f37fbc7ee579b790bf5ed0da0df31e94acb37142 Mon Sep 17 00:00:00 2001 From: Zack Corr Date: Tue, 28 Aug 2012 11:39:34 +1000 Subject: [PATCH 04/10] jit: Add passes and cleanup code --- src/rustc/back/link.rs | 26 ++++++++------------------ src/rustc/lib/llvm.rs | 7 ++----- src/rustllvm/RustWrapper.cpp | 34 +++++++++++++++------------------- src/rustllvm/rustllvm.def.in | 3 +-- 4 files changed, 26 insertions(+), 44 deletions(-) diff --git a/src/rustc/back/link.rs b/src/rustc/back/link.rs index d9e5ce3cf92a9..098b73bc1099a 100644 --- a/src/rustc/back/link.rs +++ b/src/rustc/back/link.rs @@ -146,27 +146,14 @@ mod write { if opts.jit { // If we are using JIT, go ahead and create and // execute the engine now. - - /*llvm::LLVMAddBasicAliasAnalysisPass(pm.llpm); - llvm::LLVMAddInstructionCombiningPass(pm.llpm); - llvm::LLVMAddReassociatePass(pm.llpm); - llvm::LLVMAddGVNPass(pm.llpm); - llvm::LLVMAddCFGSimplificationPass(pm.llpm);*/ - // JIT execution takes ownership of the module, - // so don't dispose and return. Due to a weird bug - // with dynamic libraries, we need to separate jitting - // into two functions and load crates inbetween. - - if !llvm::LLVMRustPrepareJIT(pm.llpm, - llmod, - CodeGenOptLevel, - true) { - llvm_err(sess, ~"Could not JIT"); - } + // so don't dispose and return. // We need to tell LLVM where to resolve all linked // symbols from. The equivalent of -lstd, -lcore, etc. + // By default the JIT will resolve symbols from the std and + // core linked into rustc. We don't want that, + // incase the user wants to use an older std library. /*let cstore = sess.cstore; for cstore::get_used_crate_files(cstore).each |cratepath| { debug!{"linking: %s", cratepath}; @@ -181,7 +168,10 @@ mod write { }); }*/ - if !llvm::LLVMRustExecuteJIT() { + if !llvm::LLVMRustJIT(pm.llpm, + llmod, + CodeGenOptLevel, + true) { llvm_err(sess, ~"Could not JIT"); } diff --git a/src/rustc/lib/llvm.rs b/src/rustc/lib/llvm.rs index 5b8bdb89f9205..527e612c3a11f 100644 --- a/src/rustc/lib/llvm.rs +++ b/src/rustc/lib/llvm.rs @@ -950,15 +950,12 @@ extern mod llvm { /** Load a shared library to resolve symbols against. */ fn LLVMRustLoadLibrary(Filename: *c_char) -> bool; - /** Create the JIT engine. */ - fn LLVMRustPrepareJIT(PM: PassManagerRef, + /** Create and execute the JIT engine. */ + fn LLVMRustJIT(PM: PassManagerRef, M: ModuleRef, OptLevel: c_int, EnableSegmentedStacks: bool) -> bool; - /** Execute the JIT engine. */ - fn LLVMRustExecuteJIT() -> bool; - /** Parses the bitcode in the given memory buffer. */ fn LLVMRustParseBitcode(MemBuf: MemoryBufferRef) -> ModuleRef; diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index 796c054e0781c..92675fd11fbde 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -15,6 +15,9 @@ #include "llvm/LLVMContext.h" #include "llvm/Linker.h" #include "llvm/PassManager.h" +#include "llvm/Analysis/Verifier.h" +#include "llvm/Analysis/Passes.h" +#include "llvm/Transforms/Scalar.h" #include "llvm/ADT/Triple.h" #include "llvm/Assembly/Parser.h" #include "llvm/Assembly/PrintModulePass.h" @@ -35,7 +38,6 @@ #include "llvm/ExecutionEngine/JITMemoryManager.h" #include "llvm/ExecutionEngine/MCJIT.h" #include "llvm/ExecutionEngine/Interpreter.h" -#include "llvm/ExecutionEngine/GenericValue.h" #include "llvm-c/Core.h" #include "llvm-c/BitReader.h" #include "llvm-c/Object.h" @@ -291,11 +293,8 @@ RustMCJITMemoryManager::~RustMCJITMemoryManager() { free(AllocatedDataMem[i].base()); } -// Separated functions because loading libraries before creating -// an execution engine seems to break stuff. - extern "C" bool -LLVMRustPrepareJIT(LLVMPassManagerRef PMR, +LLVMRustJIT(LLVMPassManagerRef PMR, LLVMModuleRef M, CodeGenOpt::Level OptLevel, bool EnableSegmentedStacks) { @@ -309,8 +308,16 @@ LLVMRustPrepareJIT(LLVMPassManagerRef PMR, Options.JITEmitDebugInfo = true; Options.NoFramePointerElim = true; Options.EnableSegmentedStacks = EnableSegmentedStacks; + PassManager *PM = unwrap(PMR); - unwrap(PMR)->run(*unwrap(M)); + PM->add(createBasicAliasAnalysisPass()); + PM->add(createInstructionCombiningPass()); + PM->add(createReassociatePass()); + PM->add(createGVNPass()); + PM->add(createPromoteMemoryToRegisterPass()); + PM->add(createCFGSimplificationPass()); + PM->add(createFunctionInliningPass()); + PM->run(*unwrap(M)); RustMCJITMemoryManager* MM = new RustMCJITMemoryManager(); EE = EngineBuilder(unwrap(M)) @@ -326,15 +333,6 @@ LLVMRustPrepareJIT(LLVMPassManagerRef PMR, } MM->invalidateInstructionCache(); - - return true; -} - -extern "C" bool -LLVMRustExecuteJIT() { - assert(EE); - - std::string Err; Function* func = EE->FindFunctionNamed("main"); if(!func || Err != "") { @@ -342,13 +340,11 @@ LLVMRustExecuteJIT() { return false; } - //std::vector args; - typedef int (*entry_t)(int, int); - entry_t entry = (entry_t) EE->getPointerToFunction(func); + typedef int (*Entry)(int, int); + Entry entry = (Entry) EE->getPointerToFunction(func); assert(entry); entry(0, 0); - //EE->runFunction(func, args); return true; } diff --git a/src/rustllvm/rustllvm.def.in b/src/rustllvm/rustllvm.def.in index 0cb047ad5d03c..1de1e3ba58f2c 100644 --- a/src/rustllvm/rustllvm.def.in +++ b/src/rustllvm/rustllvm.def.in @@ -5,8 +5,7 @@ LLVMRustGetLastError LLVMRustConstSmallInt LLVMRustConstInt LLVMRustLoadLibrary -LLVMRustPrepareJIT -LLVMRustExecuteJIT +LLVMRustJIT LLVMRustParseBitcode LLVMRustParseAssemblyFile LLVMRustPrintPassTimings From ca9c7b2895e017671f428e8d0a63da5ecdf597f5 Mon Sep 17 00:00:00 2001 From: Zack Corr Date: Tue, 28 Aug 2012 11:42:14 +1000 Subject: [PATCH 05/10] jit: Forgot header for inlining pass --- src/rustllvm/RustWrapper.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index 92675fd11fbde..0d19ccfeb36b7 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -18,6 +18,7 @@ #include "llvm/Analysis/Verifier.h" #include "llvm/Analysis/Passes.h" #include "llvm/Transforms/Scalar.h" +#include "llvm/Transforms/IPO.h" #include "llvm/ADT/Triple.h" #include "llvm/Assembly/Parser.h" #include "llvm/Assembly/PrintModulePass.h" From 9bb97d72f92d0c7eb6ffba757c6054a81552a647 Mon Sep 17 00:00:00 2001 From: Zack Corr Date: Tue, 28 Aug 2012 12:01:43 +1000 Subject: [PATCH 06/10] jit: correct formatting --- src/rustc/back/link.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/rustc/back/link.rs b/src/rustc/back/link.rs index 098b73bc1099a..67b70af4d44bc 100644 --- a/src/rustc/back/link.rs +++ b/src/rustc/back/link.rs @@ -175,7 +175,9 @@ mod write { llvm_err(sess, ~"Could not JIT"); } - if sess.time_llvm_passes() { llvm::LLVMRustPrintPassTimings(); } + if sess.time_llvm_passes() { + llvm::LLVMRustPrintPassTimings(); + } return; } From b74e62506c8dd767c616291b832b38fae04f59cf Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Tue, 28 Aug 2012 16:27:42 -0700 Subject: [PATCH 07/10] Add a morestack_addr (temporary) intrinsic --- src/rustc/middle/trans/foreign.rs | 10 ++++++++++ src/rustc/middle/typeck/check.rs | 3 +++ src/test/run-pass/morestack-address.rs | 11 +++++++++++ 3 files changed, 24 insertions(+) create mode 100644 src/test/run-pass/morestack-address.rs diff --git a/src/rustc/middle/trans/foreign.rs b/src/rustc/middle/trans/foreign.rs index d760e47296e17..4c39265c2163e 100644 --- a/src/rustc/middle/trans/foreign.rs +++ b/src/rustc/middle/trans/foreign.rs @@ -984,6 +984,16 @@ fn trans_intrinsic(ccx: @crate_ctxt, decl: ValueRef, item: @ast::foreign_item, lv_temporary), arg_vals(~[frameaddress_val]), ignore); } + ~"morestack_addr" => { + // XXX This is a hack to grab the address of this particular + // native function. There should be a general in-language + // way to do this + let llfty = type_of_fn(bcx.ccx(), ~[], ty::mk_nil(bcx.tcx())); + let morestack_addr = decl_cdecl_fn( + bcx.ccx().llmod, ~"__morestack", llfty); + let morestack_addr = PointerCast(bcx, morestack_addr, T_ptr(T_nil())); + Store(bcx, morestack_addr, fcx.llretptr); + } _ => { // Could we make this an enum rather than a string? does it get // checked earlier? diff --git a/src/rustc/middle/typeck/check.rs b/src/rustc/middle/typeck/check.rs index bc9762a43e54d..727210406ebcf 100644 --- a/src/rustc/middle/typeck/check.rs +++ b/src/rustc/middle/typeck/check.rs @@ -2562,6 +2562,9 @@ fn check_intrinsic_type(ccx: @crate_ctxt, it: @ast::foreign_item) { }); (0u, ~[arg(ast::by_ref, fty)], ty::mk_nil(tcx)) } + ~"morestack_addr" => { + (0u, ~[], ty::mk_nil_ptr(tcx)) + } other => { tcx.sess.span_err(it.span, ~"unrecognized intrinsic function: `" + other + ~"`"); diff --git a/src/test/run-pass/morestack-address.rs b/src/test/run-pass/morestack-address.rs new file mode 100644 index 0000000000000..7f0adcf0920ff --- /dev/null +++ b/src/test/run-pass/morestack-address.rs @@ -0,0 +1,11 @@ +#[nolink] +#[abi = "rust-intrinsic"] +extern mod rusti { + fn morestack_addr() -> *(); +} + +fn main() { + let addr = rusti::morestack_addr(); + assert addr.is_not_null(); + error!("%?", addr); +} \ No newline at end of file From 25a0f749986c9061c89c9fe177c01acd5767806d Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Tue, 28 Aug 2012 17:10:37 -0700 Subject: [PATCH 08/10] Add JIT testing to compiletest with --jit --- src/compiletest/common.rs | 7 ++++++- src/compiletest/compiletest.rs | 5 ++++- src/compiletest/runtest.rs | 35 ++++++++++++++++++++++++++-------- 3 files changed, 37 insertions(+), 10 deletions(-) diff --git a/src/compiletest/common.rs b/src/compiletest/common.rs index b5a20f49de43b..b39e22cb90a12 100644 --- a/src/compiletest/common.rs +++ b/src/compiletest/common.rs @@ -43,5 +43,10 @@ type config = { // Flags to pass to the compiler rustcflags: Option<~str>, + // Run tests using the JIT + jit: bool, + // Explain what's going on - verbose: bool}; + verbose: bool + +}; diff --git a/src/compiletest/compiletest.rs b/src/compiletest/compiletest.rs index ee99b32e89383..5ef0c8fece871 100644 --- a/src/compiletest/compiletest.rs +++ b/src/compiletest/compiletest.rs @@ -32,7 +32,8 @@ fn parse_config(args: ~[~str]) -> config { getopts::reqopt(~"mode"), getopts::optflag(~"ignored"), getopts::optopt(~"runtool"), getopts::optopt(~"rustcflags"), getopts::optflag(~"verbose"), - getopts::optopt(~"logfile")]; + getopts::optopt(~"logfile"), + getopts::optflag(~"jit")]; assert (vec::is_not_empty(args)); let args_ = vec::tail(args); @@ -64,6 +65,7 @@ fn parse_config(args: ~[~str]) -> config { |s| Path(s)), runtool: getopts::opt_maybe_str(matches, ~"runtool"), rustcflags: getopts::opt_maybe_str(matches, ~"rustcflags"), + jit: getopts::opt_present(matches, ~"jit"), verbose: getopts::opt_present(matches, ~"verbose")}; } @@ -81,6 +83,7 @@ fn log_config(config: config) { logv(c, fmt!("filter: %s", opt_str(config.filter))); logv(c, fmt!("runtool: %s", opt_str(config.runtool))); logv(c, fmt!("rustcflags: %s", opt_str(config.rustcflags))); + logv(c, fmt!("jit: %b", config.jit)); logv(c, fmt!("verbose: %b", config.verbose)); logv(c, fmt!("\n")); } diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs index f93dda87f3820..e456b4469fa20 100644 --- a/src/compiletest/runtest.rs +++ b/src/compiletest/runtest.rs @@ -48,11 +48,15 @@ fn run_cfail_test(config: config, props: test_props, testfile: &Path) { } fn run_rfail_test(config: config, props: test_props, testfile: &Path) { - let mut procres = compile_test(config, props, testfile); + let procres = if !config.jit { + let procres = compile_test(config, props, testfile); - if procres.status != 0 { fatal_procres(~"compilation failed!", procres); } + if procres.status != 0 { fatal_procres(~"compilation failed!", procres); } - procres = exec_compiled_test(config, props, testfile); + exec_compiled_test(config, props, testfile) + } else { + jit_test(config, props, testfile) + }; // The value our Makefile configures valgrind to return on failure const valgrind_err: int = 100; @@ -76,13 +80,19 @@ fn check_correct_failure_status(procres: procres) { } fn run_rpass_test(config: config, props: test_props, testfile: &Path) { - let mut procres = compile_test(config, props, testfile); + if !config.jit { + let mut procres = compile_test(config, props, testfile); + + if procres.status != 0 { fatal_procres(~"compilation failed!", procres); } - if procres.status != 0 { fatal_procres(~"compilation failed!", procres); } + procres = exec_compiled_test(config, props, testfile); - procres = exec_compiled_test(config, props, testfile); + if procres.status != 0 { fatal_procres(~"test run failed!", procres); } + } else { + let mut procres = jit_test(config, props, testfile); - if procres.status != 0 { fatal_procres(~"test run failed!", procres); } + if procres.status != 0 { fatal_procres(~"jit failed!", procres); } + } } fn run_pretty_test(config: config, props: test_props, testfile: &Path) { @@ -295,10 +305,19 @@ type procres = {status: int, stdout: ~str, stderr: ~str, cmdline: ~str}; fn compile_test(config: config, props: test_props, testfile: &Path) -> procres { + compile_test_(config, props, testfile, []) +} + +fn jit_test(config: config, props: test_props, testfile: &Path) -> procres { + compile_test_(config, props, testfile, [~"--jit"]) +} + +fn compile_test_(config: config, props: test_props, + testfile: &Path, extra_args: &[~str]) -> procres { let link_args = ~[~"-L", aux_output_dir_name(config, testfile).to_str()]; compose_and_run_compiler( config, props, testfile, - make_compile_args(config, props, link_args, + make_compile_args(config, props, link_args + extra_args, make_exe_name, testfile), None) } From fd89f057d58c12845c95d3288b1056925a04839d Mon Sep 17 00:00:00 2001 From: Zack Corr Date: Wed, 29 Aug 2012 15:49:35 +1000 Subject: [PATCH 09/10] jit: Clean rustllvm code, let rustc expose __morestack instead of linking in libmorestack and return _rust_main and call it from rustc --- mk/rustllvm.mk | 3 +- src/rustc/back/link.rs | 60 +++++++++++++++++++++++++++++++----- src/rustc/lib/llvm.rs | 5 +-- src/rustllvm/RustWrapper.cpp | 50 ++++++++++++++---------------- 4 files changed, 80 insertions(+), 38 deletions(-) diff --git a/mk/rustllvm.mk b/mk/rustllvm.mk index 804a2cec5f2b1..622f7d4fa090a 100644 --- a/mk/rustllvm.mk +++ b/mk/rustllvm.mk @@ -23,11 +23,10 @@ RUSTLLVM_OBJS_OBJS_$(1) := $$(RUSTLLVM_OBJS_CS_$(1):rustllvm/%.cpp=rustllvm/$(1) ALL_OBJ_FILES += $$(RUSTLLVM_OBJS_OBJS_$(1)) rustllvm/$(1)/$(CFG_RUSTLLVM): $$(RUSTLLVM_OBJS_OBJS_$(1)) \ - rt/$(1)/arch/$$(HOST_$(1))/libmorestack.a \ $$(MKFILE_DEPS) $$(RUSTLLVM_DEF_$(1)) @$$(call E, link: $$@) $$(Q)$$(call CFG_LINK_C_$(1),$$@,$$(RUSTLLVM_OBJS_OBJS_$(1)) \ - $$(CFG_GCCISH_PRE_LIB_FLAGS) $$(LLVM_LIBS_$(1)) rt/$(1)/arch/$$(HOST_$(1))/libmorestack.a \ + $$(CFG_GCCISH_PRE_LIB_FLAGS) $$(LLVM_LIBS_$(1)) \ $$(CFG_GCCISH_POST_LIB_FLAGS) \ $$(LLVM_LDFLAGS_$(1)),$$(RUSTLLVM_DEF_$(1)),$$(CFG_RUSTLLVM)) diff --git a/src/rustc/back/link.rs b/src/rustc/back/link.rs index 67b70af4d44bc..d7a9893bce249 100644 --- a/src/rustc/back/link.rs +++ b/src/rustc/back/link.rs @@ -12,7 +12,7 @@ import std::sha1::sha1; import syntax::ast; import syntax::print::pprust; import lib::llvm::{ModuleRef, mk_pass_manager, mk_target_data, True, False, - FileType}; + PassManagerRef, FileType}; import metadata::filesearch; import syntax::ast_map::{path, path_mod, path_name}; import io::{Writer, WriterUtil}; @@ -48,6 +48,57 @@ fn WriteOutputFile(sess:session, } } +#[cfg(stage0)] +mod jit { + fn exec(_sess: session, + _pm: PassManagerRef, + _m: ModuleRef, + _opt: c_int, + _stacks: bool) { + fail + } +} + +#[cfg(stage1)] +#[cfg(stage2)] +#[cfg(stage3)] +mod jit { + #[nolink] + #[abi = "rust-intrinsic"] + extern mod rusti { + fn morestack_addr() -> *(); + } + + struct Closure { + code: *(); + env: *(); + } + + fn exec(sess: session, + pm: PassManagerRef, + m: ModuleRef, + opt: c_int, + stacks: bool) unsafe { + let ptr = llvm::LLVMRustJIT(rusti::morestack_addr(), pm, m, opt, stacks); + + if ptr::is_null(ptr) { + llvm_err(sess, ~"Could not JIT"); + } else { + let bin = match os::self_exe_path() { + Some(path) => path.to_str(), + _ => ~"rustc" + }; + let closure = Closure { + code: ptr, + env: ptr::null() + }; + let func: fn(~[~str]) = unsafe::transmute(closure); + + func(~[bin]); + } + } +} + mod write { fn is_object_or_assembly_or_exe(ot: output_type) -> bool { if ot == output_type_assembly || ot == output_type_object || @@ -168,12 +219,7 @@ mod write { }); }*/ - if !llvm::LLVMRustJIT(pm.llpm, - llmod, - CodeGenOptLevel, - true) { - llvm_err(sess, ~"Could not JIT"); - } + jit::exec(sess, pm.llpm, llmod, CodeGenOptLevel, true); if sess.time_llvm_passes() { llvm::LLVMRustPrintPassTimings(); diff --git a/src/rustc/lib/llvm.rs b/src/rustc/lib/llvm.rs index 527e612c3a11f..441837dc0c6ff 100644 --- a/src/rustc/lib/llvm.rs +++ b/src/rustc/lib/llvm.rs @@ -951,10 +951,11 @@ extern mod llvm { fn LLVMRustLoadLibrary(Filename: *c_char) -> bool; /** Create and execute the JIT engine. */ - fn LLVMRustJIT(PM: PassManagerRef, + fn LLVMRustJIT(__morestack: *(), + PM: PassManagerRef, M: ModuleRef, OptLevel: c_int, - EnableSegmentedStacks: bool) -> bool; + EnableSegmentedStacks: bool) -> *(); /** Parses the bitcode in the given memory buffer. */ fn LLVMRustParseBitcode(MemBuf: MemoryBufferRef) -> ModuleRef; diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index 0d19ccfeb36b7..6ea433e6f1f05 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -52,9 +52,6 @@ #include #endif -// Does this need to be done, or can it be made to resolve from the main program? -extern "C" void __morestack(void *args, void *fn_ptr, uintptr_t stack_ptr); - using namespace llvm; static const char *LLVMRustError; @@ -95,11 +92,13 @@ void LLVMInitializeX86AsmParser(); // that rustllvm doesn't actually link to and it's pointless to put target info // into the registry that Rust can not generate machine code for. -#define INITIALIZE_TARGETS() LLVMInitializeX86TargetInfo(); \ - LLVMInitializeX86Target(); \ - LLVMInitializeX86TargetMC(); \ - LLVMInitializeX86AsmPrinter(); \ - LLVMInitializeX86AsmParser(); +void LLVMRustInitializeTargets() { + LLVMInitializeX86TargetInfo(); + LLVMInitializeX86Target(); + LLVMInitializeX86TargetMC(); + LLVMInitializeX86AsmPrinter(); + LLVMInitializeX86AsmParser(); +} extern "C" bool LLVMRustLoadLibrary(const char* file) { @@ -113,8 +112,6 @@ LLVMRustLoadLibrary(const char* file) { return true; } -ExecutionEngine* EE; - // Custom memory manager for MCJITting. It needs special features // that the generic JIT memory manager doesn't entail. Based on // code from LLI, change where needed for Rust. @@ -123,8 +120,9 @@ class RustMCJITMemoryManager : public JITMemoryManager { SmallVector AllocatedDataMem; SmallVector AllocatedCodeMem; SmallVector FreeCodeMem; + void* __morestack; - RustMCJITMemoryManager() { } + RustMCJITMemoryManager(void* sym) : __morestack(sym) { } ~RustMCJITMemoryManager(); virtual uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment, @@ -275,7 +273,7 @@ void *RustMCJITMemoryManager::getPointerToNamedFunction(const std::string &Name, if (Name == "mknod") return (void*)(intptr_t)&mknod; #endif - if (Name == "__morestack") return (void*)(intptr_t)&__morestack; + if (Name == "__morestack") return &__morestack; const char *NameStr = Name.c_str(); void *Ptr = sys::DynamicLibrary::SearchForAddressOfSymbol(NameStr); @@ -294,13 +292,13 @@ RustMCJITMemoryManager::~RustMCJITMemoryManager() { free(AllocatedDataMem[i].base()); } -extern "C" bool -LLVMRustJIT(LLVMPassManagerRef PMR, +extern "C" void* +LLVMRustJIT(void* __morestack, + LLVMPassManagerRef PMR, LLVMModuleRef M, CodeGenOpt::Level OptLevel, bool EnableSegmentedStacks) { - INITIALIZE_TARGETS(); InitializeNativeTarget(); InitializeNativeTargetAsmPrinter(); @@ -315,39 +313,37 @@ LLVMRustJIT(LLVMPassManagerRef PMR, PM->add(createInstructionCombiningPass()); PM->add(createReassociatePass()); PM->add(createGVNPass()); - PM->add(createPromoteMemoryToRegisterPass()); PM->add(createCFGSimplificationPass()); PM->add(createFunctionInliningPass()); + PM->add(createPromoteMemoryToRegisterPass()); PM->run(*unwrap(M)); - RustMCJITMemoryManager* MM = new RustMCJITMemoryManager(); - EE = EngineBuilder(unwrap(M)) + RustMCJITMemoryManager* MM = new RustMCJITMemoryManager(__morestack); + ExecutionEngine* EE = EngineBuilder(unwrap(M)) .setTargetOptions(Options) .setJITMemoryManager(MM) .setOptLevel(OptLevel) .setUseMCJIT(true) + .setAllocateGVsWithCode(false) .create(); if(!EE || Err != "") { LLVMRustError = Err.c_str(); - return false; + return 0; } MM->invalidateInstructionCache(); - Function* func = EE->FindFunctionNamed("main"); + Function* func = EE->FindFunctionNamed("_rust_main"); if(!func || Err != "") { LLVMRustError = Err.c_str(); - return false; + return 0; } - typedef int (*Entry)(int, int); - Entry entry = (Entry) EE->getPointerToFunction(func); - + void* entry = EE->getPointerToFunction(func); assert(entry); - entry(0, 0); - return true; + return entry; } extern "C" bool @@ -359,7 +355,7 @@ LLVMRustWriteOutputFile(LLVMPassManagerRef PMR, CodeGenOpt::Level OptLevel, bool EnableSegmentedStacks) { - INITIALIZE_TARGETS(); + LLVMRustInitializeTargets(); TargetOptions Options; Options.NoFramePointerElim = true; From f106761eaf3b051149bdf8411995f7ed341f2eee Mon Sep 17 00:00:00 2001 From: Zack Corr Date: Thu, 30 Aug 2012 12:24:43 +1000 Subject: [PATCH 10/10] jit: Correct formatting and argv[0] for JITted programs --- src/compiletest/runtest.rs | 12 +++++++++--- src/rustc/back/link.rs | 9 +++------ src/rustc/driver/driver.rs | 4 +++- src/rustc/driver/rustc.rs | 2 +- src/rustc/driver/session.rs | 2 ++ 5 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs index e456b4469fa20..65835e9b470ac 100644 --- a/src/compiletest/runtest.rs +++ b/src/compiletest/runtest.rs @@ -51,7 +51,9 @@ fn run_rfail_test(config: config, props: test_props, testfile: &Path) { let procres = if !config.jit { let procres = compile_test(config, props, testfile); - if procres.status != 0 { fatal_procres(~"compilation failed!", procres); } + if procres.status != 0 { + fatal_procres(~"compilation failed!", procres); + } exec_compiled_test(config, props, testfile) } else { @@ -83,11 +85,15 @@ fn run_rpass_test(config: config, props: test_props, testfile: &Path) { if !config.jit { let mut procres = compile_test(config, props, testfile); - if procres.status != 0 { fatal_procres(~"compilation failed!", procres); } + if procres.status != 0 { + fatal_procres(~"compilation failed!", procres); + } procres = exec_compiled_test(config, props, testfile); - if procres.status != 0 { fatal_procres(~"test run failed!", procres); } + if procres.status != 0 { + fatal_procres(~"test run failed!", procres); + } } else { let mut procres = jit_test(config, props, testfile); diff --git a/src/rustc/back/link.rs b/src/rustc/back/link.rs index d7a9893bce249..6fae0fd9e50f9 100644 --- a/src/rustc/back/link.rs +++ b/src/rustc/back/link.rs @@ -79,22 +79,19 @@ mod jit { m: ModuleRef, opt: c_int, stacks: bool) unsafe { - let ptr = llvm::LLVMRustJIT(rusti::morestack_addr(), pm, m, opt, stacks); + let ptr = llvm::LLVMRustJIT(rusti::morestack_addr(), + pm, m, opt, stacks); if ptr::is_null(ptr) { llvm_err(sess, ~"Could not JIT"); } else { - let bin = match os::self_exe_path() { - Some(path) => path.to_str(), - _ => ~"rustc" - }; let closure = Closure { code: ptr, env: ptr::null() }; let func: fn(~[~str]) = unsafe::transmute(closure); - func(~[bin]); + func(~[sess.opts.binary]); } } } diff --git a/src/rustc/driver/driver.rs b/src/rustc/driver/driver.rs index c9fdb8d4ccb99..7ec7b01f6a8d2 100644 --- a/src/rustc/driver/driver.rs +++ b/src/rustc/driver/driver.rs @@ -415,7 +415,8 @@ fn host_triple() -> ~str { }; } -fn build_session_options(matches: getopts::matches, +fn build_session_options(binary: ~str, + matches: getopts::matches, demitter: diagnostic::emitter) -> @session::options { let crate_type = if opt_present(matches, ~"lib") { session::lib_crate @@ -540,6 +541,7 @@ fn build_session_options(matches: getopts::matches, maybe_sysroot: sysroot_opt, target_triple: target, cfg: cfg, + binary: binary, test: test, parse_only: parse_only, no_trans: no_trans, diff --git a/src/rustc/driver/rustc.rs b/src/rustc/driver/rustc.rs index db4a129befba8..672e86dbde9e6 100644 --- a/src/rustc/driver/rustc.rs +++ b/src/rustc/driver/rustc.rs @@ -166,7 +166,7 @@ fn run_compiler(args: ~[~str], demitter: diagnostic::emitter) { _ => early_error(demitter, ~"multiple input filenames provided") }; - let sopts = build_session_options(matches, demitter); + let sopts = build_session_options(binary, matches, demitter); let sess = build_session(sopts, demitter); let odir = getopts::opt_maybe_str(matches, ~"out-dir"); let odir = option::map(odir, |o| Path(o)); diff --git a/src/rustc/driver/session.rs b/src/rustc/driver/session.rs index 97636e1489ed0..077c6ad7cf415 100644 --- a/src/rustc/driver/session.rs +++ b/src/rustc/driver/session.rs @@ -96,6 +96,7 @@ type options = maybe_sysroot: Option, target_triple: ~str, cfg: ast::crate_cfg, + binary: ~str, test: bool, parse_only: bool, no_trans: bool, @@ -238,6 +239,7 @@ fn basic_options() -> @options { maybe_sysroot: None, target_triple: driver::host_triple(), cfg: ~[], + binary: ~"rustc", test: false, parse_only: false, no_trans: false,