[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