--- /dev/null
+// RUN: mlir-opt %s \
+// RUN: -convert-scf-to-std -convert-vector-to-scf \
+// RUN: -convert-linalg-to-llvm -convert-vector-to-llvm | \
+// RUN: SPARSE_MATRIX0="%mlir_integration_test_dir/data/test.mtx" \
+// RUN: mlir-cpu-runner \
+// RUN: -e entry -entry-point-result=void \
+// RUN: -shared-libs=%mlir_integration_test_dir/libmlir_c_runner_utils%shlibext | \
+// RUN: FileCheck %s
+
+module {
+ func @openMatrix(!llvm.ptr<i8>, memref<index>, memref<index>, memref<index>) -> ()
+ func @readMatrixItem(memref<index>, memref<index>, memref<f64>) -> ()
+ func @closeMatrix() -> ()
+ func @getSparseMatrix(index) -> (!llvm.ptr<i8>)
+
+ func @entry() {
+ %d0 = constant 0.0 : f64
+ %c0 = constant 0 : index
+ %c1 = constant 1 : index
+ %c5 = constant 5 : index
+ %m = alloc() : memref<index>
+ %n = alloc() : memref<index>
+ %nnz = alloc() : memref<index>
+ %i = alloc() : memref<index>
+ %j = alloc() : memref<index>
+ %d = alloc() : memref<f64>
+
+ //
+ // Read the header of a sparse matrix. This yields the
+ // size (m x n) and number of nonzero elements (nnz).
+ //
+ %file = call @getSparseMatrix(%c0) : (index) -> (!llvm.ptr<i8>)
+ call @openMatrix(%file, %m, %n, %nnz)
+ : (!llvm.ptr<i8>, memref<index>,
+ memref<index>, memref<index>) -> ()
+ %M = load %m[] : memref<index>
+ %N = load %n[] : memref<index>
+ %Z = load %nnz[] : memref<index>
+
+ //
+ // At this point, code should prepare a proper sparse storage
+ // scheme for an m x n matrix with nnz nonzero elements. For
+ // simplicity, however, here we simply set up a dense matrix.
+ //
+ %a = alloc(%M, %N) : memref<?x?xf64>
+ scf.for %ii = %c0 to %M step %c1 {
+ scf.for %jj = %c0 to %N step %c1 {
+ store %d0, %a[%ii, %jj] : memref<?x?xf64>
+ }
+ }
+
+ //
+ // Now we are ready to read in the nonzero elements of the
+ // sparse matrix and insert these into a sparse storage
+ // scheme. In this example, we simply insert them in the
+ // dense matrix.
+ //
+ scf.for %k = %c0 to %Z step %c1 {
+ call @readMatrixItem(%i, %j, %d)
+ : (memref<index>, memref<index>, memref<f64>) -> ()
+ %I = load %i[] : memref<index>
+ %J = load %j[] : memref<index>
+ %D = load %d[] : memref<f64>
+ store %D, %a[%I, %J] : memref<?x?xf64>
+ }
+ call @closeMatrix() : () -> ()
+
+ //
+ // Verify that the results are as expected.
+ //
+ %A = vector.transfer_read %a[%c0, %c0], %d0 : memref<?x?xf64>, vector<5x5xf64>
+ vector.print %M : index
+ vector.print %N : index
+ vector.print %Z : index
+ vector.print %A : vector<5x5xf64>
+ //
+ // CHECK: 5
+ // CHECK: 5
+ // CHECK: 9
+ //
+ // CHECK: ( ( 1, 0, 0, 1.4, 0 ),
+ // CHECK-SAME: ( 0, 2, 0, 0, 2.5 ),
+ // CHECK-SAME: ( 0, 0, 3, 0, 0 ),
+ // CHECK-SAME: ( 4.1, 0, 0, 4, 0 ),
+ // CHECK-SAME: ( 0, 5.2, 0, 0, 5 ) )
+
+ //
+ // Free.
+ //
+ dealloc %m : memref<index>
+ dealloc %n : memref<index>
+ dealloc %nnz : memref<index>
+ dealloc %i : memref<index>
+ dealloc %j : memref<index>
+ dealloc %d : memref<f64>
+ dealloc %a : memref<?x?xf64>
+
+ return
+ }
+}
--- /dev/null
+//===- SparseUtils.cpp - Sparse Utils for MLIR execution ------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a light-weight runtime library that is useful for
+// sparse tensor manipulations. The functionality provided in this library
+// is meant to simplify benchmarking, testing, and debugging MLIR code that
+// operates on sparse tensors. The provided functionality is **not** part
+// of core MLIR, however.
+//
+//===----------------------------------------------------------------------===//
+
+#include <cctype>
+#include <cinttypes>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+//===----------------------------------------------------------------------===//
+//
+// Internal support for reading matrices in the Matrix Market Exchange Format.
+// See https://math.nist.gov/MatrixMarket for details on this format.
+//
+//===----------------------------------------------------------------------===//
+
+// Helper to convert string to lower case.
+static char *toLower(char *token) {
+ for (char *c = token; *c; c++)
+ *c = tolower(*c);
+ return token;
+}
+
+// Read the header of a general sparse matrix of type real.
+//
+// TODO: support other formats as well?
+//
+static void readHeader(FILE *file, char *name, uint64_t *m, uint64_t *n,
+ uint64_t *nnz) {
+ char line[1025];
+ char header[64];
+ char object[64];
+ char format[64];
+ char field[64];
+ char symmetry[64];
+ // Read header line.
+ if (fscanf(file, "%63s %63s %63s %63s %63s\n", header, object, format, field,
+ symmetry) != 5) {
+ fprintf(stderr, "Corrupt header in %s\n", name);
+ exit(1);
+ }
+ // Make sure this is a general sparse matrix.
+ if (strcmp(toLower(header), "%%matrixmarket") ||
+ strcmp(toLower(object), "matrix") ||
+ strcmp(toLower(format), "coordinate") || strcmp(toLower(field), "real") ||
+ strcmp(toLower(symmetry), "general")) {
+ fprintf(stderr,
+ "Cannot find a general sparse matrix with type real in %s\n", name);
+ exit(1);
+ }
+ // Skip comments.
+ while (1) {
+ if (!fgets(line, 1025, file)) {
+ fprintf(stderr, "Cannot find data in %s\n", name);
+ exit(1);
+ }
+ if (line[0] != '%')
+ break;
+ }
+ // Next line contains M N NNZ.
+ if (sscanf(line, "%" PRIu64 "%" PRIu64 "%" PRIu64, m, n, nnz) != 3) {
+ fprintf(stderr, "Cannot find size in %s\n", name);
+ exit(1);
+ }
+}
+
+// Read next data item.
+static void readItem(FILE *file, char *name, uint64_t *i, uint64_t *j,
+ double *d) {
+ if (fscanf(file, "%" PRIu64 " %" PRIu64 " %lg\n", i, j, d) != 3) {
+ fprintf(stderr, "Cannot find next data item in %s\n", name);
+ exit(1);
+ }
+ // Translate 1-based to 0-based.
+ *i = *i - 1;
+ *j = *j - 1;
+}
+
+//===----------------------------------------------------------------------===//
+//
+// Public API of the sparse runtime library.
+//
+// Enables MLIR code to read a matrix in Matrix Market Exchange Format
+// as follows:
+//
+// call @openMatrix("A.mtx", %m, %n, %nnz) : (!llvm.ptr<i8>,
+// memref<index>,
+// memref<index>,
+// memref<index>) -> ()
+// .... prepare reading in m x n matrix A with nnz nonzero elements ....
+// %u = load %nnz[] : memref<index>
+// scf.for %k = %c0 to %u step %c1 {
+// call @readMatrixItem(%i, %j, %d) : (memref<index>,
+// memref<index>, memref<f64>) -> ()
+// .... process next nonzero element A[i][j] = d ....
+// }
+// call @closeMatrix() : () -> ()
+//
+// The implementation is *not* thread-safe. Also, only *one* matrix file can
+// be open at the time. A matrix file must be closed before reading in a next.
+//
+// Note that input parameters mimic the layout of a MemRef<T>:
+// struct MemRef {
+// T *base;
+// T *data;
+// int64_t off;
+// }
+//===----------------------------------------------------------------------===//
+
+// Currently open matrix. This is *not* thread-safe or re-entrant.
+static FILE *sparseFile = nullptr;
+static char *sparseFilename = nullptr;
+
+extern "C" void openMatrix(char *filename, uint64_t *mbase, uint64_t *mdata,
+ int64_t moff, uint64_t *nbase, uint64_t *ndata,
+ int64_t noff, uint64_t *nnzbase, uint64_t *nnzdata,
+ int64_t nnzoff) {
+ if (sparseFile != nullptr) {
+ fprintf(stderr, "Other file still open %s vs. %s\n", sparseFilename,
+ filename);
+ exit(1);
+ }
+ sparseFile = fopen(filename, "r");
+ if (!sparseFile) {
+ fprintf(stderr, "Cannot find %s\n", filename);
+ exit(1);
+ }
+ sparseFilename = filename;
+ readHeader(sparseFile, filename, mdata, ndata, nnzdata);
+}
+
+extern "C" void readMatrixItem(uint64_t *ibase, uint64_t *idata, int64_t ioff,
+ uint64_t *jbase, uint64_t *jdata, int64_t joff,
+ double *dbase, double *ddata, int64_t doff) {
+ if (sparseFile == nullptr) {
+ fprintf(stderr, "Cannot read item from unopened matrix\n");
+ exit(1);
+ }
+ readItem(sparseFile, sparseFilename, idata, jdata, ddata);
+}
+
+extern "C" void closeMatrix() {
+ if (sparseFile == nullptr) {
+ fprintf(stderr, "Cannot close unopened matrix\n");
+ exit(1);
+ }
+ fclose(sparseFile);
+ sparseFile = nullptr;
+ sparseFilename = nullptr;
+}
+
+// Helper method to read sparse matrix filenames from the environment, defined
+// with the naming convention ${SPARSE_MATRIX0}, ${SPARSE_MATRIX1}, etc.
+extern "C" char *getSparseMatrix(uint64_t id) {
+ char var[80];
+ sprintf(var, "SPARSE_MATRIX%lu", id);
+ char *env = getenv(var);
+ return env;
+}