Skip to content

Commit e17b51f

Browse files
committed
ZJIT: globally remove duplicate CheckInterrupts
``` ruby-master-zjit: ruby 4.1.0dev (2026-09-16T16:27:13Z master 55562be) +ZJIT +PRISM [arm64-darwin25] ruby-dev-zjit: ruby 4.1.0dev (2026-09-16T16:49:19Z hm-ukpqtuorkpnknmos a6968e9c42) +ZJIT +PRISM [arm64-darwin25] ---------- --------------------- ------------------ --------------------- ------------------------------ bench ruby-master-zjit (ms) ruby-dev-zjit (ms) ruby-dev-zjit 1st itr ruby-master-zjit/ruby-dev-zjit 30k_ifelse 44.0 ± 1.8% 42.2 ± 1.8% 1.038 1.044 ---------- --------------------- ------------------ --------------------- ------------------------------ ```
1 parent 55562be commit e17b51f

2 files changed

Lines changed: 35 additions & 17 deletions

File tree

‎zjit/src/hir.rs‎

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1846,8 +1846,8 @@ impl Insn {
18461846
Insn::IsBlockParamModified { .. } => effects::Empty,
18471847
Insn::GetBlockParam { .. } => effects::Any,
18481848
Insn::Snapshot { .. } => effects::Empty,
1849-
Insn::Jump(_) => effects::Any,
1850-
Insn::CondBranch { .. } => effects::Any,
1849+
Insn::Jump(_) => effects::Control,
1850+
Insn::CondBranch { .. } => effects::Control,
18511851
Insn::CCall { elidable, .. } => {
18521852
if *elidable {
18531853
Effect::write(abstract_heaps::Allocator)
@@ -7126,26 +7126,51 @@ impl Function {
71267126
}
71277127
}
71287128

7129-
/// Remove duplicate CheckInterrupts instructions within each basic block.
7130-
/// Only the last CheckInterrupts in a block is needed unless an intervening
7131-
/// instruction writes to InterruptFlag (e.g. a call), which resets tracking.
7129+
/// Remove duplicate CheckInterrupts instructions globally.
7130+
///
7131+
/// Only the last CheckInterrupts in a block is needed unless an intervening instruction writes
7132+
/// to InterruptFlag (e.g. a call), which resets tracking. Checking as late as possible keeps
7133+
/// fewer values live in the check's frame state, which lets other passes elide more.
7134+
///
7135+
/// This tracking also propagates across blocks: a check is redundant if a later check is
7136+
/// guaranteed to run on every path forward before any InterruptFlag write or function exit.
7137+
/// That makes this a backward must-analysis. Blocks are visited so that every forward
7138+
/// successor is processed first (the reverse of reverse post-order). A back-edge successor has
7139+
/// not been processed when its source is visited, so its default `false` contribution keeps
7140+
/// the source block's own check alive and we never rely on a check from a later iteration.
71327141
fn remove_duplicate_check_interrupts(&mut self) {
7133-
for block_id in self.reverse_post_order() {
7134-
let mut seen = false;
7142+
let cfi = ControlFlowInfo::new(self);
7143+
7144+
// Whether a check is guaranteed to run on every path leaving each block, before any
7145+
// InterruptFlag write or function exit. `false` is the conservative default for
7146+
// unprocessed (back-edge) successors.
7147+
let mut checked = vec![false; self.blocks.len()];
7148+
7149+
for &block_id in cfi.reverse_post_order().iter().rev() {
7150+
// Must-analysis: require a check on every outgoing path. A block with no successors
7151+
// (a function exit) provides no such guarantee.
7152+
let successors = cfi.successors(block_id);
7153+
let mut block_checked = !successors.is_empty()
7154+
&& successors.iter().all(|&succ| checked[succ]);
7155+
71357156
let insns = std::mem::take(&mut self.blocks[block_id].insns);
71367157
let mut new_insns = Vec::with_capacity(insns.len());
7158+
71377159
for insn_id in insns.into_iter().rev() {
71387160
let insn = &self.insns[insn_id];
71397161
if matches!(insn, Insn::CheckInterrupts { .. }) {
7140-
if seen { continue; }
7141-
seen = true;
7162+
if block_checked { continue; }
7163+
block_checked = true;
71427164
} else if insn.effects_of().write_bits().overlaps(abstract_heaps::InterruptFlag) {
7143-
seen = false;
7165+
block_checked = false;
71447166
}
71457167
new_insns.push(insn_id);
71467168
}
7169+
71477170
new_insns.reverse();
71487171
self.blocks[block_id].insns = new_insns;
7172+
7173+
checked[block_id] = block_checked;
71497174
}
71507175
}
71517176

‎zjit/src/hir/opt_tests.rs‎

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11519,11 +11519,9 @@ mod hir_opt_tests {
1151911519
Jump bb13(v54, v27)
1152011520
bb13(v44:BasicObject, v45:BasicObject):
1152111521
v57:BasicObject = Send v44, :call # SendFallbackReason: Send: no profile data available
11522-
CheckInterrupts
1152311522
Jump bb4(v57)
1152411523
bb6():
1152511524
v66:Fixnum[42] = Const Value(42)
11526-
CheckInterrupts
1152711525
Jump bb4(v66)
1152811526
bb4(v72:BasicObject):
1152911527
PopInlineFrame
@@ -15185,7 +15183,6 @@ mod hir_opt_tests {
1518515183
v48:BasicObject = ArrayAref v19, v47
1518615184
v49:ArrayExact = NewArray v48
1518715185
PushInlineFrame :target, v25 (0x1040), num_args=1
15188-
CheckInterrupts
1518915186
PopInlineFrame
1519015187
Jump bb4(v49)
1519115188
bb6():
@@ -15203,7 +15200,6 @@ mod hir_opt_tests {
1520315200
v59:BasicObject = ArrayAref v19, v58
1520415201
v60:ArrayExact = NewArray v59
1520515202
PushInlineFrame :target, v31 (0x1090), num_args=1
15206-
CheckInterrupts
1520715203
PopInlineFrame
1520815204
Jump bb4(v60)
1520915205
bb8():
@@ -21560,10 +21556,8 @@ mod hir_opt_tests {
2156021556
CondBranch v37, bb7(), bb6()
2156121557
bb7():
2156221558
v42:Fixnum[0] = Const Value(0)
21563-
CheckInterrupts
2156421559
Jump bb4(v42)
2156521560
bb6():
21566-
CheckInterrupts
2156721561
Jump bb4(v62)
2156821562
bb4(v56:Fixnum):
2156921563
PopInlineFrame
@@ -22900,7 +22894,6 @@ mod hir_opt_tests {
2290022894
StoreField v95, :@y@0x108c, v49
2290122895
v176:CShape[0x108d] = Const CShape(0x108d)
2290222896
StoreField v95, :shape_id@0x1088, v176
22903-
CheckInterrupts
2290422897
PopInlineFrame
2290522898
PatchPoint MethodRedefined(Point@0x1008, ==@0x1098, cme:0x10a0)
2290622899
PushInlineFrame :==, v85 (0x10c8), num_args=1

0 commit comments

Comments
 (0)