blob: aa7b5dc2a240af3fe3480370da669b6ab921f877 [file] [log] [blame] [edit]
// RUN: mlir-opt --allow-unregistered-dialect --transform-interpreter --split-input-file --verify-diagnostics %s | FileCheck %s
// Check simple move of dependent operation before insertion.
func.func @simple_move() -> f32 {
%0 = "before"() : () -> (f32)
%1 = "moved_op"() : () -> (f32)
%2 = "foo"(%1) : (f32) -> (f32)
return %2 : f32
}
// CHECK-LABEL: func @simple_move()
// CHECK: %[[MOVED:.+]] = "moved_op"
// CHECK: %[[BEFORE:.+]] = "before"
// CHECK: %[[FOO:.+]] = "foo"(%[[MOVED]])
// CHECK: return %[[FOO]]
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg0 : !transform.any_op {transform.readonly}) {
%op1 = transform.structured.match ops{["foo"]} in %arg0
: (!transform.any_op) -> !transform.any_op
%op2 = transform.structured.match ops{["before"]} in %arg0
: (!transform.any_op) -> !transform.any_op
transform.test.move_operand_deps %op1 before %op2
: !transform.any_op, !transform.any_op
transform.yield
}
}
// -----
// Move operands that are implicitly captured by the op
func.func @move_region_dependencies() -> f32 {
%0 = "before"() : () -> (f32)
%1 = "moved_op"() : () -> (f32)
%2 = "foo"() ({
%3 = "inner_op"(%1) : (f32) -> (f32)
"yield"(%3) : (f32) -> ()
}) : () -> (f32)
return %2 : f32
}
// CHECK-LABEL: func @move_region_dependencies()
// CHECK: %[[MOVED:.+]] = "moved_op"
// CHECK: %[[BEFORE:.+]] = "before"
// CHECK: %[[FOO:.+]] = "foo"
// CHECK: return %[[FOO]]
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg0 : !transform.any_op {transform.readonly}) {
%op1 = transform.structured.match ops{["foo"]} in %arg0
: (!transform.any_op) -> !transform.any_op
%op2 = transform.structured.match ops{["before"]} in %arg0
: (!transform.any_op) -> !transform.any_op
transform.test.move_operand_deps %op1 before %op2
: !transform.any_op, !transform.any_op
transform.yield
}
}
// -----
// Move operations in toplogical sort order
func.func @move_in_topological_sort_order() -> f32 {
%0 = "before"() : () -> (f32)
%1 = "moved_op_1"() : () -> (f32)
%2 = "moved_op_2"() : () -> (f32)
%3 = "moved_op_3"(%1) : (f32) -> (f32)
%4 = "moved_op_4"(%1, %3) : (f32, f32) -> (f32)
%5 = "moved_op_5"(%2) : (f32) -> (f32)
%6 = "foo"(%4, %5) : (f32, f32) -> (f32)
return %6 : f32
}
// CHECK-LABEL: func @move_in_topological_sort_order()
// CHECK: %[[MOVED_1:.+]] = "moved_op_1"
// CHECK-DAG: %[[MOVED_2:.+]] = "moved_op_3"(%[[MOVED_1]])
// CHECK-DAG: %[[MOVED_3:.+]] = "moved_op_4"(%[[MOVED_1]], %[[MOVED_2]])
// CHECK-DAG: %[[MOVED_4:.+]] = "moved_op_2"
// CHECK-DAG: %[[MOVED_5:.+]] = "moved_op_5"(%[[MOVED_4]])
// CHECK: %[[BEFORE:.+]] = "before"
// CHECK: %[[FOO:.+]] = "foo"(%[[MOVED_3]], %[[MOVED_5]])
// CHECK: return %[[FOO]]
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg0 : !transform.any_op {transform.readonly}) {
%op1 = transform.structured.match ops{["foo"]} in %arg0
: (!transform.any_op) -> !transform.any_op
%op2 = transform.structured.match ops{["before"]} in %arg0
: (!transform.any_op) -> !transform.any_op
transform.test.move_operand_deps %op1 before %op2
: !transform.any_op, !transform.any_op
transform.yield
}
}
// -----
func.func @move_region_dependencies() -> f32 {
%0 = "before"() : () -> (f32)
%1 = "moved_op_1"() : () -> (f32)
%2 = "moved_op_2"() ({
"yield"(%1) : (f32) -> ()
}) : () -> (f32)
%3 = "foo"() ({
"yield"(%2) : (f32) -> ()
}) : () -> (f32)
return %3 : f32
}
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg0 : !transform.any_op {transform.readonly}) {
%op1 = transform.structured.match ops{["foo"]} in %arg0
: (!transform.any_op) -> !transform.any_op
%op2 = transform.structured.match ops{["before"]} in %arg0
: (!transform.any_op) -> !transform.any_op
transform.test.move_operand_deps %op1 before %op2
: !transform.any_op, !transform.any_op
transform.yield
}
}
// CHECK-LABEL: func @move_region_dependencies()
// CHECK: %[[MOVED_1:.+]] = "moved_op_1"
// CHECK: %[[MOVED_2:.+]] = "moved_op_2"
// CHECK: "yield"(%[[MOVED_1]])
// CHECK: "before"
// CHECK: %[[FOO:.+]] = "foo"
// CHECK: return %[[FOO]]
// -----
// Current implementation omits following basic block argument when
// computing slices. Verify that this gives expected result.
func.func @ignore_basic_block_arguments() -> f32 {
^bb0():
%8 = "test"() : () -> (f32)
return %8: f32
^bb1(%bbArg : f32):
%0 = "before"() : () -> (f32)
%1 = "moved_op"() ({
"yield"(%bbArg) : (f32) -> ()
}) : () -> (f32)
%2 = "foo"() ({
"yield"(%1) : (f32) -> ()
}) : () -> (f32)
return %2 : f32
}
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg0 : !transform.any_op {transform.readonly}) {
%op1 = transform.structured.match ops{["foo"]} in %arg0
: (!transform.any_op) -> !transform.any_op
%op2 = transform.structured.match ops{["before"]} in %arg0
: (!transform.any_op) -> !transform.any_op
transform.test.move_operand_deps %op1 before %op2
: !transform.any_op, !transform.any_op
transform.yield
}
}
// CHECK-LABEL: func @ignore_basic_block_arguments()
// CHECK: %[[MOVED_1:.+]] = "moved_op"
// CHECK: "before"
// CHECK: %[[FOO:.+]] = "foo"
// CHECK: return %[[FOO]]
// -----
// Fail when the "before" operation is part of the operation slice.
func.func @do_not_move_slice() -> f32 {
%0 = "before"() : () -> (f32)
%1 = "moved_op"(%0) : (f32) -> (f32)
%2 = "foo"(%1) : (f32) -> (f32)
return %2 : f32
}
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg0 : !transform.any_op {transform.readonly}) {
%op1 = transform.structured.match ops{["foo"]} in %arg0
: (!transform.any_op) -> !transform.any_op
%op2 = transform.structured.match ops{["before"]} in %arg0
: (!transform.any_op) -> !transform.any_op
// expected-remark@+1{{cannot move dependencies before operation in backward slice of op}}
transform.test.move_operand_deps %op1 before %op2
: !transform.any_op, !transform.any_op
transform.yield
}
}
// -----
// Fail when the "before" operation is part of the operation slice (computed
// when looking through implicitly captured values).
func.func @do_not_move_slice() -> f32 {
%0 = "before"() : () -> (f32)
%1 = "moved_op"() ({
"yield"(%0) : (f32) -> ()
}) : () -> (f32)
%2 = "foo"() ({
"yield"(%1) : (f32) -> ()
}) : () -> (f32)
return %2 : f32
}
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg0 : !transform.any_op {transform.readonly}) {
%op1 = transform.structured.match ops{["foo"]} in %arg0
: (!transform.any_op) -> !transform.any_op
%op2 = transform.structured.match ops{["before"]} in %arg0
: (!transform.any_op) -> !transform.any_op
// expected-remark@+1{{cannot move dependencies before operation in backward slice of op}}
transform.test.move_operand_deps %op1 before %op2
: !transform.any_op, !transform.any_op
transform.yield
}
}
// -----
// Dont move ops when insertion point does not dominate the op
func.func @do_not_move() -> f32 {
%1 = "moved_op"() : () -> (f32)
%2 = "foo"() ({
"yield"(%1) : (f32) -> ()
}) : () -> (f32)
%3 = "before"() : () -> f32
return %2 : f32
}
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg0 : !transform.any_op {transform.readonly}) {
%op1 = transform.structured.match ops{["foo"]} in %arg0
: (!transform.any_op) -> !transform.any_op
%op2 = transform.structured.match ops{["before"]} in %arg0
: (!transform.any_op) -> !transform.any_op
// expected-remark@+1{{insertion point does not dominate op}}
transform.test.move_operand_deps %op1 before %op2
: !transform.any_op, !transform.any_op
transform.yield
}
}
// -----
// Check simple move value definitions before insertion operation.
func.func @simple_move_values() -> f32 {
%0 = "before"() : () -> (f32)
%1 = "moved_op_1"() : () -> (f32)
%2 = "moved_op_2"() : () -> (f32)
%3 = "foo"(%1, %2) : (f32, f32) -> (f32)
return %3 : f32
}
// CHECK-LABEL: func @simple_move_values()
// CHECK: %[[MOVED1:.+]] = "moved_op_1"
// CHECK: %[[MOVED2:.+]] = "moved_op_2"
// CHECK: %[[BEFORE:.+]] = "before"
// CHECK: %[[FOO:.+]] = "foo"(%[[MOVED1]], %[[MOVED2]])
// CHECK: return %[[FOO]]
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg0 : !transform.any_op {transform.readonly}) {
%op1 = transform.structured.match ops{["moved_op_1"]} in %arg0
: (!transform.any_op) -> !transform.any_op
%op2 = transform.structured.match ops{["moved_op_2"]} in %arg0
: (!transform.any_op) -> !transform.any_op
%op3 = transform.structured.match ops{["before"]} in %arg0
: (!transform.any_op) -> !transform.any_op
%v1 = transform.get_result %op1[0] : (!transform.any_op) -> !transform.any_value
%v2 = transform.get_result %op2[0] : (!transform.any_op) -> !transform.any_value
transform.test.move_value_defns %v1, %v2 before %op3
: (!transform.any_value, !transform.any_value), !transform.any_op
transform.yield
}
}
// -----
// Compute slice including the implicitly captured values.
func.func @move_region_dependencies_values() -> f32 {
%0 = "before"() : () -> (f32)
%1 = "moved_op_1"() : () -> (f32)
%2 = "moved_op_2"() ({
%3 = "inner_op"(%1) : (f32) -> (f32)
"yield"(%3) : (f32) -> ()
}) : () -> (f32)
return %2 : f32
}
// CHECK-LABEL: func @move_region_dependencies_values()
// CHECK: %[[MOVED1:.+]] = "moved_op_1"
// CHECK: %[[MOVED2:.+]] = "moved_op_2"
// CHECK: %[[BEFORE:.+]] = "before"
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg0 : !transform.any_op {transform.readonly}) {
%op1 = transform.structured.match ops{["moved_op_2"]} in %arg0
: (!transform.any_op) -> !transform.any_op
%op2 = transform.structured.match ops{["before"]} in %arg0
: (!transform.any_op) -> !transform.any_op
%v1 = transform.get_result %op1[0] : (!transform.any_op) -> !transform.any_value
transform.test.move_value_defns %v1 before %op2
: (!transform.any_value), !transform.any_op
transform.yield
}
}
// -----
// Move operations in toplogical sort order
func.func @move_values_in_topological_sort_order() -> f32 {
%0 = "before"() : () -> (f32)
%1 = "moved_op_1"() : () -> (f32)
%2 = "moved_op_2"() : () -> (f32)
%3 = "moved_op_3"(%1) : (f32) -> (f32)
%4 = "moved_op_4"(%1, %3) : (f32, f32) -> (f32)
%5 = "moved_op_5"(%2) : (f32) -> (f32)
%6 = "foo"(%4, %5) : (f32, f32) -> (f32)
return %6 : f32
}
// CHECK-LABEL: func @move_values_in_topological_sort_order()
// CHECK: %[[MOVED_1:.+]] = "moved_op_1"
// CHECK-DAG: %[[MOVED_2:.+]] = "moved_op_3"(%[[MOVED_1]])
// CHECK-DAG: %[[MOVED_3:.+]] = "moved_op_4"(%[[MOVED_1]], %[[MOVED_2]])
// CHECK-DAG: %[[MOVED_4:.+]] = "moved_op_2"
// CHECK-DAG: %[[MOVED_5:.+]] = "moved_op_5"(%[[MOVED_4]])
// CHECK: %[[BEFORE:.+]] = "before"
// CHECK: %[[FOO:.+]] = "foo"(%[[MOVED_3]], %[[MOVED_5]])
// CHECK: return %[[FOO]]
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg0 : !transform.any_op {transform.readonly}) {
%op1 = transform.structured.match ops{["moved_op_4"]} in %arg0
: (!transform.any_op) -> !transform.any_op
%op2 = transform.structured.match ops{["moved_op_5"]} in %arg0
: (!transform.any_op) -> !transform.any_op
%op3 = transform.structured.match ops{["before"]} in %arg0
: (!transform.any_op) -> !transform.any_op
%v1 = transform.get_result %op1[0] : (!transform.any_op) -> !transform.any_value
%v2 = transform.get_result %op2[0] : (!transform.any_op) -> !transform.any_value
transform.test.move_value_defns %v1, %v2 before %op3
: (!transform.any_value, !transform.any_value), !transform.any_op
transform.yield
}
}
// -----
// Move only those value definitions that are not dominated by insertion point
func.func @move_only_required_defns() -> (f32, f32, f32, f32) {
%0 = "unmoved_op"() : () -> (f32)
%1 = "dummy_op"() : () -> (f32)
%2 = "before"() : () -> (f32)
%3 = "moved_op"() : () -> (f32)
return %0, %1, %2, %3 : f32, f32, f32, f32
}
// CHECK-LABEL: func @move_only_required_defns()
// CHECK: %[[UNMOVED:.+]] = "unmoved_op"
// CHECK: %[[DUMMY:.+]] = "dummy_op"
// CHECK: %[[MOVED:.+]] = "moved_op"
// CHECK: %[[BEFORE:.+]] = "before"
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg0 : !transform.any_op {transform.readonly}) {
%op1 = transform.structured.match ops{["unmoved_op"]} in %arg0
: (!transform.any_op) -> !transform.any_op
%op2 = transform.structured.match ops{["dummy_op"]} in %arg0
: (!transform.any_op) -> !transform.any_op
%op3 = transform.structured.match ops{["before"]} in %arg0
: (!transform.any_op) -> !transform.any_op
%op4 = transform.structured.match ops{["moved_op"]} in %arg0
: (!transform.any_op) -> !transform.any_op
%v1 = transform.get_result %op1[0] : (!transform.any_op) -> !transform.any_value
%v2 = transform.get_result %op4[0] : (!transform.any_op) -> !transform.any_value
transform.test.move_value_defns %v1, %v2 before %op3
: (!transform.any_value, !transform.any_value), !transform.any_op
transform.yield
}
}
// -----
// Move only those value definitions that are not dominated by insertion point
func.func @move_only_required_defns() -> (f32, f32, f32, f32) {
%0 = "unmoved_op"() : () -> (f32)
%1 = "dummy_op"() : () -> (f32)
%2 = "before"() : () -> (f32)
%3 = "moved_op"() : () -> (f32)
return %0, %1, %2, %3 : f32, f32, f32, f32
}
// CHECK-LABEL: func @move_only_required_defns()
// CHECK: %[[UNMOVED:.+]] = "unmoved_op"
// CHECK: %[[DUMMY:.+]] = "dummy_op"
// CHECK: %[[MOVED:.+]] = "moved_op"
// CHECK: %[[BEFORE:.+]] = "before"
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg0 : !transform.any_op {transform.readonly}) {
%op1 = transform.structured.match ops{["unmoved_op"]} in %arg0
: (!transform.any_op) -> !transform.any_op
%op2 = transform.structured.match ops{["dummy_op"]} in %arg0
: (!transform.any_op) -> !transform.any_op
%op3 = transform.structured.match ops{["before"]} in %arg0
: (!transform.any_op) -> !transform.any_op
%op4 = transform.structured.match ops{["moved_op"]} in %arg0
: (!transform.any_op) -> !transform.any_op
%v1 = transform.get_result %op1[0] : (!transform.any_op) -> !transform.any_value
%v2 = transform.get_result %op4[0] : (!transform.any_op) -> !transform.any_value
transform.test.move_value_defns %v1, %v2 before %op3
: (!transform.any_value, !transform.any_value), !transform.any_op
transform.yield
}
}
// -----
// Check handling of block arguments
func.func @move_only_required_defns() -> (f32, f32) {
%0 = "unmoved_op"() : () -> (f32)
cf.br ^bb0(%0 : f32)
^bb0(%arg0 : f32) :
%1 = "before"() : () -> (f32)
%2 = "moved_op"(%arg0) : (f32) -> (f32)
return %1, %2 : f32, f32
}
// CHECK-LABEL: func @move_only_required_defns()
// CHECK: %[[MOVED:.+]] = "moved_op"
// CHECK: %[[BEFORE:.+]] = "before"
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg0 : !transform.any_op {transform.readonly}) {
%op1 = transform.structured.match ops{["before"]} in %arg0
: (!transform.any_op) -> !transform.any_op
%op2 = transform.structured.match ops{["moved_op"]} in %arg0
: (!transform.any_op) -> !transform.any_op
%v1 = transform.get_result %op2[0] : (!transform.any_op) -> !transform.any_value
transform.test.move_value_defns %v1 before %op1
: (!transform.any_value), !transform.any_op
transform.yield
}
}
// -----
// Do not move across basic blocks
func.func @no_move_across_basic_blocks() -> (f32, f32) {
%0 = "unmoved_op"() : () -> (f32)
%1 = "before"() : () -> (f32)
cf.br ^bb0(%0 : f32)
^bb0(%arg0 : f32) :
%2 = "moved_op"(%arg0) : (f32) -> (f32)
return %1, %2 : f32, f32
}
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg0 : !transform.any_op {transform.readonly}) {
%op1 = transform.structured.match ops{["before"]} in %arg0
: (!transform.any_op) -> !transform.any_op
%op2 = transform.structured.match ops{["moved_op"]} in %arg0
: (!transform.any_op) -> !transform.any_op
%v1 = transform.get_result %op2[0] : (!transform.any_op) -> !transform.any_value
// expected-remark@+1{{unsupported case of moving definition of value before an insertion point in a different basic block}}
transform.test.move_value_defns %v1 before %op1
: (!transform.any_value), !transform.any_op
transform.yield
}
}