summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--zjit/src/codegen.rs16
-rw-r--r--zjit/src/hir.rs13
-rw-r--r--zjit/src/hir/tests.rs46
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)