[Mlir-commits] [mlir] [MLIR] control-flow-sink: don't sink non-pure ops with affine-valid results (PR #218216)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Sun Aug 23 00:54:42 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-mlir
Author: Peruere1828 (Peruere1828)
<details>
<summary>Changes</summary>
A non-pure operation whose result is currently a valid affine dimension or symbol must stay at the top level of its affine scope to remain valid. Sinking such an operation into a conditionally executed region will break that invariant and produce invalid IR (e.g. `index.remu` used by `affine.load`, see issue #<!-- -->216542).
The previous `isMemoryEffectFree` check was too permissive because `index.remu` is memory-effect-free but not pure. Switching the legality predicate to `isPure` was too aggressive and broke sinking of ops such as `test.region_if` whose results are not affine-relevant. Only reject sinking when the operation's result is actually a valid affine dim/symbol. Also adds MLIRAffineDialect to MLIRTransforms LINK_LIBS (required for BUILD_SHARED_LIBS builds).
Fixes: #<!-- -->216542
---
Full diff: https://github.com/llvm/llvm-project/pull/218216.diff
3 Files Affected:
- (modified) mlir/lib/Transforms/CMakeLists.txt (+1)
- (modified) mlir/lib/Transforms/ControlFlowSink.cpp (+18-2)
- (modified) mlir/test/Transforms/control-flow-sink.mlir (+32)
``````````diff
diff --git a/mlir/lib/Transforms/CMakeLists.txt b/mlir/lib/Transforms/CMakeLists.txt
index 66b39f53c91df..bf31554526730 100644
--- a/mlir/lib/Transforms/CMakeLists.txt
+++ b/mlir/lib/Transforms/CMakeLists.txt
@@ -31,6 +31,7 @@ add_mlir_library(MLIRTransforms
MLIRTransformsDialectInterfaceIncGen
LINK_LIBS PUBLIC
+ MLIRAffineDialect
MLIRAnalysis
MLIRFunctionInterfaces
MLIRLoopLikeInterface
diff --git a/mlir/lib/Transforms/ControlFlowSink.cpp b/mlir/lib/Transforms/ControlFlowSink.cpp
index eed9c1cdab36d..a093ee9fa4ec4 100644
--- a/mlir/lib/Transforms/ControlFlowSink.cpp
+++ b/mlir/lib/Transforms/ControlFlowSink.cpp
@@ -15,6 +15,7 @@
#include "mlir/Transforms/Passes.h"
+#include "mlir/Dialect/Affine/IR/AffineOps.h"
#include "mlir/IR/Dominance.h"
#include "mlir/Interfaces/ControlFlowInterfaces.h"
#include "mlir/Interfaces/SideEffectInterfaces.h"
@@ -40,10 +41,25 @@ void ControlFlowSink::runOnOperation() {
SmallVector<Region *> regionsToSink;
// Get the regions are that known to be executed at most once.
getSinglyExecutedRegionsToSink(branch, regionsToSink);
- // Sink side-effect free operations.
+ // Sink memory-effect-free operations.
numSunk = controlFlowSink(
regionsToSink, domInfo,
- [](Operation *op, Region *) { return isMemoryEffectFree(op); },
+ [](Operation *op, Region *) {
+ if (!isMemoryEffectFree(op))
+ return false;
+ // A non-pure operation whose result is currently a valid affine
+ // dimension or symbol must remain at the top level of its affine
+ // scope to stay valid. Sinking such an operation into a
+ // conditionally executed region moves its definition into a nested
+ // region, breaking that invariant and producing invalid IR (e.g.
+ // `index.remu` used by `affine.load`).
+ if (isPure(op))
+ return true;
+ for (Value result : op->getResults())
+ if (affine::isValidDim(result) || affine::isValidSymbol(result))
+ return false;
+ return true;
+ },
[](Operation *op, Region *region) {
// Move the operation to the beginning of the region's entry block.
// This guarantees the preservation of SSA dominance of all of the
diff --git a/mlir/test/Transforms/control-flow-sink.mlir b/mlir/test/Transforms/control-flow-sink.mlir
index 4efcac6cdbc29..eafbe2f05277d 100644
--- a/mlir/test/Transforms/control-flow-sink.mlir
+++ b/mlir/test/Transforms/control-flow-sink.mlir
@@ -235,3 +235,35 @@ func.func @test_not_sunk_deeply(%arg0: i32) -> i32 {
}) : () -> i32
return %1 : i32
}
+
+// Test that a non-pure operation whose result is used as an affine dimension
+// is not sunk into a conditionally executed region. `index.remu` is
+// memory-effect-free but not pure, so moving it into the `scf.if` would make
+// its result an invalid affine dimension operand (`affine.load` requires its
+// index to be defined at the top level of the affine scope or produced by a
+// pure op).
+
+// CHECK-LABEL: func.func @test_not_sunk_non_pure(
+// CHECK-SAME: %[[ARG0:.*]]: memref<8xi32>, %[[ARG1:.*]]: index, %[[ARG2:.*]]: i1) -> i32 {
+// CHECK-NEXT: %[[V0:.*]] = index.constant 8
+// CHECK-NEXT: %[[V1:.*]] = index.remu %[[ARG1]], %[[V0]]
+// CHECK-NEXT: %[[V2:.*]] = scf.if %[[ARG2]] -> (i32) {
+// CHECK-NEXT: %[[V3:.*]] = affine.load %[[ARG0]][%[[V1]]] : memref<8xi32>
+// CHECK-NEXT: scf.yield %[[V3]] : i32
+// CHECK-NEXT: } else {
+// CHECK-NEXT: %[[V4:.*]] = index.castu %[[ARG1]] : index to i32
+// CHECK-NEXT: scf.yield %[[V4]] : i32
+// CHECK-NEXT: }
+// CHECK-NEXT: return %[[V2]] : i32
+func.func @test_not_sunk_non_pure(%arg0: memref<8xi32>, %arg1: index, %arg2: i1) -> i32 {
+ %0 = index.constant 8
+ %1 = index.remu %arg1, %0
+ %2 = scf.if %arg2 -> (i32) {
+ %3 = affine.load %arg0[%1] : memref<8xi32>
+ scf.yield %3 : i32
+ } else {
+ %4 = index.castu %arg1 : index to i32
+ scf.yield %4 : i32
+ }
+ return %2 : i32
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/218216
More information about the Mlir-commits
mailing list