1// RUN: mlir-opt -convert-std-to-llvm='emit-c-wrappers=1' %s | FileCheck %s
2// RUN: mlir-opt -convert-std-to-llvm %s | FileCheck %s --check-prefix=EMIT_C_ATTRIBUTE
3
4// This tests the default memref calling convention and the emission of C
5// wrappers. We don't need to separate runs because the wrapper-emission
6// version subsumes the calling convention and only adds new functions, that we
7// can also file-check in the same run.
8
9// An external function is transformed into the glue around calling an interface function.
10// CHECK-LABEL: @external
11// CHECK: %[[ALLOC0:.*]]: !llvm.ptr<float>, %[[ALIGN0:.*]]: !llvm.ptr<float>, %[[OFFSET0:.*]]: !llvm.i64, %[[SIZE00:.*]]: !llvm.i64, %[[SIZE01:.*]]: !llvm.i64, %[[STRIDE00:.*]]: !llvm.i64, %[[STRIDE01:.*]]: !llvm.i64,
12// CHECK: %[[ALLOC1:.*]]: !llvm.ptr<float>, %[[ALIGN1:.*]]: !llvm.ptr<float>, %[[OFFSET1:.*]]: !llvm.i64)
13func private @external(%arg0: memref<?x?xf32>, %arg1: memref<f32>)
14  // Populate the descriptor for arg0.
15  // CHECK: %[[DESC00:.*]] = llvm.mlir.undef : !llvm.struct<(ptr<float>, ptr<float>, i64, array<2 x i64>, array<2 x i64>)>
16  // CHECK: %[[DESC01:.*]] = llvm.insertvalue %arg0, %[[DESC00]][0]
17  // CHECK: %[[DESC02:.*]] = llvm.insertvalue %arg1, %[[DESC01]][1]
18  // CHECK: %[[DESC03:.*]] = llvm.insertvalue %arg2, %[[DESC02]][2]
19  // CHECK: %[[DESC04:.*]] = llvm.insertvalue %arg3, %[[DESC03]][3, 0]
20  // CHECK: %[[DESC05:.*]] = llvm.insertvalue %arg5, %[[DESC04]][4, 0]
21  // CHECK: %[[DESC06:.*]] = llvm.insertvalue %arg4, %[[DESC05]][3, 1]
22  // CHECK: %[[DESC07:.*]] = llvm.insertvalue %arg6, %[[DESC06]][4, 1]
23
24  // Allocate on stack and store to comply with C calling convention.
25  // CHECK: %[[C1:.*]] = llvm.mlir.constant(1 : index)
26  // CHECK: %[[DESC0_ALLOCA:.*]] = llvm.alloca %[[C1]] x !llvm.struct<(ptr<float>, ptr<float>, i64, array<2 x i64>, array<2 x i64>)>
27  // CHECK: llvm.store %[[DESC07]], %[[DESC0_ALLOCA]]
28
29  // Populate the descriptor for arg1.
30  // CHECK: %[[DESC10:.*]] = llvm.mlir.undef : !llvm.struct<(ptr<float>, ptr<float>, i64)>
31  // CHECK: %[[DESC11:.*]] = llvm.insertvalue %arg7, %[[DESC10]][0] : !llvm.struct<(ptr<float>, ptr<float>, i64)>
32  // CHECK: %[[DESC12:.*]] = llvm.insertvalue %arg8, %[[DESC11]][1] : !llvm.struct<(ptr<float>, ptr<float>, i64)>
33  // CHECK: %[[DESC13:.*]] = llvm.insertvalue %arg9, %[[DESC12]][2] : !llvm.struct<(ptr<float>, ptr<float>, i64)>
34
35  // Allocate on stack and store to comply with C calling convention.
36  // CHECK: %[[C1:.*]] = llvm.mlir.constant(1 : index)
37  // CHECK: %[[DESC1_ALLOCA:.*]] = llvm.alloca %[[C1]] x !llvm.struct<(ptr<float>, ptr<float>, i64)>
38  // CHECK: llvm.store %[[DESC13]], %[[DESC1_ALLOCA]]
39
40  // Call the interface function.
41  // CHECK: llvm.call @_mlir_ciface_external
42
43// Verify that an interface function is emitted.
44// CHECK-LABEL: llvm.func @_mlir_ciface_external
45// CHECK: (!llvm.ptr<struct<(ptr<float>, ptr<float>, i64, array<2 x i64>, array<2 x i64>)>>, !llvm.ptr<struct<(ptr<float>, ptr<float>, i64)>>)
46
47// Verify that the return value is not affected.
48// CHECK-LABEL: @returner
49// CHECK: -> !llvm.struct<(struct<(ptr<float>, ptr<float>, i64, array<2 x i64>, array<2 x i64>)>, struct<(ptr<float>, ptr<float>, i64)>)>
50func private @returner() -> (memref<?x?xf32>, memref<f32>)
51
52// CHECK-LABEL: @caller
53func @caller() {
54  %0:2 = call @returner() : () -> (memref<?x?xf32>, memref<f32>)
55  // Extract individual values from the descriptor for the first memref.
56  // CHECK: %[[ALLOC0:.*]] = llvm.extractvalue %[[DESC0:.*]][0] : !llvm.struct<(ptr<float>, ptr<float>, i64, array<2 x i64>, array<2 x i64>)>
57  // CHECK: %[[ALIGN0:.*]] = llvm.extractvalue %[[DESC0]][1]
58  // CHECK: %[[OFFSET0:.*]] = llvm.extractvalue %[[DESC0]][2]
59  // CHECK: %[[SIZE00:.*]] = llvm.extractvalue %[[DESC0]][3, 0]
60  // CHECK: %[[SIZE01:.*]] = llvm.extractvalue %[[DESC0]][3, 1]
61  // CHECK: %[[STRIDE00:.*]] = llvm.extractvalue %[[DESC0]][4, 0]
62  // CHECK: %[[STRIDE01:.*]] = llvm.extractvalue %[[DESC0]][4, 1]
63
64  // Extract individual values from the descriptor for the second memref.
65  // CHECK: %[[ALLOC1:.*]] = llvm.extractvalue %[[DESC1:.*]][0] : !llvm.struct<(ptr<float>, ptr<float>, i64)>
66  // CHECK: %[[ALIGN1:.*]] = llvm.extractvalue %[[DESC1]][1]
67  // CHECK: %[[OFFSET1:.*]] = llvm.extractvalue %[[DESC1]][2]
68
69  // Forward the values to the call.
70  // CHECK: llvm.call @external(%[[ALLOC0]], %[[ALIGN0]], %[[OFFSET0]], %[[SIZE00]], %[[SIZE01]], %[[STRIDE00]], %[[STRIDE01]], %[[ALLOC1]], %[[ALIGN1]], %[[OFFSET1]]) : (!llvm.ptr<float>, !llvm.ptr<float>, !llvm.i64, !llvm.i64, !llvm.i64, !llvm.i64, !llvm.i64, !llvm.ptr<float>, !llvm.ptr<float>, !llvm.i64) -> ()
71  call @external(%0#0, %0#1) : (memref<?x?xf32>, memref<f32>) -> ()
72  return
73}
74
75// CHECK-LABEL: @callee
76// EMIT_C_ATTRIBUTE-LABEL: @callee
77func @callee(%arg0: memref<?xf32>, %arg1: index) {
78  %0 = load %arg0[%arg1] : memref<?xf32>
79  return
80}
81
82// Verify that an interface function is emitted.
83// CHECK-LABEL: @_mlir_ciface_callee
84// CHECK: %[[ARG0:.*]]: !llvm.ptr<struct<(ptr<float>, ptr<float>, i64, array<1 x i64>, array<1 x i64>)>>
85  // Load the memref descriptor pointer.
86  // CHECK: %[[DESC:.*]] = llvm.load %[[ARG0]] : !llvm.ptr<struct<(ptr<float>, ptr<float>, i64, array<1 x i64>, array<1 x i64>)>>
87
88  // Extract individual components of the descriptor.
89  // CHECK: %[[ALLOC:.*]] = llvm.extractvalue %[[DESC]][0]
90  // CHECK: %[[ALIGN:.*]] = llvm.extractvalue %[[DESC]][1]
91  // CHECK: %[[OFFSET:.*]] = llvm.extractvalue %[[DESC]][2]
92  // CHECK: %[[SIZE:.*]] = llvm.extractvalue %[[DESC]][3, 0]
93  // CHECK: %[[STRIDE:.*]] = llvm.extractvalue %[[DESC]][4, 0]
94
95  // Forward the descriptor components to the call.
96  // CHECK: llvm.call @callee(%[[ALLOC]], %[[ALIGN]], %[[OFFSET]], %[[SIZE]], %[[STRIDE]], %{{.*}}) : (!llvm.ptr<float>, !llvm.ptr<float>, !llvm.i64, !llvm.i64, !llvm.i64, !llvm.i64) -> ()
97
98//   EMIT_C_ATTRIBUTE-NOT: @mlir_ciface_callee
99
100// CHECK-LABEL: @other_callee
101// EMIT_C_ATTRIBUTE-LABEL: @other_callee
102func @other_callee(%arg0: memref<?xf32>, %arg1: index) attributes { llvm.emit_c_interface } {
103  %0 = load %arg0[%arg1] : memref<?xf32>
104  return
105}
106
107// CHECK: @_mlir_ciface_other_callee
108// CHECK:   llvm.call @other_callee
109
110// EMIT_C_ATTRIBUTE: @_mlir_ciface_other_callee
111// EMIT_C_ATTRIBUTE:   llvm.call @other_callee
112
113//===========================================================================//
114// Calling convention on returning unranked memrefs.
115//===========================================================================//
116
117// CHECK-LABEL: llvm.func @return_var_memref_caller
118func @return_var_memref_caller(%arg0: memref<4x3xf32>) {
119  // CHECK: %[[CALL_RES:.*]] = llvm.call @return_var_memref
120  %0 = call @return_var_memref(%arg0) : (memref<4x3xf32>) -> memref<*xf32>
121
122  // CHECK: %[[ONE:.*]] = llvm.mlir.constant(1 : index)
123  // CHECK: %[[TWO:.*]] = llvm.mlir.constant(2 : index)
124  // These sizes may depend on the data layout, not matching specific values.
125  // CHECK: %[[PTR_SIZE:.*]] = llvm.mlir.constant
126  // CHECK: %[[IDX_SIZE:.*]] = llvm.mlir.constant
127
128  // CHECK: %[[DOUBLE_PTR_SIZE:.*]] = llvm.mul %[[TWO]], %[[PTR_SIZE]]
129  // CHECK: %[[RANK:.*]] = llvm.extractvalue %[[CALL_RES]][0] : !llvm.struct<(i64, ptr<i8>)>
130  // CHECK: %[[DOUBLE_RANK:.*]] = llvm.mul %[[TWO]], %[[RANK]]
131  // CHECK: %[[DOUBLE_RANK_INC:.*]] = llvm.add %[[DOUBLE_RANK]], %[[ONE]]
132  // CHECK: %[[TABLES_SIZE:.*]] = llvm.mul %[[DOUBLE_RANK_INC]], %[[IDX_SIZE]]
133  // CHECK: %[[ALLOC_SIZE:.*]] = llvm.add %[[DOUBLE_PTR_SIZE]], %[[TABLES_SIZE]]
134  // CHECK: %[[FALSE:.*]] = llvm.mlir.constant(false)
135  // CHECK: %[[ALLOCA:.*]] = llvm.alloca %[[ALLOC_SIZE]] x !llvm.i8
136  // CHECK: %[[SOURCE:.*]] = llvm.extractvalue %[[CALL_RES]][1]
137  // CHECK: "llvm.intr.memcpy"(%[[ALLOCA]], %[[SOURCE]], %[[ALLOC_SIZE]], %[[FALSE]])
138  // CHECK: llvm.call @free(%[[SOURCE]])
139  // CHECK: %[[DESC:.*]] = llvm.mlir.undef : !llvm.struct<(i64, ptr<i8>)>
140  // CHECK: %[[RANK:.*]] = llvm.extractvalue %[[CALL_RES]][0] : !llvm.struct<(i64, ptr<i8>)>
141  // CHECK: %[[DESC_1:.*]] = llvm.insertvalue %[[RANK]], %[[DESC]][0]
142  // CHECK: llvm.insertvalue %[[ALLOCA]], %[[DESC_1]][1]
143  return
144}
145
146// CHECK-LABEL: llvm.func @return_var_memref
147func @return_var_memref(%arg0: memref<4x3xf32>) -> memref<*xf32> {
148  // Match the construction of the unranked descriptor.
149  // CHECK: %[[ALLOCA:.*]] = llvm.alloca
150  // CHECK: %[[MEMORY:.*]] = llvm.bitcast %[[ALLOCA]]
151  // CHECK: %[[DESC_0:.*]] = llvm.mlir.undef : !llvm.struct<(i64, ptr<i8>)>
152  // CHECK: %[[DESC_1:.*]] = llvm.insertvalue %{{.*}}, %[[DESC_0]][0]
153  // CHECK: %[[DESC_2:.*]] = llvm.insertvalue %[[MEMORY]], %[[DESC_1]][1]
154  %0 = memref_cast %arg0: memref<4x3xf32> to memref<*xf32>
155
156  // CHECK: %[[ONE:.*]] = llvm.mlir.constant(1 : index)
157  // CHECK: %[[TWO:.*]] = llvm.mlir.constant(2 : index)
158  // These sizes may depend on the data layout, not matching specific values.
159  // CHECK: %[[PTR_SIZE:.*]] = llvm.mlir.constant
160  // CHECK: %[[IDX_SIZE:.*]] = llvm.mlir.constant
161
162  // CHECK: %[[DOUBLE_PTR_SIZE:.*]] = llvm.mul %[[TWO]], %[[PTR_SIZE]]
163  // CHECK: %[[RANK:.*]] = llvm.extractvalue %[[DESC_2]][0] : !llvm.struct<(i64, ptr<i8>)>
164  // CHECK: %[[DOUBLE_RANK:.*]] = llvm.mul %[[TWO]], %[[RANK]]
165  // CHECK: %[[DOUBLE_RANK_INC:.*]] = llvm.add %[[DOUBLE_RANK]], %[[ONE]]
166  // CHECK: %[[TABLES_SIZE:.*]] = llvm.mul %[[DOUBLE_RANK_INC]], %[[IDX_SIZE]]
167  // CHECK: %[[ALLOC_SIZE:.*]] = llvm.add %[[DOUBLE_PTR_SIZE]], %[[TABLES_SIZE]]
168  // CHECK: %[[FALSE:.*]] = llvm.mlir.constant(false)
169  // CHECK: %[[ALLOCATED:.*]] = llvm.call @malloc(%[[ALLOC_SIZE]])
170  // CHECK: %[[SOURCE:.*]] = llvm.extractvalue %[[DESC_2]][1]
171  // CHECK: "llvm.intr.memcpy"(%[[ALLOCATED]], %[[SOURCE]], %[[ALLOC_SIZE]], %[[FALSE]])
172  // CHECK: %[[NEW_DESC:.*]] = llvm.mlir.undef : !llvm.struct<(i64, ptr<i8>)>
173  // CHECK: %[[RANK:.*]] = llvm.extractvalue %[[DESC_2]][0] : !llvm.struct<(i64, ptr<i8>)>
174  // CHECK: %[[NEW_DESC_1:.*]] = llvm.insertvalue %[[RANK]], %[[NEW_DESC]][0]
175  // CHECK: %[[NEW_DESC_2:.*]] = llvm.insertvalue %[[ALLOCATED]], %[[NEW_DESC_1]][1]
176  // CHECK: llvm.return %[[NEW_DESC_2]]
177  return %0 : memref<*xf32>
178}
179
180// CHECK-LABEL: llvm.func @return_two_var_memref_caller
181func @return_two_var_memref_caller(%arg0: memref<4x3xf32>) {
182  // Only check that we create two different descriptors using different
183  // memory, and deallocate both sources. The size computation is same as for
184  // the single result.
185  // CHECK: %[[CALL_RES:.*]] = llvm.call @return_two_var_memref
186  // CHECK: %[[RES_1:.*]] = llvm.extractvalue %[[CALL_RES]][0]
187  // CHECK: %[[RES_2:.*]] = llvm.extractvalue %[[CALL_RES]][1]
188  %0:2 = call @return_two_var_memref(%arg0) : (memref<4x3xf32>) -> (memref<*xf32>, memref<*xf32>)
189
190  // CHECK: %[[ALLOCA_1:.*]] = llvm.alloca %{{.*}} x !llvm.i8
191  // CHECK: %[[SOURCE_1:.*]] = llvm.extractvalue %[[RES_1:.*]][1] : ![[DESC_TYPE:.*]]
192  // CHECK: "llvm.intr.memcpy"(%[[ALLOCA_1]], %[[SOURCE_1]], %{{.*}}, %[[FALSE:.*]])
193  // CHECK: llvm.call @free(%[[SOURCE_1]])
194  // CHECK: %[[DESC_1:.*]] = llvm.mlir.undef : ![[DESC_TYPE]]
195  // CHECK: %[[DESC_11:.*]] = llvm.insertvalue %{{.*}}, %[[DESC_1]][0]
196  // CHECK: llvm.insertvalue %[[ALLOCA_1]], %[[DESC_11]][1]
197
198  // CHECK: %[[ALLOCA_2:.*]] = llvm.alloca %{{.*}} x !llvm.i8
199  // CHECK: %[[SOURCE_2:.*]] = llvm.extractvalue %[[RES_2:.*]][1]
200  // CHECK: "llvm.intr.memcpy"(%[[ALLOCA_2]], %[[SOURCE_2]], %{{.*}}, %[[FALSE]])
201  // CHECK: llvm.call @free(%[[SOURCE_2]])
202  // CHECK: %[[DESC_2:.*]] = llvm.mlir.undef : ![[DESC_TYPE]]
203  // CHECK: %[[DESC_21:.*]] = llvm.insertvalue %{{.*}}, %[[DESC_2]][0]
204  // CHECK: llvm.insertvalue %[[ALLOCA_2]], %[[DESC_21]][1]
205  return
206}
207
208// CHECK-LABEL: llvm.func @return_two_var_memref
209func @return_two_var_memref(%arg0: memref<4x3xf32>) -> (memref<*xf32>, memref<*xf32>) {
210  // Match the construction of the unranked descriptor.
211  // CHECK: %[[ALLOCA:.*]] = llvm.alloca
212  // CHECK: %[[MEMORY:.*]] = llvm.bitcast %[[ALLOCA]]
213  // CHECK: %[[DESC_0:.*]] = llvm.mlir.undef : !llvm.struct<(i64, ptr<i8>)>
214  // CHECK: %[[DESC_1:.*]] = llvm.insertvalue %{{.*}}, %[[DESC_0]][0]
215  // CHECK: %[[DESC_2:.*]] = llvm.insertvalue %[[MEMORY]], %[[DESC_1]][1]
216  %0 = memref_cast %arg0 : memref<4x3xf32> to memref<*xf32>
217
218  // Only check that we allocate the memory for each operand of the "return"
219  // separately, even if both operands are the same value. The calling
220  // convention requires the caller to free them and the caller cannot know
221  // whether they are the same value or not.
222  // CHECK: %[[ALLOCATED_1:.*]] = llvm.call @malloc(%{{.*}})
223  // CHECK: %[[SOURCE_1:.*]] = llvm.extractvalue %[[DESC_2]][1]
224  // CHECK: "llvm.intr.memcpy"(%[[ALLOCATED_1]], %[[SOURCE_1]], %{{.*}}, %[[FALSE:.*]])
225  // CHECK: %[[RES_1:.*]] = llvm.mlir.undef
226  // CHECK: %[[RES_11:.*]] = llvm.insertvalue %{{.*}}, %[[RES_1]][0]
227  // CHECK: %[[RES_12:.*]] = llvm.insertvalue %[[ALLOCATED_1]], %[[RES_11]][1]
228
229  // CHECK: %[[ALLOCATED_2:.*]] = llvm.call @malloc(%{{.*}})
230  // CHECK: %[[SOURCE_2:.*]] = llvm.extractvalue %[[DESC_2]][1]
231  // CHECK: "llvm.intr.memcpy"(%[[ALLOCATED_2]], %[[SOURCE_2]], %{{.*}}, %[[FALSE]])
232  // CHECK: %[[RES_2:.*]] = llvm.mlir.undef
233  // CHECK: %[[RES_21:.*]] = llvm.insertvalue %{{.*}}, %[[RES_2]][0]
234  // CHECK: %[[RES_22:.*]] = llvm.insertvalue %[[ALLOCATED_2]], %[[RES_21]][1]
235
236  // CHECK: %[[RESULTS:.*]] = llvm.mlir.undef : !llvm.struct<(struct<(i64, ptr<i8>)>, struct<(i64, ptr<i8>)>)>
237  // CHECK: %[[RESULTS_1:.*]] = llvm.insertvalue %[[RES_12]], %[[RESULTS]]
238  // CHECK: %[[RESULTS_2:.*]] = llvm.insertvalue %[[RES_22]], %[[RESULTS_1]]
239  // CHECK: llvm.return %[[RESULTS_2]]
240  return %0, %0 : memref<*xf32>, memref<*xf32>
241}
242
243