llvm-project/mlir/test/Dialect/Transform/test-pattern-application.mlir
Ingo Müller f40e620956 Reapply "[mlir][transform] Improve error message of tracking listener. (#66987)"
This commit reapplies #66987, which got original contained a memory leak
and got reverted by 78c8ab5844e618162c4cf3982d05102d4da10d23. The leak
is now fixed.

Original description:

This PR extends the error message of the tracking listener when
replacement ops cannot be found. That may happen if the applied patterns
replace an op by an op of a different kind or by block arguments.
However, this only matters if there are alive handles to the replaced
op. The new error message mentions that explicitly and reports the alive
handles.
2023-09-26 12:56:38 +00:00

366 lines
14 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
%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.
// expected-note @below {{replacement is required because alive handle(s) exist (first use in this op as operand number 0)}}
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
// expected-remark @below {{op was replaced}}
%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
transform.test_print_remark_at_operand %0, "op was replaced" : !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
}