[Mlir-commits] [mlir] [MLIR][Arith] Partially fold and / or / xor const triples (PR #195588)
Max Graey
llvmlistbot at llvm.org
Sat May 16 07:41:58 PDT 2026
================
@@ -209,6 +218,15 @@ def MulUIExtendedToMulI :
// xori is commutative and will be canonicalized to have its constants appear
// as the second operand.
+// xori(xori(x, c0), c1) -> xori(x, c0 ^ c1)
+def XOrIXOrIConstant :
----------------
MaxGraey wrote:
Regarding `hasOneUse`. I checked how LLVM and others handle this, and nobody uses this guard. The reason is pretty simple.
Here is a small example:
```mlir
func.func @andOfAndConstantChain(%arg0: i32) -> (i32, i32, i32) {
%c15 = arith.constant 15 : i32
%c14 = arith.constant 14 : i32
%c12 = arith.constant 12 : i32
%a1 = arith.andi %arg0, %c15 : i32 // arg0 & 15
%a2 = arith.andi %a1, %c14 : i32 // arg0 & 14
%a3 = arith.andi %a2, %c12 : i32 // arg0 & 12
return %a1, %a2, %a3 : i32, i32, i32
}
```
Without `hasOneUse`, which is the current behavior, this becomes:
```mlir
// CHECK-LABEL: @andOfAndConstantChain
// CHECK: %[[C15:.+]] = arith.constant 15 : i32
// CHECK: %[[C14:.+]] = arith.constant 14 : i32
// CHECK: %[[C12:.+]] = arith.constant 12 : i32
// CHECK: %[[A1:.+]] = arith.andi %arg0, %[[C15]] : i32
// CHECK: %[[A2:.+]] = arith.andi %arg0, %[[C14]] : i32
// CHECK: %[[A3:.+]] = arith.andi %arg0, %[[C12]] : i32
// CHECK: return %[[A1]], %[[A2]], %[[A3]]
```
The chain gets flattened. All three `andi` ops use `%arg0` directly, so they no longer depend on each other serially.
With the `hasOneUse` predicate, we get this instead:
```mlir
// CHECK-LABEL: @andOfAndConstantChain
// CHECK: %[[C15:.+]] = arith.constant 15 : i32
// CHECK: %[[C14:.+]] = arith.constant 14 : i32
// CHECK: %[[C12:.+]] = arith.constant 12 : i32
// CHECK: %[[A1:.+]] = arith.andi %arg0, %[[C15]] : i32 ;; the same as above
// CHECK: %[[A2:.+]] = arith.andi %[[A1]], %[[C14]] : i32 ;; <--- A1 depedence
// CHECK: %[[A3:.+]] = arith.andi %[[A2]], %[[C12]] : i32 ;; <--- A2 depedence
// CHECK: return %[[A1]], %[[A2]], %[[A3]]
```
So wt get independent `and` ops in the first case (wihout hasOneUse). If some intermediate value becomes dead the DCE will remove it. With the `hasOneUse` guarded pred, that doesn't work as well because the chain still depends on the intermediate values. There is another nice side effect too. If two masks become identical after folding CSE can merge them. With the guard - nope! This is a bit counterintuitive, but in practice the guard makes the result worse rather than better.
So in short, nobody uses this predicate here because it actually hurts the canonical form.
https://github.com/llvm/llvm-project/pull/195588
More information about the Mlir-commits
mailing list