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