// 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) -> (%1 : !pdl.range) 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) -> (%1 : !pdl.range) rewrite %2 with "transform.dialect" } pdl.pattern @return : benefit(1) { %0 = operands %1 = types %2 = operation "func.return"(%0 : !pdl.range) -> (%1 : !pdl.range) 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 } }