[mlir][scf] upliftWhileToForLoop reconstructs the wrong scf.while result (last executed IV instead of the exit value)
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
## Summary
`scf::upliftWhileToForLoop` reconstructs the removed `scf.while`'s
induction-variable result as `lb + (tripCount - 1) * step`, i.e. the **last
executed** induction value. `scf.while` instead returns the value that
`scf.condition` forwards when the condition is **false**, which is
`lb + tripCount * step`.
Every supported positive-step loop whose IV result is used gets a wrong value.
Zero-trip loops also exhibit the issue: they return a value the loop could never produce.
### Reachability
`upliftWhileToForLoop` and `populateUpliftWhileToForPatterns` are public API
(`mlir/include/mlir/Dialect/SCF/Transforms/Transforms.h:250` and
`.../Patterns.h:81`) implemented in the production
`mlir/lib/Dialect/SCF/Transforms/` library. No in-tree pass or pipeline
currently calls them, so in upstream the transformation is reachable only via
the test pass `--test-scf-uplift-while-to-for`; downstream pipelines that
populate the patterns get the wrong value directly.
## Reproducer
```mlir
func.func @exit_value() -> index {
%c0 = arith.constant 0 : index
%c2 = arith.constant 2 : index
%c5 = arith.constant 5 : index
%r = scf.while (%iv = %c0) : (index) -> index {
%continue = arith.cmpi slt, %iv, %c5 : index
scf.condition(%continue) %iv : index
} do {
^bb0(%iv: index):
%next = arith.addi %iv, %c2 : index
scf.yield %next : index
}
return %r : index
}
```
```
mlir-opt --test-scf-uplift-while-to-for -canonicalize repro.mlir
```
## Expected vs actual
`scf.condition` forwards its operands to the after region when the condition is
true, and to the `scf.while` **results** when it is false. For
`lb = 0, ub = 5, step = 2`:
| iteration | `%iv` | `%iv < 5` | effect |
|---|---|---|---|
| 0 | 0 | true | body executes |
| 1 | 2 | true | body executes |
| 2 | 4 | true | body executes |
| 3 | **6** | false | **6 becomes the result** |
So the function returns **6**. After uplifting it returns **4**.
Executed end to end (`-convert-scf-to-cf -convert-to-llvm
-reconcile-unrealized-casts` into `mlir-runner`):
```
without uplifting : 6 (correct)
with uplifting : 4 (wrong)
```
Zero-trip loops produce an impossible value. With `lb = 0, ub = 0, step = 2`
the source returns `0`, but uplifting folds to:
Contributor guide
Research direction
Start with scf::upliftWhileToForLoop and populateUpliftWhileToForPatterns in mlir/lib/Dialect/SCF/Transforms/, using their declarations in the mentioned Transforms.h and Patterns.h. Run --test-scf-uplift-while-to-for with the reproducer and inspect the transformation's result reconstruction. Done means positive-step and zero-trip loops preserve the scf.while exit value, including the shown end-to-end result.
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
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 55/100