Skip to content

Commit bb694d1

Browse files
committed
ZJIT: Inline attr_reader/attr_accessor
We can rewrite SendWithoutBlock to GetIvar.
1 parent b22eb0e commit bb694d1

2 files changed

Lines changed: 107 additions & 13 deletions

File tree

test/ruby/test_zjit.rb

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -868,6 +868,38 @@ def test() = @foo = 1
868868
}
869869
end
870870

871+
def test_attr_reader
872+
assert_compiles '[4, 4]', %q{
873+
class C
874+
attr_reader :foo
875+
876+
def initialize
877+
@foo = 4
878+
end
879+
end
880+
881+
def test(c) = c.foo
882+
c = C.new
883+
[test(c), test(c)]
884+
}, call_threshold: 2, insns: [:opt_send_without_block]
885+
end
886+
887+
def test_attr_accessor
888+
assert_compiles '[4, 4]', %q{
889+
class C
890+
attr_accessor :foo
891+
892+
def initialize
893+
@foo = 4
894+
end
895+
end
896+
897+
def test(c) = c.foo
898+
c = C.new
899+
[test(c), test(c)]
900+
}, call_threshold: 2, insns: [:opt_send_without_block]
901+
end
902+
871903
def test_uncached_getconstant_path
872904
assert_compiles RUBY_COPYRIGHT.dump, %q{
873905
def test = RUBY_COPYRIGHT

zjit/src/hir.rs

Lines changed: 75 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1535,22 +1535,28 @@ impl Function {
15351535
// It allows you to use a faster ISEQ if possible.
15361536
cme = unsafe { rb_check_overloaded_cme(cme, ci) };
15371537
let def_type = unsafe { get_cme_def_type(cme) };
1538-
if def_type != VM_METHOD_TYPE_ISEQ {
1538+
if def_type == VM_METHOD_TYPE_ISEQ {
15391539
// TODO(max): Allow non-iseq; cache cme
1540+
// Only specialize positional-positional calls
1541+
// TODO(max): Handle other kinds of parameter passing
1542+
let iseq = unsafe { get_def_iseq_ptr((*cme).def) };
1543+
if !can_direct_send(iseq) {
1544+
self.push_insn_id(block, insn_id); continue;
1545+
}
1546+
self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass, method: mid, cme }, state });
1547+
if let Some(expected) = guard_equal_to {
1548+
self_val = self.push_insn(block, Insn::GuardBitEquals { val: self_val, expected, state });
1549+
}
1550+
let send_direct = self.push_insn(block, Insn::SendWithoutBlockDirect { self_val, call_info, cd, cme, iseq, args, state });
1551+
self.make_equal_to(insn_id, send_direct);
1552+
} else if def_type == VM_METHOD_TYPE_IVAR && args.is_empty() {
1553+
self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass, method: mid, cme }, state });
1554+
let id = unsafe { get_cme_def_body_attr_id(cme) };
1555+
let getivar = self.push_insn(block, Insn::GetIvar { self_val, id, state });
1556+
self.make_equal_to(insn_id, getivar);
1557+
} else {
15401558
self.push_insn_id(block, insn_id); continue;
15411559
}
1542-
// Only specialize positional-positional calls
1543-
// TODO(max): Handle other kinds of parameter passing
1544-
let iseq = unsafe { get_def_iseq_ptr((*cme).def) };
1545-
if !can_direct_send(iseq) {
1546-
self.push_insn_id(block, insn_id); continue;
1547-
}
1548-
self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass, method: mid, cme }, state });
1549-
if let Some(expected) = guard_equal_to {
1550-
self_val = self.push_insn(block, Insn::GuardBitEquals { val: self_val, expected, state });
1551-
}
1552-
let send_direct = self.push_insn(block, Insn::SendWithoutBlockDirect { self_val, call_info, cd, cme, iseq, args, state });
1553-
self.make_equal_to(insn_id, send_direct);
15541560
}
15551561
Insn::GetConstantPath { ic, state, .. } => {
15561562
let idlist: *const ID = unsafe { (*ic).segments };
@@ -7371,4 +7377,60 @@ mod opt_tests {
73717377
Return v7
73727378
"#]]);
73737379
}
7380+
7381+
#[test]
7382+
fn test_inline_attr_reader() {
7383+
eval("
7384+
class C
7385+
attr_reader :foo
7386+
7387+
def initialize
7388+
@foo = 4
7389+
end
7390+
end
7391+
7392+
O = C.new
7393+
def test = O.foo
7394+
test
7395+
test
7396+
");
7397+
assert_optimized_method_hir("test", expect![[r#"
7398+
fn test@<compiled>:11:
7399+
bb0(v0:BasicObject):
7400+
PatchPoint SingleRactorMode
7401+
PatchPoint StableConstantNames(0x1000, O)
7402+
v9:BasicObject[VALUE(0x1008)] = Const Value(VALUE(0x1008))
7403+
PatchPoint MethodRedefined(C@0x1010, foo@0x1018, cme:0x1020)
7404+
v11:BasicObject = GetIvar v9, :@foo
7405+
Return v11
7406+
"#]]);
7407+
}
7408+
7409+
#[test]
7410+
fn test_inline_attr_accessor() {
7411+
eval("
7412+
class C
7413+
attr_accessor :foo
7414+
7415+
def initialize
7416+
@foo = 4
7417+
end
7418+
end
7419+
7420+
O = C.new
7421+
def test = O.foo
7422+
test
7423+
test
7424+
");
7425+
assert_optimized_method_hir("test", expect![[r#"
7426+
fn test@<compiled>:11:
7427+
bb0(v0:BasicObject):
7428+
PatchPoint SingleRactorMode
7429+
PatchPoint StableConstantNames(0x1000, O)
7430+
v9:BasicObject[VALUE(0x1008)] = Const Value(VALUE(0x1008))
7431+
PatchPoint MethodRedefined(C@0x1010, foo@0x1018, cme:0x1020)
7432+
v11:BasicObject = GetIvar v9, :@foo
7433+
Return v11
7434+
"#]]);
7435+
}
73747436
}

0 commit comments

Comments
 (0)