Skip to content

Commit 2945b96

Browse files
committed
Multithreading support for lazy-jit
1 parent 432285f commit 2945b96

File tree

2 files changed

+82
-8
lines changed

2 files changed

+82
-8
lines changed

src/driver/jit.rs

Lines changed: 74 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
44
use std::cell::RefCell;
55
use std::ffi::CString;
6+
use std::lazy::{Lazy, SyncOnceCell};
67
use std::os::raw::{c_char, c_int};
8+
use std::sync::{mpsc, Mutex};
79

810
use cranelift_codegen::binemit::{NullStackMapSink, NullTrapSink};
911
use rustc_codegen_ssa::CrateInfo;
@@ -23,6 +25,39 @@ thread_local! {
2325
static LAZY_JIT_STATE: RefCell<Option<JitState>> = RefCell::new(None);
2426
}
2527

28+
/// The Sender owned by the rustc thread
29+
static GLOBAL_MESSAGE_SENDER: SyncOnceCell<Mutex<mpsc::Sender<UnsafeMessage>>> = SyncOnceCell::new();
30+
31+
/// A message that is sent from the jitted runtime to the rustc thread.
32+
/// Senders are responsible for upholding `Send` semantics.
33+
enum UnsafeMessage {
34+
/// Request that the specified `Instance` be lazily jitted.
35+
///
36+
/// Nothing accessible through `instance_ptr` may be moved or mutated by the sender after
37+
/// this message is sent.
38+
JitFn {
39+
instance_ptr: *const Instance<'static>,
40+
tx: mpsc::Sender<*const u8>,
41+
},
42+
}
43+
unsafe impl Send for UnsafeMessage {}
44+
45+
impl UnsafeMessage {
46+
/// Send the message.
47+
fn send(self) -> Result<(), mpsc::SendError<UnsafeMessage>> {
48+
thread_local! {
49+
/// The Sender owned by the local thread
50+
static LOCAL_MESSAGE_SENDER: Lazy<mpsc::Sender<UnsafeMessage>> = Lazy::new(||
51+
GLOBAL_MESSAGE_SENDER
52+
.get().unwrap()
53+
.lock().unwrap()
54+
.clone()
55+
);
56+
}
57+
LOCAL_MESSAGE_SENDER.with(|sender| sender.send(self))
58+
}
59+
}
60+
2661
fn create_jit_module<'tcx>(
2762
tcx: TyCtxt<'tcx>,
2863
backend_config: &BackendConfig,
@@ -116,11 +151,6 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, backend_config: BackendConfig) -> ! {
116151
.chain(backend_config.jit_args.iter().map(|arg| &**arg))
117152
.map(|arg| CString::new(arg).unwrap())
118153
.collect::<Vec<_>>();
119-
let mut argv = args.iter().map(|arg| arg.as_ptr()).collect::<Vec<_>>();
120-
121-
// Push a null pointer as a terminating argument. This is required by POSIX and
122-
// useful as some dynamic linkers use it as a marker to jump over.
123-
argv.push(std::ptr::null());
124154

125155
let start_sig = Signature {
126156
params: vec![
@@ -141,12 +171,49 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, backend_config: BackendConfig) -> ! {
141171

142172
let f: extern "C" fn(c_int, *const *const c_char) -> c_int =
143173
unsafe { ::std::mem::transmute(finalized_start) };
144-
let ret = f(args.len() as c_int, argv.as_ptr());
145-
std::process::exit(ret);
174+
175+
let (tx, rx) = mpsc::channel();
176+
GLOBAL_MESSAGE_SENDER.set(Mutex::new(tx)).unwrap();
177+
178+
// Spawn the jitted runtime in a new thread so that this rustc thread can handle messages
179+
// (eg to lazily JIT further functions as required)
180+
std::thread::spawn(move || {
181+
let mut argv = args.iter().map(|arg| arg.as_ptr()).collect::<Vec<_>>();
182+
183+
// Push a null pointer as a terminating argument. This is required by POSIX and
184+
// useful as some dynamic linkers use it as a marker to jump over.
185+
argv.push(std::ptr::null());
186+
187+
let ret = f(args.len() as c_int, argv.as_ptr());
188+
std::process::exit(ret);
189+
});
190+
191+
// Handle messages
192+
loop {
193+
match rx.recv().unwrap() {
194+
// lazy JIT compilation request - compile requested instance and return pointer to result
195+
UnsafeMessage::JitFn { instance_ptr, tx } => {
196+
tx.send(jit_fn(instance_ptr))
197+
.expect("jitted runtime hung up before response to lazy JIT request was sent");
198+
}
199+
}
200+
}
146201
}
147202

148203
#[no_mangle]
149204
extern "C" fn __clif_jit_fn(instance_ptr: *const Instance<'static>) -> *const u8 {
205+
// send the JIT request to the rustc thread, with a channel for the response
206+
let (tx, rx) = mpsc::channel();
207+
UnsafeMessage::JitFn { instance_ptr, tx }
208+
.send()
209+
.expect("rustc thread hung up before lazy JIT request was sent");
210+
211+
// block on JIT compilation result
212+
rx.recv()
213+
.expect("rustc thread hung up before responding to sent lazy JIT request")
214+
}
215+
216+
fn jit_fn(instance_ptr: *const Instance<'static>) -> *const u8 {
150217
rustc_middle::ty::tls::with(|tcx| {
151218
// lift is used to ensure the correct lifetime for instance.
152219
let instance = tcx.lift(unsafe { *instance_ptr }).unwrap();

src/lib.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
#![feature(rustc_private, decl_macro, never_type, hash_drain_filter, vec_into_raw_parts)]
1+
#![feature(
2+
rustc_private,
3+
decl_macro,
4+
never_type,
5+
hash_drain_filter,
6+
vec_into_raw_parts,
7+
once_cell,
8+
)]
29
#![warn(rust_2018_idioms)]
310
#![warn(unused_lifetimes)]
411
#![warn(unreachable_pub)]

0 commit comments

Comments
 (0)