Files
TheRock/compiler/amd-llvm/mlir/test/Transforms/move-operation-deps.mlir
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

773 lines
30 KiB
MLIR

// 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(%arg0 : f32) -> f32 {
%0 = "before"() : () -> (f32)
%1 = arith.addf %arg0, %arg0 {moved_op} : f32
%2 = "foo"(%1) : (f32) -> (f32)
return %2 : f32
}
// CHECK-LABEL: func @simple_move(
// CHECK: %[[MOVED:.+]] = arith.addf {{.*}} {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(%arg0 : f32) -> f32 {
%0 = "before"() : () -> (f32)
%1 = arith.addf %arg0, %arg0 {moved_op} : f32
%2 = "foo"() ({
%3 = arith.mulf %1, %1 : f32
"yield"(%3) : (f32) -> ()
}) : () -> (f32)
return %2 : f32
}
// CHECK-LABEL: func @move_region_dependencies(
// CHECK: %[[MOVED:.+]] = arith.addf {{.*}} {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(%arg0 : f32, %arg1 : f32) -> f32 {
%0 = "before"() : () -> (f32)
%1 = arith.addf %arg0, %arg0 {moved_op_1} : f32
%2 = arith.addf %arg1, %arg1 {moved_op_2} : f32
%3 = arith.mulf %1, %1 {moved_op_3} : f32
%4 = arith.mulf %1, %3 {moved_op_4} : f32
%5 = arith.mulf %2, %2 {moved_op_5} : f32
%6 = "foo"(%4, %5) : (f32, f32) -> (f32)
return %6 : f32
}
// CHECK-LABEL: func @move_in_topological_sort_order(
// CHECK: %[[MOVED_1:.+]] = arith.addf {{.*}} {moved_op_1}
// CHECK-DAG: %[[MOVED_2:.+]] = arith.mulf %[[MOVED_1]], %[[MOVED_1]] {moved_op_3}
// CHECK-DAG: %[[MOVED_3:.+]] = arith.mulf %[[MOVED_1]], %[[MOVED_2]] {moved_op_4}
// CHECK-DAG: %[[MOVED_4:.+]] = arith.addf {{.*}} {moved_op_2}
// CHECK-DAG: %[[MOVED_5:.+]] = arith.mulf %[[MOVED_4]], %[[MOVED_4]] {moved_op_5}
// 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(%arg0 : f32) -> f32 {
%cond = arith.constant true
%0 = "before"() : () -> (f32)
%1 = arith.addf %arg0, %arg0 {moved_op_1} : f32
%2 = scf.if %cond -> f32 {
%inner = arith.mulf %1, %1 : f32
scf.yield %inner : f32
} else {
scf.yield %1 : f32
}
%3 = "foo"() ({
"yield"(%2) : (f32) -> ()
}) : () -> (f32)
return %3 : f32
}
// CHECK-LABEL: func @move_region_dependencies(
// CHECK: arith.constant true
// CHECK: %[[MOVED1:.+]] = arith.addf {{.*}} {moved_op_1}
// CHECK: %[[MOVED2:.+]] = scf.if
// CHECK: "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
}
}
// -----
// 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):
%cond = arith.constant true
%0 = "before"() : () -> (f32)
%1 = scf.if %cond -> f32 {
%inner = arith.addf %bbArg, %bbArg : f32
scf.yield %inner : f32
} else {
scf.yield %bbArg : f32
}
%2 = "foo"() ({
"yield"(%1) : (f32) -> ()
}) : () -> (f32)
return %2 : f32
}
// CHECK-LABEL: func @ignore_basic_block_arguments()
// CHECK: arith.constant true
// CHECK: %[[MOVED:.+]] = scf.if
// CHECK: "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
}
}
// -----
// Fail when the "before" operation is part of the operation slice.
func.func @do_not_move_slice() -> f32 {
%0 = arith.constant {before} 1.0 : f32
%1 = arith.addf %0, %0 {moved_op} : 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 attributes{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_region() -> f32 {
%cond = arith.constant true
%0 = arith.constant {before} 1.0 : f32
%1 = scf.if %cond -> f32 {
%inner = arith.addf %0, %0 : f32
scf.yield %inner : f32
} else {
scf.yield %0 : 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 attributes{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(%arg0 : f32) -> f32 {
%1 = arith.addf %arg0, %arg0 {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(%arg0 : index) -> index {
%c0 = arith.constant 0 : index
%0 = "before"() : () -> (index)
%1 = arith.addi %arg0, %c0 {"moved_op_1"} : index
%2 = arith.subi %arg0, %c0 {"moved_op_2"} : index
%3 = "foo"(%1, %2) : (index, index) -> (index)
return %3 : index
}
// CHECK-LABEL: func @simple_move_values(
// CHECK: %[[MOVED1:.+]] = arith.addi {{.*}} {moved_op_1}
// CHECK: %[[MOVED2:.+]] = arith.subi {{.*}} {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{["arith.addi"]} in %arg0
: (!transform.any_op) -> !transform.any_op
%op2 = transform.structured.match ops{["arith.subi"]} 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(%arg0 : index, %cond : i1) -> index {
%0 = "before"() : () -> (index)
%1 = arith.addi %arg0, %arg0 {moved_op_1} : index
%2 = scf.if %cond -> index {
%3 = arith.muli %1, %1 {inner_op} : index
scf.yield %3 : index
} else {
scf.yield %1 : index
}
return %2 : index
}
// CHECK-LABEL: func @move_region_dependencies_values(
// CHECK: %[[MOVED1:.+]] = arith.addi {{.*}} {moved_op_1}
// CHECK: scf.if
// CHECK: arith.muli %[[MOVED1]], %[[MOVED1]] {inner_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{["scf.if"]} 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(%arg0 : index, %arg1 : index) -> index {
%0 = "before"() : () -> (index)
%1 = arith.addi %arg0, %arg0 {moved_op_1} : index
%2 = arith.addi %arg1, %arg1 {moved_op_2} : index
%3 = arith.muli %1, %1 {moved_op_3} : index
%4 = arith.andi %1, %3 {moved_op_4} : index
%5 = arith.subi %2, %2 {moved_op_5} : index
%6 = "foo"(%4, %5) : (index, index) -> (index)
return %6 : index
}
// CHECK-LABEL: func @move_values_in_topological_sort_order(
// CHECK: %[[MOVED_1:.+]] = arith.addi {{.*}} {moved_op_1}
// CHECK-DAG: %[[MOVED_2:.+]] = arith.muli %[[MOVED_1]], %[[MOVED_1]] {moved_op_3}
// CHECK-DAG: %[[MOVED_3:.+]] = arith.andi %[[MOVED_1]], %[[MOVED_2]] {moved_op_4}
// CHECK-DAG: %[[MOVED_4:.+]] = arith.addi {{.*}} {moved_op_2}
// CHECK-DAG: %[[MOVED_5:.+]] = arith.subi %[[MOVED_4]], %[[MOVED_4]] {moved_op_5}
// 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{["arith.andi"]} in %arg0
: (!transform.any_op) -> !transform.any_op
%op2 = transform.structured.match ops{["arith.subi"]} 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(%arg0 : index) -> (index, index, index, index) {
%0 = "unmoved_op"() : () -> (index)
%1 = "dummy_op"() : () -> (index)
%2 = "before"() : () -> (index)
%3 = arith.addi %arg0, %arg0 {moved_op} : index
return %0, %1, %2, %3 : index, index, index, index
}
// CHECK-LABEL: func @move_only_required_defns(
// CHECK: %[[UNMOVED:.+]] = "unmoved_op"
// CHECK: %[[DUMMY:.+]] = "dummy_op"
// CHECK: %[[MOVED:.+]] = arith.addi {{.*}} {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{["arith.addi"]} 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 (duplicate test)
func.func @move_only_required_defns_2(%arg0 : index) -> (index, index, index, index) {
%0 = "unmoved_op"() : () -> (index)
%1 = "dummy_op"() : () -> (index)
%2 = "before"() : () -> (index)
%3 = arith.subi %arg0, %arg0 {moved_op} : index
return %0, %1, %2, %3 : index, index, index, index
}
// CHECK-LABEL: func @move_only_required_defns_2(
// CHECK: %[[UNMOVED:.+]] = "unmoved_op"
// CHECK: %[[DUMMY:.+]] = "dummy_op"
// CHECK: %[[MOVED:.+]] = arith.subi {{.*}} {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{["arith.subi"]} 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_with_block_arguments() -> (index, index) {
%0 = "unmoved_op"() : () -> (index)
cf.br ^bb0(%0 : index)
^bb0(%arg0 : index) :
%1 = "before"() : () -> (index)
%2 = arith.addi %arg0, %arg0 {moved_op} : index
return %1, %2 : index, index
}
// CHECK-LABEL: func @move_with_block_arguments()
// CHECK: %[[MOVED:.+]] = arith.addi {{.*}} {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{["arith.addi"]} 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
}
}
// -----
// Successfully move operation between blocks in the same region.
// The function argument %arg0 dominates all blocks, so the move is valid.
func.func @move_between_blocks_same_region(%arg0 : index, %cond : i1) -> index {
%0 = "before"() : () -> (index)
cf.cond_br %cond, ^bb1, ^bb2(%arg0 : index)
^bb1:
%1 = arith.addi %arg0, %arg0 {to_move} : index
cf.br ^bb2(%1 : index)
^bb2(%result : index):
return %result : index
}
// CHECK-LABEL: func @move_between_blocks_same_region
// CHECK: %[[MOVED:.+]] = arith.addi {{.*}} {to_move}
// CHECK: "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 attributes{to_move} 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
}
}
// -----
//===----------------------------------------------------------------------===//
// Cross-region move tests
//===----------------------------------------------------------------------===//
// Move multiple values with dependencies out of nested region
func.func @move_chain_out_of_region(%arg0 : index, %cond : i1) -> index {
%0 = "before"() : () -> (index)
%1 = scf.if %cond -> index {
%2 = arith.addi %arg0, %arg0 {dep1} : index
%3 = arith.muli %2, %2 {dep2} : index
%4 = arith.subi %3, %arg0 {to_move} : index
scf.yield %4 : index
} else {
scf.yield %arg0 : index
}
return %1 : index
}
// CHECK-LABEL: func @move_chain_out_of_region(
// CHECK: arith.addi {{.*}} {dep1}
// CHECK: arith.muli {{.*}} {dep2}
// CHECK: arith.subi {{.*}} {to_move}
// CHECK: "before"
// CHECK: scf.if
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 attributes{to_move} 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
}
}
// -----
// moveValueDefinitions: cannot move when slice has side-effecting ops
func.func @move_value_defs_side_effecting(%arg0 : memref<index>) -> index {
%0 = "before"() : () -> (index)
%1 = memref.load %arg0[] : memref<index>
%2 = arith.muli %1, %1 {moved_op} : index
return %2 : index
}
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 attributes{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{{cannot move value definitions with side-effecting operations in the slice}}
transform.test.move_value_defns %v1 before %op1
: (!transform.any_value), !transform.any_op
transform.yield
}
}
// -----
// moveOperationDependencies: cannot move when slice has side-effecting ops
func.func @move_op_deps_side_effecting(%arg0 : memref<index>) -> index {
%0 = "before"() : () -> (index)
%1 = memref.load %arg0[] : memref<index>
%2 = arith.muli %1, %1 {moved_op} : index
%3 = "foo"(%2) : (index) -> (index)
return %3 : index
}
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 operation with side-effecting dependencies}}
transform.test.move_operand_deps %op1 before %op2
: !transform.any_op, !transform.any_op
transform.yield
}
}
// -----
// Can move op using outer loop's IV when staying within outer loop
func.func @move_op_using_outer_loop_iv(%lb : index, %ub : index, %step : index) -> index {
%result = scf.for %outer_iv = %lb to %ub step %step iter_args(%acc = %lb) -> index {
%before = "before"() : () -> (index)
scf.for %inner_iv = %lb to %ub step %step {
// Uses outer_iv which dominates within the outer loop body
%x = arith.addi %outer_iv, %outer_iv {to_move} : index
"use"(%x) : (index) -> ()
}
scf.yield %acc : index
}
return %result : index
}
// CHECK-LABEL: func @move_op_using_outer_loop_iv(
// CHECK: scf.for %[[OUTER_IV:[a-zA-Z0-9_]+]] =
// CHECK: arith.addi %[[OUTER_IV]], %[[OUTER_IV]] {to_move}
// CHECK: "before"
// CHECK: scf.for
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 attributes{to_move} 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
}
}
// -----
// Move out of doubly nested non-isolated region
func.func @move_out_of_doubly_nested_region(%arg0 : index, %cond1 : i1, %cond2 : i1) -> index {
%0 = "before"() : () -> (index)
%1 = scf.if %cond1 -> index {
%2 = scf.if %cond2 -> index {
%3 = arith.addi %arg0, %arg0 {to_move} : index
scf.yield %3 : index
} else {
scf.yield %arg0 : index
}
scf.yield %2 : index
} else {
scf.yield %arg0 : index
}
return %1 : index
}
// CHECK-LABEL: func @move_out_of_doubly_nested_region(
// CHECK: %[[MOVED:.+]] = arith.addi {{.*}} {to_move}
// CHECK: %[[BEFORE:.+]] = "before"
// CHECK: scf.if
// CHECK: scf.if
// CHECK: scf.yield %[[MOVED]]
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 attributes{to_move} 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
}
}
// -----
// Move operand deps out of nested region
func.func @move_operand_deps_out_of_region(%arg0 : index, %cond : i1) -> index {
%0 = "before"() : () -> (index)
%1 = scf.if %cond -> index {
%2 = arith.addi %arg0, %arg0 {dep} : index
%3 = "foo"(%2) {target} : (index) -> (index)
scf.yield %3 : index
} else {
scf.yield %arg0 : index
}
return %1 : index
}
// CHECK-LABEL: func @move_operand_deps_out_of_region(
// CHECK: %[[DEP:.+]] = arith.addi {{.*}} {dep}
// CHECK: %[[BEFORE:.+]] = "before"
// CHECK: scf.if
// CHECK: "foo"(%[[DEP]]) {target}
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
}
}
// -----
// Cannot move op that depends on loop induction variable (block argument)
func.func @cannot_move_op_using_loop_iv(%arg0 : index, %lb : index, %ub : index, %step : index) -> index {
%0 = "before"() : () -> (index)
%1 = scf.for %iv = %lb to %ub step %step iter_args(%acc = %arg0) -> index {
%2 = arith.addi %iv, %iv {to_move} : index
%3 = arith.addi %acc, %2 : index
scf.yield %3 : index
}
return %1 : index
}
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 attributes{to_move} in %arg0
: (!transform.any_op) -> !transform.any_op
%v1 = transform.get_result %op2[0] : (!transform.any_op) -> !transform.any_value
// expected-remark@+1{{moving op would break dominance for block argument operand}}
transform.test.move_value_defns %v1 before %op1
: (!transform.any_value), !transform.any_op
transform.yield
}
}
// -----
// Cannot move out of an isolated-from-above region, even when op is in a
// non-isolated region nested inside the isolated region
func.func @cannot_move_out_of_isolated_region(%arg0 : index, %cond : i1) -> index {
%0 = "before"() : () -> (index)
%1 = "test.isolated_one_region_op"(%arg0, %cond) ({
^bb0(%inner_arg: index, %inner_cond: i1):
// scf.if is NOT isolated, but it's inside an isolated region
%2 = scf.if %inner_cond -> index {
%3 = arith.addi %inner_arg, %inner_arg {to_move} : index
scf.yield %3 : index
} else {
scf.yield %inner_arg : index
}
"test.region_yield"(%2) : (index) -> ()
}) : (index, i1) -> (index)
return %1 : index
}
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 attributes{to_move} in %arg0
: (!transform.any_op) -> !transform.any_op
%v1 = transform.get_result %op2[0] : (!transform.any_op) -> !transform.any_value
// expected-remark@+1{{cannot move value definition across isolated-from-above region}}
transform.test.move_value_defns %v1 before %op1
: (!transform.any_value), !transform.any_op
transform.yield
}
}
// -----
// Fail when trying to move an operation whose region captures a block argument
// that wouldn't dominate at the insertion point.
func.func @captured_block_arg_does_not_dominate(%arg0 : f32, %cond : i1) -> f32 {
%0 = arith.addf %arg0, %arg0 {before} : f32
cf.br ^bb1(%0 : f32)
^bb1(%bbArg : f32):
// scf.if will be part of the slice that needs to move.
// It has a region that captures %bbArg from bb1.
// Moving it before the {before} op in the entry block would be invalid
// because %bbArg (a block argument of bb1) doesn't dominate the entry block.
%1 = scf.if %cond -> f32 {
%inner = arith.addf %bbArg, %bbArg : f32
scf.yield %inner : f32
} else {
scf.yield %bbArg : f32
}
%2 = arith.mulf %1, %1 {target} : 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 attributes{before} in %arg0
: (!transform.any_op) -> !transform.any_op
%op2 = transform.structured.match attributes{target} in %arg0
: (!transform.any_op) -> !transform.any_op
%v1 = transform.get_result %op2[0] : (!transform.any_op) -> !transform.any_value
// expected-remark@+1{{moving op would break dominance for block argument operand}}
transform.test.move_value_defns %v1 before %op1
: (!transform.any_value), !transform.any_op
transform.yield
}
}