llvm / llvm/llvm-project

[MLIR][Transforms] `control-flow-sink` produces invalid IR by moving an affine subscript's definition into a region

Open
#216,542 1 comment 0 reactions 0 assignees View on GitHub
mlir
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

Affine validity is positional: whether an index value may be used as a dimension or symbol depends in part on where its definition and use occur. `--control-flow-sink` can move a definition into a conditionally executed region without preserving that property, turning a valid `affine.load` into invalid IR.

`control-flow-sink` currently admits an operation for sinking when it is memory-effect-free. In this example, `%n` is initially valid because it is defined at the top level of the enclosing `AffineScope`. After `index.remu` is sunk into the `scf.if`, that rule no longer applies. For this value, the relevant producer-based affine validity rule would require the defining operation to be `Pure`, but `index.remu` is `NoMemoryEffect` and not `Pure`. Thus it satisfies the sinking pass's condition, but moving it removes the property that made its result a valid affine dimension operand.

## Reproducer

### small.mlir
```mlir
func.func @f(%m: memref<8xi32>, %arg0: index, %cond: i1) -> i32 {
%c8 = index.constant 8
%n = index.remu %arg0, %c8
%zero = arith.constant 0 : i32
%r = scf.if %cond -> (i32) {
%v = affine.load %m[%n] : memref<8xi32>
scf.yield %v : i32
} else {
scf.yield %zero : i32
}
return %r : i32
}
```

### To reproduce:

```bash
$ mlir-opt small.mlir -o /dev/null
(input parses and verifies)

$ mlir-opt small.mlir --control-flow-sink -o /dev/null
small.mlir:8:10: error: 'affine.load' op operand cannot be used as a dimension id
%v = affine.load %m[%n] : memref<8xi32>
^
small.mlir:8:10: note: see current operation: %4 = "affine.load"(%arg0, %3) <{map = affine_map<(d0) -> (d0)>}> : (memref<8xi32>, index) -> i32
```

The pass sinks the computation of `%n` into the `scf.if`:

```bash
$ mlir-opt small.mlir --control-flow-sink --verify-each=false -o -
```

```mlir
%0 = "scf.if"(%arg2) ({
%2 = "index.constant"() <{value = 8 : index}> : () -> index
%3 = "index.remu"(%arg1, %2) : (index, index) -> index
%4 = "affine.load"(%arg0, %3) <{map = #map}> : (memref<8xi32>, index) -> i32
...
```

### Actual Output:

```
error: 'affine.load' op operand cannot be used as a dimension id
```

### Expected Output:

```
(the pass either sinks legally or declines; the module still verifies)
```

Contributor guide

Open the contributing guide

Research direction

Start by running the issue's small.mlir reproducer with mlir-opt, both with and without --control-flow-sink. Locate the control-flow-sink pass and inspect its memory-effect-free sinking check alongside the affine validity rule described here. Done means the pass either sinks the computation legally or declines, and the module still verifies.

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
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.