llvm / llvm/llvm-project

[mlir][scf] upliftWhileToForLoop reconstructs the wrong scf.while result (last executed IV instead of the exit value)

Open
#219,616 1 comment 0 reactions 0 assignees View on GitHub
mlir:scf
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.