MLIR 24.0.0git
Var.h
Go to the documentation of this file.
1//===- Var.h ----------------------------------------------------*- 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#ifndef MLIR_DIALECT_SPARSETENSOR_IR_DETAIL_VAR_H
10#define MLIR_DIALECT_SPARSETENSOR_IR_DETAIL_VAR_H
11
12#include "TemplateExtras.h"
13
15#include "llvm/ADT/EnumeratedArray.h"
16#include "llvm/ADT/STLForwardCompat.h"
17#include "llvm/ADT/SmallBitVector.h"
18#include "llvm/ADT/StringMap.h"
19
20namespace mlir {
21namespace sparse_tensor {
22namespace ir_detail {
23
24//===----------------------------------------------------------------------===//
25/// The three kinds of variables that `Var` can be.
26///
27/// NOTE: The numerical values used to represent this enum should be
28/// treated as an implementation detail, not as part of the API. In the
29/// API below we use the canonical ordering `{Symbol,Dimension,Level}` even
30/// though that does not agree with the numerical ordering of the numerical
31/// representation.
32enum class VarKind { Symbol = 1, Dimension = 0, Level = 2 };
33
34[[nodiscard]] constexpr bool isWF(VarKind vk) {
35 const auto vk_ = llvm::to_underlying(vk);
36 return 0 <= vk_ && vk_ <= 2;
37}
38
39/// Gets the ASCII character used as the prefix when printing `Var`.
40constexpr char toChar(VarKind vk) {
41 // If `isWF(vk)` then this computation's intermediate results are always
42 // in the range [-44..126] (where that lower bound is under worst-case
43 // rearranging of the expression); and `int_fast8_t` is the fastest type
44 // which can support that range without over-/underflow.
45 const auto vk_ = static_cast<int_fast8_t>(llvm::to_underlying(vk));
46 return static_cast<char>(100 + vk_ * (26 - vk_ * 11));
47}
48static_assert(toChar(VarKind::Symbol) == 's' &&
49 toChar(VarKind::Dimension) == 'd' &&
50 toChar(VarKind::Level) == 'l');
51
52//===----------------------------------------------------------------------===//
53/// The type of arrays indexed by `VarKind`.
54template <typename T>
55using VarKindArray = llvm::EnumeratedArray<T, VarKind, VarKind::Level>;
56
57//===----------------------------------------------------------------------===//
58/// A concrete variable, to be used in our variant of `AffineExpr`.
59/// Client-facing class for `VarKind` + `Var::Num` pairs, with RTTI
60/// support for subclasses with a fixed `VarKind`.
61class Var {
62public:
63 /// Typedef for the type of variable numbers.
64 using Num = unsigned;
65
66private:
67 /// Typedef for the underlying storage of `Var::Impl`.
68 using Storage = unsigned;
69
70 /// The largest `Var::Num` supported by `Var`/`Var::Impl`/`Var::Storage`.
71 /// Two low-order bits are reserved for storing the `VarKind`,
72 /// and one high-order bit is reserved for future use (e.g., to support
73 /// `DenseMapInfo<Var>` while maintaining the usual numeric values for
74 /// "empty" and "tombstone").
75 static constexpr Num kMaxNum =
76 static_cast<Num>(std::numeric_limits<Storage>::max() >> 3);
77
78public:
79 /// Checks whether the number would be accepted by `Var(VarKind,Var::Num)`.
80 //
81 // This must be public for `VarInfo` to use it (whereas we don't want
82 // to expose the `impl` field via friendship).
83 [[nodiscard]] static constexpr bool isWF_Num(Num n) { return n <= kMaxNum; }
84
85protected:
86 /// The underlying implementation of `Var`. Note that this must be kept
87 /// distinct from `Var` itself, since we want to ensure that the RTTI
88 /// methods will select the `U(Var::Impl)` ctor rather than selecting
89 /// the `U(Var::Num)` ctor.
90 class Impl final {
91 Storage data;
92
93 public:
94 constexpr Impl(VarKind vk, Num n)
95 : data((static_cast<Storage>(n) << 2) |
96 static_cast<Storage>(llvm::to_underlying(vk))) {
97 assert(isWF(vk) && "unknown VarKind");
98 assert(isWF_Num(n) && "Var::Num is too large");
99 }
100 constexpr bool operator==(Impl other) const { return data == other.data; }
101 constexpr bool operator!=(Impl other) const { return !(*this == other); }
102 constexpr VarKind getKind() const { return static_cast<VarKind>(data & 3); }
103 constexpr Num getNum() const { return static_cast<Num>(data >> 2); }
104 };
105 static_assert(IsZeroCostAbstraction<Impl>);
106
107private:
108 Impl impl;
109
110protected:
111 /// Protected ctor for the RTTI methods to use.
112 constexpr explicit Var(Impl impl) : impl(impl) {}
113
114public:
115 constexpr Var(VarKind vk, Num n) : impl(Impl(vk, n)) {}
116 Var(AffineSymbolExpr sym) : Var(VarKind::Symbol, sym.getPosition()) {}
117 Var(VarKind vk, AffineDimExpr var) : Var(vk, var.getPosition()) {
118 assert(vk != VarKind::Symbol);
119 }
120
121 constexpr bool operator==(Var other) const { return impl == other.impl; }
122 constexpr bool operator!=(Var other) const { return !(*this == other); }
123
124 constexpr VarKind getKind() const { return impl.getKind(); }
125 constexpr Num getNum() const { return impl.getNum(); }
126
127 template <typename U>
128 constexpr bool isa() const;
129 template <typename U>
130 constexpr U cast() const;
131 template <typename U>
132 constexpr std::optional<U> dyn_cast() const;
133
134 std::string str() const;
135 void print(llvm::raw_ostream &os) const;
136 void print(AsmPrinter &printer) const;
137#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
138 LLVM_DUMP_METHOD void dump() const;
139#endif
140};
141static_assert(IsZeroCostAbstraction<Var>);
142
143class SymVar final : public Var {
144 using Var::Var; // inherit `Var(Impl)` ctor for RTTI use.
145public:
146 static constexpr VarKind Kind = VarKind::Symbol;
147 static constexpr bool classof(Var const *var) {
148 return var->getKind() == Kind;
149 }
150 constexpr SymVar(Num sym) : Var(Kind, sym) {}
151 SymVar(AffineSymbolExpr symExpr) : Var(symExpr) {}
152};
153static_assert(IsZeroCostAbstraction<SymVar>);
154
155class DimVar final : public Var {
156 using Var::Var; // inherit `Var(Impl)` ctor for RTTI use.
157public:
158 static constexpr VarKind Kind = VarKind::Dimension;
159 static constexpr bool classof(Var const *var) {
160 return var->getKind() == Kind;
161 }
162 constexpr DimVar(Num dim) : Var(Kind, dim) {}
163 DimVar(AffineDimExpr dimExpr) : Var(Kind, dimExpr) {}
164};
165static_assert(IsZeroCostAbstraction<DimVar>);
166
167class LvlVar final : public Var {
168 using Var::Var; // inherit `Var(Impl)` ctor for RTTI use.
169public:
170 static constexpr VarKind Kind = VarKind::Level;
171 static constexpr bool classof(Var const *var) {
172 return var->getKind() == Kind;
173 }
174 constexpr LvlVar(Num lvl) : Var(Kind, lvl) {}
175 LvlVar(AffineDimExpr lvlExpr) : Var(Kind, lvlExpr) {}
176};
177static_assert(IsZeroCostAbstraction<LvlVar>);
178
179template <typename U>
180constexpr bool Var::isa() const {
181 if constexpr (std::is_same_v<U, SymVar>)
182 return getKind() == VarKind::Symbol;
183 if constexpr (std::is_same_v<U, DimVar>)
184 return getKind() == VarKind::Dimension;
185 if constexpr (std::is_same_v<U, LvlVar>)
186 return getKind() == VarKind::Level;
187}
188
189template <typename U>
190constexpr U Var::cast() const {
191 assert(isa<U>());
192 // NOTE: This should select the `U(Var::Impl)` ctor, *not* `U(Var::Num)`
193 return U(impl);
194}
195
196template <typename U>
197constexpr std::optional<U> Var::dyn_cast() const {
198 // NOTE: This should select the `U(Var::Impl)` ctor, *not* `U(Var::Num)`
199 return isa<U>() ? std::make_optional(U(impl)) : std::nullopt;
200}
201
202//===----------------------------------------------------------------------===//
203// Forward-decl so that we can declare methods of `Ranks` and `VarSet`.
204class DimLvlExpr;
205
206//===----------------------------------------------------------------------===//
207class Ranks final {
208 // Not using `VarKindArray` since `EnumeratedArray` doesn't support constexpr.
209 unsigned impl[3];
210
211 static constexpr unsigned to_index(VarKind vk) {
212 assert(isWF(vk) && "unknown VarKind");
213 return static_cast<unsigned>(llvm::to_underlying(vk));
214 }
215
216public:
217 constexpr Ranks(unsigned symRank, unsigned dimRank, unsigned lvlRank)
218 : impl() {
219 impl[to_index(VarKind::Symbol)] = symRank;
220 impl[to_index(VarKind::Dimension)] = dimRank;
221 impl[to_index(VarKind::Level)] = lvlRank;
222 }
224 : Ranks(ranks[VarKind::Symbol], ranks[VarKind::Dimension],
225 ranks[VarKind::Level]) {}
226
227 bool operator==(Ranks const &other) const;
228 bool operator!=(Ranks const &other) const { return !(*this == other); }
229
230 constexpr unsigned getRank(VarKind vk) const { return impl[to_index(vk)]; }
231 constexpr unsigned getSymRank() const { return getRank(VarKind::Symbol); }
232 constexpr unsigned getDimRank() const { return getRank(VarKind::Dimension); }
233 constexpr unsigned getLvlRank() const { return getRank(VarKind::Level); }
234
235 [[nodiscard]] constexpr bool isValid(Var var) const {
236 return var.getNum() < getRank(var.getKind());
237 }
238 [[nodiscard]] bool isValid(DimLvlExpr expr) const;
239};
240static_assert(IsZeroCostAbstraction<Ranks>);
241
242//===----------------------------------------------------------------------===//
243/// Efficient representation of a set of `Var`.
244class VarSet final {
246
247public:
248 explicit VarSet(Ranks const &ranks);
249
250 unsigned getRank(VarKind vk) const { return impl[vk].size(); }
251 unsigned getSymRank() const { return getRank(VarKind::Symbol); }
252 unsigned getDimRank() const { return getRank(VarKind::Dimension); }
253 unsigned getLvlRank() const { return getRank(VarKind::Level); }
254 Ranks getRanks() const {
255 return Ranks(getSymRank(), getDimRank(), getLvlRank());
256 }
257 /// For the `contains` method: if variables occurring in
258 /// the method parameter are OOB for the `VarSet`, then these methods will
259 /// always return false.
260 bool contains(Var var) const;
261
262 /// For the `add` methods: OOB parameters cause undefined behavior.
263 /// Currently the `add` methods will raise an assertion error.
264 void add(Var var);
265 void add(VarSet const &vars);
266 void add(DimLvlExpr expr);
267};
268
269//===----------------------------------------------------------------------===//
270/// A record of metadata for/about a variable, used by `VarEnv`.
271/// The principal goal of this record is to enable `VarEnv` to be used for
272/// incremental parsing; in particular, `VarInfo` allows the `Var::Num` to
273/// remain unknown, since each record is instead identified by `VarInfo::ID`.
274/// Therefore the `VarEnv` can freely allocate `VarInfo::ID` in whatever
275/// order it likes, irrespective of the binding order (`Var::Num`) of the
276/// associated variable.
277class VarInfo final {
278public:
279 /// Newtype for unique identifiers of `VarInfo` records, to ensure
280 /// they aren't confused with `Var::Num`.
281 enum class ID : unsigned {};
282
283private:
284 StringRef name; // The bare-id used in the MLIR source.
285 llvm::SMLoc loc; // The location of the first occurence.
286 ID id; // The unique `VarInfo`-identifier.
287 std::optional<Var::Num> num; // The unique `Var`-identifier (if resolved).
288 VarKind kind; // The kind of variable.
289
290public:
291 constexpr VarInfo(ID id, StringRef name, llvm::SMLoc loc, VarKind vk,
292 std::optional<Var::Num> n = {})
293 : name(name), loc(loc), id(id), num(n), kind(vk) {
294 assert(!name.empty() && "null StringRef");
295 assert(loc.isValid() && "null SMLoc");
296 assert(isWF(vk) && "unknown VarKind");
297 assert((!n || Var::isWF_Num(*n)) && "Var::Num is too large");
298 }
299
300 constexpr StringRef getName() const { return name; }
301 constexpr llvm::SMLoc getLoc() const { return loc; }
303 return parser.getEncodedSourceLoc(loc);
304 }
305 constexpr ID getID() const { return id; }
306 constexpr VarKind getKind() const { return kind; }
307 constexpr std::optional<Var::Num> getNum() const { return num; }
308 constexpr bool hasNum() const { return num.has_value(); }
309 void setNum(Var::Num n);
310 constexpr Var getVar() const {
311 assert(hasNum());
312 return Var(kind, *num);
313 }
314};
315
316//===----------------------------------------------------------------------===//
317enum class Policy { MustNot, May, Must };
318
319//===----------------------------------------------------------------------===//
320class VarEnv final {
321 /// Map from `VarKind` to the next free `Var::Num`; used by `bindVar`.
323 /// Map from `VarInfo::ID` to shared storage for the actual `VarInfo` objects.
325 /// Map from variable names to their `VarInfo::ID`.
326 llvm::StringMap<VarInfo::ID> ids;
327
328 VarInfo::ID nextID() const { return static_cast<VarInfo::ID>(vars.size()); }
329
330public:
331 VarEnv() : nextNum(0) {}
332
333 /// Gets the underlying storage for the `VarInfo` identified by
334 /// the `VarInfo::ID`.
335 ///
336 /// NOTE: The returned reference can become dangling if the `VarEnv`
337 /// object is mutated during the lifetime of the pointer. Therefore,
338 /// client code should not store the reference nor otherwise allow it
339 /// to live too long.
340 VarInfo const &access(VarInfo::ID id) const {
341 // `SmallVector::operator[]` already asserts the index is in-bounds.
342 return vars[llvm::to_underlying(id)];
343 }
344 VarInfo const *access(std::optional<VarInfo::ID> oid) const {
345 return oid ? &access(*oid) : nullptr;
346 }
347
348private:
350 return const_cast<VarInfo &>(std::as_const(*this).access(id));
351 }
352 VarInfo *access(std::optional<VarInfo::ID> oid) {
353 return const_cast<VarInfo *>(std::as_const(*this).access(oid));
354 }
355
356public:
357 /// Looks up the variable with the given name.
358 std::optional<VarInfo::ID> lookup(StringRef name) const;
359
360 /// Creates a new currently-unbound variable. When a variable
361 /// of that name already exists: if `verifyUsage` is true, then will assert
362 /// that the variable has the same kind and a consistent location; otherwise,
363 /// when `verifyUsage` is false, this is a noop. Returns the identifier
364 /// for the variable with the given name, and a bool indicating whether
365 /// a new variable was created.
366 std::optional<std::pair<VarInfo::ID, bool>>
367 create(StringRef name, llvm::SMLoc loc, VarKind vk, bool verifyUsage = false);
368
369 /// Looks up or creates a variable according to the given
370 /// `Policy`. Returns nullopt in one of two circumstances:
371 /// (1) the policy says we `Must` create, yet the variable already exists;
372 /// (2) the policy says we `MustNot` create, yet no such variable exists.
373 /// Otherwise, if the variable already exists then it is validated against
374 /// the given kind and location to ensure consistency.
375 std::optional<std::pair<VarInfo::ID, bool>>
376 lookupOrCreate(Policy creationPolicy, StringRef name, llvm::SMLoc loc,
377 VarKind vk);
378
379 /// Binds the given variable to the next free `Var::Num` for its `VarKind`.
380 Var bindVar(VarInfo::ID id);
381
382 /// Creates a new variable of the given kind and immediately binds it.
383 /// This should only be used whenever the variable is known to be unused
384 /// and therefore does not have a name.
385 Var bindUnusedVar(VarKind vk);
386
388
389 /// Returns the current ranks of bound variables. This method should
390 /// only be used after the environment is "finished", since binding new
391 /// variables will (semantically) invalidate any previously returned `Ranks`.
392 Ranks getRanks() const { return Ranks(nextNum); }
393
394 /// Gets the `Var` identified by the `VarInfo::ID`, raising an assertion
395 /// failure if the variable is not bound.
396 Var getVar(VarInfo::ID id) const { return access(id).getVar(); }
397};
398
399//===----------------------------------------------------------------------===//
400
401} // namespace ir_detail
402} // namespace sparse_tensor
403} // namespace mlir
404
405#endif // MLIR_DIALECT_SPARSETENSOR_IR_DETAIL_VAR_H
#define add(a, b)
A dimensional identifier appearing in an affine expression.
Definition AffineExpr.h:225
A symbolic identifier appearing in an affine expression.
Definition AffineExpr.h:233
This base class exposes generic asm parser hooks, usable across the various derived parsers.
virtual Location getEncodedSourceLoc(SMLoc loc)=0
Re-encode the given source location as an MLIR location and return it.
This base class exposes generic asm printer hooks, usable across the various derived printers.
This class represents a diagnostic that is inflight and set to be reported.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
static constexpr bool classof(Var const *var)
Definition Var.h:159
static constexpr VarKind Kind
Definition Var.h:158
DimVar(AffineDimExpr dimExpr)
Definition Var.h:163
static constexpr VarKind Kind
Definition Var.h:170
static constexpr bool classof(Var const *var)
Definition Var.h:171
LvlVar(AffineDimExpr lvlExpr)
Definition Var.h:175
constexpr unsigned getRank(VarKind vk) const
Definition Var.h:230
constexpr unsigned getLvlRank() const
Definition Var.h:233
bool operator==(Ranks const &other) const
Definition Var.cpp:52
Ranks(VarKindArray< unsigned > const &ranks)
Definition Var.h:223
bool operator!=(Ranks const &other) const
Definition Var.h:228
constexpr unsigned getDimRank() const
Definition Var.h:232
constexpr unsigned getSymRank() const
Definition Var.h:231
constexpr Ranks(unsigned symRank, unsigned dimRank, unsigned lvlRank)
Definition Var.h:217
constexpr bool isValid(Var var) const
Definition Var.h:235
SymVar(AffineSymbolExpr symExpr)
Definition Var.h:151
static constexpr VarKind Kind
Definition Var.h:146
static constexpr bool classof(Var const *var)
Definition Var.h:147
Var bindUnusedVar(VarKind vk)
Creates a new variable of the given kind and immediately binds it.
Definition Var.cpp:224
Ranks getRanks() const
Returns the current ranks of bound variables.
Definition Var.h:392
InFlightDiagnostic emitErrorIfAnyUnbound(AsmParser &parser) const
Definition Var.cpp:232
std::optional< std::pair< VarInfo::ID, bool > > lookupOrCreate(Policy creationPolicy, StringRef name, llvm::SMLoc loc, VarKind vk)
Looks up or creates a variable according to the given Policy.
Definition Var.cpp:200
Var getVar(VarInfo::ID id) const
Gets the Var identified by the VarInfo::ID, raising an assertion failure if the variable is not bound...
Definition Var.h:396
std::optional< VarInfo::ID > lookup(StringRef name) const
Looks up the variable with the given name.
Definition Var.cpp:173
VarInfo const * access(std::optional< VarInfo::ID > oid) const
Definition Var.h:344
std::optional< std::pair< VarInfo::ID, bool > > create(StringRef name, llvm::SMLoc loc, VarKind vk, bool verifyUsage=false)
Creates a new currently-unbound variable.
Definition Var.cpp:184
Var bindVar(VarInfo::ID id)
Binds the given variable to the next free Var::Num for its VarKind.
Definition Var.cpp:225
VarInfo const & access(VarInfo::ID id) const
Gets the underlying storage for the VarInfo identified by the VarInfo::ID.
Definition Var.h:340
A record of metadata for/about a variable, used by VarEnv.
Definition Var.h:277
constexpr VarKind getKind() const
Definition Var.h:306
constexpr ID getID() const
Definition Var.h:305
constexpr llvm::SMLoc getLoc() const
Definition Var.h:301
constexpr Var getVar() const
Definition Var.h:310
constexpr StringRef getName() const
Definition Var.h:300
ID
Newtype for unique identifiers of VarInfo records, to ensure they aren't confused with Var::Num.
Definition Var.h:281
constexpr bool hasNum() const
Definition Var.h:308
constexpr std::optional< Var::Num > getNum() const
Definition Var.h:307
Location getLocation(AsmParser &parser) const
Definition Var.h:302
constexpr VarInfo(ID id, StringRef name, llvm::SMLoc loc, VarKind vk, std::optional< Var::Num > n={})
Definition Var.h:291
Efficient representation of a set of Var.
Definition Var.h:244
bool contains(Var var) const
For the contains method: if variables occurring in the method parameter are OOB for the VarSet,...
Definition Var.cpp:79
unsigned getRank(VarKind vk) const
Definition Var.h:250
The underlying implementation of Var.
Definition Var.h:90
constexpr Num getNum() const
Definition Var.h:103
constexpr bool operator!=(Impl other) const
Definition Var.h:101
constexpr VarKind getKind() const
Definition Var.h:102
constexpr Impl(VarKind vk, Num n)
Definition Var.h:94
constexpr bool operator==(Impl other) const
Definition Var.h:100
A concrete variable, to be used in our variant of AffineExpr.
Definition Var.h:61
constexpr Num getNum() const
Definition Var.h:125
constexpr Var(Impl impl)
Protected ctor for the RTTI methods to use.
Definition Var.h:112
LLVM_DUMP_METHOD void dump() const
Definition Var.cpp:42
constexpr std::optional< U > dyn_cast() const
Definition Var.h:197
Var(VarKind vk, AffineDimExpr var)
Definition Var.h:117
constexpr VarKind getKind() const
Definition Var.h:124
void print(llvm::raw_ostream &os) const
Definition Var.cpp:37
constexpr bool operator!=(Var other) const
Definition Var.h:122
constexpr U cast() const
Definition Var.h:190
constexpr Var(VarKind vk, Num n)
Definition Var.h:115
std::string str() const
Definition Var.cpp:28
static constexpr bool isWF_Num(Num n)
Checks whether the number would be accepted by Var(VarKind,Var::Num).
Definition Var.h:83
constexpr bool isa() const
Definition Var.h:180
Var(AffineSymbolExpr sym)
Definition Var.h:116
constexpr bool operator==(Var other) const
Definition Var.h:121
unsigned Num
Typedef for the type of variable numbers.
Definition Var.h:64
The OpAsmOpInterface, see OpAsmInterface.td for more details.
Definition CallGraph.h:229
llvm::EnumeratedArray< T, VarKind, VarKind::Level > VarKindArray
The type of arrays indexed by VarKind.
Definition Var.h:55
constexpr bool isWF(VarKind vk)
Definition Var.h:34
VarKind
The three kinds of variables that Var can be.
Definition Var.h:32
constexpr char toChar(VarKind vk)
Gets the ASCII character used as the prefix when printing Var.
Definition Var.h:40
static constexpr bool IsZeroCostAbstraction
Include the generated interface declarations.