| // 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 | |
| } |