// RUN: mlir-opt %s --canonicalize -split-input-file | FileCheck %s --check-prefixes=CHECK,NOCSE // RUN: mlir-opt %s --canonicalize='cse-between-iterations=true' -split-input-file | FileCheck %s --check-prefixes=CHECK,CSE // Convergence / max-iterations interaction: only one pass-application iteration // is allowed, so CSE unifies the duplicates but the follow-up fold cannot fire. // RUN: mlir-opt %s --canonicalize='cse-between-iterations=true max-iterations=1' -split-input-file | FileCheck %s --check-prefixes=CHECK,ONESHOT // Two structurally identical subexpressions cannot be folded away by // canonicalization alone because they are distinct SSA values. Running CSE // between iterations unifies them, which lets `arith.subi %a, %a -> 0` fire // on the next iteration and the whole body collapses to a constant. // CHECK-LABEL: @dup_subs func.func @dup_subs(%x: i32, %y: i32) -> i32 { // NOCSE-COUNT-3: arith.subi // NOCSE-NOT: arith.subi // CSE-NOT: arith.subi // CSE: %[[C0:.*]] = arith.constant 0 : i32 // CSE: return %[[C0]] // Max-iterations=1: CSE fires once but the downstream subi(a, a) -> 0 fold // needs a second pattern-application iteration, which is disallowed. // ONESHOT-COUNT-2: arith.subi // ONESHOT-NOT: arith.constant %a = arith.subi %x, %y : i32 %b = arith.subi %x, %y : i32 %c = arith.subi %a, %b : i32 return %c : i32 } // ----- // After CSE unifies the two redundant subi ops, the downstream `arith.subi // %a, %a` folds to 0, which in turn makes the downstream `arith.addi 0, %y` // fold to %y. This demonstrates that CSE-between-iterations enables a // cascading simplification that canonicalization alone cannot achieve. // CHECK-LABEL: @cascade func.func @cascade(%x: i32, %y: i32) -> i32 { // NOCSE-COUNT-3: arith.subi // NOCSE: arith.addi // NOCSE: return // CSE-NOT: arith.subi // CSE-NOT: arith.addi // CSE: return %arg1 : i32 %a = arith.subi %x, %y : i32 %b = arith.subi %x, %y : i32 %c = arith.subi %a, %b : i32 %d = arith.addi %c, %y : i32 return %d : i32 } // ----- // Nested regions must also be reached by CSE-between-iterations. The // duplicate `arith.subi` ops inside the scf.for body are unified, unblocking // the `arith.subi %a, %a -> 0` fold on the next iteration and then the // `arith.addi 0, ...` fold that follows. The loop body still uses `%i` so // the loop itself is not dead and survives canonicalization. // CHECK-LABEL: @nested func.func @nested(%lb: index, %ub: index, %step: index, %x: i32, %y: i32, %init: i32) -> i32 { // NOCSE: scf.for // NOCSE-COUNT-3: arith.subi // CSE: scf.for // CSE-NOT: arith.subi // CSE: scf.yield %r = scf.for %i = %lb to %ub step %step iter_args(%acc = %init) -> i32 { %a = arith.subi %x, %y : i32 %b = arith.subi %x, %y : i32 %c = arith.subi %a, %b : i32 %ic = arith.index_cast %i : index to i32 %nxt = arith.addi %acc, %ic : i32 %final = arith.addi %nxt, %c : i32 scf.yield %final : i32 } return %r : i32 }