xref: /llvm-project/mlir/test/Transforms/remove-dead-values.mlir (revision aa3c31a86f39552d11f0d5bae8b50541d73aa442)
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