llvm / llvm/llvm-project

[bug][mlir] `[arith-unsigned-when-equivalent]` incorrectly replaces a dynamic `vector.extract` index with `0` after `convert-scf-to-cf`

Open
#212,994 2 comments 0 reactions 0 assignees View on GitHub
mlir
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

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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.