From 45cef756f5854ab7c633c7255730481d6203a422 Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Thu, 1 Oct 2026 18:37:13 -0700 Subject: [PATCH 1/2] CFG: compute dominance pre/post-numbers and support dom query in O(1). This is the same trick as in Cranelift and elsewhere, and replaces an O(|domtree depth|) iterative walk up the domtree. Important for fast validation of large function bodies. --- src/cfg/mod.rs | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/src/cfg/mod.rs b/src/cfg/mod.rs index 0a5d0f1..7a8e97d 100644 --- a/src/cfg/mod.rs +++ b/src/cfg/mod.rs @@ -34,6 +34,13 @@ pub struct CFGInfo { pub preds: PerEntity>, /// A given block's position in each predecessor's successor list. pub pred_pos: PerEntity>, + /// Pre- and post-order numbers of each reachable block in a DFS of + /// the dominator tree (`u32::MAX` if unreachable). + /// + /// This permits an O(1) dominance query: `a` dominates `b` iff + /// `b`'s interval nests in `a`'s interval. + dom_pre: PerEntity, + dom_post: PerEntity, } #[derive(Clone, Debug, Default)] @@ -92,6 +99,33 @@ impl CFGInfo { } } + // Number the dominator tree, iteratively (it can be as deep as + // the body is long). + let mut dom_pre: PerEntity = PerEntity::default(); + let mut dom_post: PerEntity = PerEntity::default(); + for block in f.blocks.iter() { + dom_pre[block] = u32::MAX; + dom_post[block] = u32::MAX; + } + let (mut pre, mut post) = (0u32, 0u32); + // (block, next child to visit) + let mut stack: Vec<(Block, Block)> = vec![(f.entry, domtree_children[f.entry].child)]; + dom_pre[f.entry] = pre; + pre += 1; + while let Some(top) = stack.last_mut() { + let child = top.1; + if child.is_valid() { + top.1 = domtree_children[child].next; + dom_pre[child] = pre; + pre += 1; + stack.push((child, domtree_children[child].child)); + } else { + dom_post[top.0] = post; + post += 1; + stack.pop(); + } + } + let mut def_block: PerEntity = PerEntity::default(); for (block, block_def) in f.blocks.entries() { for &(_, param) in &block_def.params { @@ -128,11 +162,20 @@ impl CFGInfo { def_block, preds, pred_pos, + dom_pre, + dom_post, } } + /// Whether `a` dominates `b` (every block dominates itself; an + /// unreachable block dominates and is dominated by nothing else). pub fn dominates(&self, a: Block, b: Block) -> bool { - domtree::dominates(&self.domtree, a, b) + let r = a == b || { + let (pa, pb) = (self.dom_pre[a], self.dom_pre[b]); + pa != u32::MAX && pb != u32::MAX && pa <= pb && self.dom_post[b] <= self.dom_post[a] + }; + debug_assert_eq!(r, domtree::dominates(&self.domtree, a, b)); + r } pub fn dom_children<'a>(&'a self, block: Block) -> DomtreeChildIter<'a> { From c27296ed24546de56d1135bbde6bb7b15c212c2e Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Thu, 1 Oct 2026 18:38:26 -0700 Subject: [PATCH 2/2] Release 0.3.3. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c737843..cec721e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "waffle" -version = "0.3.2" +version = "0.3.3" description = "Wasm Analysis Framework For Lightweight Experiments" authors = ["Chris Fallin "] license = "Apache-2.0 WITH LLVM-exception"