ZJIT: Avoid optimizing locals on eval (#13840)

* ZJIT: Avoid optimizing locals on eval

* Maintain the local state for eval
This commit is contained in:
Takashi Kokubun 2025-07-10 12:08:09 -07:00 committed by GitHub
parent 9d41541b0c
commit 9ab80a7455
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 53 additions and 25 deletions

View file

@ -1,6 +1,5 @@
use std::cell::Cell;
use std::rc::Rc;
use std::num::NonZeroU32;
use crate::backend::current::{Reg, ALLOC_REGS};
use crate::invariants::track_bop_assumption;
@ -302,8 +301,8 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
Insn::GetIvar { self_val, id, state: _ } => gen_getivar(asm, opnd!(self_val), *id),
Insn::SetGlobal { id, val, state: _ } => return Some(gen_setglobal(asm, *id, opnd!(val))),
Insn::GetGlobal { id, state: _ } => gen_getglobal(asm, *id),
&Insn::GetLocal { ep_offset, level } => gen_nested_getlocal(asm, ep_offset, level)?,
Insn::SetLocal { val, ep_offset, level } => return gen_nested_setlocal(asm, opnd!(val), *ep_offset, *level),
&Insn::GetLocal { ep_offset, level } => gen_getlocal_with_ep(asm, ep_offset, level)?,
Insn::SetLocal { val, ep_offset, level } => return gen_setlocal_with_ep(asm, opnd!(val), *ep_offset, *level),
Insn::GetConstantPath { ic, state } => gen_get_constant_path(asm, *ic, &function.frame_state(*state)),
Insn::SetIvar { self_val, id, val, state: _ } => return gen_setivar(asm, opnd!(self_val), *id, opnd!(val)),
Insn::SideExit { state, reason: _ } => return gen_side_exit(jit, asm, &function.frame_state(*state)),
@ -383,16 +382,20 @@ fn gen_defined(jit: &JITState, asm: &mut Assembler, op_type: usize, _obj: VALUE,
}
}
/// Get a local variable from a higher scope. `local_ep_offset` is in number of VALUEs.
fn gen_nested_getlocal(asm: &mut Assembler, local_ep_offset: u32, level: NonZeroU32) -> Option<lir::Opnd> {
let ep = gen_get_ep(asm, level.get());
/// Get a local variable from a higher scope or the heap. `local_ep_offset` is in number of VALUEs.
/// We generate this instruction with level=0 only when the local variable is on the heap, so we
/// can't optimize the level=0 case using the SP register.
fn gen_getlocal_with_ep(asm: &mut Assembler, local_ep_offset: u32, level: u32) -> Option<lir::Opnd> {
let ep = gen_get_ep(asm, level);
let offset = -(SIZEOF_VALUE_I32 * i32::try_from(local_ep_offset).ok()?);
Some(asm.load(Opnd::mem(64, ep, offset)))
}
/// Set a local variable from a higher scope. `local_ep_offset` is in number of VALUEs.
fn gen_nested_setlocal(asm: &mut Assembler, val: Opnd, local_ep_offset: u32, level: NonZeroU32) -> Option<()> {
let ep = gen_get_ep(asm, level.get());
/// Set a local variable from a higher scope or the heap. `local_ep_offset` is in number of VALUEs.
/// We generate this instruction with level=0 only when the local variable is on the heap, so we
/// can't optimize the level=0 case using the SP register.
fn gen_setlocal_with_ep(asm: &mut Assembler, val: Opnd, local_ep_offset: u32, level: u32) -> Option<()> {
let ep = gen_get_ep(asm, level);
let offset = -(SIZEOF_VALUE_I32 * i32::try_from(local_ep_offset).ok()?);
asm.mov(Opnd::mem(64, ep, offset), val);
Some(())