// Split the MLIR string: this will produce %t/input.mlir // RUN: split-file %s %t // Compile the MLIR file to LLVM: // RUN: mlir-opt %t/input.mlir \ // RUN: -lower-affine -convert-scf-to-cf -finalize-memref-to-llvm \ // RUN: -convert-func-to-llvm -reconcile-unrealized-casts \ // RUN: | mlir-translate --mlir-to-llvmir -o %t.ll // Generate an object file for the MLIR code // RUN: llc %t.ll -o %t.o -filetype=obj // Compile the current C file and link it to the MLIR code: // RUN: %host_cc %s %t.o -o %t.exe // Exec // RUN: %t.exe | FileCheck %s /* MLIR_BEGIN //--- input.mlir // Performs: arg0[i, j] = arg0[i, j] + arg1[i, j] func.func private @add_memref(%arg0: memref, %arg1: memref) -> i64 attributes {llvm.emit_c_interface} { %c0 = arith.constant 0 : index %c1 = arith.constant 1 : index %dimI = memref.dim %arg0, %c0 : memref %dimJ = memref.dim %arg0, %c1 : memref affine.for %i = 0 to %dimI { affine.for %j = 0 to %dimJ { %load0 = memref.load %arg0[%i, %j] : memref %load1 = memref.load %arg1[%i, %j] : memref %add = arith.addf %load0, %load1 : f64 affine.store %add, %arg0[%i, %j] : memref } } %c42 = arith.constant 42 : i64 return %c42 : i64 } //--- end_input.mlir MLIR_END */ #include #include // Define the API for the MLIR function, see // https://mlir.llvm.org/docs/TargetLLVMIR/#calling-conventions for details. // // The function takes two 2D memref, the signature in MLIR LLVM dialect will be: // llvm.func @add_memref( // // First Memref (%arg0) // %allocated_ptr0: !llvm.ptr, %aligned_ptr0: !llvm.ptr, // %offset0: i64, %size0_d0: i64, %size0_d1: i64, %stride0_d0: i64, // %stride0_d1: i64, // // Second Memref (%arg1) // %allocated_ptr1: !llvm.ptr, %aligned_ptr1: !llvm.ptr, // %offset1: i64, %size1_d0: i64, %size1_d1: i64, %stride1_d0: i64, // %stride1_d1: i64, // long long add_memref(double *allocated_ptr0, double *aligned_ptr0, intptr_t offset0, intptr_t size0_d0, intptr_t size0_d1, intptr_t stride0_d0, intptr_t stride0_d1, // Second Memref (%arg1) double *allocated_ptr1, double *aligned_ptr1, intptr_t offset1, intptr_t size1_d0, intptr_t size1_d1, intptr_t stride1_d0, intptr_t stride1_d1); // The llvm.emit_c_interface will also trigger emission of another wrapper: // llvm.func @_mlir_ciface_add_memref( // %arg0: !llvm.ptr, ptr, i64, // array<2 x i64>, array<2 x i64>)>>, // %arg1: !llvm.ptr, ptr, i64, // array<2 x i64>, array<2 x i64>)>>) // -> i64 typedef struct { double *allocated; double *aligned; intptr_t offset; intptr_t size[2]; intptr_t stride[2]; } memref_2d_descriptor; long long _mlir_ciface_add_memref(memref_2d_descriptor *arg0, memref_2d_descriptor *arg1); #define N 4 #define M 8 double arg0[N][M]; double arg1[N][M]; void dump() { for (int i = 0; i < N; i++) { printf("["); for (int j = 0; j < M; j++) printf("%d,\t", (int)arg0[i][j]); printf("] ["); for (int j = 0; j < M; j++) printf("%d,\t", (int)arg1[i][j]); printf("]\n"); } } int main() { int count = 0; for (int i = 0; i < N; i++) { for (int j = 0; j < M; j++) { arg0[i][j] = count++; arg1[i][j] = count++; } } printf("Before:\n"); dump(); // clang-format off // CHECK-LABEL: Before: // CHECK: [0, 2, 4, 6, 8, 10, 12, 14, ] [1, 3, 5, 7, 9, 11, 13, 15, ] // CHECK: [16, 18, 20, 22, 24, 26, 28, 30, ] [17, 19, 21, 23, 25, 27, 29, 31, ] // CHECK: [32, 34, 36, 38, 40, 42, 44, 46, ] [33, 35, 37, 39, 41, 43, 45, 47, ] // CHECK: [48, 50, 52, 54, 56, 58, 60, 62, ] [49, 51, 53, 55, 57, 59, 61, 63, ] // clang-format on // Call into MLIR. long long result = add_memref((double *)arg0, (double *)arg0, 0, N, M, M, 0, // (double *)arg1, (double *)arg1, 0, N, M, M, 0); // CHECK-LABEL: Result: // CHECK: 42 printf("Result: %d\n", (int)result); printf("After:\n"); dump(); // clang-format off // CHECK-LABEL: After: // CHECK: [1, 5, 9, 13, 17, 21, 25, 29, ] [1, 3, 5, 7, 9, 11, 13, 15, ] // CHECK: [33, 37, 41, 45, 49, 53, 57, 61, ] [17, 19, 21, 23, 25, 27, 29, 31, ] // CHECK: [65, 69, 73, 77, 81, 85, 89, 93, ] [33, 35, 37, 39, 41, 43, 45, 47, ] // CHECK: [97, 101, 105, 109, 113, 117, 121, 125, ] [49, 51, 53, 55, 57, 59, 61, 63, ] // clang-format on // Reset the input and re-apply the same function use the C API wrapper. count = 0; for (int i = 0; i < N; i++) { for (int j = 0; j < M; j++) { arg0[i][j] = count++; arg1[i][j] = count++; } } // Call into MLIR. memref_2d_descriptor arg0_descriptor = { (double *)arg0, (double *)arg0, 0, N, M, M, 0}; memref_2d_descriptor arg1_descriptor = { (double *)arg1, (double *)arg1, 0, N, M, M, 0}; result = _mlir_ciface_add_memref(&arg0_descriptor, &arg1_descriptor); // CHECK-LABEL: Result2: // CHECK: 42 printf("Result2: %d\n", (int)result); printf("After2:\n"); dump(); // clang-format off // CHECK-LABEL: After2: // CHECK: [1, 5, 9, 13, 17, 21, 25, 29, ] [1, 3, 5, 7, 9, 11, 13, 15, ] // CHECK: [33, 37, 41, 45, 49, 53, 57, 61, ] [17, 19, 21, 23, 25, 27, 29, 31, ] // CHECK: [65, 69, 73, 77, 81, 85, 89, 93, ] [33, 35, 37, 39, 41, 43, 45, 47, ] // CHECK: [97, 101, 105, 109, 113, 117, 121, 125, ] [49, 51, 53, 55, 57, 59, 61, 63, ] // clang-format on return 0; }