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