Files
clang-p2996/mlir/test/Transforms/cse.mlir
Mehdi Amini 387f95541b Add a new interface allowing to set a default dialect to be used for printing/parsing regions
Currently the builtin dialect is the default namespace used for parsing
and printing. As such module and func don't need to be prefixed.
In the case of some dialects that defines new regions for their own
purpose (like SpirV modules for example), it can be beneficial to
change the default dialect in order to improve readability.

Differential Revision: https://reviews.llvm.org/D107236
2021-08-31 17:52:40 +00:00

268 lines
7.3 KiB
MLIR

// RUN: mlir-opt -allow-unregistered-dialect %s -pass-pipeline='builtin.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
builtin.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
}