River Riddle 93592b726c [mlir][OpFormatGen] Format enum attribute cases as keywords when possible
In the overwhelmingly common case, enum attribute case strings represent valid identifiers in MLIR syntax. This revision updates the format generator to format as a keyword in these cases, removing the need to wrap values in a string. The parser still retains the ability to parse the string form, but the printer will use the keyword form when applicable.

Differential Revision: https://reviews.llvm.org/D94575
2021-01-14 11:35:49 -08:00

247 lines
6.7 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 = alloc() : memref<2x1xf32>
%0 = alloc() : memref<2x1xf32>
// CHECK-NEXT: %1 = alloc() : memref<2x1xf32>
%1 = 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
}