Files
clang-p2996/mlir/test/Transforms/cse.mlir
Andrew Young 9c61c76b12 [mlir][cse] do not replace operands in previously simplified operations
If an operation has been inserted as a key in to the known values
hashtable, then it can not be modified in a way which changes its hash.
This change avoids modifying the operands of any previously recorded
operation, which prevents their hash from changing.

In an SSACFG region, it is impossible to visit an operation before
visiting its operands, so this is not a problem. This situation can only
happen in regions without strict dominance, such as graph regions.

Reviewed By: rriddle

Differential Revision: https://reviews.llvm.org/D99486
2021-03-31 12:20:34 -07:00

268 lines
7.2 KiB
MLIR

// RUN: mlir-opt -allow-unregistered-dialect %s -pass-pipeline='func(cse)' | FileCheck %s
// CHECK-DAG: #[[$MAP:.*]] = affine_map<(d0) -> (d0 mod 2)>
#map0 = affine_map<(d0) -> (d0 mod 2)>
// CHECK-LABEL: @simple_constant
func @simple_constant() -> (i32, i32) {
// CHECK-NEXT: %c1_i32 = constant 1 : i32
%0 = constant 1 : i32
// CHECK-NEXT: return %c1_i32, %c1_i32 : i32, i32
%1 = constant 1 : i32
return %0, %1 : i32, i32
}
// CHECK-LABEL: @basic
func @basic() -> (index, index) {
// CHECK: %c0 = constant 0 : index
%c0 = constant 0 : index
%c1 = constant 0 : index
// CHECK-NEXT: %0 = affine.apply #[[$MAP]](%c0)
%0 = affine.apply #map0(%c0)
%1 = affine.apply #map0(%c1)
// CHECK-NEXT: return %0, %0 : index, index
return %0, %1 : index, index
}
// CHECK-LABEL: @many
func @many(f32, f32) -> (f32) {
^bb0(%a : f32, %b : f32):
// CHECK-NEXT: %0 = addf %arg0, %arg1 : f32
%c = addf %a, %b : f32
%d = addf %a, %b : f32
%e = addf %a, %b : f32
%f = addf %a, %b : f32
// CHECK-NEXT: %1 = addf %0, %0 : f32
%g = addf %c, %d : f32
%h = addf %e, %f : f32
%i = addf %c, %e : f32
// CHECK-NEXT: %2 = addf %1, %1 : f32
%j = addf %g, %h : f32
%k = addf %h, %i : f32
// CHECK-NEXT: %3 = addf %2, %2 : f32
%l = addf %j, %k : f32
// CHECK-NEXT: return %3 : f32
return %l : f32
}
/// Check that operations are not eliminated if they have different operands.
// CHECK-LABEL: @different_ops
func @different_ops() -> (i32, i32) {
// CHECK: %c0_i32 = constant 0 : i32
// CHECK: %c1_i32 = constant 1 : i32
%0 = constant 0 : i32
%1 = constant 1 : i32
// CHECK-NEXT: return %c0_i32, %c1_i32 : i32, i32
return %0, %1 : i32, i32
}
/// Check that operations are not eliminated if they have different result
/// types.
// CHECK-LABEL: @different_results
func @different_results(%arg0: tensor<*xf32>) -> (tensor<?x?xf32>, tensor<4x?xf32>) {
// CHECK: %0 = tensor.cast %arg0 : tensor<*xf32> to tensor<?x?xf32>
// CHECK-NEXT: %1 = tensor.cast %arg0 : tensor<*xf32> to tensor<4x?xf32>
%0 = tensor.cast %arg0 : tensor<*xf32> to tensor<?x?xf32>
%1 = tensor.cast %arg0 : tensor<*xf32> to tensor<4x?xf32>
// CHECK-NEXT: return %0, %1 : tensor<?x?xf32>, tensor<4x?xf32>
return %0, %1 : tensor<?x?xf32>, tensor<4x?xf32>
}
/// Check that operations are not eliminated if they have different attributes.
// CHECK-LABEL: @different_attributes
func @different_attributes(index, index) -> (i1, i1, i1) {
^bb0(%a : index, %b : index):
// CHECK: %0 = cmpi slt, %arg0, %arg1 : index
%0 = cmpi slt, %a, %b : index
// CHECK-NEXT: %1 = cmpi ne, %arg0, %arg1 : index
/// Predicate 1 means inequality comparison.
%1 = cmpi ne, %a, %b : index
%2 = "std.cmpi"(%a, %b) {predicate = 1} : (index, index) -> i1
// CHECK-NEXT: return %0, %1, %1 : i1, i1, i1
return %0, %1, %2 : i1, i1, i1
}
/// Check that operations with side effects are not eliminated.
// CHECK-LABEL: @side_effect
func @side_effect() -> (memref<2x1xf32>, memref<2x1xf32>) {
// CHECK: %0 = memref.alloc() : memref<2x1xf32>
%0 = memref.alloc() : memref<2x1xf32>
// CHECK-NEXT: %1 = memref.alloc() : memref<2x1xf32>
%1 = memref.alloc() : memref<2x1xf32>
// CHECK-NEXT: return %0, %1 : memref<2x1xf32>, memref<2x1xf32>
return %0, %1 : memref<2x1xf32>, memref<2x1xf32>
}
/// Check that operation definitions are properly propagated down the dominance
/// tree.
// CHECK-LABEL: @down_propagate_for
func @down_propagate_for() {
// CHECK: %c1_i32 = constant 1 : i32
%0 = constant 1 : i32
// CHECK-NEXT: affine.for {{.*}} = 0 to 4 {
affine.for %i = 0 to 4 {
// CHECK-NEXT: "foo"(%c1_i32, %c1_i32) : (i32, i32) -> ()
%1 = constant 1 : i32
"foo"(%0, %1) : (i32, i32) -> ()
}
return
}
// CHECK-LABEL: @down_propagate
func @down_propagate() -> i32 {
// CHECK-NEXT: %c1_i32 = constant 1 : i32
%0 = constant 1 : i32
// CHECK-NEXT: %true = constant true
%cond = constant true
// CHECK-NEXT: cond_br %true, ^bb1, ^bb2(%c1_i32 : i32)
cond_br %cond, ^bb1, ^bb2(%0 : i32)
^bb1: // CHECK: ^bb1:
// CHECK-NEXT: br ^bb2(%c1_i32 : i32)
%1 = constant 1 : i32
br ^bb2(%1 : i32)
^bb2(%arg : i32):
return %arg : i32
}
/// Check that operation definitions are NOT propagated up the dominance tree.
// CHECK-LABEL: @up_propagate_for
func @up_propagate_for() -> i32 {
// CHECK: affine.for {{.*}} = 0 to 4 {
affine.for %i = 0 to 4 {
// CHECK-NEXT: %c1_i32_0 = constant 1 : i32
// CHECK-NEXT: "foo"(%c1_i32_0) : (i32) -> ()
%0 = constant 1 : i32
"foo"(%0) : (i32) -> ()
}
// CHECK: %c1_i32 = constant 1 : i32
// CHECK-NEXT: return %c1_i32 : i32
%1 = constant 1 : i32
return %1 : i32
}
// CHECK-LABEL: func @up_propagate
func @up_propagate() -> i32 {
// CHECK-NEXT: %c0_i32 = constant 0 : i32
%0 = constant 0 : i32
// CHECK-NEXT: %true = constant true
%cond = constant true
// CHECK-NEXT: cond_br %true, ^bb1, ^bb2(%c0_i32 : i32)
cond_br %cond, ^bb1, ^bb2(%0 : i32)
^bb1: // CHECK: ^bb1:
// CHECK-NEXT: %c1_i32 = constant 1 : i32
%1 = constant 1 : i32
// CHECK-NEXT: br ^bb2(%c1_i32 : i32)
br ^bb2(%1 : i32)
^bb2(%arg : i32): // CHECK: ^bb2
// CHECK-NEXT: %c1_i32_0 = constant 1 : i32
%2 = constant 1 : i32
// CHECK-NEXT: %1 = addi %0, %c1_i32_0 : i32
%add = addi %arg, %2 : i32
// CHECK-NEXT: return %1 : i32
return %add : i32
}
/// The same test as above except that we are testing on a cfg embedded within
/// an operation region.
// CHECK-LABEL: func @up_propagate_region
func @up_propagate_region() -> i32 {
// CHECK-NEXT: %0 = "foo.region"
%0 = "foo.region"() ({
// CHECK-NEXT: %c0_i32 = constant 0 : i32
// CHECK-NEXT: %true = constant true
// CHECK-NEXT: cond_br
%1 = constant 0 : i32
%true = constant true
cond_br %true, ^bb1, ^bb2(%1 : i32)
^bb1: // CHECK: ^bb1:
// CHECK-NEXT: %c1_i32 = constant 1 : i32
// CHECK-NEXT: br
%c1_i32 = constant 1 : i32
br ^bb2(%c1_i32 : i32)
^bb2(%arg : i32): // CHECK: ^bb2(%1: i32):
// CHECK-NEXT: %c1_i32_0 = constant 1 : i32
// CHECK-NEXT: %2 = addi %1, %c1_i32_0 : i32
// CHECK-NEXT: "foo.yield"(%2) : (i32) -> ()
%c1_i32_0 = constant 1 : i32
%2 = addi %arg, %c1_i32_0 : i32
"foo.yield" (%2) : (i32) -> ()
}) : () -> (i32)
return %0 : i32
}
/// This test checks that nested regions that are isolated from above are
/// properly handled.
// CHECK-LABEL: @nested_isolated
func @nested_isolated() -> i32 {
// CHECK-NEXT: constant 1
%0 = constant 1 : i32
// CHECK-NEXT: @nested_func
func @nested_func() {
// CHECK-NEXT: constant 1
%foo = constant 1 : i32
"foo.yield"(%foo) : (i32) -> ()
}
// CHECK: "foo.region"
"foo.region"() ({
// CHECK-NEXT: constant 1
%foo = constant 1 : i32
"foo.yield"(%foo) : (i32) -> ()
}) : () -> ()
return %0 : i32
}
/// This test is checking that CSE gracefully handles values in graph regions
/// where the use occurs before the def, and one of the defs could be CSE'd with
/// the other.
// CHECK-LABEL: @use_before_def
func @use_before_def() {
// CHECK-NEXT: test.graph_region
test.graph_region {
// CHECK-NEXT: addi %c1_i32, %c1_i32_0
%0 = addi %1, %2 : i32
// CHECK-NEXT: constant 1
// CHECK-NEXT: constant 1
%1 = constant 1 : i32
%2 = constant 1 : i32
// CHECK-NEXT: "foo.yield"(%0) : (i32) -> ()
"foo.yield"(%0) : (i32) -> ()
}
return
}