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