mirror of
https://github.com/ruby/ruby.git
synced 2025-09-16 09:04:05 +02:00
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 deal well. Reject for now. Debugged with help from `@tekknolagi` and `tool/zjit_bisect.rb`. Merely avoiding direct sends is enough to pass the attached test, but also avoid compiling ISEQs with `...` 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.
This commit is contained in:
parent
eb931a09c5
commit
0ba488d7f5
2 changed files with 22 additions and 17 deletions
|
@ -70,6 +70,15 @@ class TestZJIT < Test::Unit::TestCase
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_call_a_forwardable_method
|
||||||
|
assert_runs '[]', %q{
|
||||||
|
def test_root = forwardable
|
||||||
|
def forwardable(...) = Array.[](...)
|
||||||
|
test_root
|
||||||
|
test_root
|
||||||
|
}, call_threshold: 2
|
||||||
|
end
|
||||||
|
|
||||||
def test_setlocal_on_eval_with_spill
|
def test_setlocal_on_eval_with_spill
|
||||||
assert_compiles '1', %q{
|
assert_compiles '1', %q{
|
||||||
@b = binding
|
@b = binding
|
||||||
|
|
|
@ -969,6 +969,7 @@ fn can_direct_send(iseq: *const rb_iseq_t) -> bool {
|
||||||
else if unsafe { rb_get_iseq_flags_has_kw(iseq) } { false }
|
else if unsafe { rb_get_iseq_flags_has_kw(iseq) } { false }
|
||||||
else if unsafe { rb_get_iseq_flags_has_kwrest(iseq) } { false }
|
else if unsafe { rb_get_iseq_flags_has_kwrest(iseq) } { false }
|
||||||
else if unsafe { rb_get_iseq_flags_has_block(iseq) } { false }
|
else if unsafe { rb_get_iseq_flags_has_block(iseq) } { false }
|
||||||
|
else if unsafe { rb_get_iseq_flags_forwardable(iseq) } { false }
|
||||||
else { true }
|
else { true }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2581,6 +2582,9 @@ pub enum CallType {
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum ParameterType {
|
pub enum ParameterType {
|
||||||
Optional,
|
Optional,
|
||||||
|
/// For example, `foo(...)`. Interaction of JIT
|
||||||
|
/// calling convention and side exits currently unsolved.
|
||||||
|
Forwardable,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
|
@ -2650,6 +2654,7 @@ pub const SELF_PARAM_IDX: usize = 0;
|
||||||
|
|
||||||
fn filter_unknown_parameter_type(iseq: *const rb_iseq_t) -> Result<(), ParseError> {
|
fn filter_unknown_parameter_type(iseq: *const rb_iseq_t) -> Result<(), ParseError> {
|
||||||
if unsafe { rb_get_iseq_body_param_opt_num(iseq) } != 0 { return Err(ParseError::UnknownParameterType(ParameterType::Optional)); }
|
if unsafe { rb_get_iseq_body_param_opt_num(iseq) } != 0 { return Err(ParseError::UnknownParameterType(ParameterType::Optional)); }
|
||||||
|
if unsafe { rb_get_iseq_flags_forwardable(iseq) } { return Err(ParseError::UnknownParameterType(ParameterType::Forwardable)); }
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4583,11 +4588,13 @@ mod tests {
|
||||||
eval("
|
eval("
|
||||||
def test(...) = super(...)
|
def test(...) = super(...)
|
||||||
");
|
");
|
||||||
assert_method_hir("test", expect![[r#"
|
assert_compile_fails("test", ParseError::UnknownParameterType(ParameterType::Forwardable));
|
||||||
fn test@<compiled>:2:
|
}
|
||||||
bb0(v0:BasicObject, v1:BasicObject):
|
|
||||||
SideExit UnknownOpcode(invokesuperforward)
|
#[test]
|
||||||
"#]]);
|
fn test_cant_compile_forwardable() {
|
||||||
|
eval("def forwardable(...) = nil");
|
||||||
|
assert_compile_fails("forwardable", ParseError::UnknownParameterType(ParameterType::Forwardable));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(max): Figure out how to generate a call with OPT_SEND flag
|
// TODO(max): Figure out how to generate a call with OPT_SEND flag
|
||||||
|
@ -4631,11 +4638,7 @@ mod tests {
|
||||||
eval("
|
eval("
|
||||||
def test(...) = foo(...)
|
def test(...) = foo(...)
|
||||||
");
|
");
|
||||||
assert_method_hir("test", expect![[r#"
|
assert_compile_fails("test", ParseError::UnknownParameterType(ParameterType::Forwardable));
|
||||||
fn test@<compiled>:2:
|
|
||||||
bb0(v0:BasicObject, v1:BasicObject):
|
|
||||||
SideExit UnknownOpcode(sendforward)
|
|
||||||
"#]]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -5691,7 +5694,6 @@ mod opt_tests {
|
||||||
def kw_rest(**k) = k
|
def kw_rest(**k) = k
|
||||||
def post(*rest, post) = post
|
def post(*rest, post) = post
|
||||||
def block(&b) = nil
|
def block(&b) = nil
|
||||||
def forwardable(...) = nil
|
|
||||||
");
|
");
|
||||||
|
|
||||||
assert_optimized_method_hir("rest", expect![[r#"
|
assert_optimized_method_hir("rest", expect![[r#"
|
||||||
|
@ -5721,12 +5723,6 @@ mod opt_tests {
|
||||||
bb0(v0:BasicObject, v1:ArrayExact, v2:BasicObject):
|
bb0(v0:BasicObject, v1:ArrayExact, v2:BasicObject):
|
||||||
Return v2
|
Return v2
|
||||||
"#]]);
|
"#]]);
|
||||||
assert_optimized_method_hir("forwardable", expect![[r#"
|
|
||||||
fn forwardable@<compiled>:7:
|
|
||||||
bb0(v0:BasicObject, v1:BasicObject):
|
|
||||||
v3:NilClass = Const Value(nil)
|
|
||||||
Return v3
|
|
||||||
"#]]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue