1 //===- SparseUtils.cpp - Sparse Utils for MLIR execution ------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file implements a light-weight runtime library that is useful for
10 // sparse tensor manipulations. The functionality provided in this library
11 // is meant to simplify benchmarking, testing, and debugging MLIR code that
12 // operates on sparse tensors. The provided functionality is **not** part
13 // of core MLIR, however.
14 //
15 //===----------------------------------------------------------------------===//
16
17 #include "mlir/ExecutionEngine/CRunnerUtils.h"
18
19 #ifdef MLIR_CRUNNERUTILS_DEFINE_FUNCTIONS
20
21 #include <cctype>
22 #include <cinttypes>
23 #include <cstdio>
24 #include <cstdlib>
25 #include <cstring>
26
27 //===----------------------------------------------------------------------===//
28 //
29 // Internal support for reading matrices in the Matrix Market Exchange Format.
30 // See https://math.nist.gov/MatrixMarket for details on this format.
31 //
32 //===----------------------------------------------------------------------===//
33
34 // Helper to convert string to lower case.
toLower(char * token)35 static char *toLower(char *token) {
36 for (char *c = token; *c; c++)
37 *c = tolower(*c);
38 return token;
39 }
40
41 // Read the header of a general sparse matrix of type real.
42 //
43 // TODO: support other formats as well?
44 //
readHeader(FILE * file,char * name,uint64_t * m,uint64_t * n,uint64_t * nnz)45 static void readHeader(FILE *file, char *name, uint64_t *m, uint64_t *n,
46 uint64_t *nnz) {
47 char line[1025];
48 char header[64];
49 char object[64];
50 char format[64];
51 char field[64];
52 char symmetry[64];
53 // Read header line.
54 if (fscanf(file, "%63s %63s %63s %63s %63s\n", header, object, format, field,
55 symmetry) != 5) {
56 fprintf(stderr, "Corrupt header in %s\n", name);
57 exit(1);
58 }
59 // Make sure this is a general sparse matrix.
60 if (strcmp(toLower(header), "%%matrixmarket") ||
61 strcmp(toLower(object), "matrix") ||
62 strcmp(toLower(format), "coordinate") || strcmp(toLower(field), "real") ||
63 strcmp(toLower(symmetry), "general")) {
64 fprintf(stderr,
65 "Cannot find a general sparse matrix with type real in %s\n", name);
66 exit(1);
67 }
68 // Skip comments.
69 while (1) {
70 if (!fgets(line, 1025, file)) {
71 fprintf(stderr, "Cannot find data in %s\n", name);
72 exit(1);
73 }
74 if (line[0] != '%')
75 break;
76 }
77 // Next line contains M N NNZ.
78 if (sscanf(line, "%" PRIu64 "%" PRIu64 "%" PRIu64, m, n, nnz) != 3) {
79 fprintf(stderr, "Cannot find size in %s\n", name);
80 exit(1);
81 }
82 }
83
84 // Read next data item.
readItem(FILE * file,char * name,uint64_t * i,uint64_t * j,double * d)85 static void readItem(FILE *file, char *name, uint64_t *i, uint64_t *j,
86 double *d) {
87 if (fscanf(file, "%" PRIu64 " %" PRIu64 " %lg\n", i, j, d) != 3) {
88 fprintf(stderr, "Cannot find next data item in %s\n", name);
89 exit(1);
90 }
91 // Translate 1-based to 0-based.
92 *i = *i - 1;
93 *j = *j - 1;
94 }
95
96 //===----------------------------------------------------------------------===//
97 //
98 // Public API of the sparse runtime library.
99 //
100 // Enables MLIR code to read a matrix in Matrix Market Exchange Format
101 // as follows:
102 //
103 // call @openMatrix("A.mtx", %m, %n, %nnz) : (!llvm.ptr<i8>,
104 // memref<index>,
105 // memref<index>,
106 // memref<index>) -> ()
107 // .... prepare reading in m x n matrix A with nnz nonzero elements ....
108 // %u = load %nnz[] : memref<index>
109 // scf.for %k = %c0 to %u step %c1 {
110 // call @readMatrixItem(%i, %j, %d) : (memref<index>,
111 // memref<index>, memref<f64>) -> ()
112 // .... process next nonzero element A[i][j] = d ....
113 // }
114 // call @closeMatrix() : () -> ()
115 //
116 // The implementation is *not* thread-safe. Also, only *one* matrix file can
117 // be open at the time. A matrix file must be closed before reading in a next.
118 //
119 // Note that input parameters in the "MLIRized" version of a function mimic
120 // the data layout of a MemRef<T>:
121 //
122 // struct MemRef {
123 // T *base;
124 // T *data;
125 // int64_t off;
126 // }
127 //
128 //===----------------------------------------------------------------------===//
129
130 // Currently open matrix. This is *not* thread-safe or re-entrant.
131 static FILE *sparseFile = nullptr;
132 static char *sparseFilename = nullptr;
133
openMatrixC(char * filename,uint64_t * mdata,uint64_t * ndata,uint64_t * nnzdata)134 extern "C" void openMatrixC(char *filename, uint64_t *mdata, uint64_t *ndata,
135 uint64_t *nnzdata) {
136 if (sparseFile != nullptr) {
137 fprintf(stderr, "Other file still open %s vs. %s\n", sparseFilename,
138 filename);
139 exit(1);
140 }
141 sparseFile = fopen(filename, "r");
142 if (!sparseFile) {
143 fprintf(stderr, "Cannot find %s\n", filename);
144 exit(1);
145 }
146 sparseFilename = filename;
147 readHeader(sparseFile, filename, mdata, ndata, nnzdata);
148 }
149
150 // "MLIRized" version.
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)151 extern "C" void openMatrix(char *filename, uint64_t *mbase, uint64_t *mdata,
152 int64_t moff, uint64_t *nbase, uint64_t *ndata,
153 int64_t noff, uint64_t *nnzbase, uint64_t *nnzdata,
154 int64_t nnzoff) {
155 openMatrixC(filename, mdata, ndata, nnzdata);
156 }
157
readMatrixItemC(uint64_t * idata,uint64_t * jdata,double * ddata)158 extern "C" void readMatrixItemC(uint64_t *idata, uint64_t *jdata,
159 double *ddata) {
160 if (sparseFile == nullptr) {
161 fprintf(stderr, "Cannot read item from unopened matrix\n");
162 exit(1);
163 }
164 readItem(sparseFile, sparseFilename, idata, jdata, ddata);
165 }
166
167 // "MLIRized" version.
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)168 extern "C" void readMatrixItem(uint64_t *ibase, uint64_t *idata, int64_t ioff,
169 uint64_t *jbase, uint64_t *jdata, int64_t joff,
170 double *dbase, double *ddata, int64_t doff) {
171 readMatrixItemC(idata, jdata, ddata);
172 }
173
closeMatrix()174 extern "C" void closeMatrix() {
175 if (sparseFile == nullptr) {
176 fprintf(stderr, "Cannot close unopened matrix\n");
177 exit(1);
178 }
179 fclose(sparseFile);
180 sparseFile = nullptr;
181 sparseFilename = nullptr;
182 }
183
184 // Helper method to read matrix filenames from the environment, defined
185 // with the naming convention ${MATRIX0}, ${MATRIX1}, etc.
getMatrix(uint64_t id)186 extern "C" char *getMatrix(uint64_t id) {
187 char var[80];
188 sprintf(var, "MATRIX%" PRIu64, id);
189 char *env = getenv(var);
190 return env;
191 }
192
193 #endif // MLIR_CRUNNERUTILS_DEFINE_FUNCTIONS
194