From b84d3511b4070503056c052e0ff77766d23d2e3e Mon Sep 17 00:00:00 2001 From: FUJI Goro Date: Wed, 26 Aug 2026 22:37:08 +0900 Subject: [PATCH] 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 #14210 Assisted-by: Claude Code:claude-opus-5 --- cranelift/codegen/src/alias_analysis.rs | 19 ++++++++++++++----- tests/disas/gc/array-copy-with-fuel.wat | 2 -- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/cranelift/codegen/src/alias_analysis.rs b/cranelift/codegen/src/alias_analysis.rs index f07ab3216230..eaf433e2e254 100644 --- a/cranelift/codegen/src/alias_analysis.rs +++ b/cranelift/codegen/src/alias_analysis.rs @@ -86,7 +86,8 @@ use crate::{ post_dominator_tree::PostDominatorTree, trace, }; -use core::cmp::Ordering; +use alloc::collections::BinaryHeap; +use core::cmp::{Ordering, Reverse}; use cranelift_entity::{EntityRef, SecondaryMap, packed_option::PackedOption}; /// Determine whether this opcode behaves as a memory fence, i.e., @@ -708,13 +709,21 @@ impl<'a> AliasAnalysis<'a> { } fn compute_block_input_states(&mut self, func: &Function) { - let mut queue = vec![]; + // Drain the worklist in reverse postorder: a LIFO worklist redoes the + // whole tail of the function each time it comes back to the other side + // of a branch, which is quadratic for a long chain of diamonds. + let mut rpo_index = SecondaryMap::with_default(usize::MAX); + for (i, block) in self.domtree.cfg_rpo().enumerate() { + rpo_index[*block] = i; + } + + let mut queue = BinaryHeap::new(); let mut queue_set = FxHashSet::default(); let entry = func.layout.entry_block().unwrap(); - queue.push(entry); + queue.push(Reverse((rpo_index[entry], entry))); queue_set.insert(entry); - while let Some(block) = queue.pop() { + while let Some(Reverse((_, block))) = queue.pop() { queue_set.remove(&block); let mut state = self .block_input @@ -747,7 +756,7 @@ impl<'a> AliasAnalysis<'a> { }; if updated && queue_set.insert(succ) { - queue.push(succ); + queue.push(Reverse((rpo_index[succ], succ))); } }); } diff --git a/tests/disas/gc/array-copy-with-fuel.wat b/tests/disas/gc/array-copy-with-fuel.wat index 8371c0eed235..1225d180c197 100644 --- a/tests/disas/gc/array-copy-with-fuel.wat +++ b/tests/disas/gc/array-copy-with-fuel.wat @@ -107,8 +107,6 @@ ;; @002b brif.i32 v6, block6, block9 ;; ;; block6: -;; v143 = load.i32 notrap aligned region6 v162 -;; v145 = load.i32 notrap aligned region7 v163 ;; @002b v102 = icmp.i64 ult v58, v82 ;; @002b v107 = iadd.i64 v58, v170 ;; @002b v108 = iadd.i64 v82, v170