Browse Source

egraph: Resolve all aliases at once (#8240)

* egraph: Resolve all aliases at once

This way we can use the linear-time alias rewriting pass, and then avoid
having to think about value aliases ever again.

* Resolve aliases in facts and values_labels

When resolving aliases in values_labels, this discards debug info on
values which are replaced by aliases. However, that is equivalent to the
existing behavior in `Lower::get_value_labels`, which resolves value
aliases first and only then looks for attached debug info.
pull/8264/head
Jamey Sharp 7 months ago
committed by GitHub
parent
commit
b338f921c0
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 5
      cranelift/codegen/src/egraph.rs
  2. 3
      cranelift/codegen/src/egraph/elaborate.rs
  3. 63
      cranelift/codegen/src/ir/dfg.rs
  4. 3
      cranelift/codegen/src/opts.rs
  5. 1
      cranelift/filetests/filetests/egraph/licm.clif

5
cranelift/codegen/src/egraph.rs

@ -566,6 +566,10 @@ impl<'a> EgraphPass<'a> {
/// only refer to its subset that exists at this stage, to
/// maintain acyclicity.)
fn remove_pure_and_optimize(&mut self) {
// This pass relies on every value having a unique name, so first
// eliminate any value aliases.
self.func.dfg.resolve_all_aliases();
let mut cursor = FuncCursor::new(self.func);
let mut value_to_opt_value: SecondaryMap<Value, Value> =
SecondaryMap::with_default(Value::reserved_value());
@ -661,7 +665,6 @@ impl<'a> EgraphPass<'a> {
// Rewrite args of *all* instructions using the
// value-to-opt-value map.
cursor.func.dfg.resolve_aliases_in_arguments(inst);
cursor.func.dfg.map_inst_values(inst, |arg| {
let new_value = value_to_opt_value[arg];
trace!("rewriting arg {} of inst {} to {}", arg, inst, new_value);

3
cranelift/codegen/src/egraph/elaborate.rs

@ -416,8 +416,7 @@ impl<'a> Elaborator<'a> {
while let Some(entry) = self.elab_stack.pop() {
match entry {
ElabStackEntry::Start { value, before } => {
debug_assert_ne!(value, Value::reserved_value());
let value = self.func.dfg.resolve_aliases(value);
debug_assert!(self.func.dfg.value_is_real(value));
self.stats.elaborate_visit_node += 1;

63
cranelift/codegen/src/ir/dfg.rs

@ -333,6 +333,13 @@ impl DataFlowGraph {
self.values.is_valid(v)
}
/// Check whether a value is valid and not an alias.
pub fn value_is_real(&self, value: Value) -> bool {
// Deleted or unused values are also stored as aliases so this excludes
// those as well.
self.value_is_valid(value) && !matches!(self.values[value].into(), ValueData::Alias { .. })
}
/// Get the type of a value.
pub fn value_type(&self, v: Value) -> Type {
self.values[v].ty()
@ -419,6 +426,8 @@ impl DataFlowGraph {
// Now aliases don't point to other aliases, so we can replace any use
// of an alias with the final value in constant time.
// Rewrite InstructionData in `self.insts`.
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() {
@ -429,6 +438,50 @@ impl DataFlowGraph {
});
}
// - `results` and block-params in `blocks` are not aliases, by
// definition.
// - `dynamic_types` has no values.
// - `value_lists` can only be accessed via references from elsewhere.
// - `values` only has value references in aliases (which we've
// removed), and unions (but the egraph pass ensures there are no
// aliases before creating unions).
// Merge `facts` from any alias onto the aliased value. Note that if
// there was a chain of aliases, at this point every alias that was in
// the chain points to the same final value, so their facts will all be
// merged together.
for value in self.facts.keys() {
if let ValueData::Alias { original, .. } = self.values[value].into() {
if let Some(new_fact) = self.facts[value].take() {
match &mut self.facts[original] {
Some(old_fact) => *old_fact = Fact::intersect(old_fact, &new_fact),
old_fact => *old_fact = Some(new_fact),
}
}
}
}
// - `signatures`, `old_signatures`, and `ext_funcs` have no values.
if let Some(values_labels) = &mut self.values_labels {
// Debug info is best-effort. If any is attached to value aliases,
// just discard it.
values_labels.retain(|&k, _| !matches!(self.values[k].into(), ValueData::Alias { .. }));
// If debug-info says a value should have the same labels as another
// value, then make sure that target is not a value alias.
for value_label in values_labels.values_mut() {
if let ValueLabelAssignments::Alias { value, .. } = value_label {
if let ValueData::Alias { original, .. } = self.values[*value].into() {
*value = original;
}
}
}
}
// - `constants` and `immediates` have no values.
// - `jump_tables` is updated together with instruction-data above.
// Delete all aliases now that there are no uses left.
for value in self.values.values_mut() {
if let ValueData::Alias { .. } = ValueData::from(*value) {
@ -437,16 +490,6 @@ impl DataFlowGraph {
}
}
/// Resolve all aliases among inst's arguments.
///
/// For each argument of inst which is defined by an alias, replace the
/// alias with the aliased value.
pub fn resolve_aliases_in_arguments(&mut self, inst: Inst) {
self.insts[inst].map_values(&mut self.value_lists, &mut self.jump_tables, |arg| {
resolve_aliases(&self.values, arg)
});
}
/// Turn a value into an alias of another.
///
/// Change the `dest` value to behave as an alias of `src`. This means that all uses of `dest`

3
cranelift/codegen/src/opts.rs

@ -83,8 +83,7 @@ where
fn next(&mut self, ctx: &mut IsleContext<'a, 'b, 'c>) -> Option<Self::Output> {
while let Some(value) = self.stack.pop() {
debug_assert_ne!(value, Value::reserved_value());
let value = ctx.ctx.func.dfg.resolve_aliases(value);
debug_assert!(ctx.ctx.func.dfg.value_is_real(value));
trace!("iter: value {:?}", value);
match ctx.ctx.func.dfg.value_def(value) {
ValueDef::Union(x, y) => {

1
cranelift/filetests/filetests/egraph/licm.clif

@ -58,7 +58,6 @@ block2(v8: i64x2):
; check: v9 = iconst.i32 1
; check: v10 = isub v3, v9
; check: v5 = iadd v2, v4
; check: v8 -> v5
; check: brif v10, block1(v5, v10), block2
; check: block2:
; check: return v5

Loading…
Cancel
Save