1// RUN: mlir-opt %s -remove-dead-values -split-input-file -verify-diagnostics | FileCheck %s 2 3// The IR is updated regardless of memref.global private constant 4// 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_global 11 %1 = memref.get_global @__constant_4xi32 : memref<4xi32> 12 // CHECK-NOT: tensor.empty 13 return %arg0 : i32 14 } 15} 16 17// ----- 18 19// Dead values are removed from the IR even if the module has a name 20// 21module @named_module_acceptable { 22 func.func @main(%arg0: tensor<10xf32>) -> tensor<10xf32> { 23 %0 = tensor.empty() : tensor<10xbf16> 24 // CHECK-NOT: tensor.empty 25 return %arg0 : tensor<10xf32> 26 } 27} 28 29// ----- 30 31// The IR contains both conditional and unconditional branches with a loop 32// in which the last cf.cond_br is referncing the first cf.br 33// 34func.func @acceptable_ir_has_cleanable_loop_of_conditional_and_branch_op(%arg0: i1) { 35 %non_live = arith.constant 0 : i32 36 // CHECK-NOT: arith.constant 37 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 : i32 42 cf.br ^bb3(%non_live_1, %non_live_5 : i32, i32) 43 // CHECK: cf.br ^[[BB3:bb[0-9]+]] 44 // CHECK-NOT: i32 45^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 return 52} 53 54// ----- 55 56// Checking that iter_args are properly handled 57// 58func.func @cleanable_loop_iter_args_value(%arg0: index) -> index { 59 %c0 = arith.constant 0 : index 60 %c1 = arith.constant 1 : index 61 %c10 = arith.constant 10 : index 62 %non_live = arith.constant 0 : index 63 // 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]] : index 66 %new_live = arith.addi %live_arg, %i : index 67 // CHECK: scf.yield [[SUM:%.+]] 68 scf.yield %new_live, %non_live_arg : index, index 69 } 70 // CHECK: return [[RESULT]] : index 71 return %result : index 72} 73 74// ----- 75 76// Checking that the arguments of linalg.generic are properly handled 77// All code below is removed as a result of the pass 78// 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.constant 86 %0 = tensor.empty() : tensor<1x25x13xi32> 87 // CHECK-NOT: tensor 88 %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.generic 90 ^bb0(%in: i32, %in_15: i32, %out: i32): 91 %29 = arith.xori %in, %in_15 : i32 92 // CHECK-NOT: arith.xori 93 linalg.yield %29 : i32 94 // CHECK-NOT: linalg.yield 95 } -> tensor<1x25x13xi32> 96 return 97 } 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: return 106// CHECK-NEXT: } 107// CHECK: func.func @main(%[[arg0:.*]]: i32) { 108// CHECK-NEXT: call @clean_func_op_remove_argument_and_return_value() : () -> () 109// CHECK-NEXT: return 110// CHECK-NEXT: } 111func.func private @clean_func_op_remove_argument_and_return_value(%arg0: i32) -> (i32) { 112 return %arg0 : i32 113} 114func.func @main(%arg0 : i32) { 115 %non_live = func.call @clean_func_op_remove_argument_and_return_value(%arg0) : (i32) -> (i32) 116 return 117} 118 119// ----- 120 121// %arg0 is not live because it is never used. %arg1 is not live because its 122// user `arith.addi` doesn't have any uses and the value that it is forwarded to 123// (%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 0 129// 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) -> i32 133// 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 : i32 137 %non_live = arith.addi %arg1, %arg1 : i32 138 return %c0, %arg1 : i32, i32 139} 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 of 148// @clean_func_op_remove_return_values isn't removed because %live is live 149// (liveness is checked across all callers). 150// 151// Also, the second return value of @clean_func_op_remove_return_values is 152// removed despite %c0 being live because neither %non_live nor %non_live_1 were 153// live (removal doesn't depend on the liveness of the operand itself but on the 154// 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 0 160// 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>) -> i32 165// CHECK-NEXT: %[[non_live_0:.*]] = call @clean_func_op_remove_return_values(%[[arg1]]) : (memref<i32>) -> i32 166// CHECK-NEXT: return %[[live]] : i32 167// CHECK-NEXT: } 168func.func private @clean_func_op_remove_return_values(%arg0 : memref<i32>) -> (i32, i32) { 169 %c0 = arith.constant 0 : i32 170 memref.store %c0, %arg0[] : memref<i32> 171 return %c0, %c0 : i32, i32 172} 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 : i32 177} 178 179// ----- 180 181// None of the return values of @clean_func_op_dont_remove_return_values can be 182// removed because the first one is forwarded to a live value %live and the 183// 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 : i32 187// CHECK-NEXT: return %[[c0]], %[[c0]] : i32, i32 188// 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, i32 193// CHECK-NEXT: } 194func.func private @clean_func_op_dont_remove_return_values() -> (i32, i32) { 195 %c0 = arith.constant 0 : i32 196 return %c0, %c0 : i32, i32 197} 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, i32 202} 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 be 209// 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, which 212// 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 forwards 216// to %arg3, which is not kept. %arg3 is not kept because %arg3 is not live and 217// only %arg1 and %arg7 forward to it, such that neither of them forward 218// 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, i32 226// 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]] : i32 230// CHECK-NEXT: } 231// CHECK-NEXT: return %[[live_and_non_live]]#0 232// 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 : i32 236 %non_live_1 = arith.addi %arg3, %arg3 : i32 237 scf.condition(%arg0) %live_0, %arg4, %non_live_1 : i32, i32, i32 238 } do { 239 ^bb0(%arg5: i32, %arg6: i32, %arg7: i32): 240 %live_1 = arith.addi %arg6, %arg6 : i32 241 scf.yield %arg7, %live_1 : i32, i32 242 } 243 return %live : i32 244} 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, which 254// 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` forwards 258// to it, which forwards to only %non_live_0 and %arg7, where both these are 259// 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` forwards 262// to it, which forwards to only %non_live_1 and %arg8, where both these are 263// 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, none 268// 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 and 271// %non_live_3 forward to them, and both only otherwise forward to %non_live_0 272// 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 0 278// CHECK-NEXT: %[[c1:.*]] = arith.constant 1 279// 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, i32 282// CHECK-NEXT: } do { 283// CHECK-NEXT: ^bb0(%[[arg5:.*]]: i32, %[[arg6:.*]]: i32): 284// CHECK-NEXT: scf.yield %[[arg5]], %[[arg6]] : i32, i32 285// CHECK-NEXT: } 286// CHECK-NEXT: return %[[live_and_non_live]]#0 : i32 287// CHECK-NEXT: } 288// CHECK: func.func private @identity() { 289// CHECK-NEXT: return 290// 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 : i32 293 %c1 = arith.constant 1 : i32 294 %c2 = arith.constant 2 : i32 295 %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 : i32 297 %non_live_3 = func.call @identity(%arg10) : (i32) -> (i32) 298 scf.condition(%arg2) %arg4, %arg3, %non_live_2, %non_live_3 : i32, i32, i32, i32 299 } do { 300 ^bb0(%arg5: i32, %arg6: i32, %arg7: i32, %arg8: i32): 301 %non_live_4 = arith.addi %arg7, %arg8 :i32 302 scf.yield %arg5, %arg6, %non_live_4 : i32, i32, i32 303 } 304 return %live : i32 305} 306func.func private @identity(%arg1 : i32) -> (i32) { 307 return %arg1 : i32 308} 309 310// ----- 311 312// The op isn't erased because it has memory effects but its unnecessary result 313// 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 10 321// CHECK-NEXT: memref.store %[[c10]], %[[arg1]][] 322// CHECK-NEXT: scf.yield 323// CHECK-NEXT: } 324// CHECK-NEXT: default { 325// CHECK-NEXT: } 326// CHECK-NEXT: return 327// CHECK-NEXT: } 328func.func @clean_region_branch_op_remove_result(%arg0 : index, %arg1 : memref<i32>) { 329 %non_live = scf.index_switch %arg0 -> i32 330 case 1 { 331 %c10 = arith.constant 10 : i32 332 memref.store %c10, %arg1[] : memref<i32> 333 scf.yield %c10 : i32 334 } 335 default { 336 %c11 = arith.constant 11 : i32 337 scf.yield %c11 : i32 338 } 339 return 340} 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 because 346// 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 2 353// CHECK-NEXT: %[[live_1:.*]] = arith.muli %[[live_0]], %[[c2]] 354// CHECK-NEXT: %[[c3:.*]] = arith.constant 3 355// 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 : i32 365 %c2 = arith.constant 2 : i32 366 %live_1 = arith.muli %live_0, %c2 : i32 367 %non_live_1 = arith.addi %live_1, %live_0 : i32 368 %non_live_2 = arith.constant 7 : i32 369 %non_live_3 = arith.subi %arg0, %non_live_1 : i32 370 %c3 = arith.constant 3 : i32 371 %live_2 = arith.addi %arg0, %c3 : i32 372 memref.store %live_2, %arg1[] : memref<i32> 373 return %live_1, %non_live_1, %non_live_2, %non_live_3 : i32, i32, i32, i32 374} 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 : i32 379} 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: return 389// CHECK-NEXT: } 390// CHECK: func.func @main(%[[arg3:.*]]: i32, %[[arg4:.*]]: i1) { 391// CHECK-NEXT: call @clean_region_branch_op_erase_it() : () -> () 392// CHECK-NEXT: return 393// 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 : i32 397 } do { 398 ^bb0(%arg2: i32): 399 scf.yield %arg2 : i32 400 } 401 return %non_live : i32 402} 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 return 407} 408 409// ----- 410 411#map = affine_map<(d0)[s0, s1] -> (d0 * s0 + s1)> 412func.func @kernel(%arg0: memref<18xf32>) { 413 %c1 = arith.constant 1 : index 414 %c18 = arith.constant 18 : index 415 gpu.launch blocks(%arg3, %arg4, %arg5) in (%arg9 = %c18, %arg10 = %c18, %arg11 = %c18) threads(%arg6, %arg7, %arg8) in (%arg12 = %c1, %arg13 = %c1, %arg14 = %c1) { 416 %c1_0 = arith.constant 1 : index 417 %c0_1 = arith.constant 0 : index 418 %cst_2 = arith.constant 25.4669495 : f32 419 %6 = affine.apply #map(%arg3)[%c1_0, %c0_1] 420 memref.store %cst_2, %arg0[%6] : memref<18xf32> 421 gpu.terminator 422 } {SCFToGPU_visited} 423 return 424} 425 426// CHECK-LABEL: func.func @kernel(%arg0: memref<18xf32>) { 427// CHECK: gpu.launch blocks 428// CHECK: memref.store 429// CHECK-NEXT: gpu.terminator 430 431// ----- 432 433// CHECK: func.func private @no_block_func_declaration() 434func.func private @no_block_func_declaration() -> () 435 436// ----- 437 438// CHECK: llvm.func @no_block_external_func() 439llvm.func @no_block_external_func() attributes {sym_visibility = "private"} 440