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