Skip to content

Commit 3d91ed0

Browse files
committed
ZJIT: Prefer T_* guard over checking RBasic::klass
After a8f3c34 ("ZJIT: Add missing guard on ivar access on T_{DATA,CLASS,MODULE}"), speed on getivar-module.rb in ruby-bench regressed in speed to about 1/8. It turns out that the new guard for Class was implemented as a guard over `RBasic::klass`, and the one extra register pressure for the `rb_cClass` has regalloc spilling and reloading a local each loop iteration, which is catastrophic for performance. `GuardType vN, Class` can be implemented as a check for T_CLASS in RBasic::flags. It's less register pressure since the numeric value for T_CLASS is small enough to fit in a native instruction immediate and it also doesn't add GC marking work. builtin_type_equivalent() is exactly as wide, so it's correct at any position in the gen_guard_type() decision tree. Put it before the runtime_exact_ruby_class() check.
1 parent 8aa2322 commit 3d91ed0

File tree

1 file changed

+17
-17
lines changed

1 file changed

+17
-17
lines changed

zjit/src/codegen.rs

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2546,6 +2546,23 @@ fn gen_guard_type(jit: &mut JITState, asm: &mut Assembler, val: lir::Opnd, guard
25462546
} else if guard_type.is_immediate() {
25472547
// All immediate types' guard should have been handled above
25482548
panic!("unexpected immediate guard type: {guard_type}");
2549+
} else if let Some(builtin_type) = guard_type.builtin_type_equivalent() {
2550+
let side = side_exit(jit, state, GuardType(guard_type));
2551+
2552+
// Check special constant
2553+
asm.test(val, Opnd::UImm(RUBY_IMMEDIATE_MASK as u64));
2554+
asm.jnz(jit, side.clone());
2555+
2556+
// Check false
2557+
asm.cmp(val, Qfalse.into());
2558+
asm.je(jit, side.clone());
2559+
2560+
// Mask and check the builtin type
2561+
let val = asm.load_mem(val);
2562+
let flags = asm.load(Opnd::mem(VALUE_BITS, val, RUBY_OFFSET_RBASIC_FLAGS));
2563+
let tag = asm.and(flags, Opnd::UImm(RUBY_T_MASK as u64));
2564+
asm.cmp(tag, Opnd::UImm(builtin_type as u64));
2565+
asm.jne(jit, side);
25492566
} else if let Some(expected_class) = guard_type.runtime_exact_ruby_class() {
25502567
asm_comment!(asm, "guard exact class for non-immediate types");
25512568

@@ -2586,23 +2603,6 @@ fn gen_guard_type(jit: &mut JITState, asm: &mut Assembler, val: lir::Opnd, guard
25862603
let masked = asm.and(flags, mask.into());
25872604
asm.cmp(masked, expected.into());
25882605
asm.jne(jit, side);
2589-
} else if let Some(builtin_type) = guard_type.builtin_type_equivalent() {
2590-
let side = side_exit(jit, state, GuardType(guard_type));
2591-
2592-
// Check special constant
2593-
asm.test(val, Opnd::UImm(RUBY_IMMEDIATE_MASK as u64));
2594-
asm.jnz(jit, side.clone());
2595-
2596-
// Check false
2597-
asm.cmp(val, Qfalse.into());
2598-
asm.je(jit, side.clone());
2599-
2600-
// Mask and check the builtin type
2601-
let val = asm.load_mem(val);
2602-
let flags = asm.load(Opnd::mem(VALUE_BITS, val, RUBY_OFFSET_RBASIC_FLAGS));
2603-
let tag = asm.and(flags, Opnd::UImm(RUBY_T_MASK as u64));
2604-
asm.cmp(tag, Opnd::UImm(builtin_type as u64));
2605-
asm.jne(jit, side);
26062606
} else if guard_type.bit_equal(types::HeapBasicObject) {
26072607
let side_exit = side_exit(jit, state, GuardType(guard_type));
26082608
asm.cmp(val, Opnd::Value(Qfalse));

0 commit comments

Comments
 (0)