| // RUN: mlir-opt \ |
| // RUN: -test-strict-pattern-driver="strictness=AnyOp" \ |
| // RUN: --split-input-file %s | FileCheck %s --check-prefix=CHECK-AN |
| |
| // RUN: mlir-opt \ |
| // RUN: -test-strict-pattern-driver="strictness=ExistingAndNewOps" \ |
| // RUN: --split-input-file %s | FileCheck %s --check-prefix=CHECK-EN |
| |
| // RUN: mlir-opt \ |
| // RUN: -test-strict-pattern-driver="strictness=ExistingOps" \ |
| // RUN: --split-input-file %s | FileCheck %s --check-prefix=CHECK-EX |
| |
| // CHECK-EN-LABEL: func @test_erase |
| // CHECK-EN-SAME: pattern_driver_all_erased = true, pattern_driver_changed = true} |
| // CHECK-EN: "test.arg0" |
| // CHECK-EN: "test.arg1" |
| // CHECK-EN-NOT: "test.erase_op" |
| func.func @test_erase() { |
| %0 = "test.arg0"() : () -> (i32) |
| %1 = "test.arg1"() : () -> (i32) |
| %erase = "test.erase_op"(%0, %1) : (i32, i32) -> (i32) |
| return |
| } |
| |
| // ----- |
| |
| // CHECK-EN: notifyOperationInserted: test.insert_same_op, was unlinked |
| // CHECK-EN-LABEL: func @test_insert_same_op |
| // CHECK-EN-SAME: {pattern_driver_all_erased = false, pattern_driver_changed = true} |
| // CHECK-EN: "test.insert_same_op"() {skip = true} |
| // CHECK-EN: "test.insert_same_op"() {skip = true} |
| func.func @test_insert_same_op() { |
| %0 = "test.insert_same_op"() : () -> (i32) |
| return |
| } |
| |
| // ----- |
| |
| // CHECK-EN: notifyOperationInserted: test.new_op, was unlinked |
| // CHECK-EN-LABEL: func @test_replace_with_new_op |
| // CHECK-EN-SAME: {pattern_driver_all_erased = true, pattern_driver_changed = true} |
| // CHECK-EN: %[[n:.*]] = "test.new_op" |
| // CHECK-EN: "test.dummy_user"(%[[n]]) |
| // CHECK-EN: "test.dummy_user"(%[[n]]) |
| func.func @test_replace_with_new_op() { |
| %0 = "test.replace_with_new_op"() : () -> (i32) |
| %1 = "test.dummy_user"(%0) : (i32) -> (i32) |
| %2 = "test.dummy_user"(%0) : (i32) -> (i32) |
| return |
| } |
| |
| // ----- |
| |
| // CHECK-EN: notifyOperationInserted: test.erase_op, was unlinked |
| // CHECK-EN: notifyOperationErased: test.replace_with_new_op |
| // CHECK-EN: notifyOperationErased: test.erase_op |
| // CHECK-EN-LABEL: func @test_replace_with_erase_op |
| // CHECK-EN-SAME: {pattern_driver_all_erased = true, pattern_driver_changed = true} |
| // CHECK-EN-NOT: "test.replace_with_new_op" |
| // CHECK-EN-NOT: "test.erase_op" |
| |
| // CHECK-EX-LABEL: func @test_replace_with_erase_op |
| // CHECK-EX-SAME: {pattern_driver_all_erased = true, pattern_driver_changed = true} |
| // CHECK-EX-NOT: "test.replace_with_new_op" |
| // CHECK-EX: "test.erase_op" |
| func.func @test_replace_with_erase_op() { |
| "test.replace_with_new_op"() {create_erase_op} : () -> () |
| return |
| } |
| |
| // ----- |
| |
| // CHECK-AN-LABEL: func @test_trigger_rewrite_through_block |
| // CHECK-AN: "test.change_block_op"()[^[[BB0:.*]], ^[[BB0]]] |
| // CHECK-AN: return |
| // CHECK-AN: ^[[BB1:[^:]*]]: |
| // CHECK-AN: "test.implicit_change_op"()[^[[BB1]]] |
| func.func @test_trigger_rewrite_through_block() { |
| return |
| ^bb1: |
| // Uses bb1. ChangeBlockOp replaces that and all other usages of bb1 with bb2. |
| "test.change_block_op"() [^bb1, ^bb2] : () -> () |
| ^bb2: |
| return |
| ^bb3: |
| // Also uses bb1. ChangeBlockOp replaces that usage with bb2. This triggers |
| // this op being put on the worklist, which triggers ImplicitChangeOp, which, |
| // in turn, replaces the successor with bb3. |
| "test.implicit_change_op"() [^bb1] : () -> () |
| } |
| |
| // ----- |
| |
| // CHECK-AN: notifyOperationErased: test.foo_b |
| // CHECK-AN: notifyOperationErased: test.foo_a |
| // CHECK-AN: notifyOperationErased: test.graph_region |
| // CHECK-AN: notifyOperationErased: test.erase_op |
| // CHECK-AN-LABEL: func @test_remove_graph_region() |
| // CHECK-AN-NEXT: return |
| func.func @test_remove_graph_region() { |
| "test.erase_op"() ({ |
| test.graph_region { |
| %0 = "test.foo_a"(%1) : (i1) -> (i1) |
| %1 = "test.foo_b"(%0) : (i1) -> (i1) |
| } |
| }) : () -> () |
| return |
| } |
| |
| // ----- |
| |
| // CHECK-AN: notifyOperationErased: cf.br |
| // CHECK-AN: notifyOperationErased: test.bar |
| // CHECK-AN: notifyOperationErased: cf.br |
| // CHECK-AN: notifyOperationErased: test.foo |
| // CHECK-AN: notifyOperationErased: cf.br |
| // CHECK-AN: notifyOperationErased: test.dummy_op |
| // CHECK-AN: notifyOperationErased: test.erase_op |
| // CHECK-AN-LABEL: func @test_remove_cyclic_blocks() |
| // CHECK-AN-NEXT: return |
| func.func @test_remove_cyclic_blocks() { |
| "test.erase_op"() ({ |
| %x = "test.dummy_op"() : () -> (i1) |
| cf.br ^bb1(%x: i1) |
| ^bb1(%arg0: i1): |
| "test.foo"(%x) : (i1) -> () |
| cf.br ^bb2(%arg0: i1) |
| ^bb2(%arg1: i1): |
| "test.bar"(%x) : (i1) -> () |
| cf.br ^bb1(%arg1: i1) |
| }) : () -> () |
| return |
| } |
| |
| // ----- |
| |
| // CHECK-AN: notifyOperationErased: test.dummy_op |
| // CHECK-AN: notifyOperationErased: test.bar |
| // CHECK-AN: notifyOperationErased: test.qux |
| // CHECK-AN: notifyOperationErased: test.qux_unreachable |
| // CHECK-AN: notifyOperationErased: test.nested_dummy |
| // CHECK-AN: notifyOperationErased: cf.br |
| // CHECK-AN: notifyOperationErased: test.foo |
| // CHECK-AN: notifyOperationErased: test.erase_op |
| // CHECK-AN-LABEL: func @test_remove_dead_blocks() |
| // CHECK-AN-NEXT: return |
| func.func @test_remove_dead_blocks() { |
| "test.erase_op"() ({ |
| "test.dummy_op"() : () -> (i1) |
| // The following blocks are not reachable. Still, ^bb2 should be deleted |
| // befire ^bb1. |
| ^bb1(%arg0: i1): |
| "test.foo"() : () -> () |
| cf.br ^bb2(%arg0: i1) |
| ^bb2(%arg1: i1): |
| "test.nested_dummy"() ({ |
| "test.qux"() : () -> () |
| // The following block is unreachable. |
| ^bb3: |
| "test.qux_unreachable"() : () -> () |
| }) : () -> () |
| "test.bar"() : () -> () |
| }) : () -> () |
| return |
| } |
| |
| // ----- |
| |
| // test.nested_* must be deleted before test.foo. |
| // test.bar must be deleted before test.foo. |
| |
| // CHECK-AN: notifyOperationErased: cf.br |
| // CHECK-AN: notifyOperationErased: test.bar |
| // CHECK-AN: notifyOperationErased: cf.br |
| // CHECK-AN: notifyOperationErased: test.nested_b |
| // CHECK-AN: notifyOperationErased: test.nested_a |
| // CHECK-AN: notifyOperationErased: test.nested_d |
| // CHECK-AN: notifyOperationErased: cf.br |
| // CHECK-AN: notifyOperationErased: test.nested_e |
| // CHECK-AN: notifyOperationErased: cf.br |
| // CHECK-AN: notifyOperationErased: test.nested_c |
| // CHECK-AN: notifyOperationErased: test.foo |
| // CHECK-AN: notifyOperationErased: cf.br |
| // CHECK-AN: notifyOperationErased: test.dummy_op |
| // CHECK-AN: notifyOperationErased: test.erase_op |
| // CHECK-AN-LABEL: func @test_remove_nested_ops() |
| // CHECK-AN-NEXT: return |
| func.func @test_remove_nested_ops() { |
| "test.erase_op"() ({ |
| %x = "test.dummy_op"() : () -> (i1) |
| cf.br ^bb1(%x: i1) |
| ^bb1(%arg0: i1): |
| "test.foo"() ({ |
| "test.nested_a"() : () -> () |
| "test.nested_b"() : () -> () |
| ^dead1: |
| "test.nested_c"() : () -> () |
| cf.br ^dead3 |
| ^dead2: |
| "test.nested_d"() : () -> () |
| ^dead3: |
| "test.nested_e"() : () -> () |
| cf.br ^dead2 |
| }) : () -> () |
| cf.br ^bb2(%arg0: i1) |
| ^bb2(%arg1: i1): |
| "test.bar"(%x) : (i1) -> () |
| cf.br ^bb1(%arg1: i1) |
| }) : () -> () |
| return |
| } |
| |
| // ----- |
| |
| // CHECK-AN: notifyOperationErased: test.qux |
| // CHECK-AN: notifyOperationErased: cf.br |
| // CHECK-AN: notifyOperationErased: test.foo |
| // CHECK-AN: notifyOperationErased: cf.br |
| // CHECK-AN: notifyOperationErased: test.bar |
| // CHECK-AN: notifyOperationErased: cf.cond_br |
| // CHECK-AN-LABEL: func @test_remove_diamond( |
| // CHECK-AN-NEXT: return |
| func.func @test_remove_diamond(%c: i1) { |
| "test.erase_op"() ({ |
| cf.cond_br %c, ^bb1, ^bb2 |
| ^bb1: |
| "test.foo"() : () -> () |
| cf.br ^bb3 |
| ^bb2: |
| "test.bar"() : () -> () |
| cf.br ^bb3 |
| ^bb3: |
| "test.qux"() : () -> () |
| }) : () -> () |
| return |
| } |
| |
| // ----- |
| |
| // CHECK-AN: notifyOperationInserted: test.move_before_parent_op, previous = test.dummy_terminator |
| // CHECK-AN-LABEL: func @test_move_op_before( |
| // CHECK-AN: test.move_before_parent_op |
| // CHECK-AN: test.op_with_region |
| // CHECK-AN: test.dummy_terminator |
| func.func @test_move_op_before() { |
| "test.op_with_region"() ({ |
| "test.move_before_parent_op"() : () -> () |
| "test.dummy_terminator"() : () ->() |
| }) : () -> () |
| return |
| } |
| |
| // ----- |
| |
| // CHECK-AN: notifyOperationInserted: test.op_1, previous = test.op_2 |
| // CHECK-AN: notifyOperationInserted: test.op_2, previous = test.op_3 |
| // CHECK-AN: notifyOperationInserted: test.op_3, was last in block |
| // CHECK-AN-LABEL: func @test_inline_block_before( |
| // CHECK-AN: test.op_1 |
| // CHECK-AN: test.op_2 |
| // CHECK-AN: test.op_3 |
| // CHECK-AN: test.inline_blocks_into_parent |
| // CHECK-AN: return |
| func.func @test_inline_block_before() { |
| "test.inline_blocks_into_parent"() ({ |
| "test.op_1"() : () -> () |
| "test.op_2"() : () -> () |
| "test.op_3"() : () -> () |
| }) : () -> () |
| return |
| } |
| |
| // ----- |
| |
| // CHECK-AN: notifyBlockInserted into test.op_with_region: was unlinked |
| // CHECK-AN: notifyOperationInserted: test.op_3, was last in block |
| // CHECK-AN: notifyOperationInserted: test.op_2, was last in block |
| // CHECK-AN: notifyOperationInserted: test.split_block_here, was last in block |
| // CHECK-AN: notifyOperationInserted: test.new_op, was unlinked |
| // CHECK-AN: notifyOperationErased: test.split_block_here |
| // CHECK-AN-LABEL: func @test_split_block( |
| // CHECK-AN: "test.op_with_region"() ({ |
| // CHECK-AN: test.op_1 |
| // CHECK-AN: ^{{.*}}: |
| // CHECK-AN: test.new_op |
| // CHECK-AN: test.op_2 |
| // CHECK-AN: test.op_3 |
| // CHECK-AN: }) : () -> () |
| func.func @test_split_block() { |
| "test.op_with_region"() ({ |
| "test.op_1"() : () -> () |
| "test.split_block_here"() : () -> () |
| "test.op_2"() : () -> () |
| "test.op_3"() : () -> () |
| }) : () -> () |
| return |
| } |
| |
| // ----- |
| |
| // CHECK-AN: notifyOperationInserted: test.clone_me, was unlinked |
| // CHECK-AN: notifyBlockInserted into test.clone_me: was unlinked |
| // CHECK-AN: notifyBlockInserted into test.clone_me: was unlinked |
| // CHECK-AN: notifyOperationInserted: test.foo, was unlinked |
| // CHECK-AN: notifyOperationInserted: test.bar, was unlinked |
| // CHECK-AN-LABEL: func @clone_op( |
| // CHECK-AN: "test.clone_me"() ({ |
| // CHECK-AN: "test.foo"() : () -> () |
| // CHECK-AN: ^bb1: // no predecessors |
| // CHECK-AN: "test.bar"() : () -> () |
| // CHECK-AN: }) {was_cloned} : () -> () |
| // CHECK-AN: "test.clone_me"() ({ |
| // CHECK-AN: "test.foo"() : () -> () |
| // CHECK-AN: ^bb1: // no predecessors |
| // CHECK-AN: "test.bar"() : () -> () |
| // CHECK-AN: }) : () -> () |
| func.func @clone_op() { |
| "test.clone_me"() ({ |
| ^bb0: |
| "test.foo"() : () -> () |
| ^bb1: |
| "test.bar"() : () -> () |
| }) : () -> () |
| return |
| } |
| |
| |
| // ----- |
| |
| // CHECK-AN: notifyBlockInserted into func.func: was unlinked |
| // CHECK-AN: notifyOperationInserted: test.op_1, was unlinked |
| // CHECK-AN: notifyBlockInserted into func.func: was unlinked |
| // CHECK-AN: notifyOperationInserted: test.op_2, was unlinked |
| // CHECK-AN: notifyBlockInserted into test.op_2: was unlinked |
| // CHECK-AN: notifyOperationInserted: test.op_3, was unlinked |
| // CHECK-AN: notifyOperationInserted: test.op_4, was unlinked |
| // CHECK-AN-LABEL: func @test_clone_region_before( |
| // CHECK-AN: "test.op_1"() : () -> () |
| // CHECK-AN: ^{{.*}}: |
| // CHECK-AN: "test.op_2"() ({ |
| // CHECK-AN: "test.op_3"() : () -> () |
| // CHECK-AN: }) : () -> () |
| // CHECK-AN: "test.op_4"() : () -> () |
| // CHECK-AN: ^{{.*}}: |
| // CHECK-AN: "test.clone_region_before"() ({ |
| func.func @test_clone_region_before() { |
| "test.clone_region_before"() ({ |
| "test.op_1"() : () -> () |
| ^bb0: |
| "test.op_2"() ({ |
| "test.op_3"() : () -> () |
| }) : () -> () |
| "test.op_4"() : () -> () |
| }) : () -> () |
| return |
| } |