diff options
author | Takashi Kokubun <takashikkbn@gmail.com> | 2022-12-01 07:59:56 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-12-01 10:59:56 -0500 |
commit | 2c939458cab06b4fda09b55a57b8bac30efe6b17 (patch) | |
tree | 78f2509fe19c8dda9dc5c2f2d0dcf10d739554cb | |
parent | 5872fd6f6cc5ddde33df94fb11e773eeb653e55e (diff) | |
download | ruby-2c939458cab06b4fda09b55a57b8bac30efe6b17.tar.gz |
YJIT: Reorder branches for Fixnum opt_case_dispatch (#6841)
* YJIT: Reorder branches for Fixnum opt_case_dispatch
Co-authored-by: Maxime Chevalier-Boisvert <maxime.chevalierboisvert@shopify.com>
Co-authored-by: Alan Wu <alansi.xingwu@shopify.com>
* YJIT: Don't support too large values
Co-authored-by: Maxime Chevalier-Boisvert <maxime.chevalierboisvert@shopify.com>
Co-authored-by: Alan Wu <alansi.xingwu@shopify.com>
-rw-r--r-- | test/ruby/test_yjit.rb | 17 | ||||
-rw-r--r-- | yjit/bindgen/src/main.rs | 1 | ||||
-rw-r--r-- | yjit/src/codegen.rs | 57 | ||||
-rw-r--r-- | yjit/src/cruby_bindings.inc.rs | 7 |
4 files changed, 77 insertions, 5 deletions
diff --git a/test/ruby/test_yjit.rb b/test/ruby/test_yjit.rb index aa29cf471f..1020257580 100644 --- a/test/ruby/test_yjit.rb +++ b/test/ruby/test_yjit.rb @@ -843,6 +843,23 @@ class TestYJIT < Test::Unit::TestCase RUBY end + def test_opt_case_dispatch + assert_compiles(<<~'RUBY', exits: :any, result: [:"1", "2", 3]) + def case_dispatch(val) + case val + when 1 + :"#{val}" + when 2 + "#{val}" + else + val + end + end + + [case_dispatch(1), case_dispatch(2), case_dispatch(3)] + RUBY + end + def test_code_gc assert_compiles(code_gc_helpers + <<~'RUBY', exits: :any, result: :ok) return :not_paged unless add_pages(100) # prepare freeable pages diff --git a/yjit/bindgen/src/main.rs b/yjit/bindgen/src/main.rs index 9afba864d2..ffcc148685 100644 --- a/yjit/bindgen/src/main.rs +++ b/yjit/bindgen/src/main.rs @@ -105,6 +105,7 @@ fn main() { .allowlist_function("rb_hash_aset") .allowlist_function("rb_hash_aref") .allowlist_function("rb_hash_bulk_insert") + .allowlist_function("rb_hash_stlike_lookup") // From include/ruby/internal/intern/array.h .allowlist_function("rb_ary_new_capa") diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index bb4091c5c2..5b7eb8f67e 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -1895,6 +1895,9 @@ pub const SEND_MAX_DEPTH: i32 = 5; // up to 20 different methods for send pub const SEND_MAX_CHAIN_DEPTH: i32 = 20; +// up to 20 different offsets for case-when +pub const CASE_WHEN_MAX_DEPTH: i32 = 20; + // Codegen for setting an instance variable. // Preconditions: // - receiver is in REG0 @@ -3126,10 +3129,10 @@ fn gen_opt_regexpmatch2( } fn gen_opt_case_dispatch( - _jit: &mut JITState, + jit: &mut JITState, ctx: &mut Context, - _asm: &mut Assembler, - _ocb: &mut OutlinedCb, + asm: &mut Assembler, + ocb: &mut OutlinedCb, ) -> CodegenStatus { // Normally this instruction would lookup the key in a hash and jump to an // offset based on that. @@ -3138,10 +3141,54 @@ fn gen_opt_case_dispatch( // We'd hope that our jitted code will be sufficiently fast without the // hash lookup, at least for small hashes, but it's worth revisiting this // assumption in the future. + if !jit_at_current_insn(jit) { + defer_compilation(jit, ctx, asm, ocb); + return EndBlock; + } + let starting_context = *ctx; - ctx.stack_pop(1); + let case_hash = jit_get_arg(jit, 0); + let else_offset = jit_get_arg(jit, 1).as_u32(); + + // Try to reorder case/else branches so that ones that are actually used come first. + // Supporting only Fixnum for now so that the implementation can be an equality check. + let key_opnd = ctx.stack_pop(1); + let comptime_key = jit_peek_at_stack(jit, ctx, 0); + if comptime_key.fixnum_p() && comptime_key.0 <= u32::MAX.as_usize() { + if !assume_bop_not_redefined(jit, ocb, INTEGER_REDEFINED_OP_FLAG, BOP_EQQ) { + return CantCompile; + } - KeepCompiling // continue with the next instruction + // Check if the key is the same value + asm.cmp(key_opnd, comptime_key.into()); + let side_exit = get_side_exit(jit, ocb, &starting_context); + jit_chain_guard( + JCC_JNE, + jit, + &starting_context, + asm, + ocb, + CASE_WHEN_MAX_DEPTH as i32, + side_exit, + ); + + // Get the offset for the compile-time key + let mut offset = 0; + unsafe { rb_hash_stlike_lookup(case_hash, comptime_key.0 as _, &mut offset) }; + let jump_offset = if offset == 0 { + // NOTE: If we hit the else branch with various values, it could negatively impact the performance. + else_offset + } else { + (offset as u32) >> 1 // FIX2LONG + }; + + // Jump to the offset of case or else + let jump_block = BlockId { iseq: jit.iseq, idx: jit_next_insn_idx(jit) + jump_offset }; + gen_direct_jump(jit, &ctx, jump_block, asm); + EndBlock + } else { + KeepCompiling // continue with === branches + } } fn gen_branchif_branch( diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs index ea57bd69d8..671c0ad353 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -1030,6 +1030,13 @@ extern "C" { pub fn rb_hash_resurrect(hash: VALUE) -> VALUE; } extern "C" { + pub fn rb_hash_stlike_lookup( + hash: VALUE, + key: st_data_t, + pval: *mut st_data_t, + ) -> ::std::os::raw::c_int; +} +extern "C" { pub fn rb_gvar_get(arg1: ID) -> VALUE; } extern "C" { |