Repository: bytecodealliance/wasmtime
Pull request #8238 (2024-03-26 00:15:55)

cranelift: Add DFG helper for resolving all value aliases
And demonstrate its use in bugpoint. That doesn't have much impact on anything, which I hope makes this easier to review than changing more important things right away.

There are other places we would probably be better off doing a whole-function rewrite once rather than calling `resolve_aliases` a bunch of times, such as the disas tests, the egraph pass, and lowering. I have not changed those in this PR.


Patch:
diff --git a/cranelift/codegen/src/ir/dfg.rs b/cranelift/codegen/src/ir/dfg.rs
index b7c8e161a..2ac1152ce 100644
--- a/cranelift/codegen/src/ir/dfg.rs
+++ b/cranelift/codegen/src/ir/dfg.rs
@@ -378,6 +378,65 @@ impl DataFlowGraph {
         resolve_aliases(&self.values, value)
     }
 
+    /// Replace all uses of value aliases with their resolved values, and delete
+    /// the aliases.
+    pub fn resolve_all_aliases(&mut self) {
+        let invalid_value = ValueDataPacked::from(ValueData::Alias {
+            ty: types::INVALID,
+            original: Value::reserved_value(),
+        });
+
+        // Rewrite each chain of aliases. Update every alias along the chain
+        // into an alias directly to the final value. Due to updating every
+        // alias that it looks at, this loop runs in time linear in the number
+        // of values.
+        for mut src in self.values.keys() {
+            let value_data = self.values[src];
+            if value_data == invalid_value {
+                continue;
+            }
+            if let ValueData::Alias { mut original, .. } = value_data.into() {
+                // We don't use the type after this, we just need some place to
+                // store the resolved aliases temporarily.
+                let resolved = ValueDataPacked::from(ValueData::Alias {
+                    ty: types::INVALID,
+                    original: resolve_aliases(&self.values, original),
+                });
+                // Walk the chain again, splatting the new alias everywhere.
+                // resolve_aliases panics if there's an alias cycle, so we don't
+                // need to guard against cycles here.
+                loop {
+                    self.values[src] = resolved;
+                    src = original;
+                    if let ValueData::Alias { original: next, .. } = self.values[src].into() {
+                        original = next;
+                    } else {
+                        break;
+                    }
+                }
+            }
+        }
+
+        // Now aliases don't point to other aliases, so we can replace any use
+        // of an alias with the final value in constant time.
+        for inst in self.insts.0.values_mut() {
+            inst.map_values(&mut self.value_lists, &mut self.jump_tables, |arg| {
+                if let ValueData::Alias { original, .. } = self.values[arg].into() {
+                    original
+                } else {
+                    arg
+                }
+            });
+        }
+
+        // Delete all aliases now that there are no uses left.
+        for value in self.values.values_mut() {
+            if let ValueData::Alias { .. } = ValueData::from(*value) {
+                *value = invalid_value;
+            }
+        }
+    }
+
     /// Resolve all aliases among inst's arguments.
     ///
     /// For each argument of inst which is defined by an alias, replace the
diff --git a/cranelift/src/bugpoint.rs b/cranelift/src/bugpoint.rs
index 63fa16f3e..862664b11 100644
--- a/cranelift/src/bugpoint.rs
+++ b/cranelift/src/bugpoint.rs
@@ -822,18 +822,10 @@ fn inst_count(func: &Function) -> usize {
         .sum()
 }
 
-fn resolve_aliases(func: &mut Function) {
-    for block in func.stencil.layout.blocks() {
-        for inst in func.stencil.layout.block_insts(block) {
-            func.stencil.dfg.resolve_aliases_in_arguments(inst);
-        }
-    }
-}
-
 /// Resolve aliases only if function still crashes after this.
 fn try_resolve_aliases(context: &mut CrashCheckContext, func: &mut Function) {
     let mut func_with_resolved_aliases = func.clone();
-    resolve_aliases(&mut func_with_resolved_aliases);
+    func_with_resolved_aliases.dfg.resolve_all_aliases();
     if let CheckResult::Crash(_) = context.check_for_crash(&func_with_resolved_aliases) {
         *func = func_with_resolved_aliases;
     }


Test patch:
diff --git a/cranelift/tests/bugpoint_test_expected.clif b/cranelift/tests/bugpoint_test_expected.clif
index 982fcb001..cc83b54bf 100644
--- a/cranelift/tests/bugpoint_test_expected.clif
+++ b/cranelift/tests/bugpoint_test_expected.clif
@@ -6,30 +6,11 @@ block0:
     v0 = iconst.i64 0
     v105 = iconst.i64 0
     v829 = iconst.i64 0
-    v935 -> v829
-    v962 -> v829
-    v992 -> v829
-    v1036 -> v829
-    v1049 -> v829
     v842 = iconst.i64 0
-    v976 -> v842
-    v989 -> v842
-    v1038 -> v842
-    v1061 -> v842
     v883 = iconst.i64 0
-    v934 -> v883
-    v961 -> v883
-    v991 -> v883
-    v1005 -> v883
-    v1048 -> v883
     v951 = iconst.i64 0
-    v988 -> v951
     v987 = iconst.i64 0
     v1052 = iconst.i16 0
-    v960 -> v1052
-    v990 -> v1052
-    v1051 -> v1052
-    v1055 -> v1052
     call fn0(v0, v105, v1052, v883, v829, v987, v951, v842)  ; v0 = 0, v105 = 0, v1052 = 0, v883 = 0, v829 = 0, v987 = 0, v951 = 0, v842 = 0
     trap user0
 }