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/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/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..65835e9b470ac 100644 --- a/src/compiletest/runtest.rs +++ b/src/compiletest/runtest.rs @@ -48,11 +48,17 @@ 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 +82,23 @@ 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 +311,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) } diff --git a/src/rustc/back/link.rs b/src/rustc/back/link.rs index 671962e168cf6..6fae0fd9e50f9 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,54 @@ 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 closure = Closure { + code: ptr, + env: ptr::null() + }; + let func: fn(~[~str]) = unsafe::transmute(closure); + + func(~[sess.opts.binary]); + } + } +} + mod write { fn is_object_or_assembly_or_exe(ot: output_type) -> bool { if ot == output_type_assembly || ot == output_type_object || @@ -70,6 +118,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 +178,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 +191,39 @@ mod write { session::Aggressive => LLVMOptAggressive }; + if opts.jit { + // If we are using JIT, go ahead and create and + // execute the engine now. + // JIT execution takes ownership of the module, + // 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}; + + let _: () = str::as_c_str( + cratepath, + |buf_t| { + if !llvm::LLVMRustLoadLibrary(buf_t) { + llvm_err(sess, ~"Could not link"); + } + debug!{"linked: %s", cratepath}; + }); + }*/ + + jit::exec(sess, pm.llpm, llmod, CodeGenOptLevel, true); + + 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..7ec7b01f6a8d2 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)}; } @@ -414,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 @@ -470,6 +472,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,11 +535,13 @@ 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, target_triple: target, cfg: cfg, + binary: binary, test: test, parse_only: parse_only, no_trans: no_trans, @@ -607,6 +612,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..672e86dbde9e6 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 @@ -165,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 56d2cc86aa763..077c6ad7cf415 100644 --- a/src/rustc/driver/session.rs +++ b/src/rustc/driver/session.rs @@ -90,11 +90,13 @@ 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, target_triple: ~str, cfg: ast::crate_cfg, + binary: ~str, test: bool, parse_only: bool, no_trans: bool, @@ -231,11 +233,13 @@ 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, target_triple: driver::host_triple(), cfg: ~[], + binary: ~"rustc", test: false, parse_only: false, no_trans: false, diff --git a/src/rustc/lib/llvm.rs b/src/rustc/lib/llvm.rs index d441869457805..441837dc0c6ff 100644 --- a/src/rustc/lib/llvm.rs +++ b/src/rustc/lib/llvm.rs @@ -947,6 +947,16 @@ extern mod llvm { call. */ fn LLVMRustGetLastError() -> *c_char; + /** Load a shared library to resolve symbols against. */ + fn LLVMRustLoadLibrary(Filename: *c_char) -> bool; + + /** Create and execute the JIT engine. */ + fn LLVMRustJIT(__morestack: *(), + PM: PassManagerRef, + M: ModuleRef, + OptLevel: c_int, + EnableSegmentedStacks: bool) -> *(); + /** Parses the bitcode in the given memory buffer. */ fn LLVMRustParseBitcode(MemBuf: MemoryBufferRef) -> ModuleRef; 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/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index 4a927744f07a0..6ea433e6f1f05 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -15,6 +15,10 @@ #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/Transforms/IPO.h" #include "llvm/ADT/Triple.h" #include "llvm/Assembly/Parser.h" #include "llvm/Assembly/PrintModulePass.h" @@ -28,11 +32,26 @@ #include "llvm/Target/TargetOptions.h" #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-c/Core.h" #include "llvm-c/BitReader.h" #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; @@ -68,6 +87,265 @@ 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. + +void LLVMRustInitializeTargets() { + LLVMInitializeX86TargetInfo(); + LLVMInitializeX86Target(); + LLVMInitializeX86TargetMC(); + LLVMInitializeX86AsmPrinter(); + LLVMInitializeX86AsmParser(); +} + +extern "C" bool +LLVMRustLoadLibrary(const char* file) { + std::string err; + + if(llvm::sys::DynamicLibrary::LoadLibraryPermanently(file, &err)) { + LLVMRustError = err.c_str(); + return false; + } + + return true; +} + +// 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; + void* __morestack; + + RustMCJITMemoryManager(void* sym) : __morestack(sym) { } + ~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 + + if (Name == "__morestack") return &__morestack; + + const char *NameStr = Name.c_str(); + void *Ptr = sys::DynamicLibrary::SearchForAddressOfSymbol(NameStr); + 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()); +} + +extern "C" void* +LLVMRustJIT(void* __morestack, + LLVMPassManagerRef PMR, + LLVMModuleRef M, + CodeGenOpt::Level OptLevel, + bool EnableSegmentedStacks) { + + InitializeNativeTarget(); + InitializeNativeTargetAsmPrinter(); + + std::string Err; + TargetOptions Options; + Options.JITEmitDebugInfo = true; + Options.NoFramePointerElim = true; + Options.EnableSegmentedStacks = EnableSegmentedStacks; + PassManager *PM = unwrap(PMR); + + PM->add(createBasicAliasAnalysisPass()); + PM->add(createInstructionCombiningPass()); + PM->add(createReassociatePass()); + PM->add(createGVNPass()); + PM->add(createCFGSimplificationPass()); + PM->add(createFunctionInliningPass()); + PM->add(createPromoteMemoryToRegisterPass()); + PM->run(*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 0; + } + + 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; +} + extern "C" bool LLVMRustWriteOutputFile(LLVMPassManagerRef PMR, LLVMModuleRef M, @@ -77,16 +355,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(); + LLVMRustInitializeTargets(); TargetOptions Options; Options.NoFramePointerElim = true; diff --git a/src/rustllvm/rustllvm.def.in b/src/rustllvm/rustllvm.def.in index 64b560999bec5..1de1e3ba58f2c 100644 --- a/src/rustllvm/rustllvm.def.in +++ b/src/rustllvm/rustllvm.def.in @@ -4,6 +4,8 @@ LLVMRustWriteOutputFile LLVMRustGetLastError LLVMRustConstSmallInt LLVMRustConstInt +LLVMRustLoadLibrary +LLVMRustJIT LLVMRustParseBitcode LLVMRustParseAssemblyFile LLVMRustPrintPassTimings @@ -485,6 +487,7 @@ LLVMIsThreadLocal LLVMIsUndef LLVMLabelType LLVMLabelTypeInContext +LLVMLinkInInterpreter LLVMMDNode LLVMMDNodeInContext LLVMMDString 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