@@ -16,7 +16,7 @@ use crate::stats::{exit_counter_for_compile_error, incr_counter, incr_counter_by
1616use crate :: stats:: { counter_ptr, with_time_stat, Counter , send_fallback_counter, Counter :: { compile_time_ns, exit_compile_error} } ;
1717use crate :: { asm:: CodeBlock , cruby:: * , options:: debug, virtualmem:: CodePtr } ;
1818use 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 } ;
19- use crate :: hir:: { iseq_to_hir, Block , BlockId , BranchEdge , Invariant , RangeType , SideExitReason , SideExitReason :: * , MethodType , SpecialObjectType , SpecialBackrefSymbol , SELF_PARAM_IDX } ;
19+ use crate :: hir:: { iseq_to_hir, Block , BlockId , BranchEdge , Invariant , MethodType , RangeType , SideExitReason :: { self , * } , SpecialBackrefSymbol , SpecialObjectType , SELF_PARAM_IDX } ;
2020use crate :: hir:: { Const , FrameState , Function , Insn , InsnId } ;
2121use crate :: hir_type:: { types, Type } ;
2222use crate :: options:: get_option;
@@ -34,7 +34,7 @@ struct JITState {
3434 labels : Vec < Option < Target > > ,
3535
3636 /// JIT entry point for the `iseq`
37- jit_entry : Option < Rc < RefCell < JITEntry > > > ,
37+ jit_entries : Vec < Rc < RefCell < JITEntry > > > ,
3838
3939 /// ISEQ calls that need to be compiled later
4040 iseq_calls : Vec < Rc < RefCell < IseqCall > > > ,
@@ -50,7 +50,7 @@ impl JITState {
5050 iseq,
5151 opnds : vec ! [ None ; num_insns] ,
5252 labels : vec ! [ None ; num_blocks] ,
53- jit_entry : None ,
53+ jit_entries : Vec :: default ( ) ,
5454 iseq_calls : Vec :: default ( ) ,
5555 c_stack_slots,
5656 }
@@ -228,7 +228,7 @@ fn gen_iseq_body(cb: &mut CodeBlock, iseq: IseqPtr, function: Option<&Function>,
228228 } ;
229229
230230 // Compile the High-level IR
231- let ( start_ptr , jit_entry_ptr , gc_offsets, iseq_calls) = gen_function ( cb, iseq, function) ?;
231+ let ( iseq_code_ptrs , gc_offsets, iseq_calls) = gen_function ( cb, iseq, function) ?;
232232
233233 // Stub callee ISEQs for JIT-to-JIT calls
234234 for iseq_call in iseq_calls. iter ( ) {
@@ -238,11 +238,11 @@ fn gen_iseq_body(cb: &mut CodeBlock, iseq: IseqPtr, function: Option<&Function>,
238238 // Prepare for GC
239239 payload. iseq_calls . extend ( iseq_calls. clone ( ) ) ;
240240 append_gc_offsets ( iseq, & gc_offsets) ;
241- Ok ( IseqCodePtrs { start_ptr , jit_entry_ptr } )
241+ Ok ( iseq_code_ptrs )
242242}
243243
244244/// Compile a function
245- fn gen_function ( cb : & mut CodeBlock , iseq : IseqPtr , function : & Function ) -> Result < ( CodePtr , CodePtr , Vec < CodePtr > , Vec < IseqCallRef > ) , CompileError > {
245+ fn gen_function ( cb : & mut CodeBlock , iseq : IseqPtr , function : & Function ) -> Result < ( IseqCodePtrs , Vec < CodePtr > , Vec < IseqCallRef > ) , CompileError > {
246246 let c_stack_slots = max_num_params ( function) . saturating_sub ( ALLOC_REGS . len ( ) ) ;
247247 let mut jit = JITState :: new ( iseq, function. num_insns ( ) , function. num_blocks ( ) , c_stack_slots) ;
248248 let mut asm = Assembler :: new ( ) ;
@@ -307,8 +307,13 @@ fn gen_function(cb: &mut CodeBlock, iseq: IseqPtr, function: &Function) -> Resul
307307 }
308308 }
309309 result. map ( |( start_ptr, gc_offsets) | {
310- let jit_entry_ptr = jit. jit_entry . unwrap ( ) . borrow ( ) . start_addr . get ( ) . unwrap ( ) ;
311- ( start_ptr, jit_entry_ptr, gc_offsets, jit. iseq_calls )
310+ // Make sure jit_entry_ptrs can be used as a parallel vector to jit_entry_insns()
311+ jit. jit_entries . sort_by_key ( |jit_entry| jit_entry. borrow ( ) . jit_entry_idx ) ;
312+
313+ let jit_entry_ptrs = jit. jit_entries . iter ( ) . map ( |jit_entry|
314+ jit_entry. borrow ( ) . start_addr . get ( ) . expect ( "start_addr should have been set by pos_marker in gen_entry_point" )
315+ ) . collect ( ) ;
316+ ( IseqCodePtrs { start_ptr, jit_entry_ptrs } , gc_offsets, jit. iseq_calls )
312317 } )
313318}
314319
@@ -375,7 +380,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
375380 // TODO remove this check when we have stack args (we can use Time.new to test it)
376381 Insn :: InvokeBuiltin { bf, state, .. } if bf. argc + 2 > ( C_ARG_OPNDS . len ( ) as i32 ) => return Err ( * state) ,
377382 Insn :: InvokeBuiltin { bf, args, state, .. } => gen_invokebuiltin ( jit, asm, & function. frame_state ( * state) , bf, opnds ! ( args) ) ,
378- & Insn :: EntryPoint { jit_entry } => no_output ! ( gen_entry_point( jit, asm, jit_entry ) ) ,
383+ & Insn :: EntryPoint { jit_entry_idx } => no_output ! ( gen_entry_point( jit, asm, jit_entry_idx ) ) ,
379384 Insn :: Return { val } => no_output ! ( gen_return( asm, opnd!( val) ) ) ,
380385 Insn :: FixnumAdd { left, right, state } => gen_fixnum_add ( jit, asm, opnd ! ( left) , opnd ! ( right) , & function. frame_state ( * state) ) ,
381386 Insn :: FixnumSub { left, right, state } => gen_fixnum_sub ( jit, asm, opnd ! ( left) , opnd ! ( right) , & function. frame_state ( * state) ) ,
@@ -424,7 +429,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
424429 & Insn :: DefinedIvar { self_val, id, pushval, .. } => { gen_defined_ivar ( asm, opnd ! ( self_val) , id, pushval) } ,
425430 & Insn :: ArrayExtend { left, right, state } => { no_output ! ( gen_array_extend( jit, asm, opnd!( left) , opnd!( right) , & function. frame_state( state) ) ) } ,
426431 & Insn :: GuardShape { val, shape, state } => gen_guard_shape ( jit, asm, opnd ! ( val) , shape, & function. frame_state ( state) ) ,
427- Insn :: LoadPC => gen_load_pc ( ) ,
432+ Insn :: LoadPC => gen_load_pc ( asm ) ,
428433 & Insn :: LoadIvarEmbedded { self_val, id, index } => gen_load_ivar_embedded ( asm, opnd ! ( self_val) , id, index) ,
429434 & Insn :: LoadIvarExtended { self_val, id, index } => gen_load_ivar_extended ( asm, opnd ! ( self_val) , id, index) ,
430435 & Insn :: ArrayMax { state, .. }
@@ -825,8 +830,8 @@ fn gen_guard_shape(jit: &mut JITState, asm: &mut Assembler, val: Opnd, shape: Sh
825830 val
826831}
827832
828- fn gen_load_pc ( ) -> Opnd {
829- Opnd :: mem ( 64 , CFP , RUBY_OFFSET_CFP_PC )
833+ fn gen_load_pc ( asm : & mut Assembler ) -> Opnd {
834+ asm . load ( Opnd :: mem ( 64 , CFP , RUBY_OFFSET_CFP_PC ) )
830835}
831836
832837fn gen_load_ivar_embedded ( asm : & mut Assembler , self_val : Opnd , id : ID , index : u16 ) -> Opnd {
@@ -1313,12 +1318,11 @@ fn gen_object_alloc_class(asm: &mut Assembler, class: VALUE, state: &FrameState)
13131318 }
13141319}
13151320
1316- /// Compile a frame setup. If is_jit_entry is true, remember the address of it as a JIT entry.
1317- fn gen_entry_point ( jit : & mut JITState , asm : & mut Assembler , is_jit_entry : bool ) {
1318- if is_jit_entry {
1319- assert ! ( jit. jit_entry. is_none( ) , "only one jit_entry is expected" ) ;
1320- let jit_entry = JITEntry :: new ( ) ;
1321- jit. jit_entry = Some ( jit_entry. clone ( ) ) ;
1321+ /// Compile a frame setup. If jit_entry_idx is Some, remember the address of it as a JIT entry.
1322+ fn gen_entry_point ( jit : & mut JITState , asm : & mut Assembler , jit_entry_idx : Option < usize > ) {
1323+ if let Some ( jit_entry_idx) = jit_entry_idx {
1324+ let jit_entry = JITEntry :: new ( jit_entry_idx) ;
1325+ jit. jit_entries . push ( jit_entry. clone ( ) ) ;
13221326 asm. pos_marker ( move |code_ptr, _| {
13231327 jit_entry. borrow_mut ( ) . start_addr . set ( Some ( code_ptr) ) ;
13241328 } ) ;
@@ -1776,21 +1780,14 @@ fn compile_iseq(iseq: IseqPtr) -> Result<Function, CompileError> {
17761780 let mut function = match iseq_to_hir ( iseq) {
17771781 Ok ( function) => function,
17781782 Err ( err) => {
1779- let name = crate :: cruby:: iseq_get_location ( iseq, 0 ) ;
1780- debug ! ( "ZJIT: iseq_to_hir: {err:?}: {name}" ) ;
1783+ debug ! ( "ZJIT: iseq_to_hir: {err:?}: {}" , iseq_get_location( iseq, 0 ) ) ;
17811784 return Err ( CompileError :: ParseError ( err) ) ;
17821785 }
17831786 } ;
17841787 if !get_option ! ( disable_hir_opt) {
17851788 function. optimize ( ) ;
17861789 }
17871790 function. dump_hir ( ) ;
1788- #[ cfg( debug_assertions) ]
1789- if let Err ( err) = function. validate ( ) {
1790- debug ! ( "ZJIT: compile_iseq: {err:?}" ) ;
1791- use crate :: hir:: ParseError ;
1792- return Err ( CompileError :: ParseError ( ParseError :: Validation ( err) ) ) ;
1793- }
17941791 Ok ( function)
17951792}
17961793
@@ -1928,10 +1925,15 @@ c_callable! {
19281925/// Compile an ISEQ for a function stub
19291926fn function_stub_hit_body ( cb : & mut CodeBlock , iseq_call : & Rc < RefCell < IseqCall > > ) -> Result < CodePtr , CompileError > {
19301927 // Compile the stubbed ISEQ
1931- let IseqCodePtrs { jit_entry_ptr , .. } = gen_iseq ( cb, iseq_call. borrow ( ) . iseq , None ) . inspect_err ( |err| {
1928+ let IseqCodePtrs { jit_entry_ptrs , .. } = gen_iseq ( cb, iseq_call. borrow ( ) . iseq , None ) . inspect_err ( |err| {
19321929 debug ! ( "{err:?}: gen_iseq failed: {}" , iseq_get_location( iseq_call. borrow( ) . iseq, 0 ) ) ;
19331930 } ) ?;
19341931
1932+ // We currently don't support JIT-to-JIT calls for ISEQs with optional arguments.
1933+ // So we only need to use jit_entry_ptrs[0] for now. TODO: Support optional arguments.
1934+ assert_eq ! ( 1 , jit_entry_ptrs. len( ) ) ;
1935+ let jit_entry_ptr = jit_entry_ptrs[ 0 ] ;
1936+
19351937 // Update the stub to call the code pointer
19361938 let code_addr = jit_entry_ptr. raw_ptr ( cb) ;
19371939 let iseq = iseq_call. borrow ( ) . iseq ;
@@ -2131,14 +2133,17 @@ impl Assembler {
21312133
21322134/// Store info about a JIT entry point
21332135pub struct JITEntry {
2136+ /// Index that corresponds to jit_entry_insns()
2137+ jit_entry_idx : usize ,
21342138 /// Position where the entry point starts
21352139 start_addr : Cell < Option < CodePtr > > ,
21362140}
21372141
21382142impl JITEntry {
21392143 /// Allocate a new JITEntry
2140- fn new ( ) -> Rc < RefCell < Self > > {
2144+ fn new ( jit_entry_idx : usize ) -> Rc < RefCell < Self > > {
21412145 let jit_entry = JITEntry {
2146+ jit_entry_idx,
21422147 start_addr : Cell :: new ( None ) ,
21432148 } ;
21442149 Rc :: new ( RefCell :: new ( jit_entry) )
0 commit comments