Skip to content

Commit b84d351

Browse files
committed
cranelift: fix cubic compile time for modules with many allocating globals
The alias-analysis worklist is drained LIFO, which redoes the whole tail of the function each time it comes back to the other side of a branch, so a long chain of diamonds -- a Wasm module's initializer for N allocating globals, say -- visits each block O(N) times and walks O(N) alias regions per visit. Reverse postorder converges in a single pass instead. For N=2000, compiling goes from 67.0s to 0.22s. The pass is still quadratic in the number of alias regions; that is left for a separate change. Visit order also affects precision, hence the disas test update. Refs bytecodealliance#14210 Assisted-by: Claude Code:claude-opus-5
1 parent 0ac37df commit b84d351

2 files changed

Lines changed: 14 additions & 7 deletions

File tree

cranelift/codegen/src/alias_analysis.rs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@ use crate::{
8686
post_dominator_tree::PostDominatorTree,
8787
trace,
8888
};
89-
use core::cmp::Ordering;
89+
use alloc::collections::BinaryHeap;
90+
use core::cmp::{Ordering, Reverse};
9091
use cranelift_entity::{EntityRef, SecondaryMap, packed_option::PackedOption};
9192

9293
/// Determine whether this opcode behaves as a memory fence, i.e.,
@@ -708,13 +709,21 @@ impl<'a> AliasAnalysis<'a> {
708709
}
709710

710711
fn compute_block_input_states(&mut self, func: &Function) {
711-
let mut queue = vec![];
712+
// Drain the worklist in reverse postorder: a LIFO worklist redoes the
713+
// whole tail of the function each time it comes back to the other side
714+
// of a branch, which is quadratic for a long chain of diamonds.
715+
let mut rpo_index = SecondaryMap::with_default(usize::MAX);
716+
for (i, block) in self.domtree.cfg_rpo().enumerate() {
717+
rpo_index[*block] = i;
718+
}
719+
720+
let mut queue = BinaryHeap::new();
712721
let mut queue_set = FxHashSet::default();
713722
let entry = func.layout.entry_block().unwrap();
714-
queue.push(entry);
723+
queue.push(Reverse((rpo_index[entry], entry)));
715724
queue_set.insert(entry);
716725

717-
while let Some(block) = queue.pop() {
726+
while let Some(Reverse((_, block))) = queue.pop() {
718727
queue_set.remove(&block);
719728
let mut state = self
720729
.block_input
@@ -747,7 +756,7 @@ impl<'a> AliasAnalysis<'a> {
747756
};
748757

749758
if updated && queue_set.insert(succ) {
750-
queue.push(succ);
759+
queue.push(Reverse((rpo_index[succ], succ)));
751760
}
752761
});
753762
}

tests/disas/gc/array-copy-with-fuel.wat

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,6 @@
107107
;; @002b brif.i32 v6, block6, block9
108108
;;
109109
;; block6:
110-
;; v143 = load.i32 notrap aligned region6 v162
111-
;; v145 = load.i32 notrap aligned region7 v163
112110
;; @002b v102 = icmp.i64 ult v58, v82
113111
;; @002b v107 = iadd.i64 v58, v170
114112
;; @002b v108 = iadd.i64 v82, v170

0 commit comments

Comments
 (0)