Skip to content
Snippets Groups Projects
Unverified Commit f178c13f authored by Andrew Young's avatar Andrew Young
Browse files

[mlir] Support use-def cycles in graph regions during regionDCE

When deleting operations in DCE, the algorithm uses a post-order walk of
the IR to ensure that value uses were erased before value defs. Graph
regions do not have the same structural invariants as SSA CFG, and this
post order walk could delete value defs before uses.  This problem is
guaranteed to occur when there is a cycle in the use-def graph.

This change stops DCE from visiting the operations and blocks in any
meaningful order.  Instead, we rely on explicitly dropping all uses of a
value before deleting it.

Reviewed By: mehdi_amini, rriddle

Differential Revision: https://reviews.llvm.org/D98919
parent 270a336f
No related branches found
No related tags found
No related merge requests found
...@@ -312,21 +312,18 @@ static LogicalResult deleteDeadness(MutableArrayRef<Region> regions, ...@@ -312,21 +312,18 @@ static LogicalResult deleteDeadness(MutableArrayRef<Region> regions,
if (region.empty()) if (region.empty())
continue; continue;
// We do the deletion in an order that deletes all uses before deleting // Delete every operation that is not live. Graph regions may have cycles
// defs. // in the use-def graph, so we must explicitly dropAllUses() from each
// MLIR's SSA structural invariants guarantee that except for block // operation as we erase it. Visiting the operations in post-order
// arguments, the use-def graph is acyclic, so this is possible with a // guarantees that in SSA CFG regions value uses are removed before defs,
// single walk of ops and then a final pass to clean up block arguments. // which makes dropAllUses() a no-op.
//
// To do this, we visit ops in an order that visits domtree children
// before domtree parents. A CFG post-order (with reverse iteration with a
// block) satisfies that without needing an explicit domtree calculation.
for (Block *block : llvm::post_order(&region.front())) { for (Block *block : llvm::post_order(&region.front())) {
eraseTerminatorSuccessorOperands(block->getTerminator(), liveMap); eraseTerminatorSuccessorOperands(block->getTerminator(), liveMap);
for (Operation &childOp : for (Operation &childOp :
llvm::make_early_inc_range(llvm::reverse(block->getOperations()))) { llvm::make_early_inc_range(llvm::reverse(block->getOperations()))) {
if (!liveMap.wasProvenLive(&childOp)) { if (!liveMap.wasProvenLive(&childOp)) {
erasedAnything = true; erasedAnything = true;
childOp.dropAllUses();
childOp.erase(); childOp.erase();
} else { } else {
erasedAnything |= erasedAnything |=
......
...@@ -156,3 +156,20 @@ func @f( ...@@ -156,3 +156,20 @@ func @f(
"foo.print"(%t4) : (tensor<4xf32>) -> () "foo.print"(%t4) : (tensor<4xf32>) -> ()
return return
} }
// -----
// Test case: Test values with use-def cycles are deleted properly.
// CHECK: func @f()
// CHECK-NEXT: test.graph_region
// CHECK-NEXT: "test.terminator"() : () -> ()
func @f() {
test.graph_region {
%0 = "math.exp"(%1) : (f32) -> f32
%1 = "math.exp"(%0) : (f32) -> f32
"test.terminator"() : ()->()
}
return
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment