Follow up to the discussion from #75258, and serves as an alternate solution for #74670. Set the location to Unknown for deduplicated / moved / materialized constants by OperationFolder. This makes sure that the folded constants don't end up with an arbitrary location of one of the original ops that became it, and that hoisted ops don't confuse the stepping order.
392 lines
15 KiB
MLIR
392 lines
15 KiB
MLIR
// RUN: mlir-opt %s --test-transform-dialect-interpreter -allow-unregistered-dialect --split-input-file --verify-diagnostics | FileCheck %s
|
|
|
|
// CHECK-LABEL: func @update_tracked_op_mapping()
|
|
// CHECK: "test.container"() ({
|
|
// CHECK: %0 = "test.foo"() {annotated} : () -> i32
|
|
// CHECK: }) : () -> ()
|
|
func.func @update_tracked_op_mapping() {
|
|
"test.container"() ({
|
|
%0 = "test.foo"() {replace_with_new_op = "test.foo"} : () -> (i32)
|
|
}) : () -> ()
|
|
return
|
|
}
|
|
|
|
transform.sequence failures(propagate) {
|
|
^bb1(%arg1: !transform.any_op):
|
|
%0 = transform.structured.match ops{["test.container"]} in %arg1 : (!transform.any_op) -> !transform.any_op
|
|
%1 = transform.structured.match ops{["test.foo"]} in %arg1 : (!transform.any_op) -> !transform.any_op
|
|
transform.apply_patterns to %0 {
|
|
transform.apply_patterns.transform.test_patterns
|
|
} : !transform.any_op
|
|
// Add an attribute to %1, which is now mapped to a new op.
|
|
transform.annotate %1 "annotated" : !transform.any_op
|
|
}
|
|
|
|
// -----
|
|
|
|
func.func @replacement_op_not_found() {
|
|
"test.container"() ({
|
|
// expected-note @below {{[0] replaced op}}
|
|
// expected-note @below {{[0] replacement value 0}}
|
|
%0 = "test.foo"() {replace_with_new_op = "test.bar"} : () -> (i32)
|
|
}) : () -> ()
|
|
return
|
|
}
|
|
|
|
transform.sequence failures(propagate) {
|
|
^bb1(%arg1: !transform.any_op):
|
|
%0 = transform.structured.match ops{["test.container"]} in %arg1 : (!transform.any_op) -> !transform.any_op
|
|
// expected-note @below {{replacement is required because this handle must be updated}}
|
|
%1 = transform.structured.match ops{["test.foo"]} in %arg1 : (!transform.any_op) -> !transform.any_op
|
|
// expected-error @below {{tracking listener failed to find replacement op during application of this transform op}}
|
|
// expected-note @below {{ran out of suitable replacement values}}
|
|
transform.apply_patterns to %0 {
|
|
transform.apply_patterns.transform.test_patterns
|
|
} : !transform.any_op
|
|
// %1 must be used in some way. If no replacement payload op could be found,
|
|
// an error is thrown only if the handle is not dead.
|
|
transform.annotate %1 "annotated" : !transform.any_op
|
|
}
|
|
|
|
// -----
|
|
|
|
// CHECK-LABEL: func @replacement_op_for_dead_handle_not_found()
|
|
// CHECK: "test.container"() ({
|
|
// CHECK: %0 = "test.bar"() : () -> i32
|
|
// CHECK: }) : () -> ()
|
|
func.func @replacement_op_for_dead_handle_not_found() {
|
|
"test.container"() ({
|
|
%0 = "test.foo"() {replace_with_new_op = "test.bar"} : () -> (i32)
|
|
}) : () -> ()
|
|
return
|
|
}
|
|
|
|
transform.sequence failures(propagate) {
|
|
^bb1(%arg1: !transform.any_op):
|
|
%0 = transform.structured.match ops{["test.container"]} in %arg1 : (!transform.any_op) -> !transform.any_op
|
|
%1 = transform.structured.match ops{["test.foo"]} in %arg1 : (!transform.any_op) -> !transform.any_op
|
|
// No error because %1 is dead.
|
|
transform.apply_patterns to %0 {
|
|
transform.apply_patterns.transform.test_patterns
|
|
} : !transform.any_op
|
|
}
|
|
|
|
// -----
|
|
|
|
// CHECK-LABEL: func @replacement_op_not_found_silenced()
|
|
// CHECK: "test.container"() ({
|
|
// CHECK: %0 = "test.bar"() : () -> i32
|
|
// CHECK: }) : () -> ()
|
|
func.func @replacement_op_not_found_silenced() {
|
|
"test.container"() ({
|
|
%0 = "test.foo"() {replace_with_new_op = "test.bar"} : () -> (i32)
|
|
}) : () -> ()
|
|
return
|
|
}
|
|
|
|
transform.sequence failures(propagate) {
|
|
^bb1(%arg1: !transform.any_op):
|
|
%0 = transform.structured.match ops{["test.container"]} in %arg1 : (!transform.any_op) -> !transform.any_op
|
|
%1 = transform.structured.match ops{["test.foo"]} in %arg1 : (!transform.any_op) -> !transform.any_op
|
|
transform.apply_patterns to %0 {
|
|
transform.apply_patterns.transform.test_patterns
|
|
} {transform.silence_tracking_failures} : !transform.any_op
|
|
transform.annotate %1 "annotated" : !transform.any_op
|
|
}
|
|
|
|
// -----
|
|
|
|
// CHECK-LABEL: func @patterns_apply_only_to_target_body()
|
|
// CHECK: %0 = "test.foo"() {replace_with_new_op = "test.bar"} : () -> i32
|
|
func.func @patterns_apply_only_to_target_body() {
|
|
%0 = "test.foo"() {replace_with_new_op = "test.bar"} : () -> (i32)
|
|
return
|
|
}
|
|
|
|
transform.sequence failures(propagate) {
|
|
^bb1(%arg1: !transform.any_op):
|
|
%0 = transform.structured.match ops{["test.foo"]} in %arg1 : (!transform.any_op) -> !transform.any_op
|
|
transform.apply_patterns to %0 {
|
|
transform.apply_patterns.transform.test_patterns
|
|
} : !transform.any_op
|
|
}
|
|
|
|
// -----
|
|
|
|
// CHECK-LABEL: func @erase_tracked_op()
|
|
// CHECK: "test.container"() ({
|
|
// CHECK-NEXT: ^bb0:
|
|
// CHECK-NEXT: }) : () -> ()
|
|
func.func @erase_tracked_op() {
|
|
"test.container"() ({
|
|
// expected-remark @below {{matched op}}
|
|
%0 = "test.erase_op"() {replace_with_new_op = "test.foo"} : () -> (i32)
|
|
}) : () -> ()
|
|
return
|
|
}
|
|
|
|
transform.sequence failures(propagate) {
|
|
^bb1(%arg1: !transform.any_op):
|
|
%0 = transform.structured.match ops{["test.container"]} in %arg1 : (!transform.any_op) -> !transform.any_op
|
|
%1 = transform.structured.match ops{["test.erase_op"]} in %arg1 : (!transform.any_op) -> !transform.any_op
|
|
transform.test_print_remark_at_operand %1, "matched op" : !transform.any_op
|
|
transform.apply_patterns to %0 {
|
|
transform.apply_patterns.transform.test_patterns
|
|
} : !transform.any_op
|
|
// No marker should be printed.
|
|
transform.test_print_remark_at_operand %1, "op was deleted" : !transform.any_op
|
|
}
|
|
|
|
// -----
|
|
|
|
// CHECK-LABEL: func @erase_tracked_op_in_named_sequence()
|
|
// CHECK: "test.container"() ({
|
|
// CHECK-NEXT: ^bb0:
|
|
// CHECK-NEXT: }) : () -> ()
|
|
module {
|
|
func.func @erase_tracked_op_in_named_sequence() {
|
|
"test.container"() ({
|
|
// expected-remark @below {{matched op}}
|
|
%0 = "test.erase_op"() {replace_with_new_op = "test.foo"} : () -> (i32)
|
|
}) : () -> ()
|
|
return
|
|
}
|
|
|
|
module attributes { transform.with_named_sequence } {
|
|
transform.named_sequence @foo(%arg0: !transform.any_op {transform.readonly}) -> () {
|
|
transform.apply_patterns to %arg0 {
|
|
transform.apply_patterns.transform.test_patterns
|
|
} : !transform.any_op
|
|
transform.yield
|
|
}
|
|
|
|
transform.sequence failures(propagate) {
|
|
^bb1(%arg1: !transform.any_op):
|
|
%0 = transform.structured.match ops{["test.container"]} in %arg1 : (!transform.any_op) -> !transform.any_op
|
|
%1 = transform.structured.match ops{["test.erase_op"]} in %arg1 : (!transform.any_op) -> !transform.any_op
|
|
transform.test_print_remark_at_operand %1, "matched op" : !transform.any_op
|
|
include @foo failures(propagate) (%0) : (!transform.any_op) -> ()
|
|
// No marker should be printed.
|
|
transform.test_print_remark_at_operand %1, "op was deleted" : !transform.any_op
|
|
}
|
|
}
|
|
}
|
|
|
|
// -----
|
|
|
|
// CHECK-LABEL: func @canonicalization(
|
|
// CHECK: %[[c5:.*]] = arith.constant 5 : index
|
|
// CHECK: return %[[c5]]
|
|
func.func @canonicalization(%t: tensor<5xf32>) -> index {
|
|
%c0 = arith.constant 0 : index
|
|
%dim = tensor.dim %t, %c0 : tensor<5xf32>
|
|
return %dim : index
|
|
}
|
|
|
|
transform.sequence failures(propagate) {
|
|
^bb1(%arg1: !transform.any_op):
|
|
%0 = transform.structured.match ops{["tensor.dim"]} in %arg1 : (!transform.any_op) -> !transform.any_op
|
|
%1 = transform.structured.match ops{["func.func"]} in %arg1 : (!transform.any_op) -> !transform.any_op
|
|
transform.apply_patterns to %1 {
|
|
transform.apply_patterns.canonicalization
|
|
} : !transform.any_op
|
|
}
|
|
|
|
// -----
|
|
|
|
// expected-note @below{{target payload op}}
|
|
module {
|
|
func.func @invalid_pattern_application_to_transform_ir() {
|
|
return
|
|
}
|
|
|
|
module {
|
|
transform.sequence failures(propagate) {
|
|
^bb1(%arg1: !transform.any_op):
|
|
// expected-error @below {{cannot apply transform to itself (or one of its ancestors)}}
|
|
transform.apply_patterns to %arg1 {
|
|
transform.apply_patterns.canonicalization
|
|
} : !transform.any_op
|
|
}
|
|
}
|
|
}
|
|
|
|
// -----
|
|
|
|
// CHECK-LABEL: func @canonicalization_and_cse(
|
|
// CHECK-NOT: memref.subview
|
|
// CHECK-NOT: memref.copy
|
|
func.func @canonicalization_and_cse(%m: memref<5xf32>) {
|
|
%c2 = arith.constant 2 : index
|
|
%s0 = memref.subview %m[1] [2] [1] : memref<5xf32> to memref<2xf32, strided<[1], offset: 1>>
|
|
%s1 = memref.subview %m[1] [%c2] [1] : memref<5xf32> to memref<?xf32, strided<[1], offset: 1>>
|
|
memref.copy %s0, %s1 : memref<2xf32, strided<[1], offset: 1>> to memref<?xf32, strided<[1], offset: 1>>
|
|
return
|
|
}
|
|
|
|
transform.sequence failures(propagate) {
|
|
^bb1(%arg1: !transform.any_op):
|
|
%1 = transform.structured.match ops{["func.func"]} in %arg1 : (!transform.any_op) -> !transform.any_op
|
|
transform.apply_patterns to %1 {
|
|
transform.apply_patterns.canonicalization
|
|
} {apply_cse} : !transform.any_op
|
|
}
|
|
|
|
// -----
|
|
|
|
// CHECK-LABEL: func @full_dialect_conversion
|
|
// CHECK-NEXT: %[[m:.*]] = "test.new_op"() : () -> memref<5xf32>
|
|
// CHECK-NEXT: %[[cast:.*]] = builtin.unrealized_conversion_cast %0 : memref<5xf32> to tensor<5xf32>
|
|
// CHECK-NEXT: return %[[cast]]
|
|
func.func @full_dialect_conversion() -> tensor<5xf32> {
|
|
%0 = "test.foo"() {replace_with_new_op = "test.bar"} : () -> (tensor<5xf32>)
|
|
return %0 : tensor<5xf32>
|
|
}
|
|
|
|
transform.sequence failures(propagate) {
|
|
^bb1(%arg1: !transform.any_op):
|
|
%0 = transform.structured.match ops{["func.func"]} in %arg1 : (!transform.any_op) -> !transform.any_op
|
|
transform.apply_conversion_patterns to %0 {
|
|
transform.apply_conversion_patterns.transform.test_conversion_patterns
|
|
} with type_converter {
|
|
transform.apply_conversion_patterns.transform.test_type_converter
|
|
} {legal_ops = ["func.func", "func.return", "test.new_op"]}
|
|
: !transform.any_op
|
|
}
|
|
|
|
// -----
|
|
|
|
// Full dialect conversion fails because test.bar is not replaced and not legal.
|
|
|
|
// expected-note @below{{target op}}
|
|
func.func @full_dialect_conversion_failed() -> tensor<5xf32> {
|
|
%0 = "test.foo"() {replace_with_new_op = "test.bar"} : () -> (tensor<5xf32>)
|
|
// expected-error @below{{failed to legalize operation 'test.bar'}}
|
|
"test.bar"() : () -> ()
|
|
return %0 : tensor<5xf32>
|
|
}
|
|
|
|
transform.sequence failures(propagate) {
|
|
^bb1(%arg1: !transform.any_op):
|
|
%0 = transform.structured.match ops{["func.func"]} in %arg1 : (!transform.any_op) -> !transform.any_op
|
|
// expected-error @below{{dialect conversion failed}}
|
|
transform.apply_conversion_patterns to %0 {
|
|
transform.apply_conversion_patterns.transform.test_conversion_patterns
|
|
} with type_converter {
|
|
transform.apply_conversion_patterns.transform.test_type_converter
|
|
} {legal_ops = ["func.func", "func.return", "test.new_op"]}
|
|
: !transform.any_op
|
|
}
|
|
|
|
// -----
|
|
|
|
// Partial dialect conversion succeeds because test.bar is not explicitly
|
|
// illegal.
|
|
|
|
// CHECK-LABEL: func @partial_dialect_conversion
|
|
// CHECK-NEXT: %[[m:.*]] = "test.new_op"() : () -> memref<5xf32>
|
|
// CHECK-NEXT: %[[cast:.*]] = builtin.unrealized_conversion_cast %0 : memref<5xf32> to tensor<5xf32>
|
|
// CHECK-NEXT: "test.bar"
|
|
// CHECK-NEXT: return %[[cast]]
|
|
func.func @partial_dialect_conversion() -> tensor<5xf32> {
|
|
%0 = "test.foo"() {replace_with_new_op = "test.bar"} : () -> (tensor<5xf32>)
|
|
"test.bar"() : () -> ()
|
|
return %0 : tensor<5xf32>
|
|
}
|
|
|
|
transform.sequence failures(propagate) {
|
|
^bb1(%arg1: !transform.any_op):
|
|
%0 = transform.structured.match ops{["func.func"]} in %arg1 : (!transform.any_op) -> !transform.any_op
|
|
transform.apply_conversion_patterns to %0 {
|
|
transform.apply_conversion_patterns.transform.test_conversion_patterns
|
|
} with type_converter {
|
|
transform.apply_conversion_patterns.transform.test_type_converter
|
|
} {legal_ops = ["func.func", "func.return", "test.new_op"],
|
|
partial_conversion} : !transform.any_op
|
|
}
|
|
|
|
// -----
|
|
|
|
transform.sequence failures(propagate) {
|
|
^bb1(%arg1: !transform.any_op):
|
|
%0 = transform.structured.match ops{["func.func"]} in %arg1 : (!transform.any_op) -> !transform.any_op
|
|
// expected-error @below{{pattern descriptor does not specify type converter and apply_conversion_patterns op has no default type converter}}
|
|
transform.apply_conversion_patterns to %0 {
|
|
// expected-note @below{{pattern descriptor op}}
|
|
transform.apply_conversion_patterns.transform.test_conversion_patterns
|
|
} {illegal_ops = ["test.foo"]} : !transform.any_op
|
|
}
|
|
|
|
// -----
|
|
|
|
transform.sequence failures(propagate) {
|
|
^bb1(%arg1: !transform.any_op):
|
|
%0 = transform.structured.match ops{["func.func"]} in %arg1 : (!transform.any_op) -> !transform.any_op
|
|
transform.apply_conversion_patterns to %0 {
|
|
// expected-error @below{{expected LLVMTypeConverter}}
|
|
transform.apply_conversion_patterns.dialect_to_llvm "test"
|
|
} with type_converter {
|
|
transform.apply_conversion_patterns.transform.test_type_converter
|
|
} {illegal_ops = ["test.foo"],
|
|
legal_ops = ["func.func", "func.return", "test.new_op"]}
|
|
: !transform.any_op
|
|
}
|
|
|
|
// -----
|
|
|
|
transform.sequence failures(propagate) {
|
|
^bb1(%arg1: !transform.any_op):
|
|
%0 = transform.structured.match ops{["func.func"]} in %arg1 : (!transform.any_op) -> !transform.any_op
|
|
transform.apply_conversion_patterns to %0 {
|
|
// expected-error @below{{unknown dialect or dialect not loaded: this_dialect_does_not_exist}}
|
|
transform.apply_conversion_patterns.dialect_to_llvm "this_dialect_does_not_exist"
|
|
} with type_converter {
|
|
transform.apply_conversion_patterns.memref.memref_to_llvm_type_converter
|
|
} {illegal_ops = ["test.foo"],
|
|
legal_ops = ["func.func", "func.return", "test.new_op"]}
|
|
: !transform.any_op
|
|
}
|
|
|
|
// -----
|
|
|
|
transform.sequence failures(propagate) {
|
|
^bb1(%arg1: !transform.any_op):
|
|
%0 = transform.structured.match ops{["func.func"]} in %arg1 : (!transform.any_op) -> !transform.any_op
|
|
transform.apply_conversion_patterns to %0 {
|
|
// expected-error @below{{dialect does not implement ConvertToLLVMPatternInterface or extension was not loaded: transform}}
|
|
transform.apply_conversion_patterns.dialect_to_llvm "transform"
|
|
} with type_converter {
|
|
transform.apply_conversion_patterns.memref.memref_to_llvm_type_converter
|
|
} {illegal_ops = ["test.foo"],
|
|
legal_ops = ["func.func", "func.return", "test.new_op"]}
|
|
: !transform.any_op
|
|
}
|
|
|
|
// -----
|
|
|
|
module attributes { transform.with_named_sequence } {
|
|
func.func @replacement_op_not_found() {
|
|
// No op replacement can be found, but there are no handles that must be
|
|
// updated. No error should be reported.
|
|
"test.container"() ({
|
|
%0 = "test.foo"() {replace_with_new_op = "test.bar"} : () -> (i32)
|
|
}) : () -> ()
|
|
return
|
|
}
|
|
|
|
transform.named_sequence @patterns(%container: !transform.any_op {transform.readonly}) {
|
|
transform.apply_patterns to %container {
|
|
transform.apply_patterns.transform.test_patterns
|
|
} : !transform.any_op
|
|
transform.yield
|
|
}
|
|
|
|
transform.sequence failures(propagate) {
|
|
^bb1(%arg1: !transform.any_op):
|
|
%0 = transform.structured.match ops{["test.container"]} in %arg1 : (!transform.any_op) -> !transform.any_op
|
|
%1 = transform.structured.match ops{["test.foo"]} in %arg1 : (!transform.any_op) -> !transform.any_op
|
|
transform.annotate %1 "annotated" : !transform.any_op
|
|
transform.include @patterns failures(propagate) (%0) : (!transform.any_op) -> ()
|
|
}
|
|
}
|