brintos

brintos / llvm-project-archived public Read only

0
0
Text · 146.3 KiB · fd846e4 Raw
4225 lines · cpp
1//===- AsmPrinter.cpp - MLIR Assembly Printer Implementation --------------===//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-exception6//7//===----------------------------------------------------------------------===//8//9// This file implements the MLIR AsmPrinter class, which is used to implement10// the various print() methods on the core IR objects.11//12//===----------------------------------------------------------------------===//13 14#include "mlir/IR/AffineExpr.h"15#include "mlir/IR/AffineMap.h"16#include "mlir/IR/AsmState.h"17#include "mlir/IR/Attributes.h"18#include "mlir/IR/Builders.h"19#include "mlir/IR/BuiltinAttributes.h"20#include "mlir/IR/BuiltinDialect.h"21#include "mlir/IR/BuiltinTypeInterfaces.h"22#include "mlir/IR/BuiltinTypes.h"23#include "mlir/IR/Dialect.h"24#include "mlir/IR/DialectImplementation.h"25#include "mlir/IR/DialectResourceBlobManager.h"26#include "mlir/IR/IntegerSet.h"27#include "mlir/IR/MLIRContext.h"28#include "mlir/IR/OpImplementation.h"29#include "mlir/IR/Operation.h"30#include "mlir/IR/Verifier.h"31#include "llvm/ADT/APFloat.h"32#include "llvm/ADT/ArrayRef.h"33#include "llvm/ADT/DenseMap.h"34#include "llvm/ADT/MapVector.h"35#include "llvm/ADT/STLExtras.h"36#include "llvm/ADT/ScopeExit.h"37#include "llvm/ADT/ScopedHashTable.h"38#include "llvm/ADT/SetVector.h"39#include "llvm/ADT/StringExtras.h"40#include "llvm/ADT/StringSet.h"41#include "llvm/ADT/TypeSwitch.h"42#include "llvm/Support/CommandLine.h"43#include "llvm/Support/DebugLog.h"44#include "llvm/Support/Endian.h"45#include "llvm/Support/ManagedStatic.h"46#include "llvm/Support/Regex.h"47#include "llvm/Support/SaveAndRestore.h"48#include "llvm/Support/Threading.h"49#include "llvm/Support/raw_ostream.h"50#include <type_traits>51 52#include <optional>53#include <tuple>54 55using namespace mlir;56using namespace mlir::detail;57 58#define DEBUG_TYPE "mlir-asm-printer"59 60void OperationName::print(raw_ostream &os) const { os << getStringRef(); }61 62void OperationName::dump() const { print(llvm::errs()); }63 64//===--------------------------------------------------------------------===//65// AsmParser66//===--------------------------------------------------------------------===//67 68AsmParser::~AsmParser() = default;69DialectAsmParser::~DialectAsmParser() = default;70OpAsmParser::~OpAsmParser() = default;71 72MLIRContext *AsmParser::getContext() const { return getBuilder().getContext(); }73 74/// Parse a type list.75/// This is out-of-line to work-around76/// https://github.com/llvm/llvm-project/issues/6291877ParseResult AsmParser::parseTypeList(SmallVectorImpl<Type> &result) {78  return parseCommaSeparatedList(79      [&]() { return parseType(result.emplace_back()); });80}81 82//===----------------------------------------------------------------------===//83// DialectAsmPrinter84//===----------------------------------------------------------------------===//85 86DialectAsmPrinter::~DialectAsmPrinter() = default;87 88//===----------------------------------------------------------------------===//89// OpAsmPrinter90//===----------------------------------------------------------------------===//91 92OpAsmPrinter::~OpAsmPrinter() = default;93 94void OpAsmPrinter::printFunctionalType(Operation *op) {95  auto &os = getStream();96  os << '(';97  llvm::interleaveComma(op->getOperands(), os, [&](Value operand) {98    // Print the types of null values as <<NULL TYPE>>.99    *this << (operand ? operand.getType() : Type());100  });101  os << ") -> ";102 103  // Print the result list.  We don't parenthesize single result types unless104  // it is a function (avoiding a grammar ambiguity).105  bool wrapped = op->getNumResults() != 1;106  if (!wrapped && op->getResult(0).getType() &&107      isa<FunctionType>(op->getResult(0).getType()))108    wrapped = true;109 110  if (wrapped)111    os << '(';112 113  llvm::interleaveComma(op->getResults(), os, [&](const OpResult &result) {114    // Print the types of null values as <<NULL TYPE>>.115    *this << (result ? result.getType() : Type());116  });117 118  if (wrapped)119    os << ')';120}121 122//===----------------------------------------------------------------------===//123// Operation OpAsm interface.124//===----------------------------------------------------------------------===//125 126/// The OpAsmOpInterface, see OpAsmInterface.td for more details.127#include "mlir/IR/OpAsmAttrInterface.cpp.inc"128#include "mlir/IR/OpAsmOpInterface.cpp.inc"129#include "mlir/IR/OpAsmTypeInterface.cpp.inc"130 131LogicalResult132OpAsmDialectInterface::parseResource(AsmParsedResourceEntry &entry) const {133  return entry.emitError() << "unknown 'resource' key '" << entry.getKey()134                           << "' for dialect '" << getDialect()->getNamespace()135                           << "'";136}137 138//===----------------------------------------------------------------------===//139// OpPrintingFlags140//===----------------------------------------------------------------------===//141 142namespace {143/// This struct contains command line options that can be used to initialize144/// various bits of the AsmPrinter. This uses a struct wrapper to avoid the need145/// for global command line options.146struct AsmPrinterOptions {147  llvm::cl::opt<int64_t> printElementsAttrWithHexIfLarger{148      "mlir-print-elementsattrs-with-hex-if-larger",149      llvm::cl::desc(150          "Print DenseElementsAttrs with a hex string that have "151          "more elements than the given upper limit (use -1 to disable)")};152 153  llvm::cl::opt<unsigned> elideElementsAttrIfLarger{154      "mlir-elide-elementsattrs-if-larger",155      llvm::cl::desc("Elide ElementsAttrs with \"...\" that have "156                     "more elements than the given upper limit")};157 158  llvm::cl::opt<unsigned> elideResourceStringsIfLarger{159      "mlir-elide-resource-strings-if-larger",160      llvm::cl::desc(161          "Elide printing value of resources if string is too long in chars.")};162 163  llvm::cl::opt<bool> printDebugInfoOpt{164      "mlir-print-debuginfo", llvm::cl::init(false),165      llvm::cl::desc("Print debug info in MLIR output")};166 167  llvm::cl::opt<bool> printPrettyDebugInfoOpt{168      "mlir-pretty-debuginfo", llvm::cl::init(false),169      llvm::cl::desc("Print pretty debug info in MLIR output")};170 171  // Use the generic op output form in the operation printer even if the custom172  // form is defined.173  llvm::cl::opt<bool> printGenericOpFormOpt{174      "mlir-print-op-generic", llvm::cl::init(false),175      llvm::cl::desc("Print the generic op form"), llvm::cl::Hidden};176 177  llvm::cl::opt<bool> assumeVerifiedOpt{178      "mlir-print-assume-verified", llvm::cl::init(false),179      llvm::cl::desc("Skip op verification when using custom printers"),180      llvm::cl::Hidden};181 182  llvm::cl::opt<bool> printLocalScopeOpt{183      "mlir-print-local-scope", llvm::cl::init(false),184      llvm::cl::desc("Print with local scope and inline information (eliding "185                     "aliases for attributes, types, and locations)")};186 187  llvm::cl::opt<bool> skipRegionsOpt{188      "mlir-print-skip-regions", llvm::cl::init(false),189      llvm::cl::desc("Skip regions when printing ops.")};190 191  llvm::cl::opt<bool> printValueUsers{192      "mlir-print-value-users", llvm::cl::init(false),193      llvm::cl::desc(194          "Print users of operation results and block arguments as a comment")};195 196  llvm::cl::opt<bool> printUniqueSSAIDs{197      "mlir-print-unique-ssa-ids", llvm::cl::init(false),198      llvm::cl::desc("Print unique SSA ID numbers for values, block arguments "199                     "and naming conflicts across all regions")};200 201  llvm::cl::opt<bool> useNameLocAsPrefix{202      "mlir-use-nameloc-as-prefix", llvm::cl::init(false),203      llvm::cl::desc("Print SSA IDs using NameLocs as prefixes")};204};205} // namespace206 207static llvm::ManagedStatic<AsmPrinterOptions> clOptions;208 209/// Register a set of useful command-line options that can be used to configure210/// various flags within the AsmPrinter.211void mlir::registerAsmPrinterCLOptions() {212  // Make sure that the options struct has been initialized.213  *clOptions;214}215 216/// Initialize the printing flags with default supplied by the cl::opts above.217OpPrintingFlags::OpPrintingFlags()218    : printDebugInfoFlag(false), printDebugInfoPrettyFormFlag(false),219      printGenericOpFormFlag(false), skipRegionsFlag(false),220      assumeVerifiedFlag(false), printLocalScope(false),221      printValueUsersFlag(false), printUniqueSSAIDsFlag(false),222      useNameLocAsPrefix(false) {223  // Initialize based upon command line options, if they are available.224  if (!clOptions.isConstructed())225    return;226  if (clOptions->elideElementsAttrIfLarger.getNumOccurrences())227    elementsAttrElementLimit = clOptions->elideElementsAttrIfLarger;228  if (clOptions->printElementsAttrWithHexIfLarger.getNumOccurrences())229    elementsAttrHexElementLimit =230        clOptions->printElementsAttrWithHexIfLarger.getValue();231  if (clOptions->elideResourceStringsIfLarger.getNumOccurrences())232    resourceStringCharLimit = clOptions->elideResourceStringsIfLarger;233  printDebugInfoFlag = clOptions->printDebugInfoOpt;234  printDebugInfoPrettyFormFlag = clOptions->printPrettyDebugInfoOpt;235  printGenericOpFormFlag = clOptions->printGenericOpFormOpt;236  assumeVerifiedFlag = clOptions->assumeVerifiedOpt;237  printLocalScope = clOptions->printLocalScopeOpt;238  skipRegionsFlag = clOptions->skipRegionsOpt;239  printValueUsersFlag = clOptions->printValueUsers;240  printUniqueSSAIDsFlag = clOptions->printUniqueSSAIDs;241  useNameLocAsPrefix = clOptions->useNameLocAsPrefix;242}243 244/// Enable the elision of large elements attributes, by printing a '...'245/// instead of the element data, when the number of elements is greater than246/// `largeElementLimit`. Note: The IR generated with this option is not247/// parsable.248OpPrintingFlags &249OpPrintingFlags::elideLargeElementsAttrs(int64_t largeElementLimit) {250  elementsAttrElementLimit = largeElementLimit;251  return *this;252}253 254OpPrintingFlags &255OpPrintingFlags::printLargeElementsAttrWithHex(int64_t largeElementLimit) {256  elementsAttrHexElementLimit = largeElementLimit;257  return *this;258}259 260OpPrintingFlags &261OpPrintingFlags::elideLargeResourceString(int64_t largeResourceLimit) {262  resourceStringCharLimit = largeResourceLimit;263  return *this;264}265 266/// Enable printing of debug information. If 'prettyForm' is set to true,267/// debug information is printed in a more readable 'pretty' form.268OpPrintingFlags &OpPrintingFlags::enableDebugInfo(bool enable,269                                                  bool prettyForm) {270  printDebugInfoFlag = enable;271  printDebugInfoPrettyFormFlag = prettyForm;272  return *this;273}274 275/// Always print operations in the generic form.276OpPrintingFlags &OpPrintingFlags::printGenericOpForm(bool enable) {277  printGenericOpFormFlag = enable;278  return *this;279}280 281/// Always skip Regions.282OpPrintingFlags &OpPrintingFlags::skipRegions(bool skip) {283  skipRegionsFlag = skip;284  return *this;285}286 287/// Do not verify the operation when using custom operation printers.288OpPrintingFlags &OpPrintingFlags::assumeVerified(bool enable) {289  assumeVerifiedFlag = enable;290  return *this;291}292 293/// Use local scope when printing the operation. This allows for using the294/// printer in a more localized and thread-safe setting, but may not necessarily295/// be identical of what the IR will look like when dumping the full module.296OpPrintingFlags &OpPrintingFlags::useLocalScope(bool enable) {297  printLocalScope = enable;298  return *this;299}300 301/// Print users of values as comments.302OpPrintingFlags &OpPrintingFlags::printValueUsers(bool enable) {303  printValueUsersFlag = enable;304  return *this;305}306 307/// Print unique SSA ID numbers for values, block arguments and naming conflicts308/// across all regions309OpPrintingFlags &OpPrintingFlags::printUniqueSSAIDs(bool enable) {310  printUniqueSSAIDsFlag = enable;311  return *this;312}313 314/// Return if the given ElementsAttr should be elided.315bool OpPrintingFlags::shouldElideElementsAttr(ElementsAttr attr) const {316  return elementsAttrElementLimit &&317         *elementsAttrElementLimit < int64_t(attr.getNumElements()) &&318         !llvm::isa<SplatElementsAttr>(attr);319}320 321/// Return if the given ElementsAttr should be printed as hex string.322bool OpPrintingFlags::shouldPrintElementsAttrWithHex(ElementsAttr attr) const {323  // -1 is used to disable hex printing.324  return (elementsAttrHexElementLimit != -1) &&325         (elementsAttrHexElementLimit < int64_t(attr.getNumElements())) &&326         !llvm::isa<SplatElementsAttr>(attr);327}328 329OpPrintingFlags &OpPrintingFlags::printNameLocAsPrefix(bool enable) {330  useNameLocAsPrefix = enable;331  return *this;332}333 334/// Return the size limit for printing large ElementsAttr.335std::optional<int64_t> OpPrintingFlags::getLargeElementsAttrLimit() const {336  return elementsAttrElementLimit;337}338 339/// Return the size limit for printing large ElementsAttr as hex string.340int64_t OpPrintingFlags::getLargeElementsAttrHexLimit() const {341  return elementsAttrHexElementLimit;342}343 344/// Return the size limit for printing large ElementsAttr.345std::optional<uint64_t> OpPrintingFlags::getLargeResourceStringLimit() const {346  return resourceStringCharLimit;347}348 349/// Return if debug information should be printed.350bool OpPrintingFlags::shouldPrintDebugInfo() const {351  return printDebugInfoFlag;352}353 354/// Return if debug information should be printed in the pretty form.355bool OpPrintingFlags::shouldPrintDebugInfoPrettyForm() const {356  return printDebugInfoPrettyFormFlag;357}358 359/// Return if operations should be printed in the generic form.360bool OpPrintingFlags::shouldPrintGenericOpForm() const {361  return printGenericOpFormFlag;362}363 364/// Return if Region should be skipped.365bool OpPrintingFlags::shouldSkipRegions() const { return skipRegionsFlag; }366 367/// Return if operation verification should be skipped.368bool OpPrintingFlags::shouldAssumeVerified() const {369  return assumeVerifiedFlag;370}371 372/// Return if the printer should use local scope when dumping the IR.373bool OpPrintingFlags::shouldUseLocalScope() const { return printLocalScope; }374 375/// Return if the printer should print users of values.376bool OpPrintingFlags::shouldPrintValueUsers() const {377  return printValueUsersFlag;378}379 380/// Return if the printer should use unique IDs.381bool OpPrintingFlags::shouldPrintUniqueSSAIDs() const {382  return printUniqueSSAIDsFlag || shouldPrintGenericOpForm();383}384 385/// Return if the printer should use NameLocs as prefixes when printing SSA IDs.386bool OpPrintingFlags::shouldUseNameLocAsPrefix() const {387  return useNameLocAsPrefix;388}389 390//===----------------------------------------------------------------------===//391// NewLineCounter392//===----------------------------------------------------------------------===//393 394namespace {395/// This class is a simple formatter that emits a new line when inputted into a396/// stream, that enables counting the number of newlines emitted. This class397/// should be used whenever emitting newlines in the printer.398struct NewLineCounter {399  unsigned curLine = 1;400};401 402static raw_ostream &operator<<(raw_ostream &os, NewLineCounter &newLine) {403  ++newLine.curLine;404  return os << '\n';405}406} // namespace407 408//===----------------------------------------------------------------------===//409// AsmPrinter::Impl410//===----------------------------------------------------------------------===//411 412namespace mlir {413class AsmPrinter::Impl {414public:415  Impl(raw_ostream &os, AsmStateImpl &state);416  explicit Impl(Impl &other) : Impl(other.os, other.state) {}417 418  /// Returns the output stream of the printer.419  raw_ostream &getStream() { return os; }420 421  template <typename Container, typename UnaryFunctor>422  inline void interleaveComma(const Container &c, UnaryFunctor eachFn) const {423    llvm::interleaveComma(c, os, eachFn);424  }425 426  /// This enum describes the different kinds of elision for the type of an427  /// attribute when printing it.428  enum class AttrTypeElision {429    /// The type must not be elided,430    Never,431    /// The type may be elided when it matches the default used in the parser432    /// (for example i64 is the default for integer attributes).433    May,434    /// The type must be elided.435    Must436  };437 438  /// Print the given attribute or an alias.439  void printAttribute(Attribute attr,440                      AttrTypeElision typeElision = AttrTypeElision::Never);441  /// Print the given attribute without considering an alias.442  void printAttributeImpl(Attribute attr,443                          AttrTypeElision typeElision = AttrTypeElision::Never);444  void printNamedAttribute(NamedAttribute attr);445 446  /// Print the alias for the given attribute, return failure if no alias could447  /// be printed.448  LogicalResult printAlias(Attribute attr);449 450  /// Print the given type or an alias.451  void printType(Type type);452  /// Print the given type.453  void printTypeImpl(Type type);454 455  /// Print the alias for the given type, return failure if no alias could456  /// be printed.457  LogicalResult printAlias(Type type);458 459  /// Print the given location to the stream. If `allowAlias` is true, this460  /// allows for the internal location to use an attribute alias.461  void printLocation(LocationAttr loc, bool allowAlias = false);462 463  /// Print a reference to the given resource that is owned by the given464  /// dialect.465  void printResourceHandle(const AsmDialectResourceHandle &resource);466 467  void printAffineMap(AffineMap map);468  void469  printAffineExpr(AffineExpr expr,470                  function_ref<void(unsigned, bool)> printValueName = nullptr);471  void printAffineConstraint(AffineExpr expr, bool isEq);472  void printIntegerSet(IntegerSet set);473 474  LogicalResult pushCyclicPrinting(const void *opaquePointer);475 476  void popCyclicPrinting();477 478  void printDimensionList(ArrayRef<int64_t> shape);479 480protected:481  void printOptionalAttrDict(ArrayRef<NamedAttribute> attrs,482                             ArrayRef<StringRef> elidedAttrs = {},483                             bool withKeyword = false);484  void printTrailingLocation(Location loc, bool allowAlias = true);485  void printLocationInternal(LocationAttr loc, bool pretty = false,486                             bool isTopLevel = false);487 488  /// Print a dense elements attribute. If 'allowHex' is true, a hex string is489  /// used instead of individual elements when the elements attr is large.490  void printDenseElementsAttr(DenseElementsAttr attr, bool allowHex);491 492  /// Print a dense string elements attribute.493  void printDenseStringElementsAttr(DenseStringElementsAttr attr);494 495  /// Print a dense elements attribute. If 'allowHex' is true, a hex string is496  /// used instead of individual elements when the elements attr is large.497  void printDenseIntOrFPElementsAttr(DenseIntOrFPElementsAttr attr,498                                     bool allowHex);499 500  /// Print a dense array attribute.501  void printDenseArrayAttr(DenseArrayAttr attr);502 503  void printDialectAttribute(Attribute attr);504  void printDialectType(Type type);505 506  /// Print an escaped string, wrapped with "".507  void printEscapedString(StringRef str);508 509  /// Print a hex string, wrapped with "".510  void printHexString(StringRef str);511  void printHexString(ArrayRef<char> data);512 513  /// This enum is used to represent the binding strength of the enclosing514  /// context that an AffineExprStorage is being printed in, so we can515  /// intelligently produce parens.516  enum class BindingStrength {517    Weak,   // + and -518    Strong, // All other binary operators.519  };520  void printAffineExprInternal(521      AffineExpr expr, BindingStrength enclosingTightness,522      function_ref<void(unsigned, bool)> printValueName = nullptr);523 524  /// The output stream for the printer.525  raw_ostream &os;526 527  /// An underlying assembly printer state.528  AsmStateImpl &state;529 530  /// A set of flags to control the printer's behavior.531  OpPrintingFlags printerFlags;532 533  /// A tracker for the number of new lines emitted during printing.534  NewLineCounter newLine;535};536} // namespace mlir537 538//===----------------------------------------------------------------------===//539// AliasInitializer540//===----------------------------------------------------------------------===//541 542namespace {543/// This class represents a specific instance of a symbol Alias.544class SymbolAlias {545public:546  SymbolAlias(StringRef name, uint32_t suffixIndex, bool isType,547              bool isDeferrable)548      : name(name), suffixIndex(suffixIndex), isType(isType),549        isDeferrable(isDeferrable) {}550 551  /// Print this alias to the given stream.552  void print(raw_ostream &os) const {553    os << (isType ? "!" : "#") << name;554    if (suffixIndex) {555      if (isdigit(name.back()))556        os << '_';557      os << suffixIndex;558    }559  }560 561  /// Returns true if this is a type alias.562  bool isTypeAlias() const { return isType; }563 564  /// Returns true if this alias supports deferred resolution when parsing.565  bool canBeDeferred() const { return isDeferrable; }566 567private:568  /// The main name of the alias.569  StringRef name;570  /// The suffix index of the alias.571  uint32_t suffixIndex : 30;572  /// A flag indicating whether this alias is for a type.573  bool isType : 1;574  /// A flag indicating whether this alias may be deferred or not.575  bool isDeferrable : 1;576 577public:578  /// Used to avoid printing incomplete aliases for recursive types.579  bool isPrinted = false;580};581 582/// This class represents a utility that initializes the set of attribute and583/// type aliases, without the need to store the extra information within the584/// main AliasState class or pass it around via function arguments.585class AliasInitializer {586public:587  AliasInitializer(588      DialectInterfaceCollection<OpAsmDialectInterface> &interfaces,589      llvm::BumpPtrAllocator &aliasAllocator)590      : interfaces(interfaces), aliasAllocator(aliasAllocator),591        aliasOS(aliasBuffer) {}592 593  void initialize(Operation *op, const OpPrintingFlags &printerFlags,594                  llvm::MapVector<const void *, SymbolAlias> &attrTypeToAlias);595 596  /// Visit the given attribute to see if it has an alias. `canBeDeferred` is597  /// set to true if the originator of this attribute can resolve the alias598  /// after parsing has completed (e.g. in the case of operation locations).599  /// `elideType` indicates if the type of the attribute should be skipped when600  /// looking for nested aliases. Returns the maximum alias depth of the601  /// attribute, and the alias index of this attribute.602  std::pair<size_t, size_t> visit(Attribute attr, bool canBeDeferred = false,603                                  bool elideType = false) {604    return visitImpl(attr, aliases, canBeDeferred, elideType);605  }606 607  /// Visit the given type to see if it has an alias. `canBeDeferred` is608  /// set to true if the originator of this attribute can resolve the alias609  /// after parsing has completed. Returns the maximum alias depth of the type,610  /// and the alias index of this type.611  std::pair<size_t, size_t> visit(Type type, bool canBeDeferred = false) {612    return visitImpl(type, aliases, canBeDeferred);613  }614 615private:616  struct InProgressAliasInfo {617    InProgressAliasInfo()618        : aliasDepth(0), isType(false), canBeDeferred(false) {}619    InProgressAliasInfo(StringRef alias)620        : alias(alias), aliasDepth(1), isType(false), canBeDeferred(false) {}621 622    bool operator<(const InProgressAliasInfo &rhs) const {623      // Order first by depth, then by attr/type kind, and then by name.624      if (aliasDepth != rhs.aliasDepth)625        return aliasDepth < rhs.aliasDepth;626      if (isType != rhs.isType)627        return isType;628      return alias < rhs.alias;629    }630 631    /// The alias for the attribute or type, or std::nullopt if the value has no632    /// alias.633    std::optional<StringRef> alias;634    /// The alias depth of this attribute or type, i.e. an indication of the635    /// relative ordering of when to print this alias.636    unsigned aliasDepth : 30;637    /// If this alias represents a type or an attribute.638    bool isType : 1;639    /// If this alias can be deferred or not.640    bool canBeDeferred : 1;641    /// Indices for child aliases.642    SmallVector<size_t> childIndices;643  };644 645  /// Visit the given attribute or type to see if it has an alias.646  /// `canBeDeferred` is set to true if the originator of this value can resolve647  /// the alias after parsing has completed (e.g. in the case of operation648  /// locations). Returns the maximum alias depth of the value, and its alias649  /// index.650  template <typename T, typename... PrintArgs>651  std::pair<size_t, size_t>652  visitImpl(T value,653            llvm::MapVector<const void *, InProgressAliasInfo> &aliases,654            bool canBeDeferred, PrintArgs &&...printArgs);655 656  /// Mark the given alias as non-deferrable.657  void markAliasNonDeferrable(size_t aliasIndex);658 659  /// Try to generate an alias for the provided symbol. If an alias is660  /// generated, the provided alias mapping and reverse mapping are updated.661  template <typename T>662  void generateAlias(T symbol, InProgressAliasInfo &alias, bool canBeDeferred);663 664  /// Uniques the given alias name within the printer by generating name index665  /// used as alias name suffix.666  static unsigned667  uniqueAliasNameIndex(StringRef alias, llvm::StringMap<unsigned> &nameCounts,668                       llvm::StringSet<llvm::BumpPtrAllocator &> &usedAliases);669 670  /// Given a collection of aliases and symbols, initialize a mapping from a671  /// symbol to a given alias.672  static void initializeAliases(673      llvm::MapVector<const void *, InProgressAliasInfo> &visitedSymbols,674      llvm::MapVector<const void *, SymbolAlias> &symbolToAlias);675 676  /// The set of asm interfaces within the context.677  DialectInterfaceCollection<OpAsmDialectInterface> &interfaces;678 679  /// An allocator used for alias names.680  llvm::BumpPtrAllocator &aliasAllocator;681 682  /// The set of built aliases.683  llvm::MapVector<const void *, InProgressAliasInfo> aliases;684 685  /// Storage and stream used when generating an alias.686  SmallString<32> aliasBuffer;687  llvm::raw_svector_ostream aliasOS;688};689 690/// This class implements a dummy OpAsmPrinter that doesn't print any output,691/// and merely collects the attributes and types that *would* be printed in a692/// normal print invocation so that we can generate proper aliases. This allows693/// for us to generate aliases only for the attributes and types that would be694/// in the output, and trims down unnecessary output.695class DummyAliasOperationPrinter : private OpAsmPrinter {696public:697  explicit DummyAliasOperationPrinter(const OpPrintingFlags &printerFlags,698                                      AliasInitializer &initializer)699      : printerFlags(printerFlags), initializer(initializer) {}700 701  /// Prints the entire operation with the custom assembly form, if available,702  /// or the generic assembly form, otherwise.703  void printCustomOrGenericOp(Operation *op) override {704    // Visit the operation location.705    if (printerFlags.shouldPrintDebugInfo())706      initializer.visit(op->getLoc(), /*canBeDeferred=*/true);707 708    // If requested, always print the generic form.709    if (!printerFlags.shouldPrintGenericOpForm()) {710      op->getName().printAssembly(op, *this, /*defaultDialect=*/"");711      return;712    }713 714    // Otherwise print with the generic assembly form.715    printGenericOp(op);716  }717 718private:719  /// Print the given operation in the generic form.720  void printGenericOp(Operation *op, bool printOpName = true) override {721    // Consider nested operations for aliases.722    if (!printerFlags.shouldSkipRegions()) {723      for (Region &region : op->getRegions())724        printRegion(region, /*printEntryBlockArgs=*/true,725                    /*printBlockTerminators=*/true);726    }727 728    // Visit all the types used in the operation.729    for (Type type : op->getOperandTypes())730      printType(type);731    for (Type type : op->getResultTypes())732      printType(type);733 734    // Consider the attributes of the operation for aliases.735    for (const NamedAttribute &attr : op->getAttrs())736      printAttribute(attr.getValue());737  }738 739  /// Print the given block. If 'printBlockArgs' is false, the arguments of the740  /// block are not printed. If 'printBlockTerminator' is false, the terminator741  /// operation of the block is not printed.742  void print(Block *block, bool printBlockArgs = true,743             bool printBlockTerminator = true) {744    // Consider the types of the block arguments for aliases if 'printBlockArgs'745    // is set to true.746    if (printBlockArgs) {747      for (BlockArgument arg : block->getArguments()) {748        printType(arg.getType());749 750        // Visit the argument location.751        if (printerFlags.shouldPrintDebugInfo())752          // TODO: Allow deferring argument locations.753          initializer.visit(arg.getLoc(), /*canBeDeferred=*/false);754      }755    }756 757    // Consider the operations within this block, ignoring the terminator if758    // requested.759    bool hasTerminator =760        !block->empty() && block->back().hasTrait<OpTrait::IsTerminator>();761    auto range = llvm::make_range(762        block->begin(),763        std::prev(block->end(),764                  (!hasTerminator || printBlockTerminator) ? 0 : 1));765    for (Operation &op : range)766      printCustomOrGenericOp(&op);767  }768 769  /// Print the given region.770  void printRegion(Region &region, bool printEntryBlockArgs,771                   bool printBlockTerminators,772                   bool printEmptyBlock = false) override {773    if (region.empty())774      return;775    if (printerFlags.shouldSkipRegions()) {776      os << "{...}";777      return;778    }779 780    auto *entryBlock = &region.front();781    print(entryBlock, printEntryBlockArgs, printBlockTerminators);782    for (Block &b : llvm::drop_begin(region, 1))783      print(&b);784  }785 786  void printRegionArgument(BlockArgument arg, ArrayRef<NamedAttribute> argAttrs,787                           bool omitType) override {788    printType(arg.getType());789    // Visit the argument location.790    if (printerFlags.shouldPrintDebugInfo())791      // TODO: Allow deferring argument locations.792      initializer.visit(arg.getLoc(), /*canBeDeferred=*/false);793  }794 795  /// Consider the given type to be printed for an alias.796  void printType(Type type) override { initializer.visit(type); }797 798  /// Consider the given attribute to be printed for an alias.799  void printAttribute(Attribute attr) override { initializer.visit(attr); }800  void printAttributeWithoutType(Attribute attr) override {801    printAttribute(attr);802  }803  void printNamedAttribute(NamedAttribute attr) override {804    printAttribute(attr.getValue());805  }806 807  LogicalResult printAlias(Attribute attr) override {808    initializer.visit(attr);809    return success();810  }811  LogicalResult printAlias(Type type) override {812    initializer.visit(type);813    return success();814  }815 816  /// Consider the given location to be printed for an alias.817  void printOptionalLocationSpecifier(Location loc) override {818    printAttribute(loc);819  }820 821  /// Print the given set of attributes with names not included within822  /// 'elidedAttrs'.823  void printOptionalAttrDict(ArrayRef<NamedAttribute> attrs,824                             ArrayRef<StringRef> elidedAttrs = {}) override {825    if (attrs.empty())826      return;827    if (elidedAttrs.empty()) {828      for (const NamedAttribute &attr : attrs)829        printAttribute(attr.getValue());830      return;831    }832    llvm::SmallDenseSet<StringRef> elidedAttrsSet(elidedAttrs.begin(),833                                                  elidedAttrs.end());834    for (const NamedAttribute &attr : attrs)835      if (!elidedAttrsSet.contains(attr.getName().strref()))836        printAttribute(attr.getValue());837  }838  void printOptionalAttrDictWithKeyword(839      ArrayRef<NamedAttribute> attrs,840      ArrayRef<StringRef> elidedAttrs = {}) override {841    printOptionalAttrDict(attrs, elidedAttrs);842  }843 844  /// Return a null stream as the output stream, this will ignore any data fed845  /// to it.846  raw_ostream &getStream() const override { return os; }847 848  /// The following are hooks of `OpAsmPrinter` that are not necessary for849  /// determining potential aliases.850  void printFloat(const APFloat &) override {}851  void printAffineMapOfSSAIds(AffineMapAttr, ValueRange) override {}852  void printAffineExprOfSSAIds(AffineExpr, ValueRange, ValueRange) override {}853  void printNewline() override {}854  void increaseIndent() override {}855  void decreaseIndent() override {}856  void printOperand(Value) override {}857  void printOperand(Value, raw_ostream &os) override {858    // Users expect the output string to have at least the prefixed % to signal859    // a value name. To maintain this invariant, emit a name even if it is860    // guaranteed to go unused.861    os << "%";862  }863  void printKeywordOrString(StringRef) override {}864  void printString(StringRef) override {}865  void printResourceHandle(const AsmDialectResourceHandle &) override {}866  void printSymbolName(StringRef) override {}867  void printSuccessor(Block *) override {}868  void printSuccessorAndUseList(Block *, ValueRange) override {}869  void shadowRegionArgs(Region &, ValueRange) override {}870 871  /// The printer flags to use when determining potential aliases.872  const OpPrintingFlags &printerFlags;873 874  /// The initializer to use when identifying aliases.875  AliasInitializer &initializer;876 877  /// A dummy output stream.878  mutable llvm::raw_null_ostream os;879};880 881class DummyAliasDialectAsmPrinter : public DialectAsmPrinter {882public:883  explicit DummyAliasDialectAsmPrinter(AliasInitializer &initializer,884                                       bool canBeDeferred,885                                       SmallVectorImpl<size_t> &childIndices)886      : initializer(initializer), canBeDeferred(canBeDeferred),887        childIndices(childIndices) {}888 889  /// Print the given attribute/type, visiting any nested aliases that would be890  /// generated as part of printing. Returns the maximum alias depth found while891  /// printing the given value.892  template <typename T, typename... PrintArgs>893  size_t printAndVisitNestedAliases(T value, PrintArgs &&...printArgs) {894    printAndVisitNestedAliasesImpl(value, printArgs...);895    return maxAliasDepth;896  }897 898private:899  /// Print the given attribute/type, visiting any nested aliases that would be900  /// generated as part of printing.901  void printAndVisitNestedAliasesImpl(Attribute attr, bool elideType) {902    if (!isa<BuiltinDialect>(attr.getDialect())) {903      attr.getDialect().printAttribute(attr, *this);904 905      // Process the builtin attributes.906    } else if (llvm::isa<AffineMapAttr, DenseArrayAttr, FloatAttr, IntegerAttr,907                         IntegerSetAttr, UnitAttr>(attr)) {908      return;909    } else if (auto distinctAttr = dyn_cast<DistinctAttr>(attr)) {910      printAttribute(distinctAttr.getReferencedAttr());911    } else if (auto dictAttr = dyn_cast<DictionaryAttr>(attr)) {912      for (const NamedAttribute &nestedAttr : dictAttr.getValue()) {913        printAttribute(nestedAttr.getName());914        printAttribute(nestedAttr.getValue());915      }916    } else if (auto arrayAttr = dyn_cast<ArrayAttr>(attr)) {917      for (Attribute nestedAttr : arrayAttr.getValue())918        printAttribute(nestedAttr);919    } else if (auto typeAttr = dyn_cast<TypeAttr>(attr)) {920      printType(typeAttr.getValue());921    } else if (auto locAttr = dyn_cast<OpaqueLoc>(attr)) {922      printAttribute(locAttr.getFallbackLocation());923    } else if (auto locAttr = dyn_cast<NameLoc>(attr)) {924      if (!isa<UnknownLoc>(locAttr.getChildLoc()))925        printAttribute(locAttr.getChildLoc());926    } else if (auto locAttr = dyn_cast<CallSiteLoc>(attr)) {927      printAttribute(locAttr.getCallee());928      printAttribute(locAttr.getCaller());929    } else if (auto locAttr = dyn_cast<FusedLoc>(attr)) {930      if (Attribute metadata = locAttr.getMetadata())931        printAttribute(metadata);932      for (Location nestedLoc : locAttr.getLocations())933        printAttribute(nestedLoc);934    }935 936    // Don't print the type if we must elide it, or if it is a None type.937    if (!elideType) {938      if (auto typedAttr = llvm::dyn_cast<TypedAttr>(attr)) {939        Type attrType = typedAttr.getType();940        if (!llvm::isa<NoneType>(attrType))941          printType(attrType);942      }943    }944  }945  void printAndVisitNestedAliasesImpl(Type type) {946    if (!isa<BuiltinDialect>(type.getDialect()))947      return type.getDialect().printType(type, *this);948 949    // Only visit the layout of memref if it isn't the identity.950    if (auto memrefTy = llvm::dyn_cast<MemRefType>(type)) {951      printType(memrefTy.getElementType());952      MemRefLayoutAttrInterface layout = memrefTy.getLayout();953      if (!llvm::isa<AffineMapAttr>(layout) || !layout.isIdentity())954        printAttribute(memrefTy.getLayout());955      if (memrefTy.getMemorySpace())956        printAttribute(memrefTy.getMemorySpace());957      return;958    }959 960    // For most builtin types, we can simply walk the sub elements.961    auto visitFn = [&](auto element) {962      if (element)963        (void)printAlias(element);964    };965    type.walkImmediateSubElements(visitFn, visitFn);966  }967 968  /// Consider the given type to be printed for an alias.969  void printType(Type type) override {970    recordAliasResult(initializer.visit(type, canBeDeferred));971  }972 973  /// Consider the given attribute to be printed for an alias.974  void printAttribute(Attribute attr) override {975    recordAliasResult(initializer.visit(attr, canBeDeferred));976  }977  void printAttributeWithoutType(Attribute attr) override {978    recordAliasResult(979        initializer.visit(attr, canBeDeferred, /*elideType=*/true));980  }981  void printNamedAttribute(NamedAttribute attr) override {982    printAttribute(attr.getValue());983  }984 985  LogicalResult printAlias(Attribute attr) override {986    printAttribute(attr);987    return success();988  }989  LogicalResult printAlias(Type type) override {990    printType(type);991    return success();992  }993 994  /// Record the alias result of a child element.995  void recordAliasResult(std::pair<size_t, size_t> aliasDepthAndIndex) {996    childIndices.push_back(aliasDepthAndIndex.second);997    if (aliasDepthAndIndex.first > maxAliasDepth)998      maxAliasDepth = aliasDepthAndIndex.first;999  }1000 1001  /// Return a null stream as the output stream, this will ignore any data fed1002  /// to it.1003  raw_ostream &getStream() const override { return os; }1004 1005  /// The following are hooks of `DialectAsmPrinter` that are not necessary for1006  /// determining potential aliases.1007  void printFloat(const APFloat &) override {}1008  void printKeywordOrString(StringRef) override {}1009  void printString(StringRef) override {}1010  void printSymbolName(StringRef) override {}1011  void printResourceHandle(const AsmDialectResourceHandle &) override {}1012 1013  LogicalResult pushCyclicPrinting(const void *opaquePointer) override {1014    return success(cyclicPrintingStack.insert(opaquePointer));1015  }1016 1017  void popCyclicPrinting() override { cyclicPrintingStack.pop_back(); }1018 1019  /// Stack of potentially cyclic mutable attributes or type currently being1020  /// printed.1021  SetVector<const void *> cyclicPrintingStack;1022 1023  /// The initializer to use when identifying aliases.1024  AliasInitializer &initializer;1025 1026  /// If the aliases visited by this printer can be deferred.1027  bool canBeDeferred;1028 1029  /// The indices of child aliases.1030  SmallVectorImpl<size_t> &childIndices;1031 1032  /// The maximum alias depth found by the printer.1033  size_t maxAliasDepth = 0;1034 1035  /// A dummy output stream.1036  mutable llvm::raw_null_ostream os;1037};1038} // namespace1039 1040/// Sanitize the given name such that it can be used as a valid identifier. If1041/// the string needs to be modified in any way, the provided buffer is used to1042/// store the new copy,1043static StringRef sanitizeIdentifier(StringRef name, SmallString<16> &buffer,1044                                    StringRef allowedPunctChars = "$._-") {1045  assert(!name.empty() && "Shouldn't have an empty name here");1046 1047  auto validChar = [&](char ch) {1048    return llvm::isAlnum(ch) || allowedPunctChars.contains(ch);1049  };1050 1051  auto copyNameToBuffer = [&] {1052    for (char ch : name) {1053      if (validChar(ch))1054        buffer.push_back(ch);1055      else if (ch == ' ')1056        buffer.push_back('_');1057      else1058        buffer.append(llvm::utohexstr((unsigned char)ch));1059    }1060  };1061 1062  // Check to see if this name is valid. If it starts with a digit, then it1063  // could conflict with the autogenerated numeric ID's, so add an underscore1064  // prefix to avoid problems.1065  if (isdigit(name[0]) || (!validChar(name[0]) && name[0] != ' ')) {1066    buffer.push_back('_');1067    copyNameToBuffer();1068    return buffer;1069  }1070 1071  // Check to see that the name consists of only valid identifier characters.1072  for (char ch : name) {1073    if (!validChar(ch)) {1074      copyNameToBuffer();1075      return buffer;1076    }1077  }1078 1079  // If there are no invalid characters, return the original name.1080  return name;1081}1082 1083unsigned AliasInitializer::uniqueAliasNameIndex(1084    StringRef alias, llvm::StringMap<unsigned> &nameCounts,1085    llvm::StringSet<llvm::BumpPtrAllocator &> &usedAliases) {1086  if (!usedAliases.count(alias)) {1087    usedAliases.insert(alias);1088    // 0 is not printed in SymbolAlias.1089    return 0;1090  }1091  // Otherwise, we had a conflict - probe until we find a unique name.1092  SmallString<64> probeAlias(alias);1093  // alias with trailing digit will be printed as _N1094  if (isdigit(alias.back()))1095    probeAlias.push_back('_');1096  // nameCounts start from 1 because 0 is not printed in SymbolAlias.1097  if (nameCounts[probeAlias] == 0)1098    nameCounts[probeAlias] = 1;1099  // This is guaranteed to terminate (and usually in a single iteration)1100  // because it generates new names by incrementing nameCounts.1101  while (true) {1102    unsigned nameIndex = nameCounts[probeAlias]++;1103    probeAlias += llvm::utostr(nameIndex);1104    if (!usedAliases.count(probeAlias)) {1105      usedAliases.insert(probeAlias);1106      return nameIndex;1107    }1108    // Reset probeAlias to the original alias for the next iteration.1109    probeAlias.resize(alias.size() + isdigit(alias.back()) ? 1 : 0);1110  }1111}1112 1113/// Given a collection of aliases and symbols, initialize a mapping from a1114/// symbol to a given alias.1115void AliasInitializer::initializeAliases(1116    llvm::MapVector<const void *, InProgressAliasInfo> &visitedSymbols,1117    llvm::MapVector<const void *, SymbolAlias> &symbolToAlias) {1118  SmallVector<std::pair<const void *, InProgressAliasInfo>, 0>1119      unprocessedAliases = visitedSymbols.takeVector();1120  llvm::stable_sort(unprocessedAliases, llvm::less_second());1121 1122  // This keeps track of all of the non-numeric names that are in flight,1123  // allowing us to check for duplicates.1124  llvm::BumpPtrAllocator usedAliasAllocator;1125  llvm::StringSet<llvm::BumpPtrAllocator &> usedAliases(usedAliasAllocator);1126 1127  llvm::StringMap<unsigned> nameCounts;1128  for (auto &[symbol, aliasInfo] : unprocessedAliases) {1129    if (!aliasInfo.alias)1130      continue;1131    StringRef alias = *aliasInfo.alias;1132    unsigned nameIndex = uniqueAliasNameIndex(alias, nameCounts, usedAliases);1133    symbolToAlias.insert(1134        {symbol, SymbolAlias(alias, nameIndex, aliasInfo.isType,1135                             aliasInfo.canBeDeferred)});1136  }1137}1138 1139void AliasInitializer::initialize(1140    Operation *op, const OpPrintingFlags &printerFlags,1141    llvm::MapVector<const void *, SymbolAlias> &attrTypeToAlias) {1142  // Use a dummy printer when walking the IR so that we can collect the1143  // attributes/types that will actually be used during printing when1144  // considering aliases.1145  DummyAliasOperationPrinter aliasPrinter(printerFlags, *this);1146  aliasPrinter.printCustomOrGenericOp(op);1147 1148  // Initialize the aliases.1149  initializeAliases(aliases, attrTypeToAlias);1150}1151 1152template <typename T, typename... PrintArgs>1153std::pair<size_t, size_t> AliasInitializer::visitImpl(1154    T value, llvm::MapVector<const void *, InProgressAliasInfo> &aliases,1155    bool canBeDeferred, PrintArgs &&...printArgs) {1156  auto [it, inserted] = aliases.try_emplace(value.getAsOpaquePointer());1157  size_t aliasIndex = std::distance(aliases.begin(), it);1158  if (!inserted) {1159    // Make sure that the alias isn't deferred if we don't permit it.1160    if (!canBeDeferred)1161      markAliasNonDeferrable(aliasIndex);1162    return {static_cast<size_t>(it->second.aliasDepth), aliasIndex};1163  }1164 1165  // Try to generate an alias for this value.1166  generateAlias(value, it->second, canBeDeferred);1167  it->second.isType = std::is_base_of_v<Type, T>;1168  it->second.canBeDeferred = canBeDeferred;1169 1170  // Print the value, capturing any nested elements that require aliases.1171  SmallVector<size_t> childAliases;1172  DummyAliasDialectAsmPrinter printer(*this, canBeDeferred, childAliases);1173  size_t maxAliasDepth =1174      printer.printAndVisitNestedAliases(value, printArgs...);1175 1176  // Make sure to recompute `it` in case the map was reallocated.1177  it = std::next(aliases.begin(), aliasIndex);1178 1179  // If we had sub elements, update to account for the depth.1180  it->second.childIndices = std::move(childAliases);1181  if (maxAliasDepth)1182    it->second.aliasDepth = maxAliasDepth + 1;1183 1184  // Propagate the alias depth of the value.1185  return {(size_t)it->second.aliasDepth, aliasIndex};1186}1187 1188void AliasInitializer::markAliasNonDeferrable(size_t aliasIndex) {1189  auto *it = std::next(aliases.begin(), aliasIndex);1190 1191  // If already marked non-deferrable stop the recursion.1192  // All children should already be marked non-deferrable as well.1193  if (!it->second.canBeDeferred)1194    return;1195 1196  it->second.canBeDeferred = false;1197 1198  // Propagate the non-deferrable flag to any child aliases.1199  for (size_t childIndex : it->second.childIndices)1200    markAliasNonDeferrable(childIndex);1201}1202 1203template <typename T>1204void AliasInitializer::generateAlias(T symbol, InProgressAliasInfo &alias,1205                                     bool canBeDeferred) {1206  SmallString<32> nameBuffer;1207 1208  OpAsmDialectInterface::AliasResult symbolInterfaceResult =1209      OpAsmDialectInterface::AliasResult::NoAlias;1210  using InterfaceT = std::conditional_t<std::is_base_of_v<Attribute, T>,1211                                        OpAsmAttrInterface, OpAsmTypeInterface>;1212  if (auto symbolInterface = dyn_cast<InterfaceT>(symbol)) {1213    symbolInterfaceResult = symbolInterface.getAlias(aliasOS);1214    if (symbolInterfaceResult != OpAsmDialectInterface::AliasResult::NoAlias) {1215      nameBuffer = std::move(aliasBuffer);1216      assert(!nameBuffer.empty() && "expected valid alias name");1217    }1218  }1219 1220  if (symbolInterfaceResult != OpAsmDialectInterface::AliasResult::FinalAlias) {1221    for (const auto &interface : interfaces) {1222      OpAsmDialectInterface::AliasResult result =1223          interface.getAlias(symbol, aliasOS);1224      if (result == OpAsmDialectInterface::AliasResult::NoAlias)1225        continue;1226      nameBuffer = std::move(aliasBuffer);1227      assert(!nameBuffer.empty() && "expected valid alias name");1228      if (result == OpAsmDialectInterface::AliasResult::FinalAlias)1229        break;1230    }1231  }1232 1233  if (nameBuffer.empty())1234    return;1235 1236  SmallString<16> tempBuffer;1237  StringRef name =1238      sanitizeIdentifier(nameBuffer, tempBuffer, /*allowedPunctChars=*/"$_-");1239  name = name.copy(aliasAllocator);1240  alias = InProgressAliasInfo(name);1241}1242 1243//===----------------------------------------------------------------------===//1244// AliasState1245//===----------------------------------------------------------------------===//1246 1247namespace {1248/// This class manages the state for type and attribute aliases.1249class AliasState {1250public:1251  // Initialize the internal aliases.1252  void1253  initialize(Operation *op, const OpPrintingFlags &printerFlags,1254             DialectInterfaceCollection<OpAsmDialectInterface> &interfaces);1255 1256  /// Get an alias for the given attribute if it has one and print it in `os`.1257  /// Returns success if an alias was printed, failure otherwise.1258  LogicalResult getAlias(Attribute attr, raw_ostream &os) const;1259 1260  /// Get an alias for the given type if it has one and print it in `os`.1261  /// Returns success if an alias was printed, failure otherwise.1262  LogicalResult getAlias(Type ty, raw_ostream &os) const;1263 1264  /// Print all of the referenced aliases that can not be resolved in a deferred1265  /// manner.1266  void printNonDeferredAliases(AsmPrinter::Impl &p, NewLineCounter &newLine) {1267    printAliases(p, newLine, /*isDeferred=*/false);1268  }1269 1270  /// Print all of the referenced aliases that support deferred resolution.1271  void printDeferredAliases(AsmPrinter::Impl &p, NewLineCounter &newLine) {1272    printAliases(p, newLine, /*isDeferred=*/true);1273  }1274 1275private:1276  /// Print all of the referenced aliases that support the provided resolution1277  /// behavior.1278  void printAliases(AsmPrinter::Impl &p, NewLineCounter &newLine,1279                    bool isDeferred);1280 1281  /// Mapping between attribute/type and alias.1282  llvm::MapVector<const void *, SymbolAlias> attrTypeToAlias;1283 1284  /// An allocator used for alias names.1285  llvm::BumpPtrAllocator aliasAllocator;1286};1287} // namespace1288 1289void AliasState::initialize(1290    Operation *op, const OpPrintingFlags &printerFlags,1291    DialectInterfaceCollection<OpAsmDialectInterface> &interfaces) {1292  AliasInitializer initializer(interfaces, aliasAllocator);1293  initializer.initialize(op, printerFlags, attrTypeToAlias);1294}1295 1296LogicalResult AliasState::getAlias(Attribute attr, raw_ostream &os) const {1297  const auto *it = attrTypeToAlias.find(attr.getAsOpaquePointer());1298  if (it == attrTypeToAlias.end())1299    return failure();1300  it->second.print(os);1301  return success();1302}1303 1304LogicalResult AliasState::getAlias(Type ty, raw_ostream &os) const {1305  const auto *it = attrTypeToAlias.find(ty.getAsOpaquePointer());1306  if (it == attrTypeToAlias.end())1307    return failure();1308  if (!it->second.isPrinted)1309    return failure();1310 1311  it->second.print(os);1312  return success();1313}1314 1315void AliasState::printAliases(AsmPrinter::Impl &p, NewLineCounter &newLine,1316                              bool isDeferred) {1317  auto filterFn = [=](const auto &aliasIt) {1318    return aliasIt.second.canBeDeferred() == isDeferred;1319  };1320  for (auto &[opaqueSymbol, alias] :1321       llvm::make_filter_range(attrTypeToAlias, filterFn)) {1322    alias.print(p.getStream());1323    p.getStream() << " = ";1324 1325    if (alias.isTypeAlias()) {1326      Type type = Type::getFromOpaquePointer(opaqueSymbol);1327      p.printTypeImpl(type);1328      alias.isPrinted = true;1329    } else {1330      // TODO: Support nested aliases in mutable attributes.1331      Attribute attr = Attribute::getFromOpaquePointer(opaqueSymbol);1332      if (attr.hasTrait<AttributeTrait::IsMutable>())1333        p.getStream() << attr;1334      else1335        p.printAttributeImpl(attr);1336    }1337 1338    p.getStream() << newLine;1339  }1340}1341 1342//===----------------------------------------------------------------------===//1343// SSANameState1344//===----------------------------------------------------------------------===//1345 1346namespace {1347/// Info about block printing: a number which is its position in the visitation1348/// order, and a name that is used to print reference to it, e.g. ^bb42.1349struct BlockInfo {1350  int ordering;1351  StringRef name;1352};1353 1354/// This class manages the state of SSA value names.1355class SSANameState {1356public:1357  /// A sentinel value used for values with names set.1358  enum : unsigned { NameSentinel = ~0U };1359 1360  SSANameState(Operation *op, const OpPrintingFlags &printerFlags);1361  SSANameState() = default;1362 1363  /// Print the SSA identifier for the given value to 'stream'. If1364  /// 'printResultNo' is true, it also presents the result number ('#' number)1365  /// of this value.1366  void printValueID(Value value, bool printResultNo, raw_ostream &stream) const;1367 1368  /// Print the operation identifier.1369  void printOperationID(Operation *op, raw_ostream &stream) const;1370 1371  /// Return the result indices for each of the result groups registered by this1372  /// operation, or empty if none exist.1373  ArrayRef<int> getOpResultGroups(Operation *op);1374 1375  /// Get the info for the given block.1376  BlockInfo getBlockInfo(Block *block);1377 1378  /// Renumber the arguments for the specified region to the same names as the1379  /// SSA values in namesToUse. See OperationPrinter::shadowRegionArgs for1380  /// details.1381  void shadowRegionArgs(Region &region, ValueRange namesToUse);1382 1383private:1384  /// Number the SSA values within the given IR unit.1385  void numberValuesInRegion(Region &region);1386  void numberValuesInBlock(Block &block);1387  void numberValuesInOp(Operation &op);1388 1389  /// Given a result of an operation 'result', find the result group head1390  /// 'lookupValue' and the result of 'result' within that group in1391  /// 'lookupResultNo'. 'lookupResultNo' is only filled in if the result group1392  /// has more than 1 result.1393  void getResultIDAndNumber(OpResult result, Value &lookupValue,1394                            std::optional<int> &lookupResultNo) const;1395 1396  /// Set a special value name for the given value.1397  void setValueName(Value value, StringRef name);1398 1399  /// Uniques the given value name within the printer. If the given name1400  /// conflicts, it is automatically renamed.1401  StringRef uniqueValueName(StringRef name);1402 1403  /// This is the value ID for each SSA value. If this returns NameSentinel,1404  /// then the valueID has an entry in valueNames.1405  DenseMap<Value, unsigned> valueIDs;1406  DenseMap<Value, StringRef> valueNames;1407 1408  /// When printing users of values, an operation without a result might1409  /// be the user. This map holds ids for such operations.1410  DenseMap<Operation *, unsigned> operationIDs;1411 1412  /// This is a map of operations that contain multiple named result groups,1413  /// i.e. there may be multiple names for the results of the operation. The1414  /// value of this map are the result numbers that start a result group.1415  DenseMap<Operation *, SmallVector<int, 1>> opResultGroups;1416 1417  /// This maps blocks to there visitation number in the current region as well1418  /// as the string representing their name.1419  DenseMap<Block *, BlockInfo> blockNames;1420 1421  /// This keeps track of all of the non-numeric names that are in flight,1422  /// allowing us to check for duplicates.1423  /// Note: the value of the map is unused.1424  llvm::ScopedHashTable<StringRef, char> usedNames;1425  llvm::BumpPtrAllocator usedNameAllocator;1426 1427  /// This is the next value ID to assign in numbering.1428  unsigned nextValueID = 0;1429  /// This is the next ID to assign to a region entry block argument.1430  unsigned nextArgumentID = 0;1431  /// This is the next ID to assign when a name conflict is detected.1432  unsigned nextConflictID = 0;1433 1434  /// These are the printing flags.  They control, eg., whether to print in1435  /// generic form.1436  OpPrintingFlags printerFlags;1437};1438} // namespace1439 1440SSANameState::SSANameState(Operation *op, const OpPrintingFlags &printerFlags)1441    : printerFlags(printerFlags) {1442  llvm::SaveAndRestore valueIDSaver(nextValueID);1443  llvm::SaveAndRestore argumentIDSaver(nextArgumentID);1444  llvm::SaveAndRestore conflictIDSaver(nextConflictID);1445 1446  // The naming context includes `nextValueID`, `nextArgumentID`,1447  // `nextConflictID` and `usedNames` scoped HashTable. This information is1448  // carried from the parent region.1449  using UsedNamesScopeTy = llvm::ScopedHashTable<StringRef, char>::ScopeTy;1450  using NamingContext =1451      std::tuple<Region *, unsigned, unsigned, unsigned, UsedNamesScopeTy *>;1452 1453  // Allocator for UsedNamesScopeTy1454  llvm::BumpPtrAllocator allocator;1455 1456  // Add a scope for the top level operation.1457  auto *topLevelNamesScope =1458      new (allocator.Allocate<UsedNamesScopeTy>()) UsedNamesScopeTy(usedNames);1459 1460  SmallVector<NamingContext, 8> nameContext;1461  for (Region &region : op->getRegions())1462    nameContext.push_back(std::make_tuple(&region, nextValueID, nextArgumentID,1463                                          nextConflictID, topLevelNamesScope));1464 1465  numberValuesInOp(*op);1466 1467  while (!nameContext.empty()) {1468    Region *region;1469    UsedNamesScopeTy *parentScope;1470 1471    if (printerFlags.shouldPrintUniqueSSAIDs())1472      // To print unique SSA IDs, ignore saved ID counts from parent regions1473      std::tie(region, std::ignore, std::ignore, std::ignore, parentScope) =1474          nameContext.pop_back_val();1475    else1476      std::tie(region, nextValueID, nextArgumentID, nextConflictID,1477               parentScope) = nameContext.pop_back_val();1478 1479    // When we switch from one subtree to another, pop the scopes(needless)1480    // until the parent scope.1481    while (usedNames.getCurScope() != parentScope) {1482      usedNames.getCurScope()->~UsedNamesScopeTy();1483      assert((usedNames.getCurScope() != nullptr || parentScope == nullptr) &&1484             "top level parentScope must be a nullptr");1485    }1486 1487    // Add a scope for the current region.1488    auto *curNamesScope = new (allocator.Allocate<UsedNamesScopeTy>())1489        UsedNamesScopeTy(usedNames);1490 1491    numberValuesInRegion(*region);1492 1493    for (Operation &op : region->getOps())1494      for (Region &region : op.getRegions())1495        nameContext.push_back(std::make_tuple(&region, nextValueID,1496                                              nextArgumentID, nextConflictID,1497                                              curNamesScope));1498  }1499 1500  // Manually remove all the scopes.1501  while (usedNames.getCurScope() != nullptr)1502    usedNames.getCurScope()->~UsedNamesScopeTy();1503}1504 1505void SSANameState::printValueID(Value value, bool printResultNo,1506                                raw_ostream &stream) const {1507  if (!value) {1508    stream << "<<NULL VALUE>>";1509    return;1510  }1511 1512  std::optional<int> resultNo;1513  auto lookupValue = value;1514 1515  // If this is an operation result, collect the head lookup value of the result1516  // group and the result number of 'result' within that group.1517  if (OpResult result = dyn_cast<OpResult>(value))1518    getResultIDAndNumber(result, lookupValue, resultNo);1519 1520  auto it = valueIDs.find(lookupValue);1521  if (it == valueIDs.end()) {1522    stream << "<<UNKNOWN SSA VALUE>>";1523    return;1524  }1525 1526  stream << '%';1527  if (it->second != NameSentinel) {1528    stream << it->second;1529  } else {1530    auto nameIt = valueNames.find(lookupValue);1531    assert(nameIt != valueNames.end() && "Didn't have a name entry?");1532    stream << nameIt->second;1533  }1534 1535  if (resultNo && printResultNo)1536    stream << '#' << *resultNo;1537}1538 1539void SSANameState::printOperationID(Operation *op, raw_ostream &stream) const {1540  auto it = operationIDs.find(op);1541  if (it == operationIDs.end()) {1542    stream << "<<UNKNOWN OPERATION>>";1543  } else {1544    stream << '%' << it->second;1545  }1546}1547 1548ArrayRef<int> SSANameState::getOpResultGroups(Operation *op) {1549  auto it = opResultGroups.find(op);1550  return it == opResultGroups.end() ? ArrayRef<int>() : it->second;1551}1552 1553BlockInfo SSANameState::getBlockInfo(Block *block) {1554  auto it = blockNames.find(block);1555  BlockInfo invalidBlock{-1, "INVALIDBLOCK"};1556  return it != blockNames.end() ? it->second : invalidBlock;1557}1558 1559void SSANameState::shadowRegionArgs(Region &region, ValueRange namesToUse) {1560  assert(!region.empty() && "cannot shadow arguments of an empty region");1561  assert(region.getNumArguments() == namesToUse.size() &&1562         "incorrect number of names passed in");1563  assert(region.getParentOp()->hasTrait<OpTrait::IsIsolatedFromAbove>() &&1564         "only KnownIsolatedFromAbove ops can shadow names");1565 1566  SmallVector<char, 16> nameStr;1567  for (unsigned i = 0, e = namesToUse.size(); i != e; ++i) {1568    auto nameToUse = namesToUse[i];1569    if (nameToUse == nullptr)1570      continue;1571    auto nameToReplace = region.getArgument(i);1572 1573    nameStr.clear();1574    llvm::raw_svector_ostream nameStream(nameStr);1575    printValueID(nameToUse, /*printResultNo=*/true, nameStream);1576 1577    // Entry block arguments should already have a pretty "arg" name.1578    assert(valueIDs[nameToReplace] == NameSentinel);1579 1580    // Use the name without the leading %.1581    auto name = StringRef(nameStream.str()).drop_front();1582 1583    // Overwrite the name.1584    valueNames[nameToReplace] = name.copy(usedNameAllocator);1585  }1586}1587 1588namespace {1589/// Try to get value name from value's location, fallback to `name`.1590StringRef maybeGetValueNameFromLoc(Value value, StringRef name) {1591  if (auto maybeNameLoc = value.getLoc()->findInstanceOf<NameLoc>())1592    return maybeNameLoc.getName();1593  return name;1594}1595} // namespace1596 1597void SSANameState::numberValuesInRegion(Region &region) {1598  // Indicates whether OpAsmOpInterface set a name.1599  bool opAsmOpInterfaceUsed = false;1600  auto setBlockArgNameFn = [&](Value arg, StringRef name) {1601    assert(!valueIDs.count(arg) && "arg numbered multiple times");1602    assert(llvm::cast<BlockArgument>(arg).getOwner()->getParent() == &region &&1603           "arg not defined in current region");1604    opAsmOpInterfaceUsed = true;1605    if (LLVM_UNLIKELY(printerFlags.shouldUseNameLocAsPrefix()))1606      name = maybeGetValueNameFromLoc(arg, name);1607    setValueName(arg, name);1608  };1609 1610  if (!printerFlags.shouldPrintGenericOpForm()) {1611    if (Operation *op = region.getParentOp()) {1612      if (auto asmInterface = dyn_cast<OpAsmOpInterface>(op))1613        asmInterface.getAsmBlockArgumentNames(region, setBlockArgNameFn);1614      // If the OpAsmOpInterface didn't set a name, get name from the type.1615      if (!opAsmOpInterfaceUsed) {1616        for (BlockArgument arg : region.getArguments()) {1617          if (auto interface = dyn_cast<OpAsmTypeInterface>(arg.getType())) {1618            interface.getAsmName(1619                [&](StringRef name) { setBlockArgNameFn(arg, name); });1620          }1621        }1622      }1623    }1624  }1625 1626  // Number the values within this region in a breadth-first order.1627  unsigned nextBlockID = 0;1628  for (auto &block : region) {1629    // Each block gets a unique ID, and all of the operations within it get1630    // numbered as well.1631    auto blockInfoIt = blockNames.insert({&block, {-1, ""}});1632    if (blockInfoIt.second) {1633      // This block hasn't been named through `getAsmBlockArgumentNames`, use1634      // default `^bbNNN` format.1635      std::string name;1636      llvm::raw_string_ostream(name) << "^bb" << nextBlockID;1637      blockInfoIt.first->second.name = StringRef(name).copy(usedNameAllocator);1638    }1639    blockInfoIt.first->second.ordering = nextBlockID++;1640 1641    numberValuesInBlock(block);1642  }1643}1644 1645void SSANameState::numberValuesInBlock(Block &block) {1646  // Number the block arguments. We give entry block arguments a special name1647  // 'arg'.1648  bool isEntryBlock = block.isEntryBlock();1649  SmallString<32> specialNameBuffer(isEntryBlock ? "arg" : "");1650  llvm::raw_svector_ostream specialName(specialNameBuffer);1651  for (auto arg : block.getArguments()) {1652    if (valueIDs.count(arg))1653      continue;1654    if (isEntryBlock) {1655      specialNameBuffer.resize(strlen("arg"));1656      specialName << nextArgumentID++;1657    }1658    StringRef specialNameStr = specialName.str();1659    if (LLVM_UNLIKELY(printerFlags.shouldUseNameLocAsPrefix()))1660      specialNameStr = maybeGetValueNameFromLoc(arg, specialNameStr);1661    setValueName(arg, specialNameStr);1662  }1663 1664  // Number the operations in this block.1665  for (auto &op : block)1666    numberValuesInOp(op);1667}1668 1669void SSANameState::numberValuesInOp(Operation &op) {1670  // Function used to set the special result names for the operation.1671  SmallVector<int, 2> resultGroups(/*Size=*/1, /*Value=*/0);1672  // Indicates whether OpAsmOpInterface set a name.1673  bool opAsmOpInterfaceUsed = false;1674  auto setResultNameFn = [&](Value result, StringRef name) {1675    assert(!valueIDs.count(result) && "result numbered multiple times");1676    assert(result.getDefiningOp() == &op && "result not defined by 'op'");1677    opAsmOpInterfaceUsed = true;1678    if (LLVM_UNLIKELY(printerFlags.shouldUseNameLocAsPrefix()))1679      name = maybeGetValueNameFromLoc(result, name);1680    setValueName(result, name);1681 1682    // Record the result number for groups not anchored at 0.1683    if (int resultNo = llvm::cast<OpResult>(result).getResultNumber())1684      resultGroups.push_back(resultNo);1685  };1686  // Operations can customize the printing of block names in OpAsmOpInterface.1687  auto setBlockNameFn = [&](Block *block, StringRef name) {1688    assert(block->getParentOp() == &op &&1689           "getAsmBlockArgumentNames callback invoked on a block not directly "1690           "nested under the current operation");1691    assert(!blockNames.count(block) && "block numbered multiple times");1692    SmallString<16> tmpBuffer{"^"};1693    name = sanitizeIdentifier(name, tmpBuffer);1694    if (name.data() != tmpBuffer.data()) {1695      tmpBuffer.append(name);1696      name = tmpBuffer.str();1697    }1698    name = name.copy(usedNameAllocator);1699    blockNames[block] = {-1, name};1700  };1701 1702  if (!printerFlags.shouldPrintGenericOpForm()) {1703    if (OpAsmOpInterface asmInterface = dyn_cast<OpAsmOpInterface>(&op)) {1704      asmInterface.getAsmBlockNames(setBlockNameFn);1705      asmInterface.getAsmResultNames(setResultNameFn);1706    }1707    if (!opAsmOpInterfaceUsed) {1708      // If the OpAsmOpInterface didn't set a name, and all results have1709      // OpAsmTypeInterface, get names from types.1710      bool allHaveOpAsmTypeInterface =1711          llvm::all_of(op.getResultTypes(), [&](Type type) {1712            return isa<OpAsmTypeInterface>(type);1713          });1714      if (allHaveOpAsmTypeInterface) {1715        for (OpResult result : op.getResults()) {1716          auto interface = cast<OpAsmTypeInterface>(result.getType());1717          interface.getAsmName(1718              [&](StringRef name) { setResultNameFn(result, name); });1719        }1720      }1721    }1722  }1723 1724  unsigned numResults = op.getNumResults();1725  if (numResults == 0) {1726    // If value users should be printed, operations with no result need an id.1727    if (printerFlags.shouldPrintValueUsers()) {1728      if (operationIDs.try_emplace(&op, nextValueID).second)1729        ++nextValueID;1730    }1731    return;1732  }1733  Value resultBegin = op.getResult(0);1734 1735  if (printerFlags.shouldUseNameLocAsPrefix() && !valueIDs.count(resultBegin)) {1736    if (auto nameLoc = resultBegin.getLoc()->findInstanceOf<NameLoc>()) {1737      setValueName(resultBegin, nameLoc.getName());1738    }1739  }1740 1741  // If the first result wasn't numbered, give it a default number.1742  if (valueIDs.try_emplace(resultBegin, nextValueID).second)1743    ++nextValueID;1744 1745  // If this operation has multiple result groups, mark it.1746  if (resultGroups.size() != 1) {1747    llvm::array_pod_sort(resultGroups.begin(), resultGroups.end());1748    opResultGroups.try_emplace(&op, std::move(resultGroups));1749  }1750}1751 1752void SSANameState::getResultIDAndNumber(1753    OpResult result, Value &lookupValue,1754    std::optional<int> &lookupResultNo) const {1755  Operation *owner = result.getOwner();1756  if (owner->getNumResults() == 1)1757    return;1758  int resultNo = result.getResultNumber();1759 1760  // If this operation has multiple result groups, we will need to find the1761  // one corresponding to this result.1762  auto resultGroupIt = opResultGroups.find(owner);1763  if (resultGroupIt == opResultGroups.end()) {1764    // If not, just use the first result.1765    lookupResultNo = resultNo;1766    lookupValue = owner->getResult(0);1767    return;1768  }1769 1770  // Find the correct index using a binary search, as the groups are ordered.1771  ArrayRef<int> resultGroups = resultGroupIt->second;1772  const auto *it = llvm::upper_bound(resultGroups, resultNo);1773  int groupResultNo = 0, groupSize = 0;1774 1775  // If there are no smaller elements, the last result group is the lookup.1776  if (it == resultGroups.end()) {1777    groupResultNo = resultGroups.back();1778    groupSize = static_cast<int>(owner->getNumResults()) - resultGroups.back();1779  } else {1780    // Otherwise, the previous element is the lookup.1781    groupResultNo = *std::prev(it);1782    groupSize = *it - groupResultNo;1783  }1784 1785  // We only record the result number for a group of size greater than 1.1786  if (groupSize != 1)1787    lookupResultNo = resultNo - groupResultNo;1788  lookupValue = owner->getResult(groupResultNo);1789}1790 1791void SSANameState::setValueName(Value value, StringRef name) {1792  // If the name is empty, the value uses the default numbering.1793  if (name.empty()) {1794    valueIDs[value] = nextValueID++;1795    return;1796  }1797 1798  valueIDs[value] = NameSentinel;1799  valueNames[value] = uniqueValueName(name);1800}1801 1802StringRef SSANameState::uniqueValueName(StringRef name) {1803  SmallString<16> tmpBuffer;1804  name = sanitizeIdentifier(name, tmpBuffer);1805 1806  // Check to see if this name is already unique.1807  if (!usedNames.count(name)) {1808    name = name.copy(usedNameAllocator);1809  } else {1810    // Otherwise, we had a conflict - probe until we find a unique name. This1811    // is guaranteed to terminate (and usually in a single iteration) because it1812    // generates new names by incrementing nextConflictID.1813    SmallString<64> probeName(name);1814    probeName.push_back('_');1815    while (true) {1816      probeName += llvm::utostr(nextConflictID++);1817      if (!usedNames.count(probeName)) {1818        name = probeName.str().copy(usedNameAllocator);1819        break;1820      }1821      probeName.resize(name.size() + 1);1822    }1823  }1824 1825  usedNames.insert(name, char());1826  return name;1827}1828 1829//===----------------------------------------------------------------------===//1830// DistinctState1831//===----------------------------------------------------------------------===//1832 1833namespace {1834/// This class manages the state for distinct attributes.1835class DistinctState {1836public:1837  /// Returns a unique identifier for the given distinct attribute.1838  uint64_t getId(DistinctAttr distinctAttr);1839 1840private:1841  uint64_t distinctCounter = 0;1842  DenseMap<DistinctAttr, uint64_t> distinctAttrMap;1843};1844} // namespace1845 1846uint64_t DistinctState::getId(DistinctAttr distinctAttr) {1847  auto [it, inserted] =1848      distinctAttrMap.try_emplace(distinctAttr, distinctCounter);1849  if (inserted)1850    distinctCounter++;1851  return it->getSecond();1852}1853 1854//===----------------------------------------------------------------------===//1855// Resources1856//===----------------------------------------------------------------------===//1857 1858AsmParsedResourceEntry::~AsmParsedResourceEntry() = default;1859AsmResourceBuilder::~AsmResourceBuilder() = default;1860AsmResourceParser::~AsmResourceParser() = default;1861AsmResourcePrinter::~AsmResourcePrinter() = default;1862 1863StringRef mlir::toString(AsmResourceEntryKind kind) {1864  switch (kind) {1865  case AsmResourceEntryKind::Blob:1866    return "blob";1867  case AsmResourceEntryKind::Bool:1868    return "bool";1869  case AsmResourceEntryKind::String:1870    return "string";1871  }1872  llvm_unreachable("unknown AsmResourceEntryKind");1873}1874 1875AsmResourceParser &FallbackAsmResourceMap::getParserFor(StringRef key) {1876  std::unique_ptr<ResourceCollection> &collection = keyToResources[key.str()];1877  if (!collection)1878    collection = std::make_unique<ResourceCollection>(key);1879  return *collection;1880}1881 1882std::vector<std::unique_ptr<AsmResourcePrinter>>1883FallbackAsmResourceMap::getPrinters() {1884  std::vector<std::unique_ptr<AsmResourcePrinter>> printers;1885  for (auto &it : keyToResources) {1886    ResourceCollection *collection = it.second.get();1887    auto buildValues = [=](Operation *op, AsmResourceBuilder &builder) {1888      return collection->buildResources(op, builder);1889    };1890    printers.emplace_back(1891        AsmResourcePrinter::fromCallable(collection->getName(), buildValues));1892  }1893  return printers;1894}1895 1896LogicalResult FallbackAsmResourceMap::ResourceCollection::parseResource(1897    AsmParsedResourceEntry &entry) {1898  switch (entry.getKind()) {1899  case AsmResourceEntryKind::Blob: {1900    FailureOr<AsmResourceBlob> blob = entry.parseAsBlob();1901    if (failed(blob))1902      return failure();1903    resources.emplace_back(entry.getKey(), std::move(*blob));1904    return success();1905  }1906  case AsmResourceEntryKind::Bool: {1907    FailureOr<bool> value = entry.parseAsBool();1908    if (failed(value))1909      return failure();1910    resources.emplace_back(entry.getKey(), *value);1911    break;1912  }1913  case AsmResourceEntryKind::String: {1914    FailureOr<std::string> str = entry.parseAsString();1915    if (failed(str))1916      return failure();1917    resources.emplace_back(entry.getKey(), std::move(*str));1918    break;1919  }1920  }1921  return success();1922}1923 1924void FallbackAsmResourceMap::ResourceCollection::buildResources(1925    Operation *op, AsmResourceBuilder &builder) const {1926  for (const auto &entry : resources) {1927    if (const auto *value = std::get_if<AsmResourceBlob>(&entry.value))1928      builder.buildBlob(entry.key, *value);1929    else if (const auto *value = std::get_if<bool>(&entry.value))1930      builder.buildBool(entry.key, *value);1931    else if (const auto *value = std::get_if<std::string>(&entry.value))1932      builder.buildString(entry.key, *value);1933    else1934      llvm_unreachable("unknown AsmResourceEntryKind");1935  }1936}1937 1938//===----------------------------------------------------------------------===//1939// AsmState1940//===----------------------------------------------------------------------===//1941 1942namespace mlir {1943namespace detail {1944class AsmStateImpl {1945public:1946  explicit AsmStateImpl(Operation *op, const OpPrintingFlags &printerFlags,1947                        AsmState::LocationMap *locationMap)1948      : interfaces(op->getContext()), nameState(op, printerFlags),1949        printerFlags(printerFlags), locationMap(locationMap) {}1950  explicit AsmStateImpl(MLIRContext *ctx, const OpPrintingFlags &printerFlags,1951                        AsmState::LocationMap *locationMap)1952      : interfaces(ctx), printerFlags(printerFlags), locationMap(locationMap) {}1953 1954  /// Initialize the alias state to enable the printing of aliases.1955  void initializeAliases(Operation *op) {1956    aliasState.initialize(op, printerFlags, interfaces);1957  }1958 1959  /// Get the state used for aliases.1960  AliasState &getAliasState() { return aliasState; }1961 1962  /// Get the state used for SSA names.1963  SSANameState &getSSANameState() { return nameState; }1964 1965  /// Get the state used for distinct attribute identifiers.1966  DistinctState &getDistinctState() { return distinctState; }1967 1968  /// Return the dialects within the context that implement1969  /// OpAsmDialectInterface.1970  DialectInterfaceCollection<OpAsmDialectInterface> &getDialectInterfaces() {1971    return interfaces;1972  }1973 1974  /// Return the non-dialect resource printers.1975  auto getResourcePrinters() {1976    return llvm::make_pointee_range(externalResourcePrinters);1977  }1978 1979  /// Get the printer flags.1980  const OpPrintingFlags &getPrinterFlags() const { return printerFlags; }1981 1982  /// Register the location, line and column, within the buffer that the given1983  /// operation was printed at.1984  void registerOperationLocation(Operation *op, unsigned line, unsigned col) {1985    if (locationMap)1986      (*locationMap)[op] = std::make_pair(line, col);1987  }1988 1989  /// Return the referenced dialect resources within the printer.1990  DenseMap<Dialect *, SetVector<AsmDialectResourceHandle>> &1991  getDialectResources() {1992    return dialectResources;1993  }1994 1995  LogicalResult pushCyclicPrinting(const void *opaquePointer) {1996    return success(cyclicPrintingStack.insert(opaquePointer));1997  }1998 1999  void popCyclicPrinting() { cyclicPrintingStack.pop_back(); }2000 2001private:2002  /// Collection of OpAsm interfaces implemented in the context.2003  DialectInterfaceCollection<OpAsmDialectInterface> interfaces;2004 2005  /// A collection of non-dialect resource printers.2006  SmallVector<std::unique_ptr<AsmResourcePrinter>> externalResourcePrinters;2007 2008  /// A set of dialect resources that were referenced during printing.2009  DenseMap<Dialect *, SetVector<AsmDialectResourceHandle>> dialectResources;2010 2011  /// The state used for attribute and type aliases.2012  AliasState aliasState;2013 2014  /// The state used for SSA value names.2015  SSANameState nameState;2016 2017  /// The state used for distinct attribute identifiers.2018  DistinctState distinctState;2019 2020  /// Flags that control op output.2021  OpPrintingFlags printerFlags;2022 2023  /// An optional location map to be populated.2024  AsmState::LocationMap *locationMap;2025 2026  /// Stack of potentially cyclic mutable attributes or type currently being2027  /// printed.2028  SetVector<const void *> cyclicPrintingStack;2029 2030  // Allow direct access to the impl fields.2031  friend AsmState;2032};2033 2034template <typename Range>2035static void printDimensionList(raw_ostream &stream, Range &&shape) {2036  llvm::interleave(2037      shape, stream,2038      [&stream](const auto &dimSize) {2039        if (ShapedType::isDynamic(dimSize))2040          stream << "?";2041        else2042          stream << dimSize;2043      },2044      "x");2045}2046 2047} // namespace detail2048} // namespace mlir2049 2050/// Verifies the operation and switches to generic op printing if verification2051/// fails. We need to do this because custom print functions may fail for2052/// invalid ops.2053static OpPrintingFlags verifyOpAndAdjustFlags(Operation *op,2054                                              OpPrintingFlags printerFlags) {2055  if (printerFlags.shouldPrintGenericOpForm() ||2056      printerFlags.shouldAssumeVerified())2057    return printerFlags;2058 2059  // Ignore errors emitted by the verifier. We check the thread id to avoid2060  // consuming other threads' errors.2061  auto parentThreadId = llvm::get_threadid();2062  ScopedDiagnosticHandler diagHandler(op->getContext(), [&](Diagnostic &diag) {2063    if (parentThreadId == llvm::get_threadid()) {2064      LLVM_DEBUG({2065        diag.print(llvm::dbgs());2066        llvm::dbgs() << "\n";2067      });2068      return success();2069    }2070    return failure();2071  });2072  if (failed(verify(op))) {2073    LDBG() << op->getName()2074           << "' failed to verify and will be printed in generic form";2075    printerFlags.printGenericOpForm();2076  }2077 2078  return printerFlags;2079}2080 2081AsmState::AsmState(Operation *op, const OpPrintingFlags &printerFlags,2082                   LocationMap *locationMap, FallbackAsmResourceMap *map)2083    : impl(std::make_unique<AsmStateImpl>(2084          op, verifyOpAndAdjustFlags(op, printerFlags), locationMap)) {2085  if (map)2086    attachFallbackResourcePrinter(*map);2087}2088AsmState::AsmState(MLIRContext *ctx, const OpPrintingFlags &printerFlags,2089                   LocationMap *locationMap, FallbackAsmResourceMap *map)2090    : impl(std::make_unique<AsmStateImpl>(ctx, printerFlags, locationMap)) {2091  if (map)2092    attachFallbackResourcePrinter(*map);2093}2094AsmState::~AsmState() = default;2095 2096const OpPrintingFlags &AsmState::getPrinterFlags() const {2097  return impl->getPrinterFlags();2098}2099 2100void AsmState::attachResourcePrinter(2101    std::unique_ptr<AsmResourcePrinter> printer) {2102  impl->externalResourcePrinters.emplace_back(std::move(printer));2103}2104 2105DenseMap<Dialect *, SetVector<AsmDialectResourceHandle>> &2106AsmState::getDialectResources() const {2107  return impl->getDialectResources();2108}2109 2110//===----------------------------------------------------------------------===//2111// AsmPrinter::Impl2112//===----------------------------------------------------------------------===//2113 2114AsmPrinter::Impl::Impl(raw_ostream &os, AsmStateImpl &state)2115    : os(os), state(state), printerFlags(state.getPrinterFlags()) {}2116 2117void AsmPrinter::Impl::printTrailingLocation(Location loc, bool allowAlias) {2118  // Check to see if we are printing debug information.2119  if (!printerFlags.shouldPrintDebugInfo())2120    return;2121 2122  os << " ";2123  printLocation(loc, /*allowAlias=*/allowAlias);2124}2125 2126void AsmPrinter::Impl::printLocationInternal(LocationAttr loc, bool pretty,2127                                             bool isTopLevel) {2128  // If this isn't a top-level location, check for an alias.2129  if (!isTopLevel && succeeded(state.getAliasState().getAlias(loc, os)))2130    return;2131 2132  TypeSwitch<LocationAttr>(loc)2133      .Case<OpaqueLoc>([&](OpaqueLoc loc) {2134        printLocationInternal(loc.getFallbackLocation(), pretty);2135      })2136      .Case<UnknownLoc>([&](UnknownLoc loc) {2137        if (pretty)2138          os << "[unknown]";2139        else2140          os << "unknown";2141      })2142      .Case<FileLineColRange>([&](FileLineColRange loc) {2143        if (pretty)2144          os << loc.getFilename().getValue();2145        else2146          printEscapedString(loc.getFilename());2147        if (loc.getEndColumn() == loc.getStartColumn() &&2148            loc.getStartLine() == loc.getEndLine()) {2149          os << ':' << loc.getStartLine() << ':' << loc.getStartColumn();2150          return;2151        }2152        if (loc.getStartLine() == loc.getEndLine()) {2153          os << ':' << loc.getStartLine() << ':' << loc.getStartColumn()2154             << " to :" << loc.getEndColumn();2155          return;2156        }2157        os << ':' << loc.getStartLine() << ':' << loc.getStartColumn() << " to "2158           << loc.getEndLine() << ':' << loc.getEndColumn();2159      })2160      .Case<NameLoc>([&](NameLoc loc) {2161        printEscapedString(loc.getName());2162 2163        // Print the child if it isn't unknown.2164        auto childLoc = loc.getChildLoc();2165        if (!llvm::isa<UnknownLoc>(childLoc)) {2166          os << '(';2167          printLocationInternal(childLoc, pretty);2168          os << ')';2169        }2170      })2171      .Case<CallSiteLoc>([&](CallSiteLoc loc) {2172        Location caller = loc.getCaller();2173        Location callee = loc.getCallee();2174        if (!pretty)2175          os << "callsite(";2176        printLocationInternal(callee, pretty);2177        if (pretty) {2178          if (llvm::isa<NameLoc>(callee)) {2179            if (llvm::isa<FileLineColLoc>(caller)) {2180              os << " at ";2181            } else {2182              os << newLine << " at ";2183            }2184          } else {2185            os << newLine << " at ";2186          }2187        } else {2188          os << " at ";2189        }2190        printLocationInternal(caller, pretty);2191        if (!pretty)2192          os << ")";2193      })2194      .Case<FusedLoc>([&](FusedLoc loc) {2195        if (!pretty)2196          os << "fused";2197        if (Attribute metadata = loc.getMetadata()) {2198          os << '<';2199          printAttribute(metadata);2200          os << '>';2201        }2202        os << '[';2203        interleaveComma(loc.getLocations(), [&](Location loc) {2204          printLocationInternal(loc, pretty);2205        });2206        os << ']';2207      })2208      .Default([&](LocationAttr loc) {2209        // Assumes that this is a dialect-specific attribute and prints it2210        // directly.2211        printAttribute(loc);2212      });2213}2214 2215/// Print a floating point value in a way that the parser will be able to2216/// round-trip losslessly.2217static void printFloatValue(const APFloat &apValue, raw_ostream &os,2218                            bool *printedHex = nullptr) {2219  // We would like to output the FP constant value in exponential notation,2220  // but we cannot do this if doing so will lose precision.  Check here to2221  // make sure that we only output it in exponential format if we can parse2222  // the value back and get the same value.2223  bool isInf = apValue.isInfinity();2224  bool isNaN = apValue.isNaN();2225  if (!isInf && !isNaN) {2226    SmallString<128> strValue;2227    apValue.toString(strValue, /*FormatPrecision=*/6, /*FormatMaxPadding=*/0,2228                     /*TruncateZero=*/false);2229 2230    // Check to make sure that the stringized number is not some string like2231    // "Inf" or NaN, that atof will accept, but the lexer will not.  Check2232    // that the string matches the "[-+]?[0-9]" regex.2233    assert(((strValue[0] >= '0' && strValue[0] <= '9') ||2234            ((strValue[0] == '-' || strValue[0] == '+') &&2235             (strValue[1] >= '0' && strValue[1] <= '9'))) &&2236           "[-+]?[0-9] regex does not match!");2237 2238    // Parse back the stringized version and check that the value is equal2239    // (i.e., there is no precision loss).2240    if (APFloat(apValue.getSemantics(), strValue).bitwiseIsEqual(apValue)) {2241      os << strValue;2242      return;2243    }2244 2245    // If it is not, use the default format of APFloat instead of the2246    // exponential notation.2247    strValue.clear();2248    apValue.toString(strValue);2249 2250    // Make sure that we can parse the default form as a float.2251    if (strValue.str().contains('.')) {2252      os << strValue;2253      return;2254    }2255  }2256 2257  // Print special values in hexadecimal format. The sign bit should be included2258  // in the literal.2259  if (printedHex)2260    *printedHex = true;2261  SmallVector<char, 16> str;2262  APInt apInt = apValue.bitcastToAPInt();2263  apInt.toString(str, /*Radix=*/16, /*Signed=*/false,2264                 /*formatAsCLiteral=*/true);2265  os << str;2266}2267 2268void AsmPrinter::Impl::printLocation(LocationAttr loc, bool allowAlias) {2269  if (printerFlags.shouldPrintDebugInfoPrettyForm())2270    return printLocationInternal(loc, /*pretty=*/true, /*isTopLevel=*/true);2271 2272  os << "loc(";2273  if (!allowAlias || failed(printAlias(loc)))2274    printLocationInternal(loc, /*pretty=*/false, /*isTopLevel=*/true);2275  os << ')';2276}2277 2278/// Returns true if the given dialect symbol data is simple enough to print in2279/// the pretty form. This is essentially when the symbol takes the form:2280///   identifier (`<` body `>`)?2281static bool isDialectSymbolSimpleEnoughForPrettyForm(StringRef symName) {2282  // The name must start with an identifier.2283  if (symName.empty() || !isalpha(symName.front()))2284    return false;2285 2286  // Ignore all the characters that are valid in an identifier in the symbol2287  // name.2288  symName = symName.drop_while(2289      [](char c) { return llvm::isAlnum(c) || c == '.' || c == '_'; });2290  if (symName.empty())2291    return true;2292 2293  // If we got to an unexpected character, then it must be a <>. Check that the2294  // rest of the symbol is wrapped within <>.2295  return symName.front() == '<' && symName.back() == '>';2296}2297 2298/// Print the given dialect symbol to the stream.2299static void printDialectSymbol(raw_ostream &os, StringRef symPrefix,2300                               StringRef dialectName, StringRef symString) {2301  os << symPrefix << dialectName;2302 2303  // If this symbol name is simple enough, print it directly in pretty form,2304  // otherwise, we print it as an escaped string.2305  if (isDialectSymbolSimpleEnoughForPrettyForm(symString)) {2306    os << '.' << symString;2307    return;2308  }2309 2310  os << '<' << symString << '>';2311}2312 2313/// Returns true if the given string can be represented as a bare identifier.2314static bool isBareIdentifier(StringRef name) {2315  // By making this unsigned, the value passed in to isalnum will always be2316  // in the range 0-255. This is important when building with MSVC because2317  // its implementation will assert. This situation can arise when dealing2318  // with UTF-8 multibyte characters.2319  if (name.empty() || (!isalpha(name[0]) && name[0] != '_'))2320    return false;2321  return llvm::all_of(name.drop_front(), [](unsigned char c) {2322    return isalnum(c) || c == '_' || c == '$' || c == '.';2323  });2324}2325 2326/// Print the given string as a keyword, or a quoted and escaped string if it2327/// has any special or non-printable characters in it.2328static void printKeywordOrString(StringRef keyword, raw_ostream &os) {2329  // If it can be represented as a bare identifier, write it directly.2330  if (isBareIdentifier(keyword)) {2331    os << keyword;2332    return;2333  }2334 2335  // Otherwise, output the keyword wrapped in quotes with proper escaping.2336  os << "\"";2337  printEscapedString(keyword, os);2338  os << '"';2339}2340 2341/// Print the given string as a symbol reference. A symbol reference is2342/// represented as a string prefixed with '@'. The reference is surrounded with2343/// ""'s and escaped if it has any special or non-printable characters in it.2344static void printSymbolReference(StringRef symbolRef, raw_ostream &os) {2345  if (symbolRef.empty()) {2346    os << "@<<INVALID EMPTY SYMBOL>>";2347    return;2348  }2349  os << '@';2350  printKeywordOrString(symbolRef, os);2351}2352 2353// Print out a valid ElementsAttr that is succinct and can represent any2354// potential shape/type, for use when eliding a large ElementsAttr.2355//2356// We choose to use a dense resource ElementsAttr literal with conspicuous2357// content to hopefully alert readers to the fact that this has been elided.2358static void printElidedElementsAttr(raw_ostream &os) {2359  os << R"(dense_resource<__elided__>)";2360}2361 2362void AsmPrinter::Impl::printResourceHandle(2363    const AsmDialectResourceHandle &resource) {2364  auto *interface = cast<OpAsmDialectInterface>(resource.getDialect());2365  ::printKeywordOrString(interface->getResourceKey(resource), os);2366  state.getDialectResources()[resource.getDialect()].insert(resource);2367}2368 2369LogicalResult AsmPrinter::Impl::printAlias(Attribute attr) {2370  return state.getAliasState().getAlias(attr, os);2371}2372 2373LogicalResult AsmPrinter::Impl::printAlias(Type type) {2374  return state.getAliasState().getAlias(type, os);2375}2376 2377void AsmPrinter::Impl::printAttribute(Attribute attr,2378                                      AttrTypeElision typeElision) {2379  if (!attr) {2380    os << "<<NULL ATTRIBUTE>>";2381    return;2382  }2383 2384  // Try to print an alias for this attribute.2385  if (succeeded(printAlias(attr)))2386    return;2387  return printAttributeImpl(attr, typeElision);2388}2389void AsmPrinter::Impl::printAttributeImpl(Attribute attr,2390                                          AttrTypeElision typeElision) {2391  if (!isa<BuiltinDialect>(attr.getDialect())) {2392    printDialectAttribute(attr);2393  } else if (auto opaqueAttr = llvm::dyn_cast<OpaqueAttr>(attr)) {2394    printDialectSymbol(os, "#", opaqueAttr.getDialectNamespace(),2395                       opaqueAttr.getAttrData());2396  } else if (llvm::isa<UnitAttr>(attr)) {2397    os << "unit";2398    return;2399  } else if (auto distinctAttr = llvm::dyn_cast<DistinctAttr>(attr)) {2400    os << "distinct[" << state.getDistinctState().getId(distinctAttr) << "]<";2401    if (!llvm::isa<UnitAttr>(distinctAttr.getReferencedAttr())) {2402      printAttribute(distinctAttr.getReferencedAttr());2403    }2404    os << '>';2405    return;2406  } else if (auto dictAttr = llvm::dyn_cast<DictionaryAttr>(attr)) {2407    os << '{';2408    interleaveComma(dictAttr.getValue(),2409                    [&](NamedAttribute attr) { printNamedAttribute(attr); });2410    os << '}';2411 2412  } else if (auto intAttr = llvm::dyn_cast<IntegerAttr>(attr)) {2413    Type intType = intAttr.getType();2414    if (intType.isSignlessInteger(1)) {2415      os << (intAttr.getValue().getBoolValue() ? "true" : "false");2416 2417      // Boolean integer attributes always elides the type.2418      return;2419    }2420 2421    // Only print attributes as unsigned if they are explicitly unsigned or are2422    // signless 1-bit values.  Indexes, signed values, and multi-bit signless2423    // values print as signed.2424    bool isUnsigned =2425        intType.isUnsignedInteger() || intType.isSignlessInteger(1);2426    intAttr.getValue().print(os, !isUnsigned);2427 2428    // IntegerAttr elides the type if I64.2429    if (typeElision == AttrTypeElision::May && intType.isSignlessInteger(64))2430      return;2431 2432  } else if (auto floatAttr = llvm::dyn_cast<FloatAttr>(attr)) {2433    bool printedHex = false;2434    printFloatValue(floatAttr.getValue(), os, &printedHex);2435 2436    // FloatAttr elides the type if F64.2437    if (typeElision == AttrTypeElision::May && floatAttr.getType().isF64() &&2438        !printedHex)2439      return;2440 2441  } else if (auto strAttr = llvm::dyn_cast<StringAttr>(attr)) {2442    printEscapedString(strAttr.getValue());2443 2444  } else if (auto arrayAttr = llvm::dyn_cast<ArrayAttr>(attr)) {2445    os << '[';2446    interleaveComma(arrayAttr.getValue(), [&](Attribute attr) {2447      printAttribute(attr, AttrTypeElision::May);2448    });2449    os << ']';2450 2451  } else if (auto affineMapAttr = llvm::dyn_cast<AffineMapAttr>(attr)) {2452    os << "affine_map<";2453    affineMapAttr.getValue().print(os);2454    os << '>';2455 2456    // AffineMap always elides the type.2457    return;2458 2459  } else if (auto integerSetAttr = llvm::dyn_cast<IntegerSetAttr>(attr)) {2460    os << "affine_set<";2461    integerSetAttr.getValue().print(os);2462    os << '>';2463 2464    // IntegerSet always elides the type.2465    return;2466 2467  } else if (auto typeAttr = llvm::dyn_cast<TypeAttr>(attr)) {2468    printType(typeAttr.getValue());2469 2470  } else if (auto refAttr = llvm::dyn_cast<SymbolRefAttr>(attr)) {2471    printSymbolReference(refAttr.getRootReference().getValue(), os);2472    for (FlatSymbolRefAttr nestedRef : refAttr.getNestedReferences()) {2473      os << "::";2474      printSymbolReference(nestedRef.getValue(), os);2475    }2476 2477  } else if (auto intOrFpEltAttr =2478                 llvm::dyn_cast<DenseIntOrFPElementsAttr>(attr)) {2479    if (printerFlags.shouldElideElementsAttr(intOrFpEltAttr)) {2480      printElidedElementsAttr(os);2481    } else {2482      os << "dense<";2483      printDenseIntOrFPElementsAttr(intOrFpEltAttr, /*allowHex=*/true);2484      os << '>';2485    }2486 2487  } else if (auto strEltAttr = llvm::dyn_cast<DenseStringElementsAttr>(attr)) {2488    if (printerFlags.shouldElideElementsAttr(strEltAttr)) {2489      printElidedElementsAttr(os);2490    } else {2491      os << "dense<";2492      printDenseStringElementsAttr(strEltAttr);2493      os << '>';2494    }2495 2496  } else if (auto sparseEltAttr = llvm::dyn_cast<SparseElementsAttr>(attr)) {2497    if (printerFlags.shouldElideElementsAttr(sparseEltAttr.getIndices()) ||2498        printerFlags.shouldElideElementsAttr(sparseEltAttr.getValues())) {2499      printElidedElementsAttr(os);2500    } else {2501      os << "sparse<";2502      DenseIntElementsAttr indices = sparseEltAttr.getIndices();2503      if (indices.getNumElements() != 0) {2504        printDenseIntOrFPElementsAttr(indices, /*allowHex=*/false);2505        os << ", ";2506        printDenseElementsAttr(sparseEltAttr.getValues(), /*allowHex=*/true);2507      }2508      os << '>';2509    }2510  } else if (auto stridedLayoutAttr = llvm::dyn_cast<StridedLayoutAttr>(attr)) {2511    stridedLayoutAttr.print(os);2512  } else if (auto denseArrayAttr = llvm::dyn_cast<DenseArrayAttr>(attr)) {2513    os << "array<";2514    printType(denseArrayAttr.getElementType());2515    if (!denseArrayAttr.empty()) {2516      os << ": ";2517      printDenseArrayAttr(denseArrayAttr);2518    }2519    os << ">";2520    return;2521  } else if (auto resourceAttr =2522                 llvm::dyn_cast<DenseResourceElementsAttr>(attr)) {2523    os << "dense_resource<";2524    printResourceHandle(resourceAttr.getRawHandle());2525    os << ">";2526  } else if (auto locAttr = llvm::dyn_cast<LocationAttr>(attr)) {2527    printLocation(locAttr);2528  } else {2529    llvm::report_fatal_error("Unknown builtin attribute");2530  }2531  // Don't print the type if we must elide it, or if it is a None type.2532  if (typeElision != AttrTypeElision::Must) {2533    if (auto typedAttr = llvm::dyn_cast<TypedAttr>(attr)) {2534      Type attrType = typedAttr.getType();2535      if (!llvm::isa<NoneType>(attrType)) {2536        os << " : ";2537        printType(attrType);2538      }2539    }2540  }2541}2542 2543/// Print the integer element of a DenseElementsAttr.2544static void printDenseIntElement(const APInt &value, raw_ostream &os,2545                                 Type type) {2546  if (type.isInteger(1))2547    os << (value.getBoolValue() ? "true" : "false");2548  else2549    value.print(os, !type.isUnsignedInteger());2550}2551 2552static void2553printDenseElementsAttrImpl(bool isSplat, ShapedType type, raw_ostream &os,2554                           function_ref<void(unsigned)> printEltFn) {2555  // Special case for 0-d and splat tensors.2556  if (isSplat)2557    return printEltFn(0);2558 2559  // Special case for degenerate tensors.2560  auto numElements = type.getNumElements();2561  if (numElements == 0)2562    return;2563 2564  // We use a mixed-radix counter to iterate through the shape. When we bump a2565  // non-least-significant digit, we emit a close bracket. When we next emit an2566  // element we re-open all closed brackets.2567 2568  // The mixed-radix counter, with radices in 'shape'.2569  int64_t rank = type.getRank();2570  SmallVector<unsigned, 4> counter(rank, 0);2571  // The number of brackets that have been opened and not closed.2572  unsigned openBrackets = 0;2573 2574  auto shape = type.getShape();2575  auto bumpCounter = [&] {2576    // Bump the least significant digit.2577    ++counter[rank - 1];2578    // Iterate backwards bubbling back the increment.2579    for (unsigned i = rank - 1; i > 0; --i)2580      if (counter[i] >= shape[i]) {2581        // Index 'i' is rolled over. Bump (i-1) and close a bracket.2582        counter[i] = 0;2583        ++counter[i - 1];2584        --openBrackets;2585        os << ']';2586      }2587  };2588 2589  for (unsigned idx = 0, e = numElements; idx != e; ++idx) {2590    if (idx != 0)2591      os << ", ";2592    while (openBrackets++ < rank)2593      os << '[';2594    openBrackets = rank;2595    printEltFn(idx);2596    bumpCounter();2597  }2598  while (openBrackets-- > 0)2599    os << ']';2600}2601 2602void AsmPrinter::Impl::printDenseElementsAttr(DenseElementsAttr attr,2603                                              bool allowHex) {2604  if (auto stringAttr = llvm::dyn_cast<DenseStringElementsAttr>(attr))2605    return printDenseStringElementsAttr(stringAttr);2606 2607  printDenseIntOrFPElementsAttr(llvm::cast<DenseIntOrFPElementsAttr>(attr),2608                                allowHex);2609}2610 2611void AsmPrinter::Impl::printDenseIntOrFPElementsAttr(2612    DenseIntOrFPElementsAttr attr, bool allowHex) {2613  auto type = attr.getType();2614  auto elementType = type.getElementType();2615 2616  // Check to see if we should format this attribute as a hex string.2617  if (allowHex && printerFlags.shouldPrintElementsAttrWithHex(attr)) {2618    ArrayRef<char> rawData = attr.getRawData();2619    if (llvm::endianness::native == llvm::endianness::big) {2620      // Convert endianess in big-endian(BE) machines. `rawData` is BE in BE2621      // machines. It is converted here to print in LE format.2622      SmallVector<char, 64> outDataVec(rawData.size());2623      MutableArrayRef<char> convRawData(outDataVec);2624      DenseIntOrFPElementsAttr::convertEndianOfArrayRefForBEmachine(2625          rawData, convRawData, type);2626      printHexString(convRawData);2627    } else {2628      printHexString(rawData);2629    }2630 2631    return;2632  }2633 2634  if (ComplexType complexTy = llvm::dyn_cast<ComplexType>(elementType)) {2635    Type complexElementType = complexTy.getElementType();2636    // Note: The if and else below had a common lambda function which invoked2637    // printDenseElementsAttrImpl. This lambda was hitting a bug in gcc 9.1,9.22638    // and hence was replaced.2639    if (llvm::isa<IntegerType>(complexElementType)) {2640      auto valueIt = attr.value_begin<std::complex<APInt>>();2641      printDenseElementsAttrImpl(attr.isSplat(), type, os, [&](unsigned index) {2642        auto complexValue = *(valueIt + index);2643        os << "(";2644        printDenseIntElement(complexValue.real(), os, complexElementType);2645        os << ",";2646        printDenseIntElement(complexValue.imag(), os, complexElementType);2647        os << ")";2648      });2649    } else {2650      auto valueIt = attr.value_begin<std::complex<APFloat>>();2651      printDenseElementsAttrImpl(attr.isSplat(), type, os, [&](unsigned index) {2652        auto complexValue = *(valueIt + index);2653        os << "(";2654        printFloatValue(complexValue.real(), os);2655        os << ",";2656        printFloatValue(complexValue.imag(), os);2657        os << ")";2658      });2659    }2660  } else if (elementType.isIntOrIndex()) {2661    auto valueIt = attr.value_begin<APInt>();2662    printDenseElementsAttrImpl(attr.isSplat(), type, os, [&](unsigned index) {2663      printDenseIntElement(*(valueIt + index), os, elementType);2664    });2665  } else {2666    assert(llvm::isa<FloatType>(elementType) && "unexpected element type");2667    auto valueIt = attr.value_begin<APFloat>();2668    printDenseElementsAttrImpl(attr.isSplat(), type, os, [&](unsigned index) {2669      printFloatValue(*(valueIt + index), os);2670    });2671  }2672}2673 2674void AsmPrinter::Impl::printDenseStringElementsAttr(2675    DenseStringElementsAttr attr) {2676  ArrayRef<StringRef> data = attr.getRawStringData();2677  auto printFn = [&](unsigned index) { printEscapedString(data[index]); };2678  printDenseElementsAttrImpl(attr.isSplat(), attr.getType(), os, printFn);2679}2680 2681void AsmPrinter::Impl::printDenseArrayAttr(DenseArrayAttr attr) {2682  Type type = attr.getElementType();2683  unsigned bitwidth = type.isInteger(1) ? 8 : type.getIntOrFloatBitWidth();2684  unsigned byteSize = bitwidth / 8;2685  ArrayRef<char> data = attr.getRawData();2686 2687  auto printElementAt = [&](unsigned i) {2688    APInt value(bitwidth, 0);2689    if (bitwidth) {2690      llvm::LoadIntFromMemory(2691          value, reinterpret_cast<const uint8_t *>(data.begin() + byteSize * i),2692          byteSize);2693    }2694    // Print the data as-is or as a float.2695    if (type.isIntOrIndex()) {2696      printDenseIntElement(value, getStream(), type);2697    } else {2698      APFloat fltVal(llvm::cast<FloatType>(type).getFloatSemantics(), value);2699      printFloatValue(fltVal, getStream());2700    }2701  };2702  llvm::interleaveComma(llvm::seq<unsigned>(0, attr.size()), getStream(),2703                        printElementAt);2704}2705 2706void AsmPrinter::Impl::printType(Type type) {2707  if (!type) {2708    os << "<<NULL TYPE>>";2709    return;2710  }2711 2712  // Try to print an alias for this type.2713  if (succeeded(printAlias(type)))2714    return;2715  return printTypeImpl(type);2716}2717 2718void AsmPrinter::Impl::printTypeImpl(Type type) {2719  TypeSwitch<Type>(type)2720      .Case<OpaqueType>([&](OpaqueType opaqueTy) {2721        printDialectSymbol(os, "!", opaqueTy.getDialectNamespace(),2722                           opaqueTy.getTypeData());2723      })2724      .Case<IndexType>([&](Type) { os << "index"; })2725      .Case<Float4E2M1FNType>([&](Type) { os << "f4E2M1FN"; })2726      .Case<Float6E2M3FNType>([&](Type) { os << "f6E2M3FN"; })2727      .Case<Float6E3M2FNType>([&](Type) { os << "f6E3M2FN"; })2728      .Case<Float8E5M2Type>([&](Type) { os << "f8E5M2"; })2729      .Case<Float8E4M3Type>([&](Type) { os << "f8E4M3"; })2730      .Case<Float8E4M3FNType>([&](Type) { os << "f8E4M3FN"; })2731      .Case<Float8E5M2FNUZType>([&](Type) { os << "f8E5M2FNUZ"; })2732      .Case<Float8E4M3FNUZType>([&](Type) { os << "f8E4M3FNUZ"; })2733      .Case<Float8E4M3B11FNUZType>([&](Type) { os << "f8E4M3B11FNUZ"; })2734      .Case<Float8E3M4Type>([&](Type) { os << "f8E3M4"; })2735      .Case<Float8E8M0FNUType>([&](Type) { os << "f8E8M0FNU"; })2736      .Case<BFloat16Type>([&](Type) { os << "bf16"; })2737      .Case<Float16Type>([&](Type) { os << "f16"; })2738      .Case<FloatTF32Type>([&](Type) { os << "tf32"; })2739      .Case<Float32Type>([&](Type) { os << "f32"; })2740      .Case<Float64Type>([&](Type) { os << "f64"; })2741      .Case<Float80Type>([&](Type) { os << "f80"; })2742      .Case<Float128Type>([&](Type) { os << "f128"; })2743      .Case<IntegerType>([&](IntegerType integerTy) {2744        if (integerTy.isSigned())2745          os << 's';2746        else if (integerTy.isUnsigned())2747          os << 'u';2748        os << 'i' << integerTy.getWidth();2749      })2750      .Case<FunctionType>([&](FunctionType funcTy) {2751        os << '(';2752        interleaveComma(funcTy.getInputs(), [&](Type ty) { printType(ty); });2753        os << ") -> ";2754        ArrayRef<Type> results = funcTy.getResults();2755        if (results.size() == 1 && !llvm::isa<FunctionType>(results[0])) {2756          printType(results[0]);2757        } else {2758          os << '(';2759          interleaveComma(results, [&](Type ty) { printType(ty); });2760          os << ')';2761        }2762      })2763      .Case<VectorType>([&](VectorType vectorTy) {2764        auto scalableDims = vectorTy.getScalableDims();2765        os << "vector<";2766        auto vShape = vectorTy.getShape();2767        unsigned lastDim = vShape.size();2768        unsigned dimIdx = 0;2769        for (dimIdx = 0; dimIdx < lastDim; dimIdx++) {2770          if (!scalableDims.empty() && scalableDims[dimIdx])2771            os << '[';2772          os << vShape[dimIdx];2773          if (!scalableDims.empty() && scalableDims[dimIdx])2774            os << ']';2775          os << 'x';2776        }2777        printType(vectorTy.getElementType());2778        os << '>';2779      })2780      .Case<RankedTensorType>([&](RankedTensorType tensorTy) {2781        os << "tensor<";2782        printDimensionList(tensorTy.getShape());2783        if (!tensorTy.getShape().empty())2784          os << 'x';2785        printType(tensorTy.getElementType());2786        // Only print the encoding attribute value if set.2787        if (tensorTy.getEncoding()) {2788          os << ", ";2789          printAttribute(tensorTy.getEncoding());2790        }2791        os << '>';2792      })2793      .Case<UnrankedTensorType>([&](UnrankedTensorType tensorTy) {2794        os << "tensor<*x";2795        printType(tensorTy.getElementType());2796        os << '>';2797      })2798      .Case<MemRefType>([&](MemRefType memrefTy) {2799        os << "memref<";2800        printDimensionList(memrefTy.getShape());2801        if (!memrefTy.getShape().empty())2802          os << 'x';2803        printType(memrefTy.getElementType());2804        MemRefLayoutAttrInterface layout = memrefTy.getLayout();2805        if (!llvm::isa<AffineMapAttr>(layout) || !layout.isIdentity()) {2806          os << ", ";2807          printAttribute(memrefTy.getLayout(), AttrTypeElision::May);2808        }2809        // Only print the memory space if it is the non-default one.2810        if (memrefTy.getMemorySpace()) {2811          os << ", ";2812          printAttribute(memrefTy.getMemorySpace(), AttrTypeElision::May);2813        }2814        os << '>';2815      })2816      .Case<UnrankedMemRefType>([&](UnrankedMemRefType memrefTy) {2817        os << "memref<*x";2818        printType(memrefTy.getElementType());2819        // Only print the memory space if it is the non-default one.2820        if (memrefTy.getMemorySpace()) {2821          os << ", ";2822          printAttribute(memrefTy.getMemorySpace(), AttrTypeElision::May);2823        }2824        os << '>';2825      })2826      .Case<ComplexType>([&](ComplexType complexTy) {2827        os << "complex<";2828        printType(complexTy.getElementType());2829        os << '>';2830      })2831      .Case<TupleType>([&](TupleType tupleTy) {2832        os << "tuple<";2833        interleaveComma(tupleTy.getTypes(),2834                        [&](Type type) { printType(type); });2835        os << '>';2836      })2837      .Case<NoneType>([&](Type) { os << "none"; })2838      .Case<GraphType>([&](GraphType graphTy) {2839        os << '(';2840        interleaveComma(graphTy.getInputs(), [&](Type ty) { printType(ty); });2841        os << ") -> ";2842        ArrayRef<Type> results = graphTy.getResults();2843        if (results.size() == 1 && !isa<FunctionType, GraphType>(results[0])) {2844          printType(results[0]);2845        } else {2846          os << '(';2847          interleaveComma(results, [&](Type ty) { printType(ty); });2848          os << ')';2849        }2850      })2851      .Default([&](Type type) { return printDialectType(type); });2852}2853 2854void AsmPrinter::Impl::printOptionalAttrDict(ArrayRef<NamedAttribute> attrs,2855                                             ArrayRef<StringRef> elidedAttrs,2856                                             bool withKeyword) {2857  // If there are no attributes, then there is nothing to be done.2858  if (attrs.empty())2859    return;2860 2861  // Functor used to print a filtered attribute list.2862  auto printFilteredAttributesFn = [&](auto filteredAttrs) {2863    // Print the 'attributes' keyword if necessary.2864    if (withKeyword)2865      os << " attributes";2866 2867    // Otherwise, print them all out in braces.2868    os << " {";2869    interleaveComma(filteredAttrs,2870                    [&](NamedAttribute attr) { printNamedAttribute(attr); });2871    os << '}';2872  };2873 2874  // If no attributes are elided, we can directly print with no filtering.2875  if (elidedAttrs.empty())2876    return printFilteredAttributesFn(attrs);2877 2878  // Otherwise, filter out any attributes that shouldn't be included.2879  llvm::SmallDenseSet<StringRef> elidedAttrsSet(elidedAttrs.begin(),2880                                                elidedAttrs.end());2881  auto filteredAttrs = llvm::make_filter_range(attrs, [&](NamedAttribute attr) {2882    return !elidedAttrsSet.contains(attr.getName().strref());2883  });2884  if (!filteredAttrs.empty())2885    printFilteredAttributesFn(filteredAttrs);2886}2887void AsmPrinter::Impl::printNamedAttribute(NamedAttribute attr) {2888  // Print the name without quotes if possible.2889  ::printKeywordOrString(attr.getName().strref(), os);2890 2891  // Pretty printing elides the attribute value for unit attributes.2892  if (llvm::isa<UnitAttr>(attr.getValue()))2893    return;2894 2895  os << " = ";2896  printAttribute(attr.getValue());2897}2898 2899void AsmPrinter::Impl::printDialectAttribute(Attribute attr) {2900  auto &dialect = attr.getDialect();2901 2902  // Ask the dialect to serialize the attribute to a string.2903  std::string attrName;2904  {2905    llvm::raw_string_ostream attrNameStr(attrName);2906    Impl subPrinter(attrNameStr, state);2907    DialectAsmPrinter printer(subPrinter);2908    dialect.printAttribute(attr, printer);2909  }2910  printDialectSymbol(os, "#", dialect.getNamespace(), attrName);2911}2912 2913void AsmPrinter::Impl::printDialectType(Type type) {2914  auto &dialect = type.getDialect();2915 2916  // Ask the dialect to serialize the type to a string.2917  std::string typeName;2918  {2919    llvm::raw_string_ostream typeNameStr(typeName);2920    Impl subPrinter(typeNameStr, state);2921    DialectAsmPrinter printer(subPrinter);2922    dialect.printType(type, printer);2923  }2924  printDialectSymbol(os, "!", dialect.getNamespace(), typeName);2925}2926 2927void AsmPrinter::Impl::printEscapedString(StringRef str) {2928  os << "\"";2929  llvm::printEscapedString(str, os);2930  os << "\"";2931}2932 2933void AsmPrinter::Impl::printHexString(StringRef str) {2934  os << "\"0x" << llvm::toHex(str) << "\"";2935}2936void AsmPrinter::Impl::printHexString(ArrayRef<char> data) {2937  printHexString(StringRef(data.data(), data.size()));2938}2939 2940LogicalResult AsmPrinter::Impl::pushCyclicPrinting(const void *opaquePointer) {2941  return state.pushCyclicPrinting(opaquePointer);2942}2943 2944void AsmPrinter::Impl::popCyclicPrinting() { state.popCyclicPrinting(); }2945 2946void AsmPrinter::Impl::printDimensionList(ArrayRef<int64_t> shape) {2947  detail::printDimensionList(os, shape);2948}2949 2950//===--------------------------------------------------------------------===//2951// AsmPrinter2952//===--------------------------------------------------------------------===//2953 2954AsmPrinter::~AsmPrinter() = default;2955 2956raw_ostream &AsmPrinter::getStream() const {2957  assert(impl && "expected AsmPrinter::getStream to be overriden");2958  return impl->getStream();2959}2960 2961/// Print the given floating point value in a stablized form.2962void AsmPrinter::printFloat(const APFloat &value) {2963  assert(impl && "expected AsmPrinter::printFloat to be overriden");2964  printFloatValue(value, impl->getStream());2965}2966 2967void AsmPrinter::printType(Type type) {2968  assert(impl && "expected AsmPrinter::printType to be overriden");2969  impl->printType(type);2970}2971 2972void AsmPrinter::printAttribute(Attribute attr) {2973  assert(impl && "expected AsmPrinter::printAttribute to be overriden");2974  impl->printAttribute(attr);2975}2976 2977LogicalResult AsmPrinter::printAlias(Attribute attr) {2978  assert(impl && "expected AsmPrinter::printAlias to be overriden");2979  return impl->printAlias(attr);2980}2981 2982LogicalResult AsmPrinter::printAlias(Type type) {2983  assert(impl && "expected AsmPrinter::printAlias to be overriden");2984  return impl->printAlias(type);2985}2986 2987void AsmPrinter::printAttributeWithoutType(Attribute attr) {2988  assert(impl &&2989         "expected AsmPrinter::printAttributeWithoutType to be overriden");2990  impl->printAttribute(attr, Impl::AttrTypeElision::Must);2991}2992 2993void AsmPrinter::printNamedAttribute(NamedAttribute attr) {2994  assert(impl && "expected AsmPrinter::printNamedAttribute to be overriden");2995  impl->printNamedAttribute(attr);2996}2997 2998void AsmPrinter::printKeywordOrString(StringRef keyword) {2999  assert(impl && "expected AsmPrinter::printKeywordOrString to be overriden");3000  ::printKeywordOrString(keyword, impl->getStream());3001}3002 3003void AsmPrinter::printString(StringRef keyword) {3004  assert(impl && "expected AsmPrinter::printString to be overriden");3005  *this << '"';3006  printEscapedString(keyword, getStream());3007  *this << '"';3008}3009 3010void AsmPrinter::printSymbolName(StringRef symbolRef) {3011  assert(impl && "expected AsmPrinter::printSymbolName to be overriden");3012  ::printSymbolReference(symbolRef, impl->getStream());3013}3014 3015void AsmPrinter::printResourceHandle(const AsmDialectResourceHandle &resource) {3016  assert(impl && "expected AsmPrinter::printResourceHandle to be overriden");3017  impl->printResourceHandle(resource);3018}3019 3020void AsmPrinter::printDimensionList(ArrayRef<int64_t> shape) {3021  detail::printDimensionList(getStream(), shape);3022}3023 3024LogicalResult AsmPrinter::pushCyclicPrinting(const void *opaquePointer) {3025  return impl->pushCyclicPrinting(opaquePointer);3026}3027 3028void AsmPrinter::popCyclicPrinting() { impl->popCyclicPrinting(); }3029 3030//===----------------------------------------------------------------------===//3031// Affine expressions and maps3032//===----------------------------------------------------------------------===//3033 3034void AsmPrinter::Impl::printAffineExpr(3035    AffineExpr expr, function_ref<void(unsigned, bool)> printValueName) {3036  printAffineExprInternal(expr, BindingStrength::Weak, printValueName);3037}3038 3039void AsmPrinter::Impl::printAffineExprInternal(3040    AffineExpr expr, BindingStrength enclosingTightness,3041    function_ref<void(unsigned, bool)> printValueName) {3042  const char *binopSpelling = nullptr;3043  switch (expr.getKind()) {3044  case AffineExprKind::SymbolId: {3045    unsigned pos = cast<AffineSymbolExpr>(expr).getPosition();3046    if (printValueName)3047      printValueName(pos, /*isSymbol=*/true);3048    else3049      os << 's' << pos;3050    return;3051  }3052  case AffineExprKind::DimId: {3053    unsigned pos = cast<AffineDimExpr>(expr).getPosition();3054    if (printValueName)3055      printValueName(pos, /*isSymbol=*/false);3056    else3057      os << 'd' << pos;3058    return;3059  }3060  case AffineExprKind::Constant:3061    os << cast<AffineConstantExpr>(expr).getValue();3062    return;3063  case AffineExprKind::Add:3064    binopSpelling = " + ";3065    break;3066  case AffineExprKind::Mul:3067    binopSpelling = " * ";3068    break;3069  case AffineExprKind::FloorDiv:3070    binopSpelling = " floordiv ";3071    break;3072  case AffineExprKind::CeilDiv:3073    binopSpelling = " ceildiv ";3074    break;3075  case AffineExprKind::Mod:3076    binopSpelling = " mod ";3077    break;3078  }3079 3080  auto binOp = cast<AffineBinaryOpExpr>(expr);3081  AffineExpr lhsExpr = binOp.getLHS();3082  AffineExpr rhsExpr = binOp.getRHS();3083 3084  // Handle tightly binding binary operators.3085  if (binOp.getKind() != AffineExprKind::Add) {3086    if (enclosingTightness == BindingStrength::Strong)3087      os << '(';3088 3089    // Pretty print multiplication with -1.3090    auto rhsConst = dyn_cast<AffineConstantExpr>(rhsExpr);3091    if (rhsConst && binOp.getKind() == AffineExprKind::Mul &&3092        rhsConst.getValue() == -1) {3093      os << "-";3094      printAffineExprInternal(lhsExpr, BindingStrength::Strong, printValueName);3095      if (enclosingTightness == BindingStrength::Strong)3096        os << ')';3097      return;3098    }3099 3100    printAffineExprInternal(lhsExpr, BindingStrength::Strong, printValueName);3101 3102    os << binopSpelling;3103    printAffineExprInternal(rhsExpr, BindingStrength::Strong, printValueName);3104 3105    if (enclosingTightness == BindingStrength::Strong)3106      os << ')';3107    return;3108  }3109 3110  // Print out special "pretty" forms for add.3111  if (enclosingTightness == BindingStrength::Strong)3112    os << '(';3113 3114  // Pretty print addition to a product that has a negative operand as a3115  // subtraction.3116  if (auto rhs = dyn_cast<AffineBinaryOpExpr>(rhsExpr)) {3117    if (rhs.getKind() == AffineExprKind::Mul) {3118      AffineExpr rrhsExpr = rhs.getRHS();3119      if (auto rrhs = dyn_cast<AffineConstantExpr>(rrhsExpr)) {3120        if (rrhs.getValue() == -1) {3121          printAffineExprInternal(lhsExpr, BindingStrength::Weak,3122                                  printValueName);3123          os << " - ";3124          if (rhs.getLHS().getKind() == AffineExprKind::Add) {3125            printAffineExprInternal(rhs.getLHS(), BindingStrength::Strong,3126                                    printValueName);3127          } else {3128            printAffineExprInternal(rhs.getLHS(), BindingStrength::Weak,3129                                    printValueName);3130          }3131 3132          if (enclosingTightness == BindingStrength::Strong)3133            os << ')';3134          return;3135        }3136 3137        if (rrhs.getValue() < -1) {3138          printAffineExprInternal(lhsExpr, BindingStrength::Weak,3139                                  printValueName);3140          os << " - ";3141          printAffineExprInternal(rhs.getLHS(), BindingStrength::Strong,3142                                  printValueName);3143          os << " * " << -rrhs.getValue();3144          if (enclosingTightness == BindingStrength::Strong)3145            os << ')';3146          return;3147        }3148      }3149    }3150  }3151 3152  // Pretty print addition to a negative number as a subtraction.3153  if (auto rhsConst = dyn_cast<AffineConstantExpr>(rhsExpr)) {3154    if (rhsConst.getValue() < 0) {3155      printAffineExprInternal(lhsExpr, BindingStrength::Weak, printValueName);3156      os << " - " << -rhsConst.getValue();3157      if (enclosingTightness == BindingStrength::Strong)3158        os << ')';3159      return;3160    }3161  }3162 3163  printAffineExprInternal(lhsExpr, BindingStrength::Weak, printValueName);3164 3165  os << " + ";3166  printAffineExprInternal(rhsExpr, BindingStrength::Weak, printValueName);3167 3168  if (enclosingTightness == BindingStrength::Strong)3169    os << ')';3170}3171 3172void AsmPrinter::Impl::printAffineConstraint(AffineExpr expr, bool isEq) {3173  printAffineExprInternal(expr, BindingStrength::Weak);3174  isEq ? os << " == 0" : os << " >= 0";3175}3176 3177void AsmPrinter::Impl::printAffineMap(AffineMap map) {3178  // Dimension identifiers.3179  os << '(';3180  for (int i = 0; i < (int)map.getNumDims() - 1; ++i)3181    os << 'd' << i << ", ";3182  if (map.getNumDims() >= 1)3183    os << 'd' << map.getNumDims() - 1;3184  os << ')';3185 3186  // Symbolic identifiers.3187  if (map.getNumSymbols() != 0) {3188    os << '[';3189    for (unsigned i = 0; i < map.getNumSymbols() - 1; ++i)3190      os << 's' << i << ", ";3191    if (map.getNumSymbols() >= 1)3192      os << 's' << map.getNumSymbols() - 1;3193    os << ']';3194  }3195 3196  // Result affine expressions.3197  os << " -> (";3198  interleaveComma(map.getResults(),3199                  [&](AffineExpr expr) { printAffineExpr(expr); });3200  os << ')';3201}3202 3203void AsmPrinter::Impl::printIntegerSet(IntegerSet set) {3204  // Dimension identifiers.3205  os << '(';3206  for (unsigned i = 1; i < set.getNumDims(); ++i)3207    os << 'd' << i - 1 << ", ";3208  if (set.getNumDims() >= 1)3209    os << 'd' << set.getNumDims() - 1;3210  os << ')';3211 3212  // Symbolic identifiers.3213  if (set.getNumSymbols() != 0) {3214    os << '[';3215    for (unsigned i = 0; i < set.getNumSymbols() - 1; ++i)3216      os << 's' << i << ", ";3217    if (set.getNumSymbols() >= 1)3218      os << 's' << set.getNumSymbols() - 1;3219    os << ']';3220  }3221 3222  // Print constraints.3223  os << " : (";3224  int numConstraints = set.getNumConstraints();3225  for (int i = 1; i < numConstraints; ++i) {3226    printAffineConstraint(set.getConstraint(i - 1), set.isEq(i - 1));3227    os << ", ";3228  }3229  if (numConstraints >= 1)3230    printAffineConstraint(set.getConstraint(numConstraints - 1),3231                          set.isEq(numConstraints - 1));3232  os << ')';3233}3234 3235//===----------------------------------------------------------------------===//3236// OperationPrinter3237//===----------------------------------------------------------------------===//3238 3239namespace {3240/// This class contains the logic for printing operations, regions, and blocks.3241class OperationPrinter : public AsmPrinter::Impl, private OpAsmPrinter {3242public:3243  using Impl = AsmPrinter::Impl;3244  using Impl::printType;3245 3246  explicit OperationPrinter(raw_ostream &os, AsmStateImpl &state)3247      : Impl(os, state), OpAsmPrinter(static_cast<Impl &>(*this)) {}3248 3249  /// Print the given top-level operation.3250  void printTopLevelOperation(Operation *op);3251 3252  /// Print the given operation, including its left-hand side and its right-hand3253  /// side, with its indent and location.3254  void printFullOpWithIndentAndLoc(Operation *op);3255  /// Print the given operation, including its left-hand side and its right-hand3256  /// side, but not including indentation and location.3257  void printFullOp(Operation *op);3258  /// Print the right-hand size of the given operation in the custom or generic3259  /// form.3260  void printCustomOrGenericOp(Operation *op) override;3261  /// Print the right-hand side of the given operation in the generic form.3262  void printGenericOp(Operation *op, bool printOpName) override;3263 3264  /// Print the name of the given block.3265  void printBlockName(Block *block);3266 3267  /// Print the given block. If 'printBlockArgs' is false, the arguments of the3268  /// block are not printed. If 'printBlockTerminator' is false, the terminator3269  /// operation of the block is not printed.3270  void print(Block *block, bool printBlockArgs = true,3271             bool printBlockTerminator = true);3272 3273  /// Print the ID of the given value, optionally with its result number.3274  void printValueID(Value value, bool printResultNo = true,3275                    raw_ostream *streamOverride = nullptr) const;3276 3277  /// Print the ID of the given operation.3278  void printOperationID(Operation *op,3279                        raw_ostream *streamOverride = nullptr) const;3280 3281  //===--------------------------------------------------------------------===//3282  // OpAsmPrinter methods3283  //===--------------------------------------------------------------------===//3284 3285  /// Print a loc(...) specifier if printing debug info is enabled. Locations3286  /// may be deferred with an alias.3287  void printOptionalLocationSpecifier(Location loc) override {3288    printTrailingLocation(loc);3289  }3290 3291  /// Print a newline and indent the printer to the start of the current3292  /// operation.3293  void printNewline() override {3294    os << newLine;3295    os.indent(currentIndent);3296  }3297 3298  /// Increase indentation.3299  void increaseIndent() override { currentIndent += indentWidth; }3300 3301  /// Decrease indentation.3302  void decreaseIndent() override { currentIndent -= indentWidth; }3303 3304  /// Print a block argument in the usual format of:3305  ///   %ssaName : type {attr1=42} loc("here")3306  /// where location printing is controlled by the standard internal option.3307  /// You may pass omitType=true to not print a type, and pass an empty3308  /// attribute list if you don't care for attributes.3309  void printRegionArgument(BlockArgument arg,3310                           ArrayRef<NamedAttribute> argAttrs = {},3311                           bool omitType = false) override;3312 3313  /// Print the ID for the given value.3314  void printOperand(Value value) override { printValueID(value); }3315  void printOperand(Value value, raw_ostream &os) override {3316    printValueID(value, /*printResultNo=*/true, &os);3317  }3318 3319  /// Print an optional attribute dictionary with a given set of elided values.3320  void printOptionalAttrDict(ArrayRef<NamedAttribute> attrs,3321                             ArrayRef<StringRef> elidedAttrs = {}) override {3322    Impl::printOptionalAttrDict(attrs, elidedAttrs);3323  }3324  void printOptionalAttrDictWithKeyword(3325      ArrayRef<NamedAttribute> attrs,3326      ArrayRef<StringRef> elidedAttrs = {}) override {3327    Impl::printOptionalAttrDict(attrs, elidedAttrs,3328                                /*withKeyword=*/true);3329  }3330 3331  /// Print the given successor.3332  void printSuccessor(Block *successor) override;3333 3334  /// Print an operation successor with the operands used for the block3335  /// arguments.3336  void printSuccessorAndUseList(Block *successor,3337                                ValueRange succOperands) override;3338 3339  /// Print the given region.3340  void printRegion(Region &region, bool printEntryBlockArgs,3341                   bool printBlockTerminators, bool printEmptyBlock) override;3342 3343  /// Renumber the arguments for the specified region to the same names as the3344  /// SSA values in namesToUse. This may only be used for IsolatedFromAbove3345  /// operations. If any entry in namesToUse is null, the corresponding3346  /// argument name is left alone.3347  void shadowRegionArgs(Region &region, ValueRange namesToUse) override {3348    state.getSSANameState().shadowRegionArgs(region, namesToUse);3349  }3350 3351  /// Print the given affine map with the symbol and dimension operands printed3352  /// inline with the map.3353  void printAffineMapOfSSAIds(AffineMapAttr mapAttr,3354                              ValueRange operands) override;3355 3356  /// Print the given affine expression with the symbol and dimension operands3357  /// printed inline with the expression.3358  void printAffineExprOfSSAIds(AffineExpr expr, ValueRange dimOperands,3359                               ValueRange symOperands) override;3360 3361  /// Print users of this operation or id of this operation if it has no result.3362  void printUsersComment(Operation *op);3363 3364  /// Print users of this block arg.3365  void printUsersComment(BlockArgument arg);3366 3367  /// Print the users of a value.3368  void printValueUsers(Value value);3369 3370  /// Print either the ids of the result values or the id of the operation if3371  /// the operation has no results.3372  void printUserIDs(Operation *user, bool prefixComma = false);3373 3374private:3375  /// This class represents a resource builder implementation for the MLIR3376  /// textual assembly format.3377  class ResourceBuilder : public AsmResourceBuilder {3378  public:3379    using ValueFn = function_ref<void(raw_ostream &)>;3380    using PrintFn = function_ref<void(StringRef, ValueFn)>;3381 3382    ResourceBuilder(PrintFn printFn) : printFn(printFn) {}3383    ~ResourceBuilder() override = default;3384 3385    void buildBool(StringRef key, bool data) final {3386      printFn(key, [&](raw_ostream &os) { os << (data ? "true" : "false"); });3387    }3388 3389    void buildString(StringRef key, StringRef data) final {3390      printFn(key, [&](raw_ostream &os) {3391        os << "\"";3392        llvm::printEscapedString(data, os);3393        os << "\"";3394      });3395    }3396 3397    void buildBlob(StringRef key, ArrayRef<char> data,3398                   uint32_t dataAlignment) final {3399      printFn(key, [&](raw_ostream &os) {3400        // Store the blob in a hex string containing the alignment and the data.3401        llvm::support::ulittle32_t dataAlignmentLE(dataAlignment);3402        os << "\"0x"3403           << llvm::toHex(StringRef(reinterpret_cast<char *>(&dataAlignmentLE),3404                                    sizeof(dataAlignment)))3405           << llvm::toHex(StringRef(data.data(), data.size())) << "\"";3406      });3407    }3408 3409  private:3410    PrintFn printFn;3411  };3412 3413  /// Print the metadata dictionary for the file, eliding it if it is empty.3414  void printFileMetadataDictionary(Operation *op);3415 3416  /// Print the resource sections for the file metadata dictionary.3417  /// `checkAddMetadataDict` is used to indicate that metadata is going to be3418  /// added, and the file metadata dictionary should be started if it hasn't3419  /// yet.3420  void printResourceFileMetadata(function_ref<void()> checkAddMetadataDict,3421                                 Operation *op);3422 3423  // Contains the stack of default dialects to use when printing regions.3424  // A new dialect is pushed to the stack before parsing regions nested under an3425  // operation implementing `OpAsmOpInterface`, and popped when done. At the3426  // top-level we start with "builtin" as the default, so that the top-level3427  // `module` operation prints as-is.3428  SmallVector<StringRef> defaultDialectStack{"builtin"};3429 3430  /// The number of spaces used for indenting nested operations.3431  const static unsigned indentWidth = 2;3432 3433  // This is the current indentation level for nested structures.3434  unsigned currentIndent = 0;3435};3436} // namespace3437 3438void OperationPrinter::printTopLevelOperation(Operation *op) {3439  // Output the aliases at the top level that can't be deferred.3440  state.getAliasState().printNonDeferredAliases(*this, newLine);3441 3442  // Print the module.3443  printFullOpWithIndentAndLoc(op);3444  os << newLine;3445 3446  // Output the aliases at the top level that can be deferred.3447  state.getAliasState().printDeferredAliases(*this, newLine);3448 3449  // Output any file level metadata.3450  printFileMetadataDictionary(op);3451}3452 3453void OperationPrinter::printFileMetadataDictionary(Operation *op) {3454  bool sawMetadataEntry = false;3455  auto checkAddMetadataDict = [&] {3456    if (!std::exchange(sawMetadataEntry, true))3457      os << newLine << "{-#" << newLine;3458  };3459 3460  // Add the various types of metadata.3461  printResourceFileMetadata(checkAddMetadataDict, op);3462 3463  // If the file dictionary exists, close it.3464  if (sawMetadataEntry)3465    os << newLine << "#-}" << newLine;3466}3467 3468void OperationPrinter::printResourceFileMetadata(3469    function_ref<void()> checkAddMetadataDict, Operation *op) {3470  // Functor used to add data entries to the file metadata dictionary.3471  bool hadResource = false;3472  bool needResourceComma = false;3473  bool needEntryComma = false;3474  auto processProvider = [&](StringRef dictName, StringRef name, auto &provider,3475                             auto &&...providerArgs) {3476    bool hadEntry = false;3477    auto printFn = [&](StringRef key, ResourceBuilder::ValueFn valueFn) {3478      checkAddMetadataDict();3479 3480      std::string resourceStr;3481      auto printResourceStr = [&](raw_ostream &os) { os << resourceStr; };3482      std::optional<uint64_t> charLimit =3483          printerFlags.getLargeResourceStringLimit();3484      if (charLimit.has_value()) {3485        // Don't compute resourceStr when charLimit is 0.3486        if (charLimit.value() == 0)3487          return;3488 3489        llvm::raw_string_ostream ss(resourceStr);3490        valueFn(ss);3491 3492        // Only print entry if its string is small enough.3493        if (resourceStr.size() > charLimit.value())3494          return;3495 3496        // Don't recompute resourceStr when valueFn is called below.3497        valueFn = printResourceStr;3498      }3499 3500      // Emit the top-level resource entry if we haven't yet.3501      if (!std::exchange(hadResource, true)) {3502        if (needResourceComma)3503          os << "," << newLine;3504        os << "  " << dictName << "_resources: {" << newLine;3505      }3506      // Emit the parent resource entry if we haven't yet.3507      if (!std::exchange(hadEntry, true)) {3508        if (needEntryComma)3509          os << "," << newLine;3510        os << "    " << name << ": {" << newLine;3511      } else {3512        os << "," << newLine;3513      }3514      os << "      ";3515      ::printKeywordOrString(key, os);3516      os << ": ";3517      // Call printResourceStr or original valueFn, depending on charLimit.3518      valueFn(os);3519    };3520    ResourceBuilder entryBuilder(printFn);3521    provider.buildResources(op, providerArgs..., entryBuilder);3522 3523    needEntryComma |= hadEntry;3524    if (hadEntry)3525      os << newLine << "    }";3526  };3527 3528  // Print the `dialect_resources` section if we have any dialects with3529  // resources.3530  for (const OpAsmDialectInterface &interface : state.getDialectInterfaces()) {3531    auto &dialectResources = state.getDialectResources();3532    StringRef name = interface.getDialect()->getNamespace();3533    auto it = dialectResources.find(interface.getDialect());3534    if (it != dialectResources.end())3535      processProvider("dialect", name, interface, it->second);3536    else3537      processProvider("dialect", name, interface,3538                      SetVector<AsmDialectResourceHandle>());3539  }3540  if (hadResource)3541    os << newLine << "  }";3542 3543  // Print the `external_resources` section if we have any external clients with3544  // resources.3545  needEntryComma = false;3546  needResourceComma = hadResource;3547  hadResource = false;3548  for (const auto &printer : state.getResourcePrinters())3549    processProvider("external", printer.getName(), printer);3550  if (hadResource)3551    os << newLine << "  }";3552}3553 3554/// Print a block argument in the usual format of:3555///   %ssaName : type {attr1=42} loc("here")3556/// where location printing is controlled by the standard internal option.3557/// You may pass omitType=true to not print a type, and pass an empty3558/// attribute list if you don't care for attributes.3559void OperationPrinter::printRegionArgument(BlockArgument arg,3560                                           ArrayRef<NamedAttribute> argAttrs,3561                                           bool omitType) {3562  printOperand(arg);3563  if (!omitType) {3564    os << ": ";3565    printType(arg.getType());3566  }3567  printOptionalAttrDict(argAttrs);3568  // TODO: We should allow location aliases on block arguments.3569  printTrailingLocation(arg.getLoc(), /*allowAlias*/ false);3570}3571 3572void OperationPrinter::printFullOpWithIndentAndLoc(Operation *op) {3573  // Track the location of this operation.3574  state.registerOperationLocation(op, newLine.curLine, currentIndent);3575 3576  os.indent(currentIndent);3577  printFullOp(op);3578  printTrailingLocation(op->getLoc());3579  if (printerFlags.shouldPrintValueUsers())3580    printUsersComment(op);3581}3582 3583void OperationPrinter::printFullOp(Operation *op) {3584  if (size_t numResults = op->getNumResults()) {3585    auto printResultGroup = [&](size_t resultNo, size_t resultCount) {3586      printValueID(op->getResult(resultNo), /*printResultNo=*/false);3587      if (resultCount > 1)3588        os << ':' << resultCount;3589    };3590 3591    // Check to see if this operation has multiple result groups.3592    ArrayRef<int> resultGroups = state.getSSANameState().getOpResultGroups(op);3593    if (!resultGroups.empty()) {3594      // Interleave the groups excluding the last one, this one will be handled3595      // separately.3596      interleaveComma(llvm::seq<int>(0, resultGroups.size() - 1), [&](int i) {3597        printResultGroup(resultGroups[i],3598                         resultGroups[i + 1] - resultGroups[i]);3599      });3600      os << ", ";3601      printResultGroup(resultGroups.back(), numResults - resultGroups.back());3602 3603    } else {3604      printResultGroup(/*resultNo=*/0, /*resultCount=*/numResults);3605    }3606 3607    os << " = ";3608  }3609 3610  printCustomOrGenericOp(op);3611}3612 3613void OperationPrinter::printUsersComment(Operation *op) {3614  unsigned numResults = op->getNumResults();3615  if (!numResults && op->getNumOperands()) {3616    os << " // id: ";3617    printOperationID(op);3618  } else if (numResults && op->use_empty()) {3619    os << " // unused";3620  } else if (numResults && !op->use_empty()) {3621    // Print "user" if the operation has one result used to compute one other3622    // result, or is used in one operation with no result.3623    unsigned usedInNResults = 0;3624    unsigned usedInNOperations = 0;3625    SmallPtrSet<Operation *, 1> userSet;3626    for (Operation *user : op->getUsers()) {3627      if (userSet.insert(user).second) {3628        ++usedInNOperations;3629        usedInNResults += user->getNumResults();3630      }3631    }3632 3633    // We already know that users is not empty.3634    bool exactlyOneUniqueUse =3635        usedInNResults <= 1 && usedInNOperations <= 1 && numResults == 1;3636    os << " // " << (exactlyOneUniqueUse ? "user" : "users") << ": ";3637    bool shouldPrintBrackets = numResults > 1;3638    auto printOpResult = [&](OpResult opResult) {3639      if (shouldPrintBrackets)3640        os << "(";3641      printValueUsers(opResult);3642      if (shouldPrintBrackets)3643        os << ")";3644    };3645 3646    interleaveComma(op->getResults(), printOpResult);3647  }3648}3649 3650void OperationPrinter::printUsersComment(BlockArgument arg) {3651  os << "// ";3652  printValueID(arg);3653  if (arg.use_empty()) {3654    os << " is unused";3655  } else {3656    os << " is used by ";3657    printValueUsers(arg);3658  }3659  os << newLine;3660}3661 3662void OperationPrinter::printValueUsers(Value value) {3663  if (value.use_empty())3664    os << "unused";3665 3666  // One value might be used as the operand of an operation more than once.3667  // Only print the operations results once in that case.3668  SmallPtrSet<Operation *, 1> userSet;3669  for (auto [index, user] : enumerate(value.getUsers())) {3670    if (userSet.insert(user).second)3671      printUserIDs(user, index);3672  }3673}3674 3675void OperationPrinter::printUserIDs(Operation *user, bool prefixComma) {3676  if (prefixComma)3677    os << ", ";3678 3679  if (!user->getNumResults()) {3680    printOperationID(user);3681  } else {3682    interleaveComma(user->getResults(),3683                    [this](Value result) { printValueID(result); });3684  }3685}3686 3687void OperationPrinter::printCustomOrGenericOp(Operation *op) {3688  // If requested, always print the generic form.3689  if (!printerFlags.shouldPrintGenericOpForm()) {3690    // Check to see if this is a known operation. If so, use the registered3691    // custom printer hook.3692    if (auto opInfo = op->getRegisteredInfo()) {3693      opInfo->printAssembly(op, *this, defaultDialectStack.back());3694      return;3695    }3696    // Otherwise try to dispatch to the dialect, if available.3697    if (Dialect *dialect = op->getDialect()) {3698      if (auto opPrinter = dialect->getOperationPrinter(op)) {3699        // Print the op name first.3700        StringRef name = op->getName().getStringRef();3701        // Only drop the default dialect prefix when it cannot lead to3702        // ambiguities.3703        if (name.count('.') == 1)3704          name.consume_front((defaultDialectStack.back() + ".").str());3705        os << name;3706 3707        // Print the rest of the op now.3708        opPrinter(op, *this);3709        return;3710      }3711    }3712  }3713 3714  // Otherwise print with the generic assembly form.3715  printGenericOp(op, /*printOpName=*/true);3716}3717 3718void OperationPrinter::printGenericOp(Operation *op, bool printOpName) {3719  if (printOpName)3720    printEscapedString(op->getName().getStringRef());3721  os << '(';3722  interleaveComma(op->getOperands(), [&](Value value) { printValueID(value); });3723  os << ')';3724 3725  // For terminators, print the list of successors and their operands.3726  if (op->getNumSuccessors() != 0) {3727    os << '[';3728    interleaveComma(op->getSuccessors(),3729                    [&](Block *successor) { printBlockName(successor); });3730    os << ']';3731  }3732 3733  // Print the properties.3734  if (Attribute prop = op->getPropertiesAsAttribute()) {3735    os << " <";3736    Impl::printAttribute(prop);3737    os << '>';3738  }3739 3740  // Print regions.3741  if (op->getNumRegions() != 0) {3742    os << " (";3743    interleaveComma(op->getRegions(), [&](Region &region) {3744      printRegion(region, /*printEntryBlockArgs=*/true,3745                  /*printBlockTerminators=*/true, /*printEmptyBlock=*/true);3746    });3747    os << ')';3748  }3749 3750  printOptionalAttrDict(op->getPropertiesStorage()3751                            ? llvm::to_vector(op->getDiscardableAttrs())3752                            : op->getAttrs());3753 3754  // Print the type signature of the operation.3755  os << " : ";3756  printFunctionalType(op);3757}3758 3759void OperationPrinter::printBlockName(Block *block) {3760  os << state.getSSANameState().getBlockInfo(block).name;3761}3762 3763void OperationPrinter::print(Block *block, bool printBlockArgs,3764                             bool printBlockTerminator) {3765  // Print the block label and argument list if requested.3766  if (printBlockArgs) {3767    os.indent(currentIndent);3768    printBlockName(block);3769 3770    // Print the argument list if non-empty.3771    if (!block->args_empty()) {3772      os << '(';3773      interleaveComma(block->getArguments(), [&](BlockArgument arg) {3774        printValueID(arg);3775        os << ": ";3776        printType(arg.getType());3777        // TODO: We should allow location aliases on block arguments.3778        printTrailingLocation(arg.getLoc(), /*allowAlias*/ false);3779      });3780      os << ')';3781    }3782    os << ':';3783 3784    // Print out some context information about the predecessors of this block.3785    if (!block->getParent()) {3786      os << "  // block is not in a region!";3787    } else if (block->hasNoPredecessors()) {3788      if (!block->isEntryBlock())3789        os << "  // no predecessors";3790    } else if (auto *pred = block->getSinglePredecessor()) {3791      os << "  // pred: ";3792      printBlockName(pred);3793    } else {3794      // We want to print the predecessors in a stable order, not in3795      // whatever order the use-list is in, so gather and sort them.3796      SmallVector<BlockInfo, 4> predIDs;3797      for (auto *pred : block->getPredecessors())3798        predIDs.push_back(state.getSSANameState().getBlockInfo(pred));3799      llvm::sort(predIDs, [](BlockInfo lhs, BlockInfo rhs) {3800        return lhs.ordering < rhs.ordering;3801      });3802 3803      os << "  // " << predIDs.size() << " preds: ";3804 3805      interleaveComma(predIDs, [&](BlockInfo pred) { os << pred.name; });3806    }3807    os << newLine;3808  }3809 3810  currentIndent += indentWidth;3811 3812  if (printerFlags.shouldPrintValueUsers()) {3813    for (BlockArgument arg : block->getArguments()) {3814      os.indent(currentIndent);3815      printUsersComment(arg);3816    }3817  }3818 3819  bool hasTerminator =3820      !block->empty() && block->back().hasTrait<OpTrait::IsTerminator>();3821  auto range = llvm::make_range(3822      block->begin(),3823      std::prev(block->end(),3824                (!hasTerminator || printBlockTerminator) ? 0 : 1));3825  for (auto &op : range) {3826    printFullOpWithIndentAndLoc(&op);3827    os << newLine;3828  }3829  currentIndent -= indentWidth;3830}3831 3832void OperationPrinter::printValueID(Value value, bool printResultNo,3833                                    raw_ostream *streamOverride) const {3834  state.getSSANameState().printValueID(value, printResultNo,3835                                       streamOverride ? *streamOverride : os);3836}3837 3838void OperationPrinter::printOperationID(Operation *op,3839                                        raw_ostream *streamOverride) const {3840  state.getSSANameState().printOperationID(op, streamOverride ? *streamOverride3841                                                              : os);3842}3843 3844void OperationPrinter::printSuccessor(Block *successor) {3845  printBlockName(successor);3846}3847 3848void OperationPrinter::printSuccessorAndUseList(Block *successor,3849                                                ValueRange succOperands) {3850  printBlockName(successor);3851  if (succOperands.empty())3852    return;3853 3854  os << '(';3855  interleaveComma(succOperands,3856                  [this](Value operand) { printValueID(operand); });3857  os << " : ";3858  interleaveComma(succOperands,3859                  [this](Value operand) { printType(operand.getType()); });3860  os << ')';3861}3862 3863void OperationPrinter::printRegion(Region &region, bool printEntryBlockArgs,3864                                   bool printBlockTerminators,3865                                   bool printEmptyBlock) {3866  if (printerFlags.shouldSkipRegions()) {3867    os << "{...}";3868    return;3869  }3870  os << "{" << newLine;3871  if (!region.empty()) {3872    auto restoreDefaultDialect =3873        llvm::make_scope_exit([&]() { defaultDialectStack.pop_back(); });3874    if (auto iface = dyn_cast<OpAsmOpInterface>(region.getParentOp()))3875      defaultDialectStack.push_back(iface.getDefaultDialect());3876    else3877      defaultDialectStack.push_back("");3878 3879    auto *entryBlock = &region.front();3880    // Force printing the block header if printEmptyBlock is set and the block3881    // is empty or if printEntryBlockArgs is set and there are arguments to3882    // print.3883    bool shouldAlwaysPrintBlockHeader =3884        (printEmptyBlock && entryBlock->empty()) ||3885        (printEntryBlockArgs && entryBlock->getNumArguments() != 0);3886    print(entryBlock, shouldAlwaysPrintBlockHeader, printBlockTerminators);3887    for (auto &b : llvm::drop_begin(region.getBlocks(), 1))3888      print(&b);3889  }3890  os.indent(currentIndent) << "}";3891}3892 3893void OperationPrinter::printAffineMapOfSSAIds(AffineMapAttr mapAttr,3894                                              ValueRange operands) {3895  if (!mapAttr) {3896    os << "<<NULL AFFINE MAP>>";3897    return;3898  }3899  AffineMap map = mapAttr.getValue();3900  unsigned numDims = map.getNumDims();3901  auto printValueName = [&](unsigned pos, bool isSymbol) {3902    unsigned index = isSymbol ? numDims + pos : pos;3903    assert(index < operands.size());3904    if (isSymbol)3905      os << "symbol(";3906    printValueID(operands[index]);3907    if (isSymbol)3908      os << ')';3909  };3910 3911  interleaveComma(map.getResults(), [&](AffineExpr expr) {3912    printAffineExpr(expr, printValueName);3913  });3914}3915 3916void OperationPrinter::printAffineExprOfSSAIds(AffineExpr expr,3917                                               ValueRange dimOperands,3918                                               ValueRange symOperands) {3919  auto printValueName = [&](unsigned pos, bool isSymbol) {3920    if (!isSymbol)3921      return printValueID(dimOperands[pos]);3922    os << "symbol(";3923    printValueID(symOperands[pos]);3924    os << ')';3925  };3926  printAffineExpr(expr, printValueName);3927}3928 3929//===----------------------------------------------------------------------===//3930// print and dump methods3931//===----------------------------------------------------------------------===//3932 3933void Attribute::print(raw_ostream &os, bool elideType) const {3934  if (!*this) {3935    os << "<<NULL ATTRIBUTE>>";3936    return;3937  }3938 3939  AsmState state(getContext());3940  print(os, state, elideType);3941}3942void Attribute::print(raw_ostream &os, AsmState &state, bool elideType) const {3943  using AttrTypeElision = AsmPrinter::Impl::AttrTypeElision;3944  AsmPrinter::Impl(os, state.getImpl())3945      .printAttribute(*this, elideType ? AttrTypeElision::Must3946                                       : AttrTypeElision::Never);3947}3948 3949void Attribute::dump() const {3950  print(llvm::errs());3951  llvm::errs() << "\n";3952}3953 3954void Attribute::printStripped(raw_ostream &os, AsmState &state) const {3955  if (!*this) {3956    os << "<<NULL ATTRIBUTE>>";3957    return;3958  }3959 3960  AsmPrinter::Impl subPrinter(os, state.getImpl());3961  if (succeeded(subPrinter.printAlias(*this)))3962    return;3963 3964  auto &dialect = this->getDialect();3965  uint64_t posPrior = os.tell();3966  DialectAsmPrinter printer(subPrinter);3967  dialect.printAttribute(*this, printer);3968  if (posPrior != os.tell())3969    return;3970 3971  // Fallback to printing with prefix if the above failed to write anything3972  // to the output stream.3973  print(os, state);3974}3975void Attribute::printStripped(raw_ostream &os) const {3976  if (!*this) {3977    os << "<<NULL ATTRIBUTE>>";3978    return;3979  }3980 3981  AsmState state(getContext());3982  printStripped(os, state);3983}3984 3985void Type::print(raw_ostream &os) const {3986  if (!*this) {3987    os << "<<NULL TYPE>>";3988    return;3989  }3990 3991  AsmState state(getContext());3992  print(os, state);3993}3994void Type::print(raw_ostream &os, AsmState &state) const {3995  AsmPrinter::Impl(os, state.getImpl()).printType(*this);3996}3997 3998void Type::dump() const {3999  print(llvm::errs());4000  llvm::errs() << "\n";4001}4002 4003void AffineMap::dump() const {4004  print(llvm::errs());4005  llvm::errs() << "\n";4006}4007 4008void IntegerSet::dump() const {4009  print(llvm::errs());4010  llvm::errs() << "\n";4011}4012 4013void AffineExpr::print(raw_ostream &os) const {4014  if (!expr) {4015    os << "<<NULL AFFINE EXPR>>";4016    return;4017  }4018  AsmState state(getContext());4019  AsmPrinter::Impl(os, state.getImpl()).printAffineExpr(*this);4020}4021 4022void AffineExpr::dump() const {4023  print(llvm::errs());4024  llvm::errs() << "\n";4025}4026 4027void AffineMap::print(raw_ostream &os) const {4028  if (!map) {4029    os << "<<NULL AFFINE MAP>>";4030    return;4031  }4032  AsmState state(getContext());4033  AsmPrinter::Impl(os, state.getImpl()).printAffineMap(*this);4034}4035 4036void IntegerSet::print(raw_ostream &os) const {4037  AsmState state(getContext());4038  AsmPrinter::Impl(os, state.getImpl()).printIntegerSet(*this);4039}4040 4041void Value::print(raw_ostream &os) const { print(os, OpPrintingFlags()); }4042void Value::print(raw_ostream &os, const OpPrintingFlags &flags) const {4043  if (!impl) {4044    os << "<<NULL VALUE>>";4045    return;4046  }4047 4048  if (auto *op = getDefiningOp())4049    return op->print(os, flags);4050  // TODO: Improve BlockArgument print'ing.4051  BlockArgument arg = llvm::cast<BlockArgument>(*this);4052  os << "<block argument> of type '" << arg.getType()4053     << "' at index: " << arg.getArgNumber();4054}4055void Value::print(raw_ostream &os, AsmState &state) const {4056  if (!impl) {4057    os << "<<NULL VALUE>>";4058    return;4059  }4060 4061  if (auto *op = getDefiningOp())4062    return op->print(os, state);4063 4064  // TODO: Improve BlockArgument print'ing.4065  BlockArgument arg = llvm::cast<BlockArgument>(*this);4066  os << "<block argument> of type '" << arg.getType()4067     << "' at index: " << arg.getArgNumber();4068}4069 4070void Value::dump() const {4071  print(llvm::errs());4072  llvm::errs() << "\n";4073}4074 4075void Value::printAsOperand(raw_ostream &os, AsmState &state) const {4076  // TODO: This doesn't necessarily capture all potential cases.4077  // Currently, region arguments can be shadowed when printing the main4078  // operation. If the IR hasn't been printed, this will produce the old SSA4079  // name and not the shadowed name.4080  state.getImpl().getSSANameState().printValueID(*this, /*printResultNo=*/true,4081                                                 os);4082}4083 4084static Operation *findParent(Operation *op, bool shouldUseLocalScope) {4085  do {4086    // If we are printing local scope, stop at the first operation that is4087    // isolated from above.4088    if (shouldUseLocalScope && op->hasTrait<OpTrait::IsIsolatedFromAbove>())4089      break;4090 4091    // Otherwise, traverse up to the next parent.4092    Operation *parentOp = op->getParentOp();4093    if (!parentOp)4094      break;4095    op = parentOp;4096  } while (true);4097  return op;4098}4099 4100void Value::printAsOperand(raw_ostream &os,4101                           const OpPrintingFlags &flags) const {4102  Operation *op;4103  if (auto result = llvm::dyn_cast<OpResult>(*this)) {4104    op = result.getOwner();4105  } else {4106    op = llvm::cast<BlockArgument>(*this).getOwner()->getParentOp();4107    if (!op) {4108      os << "<<UNKNOWN SSA VALUE>>";4109      return;4110    }4111  }4112  op = findParent(op, flags.shouldUseLocalScope());4113  AsmState state(op, flags);4114  printAsOperand(os, state);4115}4116 4117void Operation::print(raw_ostream &os, const OpPrintingFlags &printerFlags) {4118  // Find the operation to number from based upon the provided flags.4119  Operation *op = findParent(this, printerFlags.shouldUseLocalScope());4120  AsmState state(op, printerFlags);4121  print(os, state);4122}4123void Operation::print(raw_ostream &os, AsmState &state) {4124  OperationPrinter printer(os, state.getImpl());4125  if (!getParent() && !state.getPrinterFlags().shouldUseLocalScope()) {4126    state.getImpl().initializeAliases(this);4127    printer.printTopLevelOperation(this);4128  } else {4129    printer.printFullOpWithIndentAndLoc(this);4130  }4131}4132 4133void Operation::dump() {4134  print(llvm::errs(), OpPrintingFlags().useLocalScope());4135  llvm::errs() << "\n";4136}4137 4138void Operation::dumpPretty() {4139  print(llvm::errs(), OpPrintingFlags().useLocalScope().assumeVerified());4140  llvm::errs() << "\n";4141}4142 4143void Block::print(raw_ostream &os) {4144  Operation *parentOp = getParentOp();4145  if (!parentOp) {4146    os << "<<UNLINKED BLOCK>>\n";4147    return;4148  }4149  // Get the top-level op.4150  while (auto *nextOp = parentOp->getParentOp())4151    parentOp = nextOp;4152 4153  AsmState state(parentOp);4154  print(os, state);4155}4156void Block::print(raw_ostream &os, AsmState &state) {4157  OperationPrinter(os, state.getImpl()).print(this);4158}4159 4160void Block::dump() { print(llvm::errs()); }4161 4162/// Print out the name of the block without printing its body.4163void Block::printAsOperand(raw_ostream &os, bool printType) {4164  Operation *parentOp = getParentOp();4165  if (!parentOp) {4166    os << "<<UNLINKED BLOCK>>\n";4167    return;4168  }4169  AsmState state(parentOp);4170  printAsOperand(os, state);4171}4172void Block::printAsOperand(raw_ostream &os, AsmState &state) {4173  OperationPrinter printer(os, state.getImpl());4174  printer.printBlockName(this);4175}4176 4177raw_ostream &mlir::operator<<(raw_ostream &os, Block &block) {4178  block.print(os);4179  return os;4180}4181 4182//===--------------------------------------------------------------------===//4183// Custom printers4184//===--------------------------------------------------------------------===//4185namespace mlir {4186 4187void printDimensionList(OpAsmPrinter &printer, Operation *op,4188                        ArrayRef<int64_t> dimensions) {4189  if (dimensions.empty())4190    printer << "[";4191  printer.printDimensionList(dimensions);4192  if (dimensions.empty())4193    printer << "]";4194}4195 4196ParseResult parseDimensionList(OpAsmParser &parser,4197                               DenseI64ArrayAttr &dimensions) {4198  // Empty list case denoted by "[]".4199  if (succeeded(parser.parseOptionalLSquare())) {4200    if (failed(parser.parseRSquare())) {4201      return parser.emitError(parser.getCurrentLocation())4202             << "Failed parsing dimension list.";4203    }4204    dimensions =4205        DenseI64ArrayAttr::get(parser.getContext(), ArrayRef<int64_t>());4206    return success();4207  }4208 4209  // Non-empty list case.4210  SmallVector<int64_t> shapeArr;4211  if (failed(parser.parseDimensionList(shapeArr, true, false))) {4212    return parser.emitError(parser.getCurrentLocation())4213           << "Failed parsing dimension list.";4214  }4215  if (shapeArr.empty()) {4216    return parser.emitError(parser.getCurrentLocation())4217           << "Failed parsing dimension list. Did you mean an empty list? It "4218              "must be denoted by \"[]\".";4219  }4220  dimensions = DenseI64ArrayAttr::get(parser.getContext(), shapeArr);4221  return success();4222}4223 4224} // namespace mlir4225