brintos

brintos / llvm-project-archived public Read only

0
0
Text · 25.0 KiB · f160b0f Raw
515 lines · cpp
1//===- SparseTensorRuntime.cpp - SparseTensor runtime support lib ---------===//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-exception6//7//===----------------------------------------------------------------------===//8//9// This file implements a light-weight runtime support library for10// manipulating sparse tensors from MLIR.  More specifically, it provides11// C-API wrappers so that MLIR-generated code can call into the C++ runtime12// support library.  The functionality provided in this library is meant13// to simplify benchmarking, testing, and debugging of MLIR code operating14// on sparse tensors.  However, the provided functionality is **not**15// part of core MLIR itself.16//17// The following memory-resident sparse storage schemes are supported:18//19// (a) A coordinate scheme for temporarily storing and lexicographically20//     sorting a sparse tensor by coordinate (SparseTensorCOO).21//22// (b) A "one-size-fits-all" sparse tensor storage scheme defined by23//     per-dimension sparse/dense annnotations together with a dimension24//     ordering used by MLIR compiler-generated code (SparseTensorStorage).25//26// The following external formats are supported:27//28// (1) Matrix Market Exchange (MME): *.mtx29//     https://math.nist.gov/MatrixMarket/formats.html30//31// (2) Formidable Repository of Open Sparse Tensors and Tools (FROSTT): *.tns32//     http://frostt.io/tensors/file-formats.html33//34// Two public APIs are supported:35//36// (I) Methods operating on MLIR buffers (memrefs) to interact with sparse37//     tensors. These methods should be used exclusively by MLIR38//     compiler-generated code.39//40// (II) Methods that accept C-style data structures to interact with sparse41//      tensors. These methods can be used by any external runtime that wants42//      to interact with MLIR compiler-generated code.43//44// In both cases (I) and (II), the SparseTensorStorage format is externally45// only visible as an opaque pointer.46//47//===----------------------------------------------------------------------===//48 49#include "mlir/ExecutionEngine/SparseTensorRuntime.h"50 51#ifdef MLIR_CRUNNERUTILS_DEFINE_FUNCTIONS52 53#include "mlir/ExecutionEngine/SparseTensor/ArithmeticUtils.h"54#include "mlir/ExecutionEngine/SparseTensor/COO.h"55#include "mlir/ExecutionEngine/SparseTensor/File.h"56#include "mlir/ExecutionEngine/SparseTensor/Storage.h"57 58#include <cstring>59#include <numeric>60 61using namespace mlir::sparse_tensor;62 63//===----------------------------------------------------------------------===//64//65// Utilities for manipulating `StridedMemRefType`.66//67//===----------------------------------------------------------------------===//68 69namespace {70 71#define ASSERT_NO_STRIDE(MEMREF)                                               \72  do {                                                                         \73    assert((MEMREF) && "Memref is nullptr");                                   \74    assert(((MEMREF)->strides[0] == 1) && "Memref has non-trivial stride");    \75  } while (false)76 77#define MEMREF_GET_USIZE(MEMREF)                                               \78  detail::checkOverflowCast<uint64_t>((MEMREF)->sizes[0])79 80#define ASSERT_USIZE_EQ(MEMREF, SZ)                                            \81  assert(detail::safelyEQ(MEMREF_GET_USIZE(MEMREF), (SZ)) &&                   \82         "Memref size mismatch")83 84#define MEMREF_GET_PAYLOAD(MEMREF) ((MEMREF)->data + (MEMREF)->offset)85 86/// Initializes the memref with the provided size and data pointer. This87/// is designed for functions which want to "return" a memref that aliases88/// into memory owned by some other object (e.g., `SparseTensorStorage`),89/// without doing any actual copying.  (The "return" is in scarequotes90/// because the `_mlir_ciface_` calling convention migrates any returned91/// memrefs into an out-parameter passed before all the other function92/// parameters.)93template <typename DataSizeT, typename T>94static inline void aliasIntoMemref(DataSizeT size, T *data,95                                   StridedMemRefType<T, 1> &ref) {96  ref.basePtr = ref.data = data;97  ref.offset = 0;98  using MemrefSizeT = std::remove_reference_t<decltype(ref.sizes[0])>;99  ref.sizes[0] = detail::checkOverflowCast<MemrefSizeT>(size);100  ref.strides[0] = 1;101}102 103} // anonymous namespace104 105extern "C" {106 107//===----------------------------------------------------------------------===//108//109// Public functions which operate on MLIR buffers (memrefs) to interact110// with sparse tensors (which are only visible as opaque pointers externally).111//112//===----------------------------------------------------------------------===//113 114#define CASE(p, c, v, P, C, V)                                                 \115  if (posTp == (p) && crdTp == (c) && valTp == (v)) {                          \116    switch (action) {                                                          \117    case Action::kEmpty: {                                                     \118      return SparseTensorStorage<P, C, V>::newEmpty(                           \119          dimRank, dimSizes, lvlRank, lvlSizes, lvlTypes, dim2lvl, lvl2dim);   \120    }                                                                          \121    case Action::kFromReader: {                                                \122      assert(ptr && "Received nullptr for SparseTensorReader object");         \123      SparseTensorReader &reader = *static_cast<SparseTensorReader *>(ptr);    \124      return static_cast<void *>(reader.readSparseTensor<P, C, V>(             \125          lvlRank, lvlSizes, lvlTypes, dim2lvl, lvl2dim));                     \126    }                                                                          \127    case Action::kPack: {                                                      \128      assert(ptr && "Received nullptr for SparseTensorStorage object");        \129      intptr_t *buffers = static_cast<intptr_t *>(ptr);                        \130      return SparseTensorStorage<P, C, V>::newFromBuffers(                     \131          dimRank, dimSizes, lvlRank, lvlSizes, lvlTypes, dim2lvl, lvl2dim,    \132          dimRank, buffers);                                                   \133    }                                                                          \134    case Action::kSortCOOInPlace: {                                            \135      assert(ptr && "Received nullptr for SparseTensorStorage object");        \136      auto &tensor = *static_cast<SparseTensorStorage<P, C, V> *>(ptr);        \137      tensor.sortInPlace();                                                    \138      return ptr;                                                              \139    }                                                                          \140    }                                                                          \141    fprintf(stderr, "unknown action %d\n", static_cast<uint32_t>(action));     \142    exit(1);                                                                   \143  }144 145#define CASE_SECSAME(p, v, P, V) CASE(p, p, v, P, P, V)146 147// Assume index_type is in fact uint64_t, so that _mlir_ciface_newSparseTensor148// can safely rewrite kIndex to kU64.  We make this assertion to guarantee149// that this file cannot get out of sync with its header.150static_assert(std::is_same<index_type, uint64_t>::value,151              "Expected index_type == uint64_t");152 153// The Swiss-army-knife for sparse tensor creation.154void *_mlir_ciface_newSparseTensor( // NOLINT155    StridedMemRefType<index_type, 1> *dimSizesRef,156    StridedMemRefType<index_type, 1> *lvlSizesRef,157    StridedMemRefType<LevelType, 1> *lvlTypesRef,158    StridedMemRefType<index_type, 1> *dim2lvlRef,159    StridedMemRefType<index_type, 1> *lvl2dimRef, OverheadType posTp,160    OverheadType crdTp, PrimaryType valTp, Action action, void *ptr) {161  ASSERT_NO_STRIDE(dimSizesRef);162  ASSERT_NO_STRIDE(lvlSizesRef);163  ASSERT_NO_STRIDE(lvlTypesRef);164  ASSERT_NO_STRIDE(dim2lvlRef);165  ASSERT_NO_STRIDE(lvl2dimRef);166  const uint64_t dimRank = MEMREF_GET_USIZE(dimSizesRef);167  const uint64_t lvlRank = MEMREF_GET_USIZE(lvlSizesRef);168  ASSERT_USIZE_EQ(lvlTypesRef, lvlRank);169  ASSERT_USIZE_EQ(dim2lvlRef, lvlRank);170  ASSERT_USIZE_EQ(lvl2dimRef, dimRank);171  const index_type *dimSizes = MEMREF_GET_PAYLOAD(dimSizesRef);172  const index_type *lvlSizes = MEMREF_GET_PAYLOAD(lvlSizesRef);173  const LevelType *lvlTypes = MEMREF_GET_PAYLOAD(lvlTypesRef);174  const index_type *dim2lvl = MEMREF_GET_PAYLOAD(dim2lvlRef);175  const index_type *lvl2dim = MEMREF_GET_PAYLOAD(lvl2dimRef);176 177  // Rewrite kIndex to kU64, to avoid introducing a bunch of new cases.178  // This is safe because of the static_assert above.179  if (posTp == OverheadType::kIndex)180    posTp = OverheadType::kU64;181  if (crdTp == OverheadType::kIndex)182    crdTp = OverheadType::kU64;183 184  // Double matrices with all combinations of overhead storage.185  CASE(OverheadType::kU64, OverheadType::kU64, PrimaryType::kF64, uint64_t,186       uint64_t, double);187  CASE(OverheadType::kU64, OverheadType::kU32, PrimaryType::kF64, uint64_t,188       uint32_t, double);189  CASE(OverheadType::kU64, OverheadType::kU16, PrimaryType::kF64, uint64_t,190       uint16_t, double);191  CASE(OverheadType::kU64, OverheadType::kU8, PrimaryType::kF64, uint64_t,192       uint8_t, double);193  CASE(OverheadType::kU32, OverheadType::kU64, PrimaryType::kF64, uint32_t,194       uint64_t, double);195  CASE(OverheadType::kU32, OverheadType::kU32, PrimaryType::kF64, uint32_t,196       uint32_t, double);197  CASE(OverheadType::kU32, OverheadType::kU16, PrimaryType::kF64, uint32_t,198       uint16_t, double);199  CASE(OverheadType::kU32, OverheadType::kU8, PrimaryType::kF64, uint32_t,200       uint8_t, double);201  CASE(OverheadType::kU16, OverheadType::kU64, PrimaryType::kF64, uint16_t,202       uint64_t, double);203  CASE(OverheadType::kU16, OverheadType::kU32, PrimaryType::kF64, uint16_t,204       uint32_t, double);205  CASE(OverheadType::kU16, OverheadType::kU16, PrimaryType::kF64, uint16_t,206       uint16_t, double);207  CASE(OverheadType::kU16, OverheadType::kU8, PrimaryType::kF64, uint16_t,208       uint8_t, double);209  CASE(OverheadType::kU8, OverheadType::kU64, PrimaryType::kF64, uint8_t,210       uint64_t, double);211  CASE(OverheadType::kU8, OverheadType::kU32, PrimaryType::kF64, uint8_t,212       uint32_t, double);213  CASE(OverheadType::kU8, OverheadType::kU16, PrimaryType::kF64, uint8_t,214       uint16_t, double);215  CASE(OverheadType::kU8, OverheadType::kU8, PrimaryType::kF64, uint8_t,216       uint8_t, double);217 218  // Float matrices with all combinations of overhead storage.219  CASE(OverheadType::kU64, OverheadType::kU64, PrimaryType::kF32, uint64_t,220       uint64_t, float);221  CASE(OverheadType::kU64, OverheadType::kU32, PrimaryType::kF32, uint64_t,222       uint32_t, float);223  CASE(OverheadType::kU64, OverheadType::kU16, PrimaryType::kF32, uint64_t,224       uint16_t, float);225  CASE(OverheadType::kU64, OverheadType::kU8, PrimaryType::kF32, uint64_t,226       uint8_t, float);227  CASE(OverheadType::kU32, OverheadType::kU64, PrimaryType::kF32, uint32_t,228       uint64_t, float);229  CASE(OverheadType::kU32, OverheadType::kU32, PrimaryType::kF32, uint32_t,230       uint32_t, float);231  CASE(OverheadType::kU32, OverheadType::kU16, PrimaryType::kF32, uint32_t,232       uint16_t, float);233  CASE(OverheadType::kU32, OverheadType::kU8, PrimaryType::kF32, uint32_t,234       uint8_t, float);235  CASE(OverheadType::kU16, OverheadType::kU64, PrimaryType::kF32, uint16_t,236       uint64_t, float);237  CASE(OverheadType::kU16, OverheadType::kU32, PrimaryType::kF32, uint16_t,238       uint32_t, float);239  CASE(OverheadType::kU16, OverheadType::kU16, PrimaryType::kF32, uint16_t,240       uint16_t, float);241  CASE(OverheadType::kU16, OverheadType::kU8, PrimaryType::kF32, uint16_t,242       uint8_t, float);243  CASE(OverheadType::kU8, OverheadType::kU64, PrimaryType::kF32, uint8_t,244       uint64_t, float);245  CASE(OverheadType::kU8, OverheadType::kU32, PrimaryType::kF32, uint8_t,246       uint32_t, float);247  CASE(OverheadType::kU8, OverheadType::kU16, PrimaryType::kF32, uint8_t,248       uint16_t, float);249  CASE(OverheadType::kU8, OverheadType::kU8, PrimaryType::kF32, uint8_t,250       uint8_t, float);251 252  // Two-byte floats with both overheads of the same type.253  CASE_SECSAME(OverheadType::kU64, PrimaryType::kF16, uint64_t, f16);254  CASE_SECSAME(OverheadType::kU64, PrimaryType::kBF16, uint64_t, bf16);255  CASE_SECSAME(OverheadType::kU32, PrimaryType::kF16, uint32_t, f16);256  CASE_SECSAME(OverheadType::kU32, PrimaryType::kBF16, uint32_t, bf16);257  CASE_SECSAME(OverheadType::kU16, PrimaryType::kF16, uint16_t, f16);258  CASE_SECSAME(OverheadType::kU16, PrimaryType::kBF16, uint16_t, bf16);259  CASE_SECSAME(OverheadType::kU8, PrimaryType::kF16, uint8_t, f16);260  CASE_SECSAME(OverheadType::kU8, PrimaryType::kBF16, uint8_t, bf16);261 262  // Integral matrices with both overheads of the same type.263  CASE_SECSAME(OverheadType::kU64, PrimaryType::kI64, uint64_t, int64_t);264  CASE_SECSAME(OverheadType::kU64, PrimaryType::kI32, uint64_t, int32_t);265  CASE_SECSAME(OverheadType::kU64, PrimaryType::kI16, uint64_t, int16_t);266  CASE_SECSAME(OverheadType::kU64, PrimaryType::kI8, uint64_t, int8_t);267  CASE_SECSAME(OverheadType::kU32, PrimaryType::kI64, uint32_t, int64_t);268  CASE_SECSAME(OverheadType::kU32, PrimaryType::kI32, uint32_t, int32_t);269  CASE_SECSAME(OverheadType::kU32, PrimaryType::kI16, uint32_t, int16_t);270  CASE_SECSAME(OverheadType::kU32, PrimaryType::kI8, uint32_t, int8_t);271  CASE_SECSAME(OverheadType::kU16, PrimaryType::kI64, uint16_t, int64_t);272  CASE_SECSAME(OverheadType::kU16, PrimaryType::kI32, uint16_t, int32_t);273  CASE_SECSAME(OverheadType::kU16, PrimaryType::kI16, uint16_t, int16_t);274  CASE_SECSAME(OverheadType::kU16, PrimaryType::kI8, uint16_t, int8_t);275  CASE_SECSAME(OverheadType::kU8, PrimaryType::kI64, uint8_t, int64_t);276  CASE_SECSAME(OverheadType::kU8, PrimaryType::kI32, uint8_t, int32_t);277  CASE_SECSAME(OverheadType::kU8, PrimaryType::kI16, uint8_t, int16_t);278  CASE_SECSAME(OverheadType::kU8, PrimaryType::kI8, uint8_t, int8_t);279 280  // Complex matrices with wide overhead.281  CASE_SECSAME(OverheadType::kU64, PrimaryType::kC64, uint64_t, complex64);282  CASE_SECSAME(OverheadType::kU64, PrimaryType::kC32, uint64_t, complex32);283 284  // Unsupported case (add above if needed).285  fprintf(stderr, "unsupported combination of types: <P=%d, C=%d, V=%d>\n",286          static_cast<int>(posTp), static_cast<int>(crdTp),287          static_cast<int>(valTp));288  exit(1);289}290#undef CASE291#undef CASE_SECSAME292 293#define IMPL_SPARSEVALUES(VNAME, V)                                            \294  void _mlir_ciface_sparseValues##VNAME(StridedMemRefType<V, 1> *ref,          \295                                        void *tensor) {                        \296    assert(ref &&tensor);                                                      \297    std::vector<V> *v;                                                         \298    static_cast<SparseTensorStorageBase *>(tensor)->getValues(&v);             \299    assert(v);                                                                 \300    aliasIntoMemref(v->size(), v->data(), *ref);                               \301  }302MLIR_SPARSETENSOR_FOREVERY_V(IMPL_SPARSEVALUES)303#undef IMPL_SPARSEVALUES304 305#define IMPL_GETOVERHEAD(NAME, TYPE, LIB)                                      \306  void _mlir_ciface_##NAME(StridedMemRefType<TYPE, 1> *ref, void *tensor,      \307                           index_type lvl) {                                   \308    assert(ref &&tensor);                                                      \309    std::vector<TYPE> *v;                                                      \310    static_cast<SparseTensorStorageBase *>(tensor)->LIB(&v, lvl);              \311    assert(v);                                                                 \312    aliasIntoMemref(v->size(), v->data(), *ref);                               \313  }314 315#define IMPL_SPARSEPOSITIONS(PNAME, P)                                         \316  IMPL_GETOVERHEAD(sparsePositions##PNAME, P, getPositions)317MLIR_SPARSETENSOR_FOREVERY_O(IMPL_SPARSEPOSITIONS)318#undef IMPL_SPARSEPOSITIONS319 320#define IMPL_SPARSECOORDINATES(CNAME, C)                                       \321  IMPL_GETOVERHEAD(sparseCoordinates##CNAME, C, getCoordinates)322MLIR_SPARSETENSOR_FOREVERY_O(IMPL_SPARSECOORDINATES)323#undef IMPL_SPARSECOORDINATES324 325#define IMPL_SPARSECOORDINATESBUFFER(CNAME, C)                                 \326  IMPL_GETOVERHEAD(sparseCoordinatesBuffer##CNAME, C, getCoordinatesBuffer)327MLIR_SPARSETENSOR_FOREVERY_O(IMPL_SPARSECOORDINATESBUFFER)328#undef IMPL_SPARSECOORDINATESBUFFER329 330#undef IMPL_GETOVERHEAD331 332#define IMPL_LEXINSERT(VNAME, V)                                               \333  void _mlir_ciface_lexInsert##VNAME(                                          \334      void *t, StridedMemRefType<index_type, 1> *lvlCoordsRef,                 \335      StridedMemRefType<V, 0> *vref) {                                         \336    assert(t &&vref);                                                          \337    auto &tensor = *static_cast<SparseTensorStorageBase *>(t);                 \338    ASSERT_NO_STRIDE(lvlCoordsRef);                                            \339    index_type *lvlCoords = MEMREF_GET_PAYLOAD(lvlCoordsRef);                  \340    assert(lvlCoords);                                                         \341    V *value = MEMREF_GET_PAYLOAD(vref);                                       \342    tensor.lexInsert(lvlCoords, *value);                                       \343  }344MLIR_SPARSETENSOR_FOREVERY_V(IMPL_LEXINSERT)345#undef IMPL_LEXINSERT346 347#define IMPL_EXPINSERT(VNAME, V)                                               \348  void _mlir_ciface_expInsert##VNAME(                                          \349      void *t, StridedMemRefType<index_type, 1> *lvlCoordsRef,                 \350      StridedMemRefType<V, 1> *vref, StridedMemRefType<bool, 1> *fref,         \351      StridedMemRefType<index_type, 1> *aref, index_type count) {              \352    assert(t);                                                                 \353    auto &tensor = *static_cast<SparseTensorStorageBase *>(t);                 \354    ASSERT_NO_STRIDE(lvlCoordsRef);                                            \355    ASSERT_NO_STRIDE(vref);                                                    \356    ASSERT_NO_STRIDE(fref);                                                    \357    ASSERT_NO_STRIDE(aref);                                                    \358    ASSERT_USIZE_EQ(vref, MEMREF_GET_USIZE(fref));                             \359    index_type *lvlCoords = MEMREF_GET_PAYLOAD(lvlCoordsRef);                  \360    V *values = MEMREF_GET_PAYLOAD(vref);                                      \361    bool *filled = MEMREF_GET_PAYLOAD(fref);                                   \362    index_type *added = MEMREF_GET_PAYLOAD(aref);                              \363    uint64_t expsz = vref->sizes[0];                                           \364    tensor.expInsert(lvlCoords, values, filled, added, count, expsz);          \365  }366MLIR_SPARSETENSOR_FOREVERY_V(IMPL_EXPINSERT)367#undef IMPL_EXPINSERT368 369void *_mlir_ciface_createCheckedSparseTensorReader(370    char *filename, StridedMemRefType<index_type, 1> *dimShapeRef,371    PrimaryType valTp) {372  ASSERT_NO_STRIDE(dimShapeRef);373  const uint64_t dimRank = MEMREF_GET_USIZE(dimShapeRef);374  const index_type *dimShape = MEMREF_GET_PAYLOAD(dimShapeRef);375  auto *reader = SparseTensorReader::create(filename, dimRank, dimShape, valTp);376  return static_cast<void *>(reader);377}378 379void _mlir_ciface_getSparseTensorReaderDimSizes(380    StridedMemRefType<index_type, 1> *out, void *p) {381  assert(out && p);382  SparseTensorReader &reader = *static_cast<SparseTensorReader *>(p);383  auto *dimSizes = const_cast<uint64_t *>(reader.getDimSizes());384  aliasIntoMemref(reader.getRank(), dimSizes, *out);385}386 387#define IMPL_GETNEXT(VNAME, V, CNAME, C)                                       \388  bool _mlir_ciface_getSparseTensorReaderReadToBuffers##CNAME##VNAME(          \389      void *p, StridedMemRefType<index_type, 1> *dim2lvlRef,                   \390      StridedMemRefType<index_type, 1> *lvl2dimRef,                            \391      StridedMemRefType<C, 1> *cref, StridedMemRefType<V, 1> *vref) {          \392    assert(p);                                                                 \393    auto &reader = *static_cast<SparseTensorReader *>(p);                      \394    ASSERT_NO_STRIDE(dim2lvlRef);                                              \395    ASSERT_NO_STRIDE(lvl2dimRef);                                              \396    ASSERT_NO_STRIDE(cref);                                                    \397    ASSERT_NO_STRIDE(vref);                                                    \398    const uint64_t dimRank = reader.getRank();                                 \399    const uint64_t lvlRank = MEMREF_GET_USIZE(dim2lvlRef);                     \400    const uint64_t cSize = MEMREF_GET_USIZE(cref);                             \401    const uint64_t vSize = MEMREF_GET_USIZE(vref);                             \402    ASSERT_USIZE_EQ(lvl2dimRef, dimRank);                                      \403    assert(cSize >= lvlRank * reader.getNSE());                                \404    assert(vSize >= reader.getNSE());                                          \405    (void)dimRank;                                                             \406    (void)cSize;                                                               \407    (void)vSize;                                                               \408    index_type *dim2lvl = MEMREF_GET_PAYLOAD(dim2lvlRef);                      \409    index_type *lvl2dim = MEMREF_GET_PAYLOAD(lvl2dimRef);                      \410    C *lvlCoordinates = MEMREF_GET_PAYLOAD(cref);                              \411    V *values = MEMREF_GET_PAYLOAD(vref);                                      \412    return reader.readToBuffers<C, V>(lvlRank, dim2lvl, lvl2dim,               \413                                      lvlCoordinates, values);                 \414  }415MLIR_SPARSETENSOR_FOREVERY_V_O(IMPL_GETNEXT)416#undef IMPL_GETNEXT417 418void _mlir_ciface_outSparseTensorWriterMetaData(419    void *p, index_type dimRank, index_type nse,420    StridedMemRefType<index_type, 1> *dimSizesRef) {421  assert(p);422  ASSERT_NO_STRIDE(dimSizesRef);423  assert(dimRank != 0);424  index_type *dimSizes = MEMREF_GET_PAYLOAD(dimSizesRef);425  std::ostream &file = *static_cast<std::ostream *>(p);426  file << dimRank << " " << nse << '\n';427  for (index_type d = 0; d < dimRank - 1; d++)428    file << dimSizes[d] << " ";429  file << dimSizes[dimRank - 1] << '\n';430}431 432#define IMPL_OUTNEXT(VNAME, V)                                                 \433  void _mlir_ciface_outSparseTensorWriterNext##VNAME(                          \434      void *p, index_type dimRank,                                             \435      StridedMemRefType<index_type, 1> *dimCoordsRef,                          \436      StridedMemRefType<V, 0> *vref) {                                         \437    assert(p &&vref);                                                          \438    ASSERT_NO_STRIDE(dimCoordsRef);                                            \439    const index_type *dimCoords = MEMREF_GET_PAYLOAD(dimCoordsRef);            \440    std::ostream &file = *static_cast<std::ostream *>(p);                      \441    for (index_type d = 0; d < dimRank; d++)                                   \442      file << (dimCoords[d] + 1) << " ";                                       \443    V *value = MEMREF_GET_PAYLOAD(vref);                                       \444    file << *value << '\n';                                                    \445  }446MLIR_SPARSETENSOR_FOREVERY_V(IMPL_OUTNEXT)447#undef IMPL_OUTNEXT448 449//===----------------------------------------------------------------------===//450//451// Public functions which accept only C-style data structures to interact452// with sparse tensors (which are only visible as opaque pointers externally).453//454//===----------------------------------------------------------------------===//455 456index_type sparseLvlSize(void *tensor, index_type l) {457  return static_cast<SparseTensorStorageBase *>(tensor)->getLvlSize(l);458}459 460index_type sparseDimSize(void *tensor, index_type d) {461  return static_cast<SparseTensorStorageBase *>(tensor)->getDimSize(d);462}463 464void endLexInsert(void *tensor) {465  return static_cast<SparseTensorStorageBase *>(tensor)->endLexInsert();466}467 468void delSparseTensor(void *tensor) {469  delete static_cast<SparseTensorStorageBase *>(tensor);470}471 472char *getTensorFilename(index_type id) {473  constexpr size_t bufSize = 80;474  char var[bufSize];475  snprintf(var, bufSize, "TENSOR%" PRIu64, id);476  char *env = getenv(var);477  if (!env) {478    fprintf(stderr, "Environment variable %s is not set\n", var);479    exit(1);480  }481  return env;482}483 484index_type getSparseTensorReaderNSE(void *p) {485  return static_cast<SparseTensorReader *>(p)->getNSE();486}487 488void delSparseTensorReader(void *p) {489  delete static_cast<SparseTensorReader *>(p);490}491 492void *createSparseTensorWriter(char *filename) {493  std::ostream *file =494      (filename[0] == 0) ? &std::cout : new std::ofstream(filename);495  *file << "# extended FROSTT format\n";496  return static_cast<void *>(file);497}498 499void delSparseTensorWriter(void *p) {500  std::ostream *file = static_cast<std::ostream *>(p);501  file->flush();502  assert(file->good());503  if (file != &std::cout)504    delete file;505}506 507} // extern "C"508 509#undef MEMREF_GET_PAYLOAD510#undef ASSERT_USIZE_EQ511#undef MEMREF_GET_USIZE512#undef ASSERT_NO_STRIDE513 514#endif // MLIR_CRUNNERUTILS_DEFINE_FUNCTIONS515