//===- 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 "mlir/ExecutionEngine/CRunnerUtils.h" #ifdef MLIR_CRUNNERUTILS_DEFINE_FUNCTIONS #include #include #include #include #include //===----------------------------------------------------------------------===// // // 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, // memref, // memref, // memref) -> () // .... prepare reading in m x n matrix A with nnz nonzero elements .... // %u = load %nnz[] : memref // scf.for %k = %c0 to %u step %c1 { // call @readMatrixItem(%i, %j, %d) : (memref, // memref, memref) -> () // .... 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 in the "MLIRized" version of a function mimic // the data layout of a MemRef: // // 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 openMatrixC(char *filename, uint64_t *mdata, uint64_t *ndata, uint64_t *nnzdata) { 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); } // "MLIRized" version. 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) { openMatrixC(filename, mdata, ndata, nnzdata); } extern "C" void readMatrixItemC(uint64_t *idata, uint64_t *jdata, double *ddata) { if (sparseFile == nullptr) { fprintf(stderr, "Cannot read item from unopened matrix\n"); exit(1); } readItem(sparseFile, sparseFilename, idata, jdata, ddata); } // "MLIRized" version. 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) { readMatrixItemC(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 matrix filenames from the environment, defined // with the naming convention ${MATRIX0}, ${MATRIX1}, etc. extern "C" char *getMatrix(uint64_t id) { char var[80]; sprintf(var, "MATRIX%" PRIu64, id); char *env = getenv(var); return env; } #endif // MLIR_CRUNNERUTILS_DEFINE_FUNCTIONS