diff options
Diffstat (limited to 'zjit/src/codegen.rs')
| -rw-r--r-- | zjit/src/codegen.rs | 41 |
1 files changed, 41 insertions, 0 deletions
diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index 6cb960e0c4..f805b8b8d7 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -287,6 +287,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio Insn::SideExit { state } => return gen_side_exit(jit, asm, &function.frame_state(*state)), Insn::PutSpecialObject { value_type } => gen_putspecialobject(asm, *value_type), Insn::AnyToString { val, str, state } => gen_anytostring(asm, opnd!(val), opnd!(str), &function.frame_state(*state))?, + Insn::Defined { op_type, obj, pushval, v } => gen_defined(jit, asm, *op_type, *obj, *pushval, opnd!(v))?, _ => { debug!("ZJIT: gen_function: unexpected insn {insn}"); return None; @@ -301,6 +302,25 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio Some(()) } +/// Gets the EP of the ISeq of the containing method, or "local level". +/// Equivalent of GET_LEP() macro. +fn gen_get_lep(jit: &JITState, asm: &mut Assembler) -> Opnd { + // Equivalent of get_lvar_level() in compile.c + fn get_lvar_level(mut iseq: IseqPtr) -> u32 { + let local_iseq = unsafe { rb_get_iseq_body_local_iseq(iseq) }; + let mut level = 0; + while iseq != local_iseq { + iseq = unsafe { rb_get_iseq_body_parent_iseq(iseq) }; + level += 1; + } + + level + } + + let level = get_lvar_level(jit.iseq); + gen_get_ep(asm, level) +} + // Get EP at `level` from CFP fn gen_get_ep(asm: &mut Assembler, level: u32) -> Opnd { // Load environment pointer EP from CFP into a register @@ -320,6 +340,27 @@ fn gen_get_ep(asm: &mut Assembler, level: u32) -> Opnd { ep_opnd } +fn gen_defined(jit: &JITState, asm: &mut Assembler, op_type: usize, _obj: VALUE, pushval: VALUE, _tested_value: Opnd) -> Option<Opnd> { + match op_type as defined_type { + DEFINED_YIELD => { + // `yield` goes to the block handler stowed in the "local" iseq which is + // the current iseq or a parent. Only the "method" iseq type can be passed a + // block handler. (e.g. `yield` in the top level script is a syntax error.) + let local_iseq = unsafe { rb_get_iseq_body_local_iseq(jit.iseq) }; + if unsafe { rb_get_iseq_body_type(local_iseq) } == ISEQ_TYPE_METHOD { + let lep = gen_get_lep(jit, asm); + let block_handler = asm.load(Opnd::mem(64, lep, SIZEOF_VALUE_I32 * VM_ENV_DATA_INDEX_SPECVAL)); + let pushval = asm.load(pushval.into()); + asm.cmp(block_handler, VM_BLOCK_HANDLER_NONE.into()); + Some(asm.csel_e(Qnil.into(), pushval.into())) + } else { + Some(Qnil.into()) + } + } + _ => None + } +} + /// 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()); |
