Skip to content

Commit 2899ff1

Browse files
authored
ZJIT: Only specialize direct positional-positional calls (ruby#13899)
This is temporary until we have a unified calling convention.
1 parent 37d088a commit 2899ff1

File tree

1 file changed

+96
-1
lines changed

1 file changed

+96
-1
lines changed

zjit/src/hir.rs

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -939,6 +939,14 @@ pub enum ValidationError {
939939
DuplicateInstruction(BlockId, InsnId),
940940
}
941941

942+
fn can_direct_send(iseq: *const rb_iseq_t) -> bool {
943+
if unsafe { rb_get_iseq_flags_has_rest(iseq) } { false }
944+
else if unsafe { rb_get_iseq_flags_has_opt(iseq) } { false }
945+
else if unsafe { rb_get_iseq_flags_has_kw(iseq) } { false }
946+
else if unsafe { rb_get_iseq_flags_has_kwrest(iseq) } { false }
947+
else if unsafe { rb_get_iseq_flags_has_block(iseq) } { false }
948+
else { true }
949+
}
942950

943951
/// A [`Function`], which is analogous to a Ruby ISeq, is a control-flow graph of [`Block`]s
944952
/// containing instructions.
@@ -1507,8 +1515,13 @@ impl Function {
15071515
// TODO(max): Allow non-iseq; cache cme
15081516
self.push_insn_id(block, insn_id); continue;
15091517
}
1510-
self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass, method: mid }, state });
1518+
// Only specialize positional-positional calls
1519+
// TODO(max): Handle other kinds of parameter passing
15111520
let iseq = unsafe { get_def_iseq_ptr((*cme).def) };
1521+
if !can_direct_send(iseq) {
1522+
self.push_insn_id(block, insn_id); continue;
1523+
}
1524+
self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass, method: mid }, state });
15121525
if let Some(expected) = guard_equal_to {
15131526
self_val = self.push_insn(block, Insn::GuardBitEquals { val: self_val, expected, state });
15141527
}
@@ -6313,6 +6326,88 @@ mod opt_tests {
63136326
"#]]);
63146327
}
63156328

6329+
#[test]
6330+
fn dont_specialize_call_to_iseq_with_opt() {
6331+
eval("
6332+
def foo(arg=1) = 1
6333+
def test = foo 1
6334+
test
6335+
test
6336+
");
6337+
assert_optimized_method_hir("test", expect![[r#"
6338+
fn test@<compiled>:3:
6339+
bb0(v0:BasicObject):
6340+
v2:Fixnum[1] = Const Value(1)
6341+
v4:BasicObject = SendWithoutBlock v0, :foo, v2
6342+
Return v4
6343+
"#]]);
6344+
}
6345+
6346+
#[test]
6347+
fn dont_specialize_call_to_iseq_with_block() {
6348+
eval("
6349+
def foo(&block) = 1
6350+
def test = foo {|| }
6351+
test
6352+
test
6353+
");
6354+
assert_optimized_method_hir("test", expect![[r#"
6355+
fn test@<compiled>:3:
6356+
bb0(v0:BasicObject):
6357+
v3:BasicObject = Send v0, 0x1000, :foo
6358+
Return v3
6359+
"#]]);
6360+
}
6361+
6362+
#[test]
6363+
fn dont_specialize_call_to_iseq_with_rest() {
6364+
eval("
6365+
def foo(*args) = 1
6366+
def test = foo 1
6367+
test
6368+
test
6369+
");
6370+
assert_optimized_method_hir("test", expect![[r#"
6371+
fn test@<compiled>:3:
6372+
bb0(v0:BasicObject):
6373+
v2:Fixnum[1] = Const Value(1)
6374+
v4:BasicObject = SendWithoutBlock v0, :foo, v2
6375+
Return v4
6376+
"#]]);
6377+
}
6378+
6379+
#[test]
6380+
fn dont_specialize_call_to_iseq_with_kw() {
6381+
eval("
6382+
def foo(a:) = 1
6383+
def test = foo(a: 1)
6384+
test
6385+
test
6386+
");
6387+
assert_optimized_method_hir("test", expect![[r#"
6388+
fn test@<compiled>:3:
6389+
bb0(v0:BasicObject):
6390+
v2:Fixnum[1] = Const Value(1)
6391+
SideExit UnknownCallType
6392+
"#]]);
6393+
}
6394+
6395+
#[test]
6396+
fn dont_specialize_call_to_iseq_with_kwrest() {
6397+
eval("
6398+
def foo(**args) = 1
6399+
def test = foo(a: 1)
6400+
test
6401+
test
6402+
");
6403+
assert_optimized_method_hir("test", expect![[r#"
6404+
fn test@<compiled>:3:
6405+
bb0(v0:BasicObject):
6406+
v2:Fixnum[1] = Const Value(1)
6407+
SideExit UnknownCallType
6408+
"#]]);
6409+
}
6410+
63166411
#[test]
63176412
fn string_bytesize_simple() {
63186413
eval("

0 commit comments

Comments
 (0)