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,39 @@ 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
+ 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
+
26
61
fn create_jit_module < ' tcx > (
27
62
tcx : TyCtxt < ' tcx > ,
28
63
backend_config : & BackendConfig ,
@@ -116,11 +151,6 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, backend_config: BackendConfig) -> ! {
116
151
. chain ( backend_config. jit_args . iter ( ) . map ( |arg| & * * arg) )
117
152
. map ( |arg| CString :: new ( arg) . unwrap ( ) )
118
153
. 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
154
125
155
let start_sig = Signature {
126
156
params : vec ! [
@@ -141,12 +171,49 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, backend_config: BackendConfig) -> ! {
141
171
142
172
let f: extern "C" fn ( c_int , * const * const c_char ) -> c_int =
143
173
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
+ }
146
201
}
147
202
148
203
#[ no_mangle]
149
204
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 {
150
217
rustc_middle:: ty:: tls:: with ( |tcx| {
151
218
// lift is used to ensure the correct lifetime for instance.
152
219
let instance = tcx. lift ( unsafe { * instance_ptr } ) . unwrap ( ) ;
0 commit comments