summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMax Bernstein <ruby@bernsteinbear.com>2025-08-28 10:27:02 -0400
committerMax Bernstein <tekknolagi@gmail.com>2025-08-28 10:14:37 -0700
commit07e28ba486e26172d76211080a82dc9cdb38250d (patch)
tree0080c9630bafb88fc8c8407e965c7145e7608551
parentb108f11708228a69236b0eae4840992531af7025 (diff)
ZJIT: Generate code for DefinedIvar
-rw-r--r--test/ruby/test_zjit.rb14
-rw-r--r--zjit.c7
-rw-r--r--zjit/bindgen/src/main.rs1
-rw-r--r--zjit/src/codegen.rs6
-rw-r--r--zjit/src/cruby_bindings.inc.rs1
5 files changed, 28 insertions, 1 deletions
diff --git a/test/ruby/test_zjit.rb b/test/ruby/test_zjit.rb
index 52f2cddeb7..736c6306d1 100644
--- a/test/ruby/test_zjit.rb
+++ b/test/ruby/test_zjit.rb
@@ -139,6 +139,20 @@ class TestZJIT < Test::Unit::TestCase
}, insns: [:splatarray]
end
+ def test_definedivar
+ assert_compiles '[nil, "instance-variable", nil]', %q{
+ def test
+ v0 = defined?(@a)
+ @a = nil
+ v1 = defined?(@a)
+ remove_instance_variable :@a
+ v2 = defined?(@a)
+ [v0, v1, v2]
+ end
+ test
+ }, insns: [:definedivar]
+ end
+
def test_setglobal_with_trace_var_exception
assert_compiles '"rescued"', %q{
def test
diff --git a/zjit.c b/zjit.c
index 9cc8b51423..d4798ef860 100644
--- a/zjit.c
+++ b/zjit.c
@@ -356,6 +356,13 @@ rb_zjit_singleton_class_p(VALUE klass)
return RCLASS_SINGLETON_P(klass);
}
+VALUE
+rb_zjit_defined_ivar(VALUE obj, ID id, VALUE pushval)
+{
+ VALUE result = rb_ivar_defined(obj, id);
+ return result ? pushval : Qnil;
+}
+
// Primitives used by zjit.rb. Don't put other functions below, which wouldn't use them.
VALUE rb_zjit_assert_compiles(rb_execution_context_t *ec, VALUE self);
VALUE rb_zjit_stats(rb_execution_context_t *ec, VALUE self, VALUE target_key);
diff --git a/zjit/bindgen/src/main.rs b/zjit/bindgen/src/main.rs
index a95b8dcaaa..e4c073d48a 100644
--- a/zjit/bindgen/src/main.rs
+++ b/zjit/bindgen/src/main.rs
@@ -359,6 +359,7 @@ fn main() {
.allowlist_function("rb_zjit_icache_invalidate")
.allowlist_function("rb_zjit_print_exception")
.allowlist_function("rb_zjit_singleton_class_p")
+ .allowlist_function("rb_zjit_defined_ivar")
.allowlist_type("robject_offsets")
.allowlist_type("rstring_offsets")
.allowlist_var("RB_INVALID_SHAPE_ID")
diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs
index d87c94cf8c..cdc62043fd 100644
--- a/zjit/src/codegen.rs
+++ b/zjit/src/codegen.rs
@@ -402,9 +402,9 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
&Insn::ArrayPush { array, val, state } => { no_output!(gen_array_push(asm, opnd!(array), opnd!(val), &function.frame_state(state))) },
&Insn::ToNewArray { val, state } => { gen_to_new_array(jit, asm, opnd!(val), &function.frame_state(state)) },
&Insn::ToArray { val, state } => { gen_to_array(jit, asm, opnd!(val), &function.frame_state(state)) },
+ &Insn::DefinedIvar { self_val, id, pushval, .. } => { gen_defined_ivar(asm, opnd!(self_val), id, pushval) },
&Insn::ArrayExtend { state, .. }
| &Insn::ArrayMax { state, .. }
- | &Insn::DefinedIvar { state, .. }
| &Insn::FixnumDiv { state, .. }
| &Insn::FixnumMod { state, .. }
| &Insn::Send { state, .. }
@@ -704,6 +704,10 @@ fn gen_to_array(jit: &mut JITState, asm: &mut Assembler, val: Opnd, state: &Fram
asm_ccall!(asm, rb_vm_splat_array, Opnd::Value(Qfalse), val)
}
+fn gen_defined_ivar(asm: &mut Assembler, self_val: Opnd, id: ID, pushval: VALUE) -> lir::Opnd {
+ asm_ccall!(asm, rb_zjit_defined_ivar, self_val, id.0.into(), Opnd::Value(pushval))
+}
+
/// Compile an interpreter entry block to be inserted into an ISEQ
fn gen_entry_prologue(asm: &mut Assembler, iseq: IseqPtr) {
asm_comment!(asm, "ZJIT entry point: {}", iseq_get_location(iseq, 0));
diff --git a/zjit/src/cruby_bindings.inc.rs b/zjit/src/cruby_bindings.inc.rs
index d10e3cc8a0..33f7e0b3e6 100644
--- a/zjit/src/cruby_bindings.inc.rs
+++ b/zjit/src/cruby_bindings.inc.rs
@@ -944,6 +944,7 @@ unsafe extern "C" {
pub fn rb_zjit_print_exception();
pub fn rb_zjit_shape_obj_too_complex_p(obj: VALUE) -> bool;
pub fn rb_zjit_singleton_class_p(klass: VALUE) -> bool;
+ pub fn rb_zjit_defined_ivar(obj: VALUE, id: ID, pushval: VALUE) -> VALUE;
pub fn rb_iseq_encoded_size(iseq: *const rb_iseq_t) -> ::std::os::raw::c_uint;
pub fn rb_iseq_pc_at_idx(iseq: *const rb_iseq_t, insn_idx: u32) -> *mut VALUE;
pub fn rb_iseq_opcode_at_pc(iseq: *const rb_iseq_t, pc: *const VALUE) -> ::std::os::raw::c_int;