[Mlir-commits] [mlir] [mlir][scf] Fuse parallel loops with private allocations (PR #227766)
Ivan Butygin
llvmlistbot at llvm.org
Thu Oct 1 05:14:40 PDT 2026
Hardcode84 wrote:
The current change is LGTM, but AI agent review exposed a couple more gaps in the existing alias checks, adding there for documentation purposes:
```mlir
// DEFINE: %{lower} = -scf-parallel-for-to-nested-fors -convert-scf-to-cf \
// DEFINE: -convert-arith-to-llvm -finalize-memref-to-llvm \
// DEFINE: -convert-func-to-llvm -convert-cf-to-llvm -reconcile-unrealized-casts
// RUN: mlir-opt %s %{lower} | \
// RUN: mlir-runner -e main -entry-point-result=i32 | FileCheck %s
// RUN: mlir-opt %s -scf-parallel-loop-fusion %{lower} | \
// RUN: mlir-runner -e main -entry-point-result=i32 | FileCheck %s
// The helper writes to a different element in each iteration. All calls must
// finish before the second loop reads the buffer in reverse order.
// If the loops fuse, the first read can occur before the second helper call.
// Sequential execution of the fused loop then returns the initial value, 0.
// CHECK: {{^1$}}
func.func private @set(%buffer: memref<2xi32>, %i: index, %value: i32) {
memref.store %value, %buffer[%i] : memref<2xi32>
return
}
func.func @main() -> i32 {
%c0 = arith.constant 0 : index
%c1 = arith.constant 1 : index
%c2 = arith.constant 2 : index
%zero = arith.constant 0 : i32
%one = arith.constant 1 : i32
%buffer = memref.alloc() : memref<2xi32>
%out = memref.alloc() : memref<2xi32>
memref.store %zero, %buffer[%c0] : memref<2xi32>
memref.store %zero, %buffer[%c1] : memref<2xi32>
scf.parallel (%i) = (%c0) to (%c2) step (%c1) {
%private = memref.alloca() : memref<1xi32>
memref.store %one, %private[%c0] : memref<1xi32>
%value = memref.load %private[%c0] : memref<1xi32>
func.call @set(%buffer, %i, %value) : (memref<2xi32>, index, i32) -> ()
}
scf.parallel (%i) = (%c0) to (%c2) step (%c1) {
%j = arith.subi %c1, %i : index
%value = memref.load %buffer[%j] : memref<2xi32>
memref.store %value, %out[%i] : memref<2xi32>
}
%answer = memref.load %out[%c0] : memref<2xi32>
memref.dealloc %out : memref<2xi32>
memref.dealloc %buffer : memref<2xi32>
return %answer : i32
}
```
```mlir
// DEFINE: %{lower} = -scf-parallel-for-to-nested-fors -convert-scf-to-cf \
// DEFINE: -convert-arith-to-llvm -finalize-memref-to-llvm \
// DEFINE: -convert-func-to-llvm -convert-cf-to-llvm -reconcile-unrealized-casts
// RUN: mlir-opt %s %{lower} | \
// RUN: mlir-runner -e main -entry-point-result=i32 | FileCheck %s
// RUN: mlir-opt %s -scf-parallel-loop-fusion %{lower} | \
// RUN: mlir-runner -e main -entry-point-result=i32 | FileCheck %s
// Each loop writes to a different element in each iteration. The second loop
// must run after all writes from the first loop. Both elements must contain 2.
// If the loops fuse, writes from different iterations conflict. Sequential
// execution of the fused loop leaves 1 in the second element.
// CHECK: {{^2$}}
func.func @main() -> i32 {
%c0 = arith.constant 0 : index
%c1 = arith.constant 1 : index
%c2 = arith.constant 2 : index
%one = arith.constant 1 : i32
%two = arith.constant 2 : i32
%buffer = memref.alloc() : memref<2xi32>
scf.parallel (%i) = (%c0) to (%c2) step (%c1) {
%private = memref.alloca() : memref<1xi32>
memref.store %one, %private[%c0] : memref<1xi32>
%value = memref.load %private[%c0] : memref<1xi32>
memref.store %value, %buffer[%i] : memref<2xi32>
}
scf.parallel (%i) = (%c0) to (%c2) step (%c1) {
%j = arith.subi %c1, %i : index
memref.store %two, %buffer[%j] : memref<2xi32>
}
%answer = memref.load %buffer[%c1] : memref<2xi32>
memref.dealloc %buffer : memref<2xi32>
return %answer : i32
}
```
We can fix in follow up PRs.
https://github.com/llvm/llvm-project/pull/227766
More information about the Mlir-commits
mailing list