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 ®ion : 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 ®ion, 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 = ®ion.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 ®ion, ValueRange namesToUse);1382 1383private:1384 /// Number the SSA values within the given IR unit.1385 void numberValuesInRegion(Region ®ion);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 ®ion : op->getRegions())1462 nameContext.push_back(std::make_tuple(®ion, 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 ®ion : op.getRegions())1495 nameContext.push_back(std::make_tuple(®ion, 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 ®ion, 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 ®ion) {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() == ®ion &&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 ®ion, 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 ®ion, 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 ®ion) {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 ®ion, 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 = ®ion.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