1 //===- SDBMExpr.h - MLIR SDBM Expression ------------------------*- 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 // A striped difference-bound matrix (SDBM) expression is a constant expression,
10 // an identifier, a binary expression with constant RHS and +, stripe operators
11 // or a difference expression between two identifiers.
12 //
13 //===----------------------------------------------------------------------===//
14 
15 #ifndef MLIR_DIALECT_SDBM_SDBMEXPR_H
16 #define MLIR_DIALECT_SDBM_SDBMEXPR_H
17 
18 #include "mlir/Support/LLVM.h"
19 #include "llvm/ADT/DenseMapInfo.h"
20 
21 namespace mlir {
22 
23 class AffineExpr;
24 class MLIRContext;
25 
26 enum class SDBMExprKind { Add, Stripe, Diff, Constant, DimId, SymbolId, Neg };
27 
28 namespace detail {
29 struct SDBMExprStorage;
30 struct SDBMBinaryExprStorage;
31 struct SDBMDiffExprStorage;
32 struct SDBMTermExprStorage;
33 struct SDBMConstantExprStorage;
34 struct SDBMNegExprStorage;
35 } // namespace detail
36 
37 class SDBMConstantExpr;
38 class SDBMDialect;
39 class SDBMDimExpr;
40 class SDBMSymbolExpr;
41 class SDBMTermExpr;
42 
43 /// Striped Difference-Bounded Matrix (SDBM) expression is a base left-hand side
44 /// expression for the SDBM framework.  SDBM expressions are a subset of affine
45 /// expressions supporting low-complexity algorithms for the operations used in
46 /// loop transformations.  In particular, are supported:
47 ///   - constant expressions;
48 ///   - single variables (dimensions and symbols) with +1 or -1 coefficient;
49 ///   - stripe expressions: "x # C", where "x" is a single variable or another
50 ///     stripe expression, "#" is the stripe operator, and "C" is a constant
51 ///     expression; "#" is defined as x - x mod C.
52 ///   - sum expressions between single variable/stripe expressions and constant
53 ///     expressions;
54 ///   - difference expressions between single variable/stripe expressions.
55 /// `SDBMExpr` class hierarchy provides a type-safe interface to constructing
56 /// and operating on SDBM expressions.  For example, it requires the LHS of a
57 /// sum expression to be a single variable or a stripe expression.  These
58 /// restrictions are intended to force the caller to perform the necessary
59 /// simplifications to stay within the SDBM domain, because SDBM expressions do
60 /// not combine in more cases than they do.  This choice may be reconsidered in
61 /// the future.
62 ///
63 /// SDBM expressions are grouped into the following structure
64 /// - expression
65 ///   - varying
66 ///     - direct
67 ///       - sum <- (term, constant)
68 ///       - term
69 ///         - symbol
70 ///         - dimension
71 ///         - stripe <- (direct, constant)
72 ///     - negation <- (direct)
73 ///     - difference <- (direct, term)
74 ///   - constant
75 /// The notation <- (...) denotes the types of subexpressions a compound
76 /// expression can combine.  The tree of subexpressions essentially imposes the
77 /// following canonicalization rules:
78 ///   - constants are always folded;
79 ///   - constants can only appear on the RHS of an expression;
80 ///   - double negation must be elided;
81 ///   - an additive constant term is only allowed in a sum expression, and
82 ///     should be sunk into the nearest such expression in the tree;
83 ///   - zero constant expression can only appear at the top level.
84 ///
85 /// `SDBMExpr` and derived classes are thin wrappers around a pointer owned by
86 /// an MLIRContext, and should be used by-value.  They are uniqued in the
87 /// MLIRContext and immortal.
88 class SDBMExpr {
89 public:
90   using ImplType = detail::SDBMExprStorage;
SDBMExpr()91   SDBMExpr() : impl(nullptr) {}
SDBMExpr(ImplType * expr)92   /* implicit */ SDBMExpr(ImplType *expr) : impl(expr) {}
93 
94   /// SDBM expressions are thin wrappers around a unique'ed immutable pointer,
95   /// which makes them trivially assignable and trivially copyable.
96   SDBMExpr(const SDBMExpr &) = default;
97   SDBMExpr &operator=(const SDBMExpr &) = default;
98 
99   /// SDBM expressions can be compared straight-forwardly.
100   bool operator==(const SDBMExpr &other) const { return impl == other.impl; }
101   bool operator!=(const SDBMExpr &other) const { return !(*this == other); }
102 
103   /// SDBM expressions are convertible to `bool`: null expressions are converted
104   /// to false, non-null expressions are converted to true.
105   explicit operator bool() const { return impl != nullptr; }
106   bool operator!() const { return !static_cast<bool>(*this); }
107 
108   /// Negate the given SDBM expression.
109   SDBMExpr operator-();
110 
111   /// Prints the SDBM expression.
112   void print(raw_ostream &os) const;
113   void dump() const;
114 
115   /// LLVM-style casts.
isa()116   template <typename U> bool isa() const { return U::isClassFor(*this); }
dyn_cast()117   template <typename U> U dyn_cast() const {
118     if (!isa<U>())
119       return {};
120     return U(const_cast<SDBMExpr *>(this)->impl);
121   }
cast()122   template <typename U> U cast() const {
123     assert(isa<U>() && "cast to incorrect subtype");
124     return U(const_cast<SDBMExpr *>(this)->impl);
125   }
126 
127   /// Support for LLVM hashing.
hash_value()128   ::llvm::hash_code hash_value() const { return ::llvm::hash_value(impl); }
129 
130   /// Returns the kind of the SDBM expression.
131   SDBMExprKind getKind() const;
132 
133   /// Returns the MLIR context in which this expression lives.
134   MLIRContext *getContext() const;
135 
136   /// Returns the SDBM dialect instance.
137   SDBMDialect *getDialect() const;
138 
139   /// Convert the SDBM expression into an Affine expression.  This always
140   /// succeeds because SDBM are a subset of affine.
141   AffineExpr getAsAffineExpr() const;
142 
143   /// Try constructing an SDBM expression from the given affine expression.
144   /// This may fail if the affine expression is not representable as SDBM, in
145   /// which case llvm::None is returned.  The conversion procedure recognizes
146   /// (nested) multiplicative ((x floordiv B) * B) and additive (x - x mod B)
147   /// patterns for the stripe expression.
148   static Optional<SDBMExpr> tryConvertAffineExpr(AffineExpr affine);
149 
150 protected:
151   ImplType *impl;
152 };
153 
154 /// SDBM constant expression, wraps a 64-bit integer.
155 class SDBMConstantExpr : public SDBMExpr {
156 public:
157   using ImplType = detail::SDBMConstantExprStorage;
158 
159   using SDBMExpr::SDBMExpr;
160 
161   /// Obtain or create a constant expression unique'ed in the given dialect
162   /// (which belongs to a context).
163   static SDBMConstantExpr get(SDBMDialect *dialect, int64_t value);
164 
isClassFor(const SDBMExpr & expr)165   static bool isClassFor(const SDBMExpr &expr) {
166     return expr.getKind() == SDBMExprKind::Constant;
167   }
168 
169   int64_t getValue() const;
170 };
171 
172 /// SDBM varying expression can be one of:
173 ///   - input variable expression;
174 ///   - stripe expression;
175 ///   - negation (product with -1) of either of the above.
176 ///   - sum of a varying and a constant expression
177 ///   - difference between varying expressions
178 class SDBMVaryingExpr : public SDBMExpr {
179 public:
180   using ImplType = detail::SDBMExprStorage;
181   using SDBMExpr::SDBMExpr;
182 
isClassFor(const SDBMExpr & expr)183   static bool isClassFor(const SDBMExpr &expr) {
184     return expr.getKind() == SDBMExprKind::DimId ||
185            expr.getKind() == SDBMExprKind::SymbolId ||
186            expr.getKind() == SDBMExprKind::Neg ||
187            expr.getKind() == SDBMExprKind::Stripe ||
188            expr.getKind() == SDBMExprKind::Add ||
189            expr.getKind() == SDBMExprKind::Diff;
190   }
191 };
192 
193 /// SDBM direct expression includes exactly one variable (symbol or dimension),
194 /// which is not negated in the expression.  It can be one of:
195 ///   - term expression;
196 ///   - sum expression.
197 class SDBMDirectExpr : public SDBMVaryingExpr {
198 public:
199   using SDBMVaryingExpr::SDBMVaryingExpr;
200 
201   /// If this is a sum expression, return its variable part, otherwise return
202   /// self.
203   SDBMTermExpr getTerm();
204 
205   /// If this is a sum expression, return its constant part, otherwise return 0.
206   int64_t getConstant();
207 
isClassFor(const SDBMExpr & expr)208   static bool isClassFor(const SDBMExpr &expr) {
209     return expr.getKind() == SDBMExprKind::DimId ||
210            expr.getKind() == SDBMExprKind::SymbolId ||
211            expr.getKind() == SDBMExprKind::Stripe ||
212            expr.getKind() == SDBMExprKind::Add;
213   }
214 };
215 
216 /// SDBM term expression can be one of:
217 ///  - single variable expression;
218 ///  - stripe expression.
219 /// Stripe expressions are treated as terms since, in the SDBM domain, they are
220 /// attached to temporary variables and can appear anywhere a variable can.
221 class SDBMTermExpr : public SDBMDirectExpr {
222 public:
223   using SDBMDirectExpr::SDBMDirectExpr;
224 
isClassFor(const SDBMExpr & expr)225   static bool isClassFor(const SDBMExpr &expr) {
226     return expr.getKind() == SDBMExprKind::DimId ||
227            expr.getKind() == SDBMExprKind::SymbolId ||
228            expr.getKind() == SDBMExprKind::Stripe;
229   }
230 };
231 
232 /// SDBM sum expression.  LHS is a term expression and RHS is a constant.
233 class SDBMSumExpr : public SDBMDirectExpr {
234 public:
235   using ImplType = detail::SDBMBinaryExprStorage;
236   using SDBMDirectExpr::SDBMDirectExpr;
237 
238   /// Obtain or create a sum expression unique'ed in the given context.
239   static SDBMSumExpr get(SDBMTermExpr lhs, SDBMConstantExpr rhs);
240 
isClassFor(const SDBMExpr & expr)241   static bool isClassFor(const SDBMExpr &expr) {
242     SDBMExprKind kind = expr.getKind();
243     return kind == SDBMExprKind::Add;
244   }
245 
246   SDBMTermExpr getLHS() const;
247   SDBMConstantExpr getRHS() const;
248 };
249 
250 /// SDBM difference expression.  LHS is a direct expression, i.e. it may be a
251 /// sum of a term and a constant.  RHS is a term expression.  Thus the
252 /// expression (t1 - t2 + C) with term expressions t1,t2 is represented as
253 ///   diff(sum(t1, C), t2)
254 /// and it is possible to extract the constant factor without negating it.
255 class SDBMDiffExpr : public SDBMVaryingExpr {
256 public:
257   using ImplType = detail::SDBMDiffExprStorage;
258   using SDBMVaryingExpr::SDBMVaryingExpr;
259 
260   /// Obtain or create a difference expression unique'ed in the given context.
261   static SDBMDiffExpr get(SDBMDirectExpr lhs, SDBMTermExpr rhs);
262 
isClassFor(const SDBMExpr & expr)263   static bool isClassFor(const SDBMExpr &expr) {
264     return expr.getKind() == SDBMExprKind::Diff;
265   }
266 
267   SDBMDirectExpr getLHS() const;
268   SDBMTermExpr getRHS() const;
269 };
270 
271 /// SDBM stripe expression "x # C" where "x" is a term expression, "C" is a
272 /// constant expression and "#" is the stripe operator defined as:
273 ///   x # C = x - x mod C.
274 class SDBMStripeExpr : public SDBMTermExpr {
275 public:
276   using ImplType = detail::SDBMBinaryExprStorage;
277   using SDBMTermExpr::SDBMTermExpr;
278 
isClassFor(const SDBMExpr & expr)279   static bool isClassFor(const SDBMExpr &expr) {
280     return expr.getKind() == SDBMExprKind::Stripe;
281   }
282 
283   static SDBMStripeExpr get(SDBMDirectExpr var, SDBMConstantExpr stripeFactor);
284 
285   SDBMDirectExpr getLHS() const;
286   SDBMConstantExpr getStripeFactor() const;
287 };
288 
289 /// SDBM "input" variable expression can be either a dimension identifier or
290 /// a symbol identifier.  When used to define SDBM functions, dimensions are
291 /// interpreted as function arguments while symbols are treated as unknown but
292 /// constant values, hence the name.
293 class SDBMInputExpr : public SDBMTermExpr {
294 public:
295   using ImplType = detail::SDBMTermExprStorage;
296   using SDBMTermExpr::SDBMTermExpr;
297 
isClassFor(const SDBMExpr & expr)298   static bool isClassFor(const SDBMExpr &expr) {
299     return expr.getKind() == SDBMExprKind::DimId ||
300            expr.getKind() == SDBMExprKind::SymbolId;
301   }
302 
303   unsigned getPosition() const;
304 };
305 
306 /// SDBM dimension expression.  Dimensions correspond to function arguments
307 /// when defining functions using SDBM expressions.
308 class SDBMDimExpr : public SDBMInputExpr {
309 public:
310   using ImplType = detail::SDBMTermExprStorage;
311   using SDBMInputExpr::SDBMInputExpr;
312 
313   /// Obtain or create a dimension expression unique'ed in the given dialect
314   /// (which belongs to a context).
315   static SDBMDimExpr get(SDBMDialect *dialect, unsigned position);
316 
isClassFor(const SDBMExpr & expr)317   static bool isClassFor(const SDBMExpr &expr) {
318     return expr.getKind() == SDBMExprKind::DimId;
319   }
320 };
321 
322 /// SDBM symbol expression.  Symbols correspond to symbolic constants when
323 /// defining functions using SDBM expressions.
324 class SDBMSymbolExpr : public SDBMInputExpr {
325 public:
326   using ImplType = detail::SDBMTermExprStorage;
327   using SDBMInputExpr::SDBMInputExpr;
328 
329   /// Obtain or create a symbol expression unique'ed in the given dialect (which
330   /// belongs to a context).
331   static SDBMSymbolExpr get(SDBMDialect *dialect, unsigned position);
332 
isClassFor(const SDBMExpr & expr)333   static bool isClassFor(const SDBMExpr &expr) {
334     return expr.getKind() == SDBMExprKind::SymbolId;
335   }
336 };
337 
338 /// Negation of an SDBM variable expression.  Equivalent to multiplying the
339 /// expression with -1 (SDBM does not support other coefficients that 1 and -1).
340 class SDBMNegExpr : public SDBMVaryingExpr {
341 public:
342   using ImplType = detail::SDBMNegExprStorage;
343   using SDBMVaryingExpr::SDBMVaryingExpr;
344 
345   /// Obtain or create a negation expression unique'ed in the given context.
346   static SDBMNegExpr get(SDBMDirectExpr var);
347 
isClassFor(const SDBMExpr & expr)348   static bool isClassFor(const SDBMExpr &expr) {
349     return expr.getKind() == SDBMExprKind::Neg;
350   }
351 
352   SDBMDirectExpr getVar() const;
353 };
354 
355 /// A visitor class for SDBM expressions.  Calls the kind-specific function
356 /// depending on the kind of expression it visits.
357 template <typename Derived, typename Result = void> class SDBMVisitor {
358 public:
359   /// Visit the given SDBM expression, dispatching to kind-specific functions.
visit(SDBMExpr expr)360   Result visit(SDBMExpr expr) {
361     auto *derived = static_cast<Derived *>(this);
362     switch (expr.getKind()) {
363     case SDBMExprKind::Add:
364     case SDBMExprKind::Diff:
365     case SDBMExprKind::DimId:
366     case SDBMExprKind::SymbolId:
367     case SDBMExprKind::Neg:
368     case SDBMExprKind::Stripe:
369       return derived->visitVarying(expr.cast<SDBMVaryingExpr>());
370     case SDBMExprKind::Constant:
371       return derived->visitConstant(expr.cast<SDBMConstantExpr>());
372     }
373 
374     llvm_unreachable("unsupported SDBM expression kind");
375   }
376 
377   /// Traverse the SDBM expression tree calling `visit` on each node
378   /// in depth-first preorder.
walkPreorder(SDBMExpr expr)379   void walkPreorder(SDBMExpr expr) { return walk</*isPreorder=*/true>(expr); }
380 
381   /// Traverse the SDBM expression tree calling `visit` on each node in
382   /// depth-first postorder.
walkPostorder(SDBMExpr expr)383   void walkPostorder(SDBMExpr expr) { return walk</*isPreorder=*/false>(expr); }
384 
385 protected:
386   /// Default visitors do nothing.
visitSum(SDBMSumExpr)387   void visitSum(SDBMSumExpr) {}
visitDiff(SDBMDiffExpr)388   void visitDiff(SDBMDiffExpr) {}
visitStripe(SDBMStripeExpr)389   void visitStripe(SDBMStripeExpr) {}
visitDim(SDBMDimExpr)390   void visitDim(SDBMDimExpr) {}
visitSymbol(SDBMSymbolExpr)391   void visitSymbol(SDBMSymbolExpr) {}
visitNeg(SDBMNegExpr)392   void visitNeg(SDBMNegExpr) {}
visitConstant(SDBMConstantExpr)393   void visitConstant(SDBMConstantExpr) {}
394 
395   /// Default implementation of visitDirect dispatches to the dedicated for sums
396   /// or delegates to visitTerm for the other expression kinds.  Concrete
397   /// visitors can overload it.
visitDirect(SDBMDirectExpr expr)398   Result visitDirect(SDBMDirectExpr expr) {
399     auto *derived = static_cast<Derived *>(this);
400     if (auto sum = expr.dyn_cast<SDBMSumExpr>())
401       return derived->visitSum(sum);
402     else
403       return derived->visitTerm(expr.cast<SDBMTermExpr>());
404   }
405 
406   /// Default implementation of visitTerm dispatches to the special functions
407   /// for stripes and other variables.  Concrete visitors can override it.
visitTerm(SDBMTermExpr expr)408   Result visitTerm(SDBMTermExpr expr) {
409     auto *derived = static_cast<Derived *>(this);
410     if (expr.getKind() == SDBMExprKind::Stripe)
411       return derived->visitStripe(expr.cast<SDBMStripeExpr>());
412     else
413       return derived->visitInput(expr.cast<SDBMInputExpr>());
414   }
415 
416   /// Default implementation of visitInput dispatches to the special
417   /// functions for dimensions or symbols.  Concrete visitors can override it to
418   /// visit all variables instead.
visitInput(SDBMInputExpr expr)419   Result visitInput(SDBMInputExpr expr) {
420     auto *derived = static_cast<Derived *>(this);
421     if (expr.getKind() == SDBMExprKind::DimId)
422       return derived->visitDim(expr.cast<SDBMDimExpr>());
423     else
424       return derived->visitSymbol(expr.cast<SDBMSymbolExpr>());
425   }
426 
427   /// Default implementation of visitVarying dispatches to the special
428   /// functions for variables and negations thereof.  Concrete visitors can
429   /// override it to visit all variables and negations instead.
visitVarying(SDBMVaryingExpr expr)430   Result visitVarying(SDBMVaryingExpr expr) {
431     auto *derived = static_cast<Derived *>(this);
432     if (auto var = expr.dyn_cast<SDBMDirectExpr>())
433       return derived->visitDirect(var);
434     else if (auto neg = expr.dyn_cast<SDBMNegExpr>())
435       return derived->visitNeg(neg);
436     else if (auto diff = expr.dyn_cast<SDBMDiffExpr>())
437       return derived->visitDiff(diff);
438 
439     llvm_unreachable("unhandled subtype of varying SDBM expression");
440   }
441 
walk(SDBMExpr expr)442   template <bool isPreorder> void walk(SDBMExpr expr) {
443     if (isPreorder)
444       visit(expr);
445     if (auto sumExpr = expr.dyn_cast<SDBMSumExpr>()) {
446       walk<isPreorder>(sumExpr.getLHS());
447       walk<isPreorder>(sumExpr.getRHS());
448     } else if (auto diffExpr = expr.dyn_cast<SDBMDiffExpr>()) {
449       walk<isPreorder>(diffExpr.getLHS());
450       walk<isPreorder>(diffExpr.getRHS());
451     } else if (auto stripeExpr = expr.dyn_cast<SDBMStripeExpr>()) {
452       walk<isPreorder>(stripeExpr.getLHS());
453       walk<isPreorder>(stripeExpr.getStripeFactor());
454     } else if (auto negExpr = expr.dyn_cast<SDBMNegExpr>()) {
455       walk<isPreorder>(negExpr.getVar());
456     }
457     if (!isPreorder)
458       visit(expr);
459   }
460 };
461 
462 /// Overloaded arithmetic operators for SDBM expressions asserting that their
463 /// arguments have the proper SDBM expression subtype.  Perform canonicalization
464 /// and constant folding on these expressions.
465 namespace ops_assertions {
466 
467 /// Add two SDBM expressions.  At least one of the expressions must be a
468 /// constant or a negation, but both expressions cannot be negations
469 /// simultaneously.
470 SDBMExpr operator+(SDBMExpr lhs, SDBMExpr rhs);
471 inline SDBMExpr operator+(SDBMExpr lhs, int64_t rhs) {
472   return lhs + SDBMConstantExpr::get(lhs.getDialect(), rhs);
473 }
474 inline SDBMExpr operator+(int64_t lhs, SDBMExpr rhs) {
475   return SDBMConstantExpr::get(rhs.getDialect(), lhs) + rhs;
476 }
477 
478 /// Subtract an SDBM expression from another SDBM expression.  Both expressions
479 /// must not be difference expressions.
480 SDBMExpr operator-(SDBMExpr lhs, SDBMExpr rhs);
481 inline SDBMExpr operator-(SDBMExpr lhs, int64_t rhs) {
482   return lhs - SDBMConstantExpr::get(lhs.getDialect(), rhs);
483 }
484 inline SDBMExpr operator-(int64_t lhs, SDBMExpr rhs) {
485   return SDBMConstantExpr::get(rhs.getDialect(), lhs) - rhs;
486 }
487 
488 /// Construct a stripe expression from a positive expression and a positive
489 /// constant stripe factor.
490 SDBMExpr stripe(SDBMExpr expr, SDBMExpr factor);
stripe(SDBMExpr expr,int64_t factor)491 inline SDBMExpr stripe(SDBMExpr expr, int64_t factor) {
492   return stripe(expr, SDBMConstantExpr::get(expr.getDialect(), factor));
493 }
494 } // namespace ops_assertions
495 
496 } // end namespace mlir
497 
498 namespace llvm {
499 // SDBMExpr hash just like pointers.
500 template <> struct DenseMapInfo<mlir::SDBMExpr> {
501   static mlir::SDBMExpr getEmptyKey() {
502     auto *pointer = llvm::DenseMapInfo<void *>::getEmptyKey();
503     return mlir::SDBMExpr(static_cast<mlir::SDBMExpr::ImplType *>(pointer));
504   }
505   static mlir::SDBMExpr getTombstoneKey() {
506     auto *pointer = llvm::DenseMapInfo<void *>::getTombstoneKey();
507     return mlir::SDBMExpr(static_cast<mlir::SDBMExpr::ImplType *>(pointer));
508   }
509   static unsigned getHashValue(mlir::SDBMExpr expr) {
510     return expr.hash_value();
511   }
512   static bool isEqual(mlir::SDBMExpr lhs, mlir::SDBMExpr rhs) {
513     return lhs == rhs;
514   }
515 };
516 
517 // SDBMDirectExpr hash just like pointers.
518 template <> struct DenseMapInfo<mlir::SDBMDirectExpr> {
519   static mlir::SDBMDirectExpr getEmptyKey() {
520     auto *pointer = llvm::DenseMapInfo<void *>::getEmptyKey();
521     return mlir::SDBMDirectExpr(
522         static_cast<mlir::SDBMExpr::ImplType *>(pointer));
523   }
524   static mlir::SDBMDirectExpr getTombstoneKey() {
525     auto *pointer = llvm::DenseMapInfo<void *>::getTombstoneKey();
526     return mlir::SDBMDirectExpr(
527         static_cast<mlir::SDBMExpr::ImplType *>(pointer));
528   }
529   static unsigned getHashValue(mlir::SDBMDirectExpr expr) {
530     return expr.hash_value();
531   }
532   static bool isEqual(mlir::SDBMDirectExpr lhs, mlir::SDBMDirectExpr rhs) {
533     return lhs == rhs;
534   }
535 };
536 
537 // SDBMTermExpr hash just like pointers.
538 template <> struct DenseMapInfo<mlir::SDBMTermExpr> {
539   static mlir::SDBMTermExpr getEmptyKey() {
540     auto *pointer = llvm::DenseMapInfo<void *>::getEmptyKey();
541     return mlir::SDBMTermExpr(static_cast<mlir::SDBMExpr::ImplType *>(pointer));
542   }
543   static mlir::SDBMTermExpr getTombstoneKey() {
544     auto *pointer = llvm::DenseMapInfo<void *>::getTombstoneKey();
545     return mlir::SDBMTermExpr(static_cast<mlir::SDBMExpr::ImplType *>(pointer));
546   }
547   static unsigned getHashValue(mlir::SDBMTermExpr expr) {
548     return expr.hash_value();
549   }
550   static bool isEqual(mlir::SDBMTermExpr lhs, mlir::SDBMTermExpr rhs) {
551     return lhs == rhs;
552   }
553 };
554 
555 // SDBMConstantExpr hash just like pointers.
556 template <> struct DenseMapInfo<mlir::SDBMConstantExpr> {
557   static mlir::SDBMConstantExpr getEmptyKey() {
558     auto *pointer = llvm::DenseMapInfo<void *>::getEmptyKey();
559     return mlir::SDBMConstantExpr(
560         static_cast<mlir::SDBMExpr::ImplType *>(pointer));
561   }
562   static mlir::SDBMConstantExpr getTombstoneKey() {
563     auto *pointer = llvm::DenseMapInfo<void *>::getTombstoneKey();
564     return mlir::SDBMConstantExpr(
565         static_cast<mlir::SDBMExpr::ImplType *>(pointer));
566   }
567   static unsigned getHashValue(mlir::SDBMConstantExpr expr) {
568     return expr.hash_value();
569   }
570   static bool isEqual(mlir::SDBMConstantExpr lhs, mlir::SDBMConstantExpr rhs) {
571     return lhs == rhs;
572   }
573 };
574 } // namespace llvm
575 
576 #endif // MLIR_DIALECT_SDBM_SDBMEXPR_H
577