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