blob: f932c6b05684eecf41d4872df5ce8b2beb1f8746 [file] [log] [blame]
// RUN: mlir-opt --transform-interpreter --split-input-file --verify-diagnostics %s
// expected-note @below {{ancestor payload op}}
func.func @func() {
// expected-note @below {{nested payload op}}
return
}
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%root: !transform.any_op) {
transform.with_pdl_patterns %root : !transform.any_op {
^bb0(%arg0: !transform.any_op):
pdl.pattern @return : benefit(1) {
%0 = operands
%1 = types
%2 = operation "func.return"(%0 : !pdl.range<value>) -> (%1 : !pdl.range<type>)
rewrite %2 with "transform.dialect"
}
sequence %arg0 : !transform.any_op failures(propagate) {
^bb1(%arg1: !transform.any_op):
// expected-note @below {{handle to invalidated ops}}
%0 = pdl_match @return in %arg1 : (!transform.any_op) -> !transform.any_op
%1 = get_parent_op %0 {isolated_from_above} : (!transform.any_op) -> !transform.any_op
// expected-note @below {{invalidated by this transform op that consumes its operand #0}}
test_consume_operand %1 : !transform.any_op
// expected-error @below {{op uses a handle invalidated by a previously executed transform op}}
transform.debug.emit_remark_at %0, "remark" : !transform.any_op
}
}
transform.yield
}
}
// -----
func.func @func1() {
// expected-note @below {{repeated target op}}
return
}
func.func private @func2()
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%root: !transform.any_op) {
transform.with_pdl_patterns %root : !transform.any_op {
^bb0(%arg0: !transform.any_op):
pdl.pattern @func : benefit(1) {
%0 = operands
%1 = types
%2 = operation "func.func"(%0 : !pdl.range<value>) -> (%1 : !pdl.range<type>)
rewrite %2 with "transform.dialect"
}
pdl.pattern @return : benefit(1) {
%0 = operands
%1 = types
%2 = operation "func.return"(%0 : !pdl.range<value>) -> (%1 : !pdl.range<type>)
rewrite %2 with "transform.dialect"
}
sequence %arg0 : !transform.any_op failures(propagate) {
^bb1(%arg1: !transform.any_op):
%0 = pdl_match @func in %arg1 : (!transform.any_op) -> !transform.any_op
%1 = pdl_match @return in %arg1 : (!transform.any_op) -> !transform.any_op
%2 = replicate num(%0) %1 : !transform.any_op, !transform.any_op
// expected-error @below {{a handle passed as operand #0 and consumed by this operation points to a payload entity more than once}}
test_consume_operand %2 : !transform.any_op
transform.debug.emit_remark_at %0, "remark" : !transform.any_op
}
}
transform.yield
}
}
// -----
// expected-note @below {{ancestor payload op}}
// expected-note @below {{nested payload op}}
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%0: !transform.any_op) {
%1 = transform.test_copy_payload %0 : (!transform.any_op) -> !transform.any_op
// expected-note @below {{handle to invalidated ops}}
%2 = transform.test_copy_payload %0 : (!transform.any_op) ->!transform.any_op
// expected-note @below {{invalidated by this transform op that consumes its operand #0}}
transform.test_consume_operand %1 : !transform.any_op
// expected-error @below {{op uses a handle invalidated by a previously executed transform op}}
transform.test_consume_operand %2 : !transform.any_op
transform.yield
}
}
// -----
// expected-note @below {{ancestor payload op}}
// expected-note @below {{nested payload op}}
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%0: !transform.any_op) {
%1 = transform.test_copy_payload %0 : (!transform.any_op) -> !transform.any_op
// expected-note @below {{handle to invalidated ops}}
%2 = transform.test_copy_payload %0 : (!transform.any_op) -> !transform.any_op
// Consuming two handles in the same operation is invalid if they point
// to overlapping sets of payload IR ops.
//
// expected-error @below {{op uses a handle invalidated by a previously executed transform op}}
// expected-note @below {{invalidated by this transform op that consumes its operand #0 and invalidates all handles to payload IR entities}}
transform.test_consume_operand %1, %2 : !transform.any_op, !transform.any_op
transform.yield
}
}
// -----
// Deduplication attribute allows "merge_handles" to take repeated operands.
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%0: !transform.any_op) {
%1 = transform.test_copy_payload %0 : (!transform.any_op) -> !transform.any_op
%2 = transform.test_copy_payload %0 : (!transform.any_op) -> !transform.any_op
transform.merge_handles %1, %2 { deduplicate } : !transform.any_op
transform.yield
}
}
// -----
// expected-note @below {{payload value}}
%0 = "test.match_anchor"() : () -> (i32)
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg0: !transform.any_op) {
%2 = transform.structured.match ops{["test.match_anchor"]} in %arg0 : (!transform.any_op) -> !transform.any_op
%3 = transform.test_produce_value_handle_to_result %2, 0 : (!transform.any_op) -> !transform.any_value
// expected-note @below {{invalidated handle}}
%4 = transform.test_produce_value_handle_to_result %2, 0 : (!transform.any_op) -> !transform.any_value
// expected-note @below {{invalidated by this transform op that consumes its operand #0 and invalidates handles to the same values as associated with it}}
transform.test_consume_operand %3 : !transform.any_value
// expected-error @below {{op uses a handle invalidated by a previously executed transform op}}
transform.test_consume_operand %4 : !transform.any_value
transform.yield
}
}
// -----
// expected-note @below {{ancestor op associated with the consumed handle}}
// expected-note @below {{payload value}}
// expected-note @below {{op defining the value as result #0}}
%0 = "test.match_anchor"() : () -> (i32)
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg0: !transform.any_op) {
%2 = transform.structured.match ops{["test.match_anchor"]} in %arg0 : (!transform.any_op) -> !transform.any_op
// expected-note @below {{invalidated handle}}
%3 = transform.test_produce_value_handle_to_result %2, 0 : (!transform.any_op) -> !transform.any_value
// expected-note @below {{invalidated by this transform op that consumes its operand #0 and invalidates all handles to payload IR entities associated with this operand and entities nested in them}}
transform.test_consume_operand %2 : !transform.any_op
// expected-error @below {{op uses a handle invalidated by a previously executed transform op}}
transform.test_consume_operand %3 : !transform.any_value
transform.yield
}
}
// -----
// expected-note @below {{ancestor op associated with the consumed handle}}
"test.match_anchor_1"() ({
^bb0:
// expected-note @below {{op defining the value as result #0}}
// expected-note @below {{payload value}}
%0 = "test.match_anchor_2"() : () -> (i32)
"test.region_terminator"() : () -> ()
}) : () -> ()
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg0: !transform.any_op) {
%1 = transform.structured.match ops{["test.match_anchor_1"]} in %arg0 : (!transform.any_op) -> !transform.any_op
%2 = transform.structured.match ops{["test.match_anchor_2"]} in %arg0 : (!transform.any_op) -> !transform.any_op
// expected-note @below {{invalidated handle}}
%3 = transform.test_produce_value_handle_to_result %2, 0 : (!transform.any_op) -> !transform.any_value
// expected-note @below {{invalidated by this transform op that consumes its operand #0 and invalidates all handles to payload IR entities associated with this operand and entities nested in them}}
transform.test_consume_operand %1 : !transform.any_op
// expected-error @below {{op uses a handle invalidated by a previously executed transform op}}
transform.test_consume_operand %3 : !transform.any_value
transform.yield
}
}
// -----
// expected-note @below {{ancestor op associated with the consumed handle}}
// expected-note @below {{op defining the value as block argument #0 of block #0 in region #0}}
"test.match_anchor_1"() ({
// expected-note @below {{payload value}}
^bb0(%arg0: i32):
%0 = "test.match_anchor_2"() : () -> (i32)
"test.region_terminator"() : () -> ()
}) : () -> ()
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg0: !transform.any_op) {
%1 = transform.structured.match ops{["test.match_anchor_1"]} in %arg0 : (!transform.any_op) -> !transform.any_op
%2 = transform.structured.match ops{["test.match_anchor_2"]} in %arg0 : (!transform.any_op) -> !transform.any_op
// expected-note @below {{invalidated handle}}
%3 = transform.test_produce_value_handle_to_argument_of_parent_block %2, 0 : (!transform.any_op) -> !transform.any_value
// expected-note @below {{invalidated by this transform op that consumes its operand #0 and invalidates all handles to payload IR entities associated with this operand and entities nested in them}}
transform.test_consume_operand %1 : !transform.any_op
// expected-error @below {{op uses a handle invalidated by a previously executed transform op}}
transform.test_consume_operand %3 : !transform.any_value
transform.yield
}
}
// -----
// expected-note @below {{ancestor op associated with the consumed handle}}
"test.match_anchor_1"() ({
^bb:
// expected-note @below {{op defining the value as block argument #0 of block #0 in region #0}}
"test.op_with_regions"() ({
// expected-note @below {{payload value}}
^bb0(%arg0: i32):
%0 = "test.match_anchor_2"() : () -> (i32)
"test.region_terminator"() : () -> ()
}): () -> ()
}) : () -> ()
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg0: !transform.any_op) {
%1 = transform.structured.match ops{["test.match_anchor_1"]} in %arg0 : (!transform.any_op) -> !transform.any_op
%2 = transform.structured.match ops{["test.match_anchor_2"]} in %arg0 : (!transform.any_op) -> !transform.any_op
// expected-note @below {{invalidated handle}}
%3 = transform.test_produce_value_handle_to_argument_of_parent_block %2, 0 : (!transform.any_op) -> !transform.any_value
// expected-note @below {{invalidated by this transform op that consumes its operand #0 and invalidates all handles to payload IR entities associated with this operand and entities nested in them}}
transform.test_consume_operand %1 : !transform.any_op
// expected-error @below {{op uses a handle invalidated by a previously executed transform op}}
transform.test_consume_operand %3 : !transform.any_value
transform.yield
}
}
// -----
// expected-note @below {{ancestor payload op}}
// expected-note @below {{nested payload op}}
// expected-note @below {{consumed handle points to this payload value}}
%0 = "test.match_anchor"() : () -> (i32)
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg0: !transform.any_op) {
// expected-note @below {{handle to invalidated ops}}
%2 = transform.structured.match ops{["test.match_anchor"]} in %arg0 : (!transform.any_op) -> !transform.any_op
%3 = transform.test_produce_value_handle_to_result %2, 0 : (!transform.any_op) -> !transform.any_value
// expected-note @below {{invalidated by this transform op that consumes its operand #0 and invalidates all handles to payload IR entities associated with this operand and entities nested in them}}
transform.test_consume_operand %3 : !transform.any_value
// expected-error @below {{op uses a handle invalidated by a previously executed transform op}}
transform.test_consume_operand %2 : !transform.any_op
transform.yield
}
}
// -----
// expected-note @below {{ancestor payload op}}
// expected-note @below {{consumed handle points to this payload value}}
%0 = "test.match_anchor_1"() ({
^bb0:
// expected-note @below {{nested payload op}}
"test.match_anchor_2"() : () -> ()
"test.region_terminator"() : () -> ()
}) : () -> (i32)
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg0: !transform.any_op) {
%1 = transform.structured.match ops{["test.match_anchor_1"]} in %arg0 : (!transform.any_op) -> !transform.any_op
// expected-note @below {{handle to invalidated ops}}
%2 = transform.structured.match ops{["test.match_anchor_2"]} in %arg0 : (!transform.any_op) -> !transform.any_op
%3 = transform.test_produce_value_handle_to_result %1, 0 : (!transform.any_op) -> !transform.any_value
// expected-note @below {{invalidated by this transform op that consumes its operand #0 and invalidates all handles to payload IR entities associated with this operand and entities nested in them}}
transform.test_consume_operand %3 : !transform.any_value
// expected-error @below {{op uses a handle invalidated by a previously executed transform op}}
transform.test_consume_operand %2 : !transform.any_op
transform.yield
}
}
// -----
"test.match_anchor_1"() ({
// expected-note @below {{consumed handle points to this payload value}}
^bb0(%arg0: f32):
// expected-note @below {{ancestor payload op}}
// expected-note @below {{nested payload op}}
"test.match_anchor_2"() : () -> ()
"test.region_terminator"() : () -> ()
}) : () -> ()
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg0: !transform.any_op) {
// expected-note @below {{handle to invalidated ops}}
%2 = transform.structured.match ops{["test.match_anchor_2"]} in %arg0 : (!transform.any_op) -> !transform.any_op
%3 = transform.test_produce_value_handle_to_argument_of_parent_block %2, 0 : (!transform.any_op) -> !transform.any_value
// expected-note @below {{invalidated by this transform op that consumes its operand #0 and invalidates all handles to payload IR entities associated with this operand and entities nested in them}}
transform.test_consume_operand %3 : !transform.any_value
// expected-error @below {{op uses a handle invalidated by a previously executed transform op}}
transform.test_consume_operand %2 : !transform.any_op
transform.yield
}
}
// -----
"test.op_with_regions"() ({
// expected-note @below {{consumed handle points to this payload value}}
^bb(%arg0: i32):
// expected-note @below {{ancestor payload op}}
"test.op_with_regions"() ({
^bb0:
// expected-note @below {{nested payload op}}
"test.match_anchor_2"() : () -> ()
"test.region_terminator"() : () -> ()
}): () -> ()
"test.match_anchor_1"() : () -> ()
}) : () -> ()
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg0: !transform.any_op) {
%1 = transform.structured.match ops{["test.match_anchor_1"]} in %arg0 : (!transform.any_op) -> !transform.any_op
// expected-note @below {{handle to invalidated ops}}
%2 = transform.structured.match ops{["test.match_anchor_2"]} in %arg0 : (!transform.any_op) -> !transform.any_op
%3 = transform.test_produce_value_handle_to_argument_of_parent_block %1, 0 : (!transform.any_op) -> !transform.any_value
// expected-note @below {{invalidated by this transform op that consumes its operand #0 and invalidates all handles to payload IR entities associated with this operand and entities nested in them}}
transform.test_consume_operand %3 : !transform.any_value
// expected-error @below {{op uses a handle invalidated by a previously executed transform op}}
transform.test_consume_operand %2 : !transform.any_op
transform.yield
}
}
// -----
// Removing a block argument does not invalidate handles to operations in another block.
// Not expecting an error here.
"test.op_with_regions"() ({
^bb1(%arg0: i32):
"test.match_anchor_1"() : () -> ()
^bb2:
"test.match_anchor_2"() : () -> ()
}) : () -> ()
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg0: !transform.any_op) {
%1 = transform.structured.match ops{["test.match_anchor_1"]} in %arg0 : (!transform.any_op) -> !transform.any_op
%2 = transform.structured.match ops{["test.match_anchor_2"]} in %arg0 : (!transform.any_op) -> !transform.any_op
%3 = transform.test_produce_value_handle_to_argument_of_parent_block %1, 0 : (!transform.any_op) -> !transform.any_value
transform.test_consume_operand %3 : !transform.any_value
transform.test_consume_operand %2 : !transform.any_op
transform.yield
}
}
// -----
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg0: !transform.any_op) {
%0 = transform.test_produce_empty_payload : !transform.any_op
// expected-note @below {{invalidated by this transform op that consumes its operand #0}}
transform.test_consume_operand %0 : !transform.any_op
// expected-error @below {{uses a handle associated with empty payload and invalidated by a previously executed transform op}}
transform.debug.emit_remark_at %0, "remark" : !transform.any_op
transform.yield
}
}
// -----
// Make sure we properly report a use-after-consume error when repeated handles
// are allowed in the consuming op. We still want to report handles consumed by
// _previous_ operations, just not by this one. To bypass the quick static check
// of repeated consumption, create a handle to the transform operation and
// invalidate the handle to the root module thus invalidating all other handles.
// expected-note @below {{ancestor payload op}}
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg0: !transform.any_op) {
// expected-note @below {{handle to invalidated ops}}
// expected-note @below {{nested payload op}}
%0 = transform.test_produce_self_handle_or_forward_operand : () -> !transform.any_op
// expected-note @below {{invalidated by this transform op that consumes its operand #0 and invalidates all handles to payload IR entities associated with this operand and entities nested in them}}
transform.test_consume_operand %arg0 : !transform.any_op
// expected-error @below {{uses a handle invalidated by a previously executed transform op}}
transform.test_consume_operand %0 { allow_repeated_handles } : !transform.any_op
transform.yield
}
}
// -----
// Re-entering the region should not trigger the consumption error from previous
// execution of the region.
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg0: !transform.any_op) {
transform.test_re_enter_region {
%0 = transform.test_produce_self_handle_or_forward_operand : () -> !transform.any_op
transform.test_consume_operand %0 : !transform.any_op
transform.yield
}
transform.yield
}
}
// -----
// Re-entering the region should not trigger the consumption error from previous
// execution of the region.
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg0: !transform.any_op) {
%0 = transform.test_produce_self_handle_or_forward_operand : () -> !transform.any_op
transform.test_re_enter_region %0 : !transform.any_op {
^bb0(%arg1: !transform.any_op):
transform.test_consume_operand %arg1 : !transform.any_op
transform.yield
}
transform.yield
}
}
// -----
// Consuming the same handle repeatedly in the region should trigger an error.
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg0: !transform.any_op) {
// expected-note @below {{payload op}}
// expected-note @below {{handle to invalidated ops}}
%0 = transform.test_produce_self_handle_or_forward_operand : () -> !transform.any_op
transform.test_re_enter_region {
// expected-error @below {{op uses a handle invalidated by a previously executed transform op}}
// expected-note @below {{invalidated by this transform op}}
transform.test_consume_operand %0 : !transform.any_op
transform.yield
}
transform.yield
}
}
// -----
module @named_inclusion_and_consumption attributes { transform.with_named_sequence } {
transform.named_sequence @foo(%arg0: !transform.any_op {transform.consumed}) -> () {
// Consuming this handle removes the mapping from the current stack frame
// mapping and from the caller's stack frame mapping. (If this were not
// be the case, the "expensive checks" caching mechanism for op names
// would throw an error saying that an op is mapped but not in the cache.)
transform.test_consume_operand %arg0 : !transform.any_op
transform.yield
}
transform.named_sequence @__transform_main(%arg0: !transform.any_op) {
transform.include @foo failures(propagate) (%arg0) : (!transform.any_op) -> ()
transform.yield
}
}