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