3
3
4
4
use std:: cell:: RefCell ;
5
5
use std:: ffi:: CString ;
6
+ use std:: lazy:: { Lazy , SyncOnceCell } ;
6
7
use std:: os:: raw:: { c_char, c_int} ;
8
+ use std:: sync:: { mpsc, Mutex } ;
7
9
8
10
use cranelift_codegen:: binemit:: { NullStackMapSink , NullTrapSink } ;
9
11
use rustc_codegen_ssa:: CrateInfo ;
@@ -23,6 +25,40 @@ thread_local! {
23
25
static LAZY_JIT_STATE : RefCell <Option <JitState >> = RefCell :: new( None ) ;
24
26
}
25
27
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
+ trampoline_ptr : * const u8 ,
41
+ tx : mpsc:: Sender < * const u8 > ,
42
+ } ,
43
+ }
44
+ unsafe impl Send for UnsafeMessage { }
45
+
46
+ impl UnsafeMessage {
47
+ /// Send the message.
48
+ fn send ( self ) -> Result < ( ) , mpsc:: SendError < UnsafeMessage > > {
49
+ thread_local ! {
50
+ /// The Sender owned by the local thread
51
+ static LOCAL_MESSAGE_SENDER : Lazy <mpsc:: Sender <UnsafeMessage >> = Lazy :: new( ||
52
+ GLOBAL_MESSAGE_SENDER
53
+ . get( ) . unwrap( )
54
+ . lock( ) . unwrap( )
55
+ . clone( )
56
+ ) ;
57
+ }
58
+ LOCAL_MESSAGE_SENDER . with ( |sender| sender. send ( self ) )
59
+ }
60
+ }
61
+
26
62
fn create_jit_module < ' tcx > (
27
63
tcx : TyCtxt < ' tcx > ,
28
64
backend_config : & BackendConfig ,
@@ -116,11 +152,6 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, backend_config: BackendConfig) -> ! {
116
152
. chain ( backend_config. jit_args . iter ( ) . map ( |arg| & * * arg) )
117
153
. map ( |arg| CString :: new ( arg) . unwrap ( ) )
118
154
. 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 ( ) ) ;
124
155
125
156
let start_sig = Signature {
126
157
params : vec ! [
@@ -141,12 +172,49 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, backend_config: BackendConfig) -> ! {
141
172
142
173
let f: extern "C" fn ( c_int , * const * const c_char ) -> c_int =
143
174
unsafe { :: std:: mem:: transmute ( finalized_start) } ;
144
- let ret = f ( args. len ( ) as c_int , argv. as_ptr ( ) ) ;
145
- std:: process:: exit ( ret) ;
175
+
176
+ let ( tx, rx) = mpsc:: channel ( ) ;
177
+ GLOBAL_MESSAGE_SENDER . set ( Mutex :: new ( tx) ) . unwrap ( ) ;
178
+
179
+ // Spawn the jitted runtime in a new thread so that this rustc thread can handle messages
180
+ // (eg to lazily JIT further functions as required)
181
+ std:: thread:: spawn ( move || {
182
+ let mut argv = args. iter ( ) . map ( |arg| arg. as_ptr ( ) ) . collect :: < Vec < _ > > ( ) ;
183
+
184
+ // Push a null pointer as a terminating argument. This is required by POSIX and
185
+ // useful as some dynamic linkers use it as a marker to jump over.
186
+ argv. push ( std:: ptr:: null ( ) ) ;
187
+
188
+ let ret = f ( args. len ( ) as c_int , argv. as_ptr ( ) ) ;
189
+ std:: process:: exit ( ret) ;
190
+ } ) ;
191
+
192
+ // Handle messages
193
+ loop {
194
+ match rx. recv ( ) . unwrap ( ) {
195
+ // lazy JIT compilation request - compile requested instance and return pointer to result
196
+ UnsafeMessage :: JitFn { instance_ptr, trampoline_ptr, tx } => {
197
+ tx. send ( jit_fn ( instance_ptr, trampoline_ptr) )
198
+ . expect ( "jitted runtime hung up before response to lazy JIT request was sent" ) ;
199
+ }
200
+ }
201
+ }
146
202
}
147
203
148
204
#[ no_mangle]
149
- extern "C" fn __clif_jit_fn ( instance_ptr : * const Instance < ' static > ) -> * const u8 {
205
+ extern "C" fn __clif_jit_fn ( instance_ptr : * const Instance < ' static > , trampoline_ptr : * const u8 ) -> * const u8 {
206
+ // send the JIT request to the rustc thread, with a channel for the response
207
+ let ( tx, rx) = mpsc:: channel ( ) ;
208
+ UnsafeMessage :: JitFn { instance_ptr, trampoline_ptr, tx }
209
+ . send ( )
210
+ . expect ( "rustc thread hung up before lazy JIT request was sent" ) ;
211
+
212
+ // block on JIT compilation result
213
+ rx. recv ( )
214
+ . expect ( "rustc thread hung up before responding to sent lazy JIT request" )
215
+ }
216
+
217
+ fn jit_fn ( instance_ptr : * const Instance < ' static > , trampoline_ptr : * const u8 ) -> * const u8 {
150
218
rustc_middle:: ty:: tls:: with ( |tcx| {
151
219
// lift is used to ensure the correct lifetime for instance.
152
220
let instance = tcx. lift ( unsafe { * instance_ptr } ) . unwrap ( ) ;
@@ -160,6 +228,17 @@ extern "C" fn __clif_jit_fn(instance_ptr: *const Instance<'static>) -> *const u8
160
228
let name = tcx. symbol_name ( instance) . name ;
161
229
let sig = crate :: abi:: get_function_sig ( tcx, jit_module. isa ( ) . triple ( ) , instance) ;
162
230
let func_id = jit_module. declare_function ( name, Linkage :: Export , & sig) . unwrap ( ) ;
231
+
232
+ let current_ptr = jit_module. read_got_entry ( func_id) ;
233
+
234
+ // If the function's GOT entry has already been updated to point at something other
235
+ // than the shim trampoline, don't re-jit but just return the new pointer instead.
236
+ // This does not need synchronization as this code is executed only by a sole rustc
237
+ // thread.
238
+ if current_ptr != trampoline_ptr {
239
+ return current_ptr;
240
+ }
241
+
163
242
jit_module. prepare_for_function_redefine ( func_id) . unwrap ( ) ;
164
243
165
244
let mut cx = crate :: CodegenCx :: new ( tcx, backend_config, jit_module. isa ( ) , false ) ;
@@ -254,7 +333,7 @@ fn codegen_shim<'tcx>(cx: &mut CodegenCx<'tcx>, module: &mut JITModule, inst: In
254
333
Linkage :: Import ,
255
334
& Signature {
256
335
call_conv : module. target_config ( ) . default_call_conv ,
257
- params : vec ! [ AbiParam :: new( pointer_type) ] ,
336
+ params : vec ! [ AbiParam :: new( pointer_type) , AbiParam :: new ( pointer_type ) ] ,
258
337
returns : vec ! [ AbiParam :: new( pointer_type) ] ,
259
338
} ,
260
339
)
@@ -267,6 +346,7 @@ fn codegen_shim<'tcx>(cx: &mut CodegenCx<'tcx>, module: &mut JITModule, inst: In
267
346
let mut builder_ctx = FunctionBuilderContext :: new ( ) ;
268
347
let mut trampoline_builder = FunctionBuilder :: new ( trampoline, & mut builder_ctx) ;
269
348
349
+ let trampoline_fn = module. declare_func_in_func ( func_id, trampoline_builder. func ) ;
270
350
let jit_fn = module. declare_func_in_func ( jit_fn, trampoline_builder. func ) ;
271
351
let sig_ref = trampoline_builder. func . import_signature ( sig) ;
272
352
@@ -276,7 +356,8 @@ fn codegen_shim<'tcx>(cx: &mut CodegenCx<'tcx>, module: &mut JITModule, inst: In
276
356
277
357
trampoline_builder. switch_to_block ( entry_block) ;
278
358
let instance_ptr = trampoline_builder. ins ( ) . iconst ( pointer_type, instance_ptr as u64 as i64 ) ;
279
- let jitted_fn = trampoline_builder. ins ( ) . call ( jit_fn, & [ instance_ptr] ) ;
359
+ let trampoline_ptr = trampoline_builder. ins ( ) . func_addr ( pointer_type, trampoline_fn) ;
360
+ let jitted_fn = trampoline_builder. ins ( ) . call ( jit_fn, & [ instance_ptr, trampoline_ptr] ) ;
280
361
let jitted_fn = trampoline_builder. func . dfg . inst_results ( jitted_fn) [ 0 ] ;
281
362
let call_inst = trampoline_builder. ins ( ) . call_indirect ( sig_ref, jitted_fn, & fn_args) ;
282
363
let ret_vals = trampoline_builder. func . dfg . inst_results ( call_inst) . to_vec ( ) ;
0 commit comments