Introduce a new extension for simple print-debugging of the transform dialect scripts. The initial version of this extension consists of two ops that are printing the payload objects associated with transform dialect values. Similar ops were already available in the test extenion and several downstream projects, and were extensively used for testing.
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.debug.emit_remark_at %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.debug.emit_remark_at %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.debug.emit_remark_at %1, "matched op" : !transform.any_op
|
|
include @foo failures(propagate) (%0) : (!transform.any_op) -> ()
|
|
// No marker should be printed.
|
|
transform.debug.emit_remark_at %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) -> ()
|
|
}
|
|
}
|