@@ -107,14 +107,14 @@ fn gen_iseq_entry_point(iseq: IseqPtr) -> *const u8 {
107
107
// Compile the High-level IR
108
108
let cb = ZJITState :: get_code_block ( ) ;
109
109
let ( start_ptr, mut branch_iseqs) = match gen_function ( cb, iseq, & function) {
110
- Some ( ( start_ptr, gc_offsets, branch_iseqs ) ) => {
110
+ Some ( ( start_ptr, gc_offsets, jit ) ) => {
111
111
// Remember the block address to reuse it later
112
112
let payload = get_or_create_iseq_payload ( iseq) ;
113
113
payload. start_ptr = Some ( start_ptr) ;
114
114
payload. gc_offsets . extend ( gc_offsets) ;
115
115
116
116
// Compile an entry point to the JIT code
117
- ( gen_entry ( cb, iseq, & function, start_ptr) , branch_iseqs)
117
+ ( gen_entry ( cb, iseq, & function, start_ptr, jit . c_stack_bytes ) , jit . branch_iseqs )
118
118
} ,
119
119
None => ( None , vec ! [ ] ) ,
120
120
} ;
@@ -145,11 +145,11 @@ fn gen_iseq_entry_point(iseq: IseqPtr) -> *const u8 {
145
145
}
146
146
147
147
/// Compile a JIT entry
148
- fn gen_entry ( cb : & mut CodeBlock , iseq : IseqPtr , function : & Function , function_ptr : CodePtr ) -> Option < CodePtr > {
148
+ fn gen_entry ( cb : & mut CodeBlock , iseq : IseqPtr , function : & Function , function_ptr : CodePtr , c_stack_bytes : usize ) -> Option < CodePtr > {
149
149
// Set up registers for CFP, EC, SP, and basic block arguments
150
150
let mut asm = Assembler :: new ( ) ;
151
151
gen_entry_prologue ( & mut asm, iseq) ;
152
- gen_entry_params ( & mut asm, iseq, function. block ( BlockId ( 0 ) ) ) ;
152
+ gen_entry_params ( & mut asm, iseq, function. block ( BlockId ( 0 ) ) , c_stack_bytes ) ;
153
153
154
154
// Jump to the first block using a call instruction
155
155
asm. ccall ( function_ptr. raw_ptr ( cb) as * const u8 , vec ! [ ] ) ;
@@ -185,17 +185,17 @@ fn gen_iseq(cb: &mut CodeBlock, iseq: IseqPtr) -> Option<(CodePtr, Vec<(Rc<Branc
185
185
186
186
// Compile the High-level IR
187
187
let result = gen_function ( cb, iseq, & function) ;
188
- if let Some ( ( start_ptr, gc_offsets, branch_iseqs ) ) = result {
188
+ if let Some ( ( start_ptr, gc_offsets, jit ) ) = result {
189
189
payload. start_ptr = Some ( start_ptr) ;
190
190
payload. gc_offsets . extend ( gc_offsets) ;
191
- Some ( ( start_ptr, branch_iseqs) )
191
+ Some ( ( start_ptr, jit . branch_iseqs ) )
192
192
} else {
193
193
None
194
194
}
195
195
}
196
196
197
197
/// Compile a function
198
- fn gen_function ( cb : & mut CodeBlock , iseq : IseqPtr , function : & Function ) -> Option < ( CodePtr , Vec < CodePtr > , Vec < ( Rc < Branch > , IseqPtr ) > ) > {
198
+ fn gen_function ( cb : & mut CodeBlock , iseq : IseqPtr , function : & Function ) -> Option < ( CodePtr , Vec < CodePtr > , JITState ) > {
199
199
let c_stack_bytes = aligned_stack_bytes ( max_num_params ( function) . saturating_sub ( ALLOC_REGS . len ( ) ) ) ;
200
200
let mut jit = JITState :: new ( iseq, function. num_insns ( ) , function. num_blocks ( ) , c_stack_bytes) ;
201
201
let mut asm = Assembler :: new ( ) ;
@@ -249,7 +249,7 @@ fn gen_function(cb: &mut CodeBlock, iseq: IseqPtr, function: &Function) -> Optio
249
249
}
250
250
251
251
// Generate code if everything can be compiled
252
- asm. compile ( cb) . map ( |( start_ptr, gc_offsets) | ( start_ptr, gc_offsets, jit. branch_iseqs ) )
252
+ asm. compile ( cb) . map ( |( start_ptr, gc_offsets) | ( start_ptr, gc_offsets, jit) )
253
253
}
254
254
255
255
/// Compile an instruction
@@ -527,7 +527,7 @@ fn gen_entry_prologue(asm: &mut Assembler, iseq: IseqPtr) {
527
527
}
528
528
529
529
/// Assign method arguments to basic block arguments at JIT entry
530
- fn gen_entry_params ( asm : & mut Assembler , iseq : IseqPtr , entry_block : & Block ) {
530
+ fn gen_entry_params ( asm : & mut Assembler , iseq : IseqPtr , entry_block : & Block , c_stack_bytes : usize ) {
531
531
let self_param = gen_param ( asm, SELF_PARAM_IDX ) ;
532
532
asm. mov ( self_param, Opnd :: mem ( VALUE_BITS , CFP , RUBY_OFFSET_CFP_SELF ) ) ;
533
533
@@ -536,12 +536,32 @@ fn gen_entry_params(asm: &mut Assembler, iseq: IseqPtr, entry_block: &Block) {
536
536
asm_comment ! ( asm, "set method params: {num_params}" ) ;
537
537
538
538
// Allocate registers for basic block arguments
539
- let params: Vec < Opnd > = ( 0 ..num_params) . map ( |idx|
540
- gen_param ( asm, idx + 1 ) // +1 for self
541
- ) . collect ( ) ;
539
+ for idx in 0 ..num_params {
540
+ let param = gen_param ( asm, idx + 1 ) ; // +1 for self
541
+
542
+ // Funky offset adjustment to write into the native stack frame of the
543
+ // HIR function we'll be calling into. This only makes sense in context
544
+ // of the schedule of instructions in gen_entry() for the JIT entry point.
545
+ //
546
+ // The entry point needs to load VALUEs into native stack slots _before_ the
547
+ // frame containing the slots exists. So, we anticipate the stack frame size
548
+ // of the Function and subtract offsets based on that.
549
+ //
550
+ // native SP at entry point ─────►┌────────────┐ Native SP grows downwards
551
+ // │ │ ↓ on all arches we support.
552
+ // SP-0x8 ├────────────┤
553
+ // │ │
554
+ // where native SP SP-0x10├────────────┤
555
+ // would be while │ │
556
+ // the HIR function ────────────► └────────────┘
557
+ // is running
558
+ let param = if let Opnd :: Mem ( lir:: Mem { base, disp, num_bits } ) = param {
559
+ Opnd :: Mem ( lir:: Mem { num_bits, base, disp : disp - c_stack_bytes as i32 - Assembler :: frame_size ( ) } )
560
+ } else {
561
+ param
562
+ } ;
542
563
543
- // Assign local variables to the basic block arguments
544
- for ( idx, & param) in params. iter ( ) . enumerate ( ) {
564
+ // Assign local variables to the basic block arguments
545
565
let local = gen_entry_param ( asm, iseq, idx) ;
546
566
asm. mov ( param, local) ;
547
567
}
@@ -1045,11 +1065,12 @@ fn gen_push_frame(asm: &mut Assembler, argc: usize, state: &FrameState, frame: C
1045
1065
/// Return an operand we use for the basic block argument at a given index
1046
1066
fn param_opnd ( idx : usize ) -> Opnd {
1047
1067
// To simplify the implementation, allocate a fixed register or a stack slot for each basic block argument for now.
1068
+ // Note that this is implemented here as opposed to automatically inside LIR machineries.
1048
1069
// TODO: Allow allocating arbitrary registers for basic block arguments
1049
1070
if idx < ALLOC_REGS . len ( ) {
1050
1071
Opnd :: Reg ( ALLOC_REGS [ idx] )
1051
1072
} else {
1052
- Opnd :: mem ( 64 , NATIVE_STACK_PTR , - ( ( idx - ALLOC_REGS . len ( ) + 1 ) as i32 ) * SIZEOF_VALUE_I32 )
1073
+ Opnd :: mem ( 64 , NATIVE_STACK_PTR , ( idx - ALLOC_REGS . len ( ) ) as i32 * SIZEOF_VALUE_I32 )
1053
1074
}
1054
1075
}
1055
1076
0 commit comments