llvm/mlir/test/Analysis/DataFlow/test-dead-code-analysis.mlir

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