[bug][mlir] `[arith-unsigned-when-equivalent]` incorrectly replaces a dynamic `vector.extract` index with `0` after `convert-scf-to-cf`
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
`arith-unsigned-when-equivalent` changes the result of a program even though the
only operation it is expected to rewrite is an unused comparison. After
`convert-scf-to-cf`, the pass rewrites a dynamic `vector.extract` index in an
unrelated loop to the constant index `0`.
## Minimal reproducer
```mlir
module {
func.func @func1() {
%c0 = arith.constant 0 : index
%c1 = arith.constant 1 : index
%c13 = arith.constant 13 : index
%zero = arith.constant 0 : i32
%vector = arith.constant dense<[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]> : vector<13xi32>
// This comparison is deliberately unused.
%unused = arith.cmpi sge, %c0, %c0 : index
cf.br ^loop
^loop:
%sum = scf.for %i = %c0 to %c13 step %c1 iter_args(%acc = %zero) -> (i32) {
%element = vector.extract %vector[%i] : i32 from vector<13xi32>
%next = arith.addi %acc, %element : i32
scf.yield %next : i32
}
vector.print %sum : i32
return
}
}
```
## Commands
```sh
mlir-opt repro.mlir --verify-each --convert-scf-to-cf -o control.mlir
mlir-opt repro.mlir --verify-each --convert-scf-to-cf \
--arith-unsigned-when-equivalent -o trigger.mlir
diff -u control.mlir trigger.mlir
```
## Actual result
Besides the expected comparison rewrite
```mlir
// arith.cmpi sge, %c0, %c0 : index
arith.cmpi uge, %c0, %c0 : index
```
the pass changes the loop body from
```mlir
%element = vector.extract %vector[%iv] : i32 from vector<13xi32>
```
to
```mlir
%element = vector.extract %vector[0] : i32 from vector<13xi32>
```
`%iv` is the induction variable of a 13-iteration loop and is not provably
equal to zero. Both pipelines pass `--verify-each`.
After standard LLVM lowering, this is observable at runtime:
| Pipeline | Output |
| --- | ---: |
| `--convert-scf-to-cf` followed by lowering | `91` |
| Same pipeline plus `--arith-unsigned-when-equivalent` | `13` |
The correct result is `1 + 2 + ... + 13 = 91`. In the miscompiled result,
the first lane (`1`) is accumulated 13 times.
## Expected result
The pass may rewrite the unused `arith.cmpi sge` to `arith.cmpi uge`, but it
must not modify the dynamic index of an unrelated `vector.extract`. Both
pipelines should produce `91`.
Contributor guide
Research direction
Reproduce the issue with the provided MLIR snippet and compare the two mlir-opt pipelines using diff. Start at the arith-unsigned-when-equivalent pass and trace why it rewrites the dynamic vector.extract index after convert-scf-to-cf. Add a regression test covering the unused comparison and loop, ensuring the index remains dynamic and lowering produces 91.
Written by the indexing model from the issue text.
Assessment
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 55/100