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