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