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