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