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  interleave(
2204  loc.getLocations(),
2205  [&](Location loc) { printLocationInternal(loc, pretty); },
2206  [&]() { os << ", "; });
2207  os << ']';
2208  })
2209  .Default([&](LocationAttr loc) {
2210  // Assumes that this is a dialect-specific attribute and prints it
2211  // directly.
2212  printAttribute(loc);
2213  });
2214 }
2215 
2216 /// Print a floating point value in a way that the parser will be able to
2217 /// round-trip losslessly.
2218 static void printFloatValue(const APFloat &apValue, raw_ostream &os,
2219  bool *printedHex = nullptr) {
2220  // We would like to output the FP constant value in exponential notation,
2221  // but we cannot do this if doing so will lose precision. Check here to
2222  // make sure that we only output it in exponential format if we can parse
2223  // the value back and get the same value.
2224  bool isInf = apValue.isInfinity();
2225  bool isNaN = apValue.isNaN();
2226  if (!isInf && !isNaN) {
2227  SmallString<128> strValue;
2228  apValue.toString(strValue, /*FormatPrecision=*/6, /*FormatMaxPadding=*/0,
2229  /*TruncateZero=*/false);
2230 
2231  // Check to make sure that the stringized number is not some string like
2232  // "Inf" or NaN, that atof will accept, but the lexer will not. Check
2233  // that the string matches the "[-+]?[0-9]" regex.
2234  assert(((strValue[0] >= '0' && strValue[0] <= '9') ||
2235  ((strValue[0] == '-' || strValue[0] == '+') &&
2236  (strValue[1] >= '0' && strValue[1] <= '9'))) &&
2237  "[-+]?[0-9] regex does not match!");
2238 
2239  // Parse back the stringized version and check that the value is equal
2240  // (i.e., there is no precision loss).
2241  if (APFloat(apValue.getSemantics(), strValue).bitwiseIsEqual(apValue)) {
2242  os << strValue;
2243  return;
2244  }
2245 
2246  // If it is not, use the default format of APFloat instead of the
2247  // exponential notation.
2248  strValue.clear();
2249  apValue.toString(strValue);
2250 
2251  // Make sure that we can parse the default form as a float.
2252  if (strValue.str().contains('.')) {
2253  os << strValue;
2254  return;
2255  }
2256  }
2257 
2258  // Print special values in hexadecimal format. The sign bit should be included
2259  // in the literal.
2260  if (printedHex)
2261  *printedHex = true;
2263  APInt apInt = apValue.bitcastToAPInt();
2264  apInt.toString(str, /*Radix=*/16, /*Signed=*/false,
2265  /*formatAsCLiteral=*/true);
2266  os << str;
2267 }
2268 
2269 void AsmPrinter::Impl::printLocation(LocationAttr loc, bool allowAlias) {
2270  if (printerFlags.shouldPrintDebugInfoPrettyForm())
2271  return printLocationInternal(loc, /*pretty=*/true, /*isTopLevel=*/true);
2272 
2273  os << "loc(";
2274  if (!allowAlias || failed(printAlias(loc)))
2275  printLocationInternal(loc, /*pretty=*/false, /*isTopLevel=*/true);
2276  os << ')';
2277 }
2278 
2279 /// Returns true if the given dialect symbol data is simple enough to print in
2280 /// the pretty form. This is essentially when the symbol takes the form:
2281 /// identifier (`<` body `>`)?
2282 static bool isDialectSymbolSimpleEnoughForPrettyForm(StringRef symName) {
2283  // The name must start with an identifier.
2284  if (symName.empty() || !isalpha(symName.front()))
2285  return false;
2286 
2287  // Ignore all the characters that are valid in an identifier in the symbol
2288  // name.
2289  symName = symName.drop_while(
2290  [](char c) { return llvm::isAlnum(c) || c == '.' || c == '_'; });
2291  if (symName.empty())
2292  return true;
2293 
2294  // If we got to an unexpected character, then it must be a <>. Check that the
2295  // rest of the symbol is wrapped within <>.
2296  return symName.front() == '<' && symName.back() == '>';
2297 }
2298 
2299 /// Print the given dialect symbol to the stream.
2300 static void printDialectSymbol(raw_ostream &os, StringRef symPrefix,
2301  StringRef dialectName, StringRef symString) {
2302  os << symPrefix << dialectName;
2303 
2304  // If this symbol name is simple enough, print it directly in pretty form,
2305  // otherwise, we print it as an escaped string.
2307  os << '.' << symString;
2308  return;
2309  }
2310 
2311  os << '<' << symString << '>';
2312 }
2313 
2314 /// Returns true if the given string can be represented as a bare identifier.
2315 static bool isBareIdentifier(StringRef name) {
2316  // By making this unsigned, the value passed in to isalnum will always be
2317  // in the range 0-255. This is important when building with MSVC because
2318  // its implementation will assert. This situation can arise when dealing
2319  // with UTF-8 multibyte characters.
2320  if (name.empty() || (!isalpha(name[0]) && name[0] != '_'))
2321  return false;
2322  return llvm::all_of(name.drop_front(), [](unsigned char c) {
2323  return isalnum(c) || c == '_' || c == '$' || c == '.';
2324  });
2325 }
2326 
2327 /// Print the given string as a keyword, or a quoted and escaped string if it
2328 /// has any special or non-printable characters in it.
2329 static void printKeywordOrString(StringRef keyword, raw_ostream &os) {
2330  // If it can be represented as a bare identifier, write it directly.
2331  if (isBareIdentifier(keyword)) {
2332  os << keyword;
2333  return;
2334  }
2335 
2336  // Otherwise, output the keyword wrapped in quotes with proper escaping.
2337  os << "\"";
2338  printEscapedString(keyword, os);
2339  os << '"';
2340 }
2341 
2342 /// Print the given string as a symbol reference. A symbol reference is
2343 /// represented as a string prefixed with '@'. The reference is surrounded with
2344 /// ""'s and escaped if it has any special or non-printable characters in it.
2345 static void printSymbolReference(StringRef symbolRef, raw_ostream &os) {
2346  if (symbolRef.empty()) {
2347  os << "@<<INVALID EMPTY SYMBOL>>";
2348  return;
2349  }
2350  os << '@';
2351  printKeywordOrString(symbolRef, os);
2352 }
2353 
2354 // Print out a valid ElementsAttr that is succinct and can represent any
2355 // potential shape/type, for use when eliding a large ElementsAttr.
2356 //
2357 // We choose to use a dense resource ElementsAttr literal with conspicuous
2358 // content to hopefully alert readers to the fact that this has been elided.
2359 static void printElidedElementsAttr(raw_ostream &os) {
2360  os << R"(dense_resource<__elided__>)";
2361 }
2362 
2363 void AsmPrinter::Impl::printResourceHandle(
2364  const AsmDialectResourceHandle &resource) {
2365  auto *interface = cast<OpAsmDialectInterface>(resource.getDialect());
2366  ::printKeywordOrString(interface->getResourceKey(resource), os);
2367  state.getDialectResources()[resource.getDialect()].insert(resource);
2368 }
2369 
2370 LogicalResult AsmPrinter::Impl::printAlias(Attribute attr) {
2371  return state.getAliasState().getAlias(attr, os);
2372 }
2373 
2374 LogicalResult AsmPrinter::Impl::printAlias(Type type) {
2375  return state.getAliasState().getAlias(type, os);
2376 }
2377 
2378 void AsmPrinter::Impl::printAttribute(Attribute attr,
2379  AttrTypeElision typeElision) {
2380  if (!attr) {
2381  os << "<<NULL ATTRIBUTE>>";
2382  return;
2383  }
2384 
2385  // Try to print an alias for this attribute.
2386  if (succeeded(printAlias(attr)))
2387  return;
2388  return printAttributeImpl(attr, typeElision);
2389 }
2390 void AsmPrinter::Impl::printAttributeImpl(Attribute attr,
2391  AttrTypeElision typeElision) {
2392  if (!isa<BuiltinDialect>(attr.getDialect())) {
2393  printDialectAttribute(attr);
2394  } else if (auto opaqueAttr = llvm::dyn_cast<OpaqueAttr>(attr)) {
2395  printDialectSymbol(os, "#", opaqueAttr.getDialectNamespace(),
2396  opaqueAttr.getAttrData());
2397  } else if (llvm::isa<UnitAttr>(attr)) {
2398  os << "unit";
2399  return;
2400  } else if (auto distinctAttr = llvm::dyn_cast<DistinctAttr>(attr)) {
2401  os << "distinct[" << state.getDistinctState().getId(distinctAttr) << "]<";
2402  if (!llvm::isa<UnitAttr>(distinctAttr.getReferencedAttr())) {
2403  printAttribute(distinctAttr.getReferencedAttr());
2404  }
2405  os << '>';
2406  return;
2407  } else if (auto dictAttr = llvm::dyn_cast<DictionaryAttr>(attr)) {
2408  os << '{';
2409  interleaveComma(dictAttr.getValue(),
2410  [&](NamedAttribute attr) { printNamedAttribute(attr); });
2411  os << '}';
2412 
2413  } else if (auto intAttr = llvm::dyn_cast<IntegerAttr>(attr)) {
2414  Type intType = intAttr.getType();
2415  if (intType.isSignlessInteger(1)) {
2416  os << (intAttr.getValue().getBoolValue() ? "true" : "false");
2417 
2418  // Boolean integer attributes always elides the type.
2419  return;
2420  }
2421 
2422  // Only print attributes as unsigned if they are explicitly unsigned or are
2423  // signless 1-bit values. Indexes, signed values, and multi-bit signless
2424  // values print as signed.
2425  bool isUnsigned =
2426  intType.isUnsignedInteger() || intType.isSignlessInteger(1);
2427  intAttr.getValue().print(os, !isUnsigned);
2428 
2429  // IntegerAttr elides the type if I64.
2430  if (typeElision == AttrTypeElision::May && intType.isSignlessInteger(64))
2431  return;
2432 
2433  } else if (auto floatAttr = llvm::dyn_cast<FloatAttr>(attr)) {
2434  bool printedHex = false;
2435  printFloatValue(floatAttr.getValue(), os, &printedHex);
2436 
2437  // FloatAttr elides the type if F64.
2438  if (typeElision == AttrTypeElision::May && floatAttr.getType().isF64() &&
2439  !printedHex)
2440  return;
2441 
2442  } else if (auto strAttr = llvm::dyn_cast<StringAttr>(attr)) {
2443  printEscapedString(strAttr.getValue());
2444 
2445  } else if (auto arrayAttr = llvm::dyn_cast<ArrayAttr>(attr)) {
2446  os << '[';
2447  interleaveComma(arrayAttr.getValue(), [&](Attribute attr) {
2448  printAttribute(attr, AttrTypeElision::May);
2449  });
2450  os << ']';
2451 
2452  } else if (auto affineMapAttr = llvm::dyn_cast<AffineMapAttr>(attr)) {
2453  os << "affine_map<";
2454  affineMapAttr.getValue().print(os);
2455  os << '>';
2456 
2457  // AffineMap always elides the type.
2458  return;
2459 
2460  } else if (auto integerSetAttr = llvm::dyn_cast<IntegerSetAttr>(attr)) {
2461  os << "affine_set<";
2462  integerSetAttr.getValue().print(os);
2463  os << '>';
2464 
2465  // IntegerSet always elides the type.
2466  return;
2467 
2468  } else if (auto typeAttr = llvm::dyn_cast<TypeAttr>(attr)) {
2469  printType(typeAttr.getValue());
2470 
2471  } else if (auto refAttr = llvm::dyn_cast<SymbolRefAttr>(attr)) {
2472  printSymbolReference(refAttr.getRootReference().getValue(), os);
2473  for (FlatSymbolRefAttr nestedRef : refAttr.getNestedReferences()) {
2474  os << "::";
2475  printSymbolReference(nestedRef.getValue(), os);
2476  }
2477 
2478  } else if (auto intOrFpEltAttr =
2479  llvm::dyn_cast<DenseIntOrFPElementsAttr>(attr)) {
2480  if (printerFlags.shouldElideElementsAttr(intOrFpEltAttr)) {
2481  printElidedElementsAttr(os);
2482  } else {
2483  os << "dense<";
2484  printDenseIntOrFPElementsAttr(intOrFpEltAttr, /*allowHex=*/true);
2485  os << '>';
2486  }
2487 
2488  } else if (auto strEltAttr = llvm::dyn_cast<DenseStringElementsAttr>(attr)) {
2489  if (printerFlags.shouldElideElementsAttr(strEltAttr)) {
2490  printElidedElementsAttr(os);
2491  } else {
2492  os << "dense<";
2493  printDenseStringElementsAttr(strEltAttr);
2494  os << '>';
2495  }
2496 
2497  } else if (auto sparseEltAttr = llvm::dyn_cast<SparseElementsAttr>(attr)) {
2498  if (printerFlags.shouldElideElementsAttr(sparseEltAttr.getIndices()) ||
2499  printerFlags.shouldElideElementsAttr(sparseEltAttr.getValues())) {
2500  printElidedElementsAttr(os);
2501  } else {
2502  os << "sparse<";
2503  DenseIntElementsAttr indices = sparseEltAttr.getIndices();
2504  if (indices.getNumElements() != 0) {
2505  printDenseIntOrFPElementsAttr(indices, /*allowHex=*/false);
2506  os << ", ";
2507  printDenseElementsAttr(sparseEltAttr.getValues(), /*allowHex=*/true);
2508  }
2509  os << '>';
2510  }
2511  } else if (auto stridedLayoutAttr = llvm::dyn_cast<StridedLayoutAttr>(attr)) {
2512  stridedLayoutAttr.print(os);
2513  } else if (auto denseArrayAttr = llvm::dyn_cast<DenseArrayAttr>(attr)) {
2514  os << "array<";
2515  printType(denseArrayAttr.getElementType());
2516  if (!denseArrayAttr.empty()) {
2517  os << ": ";
2518  printDenseArrayAttr(denseArrayAttr);
2519  }
2520  os << ">";
2521  return;
2522  } else if (auto resourceAttr =
2523  llvm::dyn_cast<DenseResourceElementsAttr>(attr)) {
2524  os << "dense_resource<";
2525  printResourceHandle(resourceAttr.getRawHandle());
2526  os << ">";
2527  } else if (auto locAttr = llvm::dyn_cast<LocationAttr>(attr)) {
2528  printLocation(locAttr);
2529  } else {
2530  llvm::report_fatal_error("Unknown builtin attribute");
2531  }
2532  // Don't print the type if we must elide it, or if it is a None type.
2533  if (typeElision != AttrTypeElision::Must) {
2534  if (auto typedAttr = llvm::dyn_cast<TypedAttr>(attr)) {
2535  Type attrType = typedAttr.getType();
2536  if (!llvm::isa<NoneType>(attrType)) {
2537  os << " : ";
2538  printType(attrType);
2539  }
2540  }
2541  }
2542 }
2543 
2544 /// Print the integer element of a DenseElementsAttr.
2545 static void printDenseIntElement(const APInt &value, raw_ostream &os,
2546  Type type) {
2547  if (type.isInteger(1))
2548  os << (value.getBoolValue() ? "true" : "false");
2549  else
2550  value.print(os, !type.isUnsignedInteger());
2551 }
2552 
2553 static void
2554 printDenseElementsAttrImpl(bool isSplat, ShapedType type, raw_ostream &os,
2555  function_ref<void(unsigned)> printEltFn) {
2556  // Special case for 0-d and splat tensors.
2557  if (isSplat)
2558  return printEltFn(0);
2559 
2560  // Special case for degenerate tensors.
2561  auto numElements = type.getNumElements();
2562  if (numElements == 0)
2563  return;
2564 
2565  // We use a mixed-radix counter to iterate through the shape. When we bump a
2566  // non-least-significant digit, we emit a close bracket. When we next emit an
2567  // element we re-open all closed brackets.
2568 
2569  // The mixed-radix counter, with radices in 'shape'.
2570  int64_t rank = type.getRank();
2571  SmallVector<unsigned, 4> counter(rank, 0);
2572  // The number of brackets that have been opened and not closed.
2573  unsigned openBrackets = 0;
2574 
2575  auto shape = type.getShape();
2576  auto bumpCounter = [&] {
2577  // Bump the least significant digit.
2578  ++counter[rank - 1];
2579  // Iterate backwards bubbling back the increment.
2580  for (unsigned i = rank - 1; i > 0; --i)
2581  if (counter[i] >= shape[i]) {
2582  // Index 'i' is rolled over. Bump (i-1) and close a bracket.
2583  counter[i] = 0;
2584  ++counter[i - 1];
2585  --openBrackets;
2586  os << ']';
2587  }
2588  };
2589 
2590  for (unsigned idx = 0, e = numElements; idx != e; ++idx) {
2591  if (idx != 0)
2592  os << ", ";
2593  while (openBrackets++ < rank)
2594  os << '[';
2595  openBrackets = rank;
2596  printEltFn(idx);
2597  bumpCounter();
2598  }
2599  while (openBrackets-- > 0)
2600  os << ']';
2601 }
2602 
2603 void AsmPrinter::Impl::printDenseElementsAttr(DenseElementsAttr attr,
2604  bool allowHex) {
2605  if (auto stringAttr = llvm::dyn_cast<DenseStringElementsAttr>(attr))
2606  return printDenseStringElementsAttr(stringAttr);
2607 
2608  printDenseIntOrFPElementsAttr(llvm::cast<DenseIntOrFPElementsAttr>(attr),
2609  allowHex);
2610 }
2611 
2612 void AsmPrinter::Impl::printDenseIntOrFPElementsAttr(
2613  DenseIntOrFPElementsAttr attr, bool allowHex) {
2614  auto type = attr.getType();
2615  auto elementType = type.getElementType();
2616 
2617  // Check to see if we should format this attribute as a hex string.
2618  if (allowHex && printerFlags.shouldPrintElementsAttrWithHex(attr)) {
2619  ArrayRef<char> rawData = attr.getRawData();
2620  if (llvm::endianness::native == llvm::endianness::big) {
2621  // Convert endianess in big-endian(BE) machines. `rawData` is BE in BE
2622  // machines. It is converted here to print in LE format.
2623  SmallVector<char, 64> outDataVec(rawData.size());
2624  MutableArrayRef<char> convRawData(outDataVec);
2625  DenseIntOrFPElementsAttr::convertEndianOfArrayRefForBEmachine(
2626  rawData, convRawData, type);
2627  printHexString(convRawData);
2628  } else {
2629  printHexString(rawData);
2630  }
2631 
2632  return;
2633  }
2634 
2635  if (ComplexType complexTy = llvm::dyn_cast<ComplexType>(elementType)) {
2636  Type complexElementType = complexTy.getElementType();
2637  // Note: The if and else below had a common lambda function which invoked
2638  // printDenseElementsAttrImpl. This lambda was hitting a bug in gcc 9.1,9.2
2639  // and hence was replaced.
2640  if (llvm::isa<IntegerType>(complexElementType)) {
2641  auto valueIt = attr.value_begin<std::complex<APInt>>();
2642  printDenseElementsAttrImpl(attr.isSplat(), type, os, [&](unsigned index) {
2643  auto complexValue = *(valueIt + index);
2644  os << "(";
2645  printDenseIntElement(complexValue.real(), os, complexElementType);
2646  os << ",";
2647  printDenseIntElement(complexValue.imag(), os, complexElementType);
2648  os << ")";
2649  });
2650  } else {
2651  auto valueIt = attr.value_begin<std::complex<APFloat>>();
2652  printDenseElementsAttrImpl(attr.isSplat(), type, os, [&](unsigned index) {
2653  auto complexValue = *(valueIt + index);
2654  os << "(";
2655  printFloatValue(complexValue.real(), os);
2656  os << ",";
2657  printFloatValue(complexValue.imag(), os);
2658  os << ")";
2659  });
2660  }
2661  } else if (elementType.isIntOrIndex()) {
2662  auto valueIt = attr.value_begin<APInt>();
2663  printDenseElementsAttrImpl(attr.isSplat(), type, os, [&](unsigned index) {
2664  printDenseIntElement(*(valueIt + index), os, elementType);
2665  });
2666  } else {
2667  assert(llvm::isa<FloatType>(elementType) && "unexpected element type");
2668  auto valueIt = attr.value_begin<APFloat>();
2669  printDenseElementsAttrImpl(attr.isSplat(), type, os, [&](unsigned index) {
2670  printFloatValue(*(valueIt + index), os);
2671  });
2672  }
2673 }
2674 
2675 void AsmPrinter::Impl::printDenseStringElementsAttr(
2676  DenseStringElementsAttr attr) {
2677  ArrayRef<StringRef> data = attr.getRawStringData();
2678  auto printFn = [&](unsigned index) { printEscapedString(data[index]); };
2679  printDenseElementsAttrImpl(attr.isSplat(), attr.getType(), os, printFn);
2680 }
2681 
2682 void AsmPrinter::Impl::printDenseArrayAttr(DenseArrayAttr attr) {
2683  Type type = attr.getElementType();
2684  unsigned bitwidth = type.isInteger(1) ? 8 : type.getIntOrFloatBitWidth();
2685  unsigned byteSize = bitwidth / 8;
2686  ArrayRef<char> data = attr.getRawData();
2687 
2688  auto printElementAt = [&](unsigned i) {
2689  APInt value(bitwidth, 0);
2690  if (bitwidth) {
2691  llvm::LoadIntFromMemory(
2692  value, reinterpret_cast<const uint8_t *>(data.begin() + byteSize * i),
2693  byteSize);
2694  }
2695  // Print the data as-is or as a float.
2696  if (type.isIntOrIndex()) {
2697  printDenseIntElement(value, getStream(), type);
2698  } else {
2699  APFloat fltVal(llvm::cast<FloatType>(type).getFloatSemantics(), value);
2700  printFloatValue(fltVal, getStream());
2701  }
2702  };
2703  llvm::interleaveComma(llvm::seq<unsigned>(0, attr.size()), getStream(),
2704  printElementAt);
2705 }
2706 
2707 void AsmPrinter::Impl::printType(Type type) {
2708  if (!type) {
2709  os << "<<NULL TYPE>>";
2710  return;
2711  }
2712 
2713  // Try to print an alias for this type.
2714  if (succeeded(printAlias(type)))
2715  return;
2716  return printTypeImpl(type);
2717 }
2718 
2719 void AsmPrinter::Impl::printTypeImpl(Type type) {
2720  TypeSwitch<Type>(type)
2721  .Case<OpaqueType>([&](OpaqueType opaqueTy) {
2722  printDialectSymbol(os, "!", opaqueTy.getDialectNamespace(),
2723  opaqueTy.getTypeData());
2724  })
2725  .Case<IndexType>([&](Type) { os << "index"; })
2726  .Case<Float4E2M1FNType>([&](Type) { os << "f4E2M1FN"; })
2727  .Case<Float6E2M3FNType>([&](Type) { os << "f6E2M3FN"; })
2728  .Case<Float6E3M2FNType>([&](Type) { os << "f6E3M2FN"; })
2729  .Case<Float8E5M2Type>([&](Type) { os << "f8E5M2"; })
2730  .Case<Float8E4M3Type>([&](Type) { os << "f8E4M3"; })
2731  .Case<Float8E4M3FNType>([&](Type) { os << "f8E4M3FN"; })
2732  .Case<Float8E5M2FNUZType>([&](Type) { os << "f8E5M2FNUZ"; })
2733  .Case<Float8E4M3FNUZType>([&](Type) { os << "f8E4M3FNUZ"; })
2734  .Case<Float8E4M3B11FNUZType>([&](Type) { os << "f8E4M3B11FNUZ"; })
2735  .Case<Float8E3M4Type>([&](Type) { os << "f8E3M4"; })
2736  .Case<Float8E8M0FNUType>([&](Type) { os << "f8E8M0FNU"; })
2737  .Case<BFloat16Type>([&](Type) { os << "bf16"; })
2738  .Case<Float16Type>([&](Type) { os << "f16"; })
2739  .Case<FloatTF32Type>([&](Type) { os << "tf32"; })
2740  .Case<Float32Type>([&](Type) { os << "f32"; })
2741  .Case<Float64Type>([&](Type) { os << "f64"; })
2742  .Case<Float80Type>([&](Type) { os << "f80"; })
2743  .Case<Float128Type>([&](Type) { os << "f128"; })
2744  .Case<IntegerType>([&](IntegerType integerTy) {
2745  if (integerTy.isSigned())
2746  os << 's';
2747  else if (integerTy.isUnsigned())
2748  os << 'u';
2749  os << 'i' << integerTy.getWidth();
2750  })
2751  .Case<FunctionType>([&](FunctionType funcTy) {
2752  os << '(';
2753  interleaveComma(funcTy.getInputs(), [&](Type ty) { printType(ty); });
2754  os << ") -> ";
2755  ArrayRef<Type> results = funcTy.getResults();
2756  if (results.size() == 1 && !llvm::isa<FunctionType>(results[0])) {
2757  printType(results[0]);
2758  } else {
2759  os << '(';
2760  interleaveComma(results, [&](Type ty) { printType(ty); });
2761  os << ')';
2762  }
2763  })
2764  .Case<VectorType>([&](VectorType vectorTy) {
2765  auto scalableDims = vectorTy.getScalableDims();
2766  os << "vector<";
2767  auto vShape = vectorTy.getShape();
2768  unsigned lastDim = vShape.size();
2769  unsigned dimIdx = 0;
2770  for (dimIdx = 0; dimIdx < lastDim; dimIdx++) {
2771  if (!scalableDims.empty() && scalableDims[dimIdx])
2772  os << '[';
2773  os << vShape[dimIdx];
2774  if (!scalableDims.empty() && scalableDims[dimIdx])
2775  os << ']';
2776  os << 'x';
2777  }
2778  printType(vectorTy.getElementType());
2779  os << '>';
2780  })
2781  .Case<RankedTensorType>([&](RankedTensorType tensorTy) {
2782  os << "tensor<";
2783  printDimensionList(tensorTy.getShape());
2784  if (!tensorTy.getShape().empty())
2785  os << 'x';
2786  printType(tensorTy.getElementType());
2787  // Only print the encoding attribute value if set.
2788  if (tensorTy.getEncoding()) {
2789  os << ", ";
2790  printAttribute(tensorTy.getEncoding());
2791  }
2792  os << '>';
2793  })
2794  .Case<UnrankedTensorType>([&](UnrankedTensorType tensorTy) {
2795  os << "tensor<*x";
2796  printType(tensorTy.getElementType());
2797  os << '>';
2798  })
2799  .Case<MemRefType>([&](MemRefType memrefTy) {
2800  os << "memref<";
2801  printDimensionList(memrefTy.getShape());
2802  if (!memrefTy.getShape().empty())
2803  os << 'x';
2804  printType(memrefTy.getElementType());
2805  MemRefLayoutAttrInterface layout = memrefTy.getLayout();
2806  if (!llvm::isa<AffineMapAttr>(layout) || !layout.isIdentity()) {
2807  os << ", ";
2808  printAttribute(memrefTy.getLayout(), AttrTypeElision::May);
2809  }
2810  // Only print the memory space if it is the non-default one.
2811  if (memrefTy.getMemorySpace()) {
2812  os << ", ";
2813  printAttribute(memrefTy.getMemorySpace(), AttrTypeElision::May);
2814  }
2815  os << '>';
2816  })
2817  .Case<UnrankedMemRefType>([&](UnrankedMemRefType memrefTy) {
2818  os << "memref<*x";
2819  printType(memrefTy.getElementType());
2820  // Only print the memory space if it is the non-default one.
2821  if (memrefTy.getMemorySpace()) {
2822  os << ", ";
2823  printAttribute(memrefTy.getMemorySpace(), AttrTypeElision::May);
2824  }
2825  os << '>';
2826  })
2827  .Case<ComplexType>([&](ComplexType complexTy) {
2828  os << "complex<";
2829  printType(complexTy.getElementType());
2830  os << '>';
2831  })
2832  .Case<TupleType>([&](TupleType tupleTy) {
2833  os << "tuple<";
2834  interleaveComma(tupleTy.getTypes(),
2835  [&](Type type) { printType(type); });
2836  os << '>';
2837  })
2838  .Case<NoneType>([&](Type) { os << "none"; })
2839  .Case<GraphType>([&](GraphType graphTy) {
2840  os << '(';
2841  interleaveComma(graphTy.getInputs(), [&](Type ty) { printType(ty); });
2842  os << ") -> ";
2843  ArrayRef<Type> results = graphTy.getResults();
2844  if (results.size() == 1 && !isa<FunctionType, GraphType>(results[0])) {
2845  printType(results[0]);
2846  } else {
2847  os << '(';
2848  interleaveComma(results, [&](Type ty) { printType(ty); });
2849  os << ')';
2850  }
2851  })
2852  .Default([&](Type type) { return printDialectType(type); });
2853 }
2854 
2855 void AsmPrinter::Impl::printOptionalAttrDict(ArrayRef<NamedAttribute> attrs,
2856  ArrayRef<StringRef> elidedAttrs,
2857  bool withKeyword) {
2858  // If there are no attributes, then there is nothing to be done.
2859  if (attrs.empty())
2860  return;
2861 
2862  // Functor used to print a filtered attribute list.
2863  auto printFilteredAttributesFn = [&](auto filteredAttrs) {
2864  // Print the 'attributes' keyword if necessary.
2865  if (withKeyword)
2866  os << " attributes";
2867 
2868  // Otherwise, print them all out in braces.
2869  os << " {";
2870  interleaveComma(filteredAttrs,
2871  [&](NamedAttribute attr) { printNamedAttribute(attr); });
2872  os << '}';
2873  };
2874 
2875  // If no attributes are elided, we can directly print with no filtering.
2876  if (elidedAttrs.empty())
2877  return printFilteredAttributesFn(attrs);
2878 
2879  // Otherwise, filter out any attributes that shouldn't be included.
2880  llvm::SmallDenseSet<StringRef> elidedAttrsSet(elidedAttrs.begin(),
2881  elidedAttrs.end());
2882  auto filteredAttrs = llvm::make_filter_range(attrs, [&](NamedAttribute attr) {
2883  return !elidedAttrsSet.contains(attr.getName().strref());
2884  });
2885  if (!filteredAttrs.empty())
2886  printFilteredAttributesFn(filteredAttrs);
2887 }
2888 void AsmPrinter::Impl::printNamedAttribute(NamedAttribute attr) {
2889  // Print the name without quotes if possible.
2890  ::printKeywordOrString(attr.getName().strref(), os);
2891 
2892  // Pretty printing elides the attribute value for unit attributes.
2893  if (llvm::isa<UnitAttr>(attr.getValue()))
2894  return;
2895 
2896  os << " = ";
2897  printAttribute(attr.getValue());
2898 }
2899 
2900 void AsmPrinter::Impl::printDialectAttribute(Attribute attr) {
2901  auto &dialect = attr.getDialect();
2902 
2903  // Ask the dialect to serialize the attribute to a string.
2904  std::string attrName;
2905  {
2906  llvm::raw_string_ostream attrNameStr(attrName);
2907  Impl subPrinter(attrNameStr, state);
2908  DialectAsmPrinter printer(subPrinter);
2909  dialect.printAttribute(attr, printer);
2910  }
2911  printDialectSymbol(os, "#", dialect.getNamespace(), attrName);
2912 }
2913 
2914 void AsmPrinter::Impl::printDialectType(Type type) {
2915  auto &dialect = type.getDialect();
2916 
2917  // Ask the dialect to serialize the type to a string.
2918  std::string typeName;
2919  {
2920  llvm::raw_string_ostream typeNameStr(typeName);
2921  Impl subPrinter(typeNameStr, state);
2922  DialectAsmPrinter printer(subPrinter);
2923  dialect.printType(type, printer);
2924  }
2925  printDialectSymbol(os, "!", dialect.getNamespace(), typeName);
2926 }
2927 
2928 void AsmPrinter::Impl::printEscapedString(StringRef str) {
2929  os << "\"";
2930  llvm::printEscapedString(str, os);
2931  os << "\"";
2932 }
2933 
2935  os << "\"0x" << llvm::toHex(str) << "\"";
2936 }
2938  printHexString(StringRef(data.data(), data.size()));
2939 }
2940 
2941 LogicalResult AsmPrinter::Impl::pushCyclicPrinting(const void *opaquePointer) {
2942  return state.pushCyclicPrinting(opaquePointer);
2943 }
2944 
2945 void AsmPrinter::Impl::popCyclicPrinting() { state.popCyclicPrinting(); }
2946 
2948  detail::printDimensionList(os, shape);
2949 }
2950 
2951 //===--------------------------------------------------------------------===//
2952 // AsmPrinter
2953 //===--------------------------------------------------------------------===//
2954 
2955 AsmPrinter::~AsmPrinter() = default;
2956 
2957 raw_ostream &AsmPrinter::getStream() const {
2958  assert(impl && "expected AsmPrinter::getStream to be overriden");
2959  return impl->getStream();
2960 }
2961 
2962 /// Print the given floating point value in a stablized form.
2963 void AsmPrinter::printFloat(const APFloat &value) {
2964  assert(impl && "expected AsmPrinter::printFloat to be overriden");
2965  printFloatValue(value, impl->getStream());
2966 }
2967 
2969  assert(impl && "expected AsmPrinter::printType to be overriden");
2970  impl->printType(type);
2971 }
2972 
2974  assert(impl && "expected AsmPrinter::printAttribute to be overriden");
2975  impl->printAttribute(attr);
2976 }
2977 
2978 LogicalResult AsmPrinter::printAlias(Attribute attr) {
2979  assert(impl && "expected AsmPrinter::printAlias to be overriden");
2980  return impl->printAlias(attr);
2981 }
2982 
2983 LogicalResult AsmPrinter::printAlias(Type type) {
2984  assert(impl && "expected AsmPrinter::printAlias to be overriden");
2985  return impl->printAlias(type);
2986 }
2987 
2989  assert(impl &&
2990  "expected AsmPrinter::printAttributeWithoutType to be overriden");
2991  impl->printAttribute(attr, Impl::AttrTypeElision::Must);
2992 }
2993 
2995  assert(impl && "expected AsmPrinter::printNamedAttribute to be overriden");
2996  impl->printNamedAttribute(attr);
2997 }
2998 
2999 void AsmPrinter::printKeywordOrString(StringRef keyword) {
3000  assert(impl && "expected AsmPrinter::printKeywordOrString to be overriden");
3001  ::printKeywordOrString(keyword, impl->getStream());
3002 }
3003 
3004 void AsmPrinter::printString(StringRef keyword) {
3005  assert(impl && "expected AsmPrinter::printString to be overriden");
3006  *this << '"';
3007  printEscapedString(keyword, getStream());
3008  *this << '"';
3009 }
3010 
3011 void AsmPrinter::printSymbolName(StringRef symbolRef) {
3012  assert(impl && "expected AsmPrinter::printSymbolName to be overriden");
3013  ::printSymbolReference(symbolRef, impl->getStream());
3014 }
3015 
3017  assert(impl && "expected AsmPrinter::printResourceHandle to be overriden");
3018  impl->printResourceHandle(resource);
3019 }
3020 
3023 }
3024 
3025 LogicalResult AsmPrinter::pushCyclicPrinting(const void *opaquePointer) {
3026  return impl->pushCyclicPrinting(opaquePointer);
3027 }
3028 
3029 void AsmPrinter::popCyclicPrinting() { impl->popCyclicPrinting(); }
3030 
3031 //===----------------------------------------------------------------------===//
3032 // Affine expressions and maps
3033 //===----------------------------------------------------------------------===//
3034 
3036  AffineExpr expr, function_ref<void(unsigned, bool)> printValueName) {
3037  printAffineExprInternal(expr, BindingStrength::Weak, printValueName);
3038 }
3039 
3041  AffineExpr expr, BindingStrength enclosingTightness,
3042  function_ref<void(unsigned, bool)> printValueName) {
3043  const char *binopSpelling = nullptr;
3044  switch (expr.getKind()) {
3045  case AffineExprKind::SymbolId: {
3046  unsigned pos = cast<AffineSymbolExpr>(expr).getPosition();
3047  if (printValueName)
3048  printValueName(pos, /*isSymbol=*/true);
3049  else
3050  os << 's' << pos;
3051  return;
3052  }
3053  case AffineExprKind::DimId: {
3054  unsigned pos = cast<AffineDimExpr>(expr).getPosition();
3055  if (printValueName)
3056  printValueName(pos, /*isSymbol=*/false);
3057  else
3058  os << 'd' << pos;
3059  return;
3060  }
3062  os << cast<AffineConstantExpr>(expr).getValue();
3063  return;
3064  case AffineExprKind::Add:
3065  binopSpelling = " + ";
3066  break;
3067  case AffineExprKind::Mul:
3068  binopSpelling = " * ";
3069  break;
3071  binopSpelling = " floordiv ";
3072  break;
3074  binopSpelling = " ceildiv ";
3075  break;
3076  case AffineExprKind::Mod:
3077  binopSpelling = " mod ";
3078  break;
3079  }
3080 
3081  auto binOp = cast<AffineBinaryOpExpr>(expr);
3082  AffineExpr lhsExpr = binOp.getLHS();
3083  AffineExpr rhsExpr = binOp.getRHS();
3084 
3085  // Handle tightly binding binary operators.
3086  if (binOp.getKind() != AffineExprKind::Add) {
3087  if (enclosingTightness == BindingStrength::Strong)
3088  os << '(';
3089 
3090  // Pretty print multiplication with -1.
3091  auto rhsConst = dyn_cast<AffineConstantExpr>(rhsExpr);
3092  if (rhsConst && binOp.getKind() == AffineExprKind::Mul &&
3093  rhsConst.getValue() == -1) {
3094  os << "-";
3095  printAffineExprInternal(lhsExpr, BindingStrength::Strong, printValueName);
3096  if (enclosingTightness == BindingStrength::Strong)
3097  os << ')';
3098  return;
3099  }
3100 
3101  printAffineExprInternal(lhsExpr, BindingStrength::Strong, printValueName);
3102 
3103  os << binopSpelling;
3104  printAffineExprInternal(rhsExpr, BindingStrength::Strong, printValueName);
3105 
3106  if (enclosingTightness == BindingStrength::Strong)
3107  os << ')';
3108  return;
3109  }
3110 
3111  // Print out special "pretty" forms for add.
3112  if (enclosingTightness == BindingStrength::Strong)
3113  os << '(';
3114 
3115  // Pretty print addition to a product that has a negative operand as a
3116  // subtraction.
3117  if (auto rhs = dyn_cast<AffineBinaryOpExpr>(rhsExpr)) {
3118  if (rhs.getKind() == AffineExprKind::Mul) {
3119  AffineExpr rrhsExpr = rhs.getRHS();
3120  if (auto rrhs = dyn_cast<AffineConstantExpr>(rrhsExpr)) {
3121  if (rrhs.getValue() == -1) {
3122  printAffineExprInternal(lhsExpr, BindingStrength::Weak,
3123  printValueName);
3124  os << " - ";
3125  if (rhs.getLHS().getKind() == AffineExprKind::Add) {
3126  printAffineExprInternal(rhs.getLHS(), BindingStrength::Strong,
3127  printValueName);
3128  } else {
3129  printAffineExprInternal(rhs.getLHS(), BindingStrength::Weak,
3130  printValueName);
3131  }
3132 
3133  if (enclosingTightness == BindingStrength::Strong)
3134  os << ')';
3135  return;
3136  }
3137 
3138  if (rrhs.getValue() < -1) {
3139  printAffineExprInternal(lhsExpr, BindingStrength::Weak,
3140  printValueName);
3141  os << " - ";
3142  printAffineExprInternal(rhs.getLHS(), BindingStrength::Strong,
3143  printValueName);
3144  os << " * " << -rrhs.getValue();
3145  if (enclosingTightness == BindingStrength::Strong)
3146  os << ')';
3147  return;
3148  }
3149  }
3150  }
3151  }
3152 
3153  // Pretty print addition to a negative number as a subtraction.
3154  if (auto rhsConst = dyn_cast<AffineConstantExpr>(rhsExpr)) {
3155  if (rhsConst.getValue() < 0) {
3156  printAffineExprInternal(lhsExpr, BindingStrength::Weak, printValueName);
3157  os << " - " << -rhsConst.getValue();
3158  if (enclosingTightness == BindingStrength::Strong)
3159  os << ')';
3160  return;
3161  }
3162  }
3163 
3164  printAffineExprInternal(lhsExpr, BindingStrength::Weak, printValueName);
3165 
3166  os << " + ";
3167  printAffineExprInternal(rhsExpr, BindingStrength::Weak, printValueName);
3168 
3169  if (enclosingTightness == BindingStrength::Strong)
3170  os << ')';
3171 }
3172 
3174  printAffineExprInternal(expr, BindingStrength::Weak);
3175  isEq ? os << " == 0" : os << " >= 0";
3176 }
3177 
3179  // Dimension identifiers.
3180  os << '(';
3181  for (int i = 0; i < (int)map.getNumDims() - 1; ++i)
3182  os << 'd' << i << ", ";
3183  if (map.getNumDims() >= 1)
3184  os << 'd' << map.getNumDims() - 1;
3185  os << ')';
3186 
3187  // Symbolic identifiers.
3188  if (map.getNumSymbols() != 0) {
3189  os << '[';
3190  for (unsigned i = 0; i < map.getNumSymbols() - 1; ++i)
3191  os << 's' << i << ", ";
3192  if (map.getNumSymbols() >= 1)
3193  os << 's' << map.getNumSymbols() - 1;
3194  os << ']';
3195  }
3196 
3197  // Result affine expressions.
3198  os << " -> (";
3199  interleaveComma(map.getResults(),
3200  [&](AffineExpr expr) { printAffineExpr(expr); });
3201  os << ')';
3202 }
3203 
3205  // Dimension identifiers.
3206  os << '(';
3207  for (unsigned i = 1; i < set.getNumDims(); ++i)
3208  os << 'd' << i - 1 << ", ";
3209  if (set.getNumDims() >= 1)
3210  os << 'd' << set.getNumDims() - 1;
3211  os << ')';
3212 
3213  // Symbolic identifiers.
3214  if (set.getNumSymbols() != 0) {
3215  os << '[';
3216  for (unsigned i = 0; i < set.getNumSymbols() - 1; ++i)
3217  os << 's' << i << ", ";
3218  if (set.getNumSymbols() >= 1)
3219  os << 's' << set.getNumSymbols() - 1;
3220  os << ']';
3221  }
3222 
3223  // Print constraints.
3224  os << " : (";
3225  int numConstraints = set.getNumConstraints();
3226  for (int i = 1; i < numConstraints; ++i) {
3227  printAffineConstraint(set.getConstraint(i - 1), set.isEq(i - 1));
3228  os << ", ";
3229  }
3230  if (numConstraints >= 1)
3231  printAffineConstraint(set.getConstraint(numConstraints - 1),
3232  set.isEq(numConstraints - 1));
3233  os << ')';
3234 }
3235 
3236 //===----------------------------------------------------------------------===//
3237 // OperationPrinter
3238 //===----------------------------------------------------------------------===//
3239 
3240 namespace {
3241 /// This class contains the logic for printing operations, regions, and blocks.
3242 class OperationPrinter : public AsmPrinter::Impl, private OpAsmPrinter {
3243 public:
3244  using Impl = AsmPrinter::Impl;
3245  using Impl::printType;
3246 
3247  explicit OperationPrinter(raw_ostream &os, AsmStateImpl &state)
3248  : Impl(os, state), OpAsmPrinter(static_cast<Impl &>(*this)) {}
3249 
3250  /// Print the given top-level operation.
3251  void printTopLevelOperation(Operation *op);
3252 
3253  /// Print the given operation, including its left-hand side and its right-hand
3254  /// side, with its indent and location.
3255  void printFullOpWithIndentAndLoc(Operation *op);
3256  /// Print the given operation, including its left-hand side and its right-hand
3257  /// side, but not including indentation and location.
3258  void printFullOp(Operation *op);
3259  /// Print the right-hand size of the given operation in the custom or generic
3260  /// form.
3261  void printCustomOrGenericOp(Operation *op) override;
3262  /// Print the right-hand side of the given operation in the generic form.
3263  void printGenericOp(Operation *op, bool printOpName) override;
3264 
3265  /// Print the name of the given block.
3266  void printBlockName(Block *block);
3267 
3268  /// Print the given block. If 'printBlockArgs' is false, the arguments of the
3269  /// block are not printed. If 'printBlockTerminator' is false, the terminator
3270  /// operation of the block is not printed.
3271  void print(Block *block, bool printBlockArgs = true,
3272  bool printBlockTerminator = true);
3273 
3274  /// Print the ID of the given value, optionally with its result number.
3275  void printValueID(Value value, bool printResultNo = true,
3276  raw_ostream *streamOverride = nullptr) const;
3277 
3278  /// Print the ID of the given operation.
3279  void printOperationID(Operation *op,
3280  raw_ostream *streamOverride = nullptr) const;
3281 
3282  //===--------------------------------------------------------------------===//
3283  // OpAsmPrinter methods
3284  //===--------------------------------------------------------------------===//
3285 
3286  /// Print a loc(...) specifier if printing debug info is enabled. Locations
3287  /// may be deferred with an alias.
3288  void printOptionalLocationSpecifier(Location loc) override {
3289  printTrailingLocation(loc);
3290  }
3291 
3292  /// Print a newline and indent the printer to the start of the current
3293  /// operation.
3294  void printNewline() override {
3295  os << newLine;
3296  os.indent(currentIndent);
3297  }
3298 
3299  /// Increase indentation.
3300  void increaseIndent() override { currentIndent += indentWidth; }
3301 
3302  /// Decrease indentation.
3303  void decreaseIndent() override { currentIndent -= indentWidth; }
3304 
3305  /// Print a block argument in the usual format of:
3306  /// %ssaName : type {attr1=42} loc("here")
3307  /// where location printing is controlled by the standard internal option.
3308  /// You may pass omitType=true to not print a type, and pass an empty
3309  /// attribute list if you don't care for attributes.
3310  void printRegionArgument(BlockArgument arg,
3311  ArrayRef<NamedAttribute> argAttrs = {},
3312  bool omitType = false) override;
3313 
3314  /// Print the ID for the given value.
3315  void printOperand(Value value) override { printValueID(value); }
3316  void printOperand(Value value, raw_ostream &os) override {
3317  printValueID(value, /*printResultNo=*/true, &os);
3318  }
3319 
3320  /// Print an optional attribute dictionary with a given set of elided values.
3321  void printOptionalAttrDict(ArrayRef<NamedAttribute> attrs,
3322  ArrayRef<StringRef> elidedAttrs = {}) override {
3323  Impl::printOptionalAttrDict(attrs, elidedAttrs);
3324  }
3325  void printOptionalAttrDictWithKeyword(
3327  ArrayRef<StringRef> elidedAttrs = {}) override {
3328  Impl::printOptionalAttrDict(attrs, elidedAttrs,
3329  /*withKeyword=*/true);
3330  }
3331 
3332  /// Print the given successor.
3333  void printSuccessor(Block *successor) override;
3334 
3335  /// Print an operation successor with the operands used for the block
3336  /// arguments.
3337  void printSuccessorAndUseList(Block *successor,
3338  ValueRange succOperands) override;
3339 
3340  /// Print the given region.
3341  void printRegion(Region &region, bool printEntryBlockArgs,
3342  bool printBlockTerminators, bool printEmptyBlock) override;
3343 
3344  /// Renumber the arguments for the specified region to the same names as the
3345  /// SSA values in namesToUse. This may only be used for IsolatedFromAbove
3346  /// operations. If any entry in namesToUse is null, the corresponding
3347  /// argument name is left alone.
3348  void shadowRegionArgs(Region &region, ValueRange namesToUse) override {
3349  state.getSSANameState().shadowRegionArgs(region, namesToUse);
3350  }
3351 
3352  /// Print the given affine map with the symbol and dimension operands printed
3353  /// inline with the map.
3354  void printAffineMapOfSSAIds(AffineMapAttr mapAttr,
3355  ValueRange operands) override;
3356 
3357  /// Print the given affine expression with the symbol and dimension operands
3358  /// printed inline with the expression.
3359  void printAffineExprOfSSAIds(AffineExpr expr, ValueRange dimOperands,
3360  ValueRange symOperands) override;
3361 
3362  /// Print users of this operation or id of this operation if it has no result.
3363  void printUsersComment(Operation *op);
3364 
3365  /// Print users of this block arg.
3366  void printUsersComment(BlockArgument arg);
3367 
3368  /// Print the users of a value.
3369  void printValueUsers(Value value);
3370 
3371  /// Print either the ids of the result values or the id of the operation if
3372  /// the operation has no results.
3373  void printUserIDs(Operation *user, bool prefixComma = false);
3374 
3375 private:
3376  /// This class represents a resource builder implementation for the MLIR
3377  /// textual assembly format.
3378  class ResourceBuilder : public AsmResourceBuilder {
3379  public:
3380  using ValueFn = function_ref<void(raw_ostream &)>;
3381  using PrintFn = function_ref<void(StringRef, ValueFn)>;
3382 
3383  ResourceBuilder(PrintFn printFn) : printFn(printFn) {}
3384  ~ResourceBuilder() override = default;
3385 
3386  void buildBool(StringRef key, bool data) final {
3387  printFn(key, [&](raw_ostream &os) { os << (data ? "true" : "false"); });
3388  }
3389 
3390  void buildString(StringRef key, StringRef data) final {
3391  printFn(key, [&](raw_ostream &os) {
3392  os << "\"";
3393  llvm::printEscapedString(data, os);
3394  os << "\"";
3395  });
3396  }
3397 
3398  void buildBlob(StringRef key, ArrayRef<char> data,
3399  uint32_t dataAlignment) final {
3400  printFn(key, [&](raw_ostream &os) {
3401  // Store the blob in a hex string containing the alignment and the data.
3402  llvm::support::ulittle32_t dataAlignmentLE(dataAlignment);
3403  os << "\"0x"
3404  << llvm::toHex(StringRef(reinterpret_cast<char *>(&dataAlignmentLE),
3405  sizeof(dataAlignment)))
3406  << llvm::toHex(StringRef(data.data(), data.size())) << "\"";
3407  });
3408  }
3409 
3410  private:
3411  PrintFn printFn;
3412  };
3413 
3414  /// Print the metadata dictionary for the file, eliding it if it is empty.
3415  void printFileMetadataDictionary(Operation *op);
3416 
3417  /// Print the resource sections for the file metadata dictionary.
3418  /// `checkAddMetadataDict` is used to indicate that metadata is going to be
3419  /// added, and the file metadata dictionary should be started if it hasn't
3420  /// yet.
3421  void printResourceFileMetadata(function_ref<void()> checkAddMetadataDict,
3422  Operation *op);
3423 
3424  // Contains the stack of default dialects to use when printing regions.
3425  // A new dialect is pushed to the stack before parsing regions nested under an
3426  // operation implementing `OpAsmOpInterface`, and popped when done. At the
3427  // top-level we start with "builtin" as the default, so that the top-level
3428  // `module` operation prints as-is.
3429  SmallVector<StringRef> defaultDialectStack{"builtin"};
3430 
3431  /// The number of spaces used for indenting nested operations.
3432  const static unsigned indentWidth = 2;
3433 
3434  // This is the current indentation level for nested structures.
3435  unsigned currentIndent = 0;
3436 };
3437 } // namespace
3438 
3439 void OperationPrinter::printTopLevelOperation(Operation *op) {
3440  // Output the aliases at the top level that can't be deferred.
3441  state.getAliasState().printNonDeferredAliases(*this, newLine);
3442 
3443  // Print the module.
3444  printFullOpWithIndentAndLoc(op);
3445  os << newLine;
3446 
3447  // Output the aliases at the top level that can be deferred.
3448  state.getAliasState().printDeferredAliases(*this, newLine);
3449 
3450  // Output any file level metadata.
3451  printFileMetadataDictionary(op);
3452 }
3453 
3454 void OperationPrinter::printFileMetadataDictionary(Operation *op) {
3455  bool sawMetadataEntry = false;
3456  auto checkAddMetadataDict = [&] {
3457  if (!std::exchange(sawMetadataEntry, true))
3458  os << newLine << "{-#" << newLine;
3459  };
3460 
3461  // Add the various types of metadata.
3462  printResourceFileMetadata(checkAddMetadataDict, op);
3463 
3464  // If the file dictionary exists, close it.
3465  if (sawMetadataEntry)
3466  os << newLine << "#-}" << newLine;
3467 }
3468 
3469 void OperationPrinter::printResourceFileMetadata(
3470  function_ref<void()> checkAddMetadataDict, Operation *op) {
3471  // Functor used to add data entries to the file metadata dictionary.
3472  bool hadResource = false;
3473  bool needResourceComma = false;
3474  bool needEntryComma = false;
3475  auto processProvider = [&](StringRef dictName, StringRef name, auto &provider,
3476  auto &&...providerArgs) {
3477  bool hadEntry = false;
3478  auto printFn = [&](StringRef key, ResourceBuilder::ValueFn valueFn) {
3479  checkAddMetadataDict();
3480 
3481  std::string resourceStr;
3482  auto printResourceStr = [&](raw_ostream &os) { os << resourceStr; };
3483  std::optional<uint64_t> charLimit =
3484  printerFlags.getLargeResourceStringLimit();
3485  if (charLimit.has_value()) {
3486  // Don't compute resourceStr when charLimit is 0.
3487  if (charLimit.value() == 0)
3488  return;
3489 
3490  llvm::raw_string_ostream ss(resourceStr);
3491  valueFn(ss);
3492 
3493  // Only print entry if its string is small enough.
3494  if (resourceStr.size() > charLimit.value())
3495  return;
3496 
3497  // Don't recompute resourceStr when valueFn is called below.
3498  valueFn = printResourceStr;
3499  }
3500 
3501  // Emit the top-level resource entry if we haven't yet.
3502  if (!std::exchange(hadResource, true)) {
3503  if (needResourceComma)
3504  os << "," << newLine;
3505  os << " " << dictName << "_resources: {" << newLine;
3506  }
3507  // Emit the parent resource entry if we haven't yet.
3508  if (!std::exchange(hadEntry, true)) {
3509  if (needEntryComma)
3510  os << "," << newLine;
3511  os << " " << name << ": {" << newLine;
3512  } else {
3513  os << "," << newLine;
3514  }
3515  os << " ";
3516  ::printKeywordOrString(key, os);
3517  os << ": ";
3518  // Call printResourceStr or original valueFn, depending on charLimit.
3519  valueFn(os);
3520  };
3521  ResourceBuilder entryBuilder(printFn);
3522  provider.buildResources(op, providerArgs..., entryBuilder);
3523 
3524  needEntryComma |= hadEntry;
3525  if (hadEntry)
3526  os << newLine << " }";
3527  };
3528 
3529  // Print the `dialect_resources` section if we have any dialects with
3530  // resources.
3531  for (const OpAsmDialectInterface &interface : state.getDialectInterfaces()) {
3532  auto &dialectResources = state.getDialectResources();
3533  StringRef name = interface.getDialect()->getNamespace();
3534  auto it = dialectResources.find(interface.getDialect());
3535  if (it != dialectResources.end())
3536  processProvider("dialect", name, interface, it->second);
3537  else
3538  processProvider("dialect", name, interface,
3540  }
3541  if (hadResource)
3542  os << newLine << " }";
3543 
3544  // Print the `external_resources` section if we have any external clients with
3545  // resources.
3546  needEntryComma = false;
3547  needResourceComma = hadResource;
3548  hadResource = false;
3549  for (const auto &printer : state.getResourcePrinters())
3550  processProvider("external", printer.getName(), printer);
3551  if (hadResource)
3552  os << newLine << " }";
3553 }
3554 
3555 /// Print a block argument in the usual format of:
3556 /// %ssaName : type {attr1=42} loc("here")
3557 /// where location printing is controlled by the standard internal option.
3558 /// You may pass omitType=true to not print a type, and pass an empty
3559 /// attribute list if you don't care for attributes.
3560 void OperationPrinter::printRegionArgument(BlockArgument arg,
3561  ArrayRef<NamedAttribute> argAttrs,
3562  bool omitType) {
3563  printOperand(arg);
3564  if (!omitType) {
3565  os << ": ";
3566  printType(arg.getType());
3567  }
3568  printOptionalAttrDict(argAttrs);
3569  // TODO: We should allow location aliases on block arguments.
3570  printTrailingLocation(arg.getLoc(), /*allowAlias*/ false);
3571 }
3572 
3573 void OperationPrinter::printFullOpWithIndentAndLoc(Operation *op) {
3574  // Track the location of this operation.
3575  state.registerOperationLocation(op, newLine.curLine, currentIndent);
3576 
3577  os.indent(currentIndent);
3578  printFullOp(op);
3579  printTrailingLocation(op->getLoc());
3580  if (printerFlags.shouldPrintValueUsers())
3581  printUsersComment(op);
3582 }
3583 
3584 void OperationPrinter::printFullOp(Operation *op) {
3585  if (size_t numResults = op->getNumResults()) {
3586  auto printResultGroup = [&](size_t resultNo, size_t resultCount) {
3587  printValueID(op->getResult(resultNo), /*printResultNo=*/false);
3588  if (resultCount > 1)
3589  os << ':' << resultCount;
3590  };
3591 
3592  // Check to see if this operation has multiple result groups.
3593  ArrayRef<int> resultGroups = state.getSSANameState().getOpResultGroups(op);
3594  if (!resultGroups.empty()) {
3595  // Interleave the groups excluding the last one, this one will be handled
3596  // separately.
3597  interleaveComma(llvm::seq<int>(0, resultGroups.size() - 1), [&](int i) {
3598  printResultGroup(resultGroups[i],
3599  resultGroups[i + 1] - resultGroups[i]);
3600  });
3601  os << ", ";
3602  printResultGroup(resultGroups.back(), numResults - resultGroups.back());
3603 
3604  } else {
3605  printResultGroup(/*resultNo=*/0, /*resultCount=*/numResults);
3606  }
3607 
3608  os << " = ";
3609  }
3610 
3611  printCustomOrGenericOp(op);
3612 }
3613 
3614 void OperationPrinter::printUsersComment(Operation *op) {
3615  unsigned numResults = op->getNumResults();
3616  if (!numResults && op->getNumOperands()) {
3617  os << " // id: ";
3618  printOperationID(op);
3619  } else if (numResults && op->use_empty()) {
3620  os << " // unused";
3621  } else if (numResults && !op->use_empty()) {
3622  // Print "user" if the operation has one result used to compute one other
3623  // result, or is used in one operation with no result.
3624  unsigned usedInNResults = 0;
3625  unsigned usedInNOperations = 0;
3627  for (Operation *user : op->getUsers()) {
3628  if (userSet.insert(user).second) {
3629  ++usedInNOperations;
3630  usedInNResults += user->getNumResults();
3631  }
3632  }
3633 
3634  // We already know that users is not empty.
3635  bool exactlyOneUniqueUse =
3636  usedInNResults <= 1 && usedInNOperations <= 1 && numResults == 1;
3637  os << " // " << (exactlyOneUniqueUse ? "user" : "users") << ": ";
3638  bool shouldPrintBrackets = numResults > 1;
3639  auto printOpResult = [&](OpResult opResult) {
3640  if (shouldPrintBrackets)
3641  os << "(";
3642  printValueUsers(opResult);
3643  if (shouldPrintBrackets)
3644  os << ")";
3645  };
3646 
3647  interleaveComma(op->getResults(), printOpResult);
3648  }
3649 }
3650 
3651 void OperationPrinter::printUsersComment(BlockArgument arg) {
3652  os << "// ";
3653  printValueID(arg);
3654  if (arg.use_empty()) {
3655  os << " is unused";
3656  } else {
3657  os << " is used by ";
3658  printValueUsers(arg);
3659  }
3660  os << newLine;
3661 }
3662 
3663 void OperationPrinter::printValueUsers(Value value) {
3664  if (value.use_empty())
3665  os << "unused";
3666 
3667  // One value might be used as the operand of an operation more than once.
3668  // Only print the operations results once in that case.
3670  for (auto [index, user] : enumerate(value.getUsers())) {
3671  if (userSet.insert(user).second)
3672  printUserIDs(user, index);
3673  }
3674 }
3675 
3676 void OperationPrinter::printUserIDs(Operation *user, bool prefixComma) {
3677  if (prefixComma)
3678  os << ", ";
3679 
3680  if (!user->getNumResults()) {
3681  printOperationID(user);
3682  } else {
3683  interleaveComma(user->getResults(),
3684  [this](Value result) { printValueID(result); });
3685  }
3686 }
3687 
3688 void OperationPrinter::printCustomOrGenericOp(Operation *op) {
3689  // If requested, always print the generic form.
3690  if (!printerFlags.shouldPrintGenericOpForm()) {
3691  // Check to see if this is a known operation. If so, use the registered
3692  // custom printer hook.
3693  if (auto opInfo = op->getRegisteredInfo()) {
3694  opInfo->printAssembly(op, *this, defaultDialectStack.back());
3695  return;
3696  }
3697  // Otherwise try to dispatch to the dialect, if available.
3698  if (Dialect *dialect = op->getDialect()) {
3699  if (auto opPrinter = dialect->getOperationPrinter(op)) {
3700  // Print the op name first.
3701  StringRef name = op->getName().getStringRef();
3702  // Only drop the default dialect prefix when it cannot lead to
3703  // ambiguities.
3704  if (name.count('.') == 1)
3705  name.consume_front((defaultDialectStack.back() + ".").str());
3706  os << name;
3707 
3708  // Print the rest of the op now.
3709  opPrinter(op, *this);
3710  return;
3711  }
3712  }
3713  }
3714 
3715  // Otherwise print with the generic assembly form.
3716  printGenericOp(op, /*printOpName=*/true);
3717 }
3718 
3719 void OperationPrinter::printGenericOp(Operation *op, bool printOpName) {
3720  if (printOpName)
3721  printEscapedString(op->getName().getStringRef());
3722  os << '(';
3723  interleaveComma(op->getOperands(), [&](Value value) { printValueID(value); });
3724  os << ')';
3725 
3726  // For terminators, print the list of successors and their operands.
3727  if (op->getNumSuccessors() != 0) {
3728  os << '[';
3729  interleaveComma(op->getSuccessors(),
3730  [&](Block *successor) { printBlockName(successor); });
3731  os << ']';
3732  }
3733 
3734  // Print the properties.
3735  if (Attribute prop = op->getPropertiesAsAttribute()) {
3736  os << " <";
3737  Impl::printAttribute(prop);
3738  os << '>';
3739  }
3740 
3741  // Print regions.
3742  if (op->getNumRegions() != 0) {
3743  os << " (";
3744  interleaveComma(op->getRegions(), [&](Region &region) {
3745  printRegion(region, /*printEntryBlockArgs=*/true,
3746  /*printBlockTerminators=*/true, /*printEmptyBlock=*/true);
3747  });
3748  os << ')';
3749  }
3750 
3751  printOptionalAttrDict(op->getPropertiesStorage()
3752  ? llvm::to_vector(op->getDiscardableAttrs())
3753  : op->getAttrs());
3754 
3755  // Print the type signature of the operation.
3756  os << " : ";
3757  printFunctionalType(op);
3758 }
3759 
3760 void OperationPrinter::printBlockName(Block *block) {
3761  os << state.getSSANameState().getBlockInfo(block).name;
3762 }
3763 
3764 void OperationPrinter::print(Block *block, bool printBlockArgs,
3765  bool printBlockTerminator) {
3766  // Print the block label and argument list if requested.
3767  if (printBlockArgs) {
3768  os.indent(currentIndent);
3769  printBlockName(block);
3770 
3771  // Print the argument list if non-empty.
3772  if (!block->args_empty()) {
3773  os << '(';
3774  interleaveComma(block->getArguments(), [&](BlockArgument arg) {
3775  printValueID(arg);
3776  os << ": ";
3777  printType(arg.getType());
3778  // TODO: We should allow location aliases on block arguments.
3779  printTrailingLocation(arg.getLoc(), /*allowAlias*/ false);
3780  });
3781  os << ')';
3782  }
3783  os << ':';
3784 
3785  // Print out some context information about the predecessors of this block.
3786  if (!block->getParent()) {
3787  os << " // block is not in a region!";
3788  } else if (block->hasNoPredecessors()) {
3789  if (!block->isEntryBlock())
3790  os << " // no predecessors";
3791  } else if (auto *pred = block->getSinglePredecessor()) {
3792  os << " // pred: ";
3793  printBlockName(pred);
3794  } else {
3795  // We want to print the predecessors in a stable order, not in
3796  // whatever order the use-list is in, so gather and sort them.
3797  SmallVector<BlockInfo, 4> predIDs;
3798  for (auto *pred : block->getPredecessors())
3799  predIDs.push_back(state.getSSANameState().getBlockInfo(pred));
3800  llvm::sort(predIDs, [](BlockInfo lhs, BlockInfo rhs) {
3801  return lhs.ordering < rhs.ordering;
3802  });
3803 
3804  os << " // " << predIDs.size() << " preds: ";
3805 
3806  interleaveComma(predIDs, [&](BlockInfo pred) { os << pred.name; });
3807  }
3808  os << newLine;
3809  }
3810 
3811  currentIndent += indentWidth;
3812 
3813  if (printerFlags.shouldPrintValueUsers()) {
3814  for (BlockArgument arg : block->getArguments()) {
3815  os.indent(currentIndent);
3816  printUsersComment(arg);
3817  }
3818  }
3819 
3820  bool hasTerminator =
3821  !block->empty() && block->back().hasTrait<OpTrait::IsTerminator>();
3822  auto range = llvm::make_range(
3823  block->begin(),
3824  std::prev(block->end(),
3825  (!hasTerminator || printBlockTerminator) ? 0 : 1));
3826  for (auto &op : range) {
3827  printFullOpWithIndentAndLoc(&op);
3828  os << newLine;
3829  }
3830  currentIndent -= indentWidth;
3831 }
3832 
3833 void OperationPrinter::printValueID(Value value, bool printResultNo,
3834  raw_ostream *streamOverride) const {
3835  state.getSSANameState().printValueID(value, printResultNo,
3836  streamOverride ? *streamOverride : os);
3837 }
3838 
3839 void OperationPrinter::printOperationID(Operation *op,
3840  raw_ostream *streamOverride) const {
3841  state.getSSANameState().printOperationID(op, streamOverride ? *streamOverride
3842  : os);
3843 }
3844 
3845 void OperationPrinter::printSuccessor(Block *successor) {
3846  printBlockName(successor);
3847 }
3848 
3849 void OperationPrinter::printSuccessorAndUseList(Block *successor,
3850  ValueRange succOperands) {
3851  printBlockName(successor);
3852  if (succOperands.empty())
3853  return;
3854 
3855  os << '(';
3856  interleaveComma(succOperands,
3857  [this](Value operand) { printValueID(operand); });
3858  os << " : ";
3859  interleaveComma(succOperands,
3860  [this](Value operand) { printType(operand.getType()); });
3861  os << ')';
3862 }
3863 
3864 void OperationPrinter::printRegion(Region &region, bool printEntryBlockArgs,
3865  bool printBlockTerminators,
3866  bool printEmptyBlock) {
3867  if (printerFlags.shouldSkipRegions()) {
3868  os << "{...}";
3869  return;
3870  }
3871  os << "{" << newLine;
3872  if (!region.empty()) {
3873  auto restoreDefaultDialect =
3874  llvm::make_scope_exit([&]() { defaultDialectStack.pop_back(); });
3875  if (auto iface = dyn_cast<OpAsmOpInterface>(region.getParentOp()))
3876  defaultDialectStack.push_back(iface.getDefaultDialect());
3877  else
3878  defaultDialectStack.push_back("");
3879 
3880  auto *entryBlock = &region.front();
3881  // Force printing the block header if printEmptyBlock is set and the block
3882  // is empty or if printEntryBlockArgs is set and there are arguments to
3883  // print.
3884  bool shouldAlwaysPrintBlockHeader =
3885  (printEmptyBlock && entryBlock->empty()) ||
3886  (printEntryBlockArgs && entryBlock->getNumArguments() != 0);
3887  print(entryBlock, shouldAlwaysPrintBlockHeader, printBlockTerminators);
3888  for (auto &b : llvm::drop_begin(region.getBlocks(), 1))
3889  print(&b);
3890  }
3891  os.indent(currentIndent) << "}";
3892 }
3893 
3894 void OperationPrinter::printAffineMapOfSSAIds(AffineMapAttr mapAttr,
3895  ValueRange operands) {
3896  if (!mapAttr) {
3897  os << "<<NULL AFFINE MAP>>";
3898  return;
3899  }
3900  AffineMap map = mapAttr.getValue();
3901  unsigned numDims = map.getNumDims();
3902  auto printValueName = [&](unsigned pos, bool isSymbol) {
3903  unsigned index = isSymbol ? numDims + pos : pos;
3904  assert(index < operands.size());
3905  if (isSymbol)
3906  os << "symbol(";
3907  printValueID(operands[index]);
3908  if (isSymbol)
3909  os << ')';
3910  };
3911 
3912  interleaveComma(map.getResults(), [&](AffineExpr expr) {
3913  printAffineExpr(expr, printValueName);
3914  });
3915 }
3916 
3917 void OperationPrinter::printAffineExprOfSSAIds(AffineExpr expr,
3918  ValueRange dimOperands,
3919  ValueRange symOperands) {
3920  auto printValueName = [&](unsigned pos, bool isSymbol) {
3921  if (!isSymbol)
3922  return printValueID(dimOperands[pos]);
3923  os << "symbol(";
3924  printValueID(symOperands[pos]);
3925  os << ')';
3926  };
3927  printAffineExpr(expr, printValueName);
3928 }
3929 
3930 //===----------------------------------------------------------------------===//
3931 // print and dump methods
3932 //===----------------------------------------------------------------------===//
3933 
3934 void Attribute::print(raw_ostream &os, bool elideType) const {
3935  if (!*this) {
3936  os << "<<NULL ATTRIBUTE>>";
3937  return;
3938  }
3939 
3940  AsmState state(getContext());
3941  print(os, state, elideType);
3942 }
3943 void Attribute::print(raw_ostream &os, AsmState &state, bool elideType) const {
3944  using AttrTypeElision = AsmPrinter::Impl::AttrTypeElision;
3945  AsmPrinter::Impl(os, state.getImpl())
3946  .printAttribute(*this, elideType ? AttrTypeElision::Must
3947  : AttrTypeElision::Never);
3948 }
3949 
3950 void Attribute::dump() const {
3951  print(llvm::errs());
3952  llvm::errs() << "\n";
3953 }
3954 
3955 void Attribute::printStripped(raw_ostream &os, AsmState &state) const {
3956  if (!*this) {
3957  os << "<<NULL ATTRIBUTE>>";
3958  return;
3959  }
3960 
3961  AsmPrinter::Impl subPrinter(os, state.getImpl());
3962  if (succeeded(subPrinter.printAlias(*this)))
3963  return;
3964 
3965  auto &dialect = this->getDialect();
3966  uint64_t posPrior = os.tell();
3967  DialectAsmPrinter printer(subPrinter);
3968  dialect.printAttribute(*this, printer);
3969  if (posPrior != os.tell())
3970  return;
3971 
3972  // Fallback to printing with prefix if the above failed to write anything
3973  // to the output stream.
3974  print(os, state);
3975 }
3976 void Attribute::printStripped(raw_ostream &os) const {
3977  if (!*this) {
3978  os << "<<NULL ATTRIBUTE>>";
3979  return;
3980  }
3981 
3982  AsmState state(getContext());
3983  printStripped(os, state);
3984 }
3985 
3986 void Type::print(raw_ostream &os) const {
3987  if (!*this) {
3988  os << "<<NULL TYPE>>";
3989  return;
3990  }
3991 
3992  AsmState state(getContext());
3993  print(os, state);
3994 }
3995 void Type::print(raw_ostream &os, AsmState &state) const {
3996  AsmPrinter::Impl(os, state.getImpl()).printType(*this);
3997 }
3998 
3999 void Type::dump() const {
4000  print(llvm::errs());
4001  llvm::errs() << "\n";
4002 }
4003 
4004 void AffineMap::dump() const {
4005  print(llvm::errs());
4006  llvm::errs() << "\n";
4007 }
4008 
4009 void IntegerSet::dump() const {
4010  print(llvm::errs());
4011  llvm::errs() << "\n";
4012 }
4013 
4014 void AffineExpr::print(raw_ostream &os) const {
4015  if (!expr) {
4016  os << "<<NULL AFFINE EXPR>>";
4017  return;
4018  }
4019  AsmState state(getContext());
4020  AsmPrinter::Impl(os, state.getImpl()).printAffineExpr(*this);
4021 }
4022 
4023 void AffineExpr::dump() const {
4024  print(llvm::errs());
4025  llvm::errs() << "\n";
4026 }
4027 
4028 void AffineMap::print(raw_ostream &os) const {
4029  if (!map) {
4030  os << "<<NULL AFFINE MAP>>";
4031  return;
4032  }
4033  AsmState state(getContext());
4034  AsmPrinter::Impl(os, state.getImpl()).printAffineMap(*this);
4035 }
4036 
4037 void IntegerSet::print(raw_ostream &os) const {
4038  AsmState state(getContext());
4039  AsmPrinter::Impl(os, state.getImpl()).printIntegerSet(*this);
4040 }
4041 
4042 void Value::print(raw_ostream &os) const { print(os, OpPrintingFlags()); }
4043 void Value::print(raw_ostream &os, const OpPrintingFlags &flags) const {
4044  if (!impl) {
4045  os << "<<NULL VALUE>>";
4046  return;
4047  }
4048 
4049  if (auto *op = getDefiningOp())
4050  return op->print(os, flags);
4051  // TODO: Improve BlockArgument print'ing.
4052  BlockArgument arg = llvm::cast<BlockArgument>(*this);
4053  os << "<block argument> of type '" << arg.getType()
4054  << "' at index: " << arg.getArgNumber();
4055 }
4056 void Value::print(raw_ostream &os, AsmState &state) const {
4057  if (!impl) {
4058  os << "<<NULL VALUE>>";
4059  return;
4060  }
4061 
4062  if (auto *op = getDefiningOp())
4063  return op->print(os, state);
4064 
4065  // TODO: Improve BlockArgument print'ing.
4066  BlockArgument arg = llvm::cast<BlockArgument>(*this);
4067  os << "<block argument> of type '" << arg.getType()
4068  << "' at index: " << arg.getArgNumber();
4069 }
4070 
4071 void Value::dump() const {
4072  print(llvm::errs());
4073  llvm::errs() << "\n";
4074 }
4075 
4076 void Value::printAsOperand(raw_ostream &os, AsmState &state) const {
4077  // TODO: This doesn't necessarily capture all potential cases.
4078  // Currently, region arguments can be shadowed when printing the main
4079  // operation. If the IR hasn't been printed, this will produce the old SSA
4080  // name and not the shadowed name.
4081  state.getImpl().getSSANameState().printValueID(*this, /*printResultNo=*/true,
4082  os);
4083 }
4084 
4085 static Operation *findParent(Operation *op, bool shouldUseLocalScope) {
4086  do {
4087  // If we are printing local scope, stop at the first operation that is
4088  // isolated from above.
4089  if (shouldUseLocalScope && op->hasTrait<OpTrait::IsIsolatedFromAbove>())
4090  break;
4091 
4092  // Otherwise, traverse up to the next parent.
4093  Operation *parentOp = op->getParentOp();
4094  if (!parentOp)
4095  break;
4096  op = parentOp;
4097  } while (true);
4098  return op;
4099 }
4100 
4101 void Value::printAsOperand(raw_ostream &os,
4102  const OpPrintingFlags &flags) const {
4103  Operation *op;
4104  if (auto result = llvm::dyn_cast<OpResult>(*this)) {
4105  op = result.getOwner();
4106  } else {
4107  op = llvm::cast<BlockArgument>(*this).getOwner()->getParentOp();
4108  if (!op) {
4109  os << "<<UNKNOWN SSA VALUE>>";
4110  return;
4111  }
4112  }
4113  op = findParent(op, flags.shouldUseLocalScope());
4114  AsmState state(op, flags);
4115  printAsOperand(os, state);
4116 }
4117 
4118 void Operation::print(raw_ostream &os, const OpPrintingFlags &printerFlags) {
4119  // Find the operation to number from based upon the provided flags.
4120  Operation *op = findParent(this, printerFlags.shouldUseLocalScope());
4121  AsmState state(op, printerFlags);
4122  print(os, state);
4123 }
4124 void Operation::print(raw_ostream &os, AsmState &state) {
4125  OperationPrinter printer(os, state.getImpl());
4126  if (!getParent() && !state.getPrinterFlags().shouldUseLocalScope()) {
4127  state.getImpl().initializeAliases(this);
4128  printer.printTopLevelOperation(this);
4129  } else {
4130  printer.printFullOpWithIndentAndLoc(this);
4131  }
4132 }
4133 
4135  print(llvm::errs(), OpPrintingFlags().useLocalScope());
4136  llvm::errs() << "\n";
4137 }
4138 
4140  print(llvm::errs(), OpPrintingFlags().useLocalScope().assumeVerified());
4141  llvm::errs() << "\n";
4142 }
4143 
4144 void Block::print(raw_ostream &os) {
4145  Operation *parentOp = getParentOp();
4146  if (!parentOp) {
4147  os << "<<UNLINKED BLOCK>>\n";
4148  return;
4149  }
4150  // Get the top-level op.
4151  while (auto *nextOp = parentOp->getParentOp())
4152  parentOp = nextOp;
4153 
4154  AsmState state(parentOp);
4155  print(os, state);
4156 }
4157 void Block::print(raw_ostream &os, AsmState &state) {
4158  OperationPrinter(os, state.getImpl()).print(this);
4159 }
4160 
4161 void Block::dump() { print(llvm::errs()); }
4162 
4163 /// Print out the name of the block without printing its body.
4164 void Block::printAsOperand(raw_ostream &os, bool printType) {
4165  Operation *parentOp = getParentOp();
4166  if (!parentOp) {
4167  os << "<<UNLINKED BLOCK>>\n";
4168  return;
4169  }
4170  AsmState state(parentOp);
4171  printAsOperand(os, state);
4172 }
4173 void Block::printAsOperand(raw_ostream &os, AsmState &state) {
4174  OperationPrinter printer(os, state.getImpl());
4175  printer.printBlockName(this);
4176 }
4177 
4178 raw_ostream &mlir::operator<<(raw_ostream &os, Block &block) {
4179  block.print(os);
4180  return os;
4181 }
4182 
4183 //===--------------------------------------------------------------------===//
4184 // Custom printers
4185 //===--------------------------------------------------------------------===//
4186 namespace mlir {
4187 
4189  ArrayRef<int64_t> dimensions) {
4190  if (dimensions.empty())
4191  printer << "[";
4192  printer.printDimensionList(dimensions);
4193  if (dimensions.empty())
4194  printer << "]";
4195 }
4196 
4197 ParseResult parseDimensionList(OpAsmParser &parser,
4198  DenseI64ArrayAttr &dimensions) {
4199  // Empty list case denoted by "[]".
4200  if (succeeded(parser.parseOptionalLSquare())) {
4201  if (failed(parser.parseRSquare())) {
4202  return parser.emitError(parser.getCurrentLocation())
4203  << "Failed parsing dimension list.";
4204  }
4205  dimensions =
4207  return success();
4208  }
4209 
4210  // Non-empty list case.
4211  SmallVector<int64_t> shapeArr;
4212  if (failed(parser.parseDimensionList(shapeArr, true, false))) {
4213  return parser.emitError(parser.getCurrentLocation())
4214  << "Failed parsing dimension list.";
4215  }
4216  if (shapeArr.empty()) {
4217  return parser.emitError(parser.getCurrentLocation())
4218  << "Failed parsing dimension list. Did you mean an empty list? It "
4219  "must be denoted by \"[]\".";
4220  }
4221  dimensions = DenseI64ArrayAttr::get(parser.getContext(), shapeArr);
4222  return success();
4223 }
4224 
4225 } // 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::@1241::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:155
~DialectAsmParser() override
This is a pure-virtual base class that exposes the asmprinter hooks necessary to implement a custom p...
~DialectAsmPrinter() override
A collection of dialect interfaces within a context, for a given concrete interface type.
Dialects are groups of MLIR operations, types and attributes, as well as behavior associated with the...
Definition: Dialect.h:38
virtual void printAttribute(Attribute, DialectAsmPrinter &) const
Print an attribute registered to this dialect.
Definition: Dialect.h:99
virtual void printType(Type, DialectAsmPrinter &) const
Print a type registered to this dialect.
Definition: Dialect.h:107
An attribute that associates a referenced attribute with a unique identifier.
A fallback map containing external resources not explicitly handled by another parser/printer.
Definition: AsmState.h:421
std::vector< std::unique_ptr< AsmResourcePrinter > > getPrinters()
Build a set of resource printers to print the resources within this map.
An integer set representing a conjunction of one or more affine equalities and inequalities.
Definition: IntegerSet.h:44
unsigned getNumDims() const
Definition: IntegerSet.cpp:15
unsigned getNumConstraints() const
Definition: IntegerSet.cpp:21
AffineExpr getConstraint(unsigned idx) const
Definition: IntegerSet.cpp:45
bool isEq(unsigned idx) const
Returns true if the idx^th constraint is an equality, false if it is an inequality.
Definition: IntegerSet.cpp:55
unsigned getNumSymbols() const
Definition: IntegerSet.cpp:16
Location objects represent source locations information in MLIR.
Definition: Location.h:32
T findInstanceOf()
Return an instance of the given location type if one is nested under the current location.
Definition: Location.h:45
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition: Location.h:76
MLIRContext is the top-level object for a collection of MLIR operations.
Definition: MLIRContext.h: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:447
Operation * getOwner() const
Returns the operation that owns this result.
Definition: Value.h:456
unsigned getResultNumber() const
Returns the number of this result.
Definition: Value.h:459
This class provides the API for ops that are known to be isolated from above.
This class provides the API for ops that are known to be terminators.
Definition: OpDefinition.h: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:348
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:522
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition: Types.h:74
const void * getAsOpaquePointer() const
Methods for supporting PointerLikeTypeTraits.
Definition: Types.h:164
Dialect & getDialect() const
Get the dialect this type is registered to.
Definition: Types.h:107
static Type getFromOpaquePointer(const void *pointer)
Definition: Types.h:167
void walkImmediateSubElements(function_ref< void(Attribute)> walkAttrsFn, function_ref< void(Type)> walkTypesFn) const
Walk all of the immediately nested sub-attributes and sub-types.
Definition: Types.h:194
This class provides an abstraction over the different types of ranges over Values.
Definition: ValueRange.h:387
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition: Value.h:96
bool use_empty() const
Returns true if this value has no uses.
Definition: Value.h:208
void dump() const
void print(raw_ostream &os) const
Type getType() const
Return the type of this value.
Definition: Value.h:105
void printAsOperand(raw_ostream &os, AsmState &state) const
Print this value as if it were an operand.
user_range getUsers() const
Definition: Value.h:218
Location getLoc() const
Return the location of this value.
Definition: Value.cpp: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:491
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.