diff options
author | Takashi Kokubun <[email protected]> | 2024-05-28 17:10:33 -0700 |
---|---|---|
committer | Takashi Kokubun <[email protected]> | 2024-05-28 17:10:33 -0700 |
commit | 6383d0afac6aa02b3e72d08128cc1d8327f149fa (patch) | |
tree | 283ba110d9602218608d23b5bf5806c828482c35 | |
parent | d7ad60373988487ae746129dfd7f65df1dd13b86 (diff) |
merge revision(s) 015b0e2e1d312e2be60551587389c8da5c585e6f,ac1e9e443a0d6a4d4c0801c26d1d8bd33d9eb431: [Backport #20195]
YJIT: Fix unused warnings
```
warning: unused import: `condition::Condition`
--> src/asm/arm64/arg/mod.rs:13:9
|
13 | pub use condition::Condition;
| ^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(unused_imports)]` on by default
warning: unused import: `rb_yjit_fix_mul_fix as rb_fix_mul_fix`
--> src/cruby.rs:188:9
|
188 | pub use rb_yjit_fix_mul_fix as rb_fix_mul_fix;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: unused import: `rb_insn_len as raw_insn_len`
--> src/cruby.rs:142:9
|
142 | pub use rb_insn_len as raw_insn_len;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(unused_imports)]` on by default
```
Make asm public so it stops warning about unused public stuff in there.
YJIT: Fix ruby2_keywords splat+rest and drop bogus checks
YJIT didn't guard for ruby2_keywords hash in case of splat calls that
land in methods with a rest parameter, creating incorrect results.
The compile-time checks didn't correspond to any actual effects of
ruby2_keywords, so it was masking this bug and YJIT was needlessly
refusing to compile some code. About 16% of fallback reasons in
`lobsters` was due to the ISeq check.
We already handle the tagging part with
exit_if_supplying_kw_and_has_no_kw() and should now have a dynamic guard
for all splat cases.
Note for backporting: You also need 7f51959ff1.
[Bug #20195]
-rw-r--r-- | bootstraptest/test_yjit.rb | 31 | ||||
-rw-r--r-- | version.h | 2 | ||||
-rw-r--r-- | yjit.c | 15 | ||||
-rw-r--r-- | yjit/bindgen/src/main.rs | 2 | ||||
-rw-r--r-- | yjit/src/codegen.rs | 38 | ||||
-rw-r--r-- | yjit/src/cruby.rs | 5 | ||||
-rw-r--r-- | yjit/src/cruby_bindings.inc.rs | 2 | ||||
-rw-r--r-- | yjit/src/lib.rs | 3 | ||||
-rw-r--r-- | yjit/src/stats.rs | 2 |
9 files changed, 57 insertions, 43 deletions
diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb index a20445066d..258eef48dc 100644 --- a/bootstraptest/test_yjit.rb +++ b/bootstraptest/test_yjit.rb @@ -4423,3 +4423,34 @@ assert_equal '0', %q{ entry } + +# Integer succ and overflow +assert_equal '[2, 4611686018427387904]', %q{ + [1.succ, 4611686018427387903.succ] +} + +# Integer right shift +assert_equal '[0, 1, -4]', %q{ + [0 >> 1, 2 >> 1, -7 >> 1] +} + +assert_equal '[nil, "yield"]', %q{ + def defined_yield = defined?(yield) + [defined_yield, defined_yield {}] +} + +# splat with ruby2_keywords into rest parameter +assert_equal '[[{:a=>1}], {}]', %q{ + ruby2_keywords def foo(*args) = args + + def bar(*args, **kw) = [args, kw] + + def pass_bar(*args) = bar(*args) + + def body + args = foo(a: 1) + pass_bar(*args) + end + + body +} @@ -11,7 +11,7 @@ # define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR #define RUBY_VERSION_TEENY 1 #define RUBY_RELEASE_DATE RUBY_RELEASE_YEAR_STR"-"RUBY_RELEASE_MONTH_STR"-"RUBY_RELEASE_DAY_STR -#define RUBY_PATCHLEVEL 58 +#define RUBY_PATCHLEVEL 59 #include "ruby/version.h" #include "ruby/internal/abi.h" @@ -871,10 +871,17 @@ rb_yjit_fix_mod_fix(VALUE recv, VALUE obj) return rb_fix_mod_fix(recv, obj); } -VALUE -rb_yjit_fix_mul_fix(VALUE recv, VALUE obj) -{ - return rb_fix_mul_fix(recv, obj); +// Return non-zero when `obj` is an array and its last item is a +// `ruby2_keywords` hash. We don't support this kind of splat. +size_t +rb_yjit_ruby2_keywords_splat_p(VALUE obj) +{ + if (!RB_TYPE_P(obj, T_ARRAY)) return 0; + long len = RARRAY_LEN(obj); + if (len == 0) return 0; + VALUE last = RARRAY_AREF(obj, len - 1); + if (!RB_TYPE_P(last, T_HASH)) return 0; + return FL_TEST_RAW(last, RHASH_PASS_AS_KEYWORDS); } // Print the Ruby source location of some ISEQ for debugging purposes diff --git a/yjit/bindgen/src/main.rs b/yjit/bindgen/src/main.rs index b4b4d17f5d..83bee3b4d6 100644 --- a/yjit/bindgen/src/main.rs +++ b/yjit/bindgen/src/main.rs @@ -426,9 +426,9 @@ fn main() { .allowlist_function("rb_yarv_str_eql_internal") .allowlist_function("rb_str_neq_internal") .allowlist_function("rb_yarv_ary_entry_internal") + .allowlist_function("rb_yjit_ruby2_keywords_splat_p") .allowlist_function("rb_yjit_fix_div_fix") .allowlist_function("rb_yjit_fix_mod_fix") - .allowlist_function("rb_yjit_fix_mul_fix") .allowlist_function("rb_FL_TEST") .allowlist_function("rb_FL_TEST_RAW") .allowlist_function("rb_RB_TYPE_P") diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index e5ca2f0bf0..ea00889c0c 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -5413,18 +5413,6 @@ fn gen_send_cfunc( return None; } - // In order to handle backwards compatibility between ruby 3 and 2 - // ruby2_keywords was introduced. It is called only on methods - // with splat and changes they way they handle them. - // We are just going to not compile these. - // https://docs.ruby-lang.org/en/3.2/Module.html#method-i-ruby2_keywords - if unsafe { - get_iseq_flags_ruby2_keywords(jit.iseq) && flags & VM_CALL_ARGS_SPLAT != 0 - } { - gen_counter_incr(asm, Counter::send_args_splat_cfunc_ruby2_keywords); - return None; - } - let kw_arg = unsafe { vm_ci_kwarg(ci) }; let kw_arg_num = if kw_arg.is_null() { 0 @@ -6004,7 +5992,6 @@ fn gen_send_iseq( exit_if_has_post(asm, iseq)?; exit_if_has_kwrest(asm, iseq)?; exit_if_kw_splat(asm, flags)?; - exit_if_splat_and_ruby2_keywords(asm, jit, flags)?; exit_if_has_rest_and_captured(asm, iseq_has_rest, captured_opnd)?; exit_if_has_rest_and_supplying_kws(asm, iseq_has_rest, iseq, supplying_kws)?; exit_if_supplying_kw_and_has_no_kw(asm, supplying_kws, iseq)?; @@ -6282,6 +6269,8 @@ fn gen_send_iseq( asm.jbe(Target::side_exit(Counter::guard_send_se_cf_overflow)); if iseq_has_rest && flags & VM_CALL_ARGS_SPLAT != 0 { + let splat_pos = i32::from(block_arg) + kw_arg_num; + // Insert length guard for a call to copy_splat_args_for_rest_callee() // that will come later. We will have made changes to // the stack by spilling or handling __send__ shifting @@ -6295,12 +6284,19 @@ fn gen_send_iseq( if take_count > 0 { asm_comment!(asm, "guard splat_array_length >= {take_count}"); - let splat_array = asm.stack_opnd(i32::from(block_arg) + kw_arg_num); + let splat_array = asm.stack_opnd(splat_pos); let array_len_opnd = get_array_len(asm, splat_array); asm.cmp(array_len_opnd, take_count.into()); asm.jl(Target::side_exit(Counter::guard_send_iseq_has_rest_and_splat_too_few)); } } + + // All splats need to guard for ruby2_keywords hash. Check with a function call when + // splatting into a rest param since the index for the last item in the array is dynamic. + asm_comment!(asm, "guard no ruby2_keywords hash in splat"); + let bad_splat = asm.ccall(rb_yjit_ruby2_keywords_splat_p as _, vec![asm.stack_opnd(splat_pos)]); + asm.cmp(bad_splat, 0.into()); + asm.jnz(Target::side_exit(Counter::guard_send_splatarray_last_ruby_2_keywords)); } match block_arg_type { @@ -6846,20 +6842,6 @@ fn exit_if_kw_splat(asm: &mut Assembler, flags: u32) -> Option<()> { } #[must_use] -fn exit_if_splat_and_ruby2_keywords(asm: &mut Assembler, jit: &mut JITState, flags: u32) -> Option<()> { - // In order to handle backwards compatibility between ruby 3 and 2 - // ruby2_keywords was introduced. It is called only on methods - // with splat and changes they way they handle them. - // We are just going to not compile these. - // https://www.rubydoc.info/stdlib/core/Proc:ruby2_keywords - exit_if( - asm, - unsafe { get_iseq_flags_ruby2_keywords(jit.iseq) } && flags & VM_CALL_ARGS_SPLAT != 0, - Counter::send_iseq_ruby2_keywords, - ) -} - -#[must_use] fn exit_if_has_rest_and_captured(asm: &mut Assembler, iseq_has_rest: bool, captured_opnd: Option<Opnd>) -> Option<()> { exit_if(asm, iseq_has_rest && captured_opnd.is_some(), Counter::send_iseq_has_rest_and_captured) } diff --git a/yjit/src/cruby.rs b/yjit/src/cruby.rs index e4b5392cfe..ae69135af3 100644 --- a/yjit/src/cruby.rs +++ b/yjit/src/cruby.rs @@ -139,7 +139,6 @@ extern "C" { // Renames pub use rb_insn_name as raw_insn_name; -pub use rb_insn_len as raw_insn_len; pub use rb_get_ec_cfp as get_ec_cfp; pub use rb_get_cfp_iseq as get_cfp_iseq; pub use rb_get_cfp_pc as get_cfp_pc; @@ -166,7 +165,6 @@ pub use rb_get_iseq_flags_has_lead as get_iseq_flags_has_lead; pub use rb_get_iseq_flags_has_opt as get_iseq_flags_has_opt; pub use rb_get_iseq_flags_has_kw as get_iseq_flags_has_kw; pub use rb_get_iseq_flags_has_rest as get_iseq_flags_has_rest; -pub use rb_get_iseq_flags_ruby2_keywords as get_iseq_flags_ruby2_keywords; pub use rb_get_iseq_flags_has_post as get_iseq_flags_has_post; pub use rb_get_iseq_flags_has_kwrest as get_iseq_flags_has_kwrest; pub use rb_get_iseq_flags_has_block as get_iseq_flags_has_block; @@ -185,7 +183,6 @@ pub use rb_yarv_str_eql_internal as rb_str_eql_internal; pub use rb_yarv_ary_entry_internal as rb_ary_entry_internal; pub use rb_yjit_fix_div_fix as rb_fix_div_fix; pub use rb_yjit_fix_mod_fix as rb_fix_mod_fix; -pub use rb_yjit_fix_mul_fix as rb_fix_mul_fix; pub use rb_FL_TEST as FL_TEST; pub use rb_FL_TEST_RAW as FL_TEST_RAW; pub use rb_RB_TYPE_P as RB_TYPE_P; @@ -222,7 +219,7 @@ pub fn insn_len(opcode: usize) -> u32 { #[cfg(not(test))] unsafe { - raw_insn_len(VALUE(opcode)).try_into().unwrap() + rb_insn_len(VALUE(opcode)).try_into().unwrap() } } diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs index 394c6d013c..7a54b177f1 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -1123,7 +1123,7 @@ extern "C" { pub fn rb_yjit_rb_ary_subseq_length(ary: VALUE, beg: ::std::os::raw::c_long) -> VALUE; pub fn rb_yjit_fix_div_fix(recv: VALUE, obj: VALUE) -> VALUE; pub fn rb_yjit_fix_mod_fix(recv: VALUE, obj: VALUE) -> VALUE; - pub fn rb_yjit_fix_mul_fix(recv: VALUE, obj: VALUE) -> VALUE; + pub fn rb_yjit_ruby2_keywords_splat_p(obj: VALUE) -> usize; pub fn rb_yjit_dump_iseq_loc(iseq: *const rb_iseq_t, insn_idx: u32); pub fn rb_FL_TEST(obj: VALUE, flags: VALUE) -> VALUE; pub fn rb_FL_TEST_RAW(obj: VALUE, flags: VALUE) -> VALUE; diff --git a/yjit/src/lib.rs b/yjit/src/lib.rs index ce87cc250a..3f3d24be4b 100644 --- a/yjit/src/lib.rs +++ b/yjit/src/lib.rs @@ -3,8 +3,7 @@ #![allow(clippy::too_many_arguments)] // :shrug: #![allow(clippy::identity_op)] // Sometimes we do it for style - -mod asm; +pub mod asm; mod backend; mod codegen; mod core; diff --git a/yjit/src/stats.rs b/yjit/src/stats.rs index eab3db010f..3e5b6db176 100644 --- a/yjit/src/stats.rs +++ b/yjit/src/stats.rs @@ -355,10 +355,8 @@ make_counters! { send_args_splat_opt_call, send_args_splat_cfunc_var_args, send_args_splat_cfunc_zuper, - send_args_splat_cfunc_ruby2_keywords, send_iseq_splat_arity_error, send_splat_too_long, - send_iseq_ruby2_keywords, send_send_not_imm, send_send_wrong_args, send_send_null_mid, |