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