[Mlir-commits] [mlir] [MLIR][Affine] Fix fusion across ops with unknown memory effects (PR #203231)

Federico Bruzzone llvmlistbot at llvm.org
Fri Jun 12 04:27:37 PDT 2026


https://github.com/FedericoBruzzone commented:

Of course, thank you, this is a much appreciated contribution 🫶


The problem is reproducible locally via this AI-generated script:
<details>
<summary>test_zero_tolerance.sh</summary>

```bash
#!/bin/bash
# Script: test_zero_tolerance.sh
# Verifies that with compute-tolerance=0.0, loops are NOT fused.
# Usage: ./test_zero_tolerance.sh [path to mlir-opt]

MLIROPT="${1:-mlir-opt}"
if ! command -v "$MLIROPT" &> /dev/null; then
  echo "ERROR: '$MLIROPT' could not be found. Please ensure it is in your PATH or provide the correct path as an argument."
  exit 1
fi

INPUT=$(cat << 'EOF'
#map = affine_map<(d0) -> (d0 mod 65536)>
module {
func.func @zero_tolerance(%arg0: memref<65536xcomplex<f64>>, %arg1: memref<30x131072xi64>,
%arg2: memref<30xi64>, %arg3: memref<30xi64>, %arg4: memref<30xi64>, %arg5: memref<30xi64>) {
  %c65536 = arith.constant 65536 : index
  %cst = arith.constant 0.000000e+00 : f64
  %cst_0 = arith.constant 0x4320000000380004 : f64
  %cst_1 = arith.constant 5.000000e-01 : f64
  %alloc = memref.alloc() {alignment = 128 : i64} : memref<30x131072xi64>
  %alloc_1 = memref.alloc() {alignment = 128 : i64} : memref<131072xi1>
  %alloc_2 = memref.alloc() {alignment = 128 : i64} : memref<131072xi128>

  // First nest: produces values in alloc_2 and alloc_1
  affine.for %i = 0 to 131072 {
    %idx = affine.apply #map(%i)
    %v0 = affine.load %arg0[%idx] : memref<65536xcomplex<f64>>
    %cmp = arith.cmpi ult, %i, %c65536 : index
    %im = complex.im %v0 : complex<f64>
    %re = complex.re %v0 : complex<f64>
    %sel = arith.select %cmp, %re, %im : f64
    %olt = arith.cmpf olt, %sel, %cst : f64
    %neg = arith.negf %sel : f64
    %sel2 = arith.select %olt, %neg, %sel : f64
    %mul = arith.mulf %sel2, %cst_0 : f64
    %add = arith.addf %mul, %cst_1 : f64
    %i128 = arith.fptosi %add : f64 to i128
    affine.store %i128, %alloc_2[%i] : memref<131072xi128>
    affine.store %olt, %alloc_1[%i] : memref<131072xi1>
  }

  // Second nest: consumes alloc_2/alloc_1, calls external function
  affine.for %j = 0 to 30 {
    affine.for %k = 0 to 131072 {
      %a0 = affine.load %arg5[%j] : memref<30xi64>
      %a1 = affine.load %arg2[%j] : memref<30xi64>
      %a2 = affine.load %arg4[%j] : memref<30xi64>
      %a3 = affine.load %arg3[%j] : memref<30xi64>
      %buf = affine.load %alloc_2[%k] : memref<131072xi128>
      %flag = affine.load %alloc_1[%k] : memref<131072xi1>
      %ret = func.call @__external_reduce_barrett(%a0, %a1, %a2, %a3, %buf) {outputModFac = 1 : i64} : (i64, i64, i64, i64, i128) -> i64
      %sub = arith.subi %a0, %ret : i64
      %sel = arith.select %flag, %sub, %ret : i64
      affine.store %sel, %alloc[%j, %k] : memref<30x131072xi64>
    }
  }

  // Opaque external call (writes to alloc in-place)
  func.call @__external_levelwise_forward_ntt(%alloc) : (memref<30x131072xi64>) -> ()

  // Third nest: reads from alloc, writes to arg1
  affine.for %l = 0 to 30 {
    affine.for %m = 0 to 131072 {
      %v = affine.load %alloc[%l, %m] : memref<30x131072xi64>
      affine.store %v, %arg1[%l, %m] : memref<30x131072xi64>
    }
  }

  memref.dealloc %alloc_2 : memref<131072xi128>
  memref.dealloc %alloc_1 : memref<131072xi1>
  memref.dealloc %alloc : memref<30x131072xi64>
  return
}
func.func private @__external_levelwise_forward_ntt(memref<30x131072xi64>)
func.func private @__external_reduce_barrett(i64, i64, i64, i64, i128) -> i64
}
EOF
)

echo "=== INPUT (before fusion) ==="
echo "$INPUT"
echo

echo "=== OUTPUT (after affine-loop-fusion with compute-tolerance=0.0) ==="
RESULT=$(echo "$INPUT" | "$MLIROPT" --allow-unregistered-dialect -mlir-disable-threading \
  -pass-pipeline='builtin.module(func.func(affine-loop-fusion{compute-tolerance=0.0}))' 2>&1)

# Display the output module/function
echo "$RESULT"

echo
echo "=== VERIFICATION: Did loops remain separate? ==="
# Count how many affine.for operations are present in the output
NUM_LOOPS=$(echo "$RESULT" | grep -c 'affine.for')
echo "Number of affine.for loops in the output: $NUM_LOOPS"
if [ "$NUM_LOOPS" -eq 5 ]; then
  echo "✅ CORRECT: 5 loops (3 nests, NONE fused) — tolerance 0.0 prevents fusion"
else
  echo "⚠️  Warning: $NUM_LOOPS loops found (expected 5)"
fi
```

</details>

https://github.com/llvm/llvm-project/pull/203231


More information about the Mlir-commits mailing list