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