1 //===- Dialect.h - IR Dialect Description -----------------------*- 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 // This file defines the 'dialect' abstraction.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #ifndef MLIR_IR_DIALECT_H
14 #define MLIR_IR_DIALECT_H
15 
16 #include "mlir/IR/DialectRegistry.h"
17 #include "mlir/IR/OperationSupport.h"
18 #include "mlir/Support/TypeID.h"
19 
20 #include <map>
21 #include <tuple>
22 
23 namespace mlir {
24 class DialectAsmParser;
25 class DialectAsmPrinter;
26 class DialectInterface;
27 class OpBuilder;
28 class Type;
29 
30 //===----------------------------------------------------------------------===//
31 // Dialect
32 //===----------------------------------------------------------------------===//
33 
34 /// Dialects are groups of MLIR operations, types and attributes, as well as
35 /// behavior associated with the entire group.  For example, hooks into other
36 /// systems for constant folding, interfaces, default named types for asm
37 /// printing, etc.
38 ///
39 /// Instances of the dialect object are loaded in a specific MLIRContext.
40 ///
41 class Dialect {
42 public:
43   /// Type for a callback provided by the dialect to parse a custom operation.
44   /// This is used for the dialect to provide an alternative way to parse custom
45   /// operations, including unregistered ones.
46   using ParseOpHook =
47       function_ref<ParseResult(OpAsmParser &parser, OperationState &result)>;
48 
49   virtual ~Dialect();
50 
51   /// Utility function that returns if the given string is a valid dialect
52   /// namespace
53   static bool isValidNamespace(StringRef str);
54 
getContext()55   MLIRContext *getContext() const { return context; }
56 
getNamespace()57   StringRef getNamespace() const { return name; }
58 
59   /// Returns the unique identifier that corresponds to this dialect.
getTypeID()60   TypeID getTypeID() const { return dialectID; }
61 
62   /// Returns true if this dialect allows for unregistered operations, i.e.
63   /// operations prefixed with the dialect namespace but not registered with
64   /// addOperation.
allowsUnknownOperations()65   bool allowsUnknownOperations() const { return unknownOpsAllowed; }
66 
67   /// Return true if this dialect allows for unregistered types, i.e., types
68   /// prefixed with the dialect namespace but not registered with addType.
69   /// These are represented with OpaqueType.
allowsUnknownTypes()70   bool allowsUnknownTypes() const { return unknownTypesAllowed; }
71 
72   /// Register dialect-wide canonicalization patterns. This method should only
73   /// be used to register canonicalization patterns that do not conceptually
74   /// belong to any single operation in the dialect. (In that case, use the op's
75   /// canonicalizer.) E.g., canonicalization patterns for op interfaces should
76   /// be registered here.
getCanonicalizationPatterns(RewritePatternSet & results)77   virtual void getCanonicalizationPatterns(RewritePatternSet &results) const {}
78 
79   /// Registered hook to materialize a single constant operation from a given
80   /// attribute value with the desired resultant type. This method should use
81   /// the provided builder to create the operation without changing the
82   /// insertion position. The generated operation is expected to be constant
83   /// like, i.e. single result, zero operands, non side-effecting, etc. On
84   /// success, this hook should return the value generated to represent the
85   /// constant value. Otherwise, it should return null on failure.
materializeConstant(OpBuilder & builder,Attribute value,Type type,Location loc)86   virtual Operation *materializeConstant(OpBuilder &builder, Attribute value,
87                                          Type type, Location loc) {
88     return nullptr;
89   }
90 
91   //===--------------------------------------------------------------------===//
92   // Parsing Hooks
93   //===--------------------------------------------------------------------===//
94 
95   /// Parse an attribute registered to this dialect. If 'type' is nonnull, it
96   /// refers to the expected type of the attribute.
97   virtual Attribute parseAttribute(DialectAsmParser &parser, Type type) const;
98 
99   /// Print an attribute registered to this dialect. Note: The type of the
100   /// attribute need not be printed by this method as it is always printed by
101   /// the caller.
printAttribute(Attribute,DialectAsmPrinter &)102   virtual void printAttribute(Attribute, DialectAsmPrinter &) const {
103     llvm_unreachable("dialect has no registered attribute printing hook");
104   }
105 
106   /// Parse a type registered to this dialect.
107   virtual Type parseType(DialectAsmParser &parser) const;
108 
109   /// Print a type registered to this dialect.
printType(Type,DialectAsmPrinter &)110   virtual void printType(Type, DialectAsmPrinter &) const {
111     llvm_unreachable("dialect has no registered type printing hook");
112   }
113 
114   /// Return the hook to parse an operation registered to this dialect, if any.
115   /// By default this will lookup for registered operations and return the
116   /// `parse()` method registered on the RegisteredOperationName. Dialects can
117   /// override this behavior and handle unregistered operations as well.
118   virtual Optional<ParseOpHook> getParseOperationHook(StringRef opName) const;
119 
120   /// Print an operation registered to this dialect.
121   /// This hook is invoked for registered operation which don't override the
122   /// `print()` method to define their own custom assembly.
123   virtual llvm::unique_function<void(Operation *, OpAsmPrinter &printer)>
124   getOperationPrinter(Operation *op) const;
125 
126   //===--------------------------------------------------------------------===//
127   // Verification Hooks
128   //===--------------------------------------------------------------------===//
129 
130   /// Verify an attribute from this dialect on the argument at 'argIndex' for
131   /// the region at 'regionIndex' on the given operation. Returns failure if
132   /// the verification failed, success otherwise. This hook may optionally be
133   /// invoked from any operation containing a region.
134   virtual LogicalResult verifyRegionArgAttribute(Operation *,
135                                                  unsigned regionIndex,
136                                                  unsigned argIndex,
137                                                  NamedAttribute);
138 
139   /// Verify an attribute from this dialect on the result at 'resultIndex' for
140   /// the region at 'regionIndex' on the given operation. Returns failure if
141   /// the verification failed, success otherwise. This hook may optionally be
142   /// invoked from any operation containing a region.
143   virtual LogicalResult verifyRegionResultAttribute(Operation *,
144                                                     unsigned regionIndex,
145                                                     unsigned resultIndex,
146                                                     NamedAttribute);
147 
148   /// Verify an attribute from this dialect on the given operation. Returns
149   /// failure if the verification failed, success otherwise.
verifyOperationAttribute(Operation *,NamedAttribute)150   virtual LogicalResult verifyOperationAttribute(Operation *, NamedAttribute) {
151     return success();
152   }
153 
154   //===--------------------------------------------------------------------===//
155   // Interfaces
156   //===--------------------------------------------------------------------===//
157 
158   /// Lookup an interface for the given ID if one is registered, otherwise
159   /// nullptr.
getRegisteredInterface(TypeID interfaceID)160   const DialectInterface *getRegisteredInterface(TypeID interfaceID) {
161     auto it = registeredInterfaces.find(interfaceID);
162     return it != registeredInterfaces.end() ? it->getSecond().get() : nullptr;
163   }
164   template <typename InterfaceT>
getRegisteredInterface()165   const InterfaceT *getRegisteredInterface() {
166     return static_cast<const InterfaceT *>(
167         getRegisteredInterface(InterfaceT::getInterfaceID()));
168   }
169 
170   /// Lookup an op interface for the given ID if one is registered, otherwise
171   /// nullptr.
getRegisteredInterfaceForOp(TypeID interfaceID,OperationName opName)172   virtual void *getRegisteredInterfaceForOp(TypeID interfaceID,
173                                             OperationName opName) {
174     return nullptr;
175   }
176   template <typename InterfaceT>
177   typename InterfaceT::Concept *
getRegisteredInterfaceForOp(OperationName opName)178   getRegisteredInterfaceForOp(OperationName opName) {
179     return static_cast<typename InterfaceT::Concept *>(
180         getRegisteredInterfaceForOp(InterfaceT::getInterfaceID(), opName));
181   }
182 
183   /// Register a dialect interface with this dialect instance.
184   void addInterface(std::unique_ptr<DialectInterface> interface);
185 
186   /// Register a set of dialect interfaces with this dialect instance.
187   template <typename... Args>
addInterfaces()188   void addInterfaces() {
189     (void)std::initializer_list<int>{
190         0, (addInterface(std::make_unique<Args>(this)), 0)...};
191   }
192 
193 protected:
194   /// The constructor takes a unique namespace for this dialect as well as the
195   /// context to bind to.
196   /// Note: The namespace must not contain '.' characters.
197   /// Note: All operations belonging to this dialect must have names starting
198   ///       with the namespace followed by '.'.
199   /// Example:
200   ///       - "tf" for the TensorFlow ops like "tf.add".
201   Dialect(StringRef name, MLIRContext *context, TypeID id);
202 
203   /// This method is used by derived classes to add their operations to the set.
204   ///
205   template <typename... Args>
addOperations()206   void addOperations() {
207     (void)std::initializer_list<int>{
208         0, (RegisteredOperationName::insert<Args>(*this), 0)...};
209   }
210 
211   /// Register a set of type classes with this dialect.
212   template <typename... Args>
addTypes()213   void addTypes() {
214     (void)std::initializer_list<int>{0, (addType<Args>(), 0)...};
215   }
216 
217   /// Register a type instance with this dialect.
218   /// The use of this method is in general discouraged in favor of
219   /// 'addTypes<CustomType>()'.
220   void addType(TypeID typeID, AbstractType &&typeInfo);
221 
222   /// Register a set of attribute classes with this dialect.
223   template <typename... Args>
addAttributes()224   void addAttributes() {
225     (void)std::initializer_list<int>{0, (addAttribute<Args>(), 0)...};
226   }
227 
228   /// Register an attribute instance with this dialect.
229   /// The use of this method is in general discouraged in favor of
230   /// 'addAttributes<CustomAttr>()'.
231   void addAttribute(TypeID typeID, AbstractAttribute &&attrInfo);
232 
233   /// Enable support for unregistered operations.
234   void allowUnknownOperations(bool allow = true) { unknownOpsAllowed = allow; }
235 
236   /// Enable support for unregistered types.
237   void allowUnknownTypes(bool allow = true) { unknownTypesAllowed = allow; }
238 
239 private:
240   Dialect(const Dialect &) = delete;
241   void operator=(Dialect &) = delete;
242 
243   /// Register an attribute instance with this dialect.
244   template <typename T>
addAttribute()245   void addAttribute() {
246     // Add this attribute to the dialect and register it with the uniquer.
247     addAttribute(T::getTypeID(), AbstractAttribute::get<T>(*this));
248     detail::AttributeUniquer::registerAttribute<T>(context);
249   }
250 
251   /// Register a type instance with this dialect.
252   template <typename T>
addType()253   void addType() {
254     // Add this type to the dialect and register it with the uniquer.
255     addType(T::getTypeID(), AbstractType::get<T>(*this));
256     detail::TypeUniquer::registerType<T>(context);
257   }
258 
259   /// The namespace of this dialect.
260   StringRef name;
261 
262   /// The unique identifier of the derived Op class, this is used in the context
263   /// to allow registering multiple times the same dialect.
264   TypeID dialectID;
265 
266   /// This is the context that owns this Dialect object.
267   MLIRContext *context;
268 
269   /// Flag that specifies whether this dialect supports unregistered operations,
270   /// i.e. operations prefixed with the dialect namespace but not registered
271   /// with addOperation.
272   bool unknownOpsAllowed = false;
273 
274   /// Flag that specifies whether this dialect allows unregistered types, i.e.
275   /// types prefixed with the dialect namespace but not registered with addType.
276   /// These types are represented with OpaqueType.
277   bool unknownTypesAllowed = false;
278 
279   /// A collection of registered dialect interfaces.
280   DenseMap<TypeID, std::unique_ptr<DialectInterface>> registeredInterfaces;
281 
282   friend class DialectRegistry;
283   friend void registerDialect();
284   friend class MLIRContext;
285 };
286 
287 } // namespace mlir
288 
289 namespace llvm {
290 /// Provide isa functionality for Dialects.
291 template <typename T>
292 struct isa_impl<T, ::mlir::Dialect,
293                 std::enable_if_t<std::is_base_of<::mlir::Dialect, T>::value>> {
294   static inline bool doit(const ::mlir::Dialect &dialect) {
295     return mlir::TypeID::get<T>() == dialect.getTypeID();
296   }
297 };
298 template <typename T>
299 struct isa_impl<
300     T, ::mlir::Dialect,
301     std::enable_if_t<std::is_base_of<::mlir::DialectInterface, T>::value>> {
302   static inline bool doit(const ::mlir::Dialect &dialect) {
303     return const_cast<::mlir::Dialect &>(dialect).getRegisteredInterface<T>();
304   }
305 };
306 template <typename T>
307 struct cast_retty_impl<T, ::mlir::Dialect *> {
308   using ret_type =
309       std::conditional_t<std::is_base_of<::mlir::Dialect, T>::value, T *,
310                          const T *>;
311 };
312 template <typename T>
313 struct cast_retty_impl<T, ::mlir::Dialect> {
314   using ret_type =
315       std::conditional_t<std::is_base_of<::mlir::Dialect, T>::value, T &,
316                          const T &>;
317 };
318 
319 template <typename T>
320 struct cast_convert_val<T, ::mlir::Dialect, ::mlir::Dialect> {
321   template <typename To>
322   static std::enable_if_t<std::is_base_of<::mlir::Dialect, To>::value, To &>
323   doitImpl(::mlir::Dialect &dialect) {
324     return static_cast<To &>(dialect);
325   }
326   template <typename To>
327   static std::enable_if_t<std::is_base_of<::mlir::DialectInterface, To>::value,
328                           const To &>
329   doitImpl(::mlir::Dialect &dialect) {
330     return *dialect.getRegisteredInterface<To>();
331   }
332 
333   static auto &doit(::mlir::Dialect &dialect) { return doitImpl<T>(dialect); }
334 };
335 template <class T>
336 struct cast_convert_val<T, ::mlir::Dialect *, ::mlir::Dialect *> {
337   static auto doit(::mlir::Dialect *dialect) {
338     return &cast_convert_val<T, ::mlir::Dialect, ::mlir::Dialect>::doit(
339         *dialect);
340   }
341 };
342 
343 } // namespace llvm
344 
345 #endif
346