Skip to content

Commit 414aa62

Browse files
committed
ZJIT: Add recompile support to GuardType but don't use it
1 parent 344b5ea commit 414aa62

2 files changed

Lines changed: 50 additions & 31 deletions

File tree

zjit/src/codegen.rs

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -683,7 +683,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
683683
Insn::Test { val } => gen_test(asm, opnd!(val)),
684684
Insn::RefineType { val, .. } => opnd!(val),
685685
Insn::HasType { val, expected } => gen_has_type(jit, asm, opnd!(val), *expected),
686-
Insn::GuardType { val, guard_type, state } => gen_guard_type(jit, asm, opnd!(val), *guard_type, &function.frame_state(*state)),
686+
&Insn::GuardType { val, guard_type, state, recompile } => gen_guard_type(jit, asm, opnd!(val), guard_type, recompile, &function.frame_state(state)),
687687
&Insn::GuardBitEquals { val, expected, reason, state, recompile } => gen_guard_bit_equals(jit, asm, opnd!(val), expected, reason, recompile, &function.frame_state(state)),
688688
&Insn::GuardAnyBitSet { val, mask, reason, state, .. } => gen_guard_any_bit_set(jit, asm, opnd!(val), mask, reason, &function.frame_state(state)),
689689
&Insn::GuardNoBitsSet { val, mask, reason, state, .. } => gen_guard_no_bits_set(jit, asm, opnd!(val), mask, reason, &function.frame_state(state)),
@@ -2501,32 +2501,32 @@ fn gen_has_type(jit: &mut JITState, asm: &mut Assembler, val: lir::Opnd, ty: Typ
25012501
}
25022502

