MLIR  17.0.0git
Enums.h
Go to the documentation of this file.
1 //===- Enums.h - Enums for the SparseTensor dialect -------------*- C++ -*-===//
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 // Typedefs and enums shared between MLIR code for manipulating the
10 // IR, and the lightweight runtime support library for sparse tensor
11 // manipulations. That is, all the enums are used to define the API
12 // of the runtime library and hence are also needed when generating
13 // calls into the runtime library. Moveover, the `DimLevelType` enum
14 // is also used as the internal IR encoding of dimension level types,
15 // to avoid code duplication (e.g., for the predicates).
16 //
17 // This file also defines x-macros <https://en.wikipedia.org/wiki/X_Macro>
18 // so that we can generate variations of the public functions for each
19 // supported primary- and/or overhead-type.
20 //
21 // Because this file defines a library which is a dependency of the
22 // runtime library itself, this file must not depend on any MLIR internals
23 // (e.g., operators, attributes, ArrayRefs, etc) lest the runtime library
24 // inherit those dependencies.
25 //
26 //===----------------------------------------------------------------------===//
27 
28 #ifndef MLIR_DIALECT_SPARSETENSOR_IR_ENUMS_H
29 #define MLIR_DIALECT_SPARSETENSOR_IR_ENUMS_H
30 
31 // NOTE: Client code will need to include "mlir/ExecutionEngine/Float16bits.h"
32 // if they want to use the `MLIR_SPARSETENSOR_FOREVERY_V` macro.
33 
34 #include <cinttypes>
35 #include <complex>
36 #include <optional>
37 
38 namespace mlir {
39 namespace sparse_tensor {
40 
41 /// This type is used in the public API at all places where MLIR expects
42 /// values with the built-in type "index". For now, we simply assume that
43 /// type is 64-bit, but targets with different "index" bit widths should
44 /// link with an alternatively built runtime support library.
45 // TODO: support such targets?
46 using index_type = uint64_t;
47 
48 /// Encoding of overhead types (both pointer overhead and indices
49 /// overhead), for "overloading" @newSparseTensor.
50 enum class OverheadType : uint32_t {
51  kIndex = 0,
52  kU64 = 1,
53  kU32 = 2,
54  kU16 = 3,
55  kU8 = 4
56 };
57 
58 // This x-macro calls its argument on every overhead type which has
59 // fixed-width. It excludes `index_type` because that type is often
60 // handled specially (e.g., by translating it into the architecture-dependent
61 // equivalent fixed-width overhead type).
62 #define MLIR_SPARSETENSOR_FOREVERY_FIXED_O(DO) \
63  DO(64, uint64_t) \
64  DO(32, uint32_t) \
65  DO(16, uint16_t) \
66  DO(8, uint8_t)
67 
68 // This x-macro calls its argument on every overhead type, including
69 // `index_type`.
70 #define MLIR_SPARSETENSOR_FOREVERY_O(DO) \
71  MLIR_SPARSETENSOR_FOREVERY_FIXED_O(DO) \
72  DO(0, index_type)
73 
74 // These are not just shorthands but indicate the particular
75 // implementation used (e.g., as opposed to C99's `complex double`,
76 // or MLIR's `ComplexType`).
77 using complex64 = std::complex<double>;
78 using complex32 = std::complex<float>;
79 
80 /// Encoding of the elemental type, for "overloading" @newSparseTensor.
81 enum class PrimaryType : uint32_t {
82  kF64 = 1,
83  kF32 = 2,
84  kF16 = 3,
85  kBF16 = 4,
86  kI64 = 5,
87  kI32 = 6,
88  kI16 = 7,
89  kI8 = 8,
90  kC64 = 9,
91  kC32 = 10
92 };
93 
94 // This x-macro includes all `V` types.
95 #define MLIR_SPARSETENSOR_FOREVERY_V(DO) \
96  DO(F64, double) \
97  DO(F32, float) \
98  DO(F16, f16) \
99  DO(BF16, bf16) \
100  DO(I64, int64_t) \
101  DO(I32, int32_t) \
102  DO(I16, int16_t) \
103  DO(I8, int8_t) \
104  DO(C64, complex64) \
105  DO(C32, complex32)
106 
107 constexpr bool isFloatingPrimaryType(PrimaryType valTy) {
108  return PrimaryType::kF64 <= valTy && valTy <= PrimaryType::kBF16;
109 }
110 
111 constexpr bool isIntegralPrimaryType(PrimaryType valTy) {
112  return PrimaryType::kI64 <= valTy && valTy <= PrimaryType::kI8;
113 }
114 
115 constexpr bool isRealPrimaryType(PrimaryType valTy) {
116  return PrimaryType::kF64 <= valTy && valTy <= PrimaryType::kI8;
117 }
118 
119 constexpr bool isComplexPrimaryType(PrimaryType valTy) {
120  return PrimaryType::kC64 <= valTy && valTy <= PrimaryType::kC32;
121 }
122 
123 /// The actions performed by @newSparseTensor.
124 enum class Action : uint32_t {
125  kEmpty = 0,
126  // newSparseTensor no longer handles `kFromFile=1`, so we leave this
127  // number reserved to help catch any code that still needs updating.
128  kFromCOO = 2,
129  kSparseToSparse = 3,
130  kEmptyCOO = 4,
131  kToCOO = 5,
132  kToIterator = 6,
133 };
134 
135 /// This enum defines all the sparse representations supportable by
136 /// the SparseTensor dialect. We use a lightweight encoding to encode
137 /// both the "format" per se (dense, compressed, singleton) as well as
138 /// the "properties" (ordered, unique). The encoding is chosen for
139 /// performance of the runtime library, and thus may change in future
140 /// versions; consequently, client code should use the predicate functions
141 /// defined below, rather than relying on knowledge about the particular
142 /// binary encoding.
143 ///
144 /// The `Undef` "format" is a special value used internally for cases
145 /// where we need to store an undefined or indeterminate `DimLevelType`.
146 /// It should not be used externally, since it does not indicate an
147 /// actual/representable format.
148 enum class DimLevelType : uint8_t {
149  Undef = 0, // 0b000_00
150  Dense = 4, // 0b001_00
151  Compressed = 8, // 0b010_00
152  CompressedNu = 9, // 0b010_01
153  CompressedNo = 10, // 0b010_10
154  CompressedNuNo = 11, // 0b010_11
155  Singleton = 16, // 0b100_00
156  SingletonNu = 17, // 0b100_01
157  SingletonNo = 18, // 0b100_10
158  SingletonNuNo = 19, // 0b100_11
159 };
160 
161 /// This enum defines all the storage formats supported by the sparse compiler,
162 /// without the level properties.
163 enum class LevelFormat : uint8_t {
164  Dense = 4, // 0b001_00
165  Compressed = 8, // 0b010_00
166  Singleton = 16, // 0b100_00
167 };
168 
169 /// Returns string representation of the given dimension level type.
170 inline std::string toMLIRString(DimLevelType dlt) {
171  switch (dlt) {
172  // TODO: should probably raise an error instead of printing it...
173  case DimLevelType::Undef:
174  return "undef";
175  case DimLevelType::Dense:
176  return "dense";
178  return "compressed";
180  return "compressed-nu";
182  return "compressed-no";
184  return "compressed-nu-no";
186  return "singleton";
188  return "singleton-nu";
190  return "singleton-no";
192  return "singleton-nu-no";
193  }
194  return "";
195 }
196 
197 /// Check that the `DimLevelType` contains a valid (possibly undefined) value.
198 constexpr bool isValidDLT(DimLevelType dlt) {
199  const uint8_t formatBits = static_cast<uint8_t>(dlt) >> 2;
200  const uint8_t propertyBits = static_cast<uint8_t>(dlt) & 3;
201  // If undefined or dense, then must be unique and ordered.
202  // Otherwise, the format must be one of the known ones.
203  return (formatBits <= 1) ? (propertyBits == 0)
204  : (formatBits == 2 || formatBits == 4);
205 }
206 
207 /// Check if the `DimLevelType` is the special undefined value.
208 constexpr bool isUndefDLT(DimLevelType dlt) {
209  return dlt == DimLevelType::Undef;
210 }
211 
212 /// Check if the `DimLevelType` is dense.
213 constexpr bool isDenseDLT(DimLevelType dlt) {
214  return dlt == DimLevelType::Dense;
215 }
216 
217 // We use the idiom `(dlt & ~3) == format` in order to only return true
218 // for valid DLTs. Whereas the `dlt & format` idiom is a bit faster but
219 // can return false-positives on invalid DLTs.
220 
221 /// Check if the `DimLevelType` is compressed (regardless of properties).
222 constexpr bool isCompressedDLT(DimLevelType dlt) {
223  return (static_cast<uint8_t>(dlt) & ~3) ==
224  static_cast<uint8_t>(DimLevelType::Compressed);
225 }
226 
227 /// Check if the `DimLevelType` is singleton (regardless of properties).
228 constexpr bool isSingletonDLT(DimLevelType dlt) {
229  return (static_cast<uint8_t>(dlt) & ~3) ==
230  static_cast<uint8_t>(DimLevelType::Singleton);
231 }
232 
233 /// Check if the `DimLevelType` is ordered (regardless of storage format).
234 constexpr bool isOrderedDLT(DimLevelType dlt) {
235  return !(static_cast<uint8_t>(dlt) & 2);
236 }
237 
238 /// Check if the `DimLevelType` is unique (regardless of storage format).
239 constexpr bool isUniqueDLT(DimLevelType dlt) {
240  return !(static_cast<uint8_t>(dlt) & 1);
241 }
242 
243 /// Convert a DimLevelType to its corresponding LevelFormat.
244 /// Returns std::nullopt when input dlt is Undef.
245 constexpr std::optional<LevelFormat> getLevelFormat(DimLevelType dlt) {
246  if (dlt == DimLevelType::Undef)
247  return std::nullopt;
248  return static_cast<LevelFormat>(static_cast<uint8_t>(dlt) & ~3);
249 }
250 
251 /// Convert a LevelFormat to its corresponding DimLevelType with the given
252 /// properties. Returns std::nullopt when the properties are not applicable for
253 /// the input level format.
254 /// TODO: factor out a new LevelProperties type so we can add new properties
255 /// without changing this function's signature
256 constexpr std::optional<DimLevelType>
257 getDimLevelType(LevelFormat lf, bool ordered, bool unique) {
258  auto dlt = static_cast<DimLevelType>(static_cast<uint8_t>(lf) |
259  (ordered ? 0 : 2) | (unique ? 0 : 1));
260  return isValidDLT(dlt) ? std::optional(dlt) : std::nullopt;
261 }
262 
263 /// Ensure the above conversion works as intended.
264 static_assert(
265  (getLevelFormat(DimLevelType::Undef) == std::nullopt &&
275  "getLevelFormat conversion is broken");
276 
277 static_assert(
278  (getDimLevelType(LevelFormat::Dense, false, true) == std::nullopt &&
279  getDimLevelType(LevelFormat::Dense, true, false) == std::nullopt &&
280  getDimLevelType(LevelFormat::Dense, false, false) == std::nullopt &&
284  *getDimLevelType(LevelFormat::Compressed, true, false) ==
286  *getDimLevelType(LevelFormat::Compressed, false, true) ==
288  *getDimLevelType(LevelFormat::Compressed, false, false) ==
290  *getDimLevelType(LevelFormat::Singleton, true, true) ==
292  *getDimLevelType(LevelFormat::Singleton, true, false) ==
294  *getDimLevelType(LevelFormat::Singleton, false, true) ==
296  *getDimLevelType(LevelFormat::Singleton, false, false) ==
298  "getDimLevelType conversion is broken");
299 
300 // Ensure the above predicates work as intended.
301 static_assert((isValidDLT(DimLevelType::Undef) &&
311  "isValidDLT definition is broken");
312 
313 static_assert((!isCompressedDLT(DimLevelType::Dense) &&
322  "isCompressedDLT definition is broken");
323 
324 static_assert((!isSingletonDLT(DimLevelType::Dense) &&
333  "isSingletonDLT definition is broken");
334 
335 static_assert((isOrderedDLT(DimLevelType::Dense) &&
344  "isOrderedDLT definition is broken");
345 
346 static_assert((isUniqueDLT(DimLevelType::Dense) &&
355  "isUniqueDLT definition is broken");
356 
357 } // namespace sparse_tensor
358 } // namespace mlir
359 
360 #endif // MLIR_DIALECT_SPARSETENSOR_IR_ENUMS_H
std::complex< double > complex64
Definition: Enums.h:77
constexpr std::optional< LevelFormat > getLevelFormat(DimLevelType dlt)
Convert a DimLevelType to its corresponding LevelFormat.
Definition: Enums.h:245
LevelFormat
This enum defines all the storage formats supported by the sparse compiler, without the level propert...
Definition: Enums.h:163
OverheadType
Encoding of overhead types (both pointer overhead and indices overhead), for "overloading" @newSparse...
Definition: Enums.h:50
constexpr bool isValidDLT(DimLevelType dlt)
Check that the DimLevelType contains a valid (possibly undefined) value.
Definition: Enums.h:198
std::string toMLIRString(DimLevelType dlt)
Returns string representation of the given dimension level type.
Definition: Enums.h:170
Action
The actions performed by @newSparseTensor.
Definition: Enums.h:124
constexpr bool isSingletonDLT(DimLevelType dlt)
Check if the DimLevelType is singleton (regardless of properties).
Definition: Enums.h:228
constexpr bool isUniqueDLT(DimLevelType dlt)
Check if the DimLevelType is unique (regardless of storage format).
Definition: Enums.h:239
constexpr bool isIntegralPrimaryType(PrimaryType valTy)
Definition: Enums.h:111
constexpr bool isDenseDLT(DimLevelType dlt)
Check if the DimLevelType is dense.
Definition: Enums.h:213
PrimaryType
Encoding of the elemental type, for "overloading" @newSparseTensor.
Definition: Enums.h:81
std::complex< float > complex32
Definition: Enums.h:78
constexpr bool isUndefDLT(DimLevelType dlt)
Check if the DimLevelType is the special undefined value.
Definition: Enums.h:208
constexpr std::optional< DimLevelType > getDimLevelType(LevelFormat lf, bool ordered, bool unique)
Convert a LevelFormat to its corresponding DimLevelType with the given properties.
Definition: Enums.h:257
constexpr bool isRealPrimaryType(PrimaryType valTy)
Definition: Enums.h:115
uint64_t index_type
This type is used in the public API at all places where MLIR expects values with the built-in type "i...
Definition: Enums.h:46
DimLevelType
This enum defines all the sparse representations supportable by the SparseTensor dialect.
Definition: Enums.h:148
constexpr bool isComplexPrimaryType(PrimaryType valTy)
Definition: Enums.h:119
constexpr bool isCompressedDLT(DimLevelType dlt)
Check if the DimLevelType is compressed (regardless of properties).
Definition: Enums.h:222
constexpr bool isFloatingPrimaryType(PrimaryType valTy)
Definition: Enums.h:107
constexpr bool isOrderedDLT(DimLevelType dlt)
Check if the DimLevelType is ordered (regardless of storage format).
Definition: Enums.h:234
Include the generated interface declarations.