Skip to content

Commit 2f338c5

Browse files
committed
ZJIT: globally remove duplicate CheckInterrupts
This also required updating the effects of Jump and CondBranch, since `effects::Any` includes `InterruptFlag` (meaning the optimization would never actually work across blocks). ``` ruby-master-zjit: ruby 4.1.0dev (2026-09-17T20:29:33Z master aca98ee) +ZJIT +PRISM [arm64-darwin25] ruby-dev-zjit: ruby 4.1.0dev (2026-09-17T21:09:40Z hm-ukpqtuorkpnknmos 203f9cdd52) +ZJIT +PRISM [arm64-darwin25] last_commit=ZJIT: globally remove duplicate CheckInterrupts ---------- --------------------- ------------------ --------------------- ------------------------------ bench ruby-master-zjit (ms) ruby-dev-zjit (ms) ruby-dev-zjit 1st itr ruby-master-zjit/ruby-dev-zjit 30k_ifelse 44.7 ± 2.8% 42.5 ± 2.7% 1.045 1.050 ---------- --------------------- ------------------ --------------------- ------------------------------ ```
1 parent aca98ee commit 2f338c5

2 files changed

Lines changed: 58 additions & 26 deletions

File tree

‎zjit/src/hir.rs‎

Lines changed: 58 additions & 19 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,65 @@ 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.
71327137
fn remove_duplicate_check_interrupts(&mut self) {
7133-
for block_id in self.reverse_post_order() {
7134-
let mut seen = false;
7135-
let insns = std::mem::take(&mut self.blocks[block_id].insns);
7136-
let mut new_insns = Vec::with_capacity(insns.len());
7137-
for insn_id in insns.into_iter().rev() {
7138-
let insn = &self.insns[insn_id];
7139-
if matches!(insn, Insn::CheckInterrupts { .. }) {
7140-
if seen { continue; }
7141-
seen = true;
7142-
} else if insn.effects_of().write_bits().overlaps(abstract_heaps::InterruptFlag) {
7143-
seen = false;
7138+
let cfi = ControlFlowInfo::new(self);
7139+
let rpo = cfi.reverse_post_order();
7140+
7141+
let rpo_order = {
7142+
let mut order = vec![usize::MAX; self.blocks.len()];
7143+
for (i, &block_id) in rpo.iter().enumerate() {
7144+
order[block_id] = i;
7145+
}
7146+
order
7147+
};
7148+
7149+
// Whether the block has already CheckInterrupts, defaults to false and only becomes true
7150+
// if we can prove it
7151+
let mut checked = vec![false; self.blocks.len()];
7152+
7153+
let mut changed = true;
7154+
while changed {
7155+
changed = false;
7156+
7157+
for &block_id in rpo.iter().rev() {
7158+
let successors = cfi.successors(block_id);
7159+
7160+
let mut block_checked = !successors.is_empty()
7161+
&& successors.iter().all(|&succ| checked[succ] && !(rpo_order[succ] < rpo_order[block_id]));
7162+
7163+
let insns = std::mem::take(&mut self.blocks[block_id].insns);
7164+
let mut new_insns = Vec::with_capacity(insns.len());
7165+
7166+
for insn_id in insns.into_iter().rev() {
7167+
let insn = &self.insns[insn_id];
7168+
if matches!(insn, Insn::CheckInterrupts { .. }) {
7169+
if block_checked {
7170+
changed = true;
7171+
continue;
7172+
}
7173+
block_checked = true;
7174+
} else if insn.effects_of().write_bits().overlaps(abstract_heaps::InterruptFlag) {
7175+
block_checked = false;
7176+
}
7177+
new_insns.push(insn_id);
7178+
}
7179+
7180+
new_insns.reverse();
7181+
self.blocks[block_id].insns = new_insns;
7182+
7183+
if checked[block_id] != block_checked {
7184+
changed = true;
7185+
checked[block_id] = block_checked;
71447186
}
7145-
new_insns.push(insn_id);
71467187
}
7147-
new_insns.reverse();
7148-
self.blocks[block_id].insns = new_insns;
71497188
}
71507189
}
71517190

‎zjit/src/hir/opt_tests.rs‎

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11580,11 +11580,9 @@ mod hir_opt_tests {
1158011580
Jump bb13(v54, v27)
1158111581
bb13(v44:BasicObject, v45:BasicObject):
1158211582
v57:BasicObject = Send v44, :call # SendFallbackReason: Send: no profile data available
11583-
CheckInterrupts
1158411583
Jump bb4(v57)
1158511584
bb6():
1158611585
v66:Fixnum[42] = Const Value(42)
11587-
CheckInterrupts
1158811586
Jump bb4(v66)
1158911587
bb4(v72:BasicObject):
1159011588
PopInlineFrame
@@ -15246,7 +15244,6 @@ mod hir_opt_tests {
1524615244
v48:BasicObject = ArrayAref v19, v47
1524715245
v49:ArrayExact = NewArray v48
1524815246
PushInlineFrame :target, v25 (0x1040), num_args=1
15249-
CheckInterrupts
1525015247
PopInlineFrame
1525115248
Jump bb4(v49)
1525215249
bb6():
@@ -15264,7 +15261,6 @@ mod hir_opt_tests {
1526415261
v59:BasicObject = ArrayAref v19, v58
1526515262
v60:ArrayExact = NewArray v59
1526615263
PushInlineFrame :target, v31 (0x1090), num_args=1
15267-
CheckInterrupts
1526815264
PopInlineFrame
1526915265
Jump bb4(v60)
1527015266
bb8():
@@ -21611,10 +21607,8 @@ mod hir_opt_tests {
2161121607
CondBranch v37, bb7(), bb6()
2161221608
bb7():
2161321609
v42:Fixnum[0] = Const Value(0)
21614-
CheckInterrupts
2161521610
Jump bb4(v42)
2161621611
bb6():
21617-
CheckInterrupts
2161821612
Jump bb4(v62)
2161921613
bb4(v56:Fixnum):
2162021614
PopInlineFrame
@@ -22950,7 +22944,6 @@ mod hir_opt_tests {
2295022944
StoreField v95, :@y@0x108c, v49
2295122945
v172:CShape[0x108d] = Const CShape(0x108d)
2295222946
StoreField v95, :shape_id@0x1088, v172
22953-
CheckInterrupts
2295422947
PopInlineFrame
2295522948
PatchPoint MethodRedefined(Point@0x1008, ==@0x1098, cme:0x10a0)
2295622949
PushInlineFrame :==, v85 (0x10c8), num_args=1

0 commit comments

Comments
 (0)