25032503
/// Compile a type check with a side exit
2504-
fn gen_guard_type(jit: &mut JITState, asm: &mut Assembler, val: lir::Opnd, guard_type: Type, state: &FrameState) -> lir::Opnd {
2504+
fn gen_guard_type(jit: &mut JITState, asm: &mut Assembler, val: lir::Opnd, guard_type: Type, recompile: Option<Recompile>, state: &FrameState) -> lir::Opnd {
25052505
gen_incr_counter(asm, Counter::guard_type_count);
25062506
if guard_type.is_subtype(types::Fixnum) {
25072507
asm.test(val, Opnd::UImm(RUBY_FIXNUM_FLAG as u64));
2508-
asm.jz(jit, side_exit(jit, state, GuardType(guard_type)));
2508+
asm.jz(jit, side_exit_with_recompile(jit, state, GuardType(guard_type), recompile));
25092509
} else if guard_type.is_subtype(types::Flonum) {
25102510
// Flonum: (val & RUBY_FLONUM_MASK) == RUBY_FLONUM_FLAG
25112511
let masked = asm.and(val, Opnd::UImm(RUBY_FLONUM_MASK as u64));
25122512
asm.cmp(masked, Opnd::UImm(RUBY_FLONUM_FLAG as u64));
2513-
asm.jne(jit, side_exit(jit, state, GuardType(guard_type)));
2513+
asm.jne(jit, side_exit_with_recompile(jit, state, GuardType(guard_type), recompile));
25142514
} else if guard_type.is_subtype(types::StaticSymbol) {
25152515
// Static symbols have (val & 0xff) == RUBY_SYMBOL_FLAG
25162516
// Use 8-bit comparison like YJIT does.
25172517
// If `val` is a constant (rare but possible), put it in a register to allow masking.
25182518
let val = asm.load_imm(val);
25192519
asm.cmp(val.with_num_bits(8), Opnd::UImm(RUBY_SYMBOL_FLAG as u64));
2520-
asm.jne(jit, side_exit(jit, state, GuardType(guard_type)));
2520+
asm.jne(jit, side_exit_with_recompile(jit, state, GuardType(guard_type), recompile));
25212521
} else if guard_type.is_subtype(types::NilClass) {
25222522
asm.cmp(val, Qnil.into());
2523-
asm.jne(jit, side_exit(jit, state, GuardType(guard_type)));
2523+
asm.jne(jit, side_exit_with_recompile(jit, state, GuardType(guard_type), recompile));
25242524
} else if guard_type.is_subtype(types::TrueClass) {
25252525
asm.cmp(val, Qtrue.into());
2526-
asm.jne(jit, side_exit(jit, state, GuardType(guard_type)));
2526+
asm.jne(jit, side_exit_with_recompile(jit, state, GuardType(guard_type), recompile));
25272527
} else if guard_type.is_subtype(types::FalseClass) {
25282528
asm.cmp(val, Qfalse.into());
2529-
asm.jne(jit, side_exit(jit, state, GuardType(guard_type)));
2529+
asm.jne(jit, side_exit_with_recompile(jit, state, GuardType(guard_type), recompile));
25302530
} else if guard_type.is_immediate() {
25312531
// All immediate types' guard should have been handled above
25322532
panic!("unexpected immediate guard type: {guard_type}");
@@ -2538,7 +2538,7 @@ fn gen_guard_type(jit: &mut JITState, asm: &mut Assembler, val: lir::Opnd, guard
25382538
let val = asm.load_mem(val);
25392539

25402540
// Check if it's a special constant
2541-
let side_exit = side_exit(jit, state, GuardType(guard_type));
2541+
let side_exit = side_exit_with_recompile(jit, state, GuardType(guard_type), recompile);
25422542
asm.test(val, (RUBY_IMMEDIATE_MASK as u64).into());
25432543
asm.jnz(jit, side_exit.clone());
25442544

@@ -2551,8 +2551,27 @@ fn gen_guard_type(jit: &mut JITState, asm: &mut Assembler, val: lir::Opnd, guard
25512551

25522552
asm.cmp(klass, Opnd::Value(expected_class));
25532553
asm.jne(jit, side_exit);
2554+
} else if guard_type.is_subtype(types::TData) {
2555+
let side = side_exit_with_recompile(jit, state, GuardType(guard_type), recompile);
2556+
2557+
// Check special constant
2558+
asm.test(val, Opnd::UImm(RUBY_IMMEDIATE_MASK as u64));
2559+
asm.jnz(jit, side.clone());
2560+
2561+
// Check false
2562+
asm.cmp(val, Qfalse.into());
2563+
asm.je(jit, side.clone());
2564+
2565+
// Check the T_DATA builtin type.
2566+
let val = asm.load_mem(val);
2567+
let flags = asm.load(Opnd::mem(VALUE_BITS, val, RUBY_OFFSET_RBASIC_FLAGS));
2568+
let mask = RUBY_T_MASK.to_usize();
2569+
let expected = RUBY_T_DATA.to_usize();
2570+
let masked = asm.and(flags, mask.into());
2571+
asm.cmp(masked, expected.into());
2572+
asm.jne(jit, side);
25542573
} else if let Some(builtin_type) = guard_type.builtin_type_equivalent() {
2555-
let side = side_exit(jit, state, GuardType(guard_type));
2574+
let side = side_exit_with_recompile(jit, state, GuardType(guard_type), recompile);
25562575

25572576
// Check special constant
25582577
asm.test(val, Opnd::UImm(RUBY_IMMEDIATE_MASK as u64));
@@ -2569,7 +2588,7 @@ fn gen_guard_type(jit: &mut JITState, asm: &mut Assembler, val: lir::Opnd, guard
25692588
asm.cmp(tag, Opnd::UImm(builtin_type as u64));
25702589
asm.jne(jit, side);
25712590
} else if guard_type.bit_equal(types::HeapBasicObject) {
2572-
let side_exit = side_exit(jit, state, GuardType(guard_type));
2591+
let side_exit = side_exit_with_recompile(jit, state, GuardType(guard_type), recompile);
25732592
asm.cmp(val, Opnd::Value(Qfalse));
25742593
asm.je(jit, side_exit.clone());
25752594
asm.test(val, (RUBY_IMMEDIATE_MASK as u64).into());

zjit/src/hir.rs

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,7 +1147,7 @@ pub enum Insn {
11471147
HasType { val: InsnId, expected: Type },
11481148

11491149
/// Side-exit if val doesn't have the expected type.
1150-
GuardType { val: InsnId, guard_type: Type, state: InsnId },
1150+
GuardType { val: InsnId, guard_type: Type, state: InsnId, recompile: Option<Recompile> },
11511151
/// Side-exit if val is not the expected Const.
11521152
GuardBitEquals { val: InsnId, expected: Const, reason: SideExitReason, state: InsnId, recompile: Option<Recompile> },
11531153
/// Side-exit if (val & mask) == 0
@@ -3428,7 +3428,7 @@ impl Function {
34283428

34293429
pub fn coerce_to(&mut self, block: BlockId, val: InsnId, guard_type: Type, state: InsnId) -> InsnId {
34303430
if self.is_a(val, guard_type) { return val; }
3431-
self.push_insn(block, Insn::GuardType { val, guard_type, state })
3431+
self.push_insn(block, Insn::GuardType { val, guard_type, state , recompile: None})
34323432
}
34333433

34343434
fn count_complex_call_features(&mut self, block: BlockId, ci_flags: c_uint) {
@@ -3671,7 +3671,7 @@ impl Function {
36713671

36723672
// Add GuardType for profiled receiver
36733673
if let Some(profiled_type) = profiled_type {
3674-
recv = self.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state });
3674+
recv = self.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state, recompile: None });
36753675
}
36763676

36773677
let send_direct = self.push_insn(block, Insn::SendDirect { recv, cd, cme, iseq, args: processed_args, kw_bits, state: send_state, block: send_block });
@@ -3714,7 +3714,7 @@ impl Function {
37143714
self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass, method: mid, cme }, state });
37153715

37163716
if let Some(profiled_type) = profiled_type {
3717-
recv = self.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state });
3717+
recv = self.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state, recompile: None });
37183718
}
37193719

