// RUN: mlir-opt %s --transform-dialect-check-uses --split-input-file --verify-diagnostics func.func @use_after_free_branching_control_flow() { // expected-note @below {{allocated here}} %0 = transform.test_produce_self_handle_or_forward_operand : () -> !transform.any_op transform.test_transform_op_with_regions { "transform.test_branching_transform_op_terminator"() : () -> () }, { ^bb0: "transform.test_branching_transform_op_terminator"()[^bb1, ^bb2] : () -> () ^bb1: // expected-note @below {{freed here}} transform.test_consume_operand_of_op_kind_or_fail %0, "transform.test_produce_self_handle_or_forward_operand" : !transform.any_op "transform.test_branching_transform_op_terminator"()[^bb3] : () -> () ^bb2: "transform.test_branching_transform_op_terminator"()[^bb3] : () -> () ^bb3: // expected-warning @below {{operand #0 may be used after free}} transform.sequence %0 : !transform.any_op failures(propagate) { ^bb0(%arg0: !transform.any_op): } "transform.test_branching_transform_op_terminator"() : () -> () } return } // ----- func.func @use_after_free_in_nested_op() { // expected-note @below {{allocated here}} %0 = transform.test_produce_self_handle_or_forward_operand : () -> !transform.any_op // expected-note @below {{freed here}} transform.test_transform_op_with_regions { "transform.test_branching_transform_op_terminator"() : () -> () }, { ^bb0: "transform.test_branching_transform_op_terminator"()[^bb1, ^bb2] : () -> () ^bb1: transform.test_consume_operand_of_op_kind_or_fail %0, "transform.test_produce_self_handle_or_forward_operand" : !transform.any_op "transform.test_branching_transform_op_terminator"()[^bb3] : () -> () ^bb2: "transform.test_branching_transform_op_terminator"()[^bb3] : () -> () ^bb3: "transform.test_branching_transform_op_terminator"() : () -> () } // expected-warning @below {{operand #0 may be used after free}} transform.sequence %0 : !transform.any_op failures(propagate) { ^bb0(%arg0: !transform.any_op): } return } // ----- func.func @use_after_free_recursive_side_effects() { transform.sequence failures(propagate) { ^bb0(%arg0: !transform.any_op): // expected-note @below {{allocated here}} %0 = transform.sequence %arg0 : !transform.any_op -> !transform.any_op failures(propagate) attributes { ord = 1 } { ^bb1(%arg1: !transform.any_op): yield %arg1 : !transform.any_op } transform.sequence %0 : !transform.any_op failures(propagate) attributes { ord = 2 } { ^bb2(%arg2: !transform.any_op): } transform.sequence %0 : !transform.any_op failures(propagate) attributes { ord = 3 } { ^bb3(%arg3: !transform.any_op): } // `transform.sequence` has recursive side effects so it has the same "free" // as the child op it contains. // expected-note @below {{freed here}} transform.sequence %0 : !transform.any_op failures(propagate) attributes { ord = 4 } { ^bb4(%arg4: !transform.any_op): test_consume_operand_of_op_kind_or_fail %0, "transform.sequence" : !transform.any_op } // expected-warning @below {{operand #0 may be used after free}} transform.sequence %0 : !transform.any_op failures(propagate) attributes { ord = 5 } { ^bb3(%arg3: !transform.any_op): } } return } // ----- func.func @use_after_free() { transform.sequence failures(propagate) { ^bb0(%arg0: !transform.any_op): // expected-note @below {{allocated here}} %0 = transform.sequence %arg0 : !transform.any_op -> !transform.any_op failures(propagate) attributes { ord = 1 } { ^bb1(%arg1: !transform.any_op): yield %arg1 : !transform.any_op } transform.sequence %0 : !transform.any_op failures(propagate) attributes { ord = 2 } { ^bb2(%arg2: !transform.any_op): } transform.sequence %0 : !transform.any_op failures(propagate) attributes { ord = 3 } { ^bb3(%arg3: !transform.any_op): } // expected-note @below {{freed here}} test_consume_operand_of_op_kind_or_fail %0, "transform.sequence" : !transform.any_op // expected-warning @below {{operand #0 may be used after free}} transform.sequence %0 : !transform.any_op failures(propagate) attributes { ord = 5 } { ^bb3(%arg3: !transform.any_op): } } return } // ----- // In the case of a control flow cycle, the operation that uses the value may // precede the one that frees it in the same block. Both operations should // be reported as use-after-free. func.func @use_after_free_self_cycle() { // expected-note @below {{allocated here}} %0 = transform.test_produce_self_handle_or_forward_operand : () -> !transform.any_op transform.test_transform_op_with_regions { "transform.test_branching_transform_op_terminator"() : () -> () }, { ^bb0: "transform.test_branching_transform_op_terminator"()[^bb1] : () -> () ^bb1: // expected-warning @below {{operand #0 may be used after free}} transform.sequence %0 : !transform.any_op failures(propagate) { ^bb0(%arg0: !transform.any_op): } // expected-warning @below {{operand #0 may be used after free}} // expected-note @below {{freed here}} transform.test_consume_operand_of_op_kind_or_fail %0, "transform.test_produce_self_handle_or_forward_operand" : !transform.any_op "transform.test_branching_transform_op_terminator"()[^bb1, ^bb2] : () -> () ^bb2: "transform.test_branching_transform_op_terminator"() : () -> () } return } // ----- // Check that the "free" that happens in a cycle is also reported as potential // use-after-free. func.func @use_after_free_cycle() { // expected-note @below {{allocated here}} %0 = transform.test_produce_self_handle_or_forward_operand : () -> !transform.any_op transform.test_transform_op_with_regions { "transform.test_branching_transform_op_terminator"() : () -> () }, { ^bb0: "transform.test_branching_transform_op_terminator"()[^bb1, ^bb2] : () -> () ^bb1: // expected-warning @below {{operand #0 may be used after free}} // expected-note @below {{freed here}} transform.test_consume_operand_of_op_kind_or_fail %0, "transform.test_produce_self_handle_or_forward_operand" : !transform.any_op "transform.test_branching_transform_op_terminator"()[^bb2, ^bb3] : () -> () ^bb2: "transform.test_branching_transform_op_terminator"()[^bb1] : () -> () ^bb3: "transform.test_branching_transform_op_terminator"() : () -> () } return } // ----- // This should not crash. transform.sequence failures(propagate) { ^bb0(%arg0: !transform.any_op): alternatives %arg0 : !transform.any_op { ^bb0(%arg1: !transform.any_op): } }