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