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