Skip to content

Commit 3f6a3b5

Browse files
committed
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.
1 parent 0b9b253 commit 3f6a3b5

File tree

7 files changed

+138
-11
lines changed

7 files changed

+138
-11
lines changed

example/std_example.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ fn main() {
1515
let stderr = ::std::io::stderr();
1616
let mut stderr = stderr.lock();
1717

18+
// FIXME support lazy jit when multi threading
19+
#[cfg(not(lazy_jit))]
1820
std::thread::spawn(move || {
1921
println!("Hello from another thread!");
2022
});

scripts/tests.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ function no_sysroot_tests() {
1616
if [[ "$JIT_SUPPORTED" = "1" ]]; then
1717
echo "[JIT] mini_core_hello_world"
1818
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"
19+
20+
echo "[JIT-lazy] mini_core_hello_world"
21+
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"
1922
else
2023
echo "[JIT] mini_core_hello_world (skipped)"
2124
fi
@@ -38,6 +41,9 @@ function base_sysroot_tests() {
3841
if [[ "$JIT_SUPPORTED" = "1" ]]; then
3942
echo "[JIT] std_example"
4043
$MY_RUSTC -Cllvm-args=mode=jit -Cprefer-dynamic example/std_example.rs --target "$HOST_TRIPLE"
44+
45+
echo "[JIT-lazy] std_example"
46+
$MY_RUSTC -Cllvm-args=mode=jit-lazy -Cprefer-dynamic example/std_example.rs --cfg lazy_jit --target "$HOST_TRIPLE"
4147
else
4248
echo "[JIT] std_example (skipped)"
4349
fi

src/constant.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,8 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut impl Module, cx: &mut Constan
447447
data_ctx.write_data_addr(offset.bytes() as u32, global_value, addend as i64);
448448
}
449449

450-
module.define_data(data_id, &data_ctx).unwrap();
450+
// FIXME don't duplicate definitions in lazy jit mode
451+
let _ = module.define_data(data_id, &data_ctx);
451452
cx.done.insert(data_id);
452453
}
453454

src/driver/jit.rs

Lines changed: 122 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! The JIT driver uses [`cranelift_simplejit`] to JIT execute programs without writing any object
22
//! files.
33
4+
use std::cell::RefCell;
45
use std::ffi::CString;
56
use std::os::raw::{c_char, c_int};
67

@@ -10,8 +11,13 @@ use rustc_middle::mir::mono::MonoItem;
1011
use cranelift_jit::{JITBuilder, JITModule};
1112

1213
use crate::prelude::*;
14+
use crate::{CodegenCx, CodegenMode};
1315

14-
pub(super) fn run_jit(tcx: TyCtxt<'_>) -> ! {
16+
thread_local! {
17+
pub static CURRENT_MODULE: RefCell<Option<JITModule>> = RefCell::new(None);
18+
}
19+
20+
pub(super) fn run_jit(tcx: TyCtxt<'_>, codegen_mode: CodegenMode) -> ! {
1521
if !tcx.sess.opts.output_types.should_codegen() {
1622
tcx.sess.fatal("JIT mode doesn't work with `cargo check`.");
1723
}
@@ -40,6 +46,7 @@ pub(super) fn run_jit(tcx: TyCtxt<'_>) -> ! {
4046
crate::build_isa(tcx.sess),
4147
cranelift_module::default_libcall_names(),
4248
);
49+
jit_builder.hotswap(matches!(codegen_mode, CodegenMode::JitLazy));
4350
jit_builder.symbols(imported_symbols);
4451
let mut jit_module = JITModule::new(jit_builder);
4552
assert_eq!(pointer_ty(tcx), jit_module.target_config().pointer_type());
@@ -74,13 +81,17 @@ pub(super) fn run_jit(tcx: TyCtxt<'_>) -> ! {
7481
for (mono_item, (linkage, visibility)) in mono_items {
7582
let linkage = crate::linkage::get_clif_linkage(mono_item, linkage, visibility);
7683
match mono_item {
77-
MonoItem::Fn(inst) => {
78-
cx.tcx.sess.time("codegen fn", || {
79-
crate::base::codegen_fn(&mut cx, inst, linkage)
80-
});
81-
}
84+
MonoItem::Fn(inst) => match codegen_mode {
85+
CodegenMode::Aot => unreachable!(),
86+
CodegenMode::Jit => {
87+
cx.tcx.sess.time("codegen fn", || {
88+
crate::base::codegen_fn(&mut cx, inst, linkage)
89+
});
90+
}
91+
CodegenMode::JitLazy => codegen_shim(&mut cx, inst),
92+
},
8293
MonoItem::Static(def_id) => {
83-
crate::constant::codegen_static(&mut cx.constants_cx, def_id)
94+
crate::constant::codegen_static(&mut cx.constants_cx, def_id);
8495
}
8596
MonoItem::GlobalAsm(hir_id) => {
8697
let item = cx.tcx.hir().expect_item(hir_id);
@@ -126,11 +137,50 @@ pub(super) fn run_jit(tcx: TyCtxt<'_>) -> ! {
126137
// useful as some dynamic linkers use it as a marker to jump over.
127138
argv.push(std::ptr::null());
128139

140+
CURRENT_MODULE
141+
.with(|current_module| assert!(current_module.borrow_mut().replace(jit_module).is_none()));
142+
129143
let ret = f(args.len() as c_int, argv.as_ptr());
130144

131145
std::process::exit(ret);
132146
}
133147

148+
#[no_mangle]
149+
extern "C" fn __clif_jit_fn(instance_ptr: *const Instance<'static>) -> *const u8 {
150+
rustc_middle::ty::tls::with(|tcx| {
151+
// lift is used to ensure the correct lifetime for instance.
152+
let instance = tcx.lift(unsafe { *instance_ptr }).unwrap();
153+
154+
CURRENT_MODULE.with(|jit_module| {
155+
let mut jit_module = jit_module.borrow_mut();
156+
let jit_module = jit_module.as_mut().unwrap();
157+
let mut cx = crate::CodegenCx::new(tcx, jit_module, false, false);
158+
159+
let (name, sig) = crate::abi::get_function_name_and_sig(
160+
tcx,
161+
cx.module.isa().triple(),
162+
instance,
163+
true,
164+
);
165+
let func_id = cx
166+
.module
167+
.declare_function(&name, Linkage::Export, &sig)
168+
.unwrap();
169+
cx.module.prepare_for_function_redefine(func_id).unwrap();
170+
171+
tcx.sess.time("codegen fn", || {
172+
crate::base::codegen_fn(&mut cx, instance, Linkage::Export)
173+
});
174+
175+
let (jit_module, global_asm, _debug_context, unwind_context) = cx.finalize();
176+
assert!(global_asm.is_empty());
177+
jit_module.finalize_definitions();
178+
std::mem::forget(unsafe { unwind_context.register_jit(&jit_module) });
179+
jit_module.get_finalized_function(func_id)
180+
})
181+
})
182+
}
183+
134184
fn load_imported_symbols_for_jit(tcx: TyCtxt<'_>) -> Vec<(String, *const u8)> {
135185
use rustc_middle::middle::dependency_format::Linkage;
136186

@@ -190,3 +240,68 @@ fn load_imported_symbols_for_jit(tcx: TyCtxt<'_>) -> Vec<(String, *const u8)> {
190240

191241
imported_symbols
192242
}
243+
244+
pub(super) fn codegen_shim<'tcx>(cx: &mut CodegenCx<'tcx, impl Module>, inst: Instance<'tcx>) {
245+
let tcx = cx.tcx;
246+
247+
let pointer_type = cx.module.target_config().pointer_type();
248+
249+
let (name, sig) =
250+
crate::abi::get_function_name_and_sig(tcx, cx.module.isa().triple(), inst, true);
251+
let func_id = cx
252+
.module
253+
.declare_function(&name, Linkage::Export, &sig)
254+
.unwrap();
255+
256+
let instance_ptr = Box::into_raw(Box::new(inst));
257+
258+
let jit_fn = cx
259+
.module
260+
.declare_function(
261+
"__clif_jit_fn",
262+
Linkage::Import,
263+
&Signature {
264+
call_conv: cx.module.target_config().default_call_conv,
265+
params: vec![AbiParam::new(pointer_type)],
266+
returns: vec![AbiParam::new(pointer_type)],
267+
},
268+
)
269+
.unwrap();
270+
271+
let mut trampoline = Function::with_name_signature(ExternalName::default(), sig.clone());
272+
let mut builder_ctx = FunctionBuilderContext::new();
273+
let mut trampoline_builder = FunctionBuilder::new(&mut trampoline, &mut builder_ctx);
274+
275+
let jit_fn = cx
276+
.module
277+
.declare_func_in_func(jit_fn, trampoline_builder.func);
278+
let sig_ref = trampoline_builder.func.import_signature(sig);
279+
280+
let entry_block = trampoline_builder.create_block();
281+
trampoline_builder.append_block_params_for_function_params(entry_block);
282+
let fn_args = trampoline_builder
283+
.func
284+
.dfg
285+
.block_params(entry_block)
286+
.to_vec();
287+
288+
trampoline_builder.switch_to_block(entry_block);
289+
let instance_ptr = trampoline_builder
290+
.ins()
291+
.iconst(pointer_type, instance_ptr as u64 as i64);
292+
let jitted_fn = trampoline_builder.ins().call(jit_fn, &[instance_ptr]);
293+
let jitted_fn = trampoline_builder.func.dfg.inst_results(jitted_fn)[0];
294+
let call_inst = trampoline_builder
295+
.ins()
296+
.call_indirect(sig_ref, jitted_fn, &fn_args);
297+
let ret_vals = trampoline_builder.func.dfg.inst_results(call_inst).to_vec();
298+
trampoline_builder.ins().return_(&ret_vals);
299+
300+
cx.module
301+
.define_function(
302+
func_id,
303+
&mut Context::for_function(trampoline),
304+
&mut cranelift_codegen::binemit::NullTrapSink {},
305+
)
306+
.unwrap();
307+
}

src/driver/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ pub(crate) fn codegen_crate(
2323

2424
match config.codegen_mode {
2525
CodegenMode::Aot => aot::run_aot(tcx, metadata, need_metadata_module),
26-
CodegenMode::Jit => {
26+
CodegenMode::Jit | CodegenMode::JitLazy => {
2727
let is_executable = tcx
2828
.sess
2929
.crate_types()
@@ -33,7 +33,7 @@ pub(crate) fn codegen_crate(
3333
}
3434

3535
#[cfg(feature = "jit")]
36-
let _: ! = jit::run_jit(tcx);
36+
let _: ! = jit::run_jit(tcx, config.codegen_mode);
3737

3838
#[cfg(not(feature = "jit"))]
3939
tcx.sess

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ impl<'tcx, M: Module> CodegenCx<'tcx, M> {
177177
pub enum CodegenMode {
178178
Aot,
179179
Jit,
180+
JitLazy,
180181
}
181182

182183
impl Default for CodegenMode {
@@ -192,6 +193,7 @@ impl FromStr for CodegenMode {
192193
match s {
193194
"aot" => Ok(CodegenMode::Aot),
194195
"jit" => Ok(CodegenMode::Jit),
196+
"jit-lazy" => Ok(CodegenMode::JitLazy),
195197
_ => Err(format!("Unknown codegen mode `{}`", s)),
196198
}
197199
}

src/vtable.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,8 @@ fn build_vtable<'tcx>(
158158
)
159159
.unwrap();
160160

161-
fx.cx.module.define_data(data_id, &data_ctx).unwrap();
161+
// FIXME don't duplicate definitions in lazy jit mode
162+
let _ = fx.cx.module.define_data(data_id, &data_ctx);
162163

163164
data_id
164165
}

0 commit comments

Comments
 (0)