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