[Mlir-commits] [mlir] [mlir][scf] Allow parallel loop fusion across side-effect-free regions (PR #221586)

purnima shrivastava llvmlistbot at llvm.org
Sun Sep 6 08:51:45 PDT 2026


https://github.com/purnima-nlp created https://github.com/llvm/llvm-project/pull/221586


Allow `scf-parallel-loop-fusion` to fuse across side-effect-free region operations such as `scf.if`.
`isMemoryEffectFree` already checks nested regions for recursive memory effects, so the explicit `op.getNumRegions() == 0` restriction was unnecessarily blocking valid fusion opportunities.

Added tests for:

* empty `scf.if`
* pure `scf.if`
* side-effecting `scf.if` that must still block fusion

Fixes #221526


>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] [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



More information about the Mlir-commits mailing list