@@ -10,7 +10,7 @@ use std::slice;
10
10
use crate :: asm:: Label ;
11
11
use crate :: backend:: current:: { Reg , ALLOC_REGS } ;
12
12
use 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 } ;
14
14
use crate :: state:: ZJITState ;
15
15
use crate :: stats:: { exit_counter_for_compile_error, incr_counter, incr_counter_by, CompileError } ;
16
16
use crate :: stats:: { counter_ptr, with_time_stat, Counter , send_fallback_counter, Counter :: { compile_time_ns, exit_compile_error} } ;
@@ -33,6 +33,9 @@ struct JITState {
33
33
/// Labels for each basic block indexed by the BlockId
34
34
labels : Vec < Option < Target > > ,
35
35
36
+ /// JIT entry point for the `iseq`
37
+ jit_entry : Option < Rc < RefCell < JITEntry > > > ,
38
+
36
39
/// ISEQ calls that need to be compiled later
37
40
iseq_calls : Vec < Rc < RefCell < IseqCall > > > ,
38
41
@@ -47,6 +50,7 @@ impl JITState {
47
50
iseq,
48
51
opnds : vec ! [ None ; num_insns] ,
49
52
labels : vec ! [ None ; num_blocks] ,
53
+ jit_entry : None ,
50
54
iseq_calls : Vec :: default ( ) ,
51
55
c_stack_slots,
52
56
}
@@ -117,7 +121,7 @@ fn gen_iseq_entry_point(cb: &mut CodeBlock, iseq: IseqPtr, jit_exception: bool)
117
121
} ) ?;
118
122
119
123
// 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| {
121
125
debug ! ( "{err:?}: gen_iseq failed: {}" , iseq_get_location( iseq, 0 ) ) ;
122
126
} ) ?;
123
127
@@ -164,7 +168,7 @@ fn gen_entry(cb: &mut CodeBlock, iseq: IseqPtr, function: &Function, function_pt
164
168
// Set up registers for CFP, EC, SP, and basic block arguments
165
169
let mut asm = Assembler :: new ( ) ;
166
170
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 ( ) ) ;
168
172
169
173
// Jump to the first block using a call instruction
170
174
asm. ccall ( function_ptr. raw_ptr ( cb) , vec ! [ ] ) ;
@@ -191,40 +195,40 @@ fn gen_entry(cb: &mut CodeBlock, iseq: IseqPtr, function: &Function, function_pt
191
195
}
192
196
193
197
/// 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 > {
195
199
// Return an existing pointer if it's already compiled
196
200
let payload = get_or_create_iseq_payload ( iseq) ;
197
201
match & payload. status {
198
- IseqStatus :: Compiled ( start_ptr ) => return Ok ( * start_ptr ) ,
202
+ IseqStatus :: Compiled ( code_ptrs ) => return Ok ( code_ptrs . clone ( ) ) ,
199
203
IseqStatus :: CantCompile ( err) => return Err ( err. clone ( ) ) ,
200
204
IseqStatus :: NotCompiled => { } ,
201
205
}
202
206
203
207
// 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 ( ) ) ;
208
212
incr_counter ! ( compiled_iseq_count) ;
209
213
}
210
214
Err ( err) => {
211
215
payload. status = IseqStatus :: CantCompile ( err. clone ( ) ) ;
212
216
incr_counter ! ( failed_iseq_count) ;
213
217
}
214
218
}
215
- code_ptr
219
+ code_ptrs
216
220
}
217
221
218
222
/// 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 > {
220
224
// Convert ISEQ into optimized High-level IR if not given
221
225
let function = match function {
222
226
Some ( function) => function,
223
227
None => & compile_iseq ( iseq) ?,
224
228
} ;
225
229
226
230
// 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) ?;
228
232
229
233
// Stub callee ISEQs for JIT-to-JIT calls
230
234
for iseq_call in iseq_calls. iter ( ) {
@@ -234,11 +238,11 @@ fn gen_iseq_body(cb: &mut CodeBlock, iseq: IseqPtr, function: Option<&Function>,
234
238
// Prepare for GC
235
239
payload. iseq_calls . extend ( iseq_calls. clone ( ) ) ;
236
240
append_gc_offsets ( iseq, & gc_offsets) ;
237
- Ok ( start_ptr)
241
+ Ok ( IseqCodePtrs { start_ptr, jit_entry_ptr } )
238
242
}
239
243
240
244
/// 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 > {
242
246
let c_stack_slots = max_num_params ( function) . saturating_sub ( ALLOC_REGS . len ( ) ) ;
243
247
let mut jit = JITState :: new ( iseq, function. num_insns ( ) , function. num_blocks ( ) , c_stack_slots) ;
244
248
let mut asm = Assembler :: new ( ) ;
@@ -257,11 +261,6 @@ fn gen_function(cb: &mut CodeBlock, iseq: IseqPtr, function: &Function) -> Resul
257
261
let label = jit. get_label ( & mut asm, block_id) ;
258
262
asm. write_label ( label) ;
259
263
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
-
265
264
// Compile all parameters
266
265
for & insn_id in block. params ( ) {
267
266
match function. find ( insn_id) {
@@ -307,7 +306,10 @@ fn gen_function(cb: &mut CodeBlock, iseq: IseqPtr, function: &Function) -> Resul
307
306
ZJITState :: log_compile ( iseq_name) ;
308
307
}
309
308
}
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
+ } )
311
313
}
312
314
313
315
/// Compile an instruction
@@ -338,7 +340,8 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
338
340
}
339
341
340
342
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) ,
342
345
Insn :: Const { .. } => panic ! ( "Unexpected Const in gen_insn: {insn}" ) ,
343
346
Insn :: NewArray { elements, state } => gen_new_array ( asm, opnds ! ( elements) , & function. frame_state ( * state) ) ,
344
347
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
372
375
// TODO remove this check when we have stack args (we can use Time.new to test it)
373
376
Insn :: InvokeBuiltin { bf, state, .. } if bf. argc + 2 > ( C_ARG_OPNDS . len ( ) as i32 ) => return Err ( * state) ,
374
377
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) ) ,
375
379
Insn :: Return { val } => no_output ! ( gen_return( asm, opnd!( val) ) ) ,
376
380
Insn :: FixnumAdd { left, right, state } => gen_fixnum_add ( jit, asm, opnd ! ( left) , opnd ! ( right) , & function. frame_state ( * state) ) ,
377
381
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
386
390
Insn :: FixnumOr { left, right } => gen_fixnum_or ( asm, opnd ! ( left) , opnd ! ( right) ) ,
387
391
Insn :: IsNil { val } => gen_isnil ( asm, opnd ! ( val) ) ,
388
392
& 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) ) ,
389
394
Insn :: Test { val } => gen_test ( asm, opnd ! ( val) ) ,
390
395
Insn :: GuardType { val, guard_type, state } => gen_guard_type ( jit, asm, opnd ! ( val) , * guard_type, & function. frame_state ( * state) ) ,
391
396
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
419
424
& Insn :: DefinedIvar { self_val, id, pushval, .. } => { gen_defined_ivar ( asm, opnd ! ( self_val) , id, pushval) } ,
420
425
& Insn :: ArrayExtend { left, right, state } => { no_output ! ( gen_array_extend( jit, asm, opnd!( left) , opnd!( right) , & function. frame_state( state) ) ) } ,
421
426
& Insn :: GuardShape { val, shape, state } => gen_guard_shape ( jit, asm, opnd ! ( val) , shape, & function. frame_state ( state) ) ,
427
+ Insn :: LoadPC => gen_load_pc ( ) ,
422
428
& Insn :: LoadIvarEmbedded { self_val, id, index } => gen_load_ivar_embedded ( asm, opnd ! ( self_val) , id, index) ,
423
429
& Insn :: LoadIvarExtended { self_val, id, index } => gen_load_ivar_extended ( asm, opnd ! ( self_val) , id, index) ,
424
430
& Insn :: ArrayMax { state, .. }
@@ -819,6 +825,10 @@ fn gen_guard_shape(jit: &mut JITState, asm: &mut Assembler, val: Opnd, shape: Sh
819
825
val
820
826
}
821
827
828
+ fn gen_load_pc ( ) -> Opnd {
829
+ Opnd :: mem ( 64 , CFP , RUBY_OFFSET_CFP_PC )
830
+ }
831
+
822
832
fn gen_load_ivar_embedded ( asm : & mut Assembler , self_val : Opnd , id : ID , index : u16 ) -> Opnd {
823
833
// See ROBJECT_FIELDS() from include/ruby/internal/core/robject.h
824
834
@@ -854,23 +864,6 @@ fn gen_entry_prologue(asm: &mut Assembler, iseq: IseqPtr) {
854
864
855
865
// Load the current SP from the CFP into REG_SP
856
866
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
- }
874
867
}
875
868
876
869
/// 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:
966
959
}
967
960
968
961
/// Compile a constant
969
- fn gen_const ( val : VALUE ) -> lir:: Opnd {
962
+ fn gen_const_value ( val : VALUE ) -> lir:: Opnd {
970
963
// Just propagate the constant value and generate nothing
971
964
Opnd :: Value ( val)
972
965
}
973
966
967
+ /// Compile Const::CPtr
968
+ fn gen_const_cptr ( val : * mut u8 ) -> lir:: Opnd {
969
+ Opnd :: UImm ( val as u64 )
970
+ }
971
+
974
972
/// Compile a basic block argument
975
973
fn gen_param ( asm : & mut Assembler , idx : usize ) -> lir:: Opnd {
976
974
// Allocate a register or a stack slot
@@ -1315,6 +1313,19 @@ fn gen_object_alloc_class(asm: &mut Assembler, class: VALUE, state: &FrameState)
1315
1313
}
1316
1314
}
1317
1315
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
+
1318
1329
/// Compile code that exits from JIT code with a return value
1319
1330
fn gen_return ( asm : & mut Assembler , val : lir:: Opnd ) {
1320
1331
// Pop the current frame (ec->cfp++)
@@ -1329,7 +1340,7 @@ fn gen_return(asm: &mut Assembler, val: lir::Opnd) {
1329
1340
asm. load_into ( C_RET_OPND , val) ;
1330
1341
1331
1342
// 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()
1333
1344
asm. cret ( C_RET_OPND ) ;
1334
1345
}
1335
1346
@@ -1424,6 +1435,11 @@ fn gen_is_method_cfunc(jit: &JITState, asm: &mut Assembler, val: lir::Opnd, cd:
1424
1435
asm_ccall ! ( asm, rb_vm_method_cfunc_is, VALUE ( jit. iseq as usize ) . into( ) , ( cd as usize ) . into( ) , val, ( cfunc as usize ) . into( ) )
1425
1436
}
1426
1437
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
+
1427
1443
fn gen_anytostring ( asm : & mut Assembler , val : lir:: Opnd , str : lir:: Opnd , state : & FrameState ) -> lir:: Opnd {
1428
1444
gen_prepare_leaf_call_with_gc ( asm, state) ;
1429
1445
@@ -1912,19 +1928,19 @@ c_callable! {
1912
1928
/// Compile an ISEQ for a function stub
1913
1929
fn function_stub_hit_body ( cb : & mut CodeBlock , iseq_call : & Rc < RefCell < IseqCall > > ) -> Result < CodePtr , CompileError > {
1914
1930
// 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| {
1916
1932
debug ! ( "{err:?}: gen_iseq failed: {}" , iseq_get_location( iseq_call. borrow( ) . iseq, 0 ) ) ;
1917
1933
} ) ?;
1918
1934
1919
1935
// 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) ;
1921
1937
let iseq = iseq_call. borrow ( ) . iseq ;
1922
1938
iseq_call. borrow_mut ( ) . regenerate ( cb, |asm| {
1923
1939
asm_comment ! ( asm, "call compiled function: {}" , iseq_get_location( iseq, 0 ) ) ;
1924
1940
asm. ccall ( code_addr, vec ! [ ] ) ;
1925
1941
} ) ;
1926
1942
1927
- Ok ( code_ptr )
1943
+ Ok ( jit_entry_ptr )
1928
1944
}
1929
1945
1930
1946
/// Compile a stub for an ISEQ called by SendWithoutBlockDirect
@@ -1983,7 +1999,7 @@ pub fn gen_exit_trampoline(cb: &mut CodeBlock) -> Result<CodePtr, CompileError>
1983
1999
let mut asm = Assembler :: new ( ) ;
1984
2000
1985
2001
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()
1987
2003
asm. cret ( Qundef . into ( ) ) ;
1988
2004
1989
2005
asm. compile ( cb) . map ( |( code_ptr, gc_offsets) | {
@@ -2113,6 +2129,22 @@ impl Assembler {
2113
2129
}
2114
2130
}
2115
2131
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
+
2116
2148
/// Store info about a JIT-to-JIT call
2117
2149
#[ derive( Debug ) ]
2118
2150
pub struct IseqCall {
0 commit comments