@@ -113,6 +113,30 @@ jit_peek_at_self(jitstate_t *jit, ctx_t *ctx)
113113 return jit -> ec -> cfp -> self ;
114114}
115115
116+ // Save the incremented PC on the CFP
117+ // This is necessary when calleees can raise or allocate
118+ void
119+ jit_save_pc (jitstate_t * jit , x86opnd_t scratch_reg )
120+ {
121+ mov (cb , scratch_reg , const_ptr_opnd (jit -> pc + insn_len (jit -> opcode )));
122+ mov (cb , mem_opnd (64 , REG_CFP , offsetof(rb_control_frame_t , pc )), scratch_reg );
123+ }
124+
125+ // Save the current SP on the CFP
126+ // This realigns the interpreter SP with the JIT SP
127+ // Note: this will change the current value of REG_SP,
128+ // which could invalidate memory operands
129+ void
130+ jit_save_sp (jitstate_t * jit , ctx_t * ctx )
131+ {
132+ if (ctx -> sp_offset != 0 ) {
133+ x86opnd_t stack_pointer = ctx_sp_opnd (ctx , 0 );
134+ lea (cb , REG_SP , stack_pointer );
135+ mov (cb , member_opnd (REG_CFP , rb_control_frame_t , sp ), REG_SP );
136+ ctx -> sp_offset = 0 ;
137+ }
138+ }
139+
116140static bool jit_guard_known_klass (jitstate_t * jit , ctx_t * ctx , VALUE known_klass , insn_opnd_t insn_opnd , const int max_chain_depth , uint8_t * side_exit );
117141
118142#if RUBY_DEBUG
@@ -1344,20 +1368,14 @@ gen_opt_aref(jitstate_t *jit, ctx_t *ctx)
13441368 // Call VALUE rb_hash_aref(VALUE hash, VALUE key).
13451369 {
13461370 // Write incremented pc to cfp->pc as the routine can raise and allocate
1347- mov (cb , REG0 , const_ptr_opnd (jit -> pc + insn_len (BIN (opt_aref ))));
1348- mov (cb , member_opnd (REG_CFP , rb_control_frame_t , pc ), REG0 );
1371+ jit_save_pc (jit , REG0 );
13491372
13501373 // About to change REG_SP which these operands depend on. Yikes.
13511374 mov (cb , R8 , recv_opnd );
13521375 mov (cb , R9 , idx_opnd );
13531376
13541377 // Write sp to cfp->sp since rb_hash_aref might need to call #hash on the key
1355- if (ctx -> sp_offset != 0 ) {
1356- x86opnd_t stack_pointer = ctx_sp_opnd (ctx , 0 );
1357- lea (cb , REG_SP , stack_pointer );
1358- mov (cb , member_opnd (REG_CFP , rb_control_frame_t , sp ), REG_SP );
1359- ctx -> sp_offset = 0 ; // REG_SP now equals cfp->sp
1360- }
1378+ jit_save_sp (jit , ctx );
13611379
13621380 yjit_save_regs (cb );
13631381
@@ -1502,6 +1520,40 @@ gen_opt_plus(jitstate_t* jit, ctx_t* ctx)
15021520 return YJIT_KEEP_COMPILING ;
15031521}
15041522
1523+ VALUE rb_vm_opt_mod (VALUE recv , VALUE obj );
1524+
1525+ static codegen_status_t
1526+ gen_opt_mod (jitstate_t * jit , ctx_t * ctx )
1527+ {
1528+ // Save the PC and SP because the callee may allocate bignums
1529+ // Note that this modifies REG_SP, which is why we do it first
1530+ jit_save_pc (jit , REG0 );
1531+ jit_save_sp (jit , ctx );
1532+
1533+ uint8_t * side_exit = yjit_side_exit (jit , ctx );
1534+
1535+ // Get the operands from the stack
1536+ x86opnd_t arg1 = ctx_stack_pop (ctx , 1 );
1537+ x86opnd_t arg0 = ctx_stack_pop (ctx , 1 );
1538+
1539+ // Call rb_vm_opt_mod(VALUE recv, VALUE obj)
1540+ yjit_save_regs (cb );
1541+ mov (cb , C_ARG_REGS [0 ], arg0 );
1542+ mov (cb , C_ARG_REGS [1 ], arg1 );
1543+ call_ptr (cb , REG0 , (void * )rb_vm_opt_mod );
1544+ yjit_load_regs (cb );
1545+
1546+ // If val == Qundef, bail to do a method call
1547+ cmp (cb , RAX , imm_opnd (Qundef ));
1548+ je_ptr (cb , side_exit );
1549+
1550+ // Push the return value onto the stack
1551+ x86opnd_t stack_ret = ctx_stack_push (ctx , TYPE_UNKNOWN );
1552+ mov (cb , stack_ret , RAX );
1553+
1554+ return YJIT_KEEP_COMPILING ;
1555+ }
1556+
15051557void
15061558gen_branchif_branch (codeblock_t * cb , uint8_t * target0 , uint8_t * target1 , uint8_t shape )
15071559{
@@ -1802,8 +1854,7 @@ gen_send_cfunc(jitstate_t *jit, ctx_t *ctx, const struct rb_callinfo *ci, const
18021854 x86opnd_t recv = ctx_stack_opnd (ctx , argc );
18031855
18041856 // Store incremented PC into current control frame in case callee raises.
1805- mov (cb , REG0 , const_ptr_opnd (jit -> pc + insn_len (jit -> opcode )));
1806- mov (cb , mem_opnd (64 , REG_CFP , offsetof(rb_control_frame_t , pc )), REG0 );
1857+ jit_save_pc (jit , REG0 );
18071858
18081859 if (push_frame ) {
18091860 if (block ) {
@@ -1896,9 +1947,7 @@ gen_send_cfunc(jitstate_t *jit, ctx_t *ctx, const struct rb_callinfo *ci, const
18961947 if (block ) {
18971948 // Write interpreter SP into CFP.
18981949 // Needed in case the callee yields to the block.
1899- lea (cb , REG_SP , ctx_sp_opnd (ctx , 0 ));
1900- mov (cb , member_opnd (REG_CFP , rb_control_frame_t , sp ), REG_SP );
1901- ctx -> sp_offset = 0 ;
1950+ jit_save_sp (jit , ctx );
19021951 }
19031952
19041953 // Save YJIT registers
@@ -2074,8 +2123,7 @@ gen_send_iseq(jitstate_t *jit, ctx_t *ctx, const struct rb_callinfo *ci, const r
20742123 mov (cb , member_opnd (REG_CFP , rb_control_frame_t , sp ), REG0 );
20752124
20762125 // Store the next PC in the current frame
2077- mov (cb , REG0 , const_ptr_opnd (jit -> pc + insn_len (jit -> opcode )));
2078- mov (cb , mem_opnd (64 , REG_CFP , offsetof(rb_control_frame_t , pc )), REG0 );
2126+ jit_save_pc (jit , REG0 );
20792127
20802128 if (block ) {
20812129 // Change cfp->block_code in the current frame. See vm_caller_setup_arg_block().
@@ -2488,6 +2536,7 @@ yjit_init_codegen(void)
24882536 yjit_reg_op (BIN (opt_or ), gen_opt_or );
24892537 yjit_reg_op (BIN (opt_minus ), gen_opt_minus );
24902538 yjit_reg_op (BIN (opt_plus ), gen_opt_plus );
2539+ yjit_reg_op (BIN (opt_mod ), gen_opt_mod );
24912540 yjit_reg_op (BIN (opt_getinlinecache ), gen_opt_getinlinecache );
24922541 yjit_reg_op (BIN (branchif ), gen_branchif );
24932542 yjit_reg_op (BIN (branchunless ), gen_branchunless );
0 commit comments