1// RUN: mlir-opt %s -inline='default-pipeline=''' | FileCheck %s
2// RUN: mlir-opt %s --mlir-disable-threading -inline='default-pipeline=''' | FileCheck %s
3// RUN: mlir-opt %s -inline='default-pipeline=''' -mlir-print-debuginfo -mlir-print-local-scope | FileCheck %s --check-prefix INLINE-LOC
4// RUN: mlir-opt %s -inline | FileCheck %s --check-prefix INLINE_SIMPLIFY
5// RUN: mlir-opt %s -inline='op-pipelines=func.func(canonicalize,cse)' | FileCheck %s --check-prefix INLINE_SIMPLIFY
6
7// Inline a function that takes an argument.
8func.func @func_with_arg(%c : i32) -> i32 {
9  %b = arith.addi %c, %c : i32
10  return %b : i32
11}
12
13// CHECK-LABEL: func @inline_with_arg
14func.func @inline_with_arg(%arg0 : i32) -> i32 {
15  // CHECK-NEXT: arith.addi
16  // CHECK-NEXT: return
17
18  %0 = call @func_with_arg(%arg0) : (i32) -> i32
19  return %0 : i32
20}
21
22// Inline a function that has multiple return operations.
23func.func @func_with_multi_return(%a : i1) -> (i32) {
24  cf.cond_br %a, ^bb1, ^bb2
25
26^bb1:
27  %const_0 = arith.constant 0 : i32
28  return %const_0 : i32
29
30^bb2:
31  %const_55 = arith.constant 55 : i32
32  return %const_55 : i32
33}
34
35// CHECK-LABEL: func @inline_with_multi_return() -> i32
36func.func @inline_with_multi_return() -> i32 {
37// CHECK-NEXT:    [[VAL_7:%.*]] = arith.constant false
38// CHECK-NEXT:    cf.cond_br [[VAL_7]], ^bb1, ^bb2
39// CHECK:       ^bb1:
40// CHECK-NEXT:    [[VAL_8:%.*]] = arith.constant 0 : i32
41// CHECK-NEXT:    cf.br ^bb3([[VAL_8]] : i32)
42// CHECK:       ^bb2:
43// CHECK-NEXT:    [[VAL_9:%.*]] = arith.constant 55 : i32
44// CHECK-NEXT:    cf.br ^bb3([[VAL_9]] : i32)
45// CHECK:       ^bb3([[VAL_10:%.*]]: i32):
46// CHECK-NEXT:    return [[VAL_10]] : i32
47
48  %false = arith.constant false
49  %x = call @func_with_multi_return(%false) : (i1) -> i32
50  return %x : i32
51}
52
53// Check that location information is updated for inlined instructions.
54func.func @func_with_locations(%c : i32) -> i32 {
55  %b = arith.addi %c, %c : i32 loc("mysource.cc":10:8)
56  return %b : i32 loc("mysource.cc":11:2)
57}
58
59// INLINE-LOC-LABEL: func @inline_with_locations
60func.func @inline_with_locations(%arg0 : i32) -> i32 {
61  // INLINE-LOC-NEXT: arith.addi %{{.*}}, %{{.*}} : i32 loc(callsite("mysource.cc":10:8 at "mysource.cc":55:14))
62  // INLINE-LOC-NEXT: return
63
64  %0 = call @func_with_locations(%arg0) : (i32) -> i32 loc("mysource.cc":55:14)
65  return %0 : i32
66}
67
68
69// Check that external function declarations are not inlined.
70func.func private @func_external()
71
72// CHECK-LABEL: func @no_inline_external
73func.func @no_inline_external() {
74  // CHECK-NEXT: call @func_external()
75  call @func_external() : () -> ()
76  return
77}
78
79// Check that multiple levels of calls will be inlined.
80func.func @multilevel_func_a() {
81  return
82}
83func.func @multilevel_func_b() {
84  call @multilevel_func_a() : () -> ()
85  return
86}
87
88// CHECK-LABEL: func @inline_multilevel
89func.func @inline_multilevel() {
90  // CHECK-NOT: call
91  %fn = "test.functional_region_op"() ({
92    call @multilevel_func_b() : () -> ()
93    "test.return"() : () -> ()
94  }) : () -> (() -> ())
95
96  call_indirect %fn() : () -> ()
97  return
98}
99
100// Check that recursive calls are not inlined.
101// CHECK-LABEL: func @no_inline_recursive
102func.func @no_inline_recursive() {
103  // CHECK: test.functional_region_op
104  // CHECK-NOT: test.functional_region_op
105  %fn = "test.functional_region_op"() ({
106    call @no_inline_recursive() : () -> ()
107    "test.return"() : () -> ()
108  }) : () -> (() -> ())
109  return
110}
111
112// Check that we can convert types for inputs and results as necessary.
113func.func @convert_callee_fn(%arg : i32) -> i32 {
114  return %arg : i32
115}
116func.func @convert_callee_fn_multi_arg(%a : i32, %b : i32) -> () {
117  return
118}
119func.func @convert_callee_fn_multi_res() -> (i32, i32) {
120  %res = arith.constant 0 : i32
121  return %res, %res : i32, i32
122}
123
124// CHECK-LABEL: func @inline_convert_call
125func.func @inline_convert_call() -> i16 {
126  // CHECK: %[[INPUT:.*]] = arith.constant
127  %test_input = arith.constant 0 : i16
128
129  // CHECK: %[[CAST_INPUT:.*]] = "test.cast"(%[[INPUT]]) : (i16) -> i32
130  // CHECK: %[[CAST_RESULT:.*]] = "test.cast"(%[[CAST_INPUT]]) : (i32) -> i16
131  // CHECK-NEXT: return %[[CAST_RESULT]]
132  %res = "test.conversion_call_op"(%test_input) { callee=@convert_callee_fn } : (i16) -> (i16)
133  return %res : i16
134}
135
136func.func @convert_callee_fn_multiblock() -> i32 {
137  cf.br ^bb0
138^bb0:
139  %0 = arith.constant 0 : i32
140  return %0 : i32
141}
142
143// CHECK-LABEL: func @inline_convert_result_multiblock
144func.func @inline_convert_result_multiblock() -> i16 {
145// CHECK:   cf.br ^bb1 {inlined_conversion}
146// CHECK: ^bb1:
147// CHECK:   %[[C:.+]] = arith.constant {inlined_conversion} 0 : i32
148// CHECK:   cf.br ^bb2(%[[C]] : i32)
149// CHECK: ^bb2(%[[BBARG:.+]]: i32):
150// CHECK:   %[[CAST_RESULT:.+]] = "test.cast"(%[[BBARG]]) : (i32) -> i16
151// CHECK:   return %[[CAST_RESULT]] : i16
152
153  %res = "test.conversion_call_op"() { callee=@convert_callee_fn_multiblock } : () -> (i16)
154  return %res : i16
155}
156
157// CHECK-LABEL: func @no_inline_convert_call
158func.func @no_inline_convert_call() {
159  // CHECK: "test.conversion_call_op"
160  %test_input_i16 = arith.constant 0 : i16
161  %test_input_i64 = arith.constant 0 : i64
162  "test.conversion_call_op"(%test_input_i16, %test_input_i64) { callee=@convert_callee_fn_multi_arg } : (i16, i64) -> ()
163
164  // CHECK: "test.conversion_call_op"
165  %res_2:2 = "test.conversion_call_op"() { callee=@convert_callee_fn_multi_res } : () -> (i16, i64)
166  return
167}
168
169// Check that we properly simplify when inlining.
170func.func @simplify_return_constant() -> i32 {
171  %res = arith.constant 0 : i32
172  return %res : i32
173}
174
175func.func @simplify_return_reference() -> (() -> i32) {
176  %res = constant @simplify_return_constant : () -> i32
177  return %res : () -> i32
178}
179
180// INLINE_SIMPLIFY-LABEL: func @inline_simplify
181func.func @inline_simplify() -> i32 {
182  // INLINE_SIMPLIFY-NEXT: %[[CST:.*]] = arith.constant 0 : i32
183  // INLINE_SIMPLIFY-NEXT: return %[[CST]]
184  %fn = call @simplify_return_reference() : () -> (() -> i32)
185  %res = call_indirect %fn() : () -> i32
186  return %res : i32
187}
188
189// CHECK-LABEL: func @no_inline_invalid_call
190func.func @no_inline_invalid_call() -> i32 {
191  %res = "test.conversion_call_op"() { callee=@convert_callee_fn_multiblock, noinline } : () -> (i32)
192  return %res : i32
193}
194
195func.func @gpu_alloc() -> memref<1024xf32> {
196  %m = gpu.alloc [] () : memref<1024xf32>
197  return %m : memref<1024xf32>
198}
199
200// CHECK-LABEL: func @inline_gpu_ops
201func.func @inline_gpu_ops() -> memref<1024xf32> {
202  // CHECK-NEXT: gpu.alloc
203  %m = call @gpu_alloc() : () -> memref<1024xf32>
204  return %m : memref<1024xf32>
205}
206
207// Test block arguments location propagation.
208// Use two call-sites to force cloning.
209func.func @func_with_block_args_location(%arg0 : i32) {
210  cf.br ^bb1(%arg0 : i32)
211^bb1(%x : i32 loc("foo")):
212  "test.foo" (%x) : (i32) -> () loc("bar")
213  return
214}
215
216// INLINE-LOC-LABEL: func @func_with_block_args_location_callee1
217// INLINE-LOC: cf.br
218// INLINE-LOC: ^bb{{[0-9]+}}(%{{.*}}: i32 loc("foo")
219func.func @func_with_block_args_location_callee1(%arg0 : i32) {
220  call @func_with_block_args_location(%arg0) : (i32) -> ()
221  return
222}
223
224// CHECK-LABEL: func @func_with_block_args_location_callee2
225func.func @func_with_block_args_location_callee2(%arg0 : i32) {
226  call @func_with_block_args_location(%arg0) : (i32) -> ()
227  return
228}
229