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