brintos

brintos / llvm-project-archived public Read only

0
0
Text · 25.0 KiB · 4bae85d Raw
690 lines · plain
1// RUN: mlir-opt %s -remove-dead-values -split-input-file -verify-diagnostics | FileCheck %s2 3// The IR is updated regardless of memref.global private constant4//5module {6  // CHECK: memref.global "private" constant @__constant_4xi32 : memref<4xi32> = dense<[1, 2, 3, 4]> {alignment = 16 : i64}7  memref.global "private" constant @__constant_4xi32 : memref<4xi32> = dense<[1, 2, 3, 4]> {alignment = 16 : i64}8  func.func @main(%arg0: i32) -> i32 {9    %0 = tensor.empty() : tensor<10xbf16>10    // CHECK-NOT: memref.get_global11    %1 = memref.get_global @__constant_4xi32 : memref<4xi32>12    // CHECK-NOT: tensor.empty13    return %arg0 : i3214  }15}16 17// -----18 19// Dead values are removed from the IR even if the module has a name20//21module @named_module_acceptable {22  func.func @main(%arg0: tensor<10xf32>) -> tensor<10xf32> {23    %0 = tensor.empty() : tensor<10xbf16>24    // CHECK-NOT: tensor.empty25    return %arg0 : tensor<10xf32>26  }27}28 29// -----30 31// The IR contains both conditional and unconditional branches with a loop32// in which the last cf.cond_br is referncing the first cf.br33//34func.func @acceptable_ir_has_cleanable_loop_of_conditional_and_branch_op(%arg0: i1) {35  %non_live = arith.constant 0 : i3236  // CHECK-NOT: arith.constant37  cf.br ^bb1(%non_live : i32)38  // CHECK: cf.br ^[[BB1:bb[0-9]+]]39^bb1(%non_live_1 : i32):40  // CHECK: ^[[BB1]]:41  %non_live_5 = arith.constant 1 : i3242  cf.br ^bb3(%non_live_1, %non_live_5 : i32, i32)43  // CHECK: cf.br ^[[BB3:bb[0-9]+]]44  // CHECK-NOT: i3245^bb3(%non_live_2 : i32, %non_live_6 : i32):46  // CHECK: ^[[BB3]]:47  cf.cond_br %arg0, ^bb1(%non_live_2 : i32), ^bb4(%non_live_2 : i32)48  // CHECK: cf.cond_br %arg0, ^[[BB1]], ^[[BB4:bb[0-9]+]]49^bb4(%non_live_4 : i32):50  // CHECK: ^[[BB4]]:51  return52}53 54// -----55 56// Checking that iter_args are properly handled57//58func.func @cleanable_loop_iter_args_value(%arg0: index) -> index {59  %c0 = arith.constant 0 : index60  %c1 = arith.constant 1 : index61  %c10 = arith.constant 10 : index62  %non_live = arith.constant 0 : index63  // CHECK: [[RESULT:%.+]] = scf.for [[ARG_1:%.*]] = %c0 to %c10 step %c1 iter_args([[ARG_2:%.*]] = %arg0) -> (index) {64  %result, %result_non_live = scf.for %i = %c0 to %c10 step %c1 iter_args(%live_arg = %arg0, %non_live_arg = %non_live) -> (index, index) {65    // CHECK: [[SUM:%.+]] = arith.addi [[ARG_2]], [[ARG_1]] : index66    %new_live = arith.addi %live_arg, %i : index67    // CHECK: scf.yield [[SUM:%.+]]68    scf.yield %new_live, %non_live_arg : index, index69  }70  // CHECK: return [[RESULT]] : index71  return %result : index72}73 74// -----75 76// Checking that the arguments of linalg.generic are properly handled77// All code below is removed as a result of the pass78//79#map = affine_map<(d0, d1, d2) -> (0, d1, d2)>80#map1 = affine_map<(d0, d1, d2) -> (d0, d1, d2)>81module {82  func.func @main() {83    %cst_3 = arith.constant dense<54> : tensor<1x25x13xi32>84    %cst_7 = arith.constant dense<11> : tensor<1x25x13xi32>85    // CHECK-NOT: arith.constant86    %0 = tensor.empty() : tensor<1x25x13xi32>87    // CHECK-NOT: tensor88    %1 = linalg.generic {indexing_maps = [#map, #map, #map1], iterator_types = ["parallel", "parallel", "parallel"]} ins(%cst_3, %cst_7 : tensor<1x25x13xi32>, tensor<1x25x13xi32>) outs(%0 : tensor<1x25x13xi32>) {89    // CHECK-NOT: linalg.generic90    ^bb0(%in: i32, %in_15: i32, %out: i32):91      %29 = arith.xori %in, %in_15 : i3292      // CHECK-NOT: arith.xori93      linalg.yield %29 : i3294      // CHECK-NOT: linalg.yield95    } -> tensor<1x25x13xi32>96    return97  }98}99 100// -----101 102// Note that this cleanup cannot be done by the `canonicalize` pass.103//104// CHECK-LABEL: func.func private @clean_func_op_remove_argument_and_return_value() {105// CHECK-NEXT:    return106// CHECK-NEXT:  }107// CHECK:       func.func @main(%[[arg0:.*]]: i32) {108// CHECK-NEXT:    call @clean_func_op_remove_argument_and_return_value() : () -> ()109// CHECK-NEXT:    return110// CHECK-NEXT:  }111func.func private @clean_func_op_remove_argument_and_return_value(%arg0: i32) -> (i32) {112  return %arg0 : i32113}114func.func @main(%arg0 : i32) {115  %non_live = func.call @clean_func_op_remove_argument_and_return_value(%arg0) : (i32) -> (i32)116  return117}118 119// -----120 121// %arg0 is not live because it is never used. %arg1 is not live because its122// user `arith.addi` doesn't have any uses and the value that it is forwarded to123// (%non_live_0) also doesn't have any uses.124//125// Note that this cleanup cannot be done by the `canonicalize` pass.126//127// CHECK-LABEL: func.func private @clean_func_op_remove_arguments() -> i32 {128// CHECK-NEXT:    %[[c0:.*]] = arith.constant 0129// CHECK-NEXT:    return %[[c0]]130// CHECK-NEXT:  }131// CHECK:       func.func @main(%[[arg2:.*]]: memref<i32>, %[[arg3:.*]]: i32, %[[DEVICE:.*]]: i32) -> (i32, memref<i32>) {132// CHECK-NEXT:    %[[live:.*]] = test.call_on_device @clean_func_op_remove_arguments(), %[[DEVICE]] : (i32) -> i32133// CHECK-NEXT:    return %[[live]], %[[arg2]]134// CHECK-NEXT:  }135func.func private @clean_func_op_remove_arguments(%arg0 : memref<i32>, %arg1 : i32) -> (i32, i32) {136  %c0 = arith.constant 0 : i32137  %non_live = arith.addi %arg1, %arg1 : i32138  return %c0, %arg1 : i32, i32139}140func.func @main(%arg2 : memref<i32>, %arg3 : i32, %device : i32) -> (i32, memref<i32>) {141  %live, %non_live_0 = test.call_on_device @clean_func_op_remove_arguments(%arg2, %arg3), %device : (memref<i32>, i32, i32) -> (i32, i32)142  return %live, %arg2 : i32, memref<i32>143}144 145// -----146 147// Even though %non_live_0 is not live, the first return value of148// @clean_func_op_remove_return_values isn't removed because %live is live149// (liveness is checked across all callers).150//151// Also, the second return value of @clean_func_op_remove_return_values is152// removed despite %c0 being live because neither %non_live nor %non_live_1 were153// live (removal doesn't depend on the liveness of the operand itself but on the154// liveness of where it is forwarded).155//156// Note that this cleanup cannot be done by the `canonicalize` pass.157//158// CHECK:       func.func private @clean_func_op_remove_return_values(%[[arg0:.*]]: memref<i32>) -> i32 {159// CHECK-NEXT:    %[[c0]] = arith.constant 0160// CHECK-NEXT:    memref.store %[[c0]], %[[arg0]][]161// CHECK-NEXT:    return %[[c0]]162// CHECK-NEXT:  }163// CHECK:       func.func @main(%[[arg1:.*]]: memref<i32>) -> i32 {164// CHECK-NEXT:    %[[live:.*]] = call @clean_func_op_remove_return_values(%[[arg1]]) : (memref<i32>) -> i32165// CHECK-NEXT:    %[[non_live_0:.*]] = call @clean_func_op_remove_return_values(%[[arg1]]) : (memref<i32>) -> i32166// CHECK-NEXT:    return %[[live]] : i32167// CHECK-NEXT:  }168func.func private @clean_func_op_remove_return_values(%arg0 : memref<i32>) -> (i32, i32) {169  %c0 = arith.constant 0 : i32170  memref.store %c0, %arg0[] : memref<i32>171  return %c0, %c0 : i32, i32172}173func.func @main(%arg1 : memref<i32>) -> (i32) {174  %live, %non_live = func.call @clean_func_op_remove_return_values(%arg1) : (memref<i32>) -> (i32, i32)175  %non_live_0, %non_live_1 = func.call @clean_func_op_remove_return_values(%arg1) : (memref<i32>) -> (i32, i32)176  return %live : i32177}178 179// -----180 181// None of the return values of @clean_func_op_dont_remove_return_values can be182// removed because the first one is forwarded to a live value %live and the183// second one is forwarded to a live value %live_0.184//185// CHECK-LABEL: func.func private @clean_func_op_dont_remove_return_values() -> (i32, i32) {186// CHECK-NEXT:    %[[c0:.*]] = arith.constant 0 : i32187// CHECK-NEXT:    return %[[c0]], %[[c0]] : i32, i32188// CHECK-NEXT:  }189// CHECK-LABEL: func.func @main() -> (i32, i32) {190// CHECK-NEXT:    %[[live_and_non_live:.*]]:2 = call @clean_func_op_dont_remove_return_values() : () -> (i32, i32)191// CHECK-NEXT:    %[[non_live_0_and_live_0:.*]]:2 = call @clean_func_op_dont_remove_return_values() : () -> (i32, i32)192// CHECK-NEXT:    return %[[live_and_non_live]]#0, %[[non_live_0_and_live_0]]#1 : i32, i32193// CHECK-NEXT:  }194func.func private @clean_func_op_dont_remove_return_values() -> (i32, i32) {195  %c0 = arith.constant 0 : i32196  return %c0, %c0 : i32, i32197}198func.func @main() -> (i32, i32) {199  %live, %non_live = func.call @clean_func_op_dont_remove_return_values() : () -> (i32, i32)200  %non_live_0, %live_0 = func.call @clean_func_op_dont_remove_return_values() : () -> (i32, i32)201  return %live, %live_0 : i32, i32202}203 204// -----205 206// Values kept:207//  (1) %non_live is not live. Yet, it is kept because %arg4 in `scf.condition`208//  forwards to it, which has to be kept. %arg4 in `scf.condition` has to be209//  kept because it forwards to %arg6 which is live.210//211//  (2) %arg5 is not live. Yet, it is kept because %live_0 forwards to it, which212//  also forwards to %live, which is live.213//214// Values not kept:215//  (1) %arg1 is not kept as an operand of `scf.while` because it only forwards216//  to %arg3, which is not kept. %arg3 is not kept because %arg3 is not live and217//  only %arg1 and %arg7 forward to it, such that neither of them forward218//  anywhere else. Thus, %arg7 is also not kept in the `scf.yield` op.219//220// Note that this cleanup cannot be done by the `canonicalize` pass.221//222// CHECK:       func.func @clean_region_branch_op_dont_remove_first_2_results_but_remove_first_operand(%[[arg0:.*]]: i1, %[[arg1:.*]]: i32, %[[arg2:.*]]: i32) -> i32 {223// CHECK-NEXT:    %[[live_and_non_live:.*]]:2 = scf.while (%[[arg4:.*]] = %[[arg2]]) : (i32) -> (i32, i32) {224// CHECK-NEXT:      %[[live_0:.*]] = arith.addi %[[arg4]], %[[arg4]]225// CHECK-NEXT:      scf.condition(%arg0) %[[live_0]], %[[arg4]] : i32, i32226// CHECK-NEXT:    } do {227// CHECK-NEXT:    ^bb0(%[[arg5:.*]]: i32, %[[arg6:.*]]: i32):228// CHECK-NEXT:      %[[live_1:.*]] = arith.addi %[[arg6]], %[[arg6]]229// CHECK-NEXT:      scf.yield %[[live_1]] : i32230// CHECK-NEXT:    }231// CHECK-NEXT:    return %[[live_and_non_live]]#0232// CHECK-NEXT:  }233func.func @clean_region_branch_op_dont_remove_first_2_results_but_remove_first_operand(%arg0: i1, %arg1: i32, %arg2: i32) -> (i32) {234  %live, %non_live, %non_live_0 = scf.while (%arg3 = %arg1, %arg4 = %arg2) : (i32, i32) -> (i32, i32, i32) {235    %live_0 = arith.addi %arg4, %arg4 : i32236    %non_live_1 = arith.addi %arg3, %arg3 : i32237    scf.condition(%arg0) %live_0, %arg4, %non_live_1 : i32, i32, i32238  } do {239  ^bb0(%arg5: i32, %arg6: i32, %arg7: i32):240    %live_1 = arith.addi %arg6, %arg6 : i32241    scf.yield %arg7, %live_1 : i32, i32242  }243  return %live : i32244}245 246// -----247 248// Values kept:249//  (1) %live is kept because it is live.250//251//  (2) %non_live is not live. Yet, it is kept because %arg3 in `scf.condition`252//  forwards to it and this %arg3 has to be kept. This %arg3 in `scf.condition`253//  has to be kept because it forwards to %arg6, which forwards to %arg4, which254//  forwards to %live, which is live.255//256// Values not kept:257//  (1) %non_live_0 is not kept because %non_live_2 in `scf.condition` forwards258//  to it, which forwards to only %non_live_0 and %arg7, where both these are259//  not live and have no other value forwarding to them.260//261//  (2) %non_live_1 is not kept because %non_live_3 in `scf.condition` forwards262//  to it, which forwards to only %non_live_1 and %arg8, where both these are263//  not live and have no other value forwarding to them.264//265//  (3) %c2 is not kept because it only forwards to %arg10, which is not kept.266//267//  (4) %arg10 is not kept because only %c2 and %non_live_4 forward to it, none268//  of them forward anywhere else, and %arg10 is not.269//270//  (5) %arg7 and %arg8 are not kept because they are not live, %non_live_2 and271//  %non_live_3 forward to them, and both only otherwise forward to %non_live_0272//  and %non_live_1 which are not live and have no other predecessors.273//274// Note that this cleanup cannot be done by the `canonicalize` pass.275//276// CHECK:       func.func @clean_region_branch_op_remove_last_2_results_last_2_arguments_and_last_operand(%[[arg2:.*]]: i1) -> i32 {277// CHECK-NEXT:    %[[c0:.*]] = arith.constant 0278// CHECK-NEXT:    %[[c1:.*]] = arith.constant 1279// CHECK-NEXT:    %[[live_and_non_live:.*]]:2 = scf.while (%[[arg3:.*]] = %[[c0]], %[[arg4:.*]] = %[[c1]]) : (i32, i32) -> (i32, i32) {280// CHECK-NEXT:      func.call @identity() : () -> ()281// CHECK-NEXT:      scf.condition(%[[arg2]]) %[[arg4]], %[[arg3]] : i32, i32282// CHECK-NEXT:    } do {283// CHECK-NEXT:    ^bb0(%[[arg5:.*]]: i32, %[[arg6:.*]]: i32):284// CHECK-NEXT:      scf.yield %[[arg5]], %[[arg6]] : i32, i32285// CHECK-NEXT:    }286// CHECK-NEXT:    return %[[live_and_non_live]]#0 : i32287// CHECK-NEXT:  }288// CHECK:       func.func private @identity() {289// CHECK-NEXT:    return290// CHECK-NEXT:  }291func.func @clean_region_branch_op_remove_last_2_results_last_2_arguments_and_last_operand(%arg2: i1) -> (i32) {292  %c0 = arith.constant 0 : i32293  %c1 = arith.constant 1 : i32294  %c2 = arith.constant 2 : i32295  %live, %non_live, %non_live_0, %non_live_1 = scf.while (%arg3 = %c0, %arg4 = %c1, %arg10 = %c2) : (i32, i32, i32) -> (i32, i32, i32, i32) {296    %non_live_2 = arith.addi %arg10, %arg10 : i32297    %non_live_3 = func.call @identity(%arg10) : (i32) -> (i32)298    scf.condition(%arg2) %arg4, %arg3, %non_live_2, %non_live_3 : i32, i32, i32, i32299  } do {300  ^bb0(%arg5: i32, %arg6: i32, %arg7: i32, %arg8: i32):301    %non_live_4 = arith.addi %arg7, %arg8 :i32302    scf.yield %arg5, %arg6, %non_live_4 : i32, i32, i32303  }304  return %live : i32305}306func.func private @identity(%arg1 : i32) -> (i32) {307  return %arg1 : i32308}309 310// -----311 312// The op isn't erased because it has memory effects but its unnecessary result313// is removed.314//315// Note that this cleanup cannot be done by the `canonicalize` pass.316//317// CHECK:       func.func @clean_region_branch_op_remove_result(%[[arg0:.*]]: index, %[[arg1:.*]]: memref<i32>) {318// CHECK-NEXT:    scf.index_switch %[[arg0]]319// CHECK-NEXT:    case 1 {320// CHECK-NEXT:      %[[c10:.*]] = arith.constant 10321// CHECK-NEXT:      memref.store %[[c10]], %[[arg1]][]322// CHECK-NEXT:      scf.yield323// CHECK-NEXT:    }324// CHECK-NEXT:    default {325// CHECK-NEXT:    }326// CHECK-NEXT:    return327// CHECK-NEXT:  }328func.func @clean_region_branch_op_remove_result(%arg0 : index, %arg1 : memref<i32>) {329  %non_live = scf.index_switch %arg0 -> i32330  case 1 {331    %c10 = arith.constant 10 : i32332    memref.store %c10, %arg1[] : memref<i32>333    scf.yield %c10 : i32334  }335  default {336    %c11 = arith.constant 11 : i32337    scf.yield %c11 : i32338  }339  return340}341 342// -----343 344// The simple ops which don't have memory effects or live results get removed.345// %arg5 doesn't get removed from the @main even though it isn't live because346// the signature of a public function is always left untouched.347//348// Note that this cleanup cannot be done by the `canonicalize` pass.349//350// CHECK:       func.func private @clean_simple_ops(%[[arg0:.*]]: i32, %[[arg1:.*]]: memref<i32>)351// CHECK-NEXT:    %[[live_0:.*]] = arith.addi %[[arg0]], %[[arg0]]352// CHECK-NEXT:    %[[c2:.*]] = arith.constant 2353// CHECK-NEXT:    %[[live_1:.*]] = arith.muli %[[live_0]], %[[c2]]354// CHECK-NEXT:    %[[c3:.*]] = arith.constant 3355// CHECK-NEXT:    %[[live_2:.*]] = arith.addi %[[arg0]], %[[c3]]356// CHECK-NEXT:    memref.store %[[live_2]], %[[arg1]][]357// CHECK-NEXT:    return %[[live_1]]358// CHECK-NEXT:  }359// CHECK:       func.func @main(%[[arg3:.*]]: i32, %[[arg4:.*]]: memref<i32>, %[[arg5:.*]]360// CHECK-NEXT:    %[[live:.*]] = call @clean_simple_ops(%[[arg3]], %[[arg4]])361// CHECK-NEXT:    return %[[live]]362// CHECK-NEXT:  }363func.func private @clean_simple_ops(%arg0 : i32, %arg1 : memref<i32>, %arg2 : i32) -> (i32, i32, i32, i32) {364  %live_0 = arith.addi %arg0, %arg0 : i32365  %c2 = arith.constant 2 : i32366  %live_1 = arith.muli %live_0, %c2 : i32367  %non_live_1 = arith.addi %live_1, %live_0 : i32368  %non_live_2 = arith.constant 7 : i32369  %non_live_3 = arith.subi %arg0, %non_live_1 : i32370  %c3 = arith.constant 3 : i32371  %live_2 = arith.addi %arg0, %c3 : i32372  memref.store %live_2, %arg1[] : memref<i32>373  return %live_1, %non_live_1, %non_live_2, %non_live_3 : i32, i32, i32, i32374}375 376func.func @main(%arg3 : i32, %arg4 : memref<i32>, %arg5 : i32) -> (i32) {377  %live, %non_live_1, %non_live_2, %non_live_3 = func.call @clean_simple_ops(%arg3, %arg4, %arg5) : (i32, memref<i32>, i32) -> (i32, i32, i32, i32)378  return %live : i32379}380 381// -----382 383// The scf.while op has no memory effects and its result isn't live.384//385// Note that this cleanup cannot be done by the `canonicalize` pass.386//387// CHECK-LABEL: func.func private @clean_region_branch_op_erase_it() {388// CHECK-NEXT:    return389// CHECK-NEXT:  }390// CHECK:       func.func @main(%[[arg3:.*]]: i32, %[[arg4:.*]]: i1) {391// CHECK-NEXT:    call @clean_region_branch_op_erase_it() : () -> ()392// CHECK-NEXT:    return393// CHECK-NEXT:  }394func.func private @clean_region_branch_op_erase_it(%arg0 : i32, %arg1 : i1) -> (i32) {395  %non_live = scf.while (%arg2 = %arg0) : (i32) -> (i32) {396    scf.condition(%arg1) %arg2 : i32397  } do {398  ^bb0(%arg2: i32):399    scf.yield %arg2 : i32400  }401  return %non_live : i32402}403 404func.func @main(%arg3 : i32, %arg4 : i1) {405  %non_live_0 = func.call @clean_region_branch_op_erase_it(%arg3, %arg4) : (i32, i1) -> (i32)406  return407}408 409// -----410 411// The scf.if operation represents an if-then-else construct for conditionally412// executing two regions of code. The 'the' region has exactly 1 block, and413// the 'else' region may have 0 or 1 block. This case is to ensure 'else' region414// with 0 block not crash.415 416// CHECK-LABEL: func.func @clean_region_branch_op_with_empty_region417func.func @clean_region_branch_op_with_empty_region(%arg0: i1, %arg1: memref<f32>) {418  %cst = arith.constant 1.000000e+00 : f32419  scf.if %arg0 {420    memref.store %cst, %arg1[] : memref<f32>421  }422  return423}424 425// -----426 427#map = affine_map<(d0)[s0, s1] -> (d0 * s0 + s1)>428func.func @kernel(%arg0: memref<18xf32>) {429  %c1 = arith.constant 1 : index430  %c18 = arith.constant 18 : index431  gpu.launch blocks(%arg3, %arg4, %arg5) in (%arg9 = %c18, %arg10 = %c18, %arg11 = %c18) threads(%arg6, %arg7, %arg8) in (%arg12 = %c1, %arg13 = %c1, %arg14 = %c1) {432    %c1_0 = arith.constant 1 : index433    %c0_1 = arith.constant 0 : index434    %cst_2 = arith.constant 25.4669495 : f32435    %6 = affine.apply #map(%arg3)[%c1_0, %c0_1]436    memref.store %cst_2, %arg0[%6] : memref<18xf32>437    gpu.terminator438  } {SCFToGPU_visited}439  return440}441 442// CHECK-LABEL: func.func @kernel(%arg0: memref<18xf32>) {443// CHECK: gpu.launch blocks444// CHECK: memref.store445// CHECK-NEXT: gpu.terminator446 447// -----448 449 450// CHECK-LABEL: llvm_unreachable451// CHECK-LABEL: @fn_with_llvm_unreachable452// CHECK-LABEL: @main453// CHECK: llvm.return454module @llvm_unreachable {455  func.func private @fn_with_llvm_unreachable(%arg0: tensor<4x4xf32>) -> tensor<4x4xi1> {456    llvm.unreachable457  }458  func.func private @main(%arg0: tensor<4x4xf32>) {459    %0 = call @fn_with_llvm_unreachable(%arg0) : (tensor<4x4xf32>) -> tensor<4x4xi1>460    llvm.return461  }462}463 464// CHECK: func.func private @no_block_func_declaration()465func.func private @no_block_func_declaration() -> ()466 467// -----468 469// CHECK: llvm.func @no_block_external_func()470llvm.func @no_block_external_func() attributes {sym_visibility = "private"}471 472// -----473 474// Check that yielded values aren't incorrectly removed in gpu regions475gpu.module @test_module_3 {476  gpu.func @gpu_all_reduce_region() {477    %arg0 = arith.constant 1 : i32478    %result = gpu.all_reduce %arg0 uniform {479    ^bb(%lhs : i32, %rhs : i32):480      %xor = arith.xori %lhs, %rhs : i32481      "gpu.yield"(%xor) : (i32) -> ()482    } : (i32) -> (i32)483    gpu.return484  }485}486 487// CHECK-LABEL: func @gpu_all_reduce_region()488// CHECK: %[[yield:.*]] = arith.xori %{{.*}}, %{{.*}} : i32489// CHECK: gpu.yield %[[yield]] : i32490 491// -----492 493// Check that yielded values aren't incorrectly removed in linalg regions494module {495  func.func @linalg_red_add(%arg0: tensor<?xf32>, %arg1: tensor<1xf32>) -> tensor<1xf32> {496    %0 = linalg.generic {497      indexing_maps = [affine_map<(d0) -> (d0)>, affine_map<(d0) -> (0)>],498      iterator_types = ["reduction"]499    } ins(%arg0 : tensor<?xf32>) outs(%arg1 : tensor<1xf32>) {500    ^bb0(%in: f32, %out: f32):501      %1 = arith.addf %in, %out : f32502      %2 = arith.subf %1, %out : f32 // this should still be removed503      linalg.yield %1 : f32504    } -> tensor<1xf32>505    return %0 : tensor<1xf32>506  }507}508 509// CHECK-LABEL: func @linalg_red_add510// CHECK: %[[yield:.*]] = arith.addf %{{.*}}, %{{.*}} : f32511// CHECK: linalg.yield %[[yield]] : f32512// CHECK-NOT: arith.subf513 514 515// -----516 517// check that ops with zero operands are correctly handled518 519module {520  func.func @test_zero_operands(%I: memref<10xindex>, %I2: memref<10xf32>) {521    %v0 = arith.constant 0 : index522    %result = memref.alloca_scope -> index {523      %c = arith.addi %v0, %v0 : index524      memref.store %c, %I[%v0] : memref<10xindex>525      memref.alloca_scope.return %c: index526    }527    func.return528  }529}530 531// CHECK-LABEL: func @test_zero_operands532// CHECK: memref.alloca_scope533// CHECK: memref.store534// CHECK-NOT: memref.alloca_scope.return535 536// -----537 538// CHECK-LABEL: func.func @test_atomic_yield539func.func @test_atomic_yield(%I: memref<10xf32>, %idx : index) {540  // CHECK: memref.generic_atomic_rmw541  %x = memref.generic_atomic_rmw %I[%idx] : memref<10xf32> {542  ^bb0(%current_value : f32):543    // CHECK: arith.constant544    %c1 = arith.constant 1.0 : f32545    // CHECK: memref.atomic_yield546    memref.atomic_yield %c1 : f32547  }548  func.return549}550 551// -----552 553// CHECK-LABEL: module @return_void_with_unused_argument554module @return_void_with_unused_argument {555  // CHECK-LABEL: func.func private @fn_return_void_with_unused_argument556  // CHECK-SAME: (%[[ARG0_FN:.*]]: i32)557  func.func private @fn_return_void_with_unused_argument(%arg0: i32, %arg1: memref<4xi32>) -> () {558    %sum = arith.addi %arg0, %arg0 : i32559    %c0 = arith.constant 0 : index560    %buf = memref.alloc() : memref<1xi32>561    memref.store %sum, %buf[%c0] : memref<1xi32>562    return563  }564  // CHECK-LABEL: func.func @main565  // CHECK-SAME: (%[[ARG0_MAIN:.*]]: i32)566  // CHECK: call @fn_return_void_with_unused_argument(%[[ARG0_MAIN]]) : (i32) -> ()567  func.func @main(%arg0: i32) -> memref<4xi32> {568    %unused = memref.alloc() : memref<4xi32>569    call @fn_return_void_with_unused_argument(%arg0, %unused) : (i32, memref<4xi32>) -> ()570    return %unused : memref<4xi32>571  }572}573 574// -----575 576// CHECK-LABEL: module @dynamically_unreachable577module @dynamically_unreachable {578  func.func @dynamically_unreachable() {579    // This value is used by an operation in a dynamically unreachable block.580    %zero = arith.constant 0 : i64581 582    // Dataflow analysis knows from the constant condition that583    // ^bb1 is unreachable584    %false = arith.constant false585    cf.cond_br %false, ^bb1, ^bb4586  ^bb1:587    // This unreachable operation should be removed.588    // CHECK-NOT: arith.cmpi589    %3 = arith.cmpi eq, %zero, %zero : i64590    cf.br ^bb1591  ^bb4:592    return593  }594}595 596// CHECK-LABEL: module @last_block_not_exit597module @last_block_not_exit {598  // return value can be removed because it's private.599  func.func private @terminated_with_condbr(%arg0: i1, %arg1: i1) -> i1 {600    %true = arith.constant true601    %false = arith.constant false602    cf.cond_br %arg0, ^bb1(%false : i1), ^bb2603  ^bb1(%1: i1):  // 2 preds: ^bb0, ^bb2604    return %1 : i1605  ^bb2:  // pred: ^bb3606    cf.cond_br %arg1, ^bb1(%false : i1), ^bb1(%true : i1)607  }608 609  func.func public @call_private_but_not_use() {610    %i0 = arith.constant 0: i1611    %i1 = arith.constant 1: i1612    call @terminated_with_condbr(%i0, %i1) : (i1, i1) -> i1613    func.return614  }615  // CHECK-LABEL: @call_private_but_not_use616  // CHECK: call @terminated_with_condbr(%false, %true) : (i1, i1)617}618 619// -----620 621// Test the elimination of function arguments.622 623// CHECK-LABEL: func private @single_parameter624// CHECK-SAME: () {625func.func private @single_parameter(%arg0: index) {626  return627}628 629// CHECK-LABEL: func.func private @mutl_parameter(630// CHECK-SAME: %[[ARG0:.*]]: index)631// CHECK: return %[[ARG0]]632func.func private @mutl_parameter(%arg0: index, %arg1: index, %arg2: index) -> index {633  return %arg1 : index634}635 636// CHECK-LABEL: func private @eliminate_parameter637// CHECK-SAME: () {638func.func private @eliminate_parameter(%arg0: index, %arg1: index) {639  call @single_parameter(%arg0) : (index) -> ()640  return641}642 643// CHECK-LABEL: func @callee644// CHECK-SAME: (%[[ARG0:.*]]: index, %[[ARG1:.*]]: index, %[[ARG2:.*]]: index)645func.func @callee(%arg0: index, %arg1: index, %arg2: index) -> index {646// CHECK: call @eliminate_parameter() : () -> ()647  call @eliminate_parameter(%arg0, %arg1) : (index, index) -> ()648// CHECK: call @mutl_parameter(%[[ARG1]]) : (index) -> index649  %res = call @mutl_parameter(%arg0, %arg1, %arg2) : (index, index, index) -> (index)650  return %res : index651}652 653// -----654 655// This test verifies that the induction variables in loops are not deleted, the loop has results.656 657// CHECK-LABEL: func @dead_value_loop_ivs658func.func @dead_value_loop_ivs_has_result(%lb: index, %ub: index, %step: index, %b: i1) -> i1 {659  %loop_ret = scf.for %iv = %lb to %ub step %step iter_args(%iter = %b) -> (i1) {660    cf.assert %b, "loop not dead"661    scf.yield %b : i1662  }663  return %loop_ret : i1664}665 666// -----667 668// This test verifies that the induction variables in loops are not deleted, the loop has no results.669 670// CHECK-LABEL: func @dead_value_loop_ivs_no_result671func.func @dead_value_loop_ivs_no_result(%lb: index, %ub: index, %step: index, %input: memref<?xf32>, %value: f32, %pos: index) {672  scf.for %iv = %lb to %ub step %step {673    memref.store %value, %input[%pos] : memref<?xf32>674  }675  return676}677 678// -----679 680// CHECK-LABEL: func @op_block_have_dead_arg681func.func @op_block_have_dead_arg(%arg0: index, %arg1: index, %arg2: i1) {682  scf.execute_region {683    cf.cond_br %arg2, ^bb1(%arg0 : index), ^bb1(%arg1 : index)684  ^bb1(%0: index):685      scf.yield686  }687  // CHECK-NEXT: return688  return689}690