summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMax Bernstein <rubybugs@bernsteinbear.com>2026-02-09 12:40:27 -0500
committerGitHub <noreply@github.com>2026-02-09 12:40:27 -0500
commit21862be0afdd76d80875b4f98ce104113090c0c3 (patch)
treede2c49a3c7645da748692b9b6877452cffb6f35f
parent84c9822fefa442ef2fff174437a8bd723c8c658b (diff)
ZJIT: Add mask_name to GuardXYZBitsSet (#16064)
This makes HIR easier to debug.
-rw-r--r--zjit/src/codegen.rs4
-rw-r--r--zjit/src/cruby.rs9
-rw-r--r--zjit/src/hir.rs18
-rw-r--r--zjit/src/hir/opt_tests.rs24
-rw-r--r--zjit/src/hir/tests.rs4
5 files changed, 35 insertions, 24 deletions
diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs
index a1ac23311b..2fe9958e62 100644
--- a/zjit/src/codegen.rs
+++ b/zjit/src/codegen.rs
@@ -536,8 +536,8 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
Insn::GuardType { val, guard_type, state } => gen_guard_type(jit, asm, opnd!(val), *guard_type, &function.frame_state(*state)),
Insn::GuardTypeNot { val, guard_type, state } => gen_guard_type_not(jit, asm, opnd!(val), *guard_type, &function.frame_state(*state)),
&Insn::GuardBitEquals { val, expected, reason, state } => gen_guard_bit_equals(jit, asm, opnd!(val), expected, reason, &function.frame_state(state)),
- &Insn::GuardAnyBitSet { val, mask, reason, state } => gen_guard_any_bit_set(jit, asm, opnd!(val), mask, reason, &function.frame_state(state)),
- &Insn::GuardNoBitsSet { val, mask, reason, state } => gen_guard_no_bits_set(jit, asm, opnd!(val), mask, reason, &function.frame_state(state)),
+ &Insn::GuardAnyBitSet { val, mask, reason, state, .. } => gen_guard_any_bit_set(jit, asm, opnd!(val), mask, reason, &function.frame_state(state)),
+ &Insn::GuardNoBitsSet { val, mask, reason, state, .. } => gen_guard_no_bits_set(jit, asm, opnd!(val), mask, reason, &function.frame_state(state)),
&Insn::GuardLess { left, right, state } => gen_guard_less(jit, asm, opnd!(left), opnd!(right), &function.frame_state(state)),
&Insn::GuardGreaterEq { left, right, state } => gen_guard_greater_eq(jit, asm, opnd!(left), opnd!(right), &function.frame_state(state)),
Insn::PatchPoint { invariant, state } => no_output!(gen_patch_point(jit, asm, invariant, &function.frame_state(*state))),
diff --git a/zjit/src/cruby.rs b/zjit/src/cruby.rs
index 8c57e4cc71..8e569793a8 100644
--- a/zjit/src/cruby.rs
+++ b/zjit/src/cruby.rs
@@ -783,6 +783,12 @@ impl ID {
}
}
+impl Display for ID {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}", self.contents_lossy())
+ }
+}
+
/// Produce a Ruby string from a Rust string slice
pub fn rust_str_to_ruby(str: &str) -> VALUE {
unsafe { rb_utf8_str_new(str.as_ptr() as *const _, str.len() as i64) }
@@ -1401,6 +1407,9 @@ pub(crate) mod ids {
name: _ep_method_entry
name: _ep_specval
name: _rbasic_flags
+ name: RUBY_FL_FREEZE
+ name: RUBY_ELTS_SHARED
+ name: VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM
}
/// Get an CRuby `ID` to an interned string, e.g. a particular method name.
diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs
index eba3edee99..5d8cb2f7c0 100644
--- a/zjit/src/hir.rs
+++ b/zjit/src/hir.rs
@@ -1027,9 +1027,9 @@ pub enum Insn {
/// Side-exit if val is not the expected Const.
GuardBitEquals { val: InsnId, expected: Const, reason: SideExitReason, state: InsnId },
/// Side-exit if (val & mask) == 0
- GuardAnyBitSet { val: InsnId, mask: Const, reason: SideExitReason, state: InsnId },
+ GuardAnyBitSet { val: InsnId, mask: Const, mask_name: Option<ID>, reason: SideExitReason, state: InsnId },
/// Side-exit if (val & mask) != 0
- GuardNoBitsSet { val: InsnId, mask: Const, reason: SideExitReason, state: InsnId },
+ GuardNoBitsSet { val: InsnId, mask: Const, mask_name: Option<ID>, reason: SideExitReason, state: InsnId },
/// Side-exit if left is not greater than or equal to right (both operands are C long).
GuardGreaterEq { left: InsnId, right: InsnId, state: InsnId },
/// Side-exit if left is not less than right (both operands are C long).
@@ -1550,7 +1550,9 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
Insn::HasType { val, expected, .. } => { write!(f, "HasType {val}, {}", expected.print(self.ptr_map)) },
Insn::GuardTypeNot { val, guard_type, .. } => { write!(f, "GuardTypeNot {val}, {}", guard_type.print(self.ptr_map)) },
Insn::GuardBitEquals { val, expected, .. } => { write!(f, "GuardBitEquals {val}, {}", expected.print(self.ptr_map)) },
+ Insn::GuardAnyBitSet { val, mask, mask_name: Some(name), .. } => { write!(f, "GuardAnyBitSet {val}, {name}={}", mask.print(self.ptr_map)) },
Insn::GuardAnyBitSet { val, mask, .. } => { write!(f, "GuardAnyBitSet {val}, {}", mask.print(self.ptr_map)) },
+ Insn::GuardNoBitsSet { val, mask, mask_name: Some(name), .. } => { write!(f, "GuardNoBitsSet {val}, {name}={}", mask.print(self.ptr_map)) },
Insn::GuardNoBitsSet { val, mask, .. } => { write!(f, "GuardNoBitsSet {val}, {}", mask.print(self.ptr_map)) },
Insn::GuardLess { left, right, .. } => write!(f, "GuardLess {left}, {right}"),
Insn::GuardGreaterEq { left, right, .. } => write!(f, "GuardGreaterEq {left}, {right}"),
@@ -2236,8 +2238,8 @@ impl Function {
&GuardType { val, guard_type, state } => GuardType { val: find!(val), guard_type, state },
&GuardTypeNot { val, guard_type, state } => GuardTypeNot { val: find!(val), guard_type, state },
&GuardBitEquals { val, expected, reason, state } => GuardBitEquals { val: find!(val), expected, reason, state },
- &GuardAnyBitSet { val, mask, reason, state } => GuardAnyBitSet { val: find!(val), mask, reason, state },
- &GuardNoBitsSet { val, mask, reason, state } => GuardNoBitsSet { val: find!(val), mask, reason, state },
+ &GuardAnyBitSet { val, mask, mask_name, reason, state } => GuardAnyBitSet { val: find!(val), mask, mask_name, reason, state },
+ &GuardNoBitsSet { val, mask, mask_name, reason, state } => GuardNoBitsSet { val: find!(val), mask, mask_name, reason, state },
&GuardGreaterEq { left, right, state } => GuardGreaterEq { left: find!(left), right: find!(right), state },
&GuardLess { left, right, state } => GuardLess { left: find!(left), right: find!(right), state },
&IsBlockGiven { lep } => IsBlockGiven { lep: find!(lep) },
@@ -3004,12 +3006,12 @@ impl Function {
pub fn guard_not_frozen(&mut self, block: BlockId, recv: InsnId, state: InsnId) {
let flags = self.load_rbasic_flags(block, recv);
- self.push_insn(block, Insn::GuardNoBitsSet { val: flags, mask: Const::CUInt64(RUBY_FL_FREEZE as u64), reason: SideExitReason::GuardNotFrozen, state });
+ self.push_insn(block, Insn::GuardNoBitsSet { val: flags, mask: Const::CUInt64(RUBY_FL_FREEZE as u64), mask_name: Some(ID!(RUBY_FL_FREEZE)), reason: SideExitReason::GuardNotFrozen, state });
}
pub fn guard_not_shared(&mut self, block: BlockId, recv: InsnId, state: InsnId) {
let flags = self.load_rbasic_flags(block, recv);
- self.push_insn(block, Insn::GuardNoBitsSet { val: flags, mask: Const::CUInt64(RUBY_ELTS_SHARED as u64), reason: SideExitReason::GuardNotShared, state });
+ self.push_insn(block, Insn::GuardNoBitsSet { val: flags, mask: Const::CUInt64(RUBY_ELTS_SHARED as u64), mask_name: Some(ID!(RUBY_ELTS_SHARED)), reason: SideExitReason::GuardNotShared, state });
}
/// Rewrite eligible Send/SendWithoutBlock opcodes into SendDirect
@@ -6794,7 +6796,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
let ep = fun.push_insn(block, Insn::GetEP { level });
let flags = fun.push_insn(block, Insn::LoadField { recv: ep, id: ID!(_env_data_index_flags), offset: SIZEOF_VALUE_I32 * (VM_ENV_DATA_INDEX_FLAGS as i32), return_type: types::CInt64 });
- fun.push_insn(block, Insn::GuardNoBitsSet { val: flags, mask: Const::CUInt64(VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM.into()), reason: SideExitReason::BlockParamProxyModified, state: exit_id });
+ fun.push_insn(block, Insn::GuardNoBitsSet { val: flags, mask: Const::CUInt64(VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM.into()), mask_name: Some(ID!(VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM)), reason: SideExitReason::BlockParamProxyModified, state: exit_id });
let block_handler = fun.push_insn(block, Insn::LoadField { recv: ep, id: ID!(_env_data_index_specval), offset: SIZEOF_VALUE_I32 * VM_ENV_DATA_INDEX_SPECVAL, return_type: types::CInt64 });
@@ -6812,7 +6814,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
const _: () = assert!(RUBY_SYMBOL_FLAG & 1 == 0, "guard below rejects symbol block handlers");
// Bail out if the block handler is neither ISEQ nor ifunc
- fun.push_insn(block, Insn::GuardAnyBitSet { val: block_handler, mask: Const::CUInt64(0x1), reason: SideExitReason::BlockParamProxyNotIseqOrIfunc, state: exit_id });
+ fun.push_insn(block, Insn::GuardAnyBitSet { val: block_handler, mask: Const::CUInt64(0x1), mask_name: None, reason: SideExitReason::BlockParamProxyNotIseqOrIfunc, state: exit_id });
// TODO(Shopify/ruby#753): GC root, so we should be able to avoid unnecessary GC tracing
state.stack_push(fun.push_insn(block, Insn::Const { val: Const::Value(unsafe { rb_block_param_proxy }) }));
}
diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs
index e80175c679..d0d2e19078 100644
--- a/zjit/src/hir/opt_tests.rs
+++ b/zjit/src/hir/opt_tests.rs
@@ -3888,7 +3888,7 @@ mod hir_opt_tests {
bb2(v8:BasicObject, v9:BasicObject):
v14:CPtr = GetEP 0
v15:CInt64 = LoadField v14, :_env_data_index_flags@0x1000
- v16:CInt64 = GuardNoBitsSet v15, CUInt64(512)
+ v16:CInt64 = GuardNoBitsSet v15, VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM=CUInt64(512)
v17:CInt64 = LoadField v14, :_env_data_index_specval@0x1001
v18:CInt64 = GuardAnyBitSet v17, CUInt64(1)
v19:HeapObject[BlockParamProxy] = Const Value(VALUE(0x1008))
@@ -6560,7 +6560,7 @@ mod hir_opt_tests {
v13:ArrayExact = NewArray
v15:CPtr = GetEP 0
v16:CInt64 = LoadField v15, :_env_data_index_flags@0x1000
- v17:CInt64 = GuardNoBitsSet v16, CUInt64(512)
+ v17:CInt64 = GuardNoBitsSet v16, VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM=CUInt64(512)
v18:CInt64 = LoadField v15, :_env_data_index_specval@0x1001
v19:CInt64 = GuardAnyBitSet v18, CUInt64(1)
v20:HeapObject[BlockParamProxy] = Const Value(VALUE(0x1008))
@@ -6591,7 +6591,7 @@ mod hir_opt_tests {
v13:ArrayExact = NewArray
v15:CPtr = GetEP 0
v16:CInt64 = LoadField v15, :_env_data_index_flags@0x1000
- v17:CInt64 = GuardNoBitsSet v16, CUInt64(512)
+ v17:CInt64 = GuardNoBitsSet v16, VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM=CUInt64(512)
v18:CInt64 = LoadField v15, :_env_data_index_specval@0x1001
v19:CInt64[0] = GuardBitEquals v18, CInt64(0)
v20:NilClass = Const Value(nil)
@@ -6625,7 +6625,7 @@ mod hir_opt_tests {
v10:ArrayExact = NewArray
v12:CPtr = GetEP 1
v13:CInt64 = LoadField v12, :_env_data_index_flags@0x1000
- v14:CInt64 = GuardNoBitsSet v13, CUInt64(512)
+ v14:CInt64 = GuardNoBitsSet v13, VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM=CUInt64(512)
v15:CInt64 = LoadField v12, :_env_data_index_specval@0x1001
v16:CInt64 = GuardAnyBitSet v15, CUInt64(1)
v17:HeapObject[BlockParamProxy] = Const Value(VALUE(0x1008))
@@ -6987,7 +6987,7 @@ mod hir_opt_tests {
PatchPoint MethodRedefined(C@0x1000, foo=@0x1008, cme:0x1010)
v29:HeapObject[class_exact:C] = GuardType v11, HeapObject[class_exact:C]
v30:CUInt64 = LoadField v29, :_rbasic_flags@0x1038
- v31:CUInt64 = GuardNoBitsSet v30, CUInt64(2048)
+ v31:CUInt64 = GuardNoBitsSet v30, RUBY_FL_FREEZE=CUInt64(2048)
StoreField v29, :foo=@0x1039, v12
WriteBarrier v29, v12
CheckInterrupts
@@ -7020,7 +7020,7 @@ mod hir_opt_tests {
PatchPoint MethodRedefined(C@0x1000, foo=@0x1008, cme:0x1010)
v29:HeapObject[class_exact:C] = GuardType v11, HeapObject[class_exact:C]
v30:CUInt64 = LoadField v29, :_rbasic_flags@0x1038
- v31:CUInt64 = GuardNoBitsSet v30, CUInt64(2048)
+ v31:CUInt64 = GuardNoBitsSet v30, RUBY_FL_FREEZE=CUInt64(2048)
v32:CPtr = LoadField v29, :_as_heap@0x1039
StoreField v32, :foo=@0x103a, v12
WriteBarrier v29, v12
@@ -7641,9 +7641,9 @@ mod hir_opt_tests {
PatchPoint MethodRedefined(Array@0x1000, []=@0x1008, cme:0x1010)
v31:ArrayExact = GuardType v9, ArrayExact
v32:CUInt64 = LoadField v31, :_rbasic_flags@0x1038
- v33:CUInt64 = GuardNoBitsSet v32, CUInt64(2048)
+ v33:CUInt64 = GuardNoBitsSet v32, RUBY_FL_FREEZE=CUInt64(2048)
v34:CUInt64 = LoadField v31, :_rbasic_flags@0x1038
- v35:CUInt64 = GuardNoBitsSet v34, CUInt64(4096)
+ v35:CUInt64 = GuardNoBitsSet v34, RUBY_ELTS_SHARED=CUInt64(4096)
v36:CInt64[1] = UnboxFixnum v16
v37:CInt64 = ArrayLength v31
v38:CInt64[1] = GuardLess v36, v37
@@ -7683,9 +7683,9 @@ mod hir_opt_tests {
v35:ArrayExact = GuardType v13, ArrayExact
v36:Fixnum = GuardType v14, Fixnum
v37:CUInt64 = LoadField v35, :_rbasic_flags@0x1038
- v38:CUInt64 = GuardNoBitsSet v37, CUInt64(2048)
+ v38:CUInt64 = GuardNoBitsSet v37, RUBY_FL_FREEZE=CUInt64(2048)
v39:CUInt64 = LoadField v35, :_rbasic_flags@0x1038
- v40:CUInt64 = GuardNoBitsSet v39, CUInt64(4096)
+ v40:CUInt64 = GuardNoBitsSet v39, RUBY_ELTS_SHARED=CUInt64(4096)
v41:CInt64 = UnboxFixnum v36
v42:CInt64 = ArrayLength v35
v43:CInt64 = GuardLess v41, v42
@@ -8011,7 +8011,7 @@ mod hir_opt_tests {
v36:CInt64[0] = Const CInt64(0)
v37:CInt64 = GuardGreaterEq v35, v36
v38:CUInt64 = LoadField v30, :_rbasic_flags@0x1039
- v39:CUInt64 = GuardNoBitsSet v38, CUInt64(2048)
+ v39:CUInt64 = GuardNoBitsSet v38, RUBY_FL_FREEZE=CUInt64(2048)
v40:Fixnum = StringSetbyteFixnum v30, v31, v32
IncrCounter inline_cfunc_optimized_send_count
CheckInterrupts
@@ -8053,7 +8053,7 @@ mod hir_opt_tests {
v36:CInt64[0] = Const CInt64(0)
v37:CInt64 = GuardGreaterEq v35, v36
v38:CUInt64 = LoadField v30, :_rbasic_flags@0x1039
- v39:CUInt64 = GuardNoBitsSet v38, CUInt64(2048)
+ v39:CUInt64 = GuardNoBitsSet v38, RUBY_FL_FREEZE=CUInt64(2048)
v40:Fixnum = StringSetbyteFixnum v30, v31, v32
IncrCounter inline_cfunc_optimized_send_count
CheckInterrupts
diff --git a/zjit/src/hir/tests.rs b/zjit/src/hir/tests.rs
index 17b156a663..fab8c6e262 100644
--- a/zjit/src/hir/tests.rs
+++ b/zjit/src/hir/tests.rs
@@ -2058,7 +2058,7 @@ pub mod hir_build_tests {
PatchPoint NoEPEscape(test)
v33:CPtr = GetEP 0
v34:CInt64 = LoadField v33, :_env_data_index_flags@0x1000
- v35:CInt64 = GuardNoBitsSet v34, CUInt64(512)
+ v35:CInt64 = GuardNoBitsSet v34, VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM=CUInt64(512)
v36:CInt64 = LoadField v33, :_env_data_index_specval@0x1001
v37:CInt64 = GuardAnyBitSet v36, CUInt64(1)
v38:HeapObject[BlockParamProxy] = Const Value(VALUE(0x1008))
@@ -3435,7 +3435,7 @@ pub mod hir_build_tests {
PatchPoint NoEPEscape(open)
v31:CPtr = GetEP 0
v32:CInt64 = LoadField v31, :_env_data_index_flags@0x1000
- v33:CInt64 = GuardNoBitsSet v32, CUInt64(512)
+ v33:CInt64 = GuardNoBitsSet v32, VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM=CUInt64(512)
v34:CInt64 = LoadField v31, :_env_data_index_specval@0x1001
v35:CInt64 = GuardAnyBitSet v34, CUInt64(1)
v36:HeapObject[BlockParamProxy] = Const Value(VALUE(0x1008))