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