@@ -10,7 +10,7 @@ use std::slice;
1010use crate :: asm:: Label ;
1111use crate :: backend:: current:: { Reg , ALLOC_REGS } ;
1212use crate :: invariants:: { track_bop_assumption, track_cme_assumption, track_no_ep_escape_assumption, track_no_trace_point_assumption, track_single_ractor_assumption, track_stable_constant_names_assumption} ;
13- use crate :: gc:: { append_gc_offsets, get_or_create_iseq_payload, get_or_create_iseq_payload_ptr, IseqPayload , IseqStatus } ;
13+ use crate :: gc:: { append_gc_offsets, get_or_create_iseq_payload, get_or_create_iseq_payload_ptr, IseqCodePtrs , IseqPayload , IseqStatus } ;
1414use crate :: state:: ZJITState ;
1515use crate :: stats:: { exit_counter_for_compile_error, incr_counter, incr_counter_by, CompileError } ;
1616use crate :: stats:: { counter_ptr, with_time_stat, Counter , send_fallback_counter, Counter :: { compile_time_ns, exit_compile_error} } ;
@@ -33,6 +33,9 @@ struct JITState {
3333 /// Labels for each basic block indexed by the BlockId
3434 labels : Vec < Option < Target > > ,
3535
36+ /// JIT entry point for the `iseq`
37+ jit_entry : Option < Rc < RefCell < JITEntry > > > ,
38+
3639 /// ISEQ calls that need to be compiled later
3740 iseq_calls : Vec < Rc < RefCell < IseqCall > > > ,
3841
@@ -47,6 +50,7 @@ impl JITState {
4750 iseq,
4851 opnds : vec ! [ None ; num_insns] ,
4952 labels : vec ! [ None ; num_blocks] ,
53+ jit_entry : None ,
5054 iseq_calls : Vec :: default ( ) ,
5155 c_stack_slots,
5256 }
@@ -117,7 +121,7 @@ fn gen_iseq_entry_point(cb: &mut CodeBlock, iseq: IseqPtr, jit_exception: bool)
117121 } ) ?;
118122
119123 // Compile the High-level IR
120- let start_ptr = gen_iseq ( cb, iseq, Some ( & function) ) . inspect_err ( |err| {
124+ let IseqCodePtrs { start_ptr, .. } = gen_iseq ( cb, iseq, Some ( & function) ) . inspect_err ( |err| {
121125 debug ! ( "{err:?}: gen_iseq failed: {}" , iseq_get_location( iseq, 0 ) ) ;
122126 } ) ?;
123127
@@ -164,7 +168,7 @@ fn gen_entry(cb: &mut CodeBlock, iseq: IseqPtr, function: &Function, function_pt
164168 // Set up registers for CFP, EC, SP, and basic block arguments
165169 let mut asm = Assembler :: new ( ) ;
166170 gen_entry_prologue ( & mut asm, iseq) ;
167- gen_entry_params ( & mut asm, iseq, function. block ( BlockId ( 0 ) ) ) ;
171+ gen_entry_params ( & mut asm, iseq, function. entry_block ( ) ) ;
168172
169173 // Jump to the first block using a call instruction
170174 asm. ccall ( function_ptr. raw_ptr ( cb) , vec ! [ ] ) ;
@@ -191,40 +195,40 @@ fn gen_entry(cb: &mut CodeBlock, iseq: IseqPtr, function: &Function, function_pt
191195}
192196
193197/// Compile an ISEQ into machine code if not compiled yet
194- fn gen_iseq ( cb : & mut CodeBlock , iseq : IseqPtr , function : Option < & Function > ) -> Result < CodePtr , CompileError > {
198+ fn gen_iseq ( cb : & mut CodeBlock , iseq : IseqPtr , function : Option < & Function > ) -> Result < IseqCodePtrs , CompileError > {
195199 // Return an existing pointer if it's already compiled
196200 let payload = get_or_create_iseq_payload ( iseq) ;
197201 match & payload. status {
198- IseqStatus :: Compiled ( start_ptr ) => return Ok ( * start_ptr ) ,
202+ IseqStatus :: Compiled ( code_ptrs ) => return Ok ( code_ptrs . clone ( ) ) ,
199203 IseqStatus :: CantCompile ( err) => return Err ( err. clone ( ) ) ,
200204 IseqStatus :: NotCompiled => { } ,
201205 }
202206
203207 // Compile the ISEQ
204- let code_ptr = gen_iseq_body ( cb, iseq, function, payload) ;
205- match & code_ptr {
206- Ok ( start_ptr ) => {
207- payload. status = IseqStatus :: Compiled ( * start_ptr ) ;
208+ let code_ptrs = gen_iseq_body ( cb, iseq, function, payload) ;
209+ match & code_ptrs {
210+ Ok ( code_ptrs ) => {
211+ payload. status = IseqStatus :: Compiled ( code_ptrs . clone ( ) ) ;
208212 incr_counter ! ( compiled_iseq_count) ;
209213 }
210214 Err ( err) => {
211215 payload. status = IseqStatus :: CantCompile ( err. clone ( ) ) ;
212216 incr_counter ! ( failed_iseq_count) ;
213217 }
214218 }
215- code_ptr
219+ code_ptrs
216220}
217221
218222/// Compile an ISEQ into machine code
219- fn gen_iseq_body ( cb : & mut CodeBlock , iseq : IseqPtr , function : Option < & Function > , payload : & mut IseqPayload ) -> Result < CodePtr , CompileError > {
223+ fn gen_iseq_body ( cb : & mut CodeBlock , iseq : IseqPtr , function : Option < & Function > , payload : & mut IseqPayload ) -> Result < IseqCodePtrs , CompileError > {
220224 // Convert ISEQ into optimized High-level IR if not given
221225 let function = match function {
222226 Some ( function) => function,
223227 None => & compile_iseq ( iseq) ?,
224228 } ;
225229
226230 // Compile the High-level IR
227- let ( start_ptr, gc_offsets, iseq_calls) = gen_function ( cb, iseq, function) ?;
231+ let ( start_ptr, jit_entry_ptr , gc_offsets, iseq_calls) = gen_function ( cb, iseq, function) ?;
228232
229233 // Stub callee ISEQs for JIT-to-JIT calls
230234 for iseq_call in iseq_calls. iter ( ) {
@@ -234,11 +238,11 @@ fn gen_iseq_body(cb: &mut CodeBlock, iseq: IseqPtr, function: Option<&Function>,
234238 // Prepare for GC
235239 payload. iseq_calls . extend ( iseq_calls. clone ( ) ) ;
236240 append_gc_offsets ( iseq, & gc_offsets) ;
237- Ok ( start_ptr)
241+ Ok ( IseqCodePtrs { start_ptr, jit_entry_ptr } )
238242}
239243
240244/// Compile a function
241- fn gen_function ( cb : & mut CodeBlock , iseq : IseqPtr , function : & Function ) -> Result < ( CodePtr , Vec < CodePtr > , Vec < IseqCallRef > ) , CompileError > {
245+ fn gen_function ( cb : & mut CodeBlock , iseq : IseqPtr , function : & Function ) -> Result < ( CodePtr , CodePtr , Vec < CodePtr > , Vec < IseqCallRef > ) , CompileError > {
242246 let c_stack_slots = max_num_params ( function) . saturating_sub ( ALLOC_REGS . len ( ) ) ;
243247 let mut jit = JITState :: new ( iseq, function. num_insns ( ) , function. num_blocks ( ) , c_stack_slots) ;
244248 let mut asm = Assembler :: new ( ) ;
@@ -257,11 +261,6 @@ fn gen_function(cb: &mut CodeBlock, iseq: IseqPtr, function: &Function) -> Resul
257261 let label = jit. get_label ( & mut asm, block_id) ;
258262 asm. write_label ( label) ;
259263
260- // Set up the frame at the first block. :bb0-prologue:
261- if block_id == BlockId ( 0 ) {
262- asm. frame_setup ( & [ ] , jit. c_stack_slots ) ;
263- }
264-
265264 // Compile all parameters
266265 for & insn_id in block. params ( ) {
267266 match function. find ( insn_id) {
@@ -307,7 +306,10 @@ fn gen_function(cb: &mut CodeBlock, iseq: IseqPtr, function: &Function) -> Resul
307306 ZJITState :: log_compile ( iseq_name) ;
308307 }
309308 }
310- result. map ( |( start_ptr, gc_offsets) | ( start_ptr, gc_offsets, jit. iseq_calls ) )
309+ 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 )
312+ } )
311313}
312314
313315/// Compile an instruction
@@ -338,7 +340,8 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
338340 }
339341
340342 let out_opnd = match insn {
341- Insn :: Const { val : Const :: Value ( val) } => gen_const ( * val) ,
343+ & Insn :: Const { val : Const :: Value ( val) } => gen_const_value ( val) ,
344+ & Insn :: Const { val : Const :: CPtr ( val) } => gen_const_cptr ( val) ,
342345 Insn :: Const { .. } => panic ! ( "Unexpected Const in gen_insn: {insn}" ) ,
343346 Insn :: NewArray { elements, state } => gen_new_array ( asm, opnds ! ( elements) , & function. frame_state ( * state) ) ,
344347 Insn :: NewHash { elements, state } => gen_new_hash ( jit, asm, opnds ! ( elements) , & function. frame_state ( * state) ) ,
@@ -372,6 +375,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
372375 // TODO remove this check when we have stack args (we can use Time.new to test it)
373376 Insn :: InvokeBuiltin { bf, state, .. } if bf. argc + 2 > ( C_ARG_OPNDS . len ( ) as i32 ) => return Err ( * state) ,
374377 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) ) ,
375379 Insn :: Return { val } => no_output ! ( gen_return( asm, opnd!( val) ) ) ,
376380 Insn :: FixnumAdd { left, right, state } => gen_fixnum_add ( jit, asm, opnd ! ( left) , opnd ! ( right) , & function. frame_state ( * state) ) ,
377381 Insn :: FixnumSub { left, right, state } => gen_fixnum_sub ( jit, asm, opnd ! ( left) , opnd ! ( right) , & function. frame_state ( * state) ) ,
@@ -386,6 +390,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
386390 Insn :: FixnumOr { left, right } => gen_fixnum_or ( asm, opnd ! ( left) , opnd ! ( right) ) ,
387391 Insn :: IsNil { val } => gen_isnil ( asm, opnd ! ( val) ) ,
388392 & Insn :: IsMethodCfunc { val, cd, cfunc, state : _ } => gen_is_method_cfunc ( jit, asm, opnd ! ( val) , cd, cfunc) ,
393+ & Insn :: IsBitEqual { left, right } => gen_is_bit_equal ( asm, opnd ! ( left) , opnd ! ( right) ) ,
389394 Insn :: Test { val } => gen_test ( asm, opnd ! ( val) ) ,
390395 Insn :: GuardType { val, guard_type, state } => gen_guard_type ( jit, asm, opnd ! ( val) , * guard_type, & function. frame_state ( * state) ) ,
391396 Insn :: GuardTypeNot { val, guard_type, state } => gen_guard_type_not ( jit, asm, opnd ! ( val) , * guard_type, & function. frame_state ( * state) ) ,
@@ -419,6 +424,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
419424 & Insn :: DefinedIvar { self_val, id, pushval, .. } => { gen_defined_ivar ( asm, opnd ! ( self_val) , id, pushval) } ,
420425 & Insn :: ArrayExtend { left, right, state } => { no_output ! ( gen_array_extend( jit, asm, opnd!( left) , opnd!( right) , & function. frame_state( state) ) ) } ,
421426 & Insn :: GuardShape { val, shape, state } => gen_guard_shape ( jit, asm, opnd ! ( val) , shape, & function. frame_state ( state) ) ,
427+ Insn :: LoadPC => gen_load_pc ( ) ,
422428 & Insn :: LoadIvarEmbedded { self_val, id, index } => gen_load_ivar_embedded ( asm, opnd ! ( self_val) , id, index) ,
423429 & Insn :: LoadIvarExtended { self_val, id, index } => gen_load_ivar_extended ( asm, opnd ! ( self_val) , id, index) ,
424430 & Insn :: ArrayMax { state, .. }
@@ -819,6 +825,10 @@ fn gen_guard_shape(jit: &mut JITState, asm: &mut Assembler, val: Opnd, shape: Sh
819825 val
820826}
821827
828+ fn gen_load_pc ( ) -> Opnd {
829+ Opnd :: mem ( 64 , CFP , RUBY_OFFSET_CFP_PC )
830+ }
831+
822832fn gen_load_ivar_embedded ( asm : & mut Assembler , self_val : Opnd , id : ID , index : u16 ) -> Opnd {
823833 // See ROBJECT_FIELDS() from include/ruby/internal/core/robject.h
824834
@@ -854,23 +864,6 @@ fn gen_entry_prologue(asm: &mut Assembler, iseq: IseqPtr) {
854864
855865 // Load the current SP from the CFP into REG_SP
856866 asm. mov ( SP , Opnd :: mem ( 64 , CFP , RUBY_OFFSET_CFP_SP ) ) ;
857-
858- // Currently, we support only the case that no optional arguments are given.
859- // Bail out if any optional argument is supplied.
860- let opt_num = unsafe { get_iseq_body_param_opt_num ( iseq) } ;
861- if opt_num > 0 {
862- asm_comment ! ( asm, "guard no optional arguments" ) ;
863- let no_opts_pc = unsafe { rb_iseq_pc_at_idx ( iseq, 0 ) } ;
864- asm. cmp ( Opnd :: mem ( 64 , CFP , RUBY_OFFSET_CFP_PC ) , Opnd :: const_ptr ( no_opts_pc) ) ;
865- let no_opts_label = asm. new_label ( "no_opts" ) ;
866- asm. je ( no_opts_label. clone ( ) ) ;
867-
868- gen_incr_counter ( asm, Counter :: exit_optional_arguments) ;
869- asm. frame_teardown ( lir:: JIT_PRESERVED_REGS ) ;
870- asm. cret ( Qundef . into ( ) ) ;
871-
872- asm. write_label ( no_opts_label) ;
873- }
874867}
875868
876869/// Assign method arguments to basic block arguments at JIT entry
@@ -966,11 +959,16 @@ fn gen_entry_param(asm: &mut Assembler, iseq: IseqPtr, local_idx: usize) -> lir:
966959}
967960
968961/// Compile a constant
969- fn gen_const ( val : VALUE ) -> lir:: Opnd {
962+ fn gen_const_value ( val : VALUE ) -> lir:: Opnd {
970963 // Just propagate the constant value and generate nothing
971964 Opnd :: Value ( val)
972965}
973966
967+ /// Compile Const::CPtr
968+ fn gen_const_cptr ( val : * mut u8 ) -> lir:: Opnd {
969+ Opnd :: UImm ( val as u64 )
970+ }
971+
974972/// Compile a basic block argument
975973fn gen_param ( asm : & mut Assembler , idx : usize ) -> lir:: Opnd {
976974 // Allocate a register or a stack slot
@@ -1315,6 +1313,19 @@ fn gen_object_alloc_class(asm: &mut Assembler, class: VALUE, state: &FrameState)
13151313 }
13161314}
13171315
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 ( ) ) ;
1322+ asm. pos_marker ( move |code_ptr, _| {
1323+ jit_entry. borrow_mut ( ) . start_addr . set ( Some ( code_ptr) ) ;
1324+ } ) ;
1325+ }
1326+ asm. frame_setup ( & [ ] , jit. c_stack_slots ) ;
1327+ }
1328+
13181329/// Compile code that exits from JIT code with a return value
13191330fn gen_return ( asm : & mut Assembler , val : lir:: Opnd ) {
13201331 // Pop the current frame (ec->cfp++)
@@ -1329,7 +1340,7 @@ fn gen_return(asm: &mut Assembler, val: lir::Opnd) {
13291340 asm. load_into ( C_RET_OPND , val) ;
13301341
13311342 // Return from the function
1332- asm. frame_teardown ( & [ ] ) ; // matching the setup in :bb0-prologue:
1343+ asm. frame_teardown ( & [ ] ) ; // matching the setup in gen_entry_point()
13331344 asm. cret ( C_RET_OPND ) ;
13341345}
13351346
@@ -1424,6 +1435,11 @@ fn gen_is_method_cfunc(jit: &JITState, asm: &mut Assembler, val: lir::Opnd, cd:
14241435 asm_ccall ! ( asm, rb_vm_method_cfunc_is, VALUE ( jit. iseq as usize ) . into( ) , ( cd as usize ) . into( ) , val, ( cfunc as usize ) . into( ) )
14251436}
14261437
1438+ fn gen_is_bit_equal ( asm : & mut Assembler , left : lir:: Opnd , right : lir:: Opnd ) -> lir:: Opnd {
1439+ asm. cmp ( left, right) ;
1440+ asm. csel_e ( Opnd :: Imm ( 1 ) , Opnd :: Imm ( 0 ) )
1441+ }
1442+
14271443fn gen_anytostring ( asm : & mut Assembler , val : lir:: Opnd , str : lir:: Opnd , state : & FrameState ) -> lir:: Opnd {
14281444 gen_prepare_leaf_call_with_gc ( asm, state) ;
14291445
@@ -1912,19 +1928,19 @@ c_callable! {
19121928/// Compile an ISEQ for a function stub
19131929fn function_stub_hit_body ( cb : & mut CodeBlock , iseq_call : & Rc < RefCell < IseqCall > > ) -> Result < CodePtr , CompileError > {
19141930 // Compile the stubbed ISEQ
1915- let code_ptr = gen_iseq ( cb, iseq_call. borrow ( ) . iseq , None ) . inspect_err ( |err| {
1931+ let IseqCodePtrs { jit_entry_ptr , .. } = gen_iseq ( cb, iseq_call. borrow ( ) . iseq , None ) . inspect_err ( |err| {
19161932 debug ! ( "{err:?}: gen_iseq failed: {}" , iseq_get_location( iseq_call. borrow( ) . iseq, 0 ) ) ;
19171933 } ) ?;
19181934
19191935 // Update the stub to call the code pointer
1920- let code_addr = code_ptr . raw_ptr ( cb) ;
1936+ let code_addr = jit_entry_ptr . raw_ptr ( cb) ;
19211937 let iseq = iseq_call. borrow ( ) . iseq ;
19221938 iseq_call. borrow_mut ( ) . regenerate ( cb, |asm| {
19231939 asm_comment ! ( asm, "call compiled function: {}" , iseq_get_location( iseq, 0 ) ) ;
19241940 asm. ccall ( code_addr, vec ! [ ] ) ;
19251941 } ) ;
19261942
1927- Ok ( code_ptr )
1943+ Ok ( jit_entry_ptr )
19281944}
19291945
19301946/// Compile a stub for an ISEQ called by SendWithoutBlockDirect
@@ -1983,7 +1999,7 @@ pub fn gen_exit_trampoline(cb: &mut CodeBlock) -> Result<CodePtr, CompileError>
19831999 let mut asm = Assembler :: new ( ) ;
19842000
19852001 asm_comment ! ( asm, "side-exit trampoline" ) ;
1986- asm. frame_teardown ( & [ ] ) ; // matching the setup in :bb0-prologue:
2002+ asm. frame_teardown ( & [ ] ) ; // matching the setup in gen_entry_point()
19872003 asm. cret ( Qundef . into ( ) ) ;
19882004
19892005 asm. compile ( cb) . map ( |( code_ptr, gc_offsets) | {
@@ -2113,6 +2129,22 @@ impl Assembler {
21132129 }
21142130}
21152131
2132+ /// Store info about a JIT entry point
2133+ pub struct JITEntry {
2134+ /// Position where the entry point starts
2135+ start_addr : Cell < Option < CodePtr > > ,
2136+ }
2137+
2138+ impl JITEntry {
2139+ /// Allocate a new JITEntry
2140+ fn new ( ) -> Rc < RefCell < Self > > {
2141+ let jit_entry = JITEntry {
2142+ start_addr : Cell :: new ( None ) ,
2143+ } ;
2144+ Rc :: new ( RefCell :: new ( jit_entry) )
2145+ }
2146+ }
2147+
21162148/// Store info about a JIT-to-JIT call
21172149#[ derive( Debug ) ]
21182150pub struct IseqCall {
0 commit comments