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