Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion cranelift/codegen/src/egraph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down Expand Up @@ -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);
Expand Down
3 changes: 1 addition & 2 deletions cranelift/codegen/src/egraph/elaborate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
63 changes: 53 additions & 10 deletions cranelift/codegen/src/ir/dfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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() {
Expand All @@ -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) {
Expand All @@ -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`
Expand Down
3 changes: 1 addition & 2 deletions cranelift/codegen/src/opts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down
1 change: 0 additions & 1 deletion cranelift/filetests/filetests/egraph/licm.clif
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down