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