[llvm] [TableGen] Spit GlobalISelCombinerEmitter into multiple files (PR #73325)

via llvm-commits llvm-commits at lists.llvm.org
Fri Nov 24 05:13:09 PST 2023


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-globalisel

Author: Pierre van Houtryve (Pierre-vh)

<details>
<summary>Changes</summary>

Split MatchDataInfo, CXXPredicates and the Pattern hierarchy into their own files.

This should help with maintenance a bit, and make the API easier to navigate.
I hope this encourages a bit more experimentation with MIR patterns, e.g. I'd like to try getting them in ISel at some point.

Currently, this is pretty much only moving code around. There is no significant refactoring in there.
I want to split the Combiner backend even more at some point though, e.g. by separating the TableGen parsing logic into yet another file so other backends could very easily parse patterns themselves. 

Note: I moved the responsibility of managing string lifetimes into the backend instead of the Pattern class.
e.g. Before you'd do `P.addOperand(Name)` but now it's `P.addOperand(insertStrRef(Name))`.
I verified this was done correctly by running the tests with UBSan/ASan.

---

Patch is 122.83 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/73325.diff


8 Files Affected:

- (modified) llvm/utils/TableGen/GlobalISel/CMakeLists.txt (+3) 
- (added) llvm/utils/TableGen/GlobalISel/CXXPredicates.cpp (+51) 
- (added) llvm/utils/TableGen/GlobalISel/CXXPredicates.h (+83) 
- (added) llvm/utils/TableGen/GlobalISel/MatchDataInfo.cpp (+49) 
- (added) llvm/utils/TableGen/GlobalISel/MatchDataInfo.h (+87) 
- (added) llvm/utils/TableGen/GlobalISel/Patterns.cpp (+845) 
- (added) llvm/utils/TableGen/GlobalISel/Patterns.h (+690) 
- (modified) llvm/utils/TableGen/GlobalISelCombinerEmitter.cpp (+84-1621) 


``````````diff
diff --git a/llvm/utils/TableGen/GlobalISel/CMakeLists.txt b/llvm/utils/TableGen/GlobalISel/CMakeLists.txt
index a85f1ac6cc9366a..7262c405839934a 100644
--- a/llvm/utils/TableGen/GlobalISel/CMakeLists.txt
+++ b/llvm/utils/TableGen/GlobalISel/CMakeLists.txt
@@ -5,6 +5,9 @@ set(LLVM_LINK_COMPONENTS
 
 add_llvm_library(LLVMTableGenGlobalISel STATIC DISABLE_LLVM_LINK_LLVM_DYLIB
   CodeExpander.cpp
+  CXXPredicates.cpp
+  MatchDataInfo.cpp
+  Patterns.cpp
 
   DEPENDS
   vt_gen
diff --git a/llvm/utils/TableGen/GlobalISel/CXXPredicates.cpp b/llvm/utils/TableGen/GlobalISel/CXXPredicates.cpp
new file mode 100644
index 000000000000000..e39293ebfe7aee2
--- /dev/null
+++ b/llvm/utils/TableGen/GlobalISel/CXXPredicates.cpp
@@ -0,0 +1,51 @@
+//===- CXXPredicates.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+//
+//===----------------------------------------------------------------------===//
+
+#include "CXXPredicates.h"
+#include "llvm/ADT/STLExtras.h"
+
+namespace llvm {
+namespace gi {
+
+std::vector<const CXXPredicateCode *>
+CXXPredicateCode::getSorted(const CXXPredicateCodePool &Pool) {
+  std::vector<const CXXPredicateCode *> Out;
+  std::transform(Pool.begin(), Pool.end(), std::back_inserter(Out),
+                 [&](auto &Elt) { return Elt.second.get(); });
+  sort(Out, [](const auto *A, const auto *B) { return A->ID < B->ID; });
+  return Out;
+}
+
+const CXXPredicateCode &CXXPredicateCode::get(CXXPredicateCodePool &Pool,
+                                              std::string Code) {
+  // Check if we already have an identical piece of code, if not, create an
+  // entry in the pool.
+  const auto CodeHash = hash_value(Code);
+  if (auto It = Pool.find(CodeHash); It != Pool.end())
+    return *It->second;
+
+  const auto ID = Pool.size();
+  auto OwnedData = std::unique_ptr<CXXPredicateCode>(
+      new CXXPredicateCode(std::move(Code), ID));
+  const auto &DataRef = *OwnedData;
+  Pool[CodeHash] = std::move(OwnedData);
+  return DataRef;
+}
+
+// TODO: Make BaseEnumName prefix configurable.
+CXXPredicateCode::CXXPredicateCode(std::string Code, unsigned ID)
+    : Code(Code), ID(ID), BaseEnumName("GICombiner" + std::to_string(ID)) {}
+
+CXXPredicateCode::CXXPredicateCodePool CXXPredicateCode::AllCXXMatchCode;
+CXXPredicateCode::CXXPredicateCodePool CXXPredicateCode::AllCXXApplyCode;
+
+} // namespace gi
+} // namespace llvm
diff --git a/llvm/utils/TableGen/GlobalISel/CXXPredicates.h b/llvm/utils/TableGen/GlobalISel/CXXPredicates.h
new file mode 100644
index 000000000000000..c2aa0f0fbbc5974
--- /dev/null
+++ b/llvm/utils/TableGen/GlobalISel/CXXPredicates.h
@@ -0,0 +1,83 @@
+//===- CXXPredicates.h ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_UTILS_MIRPATTERNS_CXXPREDICATES_H
+#define LLVM_UTILS_MIRPATTERNS_CXXPREDICATES_H
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/Hashing.h"
+#include "llvm/ADT/StringRef.h"
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace llvm {
+namespace gi {
+
+/// Entry into the static pool of all CXX Predicate code. This contains
+/// fully expanded C++ code.
+///
+/// The static pool is hidden inside the object and can be accessed through
+/// getAllMatchCode/getAllApplyCode
+///
+/// Note that CXXPattern trims C++ code, so the Code is already expected to be
+/// free of leading/trailing whitespace.
+class CXXPredicateCode {
+  using CXXPredicateCodePool =
+      DenseMap<hash_code, std::unique_ptr<CXXPredicateCode>>;
+  static CXXPredicateCodePool AllCXXMatchCode;
+  static CXXPredicateCodePool AllCXXApplyCode;
+
+  /// Sorts a `CXXPredicateCodePool` by their IDs and returns it.
+  static std::vector<const CXXPredicateCode *>
+  getSorted(const CXXPredicateCodePool &Pool);
+
+  /// Gets an instance of `CXXPredicateCode` for \p Code, or returns an already
+  /// existing one.
+  static const CXXPredicateCode &get(CXXPredicateCodePool &Pool,
+                                     std::string Code);
+
+  CXXPredicateCode(std::string Code, unsigned ID);
+
+public:
+  static const CXXPredicateCode &getMatchCode(std::string Code) {
+    return get(AllCXXMatchCode, std::move(Code));
+  }
+
+  static const CXXPredicateCode &getApplyCode(std::string Code) {
+    return get(AllCXXApplyCode, std::move(Code));
+  }
+
+  static std::vector<const CXXPredicateCode *> getAllMatchCode() {
+    return getSorted(AllCXXMatchCode);
+  }
+
+  static std::vector<const CXXPredicateCode *> getAllApplyCode() {
+    return getSorted(AllCXXApplyCode);
+  }
+
+  const std::string Code;
+  const unsigned ID;
+  const std::string BaseEnumName;
+
+  bool needsUnreachable() const {
+    return !StringRef(Code).starts_with("return");
+  }
+
+  std::string getEnumNameWithPrefix(StringRef Prefix) const {
+    return Prefix.str() + BaseEnumName;
+  }
+};
+
+} // namespace gi
+} // end namespace llvm
+
+#endif // ifndef LLVM_UTILS_MIRPATTERNS_CXXPREDICATES_H
diff --git a/llvm/utils/TableGen/GlobalISel/MatchDataInfo.cpp b/llvm/utils/TableGen/GlobalISel/MatchDataInfo.cpp
new file mode 100644
index 000000000000000..b5c9e4f8c24852d
--- /dev/null
+++ b/llvm/utils/TableGen/GlobalISel/MatchDataInfo.cpp
@@ -0,0 +1,49 @@
+//===- MatchDataInfo.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+//
+//===----------------------------------------------------------------------===//
+
+#include "MatchDataInfo.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace llvm {
+namespace gi {
+
+StringMap<std::vector<std::string>> AllMatchDataVars;
+
+StringRef MatchDataInfo::getVariableName() const {
+  assert(hasVariableName());
+  return VarName;
+}
+
+void MatchDataInfo::print(raw_ostream &OS) const {
+  OS << "(MatchDataInfo pattern_symbol:" << PatternSymbol << " type:'" << Type
+     << "' var_name:" << (VarName.empty() ? "<unassigned>" : VarName) << ")";
+}
+
+void MatchDataInfo::dump() const { print(dbgs()); }
+
+void AssignMatchDataVariables(MutableArrayRef<MatchDataInfo> Infos) {
+  static unsigned NextVarID = 0;
+
+  StringMap<unsigned> SeenTypes;
+  for (auto &Info : Infos) {
+    unsigned &NumSeen = SeenTypes[Info.getType()];
+    auto &ExistingVars = AllMatchDataVars[Info.getType()];
+
+    if (NumSeen == ExistingVars.size())
+      ExistingVars.push_back("MDInfo" + std::to_string(NextVarID++));
+
+    Info.setVariableName(ExistingVars[NumSeen++]);
+  }
+}
+
+} // namespace gi
+} // namespace llvm
diff --git a/llvm/utils/TableGen/GlobalISel/MatchDataInfo.h b/llvm/utils/TableGen/GlobalISel/MatchDataInfo.h
new file mode 100644
index 000000000000000..1960a3a7c24d9b7
--- /dev/null
+++ b/llvm/utils/TableGen/GlobalISel/MatchDataInfo.h
@@ -0,0 +1,87 @@
+//===- MatchDataInfo.h ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_UTILS_MIRPATTERNS_MATCHDATAINFO_H
+#define LLVM_UTILS_MIRPATTERNS_MATCHDATAINFO_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+#include <string>
+#include <vector>
+
+namespace llvm {
+
+class raw_ostream;
+
+namespace gi {
+
+/// Represents MatchData defined by the match stage and required by the apply
+/// stage.
+///
+/// This allows the plumbing of arbitrary data from C++ predicates between the
+/// stages.
+///
+/// When this class is initially created, it only has a pattern symbol and a
+/// type. When all of the MatchDatas declarations of a given pattern have been
+/// parsed, `AssignVariables` must be called to assign storage variable names to
+/// each MatchDataInfo.
+class MatchDataInfo {
+  StringRef PatternSymbol;
+  StringRef Type;
+  std::string VarName;
+
+public:
+  static constexpr StringLiteral StructTypeName = "MatchInfosTy";
+  static constexpr StringLiteral StructName = "MatchInfos";
+
+  MatchDataInfo(StringRef PatternSymbol, StringRef Type)
+      : PatternSymbol(PatternSymbol), Type(Type.trim()) {}
+
+  StringRef getPatternSymbol() const { return PatternSymbol; };
+  StringRef getType() const { return Type; };
+
+  bool hasVariableName() const { return !VarName.empty(); }
+  void setVariableName(StringRef Name) { VarName = Name; }
+  StringRef getVariableName() const;
+
+  std::string getQualifiedVariableName() const {
+    return StructName.str() + "." + getVariableName().str();
+  }
+
+  void print(raw_ostream &OS) const;
+  void dump() const;
+};
+
+/// Pool of type -> variables used to emit MatchData variables declarations.
+///
+/// e.g. if the map contains "int64_t" -> ["MD0", "MD1"], then two variable
+/// declarations must be emitted: `int64_t MD0` and `int64_t MD1`.
+///
+/// This has a static lifetime and will outlive all the `MatchDataInfo` objects
+/// by design. It needs a static lifetime so the backends can emit variable
+/// declarations after processing all the inputs.
+extern StringMap<std::vector<std::string>> AllMatchDataVars;
+
+/// Assign variable names to all MatchDatas used by a pattern. This must be
+/// called after all MatchData decls have been parsed for a given processing
+/// unit (e.g. a combine rule)
+///
+/// Requires an array of MatchDataInfo so we can handle cases where a pattern
+/// uses multiple instances of the same MatchData type.
+///
+/// Writes to \ref AllMatchDataVars.
+void AssignMatchDataVariables(MutableArrayRef<MatchDataInfo> Infos);
+
+} // namespace gi
+} // end namespace llvm
+
+#endif // ifndef LLVM_UTILS_MIRPATTERNS_MATCHDATAINFO_H
diff --git a/llvm/utils/TableGen/GlobalISel/Patterns.cpp b/llvm/utils/TableGen/GlobalISel/Patterns.cpp
new file mode 100644
index 000000000000000..d4325771c0a8844
--- /dev/null
+++ b/llvm/utils/TableGen/GlobalISel/Patterns.cpp
@@ -0,0 +1,845 @@
+//===- Patterns.cpp --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+//
+//===----------------------------------------------------------------------===//
+
+#include "Patterns.h"
+#include "../CodeGenInstruction.h"
+#include "CXXPredicates.h"
+#include "CodeExpander.h"
+#include "CodeExpansions.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/TableGen/Error.h"
+#include "llvm/TableGen/Record.h"
+
+namespace llvm {
+namespace gi {
+
+//===- PatternType --------------------------------------------------------===//
+
+std::optional<PatternType> PatternType::get(ArrayRef<SMLoc> DiagLoc,
+                                            const Record *R, Twine DiagCtx) {
+  assert(R);
+  if (R->isSubClassOf("ValueType")) {
+    PatternType PT(PT_ValueType);
+    PT.Data.Def = R;
+    return PT;
+  }
+
+  if (R->isSubClassOf(TypeOfClassName)) {
+    auto RawOpName = R->getValueAsString("OpName");
+    if (!RawOpName.starts_with("$")) {
+      PrintError(DiagLoc, DiagCtx + ": invalid operand name format '" +
+                              RawOpName + "' in " + TypeOfClassName +
+                              ": expected '$' followed by an operand name");
+      return std::nullopt;
+    }
+
+    PatternType PT(PT_TypeOf);
+    PT.Data.Str = RawOpName.drop_front(1);
+    return PT;
+  }
+
+  PrintError(DiagLoc, DiagCtx + ": unknown type '" + R->getName() + "'");
+  return std::nullopt;
+}
+
+PatternType PatternType::getTypeOf(StringRef OpName) {
+  PatternType PT(PT_TypeOf);
+  PT.Data.Str = OpName;
+  return PT;
+}
+
+StringRef PatternType::getTypeOfOpName() const {
+  assert(isTypeOf());
+  return Data.Str;
+}
+
+const Record *PatternType::getLLTRecord() const {
+  assert(isLLT());
+  return Data.Def;
+}
+
+bool PatternType::operator==(const PatternType &Other) const {
+  if (Kind != Other.Kind)
+    return false;
+
+  switch (Kind) {
+  case PT_None:
+    return true;
+  case PT_ValueType:
+    return Data.Def == Other.Data.Def;
+  case PT_TypeOf:
+    return Data.Str == Other.Data.Str;
+  }
+
+  llvm_unreachable("Unknown Type Kind");
+}
+
+std::string PatternType::str() const {
+  switch (Kind) {
+  case PT_None:
+    return "";
+  case PT_ValueType:
+    return Data.Def->getName().str();
+  case PT_TypeOf:
+    return (TypeOfClassName + "<$" + getTypeOfOpName() + ">").str();
+  }
+
+  llvm_unreachable("Unknown type!");
+}
+
+//===- Pattern ------------------------------------------------------------===//
+
+void Pattern::dump() const { return print(dbgs()); }
+
+const char *Pattern::getKindName() const {
+  switch (Kind) {
+  case K_AnyOpcode:
+    return "AnyOpcodePattern";
+  case K_CXX:
+    return "CXXPattern";
+  case K_CodeGenInstruction:
+    return "CodeGenInstructionPattern";
+  case K_PatFrag:
+    return "PatFragPattern";
+  case K_Builtin:
+    return "BuiltinPattern";
+  }
+
+  llvm_unreachable("unknown pattern kind!");
+}
+
+void Pattern::printImpl(raw_ostream &OS, bool PrintName,
+                        function_ref<void()> ContentPrinter) const {
+  OS << "(" << getKindName() << " ";
+  if (PrintName)
+    OS << "name:" << getName() << " ";
+  ContentPrinter();
+  OS << ")";
+}
+
+//===- AnyOpcodePattern ---------------------------------------------------===//
+
+void AnyOpcodePattern::print(raw_ostream &OS, bool PrintName) const {
+  printImpl(OS, PrintName, [&OS, this]() {
+    OS << "["
+       << join(map_range(Insts,
+                         [](const auto *I) { return I->TheDef->getName(); }),
+               ", ")
+       << "]";
+  });
+}
+
+//===- CXXPattern ---------------------------------------------------------===//
+
+CXXPattern::CXXPattern(const StringInit &Code, StringRef Name)
+    : CXXPattern(Code.getAsUnquotedString(), Name) {}
+
+const CXXPredicateCode &
+CXXPattern::expandCode(const CodeExpansions &CE, ArrayRef<SMLoc> Locs,
+                       function_ref<void(raw_ostream &)> AddComment) const {
+  std::string Result;
+  raw_string_ostream OS(Result);
+
+  if (AddComment)
+    AddComment(OS);
+
+  CodeExpander Expander(RawCode, CE, Locs, /*ShowExpansions*/ false);
+  Expander.emit(OS);
+  if (IsApply)
+    return CXXPredicateCode::getApplyCode(std::move(Result));
+  return CXXPredicateCode::getMatchCode(std::move(Result));
+}
+
+void CXXPattern::print(raw_ostream &OS, bool PrintName) const {
+  printImpl(OS, PrintName, [&OS, this] {
+    OS << (IsApply ? "apply" : "match") << " code:\"";
+    printEscapedString(getRawCode(), OS);
+    OS << "\"";
+  });
+}
+
+//===- InstructionOperand -------------------------------------------------===//
+
+std::string InstructionOperand::describe() const {
+  if (!hasImmValue())
+    return "MachineOperand $" + getOperandName().str() + "";
+  std::string Str = "imm " + std::to_string(getImmValue());
+  if (isNamedImmediate())
+    Str += ":$" + getOperandName().str() + "";
+  return Str;
+}
+
+void InstructionOperand::print(raw_ostream &OS) const {
+  if (isDef())
+    OS << "<def>";
+
+  bool NeedsColon = true;
+  if (Type) {
+    if (hasImmValue())
+      OS << "(" << Type.str() << " " << getImmValue() << ")";
+    else
+      OS << Type.str();
+  } else if (hasImmValue())
+    OS << getImmValue();
+  else
+    NeedsColon = false;
+
+  if (isNamedOperand())
+    OS << (NeedsColon ? ":" : "") << "$" << getOperandName();
+}
+
+void InstructionOperand::dump() const { return print(dbgs()); }
+
+//===- InstructionPattern -------------------------------------------------===//
+
+bool InstructionPattern::diagnoseAllSpecialTypes(ArrayRef<SMLoc> Loc,
+                                                 Twine Msg) const {
+  bool HasDiag = false;
+  for (const auto &[Idx, Op] : enumerate(operands())) {
+    if (Op.getType().isSpecial()) {
+      PrintError(Loc, Msg);
+      PrintNote(Loc, "operand " + Twine(Idx) + " of '" + getName() +
+                         "' has type '" + Op.getType().str() + "'");
+      HasDiag = true;
+    }
+  }
+  return HasDiag;
+}
+
+void InstructionPattern::reportUnreachable(ArrayRef<SMLoc> Locs) const {
+  PrintError(Locs, "pattern '" + getName() + "' ('" + getInstName() +
+                       "') is unreachable from the pattern root!");
+}
+
+bool InstructionPattern::checkSemantics(ArrayRef<SMLoc> Loc) {
+  unsigned NumExpectedOperands = getNumInstOperands();
+
+  if (isVariadic()) {
+    if (Operands.size() < NumExpectedOperands) {
+      PrintError(Loc, +"'" + getInstName() + "' expected at least " +
+                          Twine(NumExpectedOperands) + " operands, got " +
+                          Twine(Operands.size()));
+      return false;
+    }
+  } else if (NumExpectedOperands != Operands.size()) {
+    PrintError(Loc, +"'" + getInstName() + "' expected " +
+                        Twine(NumExpectedOperands) + " operands, got " +
+                        Twine(Operands.size()));
+    return false;
+  }
+
+  unsigned OpIdx = 0;
+  unsigned NumDefs = getNumInstDefs();
+  for (auto &Op : Operands)
+    Op.setIsDef(OpIdx++ < NumDefs);
+
+  return true;
+}
+
+void InstructionPattern::print(raw_ostream &OS, bool PrintName) const {
+  printImpl(OS, PrintName, [&OS, this] {
+    OS << getInstName() << " operands:[";
+    StringRef Sep = "";
+    for (const auto &Op : Operands) {
+      OS << Sep;
+      Op.print(OS);
+      Sep = ", ";
+    }
+    OS << "]";
+
+    printExtras(OS);
+  });
+}
+
+//===- OperandTable -------------------------------------------------------===//
+
+bool OperandTable::addPattern(InstructionPattern *P,
+                              function_ref<void(StringRef)> DiagnoseRedef) {
+  for (const auto &Op : P->named_operands()) {
+    StringRef OpName = Op.getOperandName();
+
+    // We always create an entry in the OperandTable, even for uses.
+    // Uses of operands that don't have a def (= live-ins) will remain with a
+    // nullptr as the Def.
+    //
+    // This allows us tell whether an operand exists in a pattern or not. If
+    // there is no entry for it, it doesn't exist, if there is an entry, it's
+    // used/def'd at least once.
+    auto &Def = Table[OpName];
+
+    if (!Op.isDef())
+      continue;
+
+    if (Def) {
+      DiagnoseRedef(OpName);
+      return false;
+    }
+
+    Def = P;
+  }
+
+  return true;
+}
+
+void OperandTable::print(raw_ostream &OS, StringRef Name,
+                         StringRef Indent) const {
+  OS << Indent << "(OperandTable ";
+  if (!Name.empty())
+    OS << Name << " ";
+  if (Table.empty()) {
+    OS << "<empty>)\n";
+    return;
+  }
+
+  SmallVector<StringRef, 0> Keys(Table.keys());
+  sort(Keys);
+
+  OS << '\n';
+  for (const auto &Key : Keys) {
+    const auto *Def = Table.at(Key);
+    OS << Indent << "  " << Key << " -> "
+       << (Def ? Def->getName() : "<live-in>") << '\n';
+  }
+  OS << Indent << ")\n";
+}
+
+void OperandTable::dump() const { print(dbgs()); }
+
+//===- MIFlagsInfo --------------------------------------------------------===//
+
+void MIFlagsInfo::addSetFlag(const Record *R) {
+  SetF.insert(R->getValueAsString("EnumName"));
+}
+
+void MIFlagsInfo::addUnsetFlag(const Record *R) {
+  UnsetF.insert(R->getValueAsString("EnumName"));
+}
+
+voi...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/73325


More information about the llvm-commits mailing list