From c556e4dd354056ef3c4a9fbcd1ab67510e935833 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Thu, 17 Dec 2020 19:31:38 +0100 Subject: [PATCH 1/5] Use PIC in JIT mode too --- src/backend.rs | 4 ++-- src/debuginfo/unwind.rs | 4 ++-- src/driver/aot.rs | 9 +++++++-- src/driver/jit.rs | 4 ++-- src/lib.rs | 12 ++++-------- src/pretty_clif.rs | 4 +--- 6 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/backend.rs b/src/backend.rs index 9e32259716f..0ce34c904bd 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -162,7 +162,7 @@ impl AddConstructor for ObjectProduct { } pub(crate) fn with_object(sess: &Session, name: &str, f: impl FnOnce(&mut Object)) -> Vec { - let triple = crate::build_isa(sess, true).triple().clone(); + let triple = crate::build_isa(sess).triple().clone(); let binary_format = match triple.binary_format { target_lexicon::BinaryFormat::Elf => object::BinaryFormat::Elf, @@ -193,7 +193,7 @@ pub(crate) fn with_object(sess: &Session, name: &str, f: impl FnOnce(&mut Object pub(crate) fn make_module(sess: &Session, name: String) -> ObjectModule { let mut builder = ObjectBuilder::new( - crate::build_isa(sess, true), + crate::build_isa(sess), name + ".o", cranelift_module::default_libcall_names(), ) diff --git a/src/debuginfo/unwind.rs b/src/debuginfo/unwind.rs index dc630fa5fd4..49de927cdba 100644 --- a/src/debuginfo/unwind.rs +++ b/src/debuginfo/unwind.rs @@ -15,11 +15,11 @@ pub(crate) struct UnwindContext<'tcx> { } impl<'tcx> UnwindContext<'tcx> { - pub(crate) fn new(tcx: TyCtxt<'tcx>, isa: &dyn TargetIsa) -> Self { + pub(crate) fn new(tcx: TyCtxt<'tcx>, isa: &dyn TargetIsa, pic_eh_frame: bool) -> Self { let mut frame_table = FrameTable::default(); let cie_id = if let Some(mut cie) = isa.create_systemv_cie() { - if isa.flags().is_pic() { + if pic_eh_frame { cie.fde_address_encoding = gimli::DwEhPe(gimli::DW_EH_PE_pcrel.0 | gimli::DW_EH_PE_sdata4.0); } diff --git a/src/driver/aot.rs b/src/driver/aot.rs index cc79a4234eb..16f9bfc9918 100644 --- a/src/driver/aot.rs +++ b/src/driver/aot.rs @@ -146,7 +146,12 @@ fn module_codegen(tcx: TyCtxt<'_>, cgu_name: rustc_span::Symbol) -> ModuleCodege } } - let mut cx = crate::CodegenCx::new(tcx, module, tcx.sess.opts.debuginfo != DebugInfo::None); + let mut cx = crate::CodegenCx::new( + tcx, + module, + tcx.sess.opts.debuginfo != DebugInfo::None, + true, + ); super::predefine_mono_items(&mut cx, &mono_items); for (mono_item, (linkage, visibility)) in mono_items { let linkage = crate::linkage::get_clif_linkage(mono_item, linkage, visibility); @@ -254,7 +259,7 @@ pub(super) fn run_aot( tcx.sess.abort_if_errors(); let mut allocator_module = new_module(tcx, "allocator_shim".to_string()); - let mut allocator_unwind_context = UnwindContext::new(tcx, allocator_module.isa()); + let mut allocator_unwind_context = UnwindContext::new(tcx, allocator_module.isa(), true); let created_alloc_shim = crate::allocator::codegen(tcx, &mut allocator_module, &mut allocator_unwind_context); diff --git a/src/driver/jit.rs b/src/driver/jit.rs index 05bcade3535..4be317f8391 100644 --- a/src/driver/jit.rs +++ b/src/driver/jit.rs @@ -37,7 +37,7 @@ pub(super) fn run_jit(tcx: TyCtxt<'_>) -> ! { let imported_symbols = load_imported_symbols_for_jit(tcx); let mut jit_builder = JITBuilder::with_isa( - crate::build_isa(tcx.sess, false), + crate::build_isa(tcx.sess), cranelift_module::default_libcall_names(), ); jit_builder.symbols(imported_symbols); @@ -67,7 +67,7 @@ pub(super) fn run_jit(tcx: TyCtxt<'_>) -> ! { .into_iter() .collect::>(); - let mut cx = crate::CodegenCx::new(tcx, jit_module, false); + let mut cx = crate::CodegenCx::new(tcx, jit_module, false, false); let (mut jit_module, global_asm, _debug, mut unwind_context) = super::time(tcx, "codegen mono items", || { diff --git a/src/lib.rs b/src/lib.rs index 7b751102f8b..ee20e449bb9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -141,8 +141,8 @@ struct CodegenCx<'tcx, M: Module> { } impl<'tcx, M: Module> CodegenCx<'tcx, M> { - fn new(tcx: TyCtxt<'tcx>, module: M, debug_info: bool) -> Self { - let unwind_context = UnwindContext::new(tcx, module.isa()); + fn new(tcx: TyCtxt<'tcx>, module: M, debug_info: bool, pic_eh_frame: bool) -> Self { + let unwind_context = UnwindContext::new(tcx, module.isa(), pic_eh_frame); let debug_context = if debug_info { Some(DebugContext::new(tcx, module.isa())) } else { @@ -250,17 +250,13 @@ fn target_triple(sess: &Session) -> target_lexicon::Triple { sess.target.llvm_target.parse().unwrap() } -fn build_isa(sess: &Session, enable_pic: bool) -> Box { +fn build_isa(sess: &Session) -> Box { use target_lexicon::BinaryFormat; let target_triple = crate::target_triple(sess); let mut flags_builder = settings::builder(); - if enable_pic { - flags_builder.enable("is_pic").unwrap(); - } else { - flags_builder.set("is_pic", "false").unwrap(); - } + flags_builder.enable("is_pic").unwrap(); flags_builder.set("enable_probestack", "false").unwrap(); // __cranelift_probestack is not provided flags_builder .set( diff --git a/src/pretty_clif.rs b/src/pretty_clif.rs index 1d17157a876..1326bac74b1 100644 --- a/src/pretty_clif.rs +++ b/src/pretty_clif.rs @@ -263,9 +263,7 @@ pub(crate) fn write_clif_file<'tcx>( &mut clif, &context.func, &DisplayFunctionAnnotations { - isa: Some(&*crate::build_isa( - tcx.sess, true, /* PIC doesn't matter here */ - )), + isa: Some(&*crate::build_isa(tcx.sess)), value_ranges: value_ranges.as_ref(), }, ) From 20ffea6b8a9f614eed854adbba75626effc1f111 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Fri, 25 Dec 2020 11:31:33 +0100 Subject: [PATCH 2/5] Change the way JIT mode is selected --- Readme.md | 4 +-- scripts/cargo.sh | 2 +- scripts/filter_profile.rs | 2 +- scripts/tests.sh | 4 +-- src/bin/cg_clif.rs | 19 ++-------- src/bin/cg_clif_build_sysroot.rs | 4 +-- src/driver/jit.rs | 2 +- src/driver/mod.rs | 34 +++++++++--------- src/lib.rs | 61 ++++++++++++++++++++++++++++---- 9 files changed, 82 insertions(+), 50 deletions(-) diff --git a/Readme.md b/Readme.md index b0ea95692f3..283eec91e21 100644 --- a/Readme.md +++ b/Readme.md @@ -68,7 +68,7 @@ $ $cg_clif_dir/build/cargo.sh jit or ```bash -$ $cg_clif_dir/build/bin/cg_clif --jit my_crate.rs +$ $cg_clif_dir/build/bin/cg_clif -Cllvm-args=mode=jit -Cprefer-dynamic my_crate.rs ``` ### Shell @@ -77,7 +77,7 @@ These are a few functions that allow you to easily run rust code from the shell ```bash function jit_naked() { - echo "$@" | $cg_clif_dir/build/bin/cg_clif - --jit + echo "$@" | $cg_clif_dir/build/bin/cg_clif - -Cllvm-args=mode=jit -Cprefer-dynamic } function jit() { diff --git a/scripts/cargo.sh b/scripts/cargo.sh index dcd40acc02a..934b2c6d7a1 100755 --- a/scripts/cargo.sh +++ b/scripts/cargo.sh @@ -10,7 +10,7 @@ cmd=$1 shift || true if [[ "$cmd" = "jit" ]]; then -cargo "+${TOOLCHAIN}" rustc "$@" -- --jit +cargo "+${TOOLCHAIN}" rustc "$@" -- -Cllvm-args=mode=jit -Cprefer-dynamic else cargo "+${TOOLCHAIN}" "$cmd" "$@" fi diff --git a/scripts/filter_profile.rs b/scripts/filter_profile.rs index 3327c10089d..15388926ec9 100755 --- a/scripts/filter_profile.rs +++ b/scripts/filter_profile.rs @@ -4,7 +4,7 @@ pushd $(dirname "$0")/../ source build/config.sh popd -PROFILE=$1 OUTPUT=$2 exec $RUSTC $RUSTFLAGS --jit $0 +PROFILE=$1 OUTPUT=$2 exec $RUSTC $RUSTFLAGS -Cllvm-args=mode=jit -Cprefer-dynamic $0 #*/ //! This program filters away uninteresting samples and trims uninteresting frames for stackcollapse diff --git a/scripts/tests.sh b/scripts/tests.sh index 114b6f30a4a..60ed7aac8aa 100755 --- a/scripts/tests.sh +++ b/scripts/tests.sh @@ -15,7 +15,7 @@ function no_sysroot_tests() { if [[ "$JIT_SUPPORTED" = "1" ]]; then echo "[JIT] mini_core_hello_world" - CG_CLIF_JIT_ARGS="abc bcd" $MY_RUSTC --jit example/mini_core_hello_world.rs --cfg jit --target "$HOST_TRIPLE" + CG_CLIF_JIT_ARGS="abc bcd" $MY_RUSTC -Cllvm-args=mode=jit -Cprefer-dynamic example/mini_core_hello_world.rs --cfg jit --target "$HOST_TRIPLE" else echo "[JIT] mini_core_hello_world (skipped)" fi @@ -37,7 +37,7 @@ function base_sysroot_tests() { if [[ "$JIT_SUPPORTED" = "1" ]]; then echo "[JIT] std_example" - $MY_RUSTC --jit example/std_example.rs --target "$HOST_TRIPLE" + $MY_RUSTC -Cllvm-args=mode=jit -Cprefer-dynamic example/std_example.rs --target "$HOST_TRIPLE" else echo "[JIT] std_example (skipped)" fi diff --git a/src/bin/cg_clif.rs b/src/bin/cg_clif.rs index f4d23ebcf4e..58e45b4e9b9 100644 --- a/src/bin/cg_clif.rs +++ b/src/bin/cg_clif.rs @@ -44,9 +44,7 @@ fn main() { let mut callbacks = CraneliftPassesCallbacks::default(); rustc_driver::install_ice_hook(); let exit_code = rustc_driver::catch_with_exit_code(|| { - let mut use_jit = false; - - let mut args = std::env::args_os() + let args = std::env::args_os() .enumerate() .map(|(i, arg)| { arg.into_string().unwrap_or_else(|arg| { @@ -56,23 +54,10 @@ fn main() { ) }) }) - .filter(|arg| { - if arg == "--jit" { - use_jit = true; - false - } else { - true - } - }) .collect::>(); - if use_jit { - args.push("-Cprefer-dynamic".to_string()); - } let mut run_compiler = rustc_driver::RunCompiler::new(&args, &mut callbacks); run_compiler.set_make_codegen_backend(Some(Box::new(move |_| { - Box::new(rustc_codegen_cranelift::CraneliftCodegenBackend { - config: rustc_codegen_cranelift::BackendConfig { use_jit }, - }) + Box::new(rustc_codegen_cranelift::CraneliftCodegenBackend { config: None }) }))); run_compiler.run() }); diff --git a/src/bin/cg_clif_build_sysroot.rs b/src/bin/cg_clif_build_sysroot.rs index 165d33dcfb5..8ee4cd46c94 100644 --- a/src/bin/cg_clif_build_sysroot.rs +++ b/src/bin/cg_clif_build_sysroot.rs @@ -92,9 +92,7 @@ fn main() { let mut run_compiler = rustc_driver::RunCompiler::new(&args, &mut callbacks); if use_clif { run_compiler.set_make_codegen_backend(Some(Box::new(move |_| { - Box::new(rustc_codegen_cranelift::CraneliftCodegenBackend { - config: rustc_codegen_cranelift::BackendConfig { use_jit: false }, - }) + Box::new(rustc_codegen_cranelift::CraneliftCodegenBackend { config: None }) }))); } run_compiler.run() diff --git a/src/driver/jit.rs b/src/driver/jit.rs index 4be317f8391..3a942a15491 100644 --- a/src/driver/jit.rs +++ b/src/driver/jit.rs @@ -106,7 +106,7 @@ pub(super) fn run_jit(tcx: TyCtxt<'_>) -> ! { let finalized_main: *const u8 = jit_module.get_finalized_function(main_func_id); - println!("Rustc codegen cranelift will JIT run the executable, because --jit was passed"); + println!("Rustc codegen cranelift will JIT run the executable, because -Cllvm-args=mode=jit was passed"); let f: extern "C" fn(c_int, *const *const c_char) -> c_int = unsafe { ::std::mem::transmute(finalized_main) }; diff --git a/src/driver/mod.rs b/src/driver/mod.rs index 0b0ea8a852c..49bb79c101a 100644 --- a/src/driver/mod.rs +++ b/src/driver/mod.rs @@ -7,6 +7,7 @@ use rustc_middle::middle::cstore::EncodedMetadata; use rustc_middle::mir::mono::{Linkage as RLinkage, MonoItem, Visibility}; use crate::prelude::*; +use crate::CodegenMode; mod aot; #[cfg(feature = "jit")] @@ -20,24 +21,25 @@ pub(crate) fn codegen_crate( ) -> Box { tcx.sess.abort_if_errors(); - if config.use_jit { - let is_executable = tcx - .sess - .crate_types() - .contains(&rustc_session::config::CrateType::Executable); - if !is_executable { - tcx.sess.fatal("can't jit non-executable crate"); + match config.codegen_mode { + CodegenMode::Aot => aot::run_aot(tcx, metadata, need_metadata_module), + CodegenMode::Jit => { + let is_executable = tcx + .sess + .crate_types() + .contains(&rustc_session::config::CrateType::Executable); + if !is_executable { + tcx.sess.fatal("can't jit non-executable crate"); + } + + #[cfg(feature = "jit")] + let _: ! = jit::run_jit(tcx); + + #[cfg(not(feature = "jit"))] + tcx.sess + .fatal("jit support was disabled when compiling rustc_codegen_cranelift"); } - - #[cfg(feature = "jit")] - let _: ! = jit::run_jit(tcx); - - #[cfg(not(feature = "jit"))] - tcx.sess - .fatal("jit support was disabled when compiling rustc_codegen_cranelift"); } - - aot::run_aot(tcx, metadata, need_metadata_module) } fn predefine_mono_items<'tcx>( diff --git a/src/lib.rs b/src/lib.rs index ee20e449bb9..91e1efb71b9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,8 @@ associated_type_bounds, never_type, try_blocks, - hash_drain_filter + hash_drain_filter, + str_split_once )] #![warn(rust_2018_idioms)] #![warn(unused_lifetimes)] @@ -34,6 +35,7 @@ extern crate rustc_target; extern crate rustc_driver; use std::any::Any; +use std::str::FromStr; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_codegen_ssa::CodegenResults; @@ -172,12 +174,53 @@ impl<'tcx, M: Module> CodegenCx<'tcx, M> { } #[derive(Copy, Clone, Debug)] +pub enum CodegenMode { + Aot, + Jit, +} + +impl Default for CodegenMode { + fn default() -> Self { + CodegenMode::Aot + } +} + +impl FromStr for CodegenMode { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "aot" => Ok(CodegenMode::Aot), + "jit" => Ok(CodegenMode::Jit), + _ => Err(format!("Unknown codegen mode `{}`", s)), + } + } +} + +#[derive(Copy, Clone, Debug, Default)] pub struct BackendConfig { - pub use_jit: bool, + pub codegen_mode: CodegenMode, +} + +impl BackendConfig { + fn from_opts(opts: &[String]) -> Result { + let mut config = BackendConfig::default(); + for opt in opts { + if let Some((name, value)) = opt.split_once('=') { + match name { + "mode" => config.codegen_mode = value.parse()?, + _ => return Err(format!("Unknown option `{}`", name)), + } + } else { + return Err(format!("Invalid option `{}`", opt)); + } + } + Ok(config) + } } pub struct CraneliftCodegenBackend { - pub config: BackendConfig, + pub config: Option, } impl CodegenBackend for CraneliftCodegenBackend { @@ -204,7 +247,13 @@ impl CodegenBackend for CraneliftCodegenBackend { metadata: EncodedMetadata, need_metadata_module: bool, ) -> Box { - let res = driver::codegen_crate(tcx, metadata, need_metadata_module, self.config); + let config = if let Some(config) = self.config { + config + } else { + BackendConfig::from_opts(&tcx.sess.opts.cg.llvm_args) + .unwrap_or_else(|err| tcx.sess.fatal(&err)) + }; + let res = driver::codegen_crate(tcx, metadata, need_metadata_module, config); rustc_symbol_mangling::test::report_symbol_names(tcx); @@ -305,7 +354,5 @@ fn build_isa(sess: &Session) -> Box { /// This is the entrypoint for a hot plugged rustc_codegen_cranelift #[no_mangle] pub fn __rustc_codegen_backend() -> Box { - Box::new(CraneliftCodegenBackend { - config: BackendConfig { use_jit: false }, - }) + Box::new(CraneliftCodegenBackend { config: None }) } From 0b9b2532d262bf590b769a7cd2f32602b8925183 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Fri, 25 Dec 2020 11:49:50 +0100 Subject: [PATCH 3/5] Move finalize CodegenCx timer out of codegen mono items timer --- src/driver/jit.rs | 46 +++++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/src/driver/jit.rs b/src/driver/jit.rs index 3a942a15491..cb386ea83b9 100644 --- a/src/driver/jit.rs +++ b/src/driver/jit.rs @@ -69,32 +69,36 @@ pub(super) fn run_jit(tcx: TyCtxt<'_>) -> ! { let mut cx = crate::CodegenCx::new(tcx, jit_module, false, false); - let (mut jit_module, global_asm, _debug, mut unwind_context) = - super::time(tcx, "codegen mono items", || { - super::predefine_mono_items(&mut cx, &mono_items); - for (mono_item, (linkage, visibility)) in mono_items { - let linkage = crate::linkage::get_clif_linkage(mono_item, linkage, visibility); - match mono_item { - MonoItem::Fn(inst) => { - cx.tcx.sess.time("codegen fn", || { - crate::base::codegen_fn(&mut cx, inst, linkage) - }); - } - MonoItem::Static(def_id) => { - crate::constant::codegen_static(&mut cx.constants_cx, def_id) - } - MonoItem::GlobalAsm(hir_id) => { - let item = cx.tcx.hir().expect_item(hir_id); - tcx.sess - .span_fatal(item.span, "Global asm is not supported in JIT mode"); - } + super::time(tcx, "codegen mono items", || { + super::predefine_mono_items(&mut cx, &mono_items); + for (mono_item, (linkage, visibility)) in mono_items { + let linkage = crate::linkage::get_clif_linkage(mono_item, linkage, visibility); + match mono_item { + MonoItem::Fn(inst) => { + cx.tcx.sess.time("codegen fn", || { + crate::base::codegen_fn(&mut cx, inst, linkage) + }); + } + MonoItem::Static(def_id) => { + crate::constant::codegen_static(&mut cx.constants_cx, def_id) + } + MonoItem::GlobalAsm(hir_id) => { + let item = cx.tcx.hir().expect_item(hir_id); + tcx.sess + .span_fatal(item.span, "Global asm is not supported in JIT mode"); } } - tcx.sess.time("finalize CodegenCx", || cx.finalize()) - }); + } + }); + + let (mut jit_module, global_asm, _debug, mut unwind_context) = + tcx.sess.time("finalize CodegenCx", || cx.finalize()); + jit_module.finalize_definitions(); + if !global_asm.is_empty() { tcx.sess.fatal("Inline asm is not supported in JIT mode"); } + crate::main_shim::maybe_create_entry_wrapper(tcx, &mut jit_module, &mut unwind_context, true); crate::allocator::codegen(tcx, &mut jit_module, &mut unwind_context); From 3f6a3b5ebece5f6d3b1010b68a5fd86756593098 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Fri, 25 Dec 2020 11:41:48 +0100 Subject: [PATCH 4/5] Implement lazy compilation in JIT mode Lazy compilation has the potential to significantly improve the startup time of a program. While functions have to be codegened when called, it is expected that a significant amount of all code is only required when an error occurs or only when the program is used in certain ways. The basic approach is to first codegen a shim for each function. This shim calls the `__cg_clif_jit` function of cg_clif with a pointer to the `Instance` corresponding to the function for which it is a shim. `__cg_clif_jit` function then codegens this function and uses the hot code swapping support of SimpleJIT to redirect future calls to the function to the real version. Finally it calls the newly codegened function. --- example/std_example.rs | 2 + scripts/tests.sh | 6 ++ src/constant.rs | 3 +- src/driver/jit.rs | 129 ++++++++++++++++++++++++++++++++++++++--- src/driver/mod.rs | 4 +- src/lib.rs | 2 + src/vtable.rs | 3 +- 7 files changed, 138 insertions(+), 11 deletions(-) diff --git a/example/std_example.rs b/example/std_example.rs index b38e25328a4..015bbdfed46 100644 --- a/example/std_example.rs +++ b/example/std_example.rs @@ -15,6 +15,8 @@ fn main() { let stderr = ::std::io::stderr(); let mut stderr = stderr.lock(); + // FIXME support lazy jit when multi threading + #[cfg(not(lazy_jit))] std::thread::spawn(move || { println!("Hello from another thread!"); }); diff --git a/scripts/tests.sh b/scripts/tests.sh index 60ed7aac8aa..a61774f479e 100755 --- a/scripts/tests.sh +++ b/scripts/tests.sh @@ -16,6 +16,9 @@ function no_sysroot_tests() { if [[ "$JIT_SUPPORTED" = "1" ]]; then echo "[JIT] mini_core_hello_world" CG_CLIF_JIT_ARGS="abc bcd" $MY_RUSTC -Cllvm-args=mode=jit -Cprefer-dynamic example/mini_core_hello_world.rs --cfg jit --target "$HOST_TRIPLE" + + echo "[JIT-lazy] mini_core_hello_world" + CG_CLIF_JIT_ARGS="abc bcd" $MY_RUSTC -Cllvm-args=mode=jit-lazy -Cprefer-dynamic example/mini_core_hello_world.rs --cfg jit --target "$HOST_TRIPLE" else echo "[JIT] mini_core_hello_world (skipped)" fi @@ -38,6 +41,9 @@ function base_sysroot_tests() { if [[ "$JIT_SUPPORTED" = "1" ]]; then echo "[JIT] std_example" $MY_RUSTC -Cllvm-args=mode=jit -Cprefer-dynamic example/std_example.rs --target "$HOST_TRIPLE" + + echo "[JIT-lazy] std_example" + $MY_RUSTC -Cllvm-args=mode=jit-lazy -Cprefer-dynamic example/std_example.rs --cfg lazy_jit --target "$HOST_TRIPLE" else echo "[JIT] std_example (skipped)" fi diff --git a/src/constant.rs b/src/constant.rs index 544b020b711..bfa2076d3d9 100644 --- a/src/constant.rs +++ b/src/constant.rs @@ -447,7 +447,8 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut impl Module, cx: &mut Constan data_ctx.write_data_addr(offset.bytes() as u32, global_value, addend as i64); } - module.define_data(data_id, &data_ctx).unwrap(); + // FIXME don't duplicate definitions in lazy jit mode + let _ = module.define_data(data_id, &data_ctx); cx.done.insert(data_id); } diff --git a/src/driver/jit.rs b/src/driver/jit.rs index cb386ea83b9..9a42c675cc1 100644 --- a/src/driver/jit.rs +++ b/src/driver/jit.rs @@ -1,6 +1,7 @@ //! The JIT driver uses [`cranelift_simplejit`] to JIT execute programs without writing any object //! files. +use std::cell::RefCell; use std::ffi::CString; use std::os::raw::{c_char, c_int}; @@ -10,8 +11,13 @@ use rustc_middle::mir::mono::MonoItem; use cranelift_jit::{JITBuilder, JITModule}; use crate::prelude::*; +use crate::{CodegenCx, CodegenMode}; -pub(super) fn run_jit(tcx: TyCtxt<'_>) -> ! { +thread_local! { + pub static CURRENT_MODULE: RefCell> = RefCell::new(None); +} + +pub(super) fn run_jit(tcx: TyCtxt<'_>, codegen_mode: CodegenMode) -> ! { if !tcx.sess.opts.output_types.should_codegen() { tcx.sess.fatal("JIT mode doesn't work with `cargo check`."); } @@ -40,6 +46,7 @@ pub(super) fn run_jit(tcx: TyCtxt<'_>) -> ! { crate::build_isa(tcx.sess), cranelift_module::default_libcall_names(), ); + jit_builder.hotswap(matches!(codegen_mode, CodegenMode::JitLazy)); jit_builder.symbols(imported_symbols); let mut jit_module = JITModule::new(jit_builder); assert_eq!(pointer_ty(tcx), jit_module.target_config().pointer_type()); @@ -74,13 +81,17 @@ pub(super) fn run_jit(tcx: TyCtxt<'_>) -> ! { for (mono_item, (linkage, visibility)) in mono_items { let linkage = crate::linkage::get_clif_linkage(mono_item, linkage, visibility); match mono_item { - MonoItem::Fn(inst) => { - cx.tcx.sess.time("codegen fn", || { - crate::base::codegen_fn(&mut cx, inst, linkage) - }); - } + MonoItem::Fn(inst) => match codegen_mode { + CodegenMode::Aot => unreachable!(), + CodegenMode::Jit => { + cx.tcx.sess.time("codegen fn", || { + crate::base::codegen_fn(&mut cx, inst, linkage) + }); + } + CodegenMode::JitLazy => codegen_shim(&mut cx, inst), + }, MonoItem::Static(def_id) => { - crate::constant::codegen_static(&mut cx.constants_cx, def_id) + crate::constant::codegen_static(&mut cx.constants_cx, def_id); } MonoItem::GlobalAsm(hir_id) => { let item = cx.tcx.hir().expect_item(hir_id); @@ -126,11 +137,50 @@ pub(super) fn run_jit(tcx: TyCtxt<'_>) -> ! { // useful as some dynamic linkers use it as a marker to jump over. argv.push(std::ptr::null()); + CURRENT_MODULE + .with(|current_module| assert!(current_module.borrow_mut().replace(jit_module).is_none())); + let ret = f(args.len() as c_int, argv.as_ptr()); std::process::exit(ret); } +#[no_mangle] +extern "C" fn __clif_jit_fn(instance_ptr: *const Instance<'static>) -> *const u8 { + rustc_middle::ty::tls::with(|tcx| { + // lift is used to ensure the correct lifetime for instance. + let instance = tcx.lift(unsafe { *instance_ptr }).unwrap(); + + CURRENT_MODULE.with(|jit_module| { + let mut jit_module = jit_module.borrow_mut(); + let jit_module = jit_module.as_mut().unwrap(); + let mut cx = crate::CodegenCx::new(tcx, jit_module, false, false); + + let (name, sig) = crate::abi::get_function_name_and_sig( + tcx, + cx.module.isa().triple(), + instance, + true, + ); + let func_id = cx + .module + .declare_function(&name, Linkage::Export, &sig) + .unwrap(); + cx.module.prepare_for_function_redefine(func_id).unwrap(); + + tcx.sess.time("codegen fn", || { + crate::base::codegen_fn(&mut cx, instance, Linkage::Export) + }); + + let (jit_module, global_asm, _debug_context, unwind_context) = cx.finalize(); + assert!(global_asm.is_empty()); + jit_module.finalize_definitions(); + std::mem::forget(unsafe { unwind_context.register_jit(&jit_module) }); + jit_module.get_finalized_function(func_id) + }) + }) +} + fn load_imported_symbols_for_jit(tcx: TyCtxt<'_>) -> Vec<(String, *const u8)> { use rustc_middle::middle::dependency_format::Linkage; @@ -190,3 +240,68 @@ fn load_imported_symbols_for_jit(tcx: TyCtxt<'_>) -> Vec<(String, *const u8)> { imported_symbols } + +pub(super) fn codegen_shim<'tcx>(cx: &mut CodegenCx<'tcx, impl Module>, inst: Instance<'tcx>) { + let tcx = cx.tcx; + + let pointer_type = cx.module.target_config().pointer_type(); + + let (name, sig) = + crate::abi::get_function_name_and_sig(tcx, cx.module.isa().triple(), inst, true); + let func_id = cx + .module + .declare_function(&name, Linkage::Export, &sig) + .unwrap(); + + let instance_ptr = Box::into_raw(Box::new(inst)); + + let jit_fn = cx + .module + .declare_function( + "__clif_jit_fn", + Linkage::Import, + &Signature { + call_conv: cx.module.target_config().default_call_conv, + params: vec![AbiParam::new(pointer_type)], + returns: vec![AbiParam::new(pointer_type)], + }, + ) + .unwrap(); + + let mut trampoline = Function::with_name_signature(ExternalName::default(), sig.clone()); + let mut builder_ctx = FunctionBuilderContext::new(); + let mut trampoline_builder = FunctionBuilder::new(&mut trampoline, &mut builder_ctx); + + let jit_fn = cx + .module + .declare_func_in_func(jit_fn, trampoline_builder.func); + let sig_ref = trampoline_builder.func.import_signature(sig); + + let entry_block = trampoline_builder.create_block(); + trampoline_builder.append_block_params_for_function_params(entry_block); + let fn_args = trampoline_builder + .func + .dfg + .block_params(entry_block) + .to_vec(); + + trampoline_builder.switch_to_block(entry_block); + let instance_ptr = trampoline_builder + .ins() + .iconst(pointer_type, instance_ptr as u64 as i64); + let jitted_fn = trampoline_builder.ins().call(jit_fn, &[instance_ptr]); + let jitted_fn = trampoline_builder.func.dfg.inst_results(jitted_fn)[0]; + let call_inst = trampoline_builder + .ins() + .call_indirect(sig_ref, jitted_fn, &fn_args); + let ret_vals = trampoline_builder.func.dfg.inst_results(call_inst).to_vec(); + trampoline_builder.ins().return_(&ret_vals); + + cx.module + .define_function( + func_id, + &mut Context::for_function(trampoline), + &mut cranelift_codegen::binemit::NullTrapSink {}, + ) + .unwrap(); +} diff --git a/src/driver/mod.rs b/src/driver/mod.rs index 49bb79c101a..9f4ea9a3865 100644 --- a/src/driver/mod.rs +++ b/src/driver/mod.rs @@ -23,7 +23,7 @@ pub(crate) fn codegen_crate( match config.codegen_mode { CodegenMode::Aot => aot::run_aot(tcx, metadata, need_metadata_module), - CodegenMode::Jit => { + CodegenMode::Jit | CodegenMode::JitLazy => { let is_executable = tcx .sess .crate_types() @@ -33,7 +33,7 @@ pub(crate) fn codegen_crate( } #[cfg(feature = "jit")] - let _: ! = jit::run_jit(tcx); + let _: ! = jit::run_jit(tcx, config.codegen_mode); #[cfg(not(feature = "jit"))] tcx.sess diff --git a/src/lib.rs b/src/lib.rs index 91e1efb71b9..6e4f3bf2898 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -177,6 +177,7 @@ impl<'tcx, M: Module> CodegenCx<'tcx, M> { pub enum CodegenMode { Aot, Jit, + JitLazy, } impl Default for CodegenMode { @@ -192,6 +193,7 @@ impl FromStr for CodegenMode { match s { "aot" => Ok(CodegenMode::Aot), "jit" => Ok(CodegenMode::Jit), + "jit-lazy" => Ok(CodegenMode::JitLazy), _ => Err(format!("Unknown codegen mode `{}`", s)), } } diff --git a/src/vtable.rs b/src/vtable.rs index 238abc0d8bd..8f15586a9dc 100644 --- a/src/vtable.rs +++ b/src/vtable.rs @@ -158,7 +158,8 @@ fn build_vtable<'tcx>( ) .unwrap(); - fx.cx.module.define_data(data_id, &data_ctx).unwrap(); + // FIXME don't duplicate definitions in lazy jit mode + let _ = fx.cx.module.define_data(data_id, &data_ctx); data_id } From 35f4a25c6b216c27c5d87a171620d3f2c5c0a18b Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Fri, 25 Dec 2020 12:13:11 +0100 Subject: [PATCH 5/5] Add documentation --- Readme.md | 8 ++++++++ scripts/cargo.sh | 2 ++ 2 files changed, 10 insertions(+) diff --git a/Readme.md b/Readme.md index 283eec91e21..22d9e00923f 100644 --- a/Readme.md +++ b/Readme.md @@ -71,6 +71,14 @@ or $ $cg_clif_dir/build/bin/cg_clif -Cllvm-args=mode=jit -Cprefer-dynamic my_crate.rs ``` +There is also an experimental lazy jit mode. In this mode functions are only compiled once they are +first called. It currently does not work with multi-threaded programs. When a not yet compiled +function is called from another thread than the main thread, you will get an ICE. + +```bash +$ $cg_clif_dir/build/cargo.sh lazy-jit +``` + ### Shell These are a few functions that allow you to easily run rust code from the shell using cg_clif as jit. diff --git a/scripts/cargo.sh b/scripts/cargo.sh index 934b2c6d7a1..a3d6d303057 100755 --- a/scripts/cargo.sh +++ b/scripts/cargo.sh @@ -11,6 +11,8 @@ shift || true if [[ "$cmd" = "jit" ]]; then cargo "+${TOOLCHAIN}" rustc "$@" -- -Cllvm-args=mode=jit -Cprefer-dynamic +elif [[ "$cmd" = "lazy-jit" ]]; then +cargo "+${TOOLCHAIN}" rustc "$@" -- -Cllvm-args=mode=jit-lazy -Cprefer-dynamic else cargo "+${TOOLCHAIN}" "$cmd" "$@" fi