diff options
| -rw-r--r-- | zjit/src/codegen.rs | 16 | ||||
| -rw-r--r-- | zjit/src/hir.rs | 13 | ||||
| -rw-r--r-- | zjit/src/hir/tests.rs | 46 |
3 files changed, 65 insertions, 10 deletions
diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index 2fe9958e62..3ba90851db 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -672,15 +672,13 @@ fn gen_defined(jit: &JITState, asm: &mut Assembler, op_type: usize, obj: VALUE, // // Similar to gen_is_block_given 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()); - asm.csel_e(Qnil.into(), pushval) - } else { - Qnil.into() - } + assert_eq!(unsafe { rb_get_iseq_body_type(local_iseq) }, ISEQ_TYPE_METHOD, + "defined?(yield) in non-method iseq should be handled by HIR construction"); + 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()); + asm.csel_e(Qnil.into(), pushval) } _ => { // Save the PC and SP because the callee may allocate or call #respond_to? diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 5d8cb2f7c0..65f7c3396b 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -6587,7 +6587,18 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> { let obj = get_arg(pc, 1); let pushval = get_arg(pc, 2); let v = state.stack_pop()?; - state.stack_push(fun.push_insn(block, Insn::Defined { op_type, obj, pushval, v, state: exit_id })); + let local_iseq = unsafe { rb_get_iseq_body_local_iseq(iseq) }; + let insn = if op_type == DEFINED_YIELD as usize && unsafe { rb_get_iseq_body_type(local_iseq) } != ISEQ_TYPE_METHOD { + // `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.) + // + // Similar to gen_is_block_given + Insn::Const { val: Const::Value(Qnil) } + } else { + Insn::Defined { op_type, obj, pushval, v, state: exit_id } + }; + state.stack_push(fun.push_insn(block, insn)); } YARVINSN_definedivar => { // (ID id, IVC ic, VALUE pushval) diff --git a/zjit/src/hir/tests.rs b/zjit/src/hir/tests.rs index fab8c6e262..d8975f677f 100644 --- a/zjit/src/hir/tests.rs +++ b/zjit/src/hir/tests.rs @@ -1124,6 +1124,52 @@ pub mod hir_build_tests { } #[test] + fn defined_yield_in_method_local_iseq_returns_defined() { + eval(" + def test = defined?(yield) + "); + assert_contains_opcode("test", YARVINSN_defined); + assert_snapshot!(hir_string("test"), @r" + fn test@<compiled>:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:NilClass = Const Value(nil) + v12:StringExact|NilClass = Defined yield, v10 + CheckInterrupts + Return v12 + "); + } + + #[test] + fn defined_yield_in_non_method_local_iseq_returns_nil() { + eval(" + define_method(:test) { defined?(yield) } + "); + assert_contains_opcode("test", YARVINSN_defined); + assert_snapshot!(hir_string("test"), @r" + fn block in <compiled>@<compiled>:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v10:NilClass = Const Value(nil) + v12:NilClass = Const Value(nil) + CheckInterrupts + Return v12 + "); + } + + #[test] fn test_return_const() { eval(" def test(cond) |
