@@ -6,8 +6,9 @@ use std::slice;
66use crate :: asm:: Label ;
77use crate :: backend:: current:: { Reg , ALLOC_REGS } ;
88use crate :: invariants:: { track_bop_assumption, track_cme_assumption, track_single_ractor_assumption, track_stable_constant_names_assumption} ;
9- use crate :: gc:: { append_gc_offsets, get_or_create_iseq_payload, get_or_create_iseq_payload_ptr, IseqStatus } ;
9+ use crate :: gc:: { append_gc_offsets, get_or_create_iseq_payload, get_or_create_iseq_payload_ptr, IseqPayload , IseqStatus } ;
1010use crate :: state:: ZJITState ;
11+ use crate :: stats:: incr_counter;
1112use crate :: stats:: { counter_ptr, with_time_stat, Counter , Counter :: compile_time_ns} ;
1213use crate :: { asm:: CodeBlock , cruby:: * , options:: debug, virtualmem:: CodePtr } ;
1314use crate :: backend:: lir:: { self , asm_comment, asm_ccall, Assembler , Opnd , Target , CFP , C_ARG_OPNDS , C_RET_OPND , EC , NATIVE_STACK_PTR , NATIVE_BASE_PTR , SCRATCH_OPND , SP } ;
@@ -112,11 +113,14 @@ fn gen_iseq_entry_point(iseq: IseqPtr) -> *const u8 {
112113/// Compile an entry point for a given ISEQ
113114fn gen_iseq_entry_point_body ( cb : & mut CodeBlock , iseq : IseqPtr ) -> Option < CodePtr > {
114115 // Compile ISEQ into High-level IR
115- let function = compile_iseq ( iseq) ?;
116+ let Some ( function) = compile_iseq ( iseq) else {
117+ incr_counter ! ( compilation_failure) ;
118+ return None ;
119+ } ;
116120
117121 // Compile the High-level IR
118- let Some ( ( start_ptr, gc_offsets , jit ) ) = gen_function ( cb, iseq, & function) else {
119- debug ! ( "Failed to compile iseq: gen_function failed: {}" , iseq_get_location( iseq, 0 ) ) ;
122+ let Some ( start_ptr) = gen_iseq ( cb, iseq, Some ( & function) ) else {
123+ debug ! ( "Failed to compile iseq: gen_iseq failed: {}" , iseq_get_location( iseq, 0 ) ) ;
120124 return None ;
121125 } ;
122126
@@ -126,17 +130,6 @@ fn gen_iseq_entry_point_body(cb: &mut CodeBlock, iseq: IseqPtr) -> Option<CodePt
126130 return None ;
127131 } ;
128132
129- // Stub callee ISEQs for JIT-to-JIT calls
130- for iseq_call in jit. iseq_calls . iter ( ) {
131- gen_iseq_call ( cb, iseq, iseq_call) ?;
132- }
133-
134- // Remember the block address to reuse it later
135- let payload = get_or_create_iseq_payload ( iseq) ;
136- payload. status = IseqStatus :: Compiled ( start_ptr) ;
137- payload. iseq_calls . extend ( jit. iseq_calls ) ;
138- append_gc_offsets ( iseq, & gc_offsets) ;
139-
140133 // Return a JIT code address
141134 Some ( entry_ptr)
142135}
@@ -194,53 +187,64 @@ fn gen_entry(cb: &mut CodeBlock, iseq: IseqPtr, function: &Function, function_pt
194187 println ! ( "LIR:\n JIT entry for {}:\n {:?}" , iseq_name( iseq) , asm) ;
195188 }
196189
197- let result = asm. compile ( cb) . map ( |( start_ptr, _) | start_ptr) ;
198- if let Some ( start_addr) = result {
199- if get_option ! ( perf) {
200- let start_ptr = start_addr. raw_ptr ( cb) as usize ;
201- let end_ptr = cb. get_write_ptr ( ) . raw_ptr ( cb) as usize ;
202- let code_size = end_ptr - start_ptr;
203- let iseq_name = iseq_get_location ( iseq, 0 ) ;
204- register_with_perf ( format ! ( "entry for {iseq_name}" ) , start_ptr, code_size) ;
205- }
190+ let ( code_ptr, gc_offsets) = asm. compile ( cb) ?;
191+ assert ! ( gc_offsets. is_empty( ) ) ;
192+ if get_option ! ( perf) {
193+ let start_ptr = code_ptr. raw_ptr ( cb) as usize ;
194+ let end_ptr = cb. get_write_ptr ( ) . raw_ptr ( cb) as usize ;
195+ let code_size = end_ptr - start_ptr;
196+ let iseq_name = iseq_get_location ( iseq, 0 ) ;
197+ register_with_perf ( format ! ( "entry for {iseq_name}" ) , start_ptr, code_size) ;
206198 }
207- result
199+ Some ( code_ptr )
208200}
209201
210- /// Compile an ISEQ into machine code
211- fn gen_iseq ( cb : & mut CodeBlock , iseq : IseqPtr ) -> Option < ( CodePtr , Vec < Rc < RefCell < IseqCall > > > ) > {
202+ /// Compile an ISEQ into machine code if not compiled yet
203+ fn gen_iseq ( cb : & mut CodeBlock , iseq : IseqPtr , function : Option < & Function > ) -> Option < CodePtr > {
212204 // Return an existing pointer if it's already compiled
213205 let payload = get_or_create_iseq_payload ( iseq) ;
214206 match payload. status {
215- IseqStatus :: Compiled ( start_ptr) => return Some ( ( start_ptr, vec ! [ ] ) ) ,
207+ IseqStatus :: Compiled ( start_ptr) => return Some ( start_ptr) ,
216208 IseqStatus :: CantCompile => return None ,
217209 IseqStatus :: NotCompiled => { } ,
218210 }
219211
220- // Convert ISEQ into High-level IR and optimize HIR
221- let function = match compile_iseq ( iseq) {
212+ // Compile the ISEQ
213+ let code_ptr = gen_iseq_body ( cb, iseq, function, payload) ;
214+ if let Some ( start_ptr) = code_ptr {
215+ payload. status = IseqStatus :: Compiled ( start_ptr) ;
216+ incr_counter ! ( compiled_iseq_count) ;
217+ } else {
218+ payload. status = IseqStatus :: CantCompile ;
219+ incr_counter ! ( compilation_failure) ;
220+ }
221+ code_ptr
222+ }
223+
224+ /// Compile an ISEQ into machine code
225+ fn gen_iseq_body ( cb : & mut CodeBlock , iseq : IseqPtr , function : Option < & Function > , payload : & mut IseqPayload ) -> Option < CodePtr > {
226+ // Convert ISEQ into optimized High-level IR if not given
227+ let function = match function {
222228 Some ( function) => function,
223- None => {
224- payload. status = IseqStatus :: CantCompile ;
225- return None ;
226- }
229+ None => & compile_iseq ( iseq) ?,
227230 } ;
228231
229232 // Compile the High-level IR
230- let result = gen_function ( cb, iseq, & function) ;
231- if let Some ( ( start_ptr, gc_offsets, jit) ) = result {
232- payload. status = IseqStatus :: Compiled ( start_ptr) ;
233- payload. iseq_calls . extend ( jit. iseq_calls . clone ( ) ) ;
234- append_gc_offsets ( iseq, & gc_offsets) ;
235- Some ( ( start_ptr, jit. iseq_calls ) )
236- } else {
237- payload. status = IseqStatus :: CantCompile ;
238- None
233+ let ( start_ptr, gc_offsets, iseq_calls) = gen_function ( cb, iseq, function) ?;
234+
235+ // Stub callee ISEQs for JIT-to-JIT calls
236+ for iseq_call in iseq_calls. iter ( ) {
237+ gen_iseq_call ( cb, iseq, iseq_call) ?;
239238 }
239+
240+ // Prepare for GC
241+ payload. iseq_calls . extend ( iseq_calls. clone ( ) ) ;
242+ append_gc_offsets ( iseq, & gc_offsets) ;
243+ Some ( start_ptr)
240244}
241245
242246/// Compile a function
243- fn gen_function ( cb : & mut CodeBlock , iseq : IseqPtr , function : & Function ) -> Option < ( CodePtr , Vec < CodePtr > , JITState ) > {
247+ fn gen_function ( cb : & mut CodeBlock , iseq : IseqPtr , function : & Function ) -> Option < ( CodePtr , Vec < CodePtr > , Vec < Rc < RefCell < IseqCall > > > ) > {
244248 let c_stack_slots = max_num_params ( function) . saturating_sub ( ALLOC_REGS . len ( ) ) ;
245249 let mut jit = JITState :: new ( iseq, function. num_insns ( ) , function. num_blocks ( ) , c_stack_slots) ;
246250 let mut asm = Assembler :: new ( ) ;
@@ -279,6 +283,7 @@ fn gen_function(cb: &mut CodeBlock, iseq: IseqPtr, function: &Function) -> Optio
279283 let insn = function. find ( insn_id) ;
280284 if gen_insn ( cb, & mut jit, & mut asm, function, insn_id, & insn) . is_none ( ) {
281285 debug ! ( "Failed to compile insn: {insn_id} {insn}" ) ;
286+ incr_counter ! ( failed_gen_insn) ;
282287 return None ;
283288 }
284289 }
@@ -291,8 +296,8 @@ fn gen_function(cb: &mut CodeBlock, iseq: IseqPtr, function: &Function) -> Optio
291296 }
292297
293298 // Generate code if everything can be compiled
294- let result = asm. compile ( cb) . map ( | ( start_ptr , gc_offsets ) | ( start_ptr , gc_offsets , jit ) ) ;
295- if let Some ( ( start_ptr, _, _ ) ) = result {
299+ let result = asm. compile ( cb) ;
300+ if let Some ( ( start_ptr, _) ) = result {
296301 if get_option ! ( perf) {
297302 let start_usize = start_ptr. raw_ptr ( cb) as usize ;
298303 let end_usize = cb. get_write_ptr ( ) . raw_ptr ( cb) as usize ;
@@ -304,8 +309,10 @@ fn gen_function(cb: &mut CodeBlock, iseq: IseqPtr, function: &Function) -> Optio
304309 let iseq_name = iseq_get_location ( iseq, 0 ) ;
305310 ZJITState :: log_compile ( iseq_name) ;
306311 }
312+ } else {
313+ incr_counter ! ( failed_asm_compile) ;
307314 }
308- result
315+ result. map ( | ( start_ptr , gc_offsets ) | ( start_ptr , gc_offsets , jit . iseq_calls ) )
309316}
310317
311318/// Compile an instruction
@@ -410,6 +417,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
410417 | Insn :: Const { .. }
411418 => {
412419 debug ! ( "ZJIT: gen_function: unexpected insn {insn}" ) ;
420+ incr_counter ! ( failed_gen_insn_unexpected) ;
413421 return None ;
414422 }
415423 } ;
@@ -1372,6 +1380,7 @@ fn compile_iseq(iseq: IseqPtr) -> Option<Function> {
13721380 Err ( err) => {
13731381 let name = crate :: cruby:: iseq_get_location ( iseq, 0 ) ;
13741382 debug ! ( "ZJIT: iseq_to_hir: {err:?}: {name}" ) ;
1383+ incr_counter ! ( failed_hir_compile) ;
13751384 return None ;
13761385 }
13771386 } ;
@@ -1382,6 +1391,7 @@ fn compile_iseq(iseq: IseqPtr) -> Option<Function> {
13821391 #[ cfg( debug_assertions) ]
13831392 if let Err ( err) = function. validate ( ) {
13841393 debug ! ( "ZJIT: compile_iseq: {err:?}" ) ;
1394+ incr_counter ! ( failed_hir_optimize) ;
13851395 return None ;
13861396 }
13871397 Some ( function)
@@ -1516,16 +1526,11 @@ c_callable! {
15161526/// Compile an ISEQ for a function stub
15171527fn function_stub_hit_body ( cb : & mut CodeBlock , iseq_call : & Rc < RefCell < IseqCall > > ) -> Option < CodePtr > {
15181528 // Compile the stubbed ISEQ
1519- let Some ( ( code_ptr, iseq_calls ) ) = gen_iseq ( cb, iseq_call. borrow ( ) . iseq ) else {
1529+ let Some ( code_ptr) = gen_iseq ( cb, iseq_call. borrow ( ) . iseq , None ) else {
15201530 debug ! ( "Failed to compile iseq: gen_iseq failed: {}" , iseq_get_location( iseq_call. borrow( ) . iseq, 0 ) ) ;
15211531 return None ;
15221532 } ;
15231533
1524- // Stub callee ISEQs for JIT-to-JIT calls
1525- for callee_iseq_call in iseq_calls. iter ( ) {
1526- gen_iseq_call ( cb, iseq_call. borrow ( ) . iseq , callee_iseq_call) ?;
1527- }
1528-
15291534 // Update the stub to call the code pointer
15301535 let code_addr = code_ptr. raw_ptr ( cb) ;
15311536 let iseq = iseq_call. borrow ( ) . iseq ;
0 commit comments