37203720
let send_direct = self.push_insn(block, Insn::SendDirect { recv, cd, cme, iseq, args: processed_args, kw_bits, state: send_state, block: None });
@@ -3734,7 +3734,7 @@ impl Function {
37343734

37353735
self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass, method: mid, cme }, state });
37363736
if let Some(profiled_type) = profiled_type {
3737-
recv = self.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state });
3737+
recv = self.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state, recompile: None });
37383738
}
37393739
let id = unsafe { get_cme_def_body_attr_id(cme) };
37403740

@@ -3750,7 +3750,7 @@ impl Function {
37503750

37513751
self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass, method: mid, cme }, state });
37523752
if let Some(profiled_type) = profiled_type {
3753-
recv = self.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state });
3753+
recv = self.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state, recompile: None });
37543754
}
37553755
let id = unsafe { get_cme_def_body_attr_id(cme) };
37563756

@@ -3772,7 +3772,7 @@ impl Function {
37723772
}
37733773
self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass, method: mid, cme }, state });
37743774
if let Some(profiled_type) = profiled_type {
3775-
recv = self.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state });
3775+
recv = self.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state, recompile: None });
37763776
}
37773777
let kw_splat = flags & VM_CALL_KW_SPLAT != 0;
37783778
let invoke_proc = self.push_insn(block, Insn::InvokeProc { recv, args: args.clone(), state, kw_splat });
@@ -3810,7 +3810,7 @@ impl Function {
38103810
}
38113811
self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass, method: mid, cme }, state });
38123812
if let Some(profiled_type) = profiled_type {
3813-
recv = self.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state });
3813+
recv = self.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state, recompile: None });
38143814
}
38153815
// All structs from the same Struct class should have the same
38163816
// length. So if our recv is embedded all runtime
@@ -3881,12 +3881,12 @@ impl Function {
38813881

38823882
if recv_type.is_string() {
38833883
self.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoSingletonClass { klass: recv_type.class() }, state });
3884-
let guard = self.push_insn(block, Insn::GuardType { val, guard_type: types::String, state });
3884+
let guard = self.push_insn(block, Insn::GuardType { val, guard_type: types::String, state, recompile: None });
38853885
// Infer type so AnyToString can fold off this
38863886
self.insn_types[guard.0] = self.infer_type(guard);
38873887
self.make_equal_to(insn_id, guard);
38883888
} else {
3889-
let recv = self.push_insn(block, Insn::GuardType { val, guard_type: Type::from_profiled_type(recv_type), state});
3889+
let recv = self.push_insn(block, Insn::GuardType { val, guard_type: Type::from_profiled_type(recv_type), state, recompile: None });
38903890
let send_to_s = self.push_insn(block, Insn::Send { recv, cd, block: None, args: vec![], state, reason: ObjToStringNotString });
38913891
self.make_equal_to(insn_id, send_to_s);
38923892
}
@@ -4333,16 +4333,16 @@ impl Function {
43334333
fn load_ivar_guard_type(&mut self, block: BlockId, recv: InsnId, recv_type: ProfiledType, state: InsnId) -> InsnId {
43344334
if recv_type.flags().is_t_class() {
43354335
// Check class first since `Class < Module`
4336-
self.push_insn(block, Insn::GuardType { val: recv, guard_type: types::Class, state })
4336+
self.push_insn(block, Insn::GuardType { val: recv, guard_type: types::Class, state , recompile: None})
43374337
} else if recv_type.flags().is_t_module() {
4338-
self.push_insn(block, Insn::GuardType { val: recv, guard_type: types::Module, state })
4338+
self.push_insn(block, Insn::GuardType { val: recv, guard_type: types::Module, state , recompile: None})
43394339
} else if recv_type.flags().is_t_data() {
4340-
self.push_insn(block, Insn::GuardType { val: recv, guard_type: types::TData, state })
4340+
self.push_insn(block, Insn::GuardType { val: recv, guard_type: types::TData, state , recompile: None})
43414341
} else {
43424342
// HeapBasicObject is wider than T_OBJECT, but shapes for T_OBJECTs are in a pool of
43434343
// its own and are guaranteed to be different from shapes of any other T_* types. So
43444344
// the shape check that follows already covers checking for T_OBJECT.
4345-
self.push_insn(block, Insn::GuardType { val: recv, guard_type: types::HeapBasicObject, state })
4345+
self.push_insn(block, Insn::GuardType { val: recv, guard_type: types::HeapBasicObject, state , recompile: None})
43464346
}
43474347
}
43484348

@@ -4731,7 +4731,7 @@ impl Function {
47314731

47324732
if let Some(profiled_type) = profiled_type {
47334733
// Guard receiver class
4734-
recv = fun.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state });
4734+
recv = fun.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state, recompile: None });
47354735
fun.insn_types[recv.0] = fun.infer_type(recv);
47364736
}
47374737

