@@ -20,7 +20,7 @@ use alloy_primitives::Bytes;
2020use crossbeam_channel as chan;
2121use rayon:: { ThreadPool , ThreadPoolBuilder } ;
2222#[ cfg( feature = "llvm" ) ]
23- use std:: { fs:: File , io:: Read , time:: Instant } ;
23+ use std:: { cell :: RefCell , fs:: File , io:: Read , time:: Instant } ;
2424use std:: {
2525 sync:: {
2626 Arc ,
@@ -188,6 +188,7 @@ impl WorkerPool {
188188 ThreadPoolBuilder :: new ( )
189189 . num_threads ( worker_count)
190190 . thread_name ( |i| format ! ( "revmc-{i:02}" ) )
191+ . exit_handler ( |_| clear_thread_local_compilers ( ) )
191192 . build ( )
192193 . expect ( "failed to spawn compile workers" )
193194 } ) ;
@@ -240,6 +241,9 @@ impl WorkerPool {
240241 /// Shuts down all workers after draining queued jobs.
241242 pub ( crate ) fn shutdown ( & mut self ) {
242243 self . shutdown . store ( true , Ordering :: Release ) ;
244+ if let Some ( pool) = & self . pool {
245+ pool. broadcast ( |_| clear_thread_local_compilers ( ) ) ;
246+ }
243247 self . pool . take ( ) ;
244248 }
245249}
@@ -250,9 +254,34 @@ impl Drop for WorkerPool {
250254 }
251255}
252256
257+ #[ cfg( feature = "llvm" ) ]
258+ fn clear_thread_local_compilers ( ) {
259+ JIT_COMPILER . with_borrow_mut ( Option :: take) ;
260+ AOT_COMPILER . with_borrow_mut ( Option :: take) ;
261+ }
262+
263+ #[ cfg( not( feature = "llvm" ) ) ]
264+ fn clear_thread_local_compilers ( ) { }
265+
253266#[ cfg( feature = "llvm" ) ]
254267fn compile_job ( job : CompileJob , config : & RuntimeConfig ) -> WorkerResult {
255268 trace ! ( ?job, "received job" ) ;
269+ match job. kind {
270+ CompilationKind :: Jit => {
271+ JIT_COMPILER . with_borrow_mut ( |state| compile_with_state ( job, config, state) )
272+ }
273+ CompilationKind :: Aot => {
274+ AOT_COMPILER . with_borrow_mut ( |state| compile_with_state ( job, config, state) )
275+ }
276+ }
277+ }
278+
279+ #[ cfg( feature = "llvm" ) ]
280+ fn compile_with_state (
281+ job : CompileJob ,
282+ config : & RuntimeConfig ,
283+ state_slot : & mut Option < CompilerState > ,
284+ ) -> WorkerResult {
256285 let _span = match job. kind {
257286 CompilationKind :: Jit => {
258287 debug_span ! ( "jit_compile" , hash=%job. key. code_hash, spec_id=?job. key. spec_id) . entered ( )
@@ -263,21 +292,26 @@ fn compile_job(job: CompileJob, config: &RuntimeConfig) -> WorkerResult {
263292 } ;
264293 let t0 = Instant :: now ( ) ;
265294
266- let mut compiler = match create_compiler ( config, job. kind == CompilationKind :: Aot ) {
267- Ok ( compiler) => compiler,
268- Err ( e) => {
269- error ! ( error = %e, "failed to create LLVM backend" ) ;
270- return WorkerResult {
271- key : job. key ,
272- outcome : Err ( e) ,
273- kind : job. kind ,
274- sync_notifier : job. sync_notifier ,
275- generation : job. generation ,
276- compile_duration : t0. elapsed ( ) ,
277- timings : CompileTimings :: default ( ) ,
278- } ;
295+ if state_slot. is_none ( ) {
296+ match CompilerState :: new ( config, job. kind ) {
297+ Ok ( s) => * state_slot = Some ( s) ,
298+ Err ( e) => {
299+ error ! ( error = %e, "failed to create LLVM backend" ) ;
300+ return WorkerResult {
301+ key : job. key ,
302+ outcome : Err ( e) ,
303+ kind : job. kind ,
304+ sync_notifier : job. sync_notifier ,
305+ generation : job. generation ,
306+ compile_duration : t0. elapsed ( ) ,
307+ timings : CompileTimings :: default ( ) ,
308+ } ;
309+ }
279310 }
280- } ;
311+ }
312+
313+ let state = state_slot. as_mut ( ) . unwrap ( ) ;
314+ let compiler = & mut state. compiler ;
281315
282316 if job. kind == CompilationKind :: Jit
283317 && let Some ( base) = & config. dump_dir
@@ -289,11 +323,32 @@ fn compile_job(job: CompileJob, config: &RuntimeConfig) -> WorkerResult {
289323 compiler. set_opt_level ( job. opt_level ) ;
290324
291325 let outcome = match job. kind {
292- CompilationKind :: Jit => compile_jit_artifact ( & job, & mut compiler) ,
293- CompilationKind :: Aot => compile_aot_artifact ( & job, & mut compiler) ,
326+ CompilationKind :: Jit => compile_jit_artifact ( & job, compiler) ,
327+ CompilationKind :: Aot => compile_aot_artifact ( & job, compiler) ,
294328 } ;
295329 let timings = compiler. take_timings ( ) ;
296330
331+ if let Err ( err) = compiler. clear_ir ( ) {
332+ warn ! ( %err, "clear_ir failed" ) ;
333+ }
334+
335+ state. compilations_since_recycle += 1 ;
336+ if config. tuning . compiler_recycle_threshold > 0
337+ && state. compilations_since_recycle >= config. tuning . compiler_recycle_threshold
338+ {
339+ debug ! ( compilations_since_recycle = state. compilations_since_recycle, "recycling compiler" ) ;
340+ match CompilerState :: new ( config, job. kind ) {
341+ Ok ( new_state) => {
342+ * state_slot = Some ( new_state) ;
343+ revmc_llvm:: global_gc ( ) ;
344+ }
345+ Err ( e) => {
346+ error ! ( error = %e, "failed to recreate compiler" ) ;
347+ state. compilations_since_recycle = 0 ;
348+ }
349+ }
350+ }
351+
297352 WorkerResult {
298353 key : job. key ,
299354 outcome,
@@ -305,6 +360,28 @@ fn compile_job(job: CompileJob, config: &RuntimeConfig) -> WorkerResult {
305360 }
306361}
307362
363+ #[ cfg( feature = "llvm" ) ]
364+ struct CompilerState {
365+ compiler : EvmCompiler < EvmLlvmBackend > ,
366+ compilations_since_recycle : usize ,
367+ }
368+
369+ #[ cfg( feature = "llvm" ) ]
370+ impl CompilerState {
371+ fn new ( config : & RuntimeConfig , kind : CompilationKind ) -> Result < Self , String > {
372+ Ok ( Self {
373+ compiler : create_compiler ( config, kind == CompilationKind :: Aot ) ?,
374+ compilations_since_recycle : 0 ,
375+ } )
376+ }
377+ }
378+
379+ #[ cfg( feature = "llvm" ) ]
380+ thread_local ! {
381+ static JIT_COMPILER : RefCell <Option <CompilerState >> = const { RefCell :: new( None ) } ;
382+ static AOT_COMPILER : RefCell <Option <CompilerState >> = const { RefCell :: new( None ) } ;
383+ }
384+
308385#[ cfg( feature = "llvm" ) ]
309386fn create_compiler (
310387 config : & RuntimeConfig ,
0 commit comments