[llvm] [GlobalISel] Optimized MatchData Handling (PR #92115)

via llvm-commits llvm-commits at lists.llvm.org
Tue May 14 06:26:58 PDT 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-globalisel

Author: Pierre van Houtryve (Pierre-vh)

<details>
<summary>Changes</summary>

Surprisingly, MatchDatas were very, very expensive in GlobalISel Combiners. The time spent on constructing the big MatchData struct, and copying it to reset it (`operator=`) was equal or greater than the time spent iterating the MatchTable itself and executing it (excluding the time spent in callees, of course).

This was because I put every MatchData we could possibly need in a big struct, and rebuilt that struct every instruction we looked at. Turns out that when all your function is doing is iterating over a byte array and executing opcodes, the time spent allocating and initializing a huge non-trivially constructible/destructible struct is very, very significant.

Now, we use a simpler and more efficient strategy: be as lazy as possible, only allocate and deallocate as needed, end of story. The backend doesn't think about it anymore, and we simply don't pay for what we don't use.

---
Full diff: https://github.com/llvm/llvm-project/pull/92115.diff


7 Files Affected:

- (modified) llvm/include/llvm/CodeGen/GlobalISel/Combiner.h (+50) 
- (modified) llvm/test/TableGen/GlobalISelCombinerEmitter/match-table.td (+13-10) 
- (modified) llvm/test/TableGen/GlobalISelCombinerEmitter/pattern-parsing.td (+4-4) 
- (modified) llvm/utils/TableGen/Common/CMakeLists.txt (-1) 
- (removed) llvm/utils/TableGen/Common/GlobalISel/MatchDataInfo.cpp (-49) 
- (removed) llvm/utils/TableGen/Common/GlobalISel/MatchDataInfo.h (-90) 
- (modified) llvm/utils/TableGen/GlobalISelCombinerEmitter.cpp (+77-29) 


``````````diff
diff --git a/llvm/include/llvm/CodeGen/GlobalISel/Combiner.h b/llvm/include/llvm/CodeGen/GlobalISel/Combiner.h
index f826601544932..c3b483bdc200f 100644
--- a/llvm/include/llvm/CodeGen/GlobalISel/Combiner.h
+++ b/llvm/include/llvm/CodeGen/GlobalISel/Combiner.h
@@ -60,6 +60,56 @@ class Combiner : public GIMatchTableExecutor {
 
   bool combineMachineInstrs();
 
+  /// Base class for all dynamic MatchDatas definitions, used for type erasure
+  /// purposes.
+  ///
+  /// As derived classes may have non-trivial destructors, we need to be able to
+  /// call the destructor through the unique_ptr of the base class.
+  ///
+  /// There are two ways to achieve this.
+  ///   - (Easiest) Make MatchDataBase have a virtual destructor.
+  ///   - Use a custom deleter
+  ///
+  /// We use solution number 2, that way we don't have a vtable in all MatchData
+  /// objects (making them more compact), and we can have trivially destructible
+  /// MatchDatas, which avoids useless virtual function calls and allows the
+  /// compiler/libraries to use faster code (as it knows no destructor needs to
+  /// be called).
+  ///
+  /// This is really a micro-optimization, but MatchData used to be a
+  /// performance issue so better safe than sorry.
+  struct MatchDataBase {};
+
+  /// If a MatchData instance is active, cast it to Ty and return it.
+  /// Otherwise, create a new MatchData instance of type Ty and return it.
+  template <typename Ty> Ty &getOrCreateMatchData() const {
+    static_assert(std::is_base_of_v<MatchDataBase, Ty>,
+                  "Type must derive from MatchDataBase to be allocatable!");
+    // Allocate if we have no MatchData active, or if the MatchData that's
+    // active is not the one that we want.
+    if (!MatchData || Ty::ID != MatchDataID) {
+      MatchDataID = Ty::ID;
+      MatchData = std::unique_ptr<Ty, MatchDataDeleter>(
+          new Ty(), [](MatchDataBase *MD) { delete static_cast<Ty *>(MD); });
+    }
+    return *static_cast<Ty *>(MatchData.get());
+  }
+
+  /// Deallocates the currently active MatchData instance.
+  ///
+  /// TODO: Can we get away with never actually calling this? The MatchData
+  /// instance would then just be deleted by getOrCreateMatchData or when the
+  /// Combiner class is deallocated. I'm just a bit scared it'd cause issues
+  /// with captured values in some of the lambdas used as MatchInfo.
+  void resetMatchData() const { MatchData.reset(); }
+
+private:
+  using MatchDataDeleter = void (*)(MatchDataBase *);
+
+  mutable std::unique_ptr<MatchDataBase, MatchDataDeleter> MatchData = {
+      nullptr, [](auto *) {}};
+  mutable unsigned MatchDataID = unsigned(-1);
+
 protected:
   CombinerInfo &CInfo;
   GISelChangeObserver &Observer;
diff --git a/llvm/test/TableGen/GlobalISelCombinerEmitter/match-table.td b/llvm/test/TableGen/GlobalISelCombinerEmitter/match-table.td
index 1052e31b2d051..837f4c76c0f18 100644
--- a/llvm/test/TableGen/GlobalISelCombinerEmitter/match-table.td
+++ b/llvm/test/TableGen/GlobalISelCombinerEmitter/match-table.td
@@ -76,8 +76,15 @@ def MyCombiner: GICombiner<"GenMyCombiner", [
 
 // We have at most 2 registers used by one rule at a time, so we should only have 2 registers MDInfos.
 
-// CHECK:      struct MatchInfosTy {
-// CHECK-NEXT:   Register MDInfo0, MDInfo1;
+// CHECK:      struct MatchDataRule2 : MatchDataBase {
+// CHECK-NEXT:   static constexpr unsigned ID = 2;
+// CHECK-NEXT:   Register   r0;
+// CHECK-NEXT:   Register   r1;
+// CHECK-NEXT: };
+
+// CHECK:      struct MatchDataRule3 : MatchDataBase {
+// CHECK-NEXT:   static constexpr unsigned ID = 3;
+// CHECK-NEXT:   Register   r0;
 // CHECK-NEXT: };
 
 // Check predicates
@@ -96,13 +103,9 @@ def MyCombiner: GICombiner<"GenMyCombiner", [
 // CHECK-NEXT:   B.setInstrAndDebugLoc(I);
 // CHECK-NEXT:   State.MIs.clear();
 // CHECK-NEXT:   State.MIs.push_back(&I);
-// CHECK-NEXT:   MatchInfos = MatchInfosTy();
-// CHECK-EMPTY:
-// CHECK-NEXT:   if (executeMatchTable(*this, State, ExecInfo, B, getMatchTable(), *ST.getInstrInfo(), MRI, *MRI.getTargetRegisterInfo(), *ST.getRegBankInfo(), AvailableFeatures, /*CoverageInfo*/ nullptr))
-// CHECK-NEXT:     return true;
-// CHECK-NEXT:   }
-// CHECK-EMPTY:
-// CHECK-NEXT:   return false;
+// CHECK-NEXT:   const bool Result = executeMatchTable(*this, State, ExecInfo, B, getMatchTable(), *ST.getInstrInfo(), MRI, *MRI.getTargetRegisterInfo(), *ST.getRegBankInfo(), AvailableFeatures, /*CoverageInfo*/ nullptr);
+// CHECK-NEXT:   resetMatchData();
+// CHECK-NEXT:   return Result;
 // CHECK-NEXT: }
 
 // Check apply
@@ -120,7 +123,7 @@ def MyCombiner: GICombiner<"GenMyCombiner", [
 // CHECK-NEXT:   }
 // CHECK-NEXT:   case GICXXCustomAction_CombineApplyGICombiner1:{
 // CHECK-NEXT:     Helper.getBuilder().setInstrAndDebugLoc(*State.MIs[0]);
-// CHECK-NEXT:     APPLY MatchInfos.MDInfo0, MatchInfos.MDInfo1
+// CHECK-NEXT:     APPLY getOrCreateMatchData<MatchDataRule2>().r0, getOrCreateMatchData<MatchDataRule2>().r1
 // CHECK-NEXT:     return;
 // CHECK-NEXT:   }
 // CHECK-NEXT:   case GICXXCustomAction_CombineApplyGICombiner2:{
diff --git a/llvm/test/TableGen/GlobalISelCombinerEmitter/pattern-parsing.td b/llvm/test/TableGen/GlobalISelCombinerEmitter/pattern-parsing.td
index c261ec4575453..3de2924608faa 100644
--- a/llvm/test/TableGen/GlobalISelCombinerEmitter/pattern-parsing.td
+++ b/llvm/test/TableGen/GlobalISelCombinerEmitter/pattern-parsing.td
@@ -64,8 +64,8 @@ def InstTest0 : GICombineRule<
   (apply [{ APPLY }])>;
 
 // CHECK:      (CombineRule name:InstTest1 id:3 root:d
-// CHECK-NEXT:   (MatchDatas
-// CHECK-NEXT:      (MatchDataInfo pattern_symbol:r0 type:'Register' var_name:MDInfo0)
+// CHECK-NEXT:   (MatchData typename:MatchDataRule3 id:3
+// CHECK-NEXT:     (MatchDataDef symbol:r0 type:Register)
 // CHECK-NEXT:   )
 // CHECK-NEXT:   (MatchPats
 // CHECK-NEXT:     <match_root>d:(CodeGenInstructionPattern COPY operands:[<def>$a, i32:$b])
@@ -89,8 +89,8 @@ def InstTest1 : GICombineRule<
   (apply [{ APPLY }])>;
 
 // CHECK:      (CombineRule name:InstTest2 id:4 root:d
-// CHECK-NEXT:   (MatchDatas
-// CHECK-NEXT:     (MatchDataInfo pattern_symbol:r0 type:'Register' var_name:MDInfo0)
+// CHECK-NEXT:   (MatchData typename:MatchDataRule4 id:4
+// CHECK-NEXT:     (MatchDataDef symbol:r0 type:Register)
 // CHECK-NEXT:   )
 // CHECK-NEXT:   (MatchPats
 // CHECK-NEXT:     <match_root>__InstTest2_match_0:(CodeGenInstructionPattern COPY operands:[<def>$d, (i32 0):$x])
diff --git a/llvm/utils/TableGen/Common/CMakeLists.txt b/llvm/utils/TableGen/Common/CMakeLists.txt
index c31ed5a1de690..30f188ae48a2a 100644
--- a/llvm/utils/TableGen/Common/CMakeLists.txt
+++ b/llvm/utils/TableGen/Common/CMakeLists.txt
@@ -16,7 +16,6 @@ add_llvm_library(LLVMTableGenCommon STATIC OBJECT EXCLUDE_FROM_ALL
   GlobalISel/CXXPredicates.cpp
   GlobalISel/GlobalISelMatchTable.cpp
   GlobalISel/GlobalISelMatchTableExecutorEmitter.cpp
-  GlobalISel/MatchDataInfo.cpp
   GlobalISel/PatternParser.cpp
   GlobalISel/Patterns.cpp
 
diff --git a/llvm/utils/TableGen/Common/GlobalISel/MatchDataInfo.cpp b/llvm/utils/TableGen/Common/GlobalISel/MatchDataInfo.cpp
deleted file mode 100644
index b5c9e4f8c2485..0000000000000
--- a/llvm/utils/TableGen/Common/GlobalISel/MatchDataInfo.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-//===- 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/Common/GlobalISel/MatchDataInfo.h b/llvm/utils/TableGen/Common/GlobalISel/MatchDataInfo.h
deleted file mode 100644
index abe1245bc67d0..0000000000000
--- a/llvm/utils/TableGen/Common/GlobalISel/MatchDataInfo.h
+++ /dev/null
@@ -1,90 +0,0 @@
-//===- 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
-//
-//===----------------------------------------------------------------------===//
-//
-/// \file Contains utilities related to handling "match data" for GlobalISel
-///  Combiners. Match data allows for setting some arbitrary data in the "match"
-///  phase and pass it down to the "apply" phase.
-//
-//===----------------------------------------------------------------------===//
-
-#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/GlobalISelCombinerEmitter.cpp b/llvm/utils/TableGen/GlobalISelCombinerEmitter.cpp
index ef9e9ff04f85f..02f84bd9f02df 100644
--- a/llvm/utils/TableGen/GlobalISelCombinerEmitter.cpp
+++ b/llvm/utils/TableGen/GlobalISelCombinerEmitter.cpp
@@ -35,7 +35,6 @@
 #include "Common/GlobalISel/CombinerUtils.h"
 #include "Common/GlobalISel/GlobalISelMatchTable.h"
 #include "Common/GlobalISel/GlobalISelMatchTableExecutorEmitter.h"
-#include "Common/GlobalISel/MatchDataInfo.h"
 #include "Common/GlobalISel/PatternParser.h"
 #include "Common/GlobalISel/Patterns.h"
 #include "Common/SubtargetFeatureInfo.h"
@@ -605,6 +604,56 @@ CombineRuleOperandTypeChecker::getRuleEqClasses() const {
   return TECs;
 }
 
+//===- MatchData Handling -------------------------------------------------===//
+//
+/// Parsing
+///   Record all MatchData definitions seen.
+///
+///   Once all defs have been parsed, handleRuleMatchDataDefs is used to create
+///   a new MatchDataStruct (if at least one MatchDataDef exists) and add it to
+///   AllMatchDataStructDecls for emission later.
+///
+/// Layout
+///   Each rule that has at least one MatchData will create a struct (See
+///   getMatchDataStructTypeName) that derives from Combiner::MatchDataBase
+///   and containing each MatchData definition. Fields in that struct are
+///   named after the MatchData symbols.
+///
+/// Access/Code Expansion
+///   Combiner::getOrCreateMatchData is used to fetch (and lazily allocate)
+///   MatchDatas as they're needed.
+struct MatchDataDef {
+  MatchDataDef(StringRef Symbol, StringRef Type) : Symbol(Symbol), Type(Type) {}
+
+  StringRef Symbol;
+  StringRef Type;
+};
+
+struct MatchDataStruct {
+  std::string TypeName;
+  unsigned ID;
+  std::vector<MatchDataDef> Fields;
+};
+
+static std::vector<std::unique_ptr<MatchDataStruct>> AllMatchDataStructs;
+
+/// If \p Defs has at least one item, prepare a new MatchDataStruct to contain
+/// this rule's MatchData defs. If \p Defs is empty, return nullptr.
+static const MatchDataStruct *
+handleRuleMatchDataDefs(ArrayRef<MatchDataDef> Defs, unsigned RuleID) {
+  if (Defs.empty())
+    return nullptr;
+
+  auto MDS = std::make_unique<MatchDataStruct>();
+  MDS->TypeName = "MatchDataRule" + std::to_string(RuleID);
+  MDS->ID = RuleID;
+  for (const auto &Def : Defs)
+    MDS->Fields.push_back(Def);
+  auto *Ptr = MDS.get();
+  AllMatchDataStructs.push_back(std::move(MDS));
+  return Ptr;
+}
+
 //===- CombineRuleBuilder -------------------------------------------------===//
 
 /// Parses combine rule and builds a small intermediate representation to tie
@@ -777,8 +826,8 @@ class CombineRuleBuilder {
   Pattern *MatchRoot = nullptr;
   SmallDenseSet<InstructionPattern *, 2> ApplyRoots;
 
-  SmallVector<MatchDataInfo, 2> MatchDatas;
   SmallVector<PatternAlternatives, 1> PermutationsToEmit;
+  const MatchDataStruct *RuleMatchData = nullptr;
 };
 
 bool CombineRuleBuilder::parseAll() {
@@ -842,13 +891,12 @@ void CombineRuleBuilder::print(raw_ostream &OS) const {
   OS << "(CombineRule name:" << RuleDef.getName() << " id:" << RuleID
      << " root:" << RootName << '\n';
 
-  if (!MatchDatas.empty()) {
-    OS << "  (MatchDatas\n";
-    for (const auto &MD : MatchDatas) {
-      OS << "    ";
-      MD.print(OS);
-      OS << '\n';
-    }
+  if (RuleMatchData) {
+    OS << "  (MatchData typename:" << RuleMatchData->TypeName
+       << " id:" << RuleMatchData->ID << "\n";
+    for (const auto &MD : RuleMatchData->Fields)
+      OS << "    (MatchDataDef symbol:" << MD.Symbol << " type:" << MD.Type
+         << ")\n";
     OS << "  )\n";
   }
 
@@ -1007,8 +1055,13 @@ bool CombineRuleBuilder::addMatchPattern(std::unique_ptr<Pattern> Pat) {
 
 void CombineRuleBuilder::declareAllMatchDatasExpansions(
     CodeExpansions &CE) const {
-  for (const auto &MD : MatchDatas)
-    CE.declare(MD.getPatternSymbol(), MD.getQualifiedVariableName());
+  if (!RuleMatchData)
+    return;
+
+  const std::string BaseAccessor =
+      "getOrCreateMatchData<" + RuleMatchData->TypeName + ">().";
+  for (const auto &MD : RuleMatchData->Fields)
+    CE.declare(MD.Symbol, (BaseAccessor + MD.Symbol).str());
 }
 
 void CombineRuleBuilder::addCXXPredicate(RuleMatcher &M,
@@ -1429,6 +1482,7 @@ bool CombineRuleBuilder::parseDefs(const DagInit &Def) {
     return false;
   }
 
+  SmallVector<MatchDataDef, 2> MatchDatas;
   SmallVector<StringRef> Roots;
   for (unsigned I = 0, E = Def.getNumArgs(); I < E; ++I) {
     if (isSpecificDef(*Def.getArg(I), "root")) {
@@ -1463,9 +1517,7 @@ bool CombineRuleBuilder::parseDefs(const DagInit &Def) {
   }
 
   RootName = Roots.front();
-
-  // Assign variables to all MatchDatas.
-  AssignMatchDataVariables(MatchDatas);
+  RuleMatchData = handleRuleMatchDataDefs(MatchDatas, RuleID);
   return true;
 }
 
@@ -2371,15 +2423,12 @@ void GICombinerEmitter::emitAdditionalImpl(raw_ostream &OS) {
      << "  B.setInstrAndDebugLoc(I);\n"
      << "  State.MIs.clear();\n"
      << "  State.MIs.push_back(&I);\n"
-     << "  " << MatchDataInfo::StructName << " = "
-     << MatchDataInfo::StructTypeName << "();\n\n"
-     << "  if (executeMatchTable(*this, State, ExecInfo, B"
+     << "  const bool Result = executeMatchTable(*this, State, ExecInfo, B"
      << ", getMatchTable(), *ST.getInstrInfo(), MRI, "
         "*MRI.getTargetRegisterInfo(), *ST.getRegBankInfo(), AvailableFeatures"
-     << ", /*CoverageInfo*/ nullptr)) {\n"
-     << "    return true;\n"
-     << "  }\n\n"
-     << "  return false;\n"
+     << ", /*CoverageInfo*/ nullptr);\n"
+     << "  resetMatchData();\n"
+     << "  return Result;\n"
      << "}\n\n";
 }
 
@@ -2472,14 +2521,13 @@ void GICombinerEmitter::emitRunCustomAction(raw_ostream &OS) {
 
 void GICombinerEmitter::emitAdditionalTemporariesDecl(raw_ostream &OS,
                                                       StringRef Indent) {
-  OS << Indent << "struct " << MatchDataInfo::StructTypeName << " {\n";
-  for (const auto &[Type, VarNames] : AllMatchDataVars) {
-    assert(!VarNames.empty() && "Cannot have no vars for this type!");
-    OS << Indent << "  " << Type << " " << join(VarNames, ", ") << ";\n";
-  }
-  OS << Indent << "};\n"
-     << Indent << "mutable " << MatchDataInfo::StructTypeName << " "
-     << MatchDataInfo::StructName << ";\n\n";
+  for (const auto &MDS : AllMatchDataStructs) {
+    OS << Indent << "struct " << MDS->TypeName << " : MatchDataBase {\n"
+       << Indent << "  static constexpr unsigned ID = " << MDS->ID << ";\n";
+    for (const auto &Field : MDS->Fields)
+      OS << Indent << "  " << Field.Type << " " << Field.Symbol << ";\n";
+    OS << Indent << "};\n\n";
+  }
 }
 
 GICombinerEmitter::GICombinerEmitter(RecordKeeper &RK,

``````````

</details>


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


More information about the llvm-commits mailing list