Skip to content

Commit 40bb476

Browse files
authored
ZJIT: Inline attr_accessor/attr_writer to SetIvar (ruby#14629)
1 parent 0e60426 commit 40bb476

File tree

2 files changed

+106
-1
lines changed

2 files changed

+106
-1
lines changed

test/ruby/test_zjit.rb

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1642,7 +1642,7 @@ def test(c) = c.foo
16421642
}, call_threshold: 2, insns: [:opt_send_without_block]
16431643
end
16441644

1645-
def test_attr_accessor
1645+
def test_attr_accessor_getivar
16461646
assert_compiles '[4, 4]', %q{
16471647
class C
16481648
attr_accessor :foo
@@ -1658,6 +1658,47 @@ def test(c) = c.foo
16581658
}, call_threshold: 2, insns: [:opt_send_without_block]
16591659
end
16601660

1661+
def test_attr_accessor_setivar
1662+
assert_compiles '[5, 5]', %q{
1663+
class C
1664+
attr_accessor :foo
1665+
1666+
def initialize
1667+
@foo = 4
1668+
end
1669+
end
1670+
1671+
def test(c)
1672+
c.foo = 5
1673+
c.foo
1674+
end
1675+
1676+
c = C.new
1677+
[test(c), test(c)]
1678+
}, call_threshold: 2, insns: [:opt_send_without_block]
1679+
end
1680+
1681+
def test_attr_writer
1682+
assert_compiles '[5, 5]', %q{
1683+
class C
1684+
attr_writer :foo
1685+
1686+
def initialize
1687+
@foo = 4
1688+
end
1689+
1690+
def get_foo = @foo
1691+
end
1692+
1693+
def test(c)
1694+
c.foo = 5
1695+
c.get_foo
1696+
end
1697+
c = C.new
1698+
[test(c), test(c)]
1699+
}, call_threshold: 2, insns: [:opt_send_without_block]
1700+
end
1701+
16611702
def test_uncached_getconstant_path
16621703
assert_compiles RUBY_COPYRIGHT.dump, %q{
16631704
def test = RUBY_COPYRIGHT

zjit/src/hir.rs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1986,6 +1986,24 @@ impl Function {
19861986
}
19871987
let getivar = self.push_insn(block, Insn::GetIvar { self_val: recv, id, state });
19881988
self.make_equal_to(insn_id, getivar);
1989+
} else if def_type == VM_METHOD_TYPE_ATTRSET && args.len() == 1 {
1990+
self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass, method: mid, cme }, state });
1991+
if let Some(profiled_type) = profiled_type {
1992+
recv = self.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state });
1993+
}
1994+
let id = unsafe { get_cme_def_body_attr_id(cme) };
1995+
1996+
// Check if we're accessing ivars of a Class or Module object as they require single-ractor mode.
1997+
// We omit gen_prepare_non_leaf_call on gen_setivar, so it's unsafe to raise for multi-ractor mode.
1998+
if unsafe { rb_zjit_singleton_class_p(klass) } {
1999+
let attached = unsafe { rb_class_attached_object(klass) };
2000+
if unsafe { RB_TYPE_P(attached, RUBY_T_CLASS) || RB_TYPE_P(attached, RUBY_T_MODULE) } {
2001+
self.push_insn(block, Insn::PatchPoint { invariant: Invariant::SingleRactorMode, state });
2002+
}
2003+
}
2004+
let val = args[0];
2005+
let setivar = self.push_insn(block, Insn::SetIvar { self_val: recv, id, val, state });
2006+
self.make_equal_to(insn_id, setivar);
19892007
} else {
19902008
if let Insn::SendWithoutBlock { def_type: insn_def_type, .. } = &mut self.insns[insn_id.0] {
19912009
*insn_def_type = Some(MethodType::from(def_type));
@@ -12133,4 +12151,50 @@ mod opt_tests {
1213312151
Return v25
1213412152
");
1213512153
}
12154+
12155+
#[test]
12156+
fn test_inline_attr_accessor_set() {
12157+
eval("
12158+
class C
12159+
attr_accessor :foo
12160+
end
12161+
12162+
def test(o) = o.foo = 5
12163+
test C.new
12164+
test C.new
12165+
");
12166+
assert_snapshot!(hir_string("test"), @r"
12167+
fn test@<compiled>:6:
12168+
bb0(v0:BasicObject, v1:BasicObject):
12169+
v6:Fixnum[5] = Const Value(5)
12170+
PatchPoint MethodRedefined(C@0x1000, foo=@0x1008, cme:0x1010)
12171+
v15:HeapObject[class_exact:C] = GuardType v1, HeapObject[class_exact:C]
12172+
SetIvar v15, :@foo, v6
12173+
CheckInterrupts
12174+
Return v6
12175+
");
12176+
}
12177+
12178+
#[test]
12179+
fn test_inline_attr_writer_set() {
12180+
eval("
12181+
class C
12182+
attr_writer :foo
12183+
end
12184+
12185+
def test(o) = o.foo = 5
12186+
test C.new
12187+
test C.new
12188+
");
12189+
assert_snapshot!(hir_string("test"), @r"
12190+
fn test@<compiled>:6:
12191+
bb0(v0:BasicObject, v1:BasicObject):
12192+
v6:Fixnum[5] = Const Value(5)
12193+
PatchPoint MethodRedefined(C@0x1000, foo=@0x1008, cme:0x1010)
12194+
v15:HeapObject[class_exact:C] = GuardType v1, HeapObject[class_exact:C]
12195+
SetIvar v15, :@foo, v6
12196+
CheckInterrupts
12197+
Return v6
12198+
");
12199+
}
1213612200
}

0 commit comments

Comments
 (0)