[Mlir-commits] [mlir] Fix scf parallel unroll empty factors (PR #222640)
purnima shrivastava
llvmlistbot at llvm.org
Thu Sep 10 06:01:01 PDT 2026
https://github.com/purnima-nlp created https://github.com/llvm/llvm-project/pull/222640
This fixes a crash in `--test-parallel-loop-unrolling` when the pass is invoked without `unroll-factors`.
In that case, the `unrollFactors` list is empty, but the test pass still forwards it to `parallelLoopUnrollByFactors`, which requires a non-empty factor list and asserts.
The fix makes the test pass return early when no unroll factors are provided. A regression test was added to verify that the pass leaves the parallel loop unchanged instead of crashing.
Fixes #222535.
>From 5b449882720f0257d9155d83611e32199999e029 Mon Sep 17 00:00:00 2001
From: Purnima Shrivastava <purnimashrivastava05 at .com>
Date: Sun, 6 Sep 2026 21:16:50 +0530
Subject: [PATCH 1/2] [mlir][scf] Allow parallel loop fusion across
side-effect-free regions
---
.../SCF/Transforms/ParallelLoopFusion.cpp | 3 +-
.../Dialect/SCF/parallel-loop-fusion.mlir | 99 +++++++++++++++++++
2 files changed, 100 insertions(+), 2 deletions(-)
diff --git a/mlir/lib/Dialect/SCF/Transforms/ParallelLoopFusion.cpp b/mlir/lib/Dialect/SCF/Transforms/ParallelLoopFusion.cpp
index 397920565c5ba..b6a7dfe1188af 100644
--- a/mlir/lib/Dialect/SCF/Transforms/ParallelLoopFusion.cpp
+++ b/mlir/lib/Dialect/SCF/Transforms/ParallelLoopFusion.cpp
@@ -1072,8 +1072,7 @@ void mlir::scf::naivelyFuseParallelOps(
}
continue;
}
- // TODO: Handle region side effects properly.
- noSideEffects &= isMemoryEffectFree(&op) && op.getNumRegions() == 0;
+ noSideEffects &= isMemoryEffectFree(&op);
}
for (MutableArrayRef<ParallelOp> ploops : ploopChains) {
for (int i = 0, e = ploops.size(); i + 1 < e; ++i)
diff --git a/mlir/test/Dialect/SCF/parallel-loop-fusion.mlir b/mlir/test/Dialect/SCF/parallel-loop-fusion.mlir
index 9472e9ebb9d22..631c6764015a3 100644
--- a/mlir/test/Dialect/SCF/parallel-loop-fusion.mlir
+++ b/mlir/test/Dialect/SCF/parallel-loop-fusion.mlir
@@ -50,6 +50,105 @@ func.func @fuse_ops_between(%A: f32, %B: f32) -> f32 {
// -----
+func.func @fuse_across_empty_region(%A: memref<16xf32>, %B: memref<16xf32>, %cond: i1) {
+ %c0 = arith.constant 0 : index
+ %c1 = arith.constant 1 : index
+ %c16 = arith.constant 16 : index
+ %value = arith.constant 6.0 : f32
+ scf.parallel (%i) = (%c0) to (%c16) step (%c1) {
+ memref.store %value, %A[%i] : memref<16xf32>
+ scf.reduce
+ }
+ scf.if %cond {
+ } else {
+ }
+ scf.parallel (%i) = (%c0) to (%c16) step (%c1) {
+ %v = memref.load %A[%i] : memref<16xf32>
+ memref.store %v, %B[%i] : memref<16xf32>
+ scf.reduce
+ }
+ return
+}
+// CHECK-LABEL: func @fuse_across_empty_region
+// CHECK-NOT: scf.parallel
+// CHECK: scf.if
+// CHECK: scf.parallel
+// CHECK-NEXT: memref.store
+// CHECK-NEXT: %[[V:.*]] = memref.load
+// CHECK-NEXT: memref.store %[[V]]
+// CHECK-NEXT: scf.reduce
+// CHECK-NEXT: }
+// CHECK-NOT: scf.parallel
+// CHECK: return
+
+// -----
+
+func.func @fuse_across_pure_region(%A: memref<16xf32>, %B: memref<16xf32>, %cond: i1) {
+ %c0 = arith.constant 0 : index
+ %c1 = arith.constant 1 : index
+ %c16 = arith.constant 16 : index
+ %value = arith.constant 6.0 : f32
+ scf.parallel (%i) = (%c0) to (%c16) step (%c1) {
+ memref.store %value, %A[%i] : memref<16xf32>
+ scf.reduce
+ }
+ scf.if %cond {
+ %x = arith.addi %c0, %c1 : index
+ }
+ scf.parallel (%i) = (%c0) to (%c16) step (%c1) {
+ %v = memref.load %A[%i] : memref<16xf32>
+ memref.store %v, %B[%i] : memref<16xf32>
+ scf.reduce
+ }
+ return
+}
+// CHECK-LABEL: func @fuse_across_pure_region
+// CHECK-NOT: scf.parallel
+// CHECK: scf.if
+// CHECK-NEXT: %{{.*}} = arith.addi
+// CHECK-NEXT: }
+// CHECK-NEXT: scf.parallel
+// CHECK-NEXT: memref.store
+// CHECK-NEXT: %[[V:.*]] = memref.load
+// CHECK-NEXT: memref.store %[[V]]
+// CHECK-NEXT: scf.reduce
+// CHECK-NEXT: }
+// CHECK-NOT: scf.parallel
+// CHECK: return
+
+// -----
+
+func.func @do_not_fuse_across_region_store(%A: memref<16xf32>, %B: memref<16xf32>, %cond: i1) {
+ %c0 = arith.constant 0 : index
+ %c1 = arith.constant 1 : index
+ %c16 = arith.constant 16 : index
+ %value = arith.constant 6.0 : f32
+ scf.parallel (%i) = (%c0) to (%c16) step (%c1) {
+ memref.store %value, %A[%i] : memref<16xf32>
+ scf.reduce
+ }
+ scf.if %cond {
+ memref.store %value, %A[%c0] : memref<16xf32>
+ }
+ scf.parallel (%i) = (%c0) to (%c16) step (%c1) {
+ %v = memref.load %A[%i] : memref<16xf32>
+ memref.store %v, %B[%i] : memref<16xf32>
+ scf.reduce
+ }
+ return
+}
+// CHECK-LABEL: func @do_not_fuse_across_region_store
+// CHECK: scf.parallel
+// CHECK: memref.store
+// CHECK: scf.if
+// CHECK-NEXT: memref.store
+// CHECK: scf.parallel
+// CHECK-NEXT: %[[V:.*]] = memref.load
+// CHECK-NEXT: memref.store %[[V]]
+// CHECK: return
+
+// -----
+
func.func @fuse_two(%A: memref<2x2xf32>, %B: memref<2x2xf32>) {
%c2 = arith.constant 2 : index
%c0 = arith.constant 0 : index
>From 4d4613fc32fd8ccaa941824b07c4c3a2856506b1 Mon Sep 17 00:00:00 2001
From: Purnima Shrivastava <purnimashrivastava05 at .com>
Date: Thu, 10 Sep 2026 18:26:05 +0530
Subject: [PATCH 2/2] [mlir][SCF] Avoid crash in parallel loop unrolling test
pass
---
mlir/test/Dialect/SCF/parallel-loop-unroll.mlir | 6 ++++++
mlir/test/lib/Dialect/SCF/TestParallelLoopUnrolling.cpp | 3 +++
2 files changed, 9 insertions(+)
diff --git a/mlir/test/Dialect/SCF/parallel-loop-unroll.mlir b/mlir/test/Dialect/SCF/parallel-loop-unroll.mlir
index 12b502e996c60..6759f456938ed 100644
--- a/mlir/test/Dialect/SCF/parallel-loop-unroll.mlir
+++ b/mlir/test/Dialect/SCF/parallel-loop-unroll.mlir
@@ -1,7 +1,13 @@
+// RUN: mlir-opt %s -test-parallel-loop-unrolling -split-input-file | FileCheck %s --check-prefix=CHECK-NO-UNROLL
// RUN: mlir-opt %s -test-parallel-loop-unrolling='unroll-factors=1,2' -split-input-file | FileCheck %s
// RUN: mlir-opt %s -test-parallel-loop-unrolling='unroll-factors=1,2 loop-depth=1' -split-input-file | FileCheck %s --check-prefix CHECK-UNROLL-INNER
// RUN: mlir-opt %s -test-parallel-loop-unrolling='unroll-factors=3,1' -split-input-file | FileCheck %s --check-prefix CHECK-UNROLL-BY-3
+// CHECK-NO-UNROLL-LABEL: func @unroll_simple_parallel_loop
+// CHECK-NO-UNROLL: [[C1:%.*]] = arith.constant 1 : index
+// CHECK-NO-UNROLL: scf.parallel {{.*}} step ([[C1]], [[C1]], [[C1]])
+// CHECK-NO-UNROLL-NOT: affine.apply
+// CHECK-NO-UNROLL: return
func.func @unroll_simple_parallel_loop(%src: memref<1x16x12xf32>, %dst: memref<1x16x12xf32>) {
%c12 = arith.constant 12 : index
%c16 = arith.constant 16 : index
diff --git a/mlir/test/lib/Dialect/SCF/TestParallelLoopUnrolling.cpp b/mlir/test/lib/Dialect/SCF/TestParallelLoopUnrolling.cpp
index 77a22a1812537..a2cfb1f3c6fce 100644
--- a/mlir/test/lib/Dialect/SCF/TestParallelLoopUnrolling.cpp
+++ b/mlir/test/lib/Dialect/SCF/TestParallelLoopUnrolling.cpp
@@ -46,6 +46,9 @@ struct TestParallelLoopUnrollingPass
}
void runOnOperation() override {
+ if (unrollFactors.empty())
+ return;
+
SmallVector<scf::ParallelOp, 4> loops;
getOperation()->walk([&](scf::ParallelOp parLoop) {
if (getNestingDepth(parLoop) == loopDepth)
More information about the Mlir-commits
mailing list