1 //===- OperationSupport.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 // This file defines a number of support types that Operation and related 10 // classes build on top of. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #ifndef MLIR_IR_OPERATIONSUPPORT_H 15 #define MLIR_IR_OPERATIONSUPPORT_H 16 17 #include "mlir/IR/Attributes.h" 18 #include "mlir/IR/BlockSupport.h" 19 #include "mlir/IR/BuiltinAttributes.h" 20 #include "mlir/IR/Diagnostics.h" 21 #include "mlir/IR/DialectRegistry.h" 22 #include "mlir/IR/Location.h" 23 #include "mlir/IR/TypeRange.h" 24 #include "mlir/IR/Types.h" 25 #include "mlir/IR/Value.h" 26 #include "mlir/Support/InterfaceSupport.h" 27 #include "llvm/ADT/BitmaskEnum.h" 28 #include "llvm/ADT/PointerUnion.h" 29 #include "llvm/ADT/STLFunctionalExtras.h" 30 #include "llvm/Support/ErrorHandling.h" 31 #include "llvm/Support/PointerLikeTypeTraits.h" 32 #include "llvm/Support/TrailingObjects.h" 33 #include <memory> 34 #include <optional> 35 36 namespace llvm { 37 class BitVector; 38 } // namespace llvm 39 40 namespace mlir { 41 class Dialect; 42 class DictionaryAttr; 43 class ElementsAttr; 44 struct EmptyProperties; 45 class MutableOperandRangeRange; 46 class NamedAttrList; 47 class Operation; 48 struct OperationState; 49 class OpAsmParser; 50 class OpAsmPrinter; 51 class OperandRange; 52 class OperandRangeRange; 53 class OpFoldResult; 54 class Pattern; 55 class Region; 56 class ResultRange; 57 class RewritePattern; 58 class RewritePatternSet; 59 class Type; 60 class Value; 61 class ValueRange; 62 template <typename ValueRangeT> 63 class ValueTypeRange; 64 65 //===----------------------------------------------------------------------===// 66 // OpaqueProperties 67 //===----------------------------------------------------------------------===// 68 69 /// Simple wrapper around a void* in order to express generically how to pass 70 /// in op properties through APIs. 71 class OpaqueProperties { 72 public: 73 OpaqueProperties(void *prop) : properties(prop) {} 74 operator bool() const { return properties != nullptr; } 75 template <typename Dest> 76 Dest as() const { 77 return static_cast<Dest>(const_cast<void *>(properties)); 78 } 79 80 private: 81 void *properties; 82 }; 83 84 //===----------------------------------------------------------------------===// 85 // OperationName 86 //===----------------------------------------------------------------------===// 87 88 class OperationName { 89 public: 90 using FoldHookFn = llvm::unique_function<LogicalResult( 91 Operation *, ArrayRef<Attribute>, SmallVectorImpl<OpFoldResult> &) const>; 92 using HasTraitFn = llvm::unique_function<bool(TypeID) const>; 93 using ParseAssemblyFn = 94 llvm::unique_function<ParseResult(OpAsmParser &, OperationState &)>; 95 // Note: RegisteredOperationName is passed as reference here as the derived 96 // class is defined below. 97 using PopulateDefaultAttrsFn = 98 llvm::unique_function<void(const OperationName &, NamedAttrList &) const>; 99 using PrintAssemblyFn = 100 llvm::unique_function<void(Operation *, OpAsmPrinter &, StringRef) const>; 101 using VerifyInvariantsFn = 102 llvm::unique_function<LogicalResult(Operation *) const>; 103 using VerifyRegionInvariantsFn = 104 llvm::unique_function<LogicalResult(Operation *) const>; 105 106 /// This class represents a type erased version of an operation. It contains 107 /// all of the components necessary for opaquely interacting with an 108 /// operation. If the operation is not registered, some of these components 109 /// may not be populated. 110 struct InterfaceConcept { 111 virtual ~InterfaceConcept() = default; 112 virtual LogicalResult foldHook(Operation *, ArrayRef<Attribute>, 113 SmallVectorImpl<OpFoldResult> &) = 0; 114 virtual void getCanonicalizationPatterns(RewritePatternSet &, 115 MLIRContext *) = 0; 116 virtual bool hasTrait(TypeID) = 0; 117 virtual OperationName::ParseAssemblyFn getParseAssemblyFn() = 0; 118 virtual void populateDefaultAttrs(const OperationName &, 119 NamedAttrList &) = 0; 120 virtual void printAssembly(Operation *, OpAsmPrinter &, StringRef) = 0; 121 virtual LogicalResult verifyInvariants(Operation *) = 0; 122 virtual LogicalResult verifyRegionInvariants(Operation *) = 0; 123 /// Implementation for properties 124 virtual std::optional<Attribute> getInherentAttr(Operation *, 125 StringRef name) = 0; 126 virtual void setInherentAttr(Operation *op, StringAttr name, 127 Attribute value) = 0; 128 virtual void populateInherentAttrs(Operation *op, NamedAttrList &attrs) = 0; 129 virtual LogicalResult 130 verifyInherentAttrs(OperationName opName, NamedAttrList &attributes, 131 function_ref<InFlightDiagnostic()> emitError) = 0; 132 virtual int getOpPropertyByteSize() = 0; 133 virtual void initProperties(OperationName opName, OpaqueProperties storage, 134 OpaqueProperties init) = 0; 135 virtual void deleteProperties(OpaqueProperties) = 0; 136 virtual void populateDefaultProperties(OperationName opName, 137 OpaqueProperties properties) = 0; 138 virtual LogicalResult 139 setPropertiesFromAttr(OperationName, OpaqueProperties, Attribute, 140 function_ref<InFlightDiagnostic()> emitError) = 0; 141 virtual Attribute getPropertiesAsAttr(Operation *) = 0; 142 virtual void copyProperties(OpaqueProperties, OpaqueProperties) = 0; 143 virtual bool compareProperties(OpaqueProperties, OpaqueProperties) = 0; 144 virtual llvm::hash_code hashProperties(OpaqueProperties) = 0; 145 }; 146 147 public: 148 class Impl : public InterfaceConcept { 149 public: 150 Impl(StringRef, Dialect *dialect, TypeID typeID, 151 detail::InterfaceMap interfaceMap); 152 Impl(StringAttr name, Dialect *dialect, TypeID typeID, 153 detail::InterfaceMap interfaceMap) 154 : name(name), typeID(typeID), dialect(dialect), 155 interfaceMap(std::move(interfaceMap)) {} 156 157 /// Returns true if this is a registered operation. 158 bool isRegistered() const { return typeID != TypeID::get<void>(); } 159 detail::InterfaceMap &getInterfaceMap() { return interfaceMap; } 160 Dialect *getDialect() const { return dialect; } 161 StringAttr getName() const { return name; } 162 TypeID getTypeID() const { return typeID; } 163 ArrayRef<StringAttr> getAttributeNames() const { return attributeNames; } 164 165 protected: 166 //===------------------------------------------------------------------===// 167 // Registered Operation Info 168 169 /// The name of the operation. 170 StringAttr name; 171 172 /// The unique identifier of the derived Op class. 173 TypeID typeID; 174 175 /// The following fields are only populated when the operation is 176 /// registered. 177 178 /// This is the dialect that this operation belongs to. 179 Dialect *dialect; 180 181 /// A map of interfaces that were registered to this operation. 182 detail::InterfaceMap interfaceMap; 183 184 /// A list of attribute names registered to this operation in StringAttr 185 /// form. This allows for operation classes to use StringAttr for attribute 186 /// lookup/creation/etc., as opposed to raw strings. 187 ArrayRef<StringAttr> attributeNames; 188 189 friend class RegisteredOperationName; 190 }; 191 192 protected: 193 /// Default implementation for unregistered operations. 194 struct UnregisteredOpModel : public Impl { 195 using Impl::Impl; 196 LogicalResult foldHook(Operation *, ArrayRef<Attribute>, 197 SmallVectorImpl<OpFoldResult> &) final; 198 void getCanonicalizationPatterns(RewritePatternSet &, MLIRContext *) final; 199 bool hasTrait(TypeID) final; 200 OperationName::ParseAssemblyFn getParseAssemblyFn() final; 201 void populateDefaultAttrs(const OperationName &, NamedAttrList &) final; 202 void printAssembly(Operation *, OpAsmPrinter &, StringRef) final; 203 LogicalResult verifyInvariants(Operation *) final; 204 LogicalResult verifyRegionInvariants(Operation *) final; 205 /// Implementation for properties 206 std::optional<Attribute> getInherentAttr(Operation *op, 207 StringRef name) final; 208 void setInherentAttr(Operation *op, StringAttr name, Attribute value) final; 209 void populateInherentAttrs(Operation *op, NamedAttrList &attrs) final; 210 LogicalResult 211 verifyInherentAttrs(OperationName opName, NamedAttrList &attributes, 212 function_ref<InFlightDiagnostic()> emitError) final; 213 int getOpPropertyByteSize() final; 214 void initProperties(OperationName opName, OpaqueProperties storage, 215 OpaqueProperties init) final; 216 void deleteProperties(OpaqueProperties) final; 217 void populateDefaultProperties(OperationName opName, 218 OpaqueProperties properties) final; 219 LogicalResult 220 setPropertiesFromAttr(OperationName, OpaqueProperties, Attribute, 221 function_ref<InFlightDiagnostic()> emitError) final; 222 Attribute getPropertiesAsAttr(Operation *) final; 223 void copyProperties(OpaqueProperties, OpaqueProperties) final; 224 bool compareProperties(OpaqueProperties, OpaqueProperties) final; 225 llvm::hash_code hashProperties(OpaqueProperties) final; 226 }; 227 228 public: 229 OperationName(StringRef name, MLIRContext *context); 230 231 /// Return if this operation is registered. 232 bool isRegistered() const { return getImpl()->isRegistered(); } 233 234 /// Return the unique identifier of the derived Op class, or null if not 235 /// registered. 236 TypeID getTypeID() const { return getImpl()->getTypeID(); } 237 238 /// If this operation is registered, returns the registered information, 239 /// std::nullopt otherwise. 240 std::optional<RegisteredOperationName> getRegisteredInfo() const; 241 242 /// This hook implements a generalized folder for this operation. Operations 243 /// can implement this to provide simplifications rules that are applied by 244 /// the Builder::createOrFold API and the canonicalization pass. 245 /// 246 /// This is an intentionally limited interface - implementations of this 247 /// hook can only perform the following changes to the operation: 248 /// 249 /// 1. They can leave the operation alone and without changing the IR, and 250 /// return failure. 251 /// 2. They can mutate the operation in place, without changing anything 252 /// else in the IR. In this case, return success. 253 /// 3. They can return a list of existing values that can be used instead 254 /// of the operation. In this case, fill in the results list and return 255 /// success. The caller will remove the operation and use those results 256 /// instead. 257 /// 258 /// This allows expression of some simple in-place canonicalizations (e.g. 259 /// "x+0 -> x", "min(x,y,x,z) -> min(x,y,z)", "x+y-x -> y", etc), as well as 260 /// generalized constant folding. 261 LogicalResult foldHook(Operation *op, ArrayRef<Attribute> operands, 262 SmallVectorImpl<OpFoldResult> &results) const { 263 return getImpl()->foldHook(op, operands, results); 264 } 265 266 /// This hook returns any canonicalization pattern rewrites that the 267 /// operation supports, for use by the canonicalization pass. 268 void getCanonicalizationPatterns(RewritePatternSet &results, 269 MLIRContext *context) const { 270 return getImpl()->getCanonicalizationPatterns(results, context); 271 } 272 273 /// Returns true if the operation was registered with a particular trait, e.g. 274 /// hasTrait<OperandsAreSignlessIntegerLike>(). Returns false if the operation 275 /// is unregistered. 276 template <template <typename T> class Trait> 277 bool hasTrait() const { 278 return hasTrait(TypeID::get<Trait>()); 279 } 280 bool hasTrait(TypeID traitID) const { return getImpl()->hasTrait(traitID); } 281 282 /// Returns true if the operation *might* have the provided trait. This 283 /// means that either the operation is unregistered, or it was registered with 284 /// the provide trait. 285 template <template <typename T> class Trait> 286 bool mightHaveTrait() const { 287 return mightHaveTrait(TypeID::get<Trait>()); 288 } 289 bool mightHaveTrait(TypeID traitID) const { 290 return !isRegistered() || getImpl()->hasTrait(traitID); 291 } 292 293 /// Return the static hook for parsing this operation assembly. 294 ParseAssemblyFn getParseAssemblyFn() const { 295 return getImpl()->getParseAssemblyFn(); 296 } 297 298 /// This hook implements the method to populate defaults attributes that are 299 /// unset. 300 void populateDefaultAttrs(NamedAttrList &attrs) const { 301 getImpl()->populateDefaultAttrs(*this, attrs); 302 } 303 304 /// This hook implements the AsmPrinter for this operation. 305 void printAssembly(Operation *op, OpAsmPrinter &p, 306 StringRef defaultDialect) const { 307 return getImpl()->printAssembly(op, p, defaultDialect); 308 } 309 310 /// These hooks implement the verifiers for this operation. It should emits 311 /// an error message and returns failure if a problem is detected, or 312 /// returns success if everything is ok. 313 LogicalResult verifyInvariants(Operation *op) const { 314 return getImpl()->verifyInvariants(op); 315 } 316 LogicalResult verifyRegionInvariants(Operation *op) const { 317 return getImpl()->verifyRegionInvariants(op); 318 } 319 320 /// Return the list of cached attribute names registered to this operation. 321 /// The order of attributes cached here is unique to each type of operation, 322 /// and the interpretation of this attribute list should generally be driven 323 /// by the respective operation. In many cases, this caching removes the 324 /// need to use the raw string name of a known attribute. 325 /// 326 /// For example the ODS generator, with an op defining the following 327 /// attributes: 328 /// 329 /// let arguments = (ins I32Attr:$attr1, I32Attr:$attr2); 330 /// 331 /// ... may produce an order here of ["attr1", "attr2"]. This allows for the 332 /// ODS generator to directly access the cached name for a known attribute, 333 /// greatly simplifying the cost and complexity of attribute usage produced 334 /// by the generator. 335 /// 336 ArrayRef<StringAttr> getAttributeNames() const { 337 return getImpl()->getAttributeNames(); 338 } 339 340 /// Returns an instance of the concept object for the given interface if it 341 /// was registered to this operation, null otherwise. This should not be used 342 /// directly. 343 template <typename T> 344 typename T::Concept *getInterface() const { 345 return getImpl()->getInterfaceMap().lookup<T>(); 346 } 347 348 /// Attach the given models as implementations of the corresponding 349 /// interfaces for the concrete operation. 350 template <typename... Models> 351 void attachInterface() { 352 // Handle the case where the models resolve a promised interface. 353 (dialect_extension_detail::handleAdditionOfUndefinedPromisedInterface( 354 *getDialect(), getTypeID(), Models::Interface::getInterfaceID()), 355 ...); 356 357 getImpl()->getInterfaceMap().insertModels<Models...>(); 358 } 359 360 /// Returns true if `InterfaceT` has been promised by the dialect or 361 /// implemented. 362 template <typename InterfaceT> 363 bool hasPromiseOrImplementsInterface() const { 364 return dialect_extension_detail::hasPromisedInterface( 365 getDialect(), getTypeID(), InterfaceT::getInterfaceID()) || 366 hasInterface<InterfaceT>(); 367 } 368 369 /// Returns true if this operation has the given interface registered to it. 370 template <typename T> 371 bool hasInterface() const { 372 return hasInterface(TypeID::get<T>()); 373 } 374 bool hasInterface(TypeID interfaceID) const { 375 return getImpl()->getInterfaceMap().contains(interfaceID); 376 } 377 378 /// Returns true if the operation *might* have the provided interface. This 379 /// means that either the operation is unregistered, or it was registered with 380 /// the provide interface. 381 template <typename T> 382 bool mightHaveInterface() const { 383 return mightHaveInterface(TypeID::get<T>()); 384 } 385 bool mightHaveInterface(TypeID interfaceID) const { 386 return !isRegistered() || hasInterface(interfaceID); 387 } 388 389 /// Lookup an inherent attribute by name, this method isn't recommended 390 /// and may be removed in the future. 391 std::optional<Attribute> getInherentAttr(Operation *op, 392 StringRef name) const { 393 return getImpl()->getInherentAttr(op, name); 394 } 395 396 void setInherentAttr(Operation *op, StringAttr name, Attribute value) const { 397 return getImpl()->setInherentAttr(op, name, value); 398 } 399 400 void populateInherentAttrs(Operation *op, NamedAttrList &attrs) const { 401 return getImpl()->populateInherentAttrs(op, attrs); 402 } 403 /// This method exists for backward compatibility purpose when using 404 /// properties to store inherent attributes, it enables validating the 405 /// attributes when parsed from the older generic syntax pre-Properties. 406 LogicalResult 407 verifyInherentAttrs(NamedAttrList &attributes, 408 function_ref<InFlightDiagnostic()> emitError) const { 409 return getImpl()->verifyInherentAttrs(*this, attributes, emitError); 410 } 411 /// This hooks return the number of bytes to allocate for the op properties. 412 int getOpPropertyByteSize() const { 413 return getImpl()->getOpPropertyByteSize(); 414 } 415 416 /// This hooks destroy the op properties. 417 void destroyOpProperties(OpaqueProperties properties) const { 418 getImpl()->deleteProperties(properties); 419 } 420 421 /// Initialize the op properties. 422 void initOpProperties(OpaqueProperties storage, OpaqueProperties init) const { 423 getImpl()->initProperties(*this, storage, init); 424 } 425 426 /// Set the default values on the ODS attribute in the properties. 427 void populateDefaultProperties(OpaqueProperties properties) const { 428 getImpl()->populateDefaultProperties(*this, properties); 429 } 430 431 /// Return the op properties converted to an Attribute. 432 Attribute getOpPropertiesAsAttribute(Operation *op) const { 433 return getImpl()->getPropertiesAsAttr(op); 434 } 435 436 /// Define the op properties from the provided Attribute. 437 LogicalResult setOpPropertiesFromAttribute( 438 OperationName opName, OpaqueProperties properties, Attribute attr, 439 function_ref<InFlightDiagnostic()> emitError) const { 440 return getImpl()->setPropertiesFromAttr(opName, properties, attr, 441 emitError); 442 } 443 444 void copyOpProperties(OpaqueProperties lhs, OpaqueProperties rhs) const { 445 return getImpl()->copyProperties(lhs, rhs); 446 } 447 448 bool compareOpProperties(OpaqueProperties lhs, OpaqueProperties rhs) const { 449 return getImpl()->compareProperties(lhs, rhs); 450 } 451 452 llvm::hash_code hashOpProperties(OpaqueProperties properties) const { 453 return getImpl()->hashProperties(properties); 454 } 455 456 /// Return the dialect this operation is registered to if the dialect is 457 /// loaded in the context, or nullptr if the dialect isn't loaded. 458 Dialect *getDialect() const { 459 return isRegistered() ? getImpl()->getDialect() 460 : getImpl()->getName().getReferencedDialect(); 461 } 462 463 /// Return the name of the dialect this operation is registered to. 464 StringRef getDialectNamespace() const; 465 466 /// Return the operation name with dialect name stripped, if it has one. 467 StringRef stripDialect() const { return getStringRef().split('.').second; } 468 469 /// Return the context this operation is associated with. 470 MLIRContext *getContext() { return getIdentifier().getContext(); } 471 472 /// Return the name of this operation. This always succeeds. 473 StringRef getStringRef() const { return getIdentifier(); } 474 475 /// Return the name of this operation as a StringAttr. 476 StringAttr getIdentifier() const { return getImpl()->getName(); } 477 478 void print(raw_ostream &os) const; 479 void dump() const; 480 481 /// Represent the operation name as an opaque pointer. (Used to support 482 /// PointerLikeTypeTraits). 483 void *getAsOpaquePointer() const { return const_cast<Impl *>(impl); } 484 static OperationName getFromOpaquePointer(const void *pointer) { 485 return OperationName( 486 const_cast<Impl *>(reinterpret_cast<const Impl *>(pointer))); 487 } 488 489 bool operator==(const OperationName &rhs) const { return impl == rhs.impl; } 490 bool operator!=(const OperationName &rhs) const { return !(*this == rhs); } 491 492 protected: 493 OperationName(Impl *impl) : impl(impl) {} 494 Impl *getImpl() const { return impl; } 495 void setImpl(Impl *rhs) { impl = rhs; } 496 497 private: 498 /// The internal implementation of the operation name. 499 Impl *impl = nullptr; 500 501 /// Allow access to the Impl struct. 502 friend MLIRContextImpl; 503 friend DenseMapInfo<mlir::OperationName>; 504 friend DenseMapInfo<mlir::RegisteredOperationName>; 505 }; 506 507 inline raw_ostream &operator<<(raw_ostream &os, OperationName info) { 508 info.print(os); 509 return os; 510 } 511 512 // Make operation names hashable. 513 inline llvm::hash_code hash_value(OperationName arg) { 514 return llvm::hash_value(arg.getAsOpaquePointer()); 515 } 516 517 //===----------------------------------------------------------------------===// 518 // RegisteredOperationName 519 //===----------------------------------------------------------------------===// 520 521 /// This is a "type erased" representation of a registered operation. This 522 /// should only be used by things like the AsmPrinter and other things that need 523 /// to be parameterized by generic operation hooks. Most user code should use 524 /// the concrete operation types. 525 class RegisteredOperationName : public OperationName { 526 public: 527 /// Implementation of the InterfaceConcept for operation APIs that forwarded 528 /// to a concrete op implementation. 529 template <typename ConcreteOp> 530 struct Model : public Impl { 531 Model(Dialect *dialect) 532 : Impl(ConcreteOp::getOperationName(), dialect, 533 TypeID::get<ConcreteOp>(), ConcreteOp::getInterfaceMap()) {} 534 LogicalResult foldHook(Operation *op, ArrayRef<Attribute> attrs, 535 SmallVectorImpl<OpFoldResult> &results) final { 536 return ConcreteOp::getFoldHookFn()(op, attrs, results); 537 } 538 void getCanonicalizationPatterns(RewritePatternSet &set, 539 MLIRContext *context) final { 540 ConcreteOp::getCanonicalizationPatterns(set, context); 541 } 542 bool hasTrait(TypeID id) final { return ConcreteOp::getHasTraitFn()(id); } 543 OperationName::ParseAssemblyFn getParseAssemblyFn() final { 544 return ConcreteOp::parse; 545 } 546 void populateDefaultAttrs(const OperationName &name, 547 NamedAttrList &attrs) final { 548 ConcreteOp::populateDefaultAttrs(name, attrs); 549 } 550 void printAssembly(Operation *op, OpAsmPrinter &printer, 551 StringRef name) final { 552 ConcreteOp::getPrintAssemblyFn()(op, printer, name); 553 } 554 LogicalResult verifyInvariants(Operation *op) final { 555 return ConcreteOp::getVerifyInvariantsFn()(op); 556 } 557 LogicalResult verifyRegionInvariants(Operation *op) final { 558 return ConcreteOp::getVerifyRegionInvariantsFn()(op); 559 } 560 561 /// Implementation for "Properties" 562 563 using Properties = std::remove_reference_t< 564 decltype(std::declval<ConcreteOp>().getProperties())>; 565 566 std::optional<Attribute> getInherentAttr(Operation *op, 567 StringRef name) final { 568 if constexpr (hasProperties) { 569 auto concreteOp = cast<ConcreteOp>(op); 570 return ConcreteOp::getInherentAttr(concreteOp->getContext(), 571 concreteOp.getProperties(), name); 572 } 573 // If the op does not have support for properties, we dispatch back to the 574 // dictionnary of discardable attributes for now. 575 return cast<ConcreteOp>(op)->getDiscardableAttr(name); 576 } 577 void setInherentAttr(Operation *op, StringAttr name, 578 Attribute value) final { 579 if constexpr (hasProperties) { 580 auto concreteOp = cast<ConcreteOp>(op); 581 return ConcreteOp::setInherentAttr(concreteOp.getProperties(), name, 582 value); 583 } 584 // If the op does not have support for properties, we dispatch back to the 585 // dictionnary of discardable attributes for now. 586 return cast<ConcreteOp>(op)->setDiscardableAttr(name, value); 587 } 588 void populateInherentAttrs(Operation *op, NamedAttrList &attrs) final { 589 if constexpr (hasProperties) { 590 auto concreteOp = cast<ConcreteOp>(op); 591 ConcreteOp::populateInherentAttrs(concreteOp->getContext(), 592 concreteOp.getProperties(), attrs); 593 } 594 } 595 LogicalResult 596 verifyInherentAttrs(OperationName opName, NamedAttrList &attributes, 597 function_ref<InFlightDiagnostic()> emitError) final { 598 if constexpr (hasProperties) 599 return ConcreteOp::verifyInherentAttrs(opName, attributes, emitError); 600 return success(); 601 } 602 // Detect if the concrete operation defined properties. 603 static constexpr bool hasProperties = !std::is_same_v< 604 typename ConcreteOp::template InferredProperties<ConcreteOp>, 605 EmptyProperties>; 606 607 int getOpPropertyByteSize() final { 608 if constexpr (hasProperties) 609 return sizeof(Properties); 610 return 0; 611 } 612 void initProperties(OperationName opName, OpaqueProperties storage, 613 OpaqueProperties init) final { 614 using Properties = 615 typename ConcreteOp::template InferredProperties<ConcreteOp>; 616 if (init) 617 new (storage.as<Properties *>()) Properties(*init.as<Properties *>()); 618 else 619 new (storage.as<Properties *>()) Properties(); 620 if constexpr (hasProperties) 621 ConcreteOp::populateDefaultProperties(opName, 622 *storage.as<Properties *>()); 623 } 624 void deleteProperties(OpaqueProperties prop) final { 625 prop.as<Properties *>()->~Properties(); 626 } 627 void populateDefaultProperties(OperationName opName, 628 OpaqueProperties properties) final { 629 if constexpr (hasProperties) 630 ConcreteOp::populateDefaultProperties(opName, 631 *properties.as<Properties *>()); 632 } 633 634 LogicalResult 635 setPropertiesFromAttr(OperationName opName, OpaqueProperties properties, 636 Attribute attr, 637 function_ref<InFlightDiagnostic()> emitError) final { 638 if constexpr (hasProperties) { 639 auto p = properties.as<Properties *>(); 640 return ConcreteOp::setPropertiesFromAttr(*p, attr, emitError); 641 } 642 emitError() << "this operation does not support properties"; 643 return failure(); 644 } 645 Attribute getPropertiesAsAttr(Operation *op) final { 646 if constexpr (hasProperties) { 647 auto concreteOp = cast<ConcreteOp>(op); 648 return ConcreteOp::getPropertiesAsAttr(concreteOp->getContext(), 649 concreteOp.getProperties()); 650 } 651 return {}; 652 } 653 bool compareProperties(OpaqueProperties lhs, OpaqueProperties rhs) final { 654 if constexpr (hasProperties) { 655 return *lhs.as<Properties *>() == *rhs.as<Properties *>(); 656 } else { 657 return true; 658 } 659 } 660 void copyProperties(OpaqueProperties lhs, OpaqueProperties rhs) final { 661 *lhs.as<Properties *>() = *rhs.as<Properties *>(); 662 } 663 llvm::hash_code hashProperties(OpaqueProperties prop) final { 664 if constexpr (hasProperties) 665 return ConcreteOp::computePropertiesHash(*prop.as<Properties *>()); 666 667 return {}; 668 } 669 }; 670 671 /// Lookup the registered operation information for the given operation. 672 /// Returns std::nullopt if the operation isn't registered. 673 static std::optional<RegisteredOperationName> lookup(StringRef name, 674 MLIRContext *ctx); 675 676 /// Lookup the registered operation information for the given operation. 677 /// Returns std::nullopt if the operation isn't registered. 678 static std::optional<RegisteredOperationName> lookup(TypeID typeID, 679 MLIRContext *ctx); 680 681 /// Register a new operation in a Dialect object. 682 /// This constructor is used by Dialect objects when they register the list 683 /// of operations they contain. 684 template <typename T> 685 static void insert(Dialect &dialect) { 686 insert(std::make_unique<Model<T>>(&dialect), T::getAttributeNames()); 687 } 688 /// The use of this method is in general discouraged in favor of 689 /// 'insert<CustomOp>(dialect)'. 690 static void insert(std::unique_ptr<OperationName::Impl> ownedImpl, 691 ArrayRef<StringRef> attrNames); 692 693 /// Return the dialect this operation is registered to. 694 Dialect &getDialect() const { return *getImpl()->getDialect(); } 695 696 /// Represent the operation name as an opaque pointer. (Used to support 697 /// PointerLikeTypeTraits). 698 static RegisteredOperationName getFromOpaquePointer(const void *pointer) { 699 return RegisteredOperationName( 700 const_cast<Impl *>(reinterpret_cast<const Impl *>(pointer))); 701 } 702 703 private: 704 RegisteredOperationName(Impl *impl) : OperationName(impl) {} 705 706 /// Allow access to the constructor. 707 friend OperationName; 708 }; 709 710 inline std::optional<RegisteredOperationName> 711 OperationName::getRegisteredInfo() const { 712 return isRegistered() ? RegisteredOperationName(impl) 713 : std::optional<RegisteredOperationName>(); 714 } 715 716 //===----------------------------------------------------------------------===// 717 // Attribute Dictionary-Like Interface 718 //===----------------------------------------------------------------------===// 719 720 /// Attribute collections provide a dictionary-like interface. Define common 721 /// lookup functions. 722 namespace impl { 723 724 /// Unsorted string search or identifier lookups are linear scans. 725 template <typename IteratorT, typename NameT> 726 std::pair<IteratorT, bool> findAttrUnsorted(IteratorT first, IteratorT last, 727 NameT name) { 728 for (auto it = first; it != last; ++it) 729 if (it->getName() == name) 730 return {it, true}; 731 return {last, false}; 732 } 733 734 /// Using llvm::lower_bound requires an extra string comparison to check whether 735 /// the returned iterator points to the found element or whether it indicates 736 /// the lower bound. Skip this redundant comparison by checking if `compare == 737 /// 0` during the binary search. 738 template <typename IteratorT> 739 std::pair<IteratorT, bool> findAttrSorted(IteratorT first, IteratorT last, 740 StringRef name) { 741 ptrdiff_t length = std::distance(first, last); 742 743 while (length > 0) { 744 ptrdiff_t half = length / 2; 745 IteratorT mid = first + half; 746 int compare = mid->getName().strref().compare(name); 747 if (compare < 0) { 748 first = mid + 1; 749 length = length - half - 1; 750 } else if (compare > 0) { 751 length = half; 752 } else { 753 return {mid, true}; 754 } 755 } 756 return {first, false}; 757 } 758 759 /// StringAttr lookups on large attribute lists will switch to string binary 760 /// search. String binary searches become significantly faster than linear scans 761 /// with the identifier when the attribute list becomes very large. 762 template <typename IteratorT> 763 std::pair<IteratorT, bool> findAttrSorted(IteratorT first, IteratorT last, 764 StringAttr name) { 765 constexpr unsigned kSmallAttributeList = 16; 766 if (std::distance(first, last) > kSmallAttributeList) 767 return findAttrSorted(first, last, name.strref()); 768 return findAttrUnsorted(first, last, name); 769 } 770 771 /// Get an attribute from a sorted range of named attributes. Returns null if 772 /// the attribute was not found. 773 template <typename IteratorT, typename NameT> 774 Attribute getAttrFromSortedRange(IteratorT first, IteratorT last, NameT name) { 775 std::pair<IteratorT, bool> result = findAttrSorted(first, last, name); 776 return result.second ? result.first->getValue() : Attribute(); 777 } 778 779 /// Get an attribute from a sorted range of named attributes. Returns 780 /// std::nullopt if the attribute was not found. 781 template <typename IteratorT, typename NameT> 782 std::optional<NamedAttribute> 783 getNamedAttrFromSortedRange(IteratorT first, IteratorT last, NameT name) { 784 std::pair<IteratorT, bool> result = findAttrSorted(first, last, name); 785 return result.second ? *result.first : std::optional<NamedAttribute>(); 786 } 787 788 } // namespace impl 789 790 //===----------------------------------------------------------------------===// 791 // NamedAttrList 792 //===----------------------------------------------------------------------===// 793 794 /// NamedAttrList is array of NamedAttributes that tracks whether it is sorted 795 /// and does some basic work to remain sorted. 796 class NamedAttrList { 797 public: 798 using iterator = SmallVectorImpl<NamedAttribute>::iterator; 799 using const_iterator = SmallVectorImpl<NamedAttribute>::const_iterator; 800 using reference = NamedAttribute &; 801 using const_reference = const NamedAttribute &; 802 using size_type = size_t; 803 804 NamedAttrList() : dictionarySorted({}, true) {} 805 NamedAttrList(std::nullopt_t none) : NamedAttrList() {} 806 NamedAttrList(ArrayRef<NamedAttribute> attributes); 807 NamedAttrList(DictionaryAttr attributes); 808 NamedAttrList(const_iterator inStart, const_iterator inEnd); 809 810 template <typename Container> 811 NamedAttrList(const Container &vec) 812 : NamedAttrList(ArrayRef<NamedAttribute>(vec)) {} 813 814 bool operator!=(const NamedAttrList &other) const { 815 return !(*this == other); 816 } 817 bool operator==(const NamedAttrList &other) const { 818 return attrs == other.attrs; 819 } 820 821 /// Add an attribute with the specified name. 822 void append(StringRef name, Attribute attr) { 823 append(NamedAttribute(name, attr)); 824 } 825 826 /// Add an attribute with the specified name. 827 void append(StringAttr name, Attribute attr) { 828 append(NamedAttribute(name, attr)); 829 } 830 831 /// Append the given named attribute. 832 void append(NamedAttribute attr) { push_back(attr); } 833 834 /// Add an array of named attributes. 835 template <typename RangeT> 836 void append(RangeT &&newAttributes) { 837 append(std::begin(newAttributes), std::end(newAttributes)); 838 } 839 840 /// Add a range of named attributes. 841 template <typename IteratorT, 842 typename = std::enable_if_t<std::is_convertible< 843 typename std::iterator_traits<IteratorT>::iterator_category, 844 std::input_iterator_tag>::value>> 845 void append(IteratorT inStart, IteratorT inEnd) { 846 // TODO: expand to handle case where values appended are in order & after 847 // end of current list. 848 dictionarySorted.setPointerAndInt(nullptr, false); 849 attrs.append(inStart, inEnd); 850 } 851 852 /// Replaces the attributes with new list of attributes. 853 void assign(const_iterator inStart, const_iterator inEnd); 854 855 /// Replaces the attributes with new list of attributes. 856 void assign(ArrayRef<NamedAttribute> range) { 857 assign(range.begin(), range.end()); 858 } 859 860 void clear() { 861 attrs.clear(); 862 dictionarySorted.setPointerAndInt(nullptr, false); 863 } 864 865 bool empty() const { return attrs.empty(); } 866 867 void reserve(size_type N) { attrs.reserve(N); } 868 869 /// Add an attribute with the specified name. 870 void push_back(NamedAttribute newAttribute); 871 872 /// Pop last element from list. 873 void pop_back() { attrs.pop_back(); } 874 875 /// Returns an entry with a duplicate name the list, if it exists, else 876 /// returns std::nullopt. 877 std::optional<NamedAttribute> findDuplicate() const; 878 879 /// Return a dictionary attribute for the underlying dictionary. This will 880 /// return an empty dictionary attribute if empty rather than null. 881 DictionaryAttr getDictionary(MLIRContext *context) const; 882 883 /// Return all of the attributes on this operation. 884 ArrayRef<NamedAttribute> getAttrs() const; 885 886 /// Return the specified attribute if present, null otherwise. 887 Attribute get(StringAttr name) const; 888 Attribute get(StringRef name) const; 889 890 /// Return the specified named attribute if present, std::nullopt otherwise. 891 std::optional<NamedAttribute> getNamed(StringRef name) const; 892 std::optional<NamedAttribute> getNamed(StringAttr name) const; 893 894 /// If the an attribute exists with the specified name, change it to the new 895 /// value. Otherwise, add a new attribute with the specified name/value. 896 /// Returns the previous attribute value of `name`, or null if no 897 /// attribute previously existed with `name`. 898 Attribute set(StringAttr name, Attribute value); 899 Attribute set(StringRef name, Attribute value); 900 901 /// Erase the attribute with the given name from the list. Return the 902 /// attribute that was erased, or nullptr if there was no attribute with such 903 /// name. 904 Attribute erase(StringAttr name); 905 Attribute erase(StringRef name); 906 907 iterator begin() { return attrs.begin(); } 908 iterator end() { return attrs.end(); } 909 const_iterator begin() const { return attrs.begin(); } 910 const_iterator end() const { return attrs.end(); } 911 912 NamedAttrList &operator=(const SmallVectorImpl<NamedAttribute> &rhs); 913 operator ArrayRef<NamedAttribute>() const; 914 915 private: 916 /// Return whether the attributes are sorted. 917 bool isSorted() const { return dictionarySorted.getInt(); } 918 919 /// Erase the attribute at the given iterator position. 920 Attribute eraseImpl(SmallVectorImpl<NamedAttribute>::iterator it); 921 922 /// Lookup an attribute in the list. 923 template <typename AttrListT, typename NameT> 924 static auto findAttr(AttrListT &attrs, NameT name) { 925 return attrs.isSorted() 926 ? impl::findAttrSorted(attrs.begin(), attrs.end(), name) 927 : impl::findAttrUnsorted(attrs.begin(), attrs.end(), name); 928 } 929 930 // These are marked mutable as they may be modified (e.g., sorted) 931 mutable SmallVector<NamedAttribute, 4> attrs; 932 // Pair with cached DictionaryAttr and status of whether attrs is sorted. 933 // Note: just because sorted does not mean a DictionaryAttr has been created 934 // but the case where there is a DictionaryAttr but attrs isn't sorted should 935 // not occur. 936 mutable llvm::PointerIntPair<Attribute, 1, bool> dictionarySorted; 937 }; 938 939 //===----------------------------------------------------------------------===// 940 // OperationState 941 //===----------------------------------------------------------------------===// 942 943 /// This represents an operation in an abstracted form, suitable for use with 944 /// the builder APIs. This object is a large and heavy weight object meant to 945 /// be used as a temporary object on the stack. It is generally unwise to put 946 /// this in a collection. 947 struct OperationState { 948 Location location; 949 OperationName name; 950 SmallVector<Value, 4> operands; 951 /// Types of the results of this operation. 952 SmallVector<Type, 4> types; 953 NamedAttrList attributes; 954 /// Successors of this operation and their respective operands. 955 SmallVector<Block *, 1> successors; 956 /// Regions that the op will hold. 957 SmallVector<std::unique_ptr<Region>, 1> regions; 958 959 /// This Attribute is used to opaquely construct the properties of the 960 /// operation. If we're creating an unregistered operation, the Attribute is 961 /// used as-is as the Properties storage of the operation. Otherwise, the 962 /// operation properties are constructed opaquely using its 963 /// `setPropertiesFromAttr` hook. Note that `getOrAddProperties` is the 964 /// preferred method to construct properties from C++. 965 Attribute propertiesAttr; 966 967 private: 968 OpaqueProperties properties = nullptr; 969 TypeID propertiesId; 970 llvm::function_ref<void(OpaqueProperties)> propertiesDeleter; 971 llvm::function_ref<void(OpaqueProperties, const OpaqueProperties)> 972 propertiesSetter; 973 friend class Operation; 974 975 public: 976 OperationState(Location location, StringRef name); 977 OperationState(Location location, OperationName name); 978 979 OperationState(Location location, OperationName name, ValueRange operands, 980 TypeRange types, ArrayRef<NamedAttribute> attributes = {}, 981 BlockRange successors = {}, 982 MutableArrayRef<std::unique_ptr<Region>> regions = {}); 983 OperationState(Location location, StringRef name, ValueRange operands, 984 TypeRange types, ArrayRef<NamedAttribute> attributes = {}, 985 BlockRange successors = {}, 986 MutableArrayRef<std::unique_ptr<Region>> regions = {}); 987 OperationState(OperationState &&other) = default; 988 OperationState(const OperationState &other) = default; 989 OperationState &operator=(OperationState &&other) = default; 990 OperationState &operator=(const OperationState &other) = default; 991 ~OperationState(); 992 993 /// Get (or create) a properties of the provided type to be set on the 994 /// operation on creation. 995 template <typename T> 996 T &getOrAddProperties() { 997 if (!properties) { 998 T *p = new T{}; 999 properties = p; 1000 propertiesDeleter = [](OpaqueProperties prop) { 1001 delete prop.as<const T *>(); 1002 }; 1003 propertiesSetter = [](OpaqueProperties new_prop, 1004 const OpaqueProperties prop) { 1005 *new_prop.as<T *>() = *prop.as<const T *>(); 1006 }; 1007 propertiesId = TypeID::get<T>(); 1008 } 1009 assert(propertiesId == TypeID::get<T>() && "Inconsistent properties"); 1010 return *properties.as<T *>(); 1011 } 1012 OpaqueProperties getRawProperties() { return properties; } 1013 1014 // Set the properties defined on this OpState on the given operation, 1015 // optionally emit diagnostics on error through the provided diagnostic. 1016 LogicalResult 1017 setProperties(Operation *op, 1018 function_ref<InFlightDiagnostic()> emitError) const; 1019 1020 void addOperands(ValueRange newOperands); 1021 1022 void addTypes(ArrayRef<Type> newTypes) { 1023 types.append(newTypes.begin(), newTypes.end()); 1024 } 1025 template <typename RangeT> 1026 std::enable_if_t<!std::is_convertible<RangeT, ArrayRef<Type>>::value> 1027 addTypes(RangeT &&newTypes) { 1028 types.append(newTypes.begin(), newTypes.end()); 1029 } 1030 1031 /// Add an attribute with the specified name. 1032 void addAttribute(StringRef name, Attribute attr) { 1033 addAttribute(StringAttr::get(getContext(), name), attr); 1034 } 1035 1036 /// Add an attribute with the specified name. `name` and `attr` must not be 1037 /// null. 1038 void addAttribute(StringAttr name, Attribute attr) { 1039 assert(name && "attribute name cannot be null"); 1040 assert(attr && "attribute cannot be null"); 1041 attributes.append(name, attr); 1042 } 1043 1044 /// Add an array of named attributes. 1045 void addAttributes(ArrayRef<NamedAttribute> newAttributes) { 1046 attributes.append(newAttributes); 1047 } 1048 1049 /// Adds a successor to the operation sate. `successor` must not be null. 1050 void addSuccessors(Block *successor) { 1051 assert(successor && "successor cannot be null"); 1052 successors.push_back(successor); 1053 } 1054 void addSuccessors(BlockRange newSuccessors); 1055 1056 /// Create a region that should be attached to the operation. These regions 1057 /// can be filled in immediately without waiting for Operation to be 1058 /// created. When it is, the region bodies will be transferred. 1059 Region *addRegion(); 1060 1061 /// Take a region that should be attached to the Operation. The body of the 1062 /// region will be transferred when the Operation is constructed. If the 1063 /// region is null, a new empty region will be attached to the Operation. 1064 void addRegion(std::unique_ptr<Region> &®ion); 1065 1066 /// Take ownership of a set of regions that should be attached to the 1067 /// Operation. 1068 void addRegions(MutableArrayRef<std::unique_ptr<Region>> regions); 1069 1070 /// Get the context held by this operation state. 1071 MLIRContext *getContext() const { return location->getContext(); } 1072 }; 1073 1074 //===----------------------------------------------------------------------===// 1075 // OperandStorage 1076 //===----------------------------------------------------------------------===// 1077 1078 namespace detail { 1079 /// This class handles the management of operation operands. Operands are 1080 /// stored either in a trailing array, or a dynamically resizable vector. 1081 class alignas(8) OperandStorage { 1082 public: 1083 OperandStorage(Operation *owner, OpOperand *trailingOperands, 1084 ValueRange values); 1085 ~OperandStorage(); 1086 1087 /// Replace the operands contained in the storage with the ones provided in 1088 /// 'values'. 1089 void setOperands(Operation *owner, ValueRange values); 1090 1091 /// Replace the operands beginning at 'start' and ending at 'start' + 'length' 1092 /// with the ones provided in 'operands'. 'operands' may be smaller or larger 1093 /// than the range pointed to by 'start'+'length'. 1094 void setOperands(Operation *owner, unsigned start, unsigned length, 1095 ValueRange operands); 1096 1097 /// Erase the operands held by the storage within the given range. 1098 void eraseOperands(unsigned start, unsigned length); 1099 1100 /// Erase the operands held by the storage that have their corresponding bit 1101 /// set in `eraseIndices`. 1102 void eraseOperands(const BitVector &eraseIndices); 1103 1104 /// Get the operation operands held by the storage. 1105 MutableArrayRef<OpOperand> getOperands() { return {operandStorage, size()}; } 1106 1107 /// Return the number of operands held in the storage. 1108 unsigned size() { return numOperands; } 1109 1110 private: 1111 /// Resize the storage to the given size. Returns the array containing the new 1112 /// operands. 1113 MutableArrayRef<OpOperand> resize(Operation *owner, unsigned newSize); 1114 1115 /// The total capacity number of operands that the storage can hold. 1116 unsigned capacity : 31; 1117 /// A flag indicating if the operand storage was dynamically allocated, as 1118 /// opposed to inlined into the owning operation. 1119 unsigned isStorageDynamic : 1; 1120 /// The number of operands within the storage. 1121 unsigned numOperands; 1122 /// A pointer to the operand storage. 1123 OpOperand *operandStorage; 1124 }; 1125 } // namespace detail 1126 1127 //===----------------------------------------------------------------------===// 1128 // OpPrintingFlags 1129 //===----------------------------------------------------------------------===// 1130 1131 /// Set of flags used to control the behavior of the various IR print methods 1132 /// (e.g. Operation::Print). 1133 class OpPrintingFlags { 1134 public: 1135 OpPrintingFlags(); 1136 OpPrintingFlags(std::nullopt_t) : OpPrintingFlags() {} 1137 1138 /// Enables the elision of large elements attributes by printing a lexically 1139 /// valid but otherwise meaningless form instead of the element data. The 1140 /// `largeElementLimit` is used to configure what is considered to be a 1141 /// "large" ElementsAttr by providing an upper limit to the number of 1142 /// elements. 1143 OpPrintingFlags &elideLargeElementsAttrs(int64_t largeElementLimit = 16); 1144 1145 /// Enables the printing of large element attributes with a hex string. The 1146 /// `largeElementLimit` is used to configure what is considered to be a 1147 /// "large" ElementsAttr by providing an upper limit to the number of 1148 /// elements. Use -1 to disable the hex printing. 1149 OpPrintingFlags & 1150 printLargeElementsAttrWithHex(int64_t largeElementLimit = 100); 1151 1152 /// Enables the elision of large resources strings by omitting them from the 1153 /// `dialect_resources` section. The `largeResourceLimit` is used to configure 1154 /// what is considered to be a "large" resource by providing an upper limit to 1155 /// the string size. 1156 OpPrintingFlags &elideLargeResourceString(int64_t largeResourceLimit = 64); 1157 1158 /// Enable or disable printing of debug information (based on `enable`). If 1159 /// 'prettyForm' is set to true, debug information is printed in a more 1160 /// readable 'pretty' form. Note: The IR generated with 'prettyForm' is not 1161 /// parsable. 1162 OpPrintingFlags &enableDebugInfo(bool enable = true, bool prettyForm = false); 1163 1164 /// Always print operations in the generic form. 1165 OpPrintingFlags &printGenericOpForm(bool enable = true); 1166 1167 /// Skip printing regions. 1168 OpPrintingFlags &skipRegions(bool skip = true); 1169 1170 /// Do not verify the operation when using custom operation printers. 1171 OpPrintingFlags &assumeVerified(bool enable = true); 1172 1173 /// Use local scope when printing the operation. This allows for using the 1174 /// printer in a more localized and thread-safe setting, but may not 1175 /// necessarily be identical to what the IR will look like when dumping 1176 /// the full module. 1177 OpPrintingFlags &useLocalScope(bool enable = true); 1178 1179 /// Print users of values as comments. 1180 OpPrintingFlags &printValueUsers(bool enable = true); 1181 1182 /// Print unique SSA ID numbers for values, block arguments and naming 1183 /// conflicts across all regions 1184 OpPrintingFlags &printUniqueSSAIDs(bool enable = true); 1185 1186 /// Return if the given ElementsAttr should be elided. 1187 bool shouldElideElementsAttr(ElementsAttr attr) const; 1188 1189 /// Return if the given ElementsAttr should be printed as hex string. 1190 bool shouldPrintElementsAttrWithHex(ElementsAttr attr) const; 1191 1192 /// Return the size limit for printing large ElementsAttr. 1193 std::optional<int64_t> getLargeElementsAttrLimit() const; 1194 1195 /// Return the size limit for printing large ElementsAttr as hex string. 1196 int64_t getLargeElementsAttrHexLimit() const; 1197 1198 /// Return the size limit in chars for printing large resources. 1199 std::optional<uint64_t> getLargeResourceStringLimit() const; 1200 1201 /// Return if debug information should be printed. 1202 bool shouldPrintDebugInfo() const; 1203 1204 /// Return if debug information should be printed in the pretty form. 1205 bool shouldPrintDebugInfoPrettyForm() const; 1206 1207 /// Return if operations should be printed in the generic form. 1208 bool shouldPrintGenericOpForm() const; 1209 1210 /// Return if regions should be skipped. 1211 bool shouldSkipRegions() const; 1212 1213 /// Return if operation verification should be skipped. 1214 bool shouldAssumeVerified() const; 1215 1216 /// Return if the printer should use local scope when dumping the IR. 1217 bool shouldUseLocalScope() const; 1218 1219 /// Return if the printer should print users of values. 1220 bool shouldPrintValueUsers() const; 1221 1222 /// Return if printer should use unique SSA IDs. 1223 bool shouldPrintUniqueSSAIDs() const; 1224 1225 /// Return if the printer should use NameLocs as prefixes when printing SSA 1226 /// IDs 1227 bool shouldUseNameLocAsPrefix() const; 1228 1229 private: 1230 /// Elide large elements attributes if the number of elements is larger than 1231 /// the upper limit. 1232 std::optional<int64_t> elementsAttrElementLimit; 1233 1234 /// Elide printing large resources based on size of string. 1235 std::optional<uint64_t> resourceStringCharLimit; 1236 1237 /// Print large element attributes with hex strings if the number of elements 1238 /// is larger than the upper limit. 1239 int64_t elementsAttrHexElementLimit = 100; 1240 1241 /// Print debug information. 1242 bool printDebugInfoFlag : 1; 1243 bool printDebugInfoPrettyFormFlag : 1; 1244 1245 /// Print operations in the generic form. 1246 bool printGenericOpFormFlag : 1; 1247 1248 /// Always skip Regions. 1249 bool skipRegionsFlag : 1; 1250 1251 /// Skip operation verification. 1252 bool assumeVerifiedFlag : 1; 1253 1254 /// Print operations with numberings local to the current operation. 1255 bool printLocalScope : 1; 1256 1257 /// Print users of values. 1258 bool printValueUsersFlag : 1; 1259 1260 /// Print unique SSA IDs for values, block arguments and naming conflicts 1261 bool printUniqueSSAIDsFlag : 1; 1262 1263 /// Print SSA IDs using NameLocs as prefixes 1264 bool useNameLocAsPrefix : 1; 1265 }; 1266 1267 //===----------------------------------------------------------------------===// 1268 // Operation Equivalency 1269 //===----------------------------------------------------------------------===// 1270 1271 /// This class provides utilities for computing if two operations are 1272 /// equivalent. 1273 struct OperationEquivalence { 1274 enum Flags { 1275 None = 0, 1276 1277 // When provided, the location attached to the operation are ignored. 1278 IgnoreLocations = 1, 1279 1280 LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ IgnoreLocations) 1281 }; 1282 1283 /// Compute a hash for the given operation. 1284 /// The `hashOperands` and `hashResults` callbacks are expected to return a 1285 /// unique hash_code for a given Value. 1286 static llvm::hash_code computeHash( 1287 Operation *op, 1288 function_ref<llvm::hash_code(Value)> hashOperands = 1289 [](Value v) { return hash_value(v); }, 1290 function_ref<llvm::hash_code(Value)> hashResults = 1291 [](Value v) { return hash_value(v); }, 1292 Flags flags = Flags::None); 1293 1294 /// Helper that can be used with `computeHash` above to ignore operation 1295 /// operands/result mapping. 1296 static llvm::hash_code ignoreHashValue(Value) { return llvm::hash_code{}; } 1297 /// Helper that can be used with `computeHash` above to ignore operation 1298 /// operands/result mapping. 1299 static llvm::hash_code directHashValue(Value v) { return hash_value(v); } 1300 1301 /// Compare two operations (including their regions) and return if they are 1302 /// equivalent. 1303 /// 1304 /// * `checkEquivalent` is a callback to check if two values are equivalent. 1305 /// For two operations to be equivalent, their operands must be the same SSA 1306 /// value or this callback must return `success`. 1307 /// * `markEquivalent` is a callback to inform the caller that the analysis 1308 /// determined that two values are equivalent. 1309 /// * `checkCommutativeEquivalent` is an optional callback to check for 1310 /// equivalence across two ranges for a commutative operation. If not passed 1311 /// in, then equivalence is checked pairwise. This callback is needed to be 1312 /// able to query the optional equivalence classes. 1313 /// 1314 /// Note: Additional information regarding value equivalence can be injected 1315 /// into the analysis via `checkEquivalent`. Typically, callers may want 1316 /// values that were determined to be equivalent as per `markEquivalent` to be 1317 /// reflected in `checkEquivalent`, unless `exactValueMatch` or a different 1318 /// equivalence relationship is desired. 1319 static bool 1320 isEquivalentTo(Operation *lhs, Operation *rhs, 1321 function_ref<LogicalResult(Value, Value)> checkEquivalent, 1322 function_ref<void(Value, Value)> markEquivalent = nullptr, 1323 Flags flags = Flags::None, 1324 function_ref<LogicalResult(ValueRange, ValueRange)> 1325 checkCommutativeEquivalent = nullptr); 1326 1327 /// Compare two operations and return if they are equivalent. 1328 static bool isEquivalentTo(Operation *lhs, Operation *rhs, Flags flags); 1329 1330 /// Compare two regions (including their subregions) and return if they are 1331 /// equivalent. See also `isEquivalentTo` for details. 1332 static bool isRegionEquivalentTo( 1333 Region *lhs, Region *rhs, 1334 function_ref<LogicalResult(Value, Value)> checkEquivalent, 1335 function_ref<void(Value, Value)> markEquivalent, 1336 OperationEquivalence::Flags flags, 1337 function_ref<LogicalResult(ValueRange, ValueRange)> 1338 checkCommutativeEquivalent = nullptr); 1339 1340 /// Compare two regions and return if they are equivalent. 1341 static bool isRegionEquivalentTo(Region *lhs, Region *rhs, 1342 OperationEquivalence::Flags flags); 1343 1344 /// Helper that can be used with `isEquivalentTo` above to consider ops 1345 /// equivalent even if their operands are not equivalent. 1346 static LogicalResult ignoreValueEquivalence(Value lhs, Value rhs) { 1347 return success(); 1348 } 1349 /// Helper that can be used with `isEquivalentTo` above to consider ops 1350 /// equivalent only if their operands are the exact same SSA values. 1351 static LogicalResult exactValueMatch(Value lhs, Value rhs) { 1352 return success(lhs == rhs); 1353 } 1354 }; 1355 1356 /// Enable Bitmask enums for OperationEquivalence::Flags. 1357 LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); 1358 1359 //===----------------------------------------------------------------------===// 1360 // OperationFingerPrint 1361 //===----------------------------------------------------------------------===// 1362 1363 /// A unique fingerprint for a specific operation, and all of it's internal 1364 /// operations (if `includeNested` is set). 1365 class OperationFingerPrint { 1366 public: 1367 OperationFingerPrint(Operation *topOp, bool includeNested = true); 1368 OperationFingerPrint(const OperationFingerPrint &) = default; 1369 OperationFingerPrint &operator=(const OperationFingerPrint &) = default; 1370 1371 bool operator==(const OperationFingerPrint &other) const { 1372 return hash == other.hash; 1373 } 1374 bool operator!=(const OperationFingerPrint &other) const { 1375 return !(*this == other); 1376 } 1377 1378 private: 1379 std::array<uint8_t, 20> hash; 1380 }; 1381 1382 } // namespace mlir 1383 1384 namespace llvm { 1385 template <> 1386 struct DenseMapInfo<mlir::OperationName> { 1387 static mlir::OperationName getEmptyKey() { 1388 void *pointer = llvm::DenseMapInfo<void *>::getEmptyKey(); 1389 return mlir::OperationName::getFromOpaquePointer(pointer); 1390 } 1391 static mlir::OperationName getTombstoneKey() { 1392 void *pointer = llvm::DenseMapInfo<void *>::getTombstoneKey(); 1393 return mlir::OperationName::getFromOpaquePointer(pointer); 1394 } 1395 static unsigned getHashValue(mlir::OperationName val) { 1396 return DenseMapInfo<void *>::getHashValue(val.getAsOpaquePointer()); 1397 } 1398 static bool isEqual(mlir::OperationName lhs, mlir::OperationName rhs) { 1399 return lhs == rhs; 1400 } 1401 }; 1402 template <> 1403 struct DenseMapInfo<mlir::RegisteredOperationName> 1404 : public DenseMapInfo<mlir::OperationName> { 1405 static mlir::RegisteredOperationName getEmptyKey() { 1406 void *pointer = llvm::DenseMapInfo<void *>::getEmptyKey(); 1407 return mlir::RegisteredOperationName::getFromOpaquePointer(pointer); 1408 } 1409 static mlir::RegisteredOperationName getTombstoneKey() { 1410 void *pointer = llvm::DenseMapInfo<void *>::getTombstoneKey(); 1411 return mlir::RegisteredOperationName::getFromOpaquePointer(pointer); 1412 } 1413 }; 1414 1415 template <> 1416 struct PointerLikeTypeTraits<mlir::OperationName> { 1417 static inline void *getAsVoidPointer(mlir::OperationName I) { 1418 return const_cast<void *>(I.getAsOpaquePointer()); 1419 } 1420 static inline mlir::OperationName getFromVoidPointer(void *P) { 1421 return mlir::OperationName::getFromOpaquePointer(P); 1422 } 1423 static constexpr int NumLowBitsAvailable = 1424 PointerLikeTypeTraits<void *>::NumLowBitsAvailable; 1425 }; 1426 template <> 1427 struct PointerLikeTypeTraits<mlir::RegisteredOperationName> 1428 : public PointerLikeTypeTraits<mlir::OperationName> { 1429 static inline mlir::RegisteredOperationName getFromVoidPointer(void *P) { 1430 return mlir::RegisteredOperationName::getFromOpaquePointer(P); 1431 } 1432 }; 1433 1434 } // namespace llvm 1435 1436 #endif 1437