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