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