| // RUN: mlir-opt %s --test-walk-pattern-rewrite-driver="dump-notifications=true" \ |
| // RUN: --allow-unregistered-dialect --split-input-file | FileCheck %s |
| |
| // The following op is updated in-place and will not be added back to the worklist. |
| // CHECK-LABEL: func.func @inplace_update() |
| // CHECK: "test.any_attr_of_i32_str"() <{attr = 1 : i32}> : () -> () |
| // CHECK: "test.any_attr_of_i32_str"() <{attr = 2 : i32}> : () -> () |
| func.func @inplace_update() { |
| "test.any_attr_of_i32_str"() {attr = 0 : i32} : () -> () |
| "test.any_attr_of_i32_str"() {attr = 1 : i32} : () -> () |
| return |
| } |
| |
| // Check that the driver does not fold visited ops. |
| // CHECK-LABEL: func.func @add_no_fold() |
| // CHECK: arith.constant |
| // CHECK: arith.constant |
| // CHECK: %[[RES:.+]] = arith.addi |
| // CHECK: return %[[RES]] |
| func.func @add_no_fold() -> i32 { |
| %c0 = arith.constant 0 : i32 |
| %c1 = arith.constant 1 : i32 |
| %res = arith.addi %c0, %c1 : i32 |
| return %res : i32 |
| } |
| |
| // Check that the driver handles rewriter.moveBefore. |
| // CHECK-LABEL: func.func @move_before( |
| // CHECK: "test.move_before_parent_op" |
| // CHECK: "test.any_attr_of_i32_str"() <{attr = 1 : i32}> : () -> () |
| // CHECK: scf.if |
| // CHECK: return |
| func.func @move_before(%cond : i1) { |
| scf.if %cond { |
| "test.move_before_parent_op"() ({ |
| "test.any_attr_of_i32_str"() {attr = 0 : i32} : () -> () |
| }) : () -> () |
| } |
| return |
| } |
| |
| // Check that the driver handles rewriter.moveAfter. In this case, we expect |
| // the moved op to be visited twice. |
| // CHECK-LABEL: func.func @move_after( |
| // CHECK: scf.if |
| // CHECK: } |
| // CHECK: "test.move_after_parent_op" |
| // CHECK: "test.any_attr_of_i32_str"() <{attr = 2 : i32}> : () -> () |
| // CHECK: return |
| func.func @move_after(%cond : i1) { |
| scf.if %cond { |
| "test.move_after_parent_op"() ({ |
| "test.any_attr_of_i32_str"() {attr = 0 : i32} : () -> () |
| }) : () -> () |
| } |
| return |
| } |
| |
| // Check that the driver handles rewriter.moveAfter. In this case, we expect |
| // the moved op to be visited twice since we advance its position to the next |
| // node after the parent. |
| // CHECK-LABEL: func.func @move_forward_and_revisit( |
| // CHECK: scf.if |
| // CHECK: } |
| // CHECK: arith.addi |
| // CHECK: "test.move_after_parent_op" |
| // CHECK: "test.any_attr_of_i32_str"() <{attr = 2 : i32}> : () -> () |
| // CHECK: arith.addi |
| // CHECK: return |
| func.func @move_forward_and_revisit(%cond : i1) { |
| scf.if %cond { |
| "test.move_after_parent_op"() ({ |
| "test.any_attr_of_i32_str"() {attr = 0 : i32} : () -> () |
| }) {advance = 1 : i32} : () -> () |
| } |
| %a = arith.addi %cond, %cond : i1 |
| %b = arith.addi %a, %cond : i1 |
| return |
| } |
| |
| // Operation inserted just after the currently visited one won't be visited. |
| // CHECK-LABEL: func.func @insert_just_after |
| // CHECK: "test.clone_me"() ({ |
| // CHECK: "test.any_attr_of_i32_str"() <{attr = 1 : i32}> : () -> () |
| // CHECK: }) {was_cloned} : () -> () |
| // CHECK: "test.clone_me"() ({ |
| // CHECK: "test.any_attr_of_i32_str"() <{attr = 1 : i32}> : () -> () |
| // CHECK: }) : () -> () |
| // CHECK: return |
| func.func @insert_just_after(%cond : i1) { |
| "test.clone_me"() ({ |
| "test.any_attr_of_i32_str"() {attr = 0 : i32} : () -> () |
| }) : () -> () |
| return |
| } |
| |
| // Check that we can replace the current operation with a new one. |
| // Note that the new op won't be visited. |
| // CHECK-LABEL: func.func @replace_with_new_op |
| // CHECK: %[[NEW:.+]] = "test.new_op" |
| // CHECK: %[[RES:.+]] = arith.addi %[[NEW]], %[[NEW]] |
| // CHECK: return %[[RES]] |
| func.func @replace_with_new_op() -> i32 { |
| %a = "test.replace_with_new_op"() : () -> (i32) |
| %res = arith.addi %a, %a : i32 |
| return %res : i32 |
| } |
| |
| // Check that we can erase nested blocks. |
| // CHECK-LABEL: func.func @erase_nested_block |
| // CHECK: %[[RES:.+]] = "test.erase_first_block" |
| // CHECK-NEXT: foo.bar |
| // CHECK: return %[[RES]] |
| func.func @erase_nested_block() -> i32 { |
| %a = "test.erase_first_block"() ({ |
| "foo.foo"() : () -> () |
| ^bb1: |
| "foo.bar"() : () -> () |
| }): () -> (i32) |
| return %a : i32 |
| } |
| |
| |
| // CHECK-LABEL: func.func @unreachable_replace_with_new_op |
| // CHECK: "test.new_op" |
| // CHECK: "test.replace_with_new_op" |
| // CHECK-SAME: unreachable |
| // CHECK: "test.new_op" |
| func.func @unreachable_replace_with_new_op() { |
| "test.br"()[^bb1] : () -> () |
| ^bb1: |
| %a = "test.replace_with_new_op"() : () -> (i32) |
| "test.br"()[^end] : () -> () // Test jumping over the unreachable block is visited as well. |
| ^unreachable: |
| %b = "test.replace_with_new_op"() {test.unreachable} : () -> (i32) |
| return |
| ^end: |
| %c = "test.replace_with_new_op"() : () -> (i32) |
| return |
| } |
| |