1 //===- ExtensibleDialect.h - Extensible dialect -----------------*- C++ -*-===//
2 //
3 // This file is licensed 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 defines the DynamicOpDefinition class, the DynamicTypeDefinition
10 // class, and the DynamicAttrDefinition class, which represent respectively
11 // operations, types, and attributes that can be defined at runtime. They can
12 // be registered at runtime to an extensible dialect, using the
13 // ExtensibleDialect class defined in this file.
14 //
15 // For a more complete documentation, see
16 // https://mlir.llvm.org/docs/ExtensibleDialects/ .
17 //
18 //===----------------------------------------------------------------------===//
19 
20 #ifndef MLIR_IR_EXTENSIBLEDIALECT_H
21 #define MLIR_IR_EXTENSIBLEDIALECT_H
22 
23 #include "mlir/IR/Dialect.h"
24 #include "mlir/IR/DialectInterface.h"
25 #include "mlir/IR/MLIRContext.h"
26 #include "mlir/IR/OpDefinition.h"
27 #include "mlir/Support/TypeID.h"
28 #include "llvm/ADT/StringMap.h"
29 
30 namespace mlir {
31 class AsmParser;
32 class AsmPrinter;
33 class DynamicAttr;
34 class DynamicType;
35 class ExtensibleDialect;
36 class MLIRContext;
37 class OptionalParseResult;
38 class ParseResult;
39 
40 namespace detail {
41 struct DynamicAttrStorage;
42 struct DynamicTypeStorage;
43 } // namespace detail
44 
45 //===----------------------------------------------------------------------===//
46 // Dynamic attribute
47 //===----------------------------------------------------------------------===//
48 
49 /// The definition of a dynamic attribute. A dynamic attribute is an attribute
50 /// that is defined at runtime, and that can be registered at runtime by an
51 /// extensible dialect (a dialect inheriting ExtensibleDialect). This class
52 /// stores the parser, the printer, and the verifier of the attribute. Each
53 /// dynamic attribute definition refers to one instance of this class.
54 class DynamicAttrDefinition : SelfOwningTypeID {
55 public:
56   using VerifierFn = llvm::unique_function<LogicalResult(
57       function_ref<InFlightDiagnostic()>, ArrayRef<Attribute>) const>;
58   using ParserFn = llvm::unique_function<ParseResult(
59       AsmParser &parser, llvm::SmallVectorImpl<Attribute> &parsedAttributes)
60                                              const>;
61   using PrinterFn = llvm::unique_function<void(
62       AsmPrinter &printer, ArrayRef<Attribute> params) const>;
63 
64   /// Create a new attribute definition at runtime. The attribute is registered
65   /// only after passing it to the dialect using registerDynamicAttr.
66   static std::unique_ptr<DynamicAttrDefinition>
67   get(StringRef name, ExtensibleDialect *dialect, VerifierFn &&verifier);
68   static std::unique_ptr<DynamicAttrDefinition>
69   get(StringRef name, ExtensibleDialect *dialect, VerifierFn &&verifier,
70       ParserFn &&parser, PrinterFn &&printer);
71 
72   /// Check that the attribute parameters are valid.
verify(function_ref<InFlightDiagnostic ()> emitError,ArrayRef<Attribute> params)73   LogicalResult verify(function_ref<InFlightDiagnostic()> emitError,
74                        ArrayRef<Attribute> params) const {
75     return verifier(emitError, params);
76   }
77 
78   /// Return the MLIRContext in which the dynamic attributes are uniqued.
getContext()79   MLIRContext &getContext() const { return *ctx; }
80 
81   /// Return the name of the attribute, in the format 'attrname' and
82   /// not 'dialectname.attrname'.
getName()83   StringRef getName() const { return name; }
84 
85   /// Return the dialect defining the attribute.
getDialect()86   ExtensibleDialect *getDialect() const { return dialect; }
87 
88 private:
89   DynamicAttrDefinition(StringRef name, ExtensibleDialect *dialect,
90                         VerifierFn &&verifier, ParserFn &&parser,
91                         PrinterFn &&printer);
92 
93   /// This constructor should only be used when we need a pointer to
94   /// the DynamicAttrDefinition in the verifier, the parser, or the printer.
95   /// The verifier, parser, and printer need thus to be initialized after the
96   /// constructor.
97   DynamicAttrDefinition(ExtensibleDialect *dialect, StringRef name);
98 
99   /// Register the concrete attribute in the attribute Uniquer.
100   void registerInAttrUniquer();
101 
102   /// The name should be prefixed with the dialect name followed by '.'.
103   std::string name;
104 
105   /// Dialect in which this attribute is defined.
106   ExtensibleDialect *dialect;
107 
108   /// The attribute verifier. It checks that the attribute parameters satisfy
109   /// the invariants.
110   VerifierFn verifier;
111 
112   /// The attribute parameters parser. It parses only the parameters, and
113   /// expects the attribute name to have already been parsed.
114   ParserFn parser;
115 
116   /// The attribute parameters printer. It prints only the parameters, and
117   /// expects the attribute name to have already been printed.
118   PrinterFn printer;
119 
120   /// Context in which the concrete attributes are uniqued.
121   MLIRContext *ctx;
122 
123   friend ExtensibleDialect;
124   friend DynamicAttr;
125 };
126 
127 /// This trait is used to determine if an attribute is a dynamic attribute or
128 /// not; it should only be implemented by dynamic attributes.
129 /// Note: This is only required because dynamic attributes do not have a
130 /// static/single TypeID.
131 namespace AttributeTrait {
132 template <typename ConcreteType>
133 class IsDynamicAttr : public TraitBase<ConcreteType, IsDynamicAttr> {};
134 } // namespace AttributeTrait
135 
136 /// A dynamic attribute instance. This is an attribute whose definition is
137 /// defined at runtime.
138 /// It is possible to check if an attribute is a dynamic attribute using
139 /// `my_attr.isa<DynamicAttr>()`, and getting the attribute definition of a
140 /// dynamic attribute using the `DynamicAttr::getAttrDef` method.
141 /// All dynamic attributes have the same storage, which is an array of
142 /// attributes.
143 
144 class DynamicAttr : public Attribute::AttrBase<DynamicAttr, Attribute,
145                                                detail::DynamicAttrStorage,
146                                                AttributeTrait::IsDynamicAttr> {
147 public:
148   // Inherit Base constructors.
149   using Base::Base;
150 
151   /// Return an instance of a dynamic attribute given a dynamic attribute
152   /// definition and attribute parameters.
153   /// This asserts that the attribute verifier succeeded.
154   static DynamicAttr get(DynamicAttrDefinition *attrDef,
155                          ArrayRef<Attribute> params = {});
156 
157   /// Return an instance of a dynamic attribute given a dynamic attribute
158   /// definition and attribute parameters. If the parameters provided are
159   /// invalid, errors are emitted using the provided location and a null object
160   /// is returned.
161   static DynamicAttr getChecked(function_ref<InFlightDiagnostic()> emitError,
162                                 DynamicAttrDefinition *attrDef,
163                                 ArrayRef<Attribute> params = {});
164 
165   /// Return the attribute definition of the concrete attribute.
166   DynamicAttrDefinition *getAttrDef();
167 
168   /// Return the attribute parameters.
169   ArrayRef<Attribute> getParams();
170 
171   /// Check if an attribute is a specific dynamic attribute.
isa(Attribute attr,DynamicAttrDefinition * attrDef)172   static bool isa(Attribute attr, DynamicAttrDefinition *attrDef) {
173     return attr.getTypeID() == attrDef->getTypeID();
174   }
175 
176   /// Check if an attribute is a dynamic attribute.
177   static bool classof(Attribute attr);
178 
179   /// Parse the dynamic attribute parameters and construct the attribute.
180   /// The parameters are either empty, and nothing is parsed,
181   /// or they are in the format '<>' or '<attr (,attr)*>'.
182   static ParseResult parse(AsmParser &parser, DynamicAttrDefinition *attrDef,
183                            DynamicAttr &parsedAttr);
184 
185   /// Print the dynamic attribute with the format 'attrname' if there is no
186   /// parameters, or 'attrname<attr (,attr)*>'.
187   void print(AsmPrinter &printer);
188 };
189 
190 //===----------------------------------------------------------------------===//
191 // Dynamic type
192 //===----------------------------------------------------------------------===//
193 
194 /// The definition of a dynamic type. A dynamic type is a type that is
195 /// defined at runtime, and that can be registered at runtime by an
196 /// extensible dialect (a dialect inheriting ExtensibleDialect). This class
197 /// stores the parser, the printer, and the verifier of the type. Each dynamic
198 /// type definition refers to one instance of this class.
199 class DynamicTypeDefinition : SelfOwningTypeID {
200 public:
201   using VerifierFn = llvm::unique_function<LogicalResult(
202       function_ref<InFlightDiagnostic()>, ArrayRef<Attribute>) const>;
203   using ParserFn = llvm::unique_function<ParseResult(
204       AsmParser &parser, llvm::SmallVectorImpl<Attribute> &parsedAttributes)
205                                              const>;
206   using PrinterFn = llvm::unique_function<void(
207       AsmPrinter &printer, ArrayRef<Attribute> params) const>;
208 
209   /// Create a new dynamic type definition. The type is registered only after
210   /// passing it to the dialect using registerDynamicType.
211   static std::unique_ptr<DynamicTypeDefinition>
212   get(StringRef name, ExtensibleDialect *dialect, VerifierFn &&verifier);
213   static std::unique_ptr<DynamicTypeDefinition>
214   get(StringRef name, ExtensibleDialect *dialect, VerifierFn &&verifier,
215       ParserFn &&parser, PrinterFn &&printer);
216 
217   /// Check that the type parameters are valid.
verify(function_ref<InFlightDiagnostic ()> emitError,ArrayRef<Attribute> params)218   LogicalResult verify(function_ref<InFlightDiagnostic()> emitError,
219                        ArrayRef<Attribute> params) const {
220     return verifier(emitError, params);
221   }
222 
223   /// Return the MLIRContext in which the dynamic types is uniqued.
getContext()224   MLIRContext &getContext() const { return *ctx; }
225 
226   /// Return the name of the type, in the format 'typename' and
227   /// not 'dialectname.typename'.
getName()228   StringRef getName() const { return name; }
229 
230   /// Return the dialect defining the type.
getDialect()231   ExtensibleDialect *getDialect() const { return dialect; }
232 
233 private:
234   DynamicTypeDefinition(StringRef name, ExtensibleDialect *dialect,
235                         VerifierFn &&verifier, ParserFn &&parser,
236                         PrinterFn &&printer);
237 
238   /// This constructor should only be used when we need a pointer to
239   /// the DynamicTypeDefinition in the verifier, the parser, or the printer.
240   /// The verifier, parser, and printer need thus to be initialized after the
241   /// constructor.
242   DynamicTypeDefinition(ExtensibleDialect *dialect, StringRef name);
243 
244   /// Register the concrete type in the type Uniquer.
245   void registerInTypeUniquer();
246 
247   /// The name should be prefixed with the dialect name followed by '.'.
248   std::string name;
249 
250   /// Dialect in which this type is defined.
251   ExtensibleDialect *dialect;
252 
253   /// The type verifier. It checks that the type parameters satisfy the
254   /// invariants.
255   VerifierFn verifier;
256 
257   /// The type parameters parser. It parses only the parameters, and expects the
258   /// type name to have already been parsed.
259   ParserFn parser;
260 
261   /// The type parameters printer. It prints only the parameters, and expects
262   /// the type name to have already been printed.
263   PrinterFn printer;
264 
265   /// Context in which the concrete types are uniqued.
266   MLIRContext *ctx;
267 
268   friend ExtensibleDialect;
269   friend DynamicType;
270 };
271 
272 /// This trait is used to determine if a type is a dynamic type or not;
273 /// it should only be implemented by dynamic types.
274 /// Note: This is only required because dynamic type do not have a
275 /// static/single TypeID.
276 namespace TypeTrait {
277 template <typename ConcreteType>
278 class IsDynamicType : public TypeTrait::TraitBase<ConcreteType, IsDynamicType> {
279 };
280 } // namespace TypeTrait
281 
282 /// A dynamic type instance. This is a type whose definition is defined at
283 /// runtime.
284 /// It is possible to check if a type is a dynamic type using
285 /// `my_type.isa<DynamicType>()`, and getting the type definition of a dynamic
286 /// type using the `DynamicType::getTypeDef` method.
287 /// All dynamic types have the same storage, which is an array of attributes.
288 class DynamicType
289     : public Type::TypeBase<DynamicType, Type, detail::DynamicTypeStorage,
290                             TypeTrait::IsDynamicType> {
291 public:
292   // Inherit Base constructors.
293   using Base::Base;
294 
295   /// Return an instance of a dynamic type given a dynamic type definition and
296   /// type parameters.
297   /// This asserts that the type verifier succeeded.
298   static DynamicType get(DynamicTypeDefinition *typeDef,
299                          ArrayRef<Attribute> params = {});
300 
301   /// Return an instance of a dynamic type given a dynamic type definition and
302   /// type parameters. If the parameters provided are invalid, errors are
303   /// emitted using the provided location and a null object is returned.
304   static DynamicType getChecked(function_ref<InFlightDiagnostic()> emitError,
305                                 DynamicTypeDefinition *typeDef,
306                                 ArrayRef<Attribute> params = {});
307 
308   /// Return the type definition of the concrete type.
309   DynamicTypeDefinition *getTypeDef();
310 
311   /// Return the type parameters.
312   ArrayRef<Attribute> getParams();
313 
314   /// Check if a type is a specific dynamic type.
isa(Type type,DynamicTypeDefinition * typeDef)315   static bool isa(Type type, DynamicTypeDefinition *typeDef) {
316     return type.getTypeID() == typeDef->getTypeID();
317   }
318 
319   /// Check if a type is a dynamic type.
320   static bool classof(Type type);
321 
322   /// Parse the dynamic type parameters and construct the type.
323   /// The parameters are either empty, and nothing is parsed,
324   /// or they are in the format '<>' or '<attr (,attr)*>'.
325   static ParseResult parse(AsmParser &parser, DynamicTypeDefinition *typeDef,
326                            DynamicType &parsedType);
327 
328   /// Print the dynamic type with the format
329   /// 'type' or 'type<>' if there is no parameters, or 'type<attr (,attr)*>'.
330   void print(AsmPrinter &printer);
331 };
332 
333 //===----------------------------------------------------------------------===//
334 // Dynamic operation
335 //===----------------------------------------------------------------------===//
336 
337 /// The definition of a dynamic op. A dynamic op is an op that is defined at
338 /// runtime, and that can be registered at runtime by an extensible dialect (a
339 /// dialect inheriting ExtensibleDialect). This class stores the functions that
340 /// are in the OperationName class, and in addition defines the TypeID of the op
341 /// that will be defined.
342 /// Each dynamic operation definition refers to one instance of this class.
343 class DynamicOpDefinition {
344 public:
345   /// Create a new op at runtime. The op is registered only after passing it to
346   /// the dialect using registerDynamicOp.
347   static std::unique_ptr<DynamicOpDefinition>
348   get(StringRef name, ExtensibleDialect *dialect,
349       OperationName::VerifyInvariantsFn &&verifyFn,
350       OperationName::VerifyRegionInvariantsFn &&verifyRegionFn);
351   static std::unique_ptr<DynamicOpDefinition>
352   get(StringRef name, ExtensibleDialect *dialect,
353       OperationName::VerifyInvariantsFn &&verifyFn,
354       OperationName::VerifyRegionInvariantsFn &&verifyRegionFn,
355       OperationName::ParseAssemblyFn &&parseFn,
356       OperationName::PrintAssemblyFn &&printFn);
357   static std::unique_ptr<DynamicOpDefinition>
358   get(StringRef name, ExtensibleDialect *dialect,
359       OperationName::VerifyInvariantsFn &&verifyFn,
360       OperationName::VerifyRegionInvariantsFn &&verifyRegionFn,
361       OperationName::ParseAssemblyFn &&parseFn,
362       OperationName::PrintAssemblyFn &&printFn,
363       OperationName::FoldHookFn &&foldHookFn,
364       OperationName::GetCanonicalizationPatternsFn
365           &&getCanonicalizationPatternsFn,
366       OperationName::PopulateDefaultAttrsFn &&populateDefaultAttrsFn);
367 
368   /// Returns the op typeID.
getTypeID()369   TypeID getTypeID() { return typeID; }
370 
371   /// Sets the verifier function for this operation. It should emits an error
372   /// message and returns failure if a problem is detected, or returns success
373   /// if everything is ok.
setVerifyFn(OperationName::VerifyInvariantsFn && verify)374   void setVerifyFn(OperationName::VerifyInvariantsFn &&verify) {
375     verifyFn = std::move(verify);
376   }
377 
378   /// Sets the region verifier function for this operation. It should emits an
379   /// error message and returns failure if a problem is detected, or returns
380   /// success if everything is ok.
setVerifyRegionFn(OperationName::VerifyRegionInvariantsFn && verify)381   void setVerifyRegionFn(OperationName::VerifyRegionInvariantsFn &&verify) {
382     verifyRegionFn = std::move(verify);
383   }
384 
385   /// Sets the static hook for parsing this op assembly.
setParseFn(OperationName::ParseAssemblyFn && parse)386   void setParseFn(OperationName::ParseAssemblyFn &&parse) {
387     parseFn = std::move(parse);
388   }
389 
390   /// Sets the static hook for printing this op assembly.
setPrintFn(OperationName::PrintAssemblyFn && print)391   void setPrintFn(OperationName::PrintAssemblyFn &&print) {
392     printFn = std::move(print);
393   }
394 
395   /// Sets the hook implementing a generalized folder for the op. See
396   /// `RegisteredOperationName::foldHook` for more details
setFoldHookFn(OperationName::FoldHookFn && foldHook)397   void setFoldHookFn(OperationName::FoldHookFn &&foldHook) {
398     foldHookFn = std::move(foldHook);
399   }
400 
401   /// Set the hook returning any canonicalization pattern rewrites that the op
402   /// supports, for use by the canonicalization pass.
403   void
setGetCanonicalizationPatternsFn(OperationName::GetCanonicalizationPatternsFn && getCanonicalizationPatterns)404   setGetCanonicalizationPatternsFn(OperationName::GetCanonicalizationPatternsFn
405                                        &&getCanonicalizationPatterns) {
406     getCanonicalizationPatternsFn = std::move(getCanonicalizationPatterns);
407   }
408 
409   /// Set the hook populating default attributes.
setPopulateDefaultAttrsFn(OperationName::PopulateDefaultAttrsFn && populateDefaultAttrs)410   void setPopulateDefaultAttrsFn(
411       OperationName::PopulateDefaultAttrsFn &&populateDefaultAttrs) {
412     populateDefaultAttrsFn = std::move(populateDefaultAttrs);
413   }
414 
415 private:
416   DynamicOpDefinition(
417       StringRef name, ExtensibleDialect *dialect,
418       OperationName::VerifyInvariantsFn &&verifyFn,
419       OperationName::VerifyRegionInvariantsFn &&verifyRegionFn,
420       OperationName::ParseAssemblyFn &&parseFn,
421       OperationName::PrintAssemblyFn &&printFn,
422       OperationName::FoldHookFn &&foldHookFn,
423       OperationName::GetCanonicalizationPatternsFn
424           &&getCanonicalizationPatternsFn,
425       OperationName::PopulateDefaultAttrsFn &&populateDefaultAttrsFn);
426 
427   /// Unique identifier for this operation.
428   TypeID typeID;
429 
430   /// Name of the operation.
431   /// The name is prefixed with the dialect name.
432   std::string name;
433 
434   /// Dialect defining this operation.
435   ExtensibleDialect *dialect;
436 
437   OperationName::VerifyInvariantsFn verifyFn;
438   OperationName::VerifyRegionInvariantsFn verifyRegionFn;
439   OperationName::ParseAssemblyFn parseFn;
440   OperationName::PrintAssemblyFn printFn;
441   OperationName::FoldHookFn foldHookFn;
442   OperationName::GetCanonicalizationPatternsFn getCanonicalizationPatternsFn;
443   OperationName::PopulateDefaultAttrsFn populateDefaultAttrsFn;
444 
445   friend ExtensibleDialect;
446 };
447 
448 //===----------------------------------------------------------------------===//
449 // Extensible dialect
450 //===----------------------------------------------------------------------===//
451 
452 /// A dialect that can be extended with new operations/types/attributes at
453 /// runtime.
454 class ExtensibleDialect : public mlir::Dialect {
455 public:
456   ExtensibleDialect(StringRef name, MLIRContext *ctx, TypeID typeID);
457 
458   /// Add a new type defined at runtime to the dialect.
459   void registerDynamicType(std::unique_ptr<DynamicTypeDefinition> &&type);
460 
461   /// Add a new attribute defined at runtime to the dialect.
462   void registerDynamicAttr(std::unique_ptr<DynamicAttrDefinition> &&attr);
463 
464   /// Add a new operation defined at runtime to the dialect.
465   void registerDynamicOp(std::unique_ptr<DynamicOpDefinition> &&type);
466 
467   /// Check if the dialect is an extensible dialect.
468   static bool classof(const Dialect *dialect);
469 
470   /// Returns nullptr if the definition was not found.
lookupTypeDefinition(StringRef name)471   DynamicTypeDefinition *lookupTypeDefinition(StringRef name) const {
472     auto it = nameToDynTypes.find(name);
473     if (it == nameToDynTypes.end())
474       return nullptr;
475     return it->second;
476   }
477 
478   /// Returns nullptr if the definition was not found.
lookupTypeDefinition(TypeID id)479   DynamicTypeDefinition *lookupTypeDefinition(TypeID id) const {
480     auto it = dynTypes.find(id);
481     if (it == dynTypes.end())
482       return nullptr;
483     return it->second.get();
484   }
485 
486   /// Returns nullptr if the definition was not found.
lookupAttrDefinition(StringRef name)487   DynamicAttrDefinition *lookupAttrDefinition(StringRef name) const {
488     auto it = nameToDynAttrs.find(name);
489     if (it == nameToDynAttrs.end())
490       return nullptr;
491     return it->second;
492   }
493 
494   /// Returns nullptr if the definition was not found.
lookupAttrDefinition(TypeID id)495   DynamicAttrDefinition *lookupAttrDefinition(TypeID id) const {
496     auto it = dynAttrs.find(id);
497     if (it == dynAttrs.end())
498       return nullptr;
499     return it->second.get();
500   }
501 
502 protected:
503   /// Parse the dynamic type 'typeName' in the dialect 'dialect'.
504   /// typename should not be prefixed with the dialect name.
505   /// If the dynamic type does not exist, return no value.
506   /// Otherwise, parse it, and return the parse result.
507   /// If the parsing succeed, put the resulting type in 'resultType'.
508   OptionalParseResult parseOptionalDynamicType(StringRef typeName,
509                                                AsmParser &parser,
510                                                Type &resultType) const;
511 
512   /// If 'type' is a dynamic type, print it.
513   /// Returns success if the type was printed, and failure if the type was not a
514   /// dynamic type.
515   static LogicalResult printIfDynamicType(Type type, AsmPrinter &printer);
516 
517   /// Parse the dynamic attribute 'attrName' in the dialect 'dialect'.
518   /// attrname should not be prefixed with the dialect name.
519   /// If the dynamic attribute does not exist, return no value.
520   /// Otherwise, parse it, and return the parse result.
521   /// If the parsing succeed, put the resulting attribute in 'resultAttr'.
522   OptionalParseResult parseOptionalDynamicAttr(StringRef attrName,
523                                                AsmParser &parser,
524                                                Attribute &resultAttr) const;
525 
526   /// If 'attr' is a dynamic attribute, print it.
527   /// Returns success if the attribute was printed, and failure if the
528   /// attribute was not a dynamic attribute.
529   static LogicalResult printIfDynamicAttr(Attribute attr, AsmPrinter &printer);
530 
531 private:
532   /// The set of all dynamic types registered.
533   DenseMap<TypeID, std::unique_ptr<DynamicTypeDefinition>> dynTypes;
534 
535   /// This structure allows to get in O(1) a dynamic type given its name.
536   llvm::StringMap<DynamicTypeDefinition *> nameToDynTypes;
537 
538   /// The set of all dynamic attributes registered.
539   DenseMap<TypeID, std::unique_ptr<DynamicAttrDefinition>> dynAttrs;
540 
541   /// This structure allows to get in O(1) a dynamic attribute given its name.
542   llvm::StringMap<DynamicAttrDefinition *> nameToDynAttrs;
543 
544   /// Give DynamicOpDefinition access to allocateTypeID.
545   friend DynamicOpDefinition;
546 
547   /// Allocates a type ID to uniquify operations.
allocateTypeID()548   TypeID allocateTypeID() { return typeIDAllocator.allocate(); }
549 
550   /// Owns the TypeID generated at runtime for operations.
551   TypeIDAllocator typeIDAllocator;
552 };
553 } // namespace mlir
554 
555 namespace llvm {
556 /// Provide isa functionality for ExtensibleDialect.
557 /// This is to override the isa functionality for Dialect.
558 template <>
559 struct isa_impl<mlir::ExtensibleDialect, mlir::Dialect> {
560   static inline bool doit(const ::mlir::Dialect &dialect) {
561     return mlir::ExtensibleDialect::classof(&dialect);
562   }
563 };
564 } // namespace llvm
565 
566 #endif // MLIR_IR_EXTENSIBLEDIALECT_H
567