| // RUN: mlir-opt -test-dead-code-analysis 2>&1 %s | FileCheck %s |
| |
| // CHECK: test_cfg: |
| // CHECK: region #0 |
| // CHECK: ^bb0 = live |
| // CHECK: ^bb1 = live |
| // CHECK: from ^bb1 = live |
| // CHECK: from ^bb0 = live |
| // CHECK: ^bb2 = live |
| // CHECK: from ^bb1 = live |
| func.func @test_cfg(%cond: i1) -> () |
| attributes {tag = "test_cfg"} { |
| cf.br ^bb1 |
| |
| ^bb1: |
| cf.cond_br %cond, ^bb1, ^bb2 |
| |
| ^bb2: |
| return |
| } |
| |
| func.func @test_region_control_flow(%cond: i1, %arg0: i64, %arg1: i64) -> () { |
| // CHECK: test_if: |
| // CHECK: region #0 |
| // CHECK: region_preds: (all) predecessors: |
| // CHECK: scf.if |
| // CHECK: region #1 |
| // CHECK: region_preds: (all) predecessors: |
| // CHECK: scf.if |
| // CHECK: op_preds: (all) predecessors: |
| // CHECK: scf.yield {then} |
| // CHECK: scf.yield {else} |
| scf.if %cond { |
| scf.yield {then} |
| } else { |
| scf.yield {else} |
| } {tag = "test_if"} |
| |
| // test_while: |
| // region #0 |
| // region_preds: (all) predecessors: |
| // scf.while |
| // scf.yield |
| // region #1 |
| // region_preds: (all) predecessors: |
| // scf.condition |
| // op_preds: (all) predecessors: |
| // scf.condition |
| %c2_i64 = arith.constant 2 : i64 |
| %0:2 = scf.while (%arg2 = %arg0) : (i64) -> (i64, i64) { |
| %1 = arith.cmpi slt, %arg2, %arg1 : i64 |
| scf.condition(%1) %arg2, %arg2 : i64, i64 |
| } do { |
| ^bb0(%arg2: i64, %arg3: i64): |
| %1 = arith.muli %arg3, %c2_i64 : i64 |
| scf.yield %1 : i64 |
| } attributes {tag = "test_while"} |
| |
| return |
| } |
| |
| // CHECK: foo: |
| // CHECK: region #0 |
| // CHECK: ^bb0 = live |
| // CHECK: op_preds: (all) predecessors: |
| // CHECK: func.call @foo(%{{.*}}) {tag = "a"} |
| // CHECK: func.call @foo(%{{.*}}) {tag = "b"} |
| func.func private @foo(%arg0: i32) -> i32 |
| attributes {tag = "foo"} { |
| return {a} %arg0 : i32 |
| } |
| |
| // CHECK: bar: |
| // CHECK: region #0 |
| // CHECK: ^bb0 = live |
| // CHECK: op_preds: predecessors: |
| // CHECK: func.call @bar(%{{.*}}) {tag = "c"} |
| func.func @bar(%cond: i1) -> i32 |
| attributes {tag = "bar"} { |
| cf.cond_br %cond, ^bb1, ^bb2 |
| |
| ^bb1: |
| %c0 = arith.constant 0 : i32 |
| return {b} %c0 : i32 |
| |
| ^bb2: |
| %c1 = arith.constant 1 : i32 |
| return {c} %c1 : i32 |
| } |
| |
| // CHECK: baz |
| // CHECK: op_preds: (all) predecessors: |
| func.func private @baz(i32) -> i32 attributes {tag = "baz"} |
| |
| func.func @test_callgraph(%cond: i1, %arg0: i32) -> i32 { |
| // CHECK: a: |
| // CHECK: op_preds: (all) predecessors: |
| // CHECK: func.return {a} |
| %0 = func.call @foo(%arg0) {tag = "a"} : (i32) -> i32 |
| cf.cond_br %cond, ^bb1, ^bb2 |
| |
| ^bb1: |
| // CHECK: b: |
| // CHECK: op_preds: (all) predecessors: |
| // CHECK: func.return {a} |
| %1 = func.call @foo(%arg0) {tag = "b"} : (i32) -> i32 |
| return %1 : i32 |
| |
| ^bb2: |
| // CHECK: c: |
| // CHECK: op_preds: (all) predecessors: |
| // CHECK: func.return {b} |
| // CHECK: func.return {c} |
| %2 = func.call @bar(%cond) {tag = "c"} : (i1) -> i32 |
| // CHECK: d: |
| // CHECK: op_preds: predecessors: |
| %3 = func.call @baz(%arg0) {tag = "d"} : (i32) -> i32 |
| return %2 : i32 |
| } |
| |
| func.func private @bax(%arg0: i32) { |
| return {void_return} |
| } |
| |
| func.func @test_callgraph_void_return(%arg0: i32) -> i32 { |
| // CHECK: call_void_return: |
| // CHECK: op_preds: (all) predecessors: |
| // CHECK: func.return {void_return} |
| func.call @bax(%arg0) {tag = "call_void_return"}: (i32) -> () |
| return %arg0 : i32 |
| } |
| |
| // CHECK: test_unknown_branch: |
| // CHECK: region #0 |
| // CHECK: ^bb0 = live |
| // CHECK: ^bb1 = live |
| // CHECK: from ^bb0 = live |
| // CHECK: ^bb2 = live |
| // CHECK: from ^bb0 = live |
| func.func @test_unknown_branch() -> () |
| attributes {tag = "test_unknown_branch"} { |
| "test.unknown_br"() [^bb1, ^bb2] : () -> () |
| |
| ^bb1: |
| return |
| |
| ^bb2: |
| return |
| } |
| |
| // CHECK: test_unknown_region: |
| // CHECK: region #0 |
| // CHECK: ^bb0 = live |
| // CHECK: region #1 |
| // CHECK: ^bb0 = live |
| func.func @test_unknown_region() -> () { |
| "test.unknown_region_br"() ({ |
| ^bb0: |
| "test.unknown_region_end"() : () -> () |
| }, { |
| ^bb0: |
| "test.unknown_region_end"() : () -> () |
| }) {tag = "test_unknown_region"} : () -> () |
| return |
| } |
| |
| // CHECK: test_known_dead_block: |
| // CHECK: region #0 |
| // CHECK: ^bb0 = live |
| // CHECK: ^bb1 = live |
| // CHECK: ^bb2 = dead |
| func.func @test_known_dead_block() -> () |
| attributes {tag = "test_known_dead_block"} { |
| %true = arith.constant true |
| cf.cond_br %true, ^bb1, ^bb2 |
| |
| ^bb1: |
| return |
| |
| ^bb2: |
| return |
| } |
| |
| // CHECK: test_known_dead_edge: |
| // CHECK: ^bb2 = live |
| // CHECK: from ^bb1 = dead |
| // CHECK: from ^bb0 = live |
| func.func @test_known_dead_edge(%arg0: i1) -> () |
| attributes {tag = "test_known_dead_edge"} { |
| cf.cond_br %arg0, ^bb1, ^bb2 |
| |
| ^bb1: |
| %true = arith.constant true |
| cf.cond_br %true, ^bb3, ^bb2 |
| |
| ^bb2: |
| return |
| |
| ^bb3: |
| return |
| } |
| |
| func.func @test_known_region_predecessors() -> () { |
| %false = arith.constant false |
| // CHECK: test_known_if: |
| // CHECK: region #0 |
| // CHECK: ^bb0 = dead |
| // CHECK: region #1 |
| // CHECK: ^bb0 = live |
| // CHECK: region_preds: (all) predecessors: |
| // CHECK: scf.if |
| // CHECK: op_preds: (all) predecessors: |
| // CHECK: scf.yield {else} |
| scf.if %false { |
| scf.yield {then} |
| } else { |
| scf.yield {else} |
| } {tag = "test_known_if"} |
| return |
| } |
| |
| // CHECK: callable: |
| // CHECK: region #0 |
| // CHECK: ^bb0 = live |
| // CHECK: op_preds: predecessors: |
| // CHECK: func.call @callable() {then} |
| func.func @callable() attributes {tag = "callable"} { |
| return |
| } |
| |
| func.func @test_dead_callsite() -> () { |
| %true = arith.constant true |
| scf.if %true { |
| func.call @callable() {then} : () -> () |
| scf.yield |
| } else { |
| func.call @callable() {else} : () -> () |
| scf.yield |
| } |
| return |
| } |
| |
| func.func private @test_dead_return(%arg0: i32) -> i32 { |
| %true = arith.constant true |
| cf.cond_br %true, ^bb1, ^bb1 |
| |
| ^bb1: |
| return {true} %arg0 : i32 |
| |
| ^bb2: |
| return {false} %arg0 : i32 |
| } |
| |
| func.func @test_call_dead_return(%arg0: i32) -> () { |
| // CHECK: test_dead_return: |
| // CHECK: op_preds: (all) predecessors: |
| // CHECK: func.return {true} |
| %0 = func.call @test_dead_return(%arg0) {tag = "test_dead_return"} : (i32) -> i32 |
| return |
| } |
| |
| func.func @test_dca_doesnt_crash() -> () { |
| %0 = scf.execute_region -> tensor<5x16xi16> { |
| llvm.unreachable |
| } |
| return |
| } |
| |
| func.func @test_dca_doesnt_crash_2() -> () attributes {symbol = @notexistant} { |
| return |
| } |