[MLIR][Affine] affine-loop-fusion miscompiles a producer loop with an external func.call
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
`affine-loop-fusion` incorrectly fuses a consumer into a producer loop that passes the produced memref to an external `func.call`.
The silent miscompilation is the primary issue. The same reduced input also crashes in assertions-enabled/current public trunk builds.
Public reproducer: https://www.compiler-explorer.com/z/a8ba738Pb
Tested locally at `llvm/llvm-project` commit `450cf0aafeab9fed6666b112b018d3913a36527c` (`LLVM 24.0.0git`).
## Reproducer
```mlir
module {
func.func private @opaque(memref<32xf64>)
func.func @control(%in: memref<32xf64>, %comm: memref<32xf64>, %out: memref<32xf64>) {
affine.for %i = 0 to 16 {
%a = affine.load %in[%i] : memref<32xf64>
%b = arith.addf %a, %a : f64
affine.store %b, %comm[%i] : memref<32xf64>
func.call @opaque(%comm) : (memref<32xf64>) -> ()
}
affine.for %j = 0 to 16 {
%c = affine.load %comm[%j] : memref<32xf64>
%d = arith.addf %c, %c : f64
affine.store %d, %out[%j] : memref<32xf64>
}
return
}
}
```
## Command
```sh
mlir-opt dc6.mlir \
--pass-pipeline='builtin.module(func.func(affine-loop-fusion{mode=producer maximal}))'
```
## Result
With assertions disabled, the consumer is fused into the producer:
```mlir
affine.for %i = 0 to 16 {
%a = affine.load %in[%i] : memref<32xf64>
%b = arith.addf %a, %a : f64
affine.store %b, %comm[%i] : memref<32xf64>
func.call @opaque(%comm) : (memref<32xf64>) -> ()
%c = affine.load %comm[%i] : memref<32xf64>
%d = arith.addf %c, %c : f64
affine.store %d, %out[%i] : memref<32xf64>
}
```
In the source, all calls complete before the consumer reads `%comm`. After fusion, each consumer iteration runs before the remaining calls.
Using a legal synchronous callee that updates `%comm[0]` on every call, the source produces `out[0] = 8222`, while the transformed program produces `out[0] = 8192`.
Both IR modules verify, and the concrete execution uses initialized buffers, in-bounds accesses, and a synchronous non-retaining callee.
With assertions enabled on the local commit, the same command exits with status 134:
```text
unexpected op
UNREACHABLE executed at mlir/lib/Dialect/Affine/Analysis/Utils.cpp:256!
```
At filing time, Compiler Explorer's `MLIR opt (trunk)` terminates on the same input with `SIGSEGV` (exit code 139).
Related: #205745 reports a crash in the same pass area. PR #205865 prevents the local assertion failure for this reproducer, but the assertions-disabled build still emits the incorrect fusion.
Contributor guide
Research direction
Start by reproducing the issue with mlir-opt using the inline MLIR and affine-loop-fusion pipeline, then trace the fusion decision around the external func.call; the assertion location is mlir/lib/Dialect/Affine/Analysis/Utils.cpp:256. Done means the pass does not fuse across the call or miscompile the program, and the reproducer no longer fails in assertion-enabled builds.
Written by the indexing model from the issue text.
Assessment
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 50/100