[MLIR] Stop visiting unreachable blocks in the walkAndApplyPatterns driver (#154038)

This is similar to the fix to the greedy driver in #153957 ; except that
instead of removing unreachable code, we just ignore it.

Operations like:

```
%add = arith.addi %add, %add : i64
```

are legal in unreachable code.
Unfortunately many patterns would be unsafe to apply on such IR and can
lead to crashes or infinite loops.
This commit is contained in:
Mehdi Amini 2025-08-18 22:46:59 +02:00 committed by GitHub
parent 624b724ca6
commit 191e7eba93
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 49 additions and 0 deletions

View File

@ -27,6 +27,8 @@ namespace mlir {
/// This is intended as the simplest and most lightweight pattern rewriter in
/// cases when a simple walk gets the job done.
///
/// The driver will skip unreachable blocks.
///
/// Note: Does not apply patterns to the given operation itself.
void walkAndApplyPatterns(Operation *op,
const FrozenRewritePatternSet &patterns,

View File

@ -27,6 +27,26 @@
namespace mlir {
// Find all reachable blocks in the region and add them to the visitedBlocks
// set.
static void findReachableBlocks(Region &region,
DenseSet<Block *> &reachableBlocks) {
Block *entryBlock = &region.front();
reachableBlocks.insert(entryBlock);
// Traverse the CFG and add all reachable blocks to the blockList.
SmallVector<Block *> worklist({entryBlock});
while (!worklist.empty()) {
Block *block = worklist.pop_back_val();
Operation *terminator = &block->back();
for (Block *successor : terminator->getSuccessors()) {
if (reachableBlocks.contains(successor))
continue;
worklist.push_back(successor);
reachableBlocks.insert(successor);
}
}
}
namespace {
struct WalkAndApplyPatternsAction final
: tracing::ActionImpl<WalkAndApplyPatternsAction> {
@ -98,6 +118,8 @@ void walkAndApplyPatterns(Operation *op,
regionIt = region->begin();
if (regionIt != region->end())
blockIt = regionIt->begin();
if (!llvm::hasSingleElement(*region))
findReachableBlocks(*region, reachableBlocks);
}
// Advance the iterator to the next reachable operation.
void advance() {
@ -105,6 +127,9 @@ void walkAndApplyPatterns(Operation *op,
hasVisitedRegions = false;
if (blockIt == regionIt->end()) {
++regionIt;
while (regionIt != region->end() &&
!reachableBlocks.contains(&*regionIt))
++regionIt;
if (regionIt != region->end())
blockIt = regionIt->begin();
return;
@ -121,6 +146,8 @@ void walkAndApplyPatterns(Operation *op,
Region::iterator regionIt;
// The Operation currently being iterated over.
Block::iterator blockIt;
// The set of blocks that are reachable in the current region.
DenseSet<Block *> reachableBlocks;
// Whether we've visited the nested regions of the current op already.
bool hasVisitedRegions = false;
};

View File

@ -119,3 +119,23 @@ func.func @erase_nested_block() -> i32 {
}): () -> (i32)
return %a : i32
}
// CHECK-LABEL: func.func @unreachable_replace_with_new_op
// CHECK: "test.new_op"
// CHECK: "test.replace_with_new_op"
// CHECK-SAME: unreachable
// CHECK: "test.new_op"
func.func @unreachable_replace_with_new_op() {
"test.br"()[^bb1] : () -> ()
^bb1:
%a = "test.replace_with_new_op"() : () -> (i32)
"test.br"()[^end] : () -> () // Test jumping over the unreachable block is visited as well.
^unreachable:
%b = "test.replace_with_new_op"() {test.unreachable} : () -> (i32)
return
^end:
%c = "test.replace_with_new_op"() : () -> (i32)
return
}