Skip to content

Commit d365733

Browse files
committed
ZJIT: Avoid compiling and direct sends to forwardable ISEQs
These `...` ISEQs have a special calling convention in the interpreter and our stubs and JIT calling convention don't handle these correctly. The attached test passes if we just avoid direct sends, but also avoid compiling ISEQs with a `...` parameter to limit exposure for now. `SendWithoutBlock`, which does dynamic dispatch using interpreter code, seems to handle calling into forwardable ISEQs correctly, so they are fine -- we can't predict where these dynamic sends land anyways.
1 parent 7b5cd5c commit d365733

2 files changed

Lines changed: 22 additions & 17 deletions

File tree

test/ruby/test_zjit.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,15 @@ def test_setlocal_on_eval
7070
}
7171
end
7272

73+
def test_call_a_forwardable_method
74+
assert_runs '[]', %q{
75+
def test_root = forwardable
76+
def forwardable(...) = Array.[](...)
77+
test_root
78+
test_root
79+
}, call_threshold: 2
80+
end
81+
7382
def test_setlocal_on_eval_with_spill
7483
assert_compiles '1', %q{
7584
@b = binding

zjit/src/hir.rs

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -953,6 +953,7 @@ fn can_direct_send(iseq: *const rb_iseq_t) -> bool {
953953
else if unsafe { rb_get_iseq_flags_has_kw(iseq) } { false }
954954
else if unsafe { rb_get_iseq_flags_has_kwrest(iseq) } { false }
955955
else if unsafe { rb_get_iseq_flags_has_block(iseq) } { false }
956+
else if unsafe { rb_get_iseq_flags_forwardable(iseq) } { false }
956957
else { true }
957958
}
958959

@@ -2480,6 +2481,9 @@ pub enum CallType {
24802481
#[derive(Debug, PartialEq)]
24812482
pub enum ParameterType {
24822483
Optional,
2484+
/// For example, `foo(...)`. Interaction of JIT
2485+
/// calling convention and side exits currently unsolved.
2486+
Forwardable,
24832487
}
24842488

24852489
#[derive(Debug, PartialEq)]
@@ -2549,6 +2553,7 @@ pub const SELF_PARAM_IDX: usize = 0;
25492553

25502554
fn filter_unknown_parameter_type(iseq: *const rb_iseq_t) -> Result<(), ParseError> {
25512555
if unsafe { rb_get_iseq_body_param_opt_num(iseq) } != 0 { return Err(ParseError::UnknownParameterType(ParameterType::Optional)); }
2556+
if unsafe { rb_get_iseq_flags_forwardable(iseq) } { return Err(ParseError::UnknownParameterType(ParameterType::Forwardable)); }
25522557
Ok(())
25532558
}
25542559

@@ -4482,11 +4487,13 @@ mod tests {
44824487
eval("
44834488
def test(...) = super(...)
44844489
");
4485-
assert_method_hir("test", expect![[r#"
4486-
fn test@<compiled>:2:
4487-
bb0(v0:BasicObject, v1:BasicObject):
4488-
SideExit UnknownOpcode(invokesuperforward)
4489-
"#]]);
4490+
assert_compile_fails("test", ParseError::UnknownParameterType(ParameterType::Forwardable));
4491+
}
4492+
4493+
#[test]
4494+
fn test_cant_compile_forwardable() {
4495+
eval("def forwardable(...) = nil");
4496+
assert_compile_fails("forwardable", ParseError::UnknownParameterType(ParameterType::Forwardable));
44904497
}
44914498

44924499
// TODO(max): Figure out how to generate a call with OPT_SEND flag
@@ -4530,11 +4537,7 @@ mod tests {
45304537
eval("
45314538
def test(...) = foo(...)
45324539
");
4533-
assert_method_hir("test", expect![[r#"
4534-
fn test@<compiled>:2:
4535-
bb0(v0:BasicObject, v1:BasicObject):
4536-
SideExit UnknownOpcode(sendforward)
4537-
"#]]);
4540+
assert_compile_fails("test", ParseError::UnknownParameterType(ParameterType::Forwardable));
45384541
}
45394542

45404543
#[test]
@@ -5515,7 +5518,6 @@ mod opt_tests {
55155518
def kw_rest(**k) = k
55165519
def post(*rest, post) = post
55175520
def block(&b) = nil
5518-
def forwardable(...) = nil
55195521
");
55205522

55215523
assert_optimized_method_hir("rest", expect![[r#"
@@ -5545,12 +5547,6 @@ mod opt_tests {
55455547
bb0(v0:BasicObject, v1:ArrayExact, v2:BasicObject):
55465548
Return v2
55475549
"#]]);
5548-
assert_optimized_method_hir("forwardable", expect![[r#"
5549-
fn forwardable@<compiled>:7:
5550-
bb0(v0:BasicObject, v1:BasicObject):
5551-
v3:NilClass = Const Value(nil)
5552-
Return v3
5553-
"#]]);
55545550
}
55555551

55565552
#[test]

0 commit comments

Comments
 (0)