@@ -4797,7 +4797,7 @@ impl Function {
47974797

47984798
if let Some(profiled_type) = profiled_type {
47994799
// Guard receiver class
4800-
recv = fun.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state });
4800+
recv = fun.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state, recompile: None });
48014801
fun.insn_types[recv.0] = fun.infer_type(recv);
48024802
}
48034803

@@ -7017,9 +7017,9 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
70177017
}
70187018
let ty = Type::from_profiled_type(summary.bucket(0));
70197019
let obj = if ty.is_subtype(types::NilClass) {
7020-
fun.push_insn(block, Insn::GuardType { val: hash, guard_type: types::NilClass, state: exit_id })
7020+
fun.push_insn(block, Insn::GuardType { val: hash, guard_type: types::NilClass, state: exit_id , recompile: None})
70217021
} else if ty.is_subtype(types::HashExact) {
7022-
fun.push_insn(block, Insn::GuardType { val: hash, guard_type: types::HashExact, state: exit_id })
7022+
fun.push_insn(block, Insn::GuardType { val: hash, guard_type: types::HashExact, state: exit_id , recompile: None})
70237023
} else {
70247024
fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::SplatKwNotNilOrHash, recompile: None });
70257025
break; // End the block
@@ -8195,7 +8195,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
81958195
break; // End the block
81968196
}
81978197
if let Some(summary) = fun.polymorphic_summary(&profiles, self_param, exit_state.insn_idx) {
8198-
self_param = fun.push_insn(block, Insn::GuardType { val: self_param, guard_type: types::HeapBasicObject, state: exit_id });
8198+
self_param = fun.push_insn(block, Insn::GuardType { val: self_param, guard_type: types::HeapBasicObject, state: exit_id, recompile: None });
81998199
let rbasic_flags = fun.load_rbasic_flags(block, self_param);
82008200
let join_block = insn_idx_to_block.get(&insn_idx).copied().unwrap_or_else(|| fun.new_block(insn_idx));
82018201
let join_param = fun.push_insn(join_block, Insn::Param);
@@ -8403,7 +8403,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
84038403
break; // End the block
84048404
}
84058405
let val = state.stack_pop()?;
8406-
let array = fun.push_insn(block, Insn::GuardType { val, guard_type: types::ArrayExact, state: exit_id, });
8406+
let array = fun.push_insn(block, Insn::GuardType { val, guard_type: types::ArrayExact, state: exit_id, recompile: None });
84078407
let length = fun.push_insn(block, Insn::ArrayLength { array });
84088408
let expected = fun.push_insn(block, Insn::Const { val: Const::CInt64(num as i64) });
84098409
fun.push_insn(block, Insn::GuardGreaterEq { left: length, right: expected, reason: SideExitReason::ExpandArray, state: exit_id });

0 commit comments

Comments
 (0)