[llvm] [TableGen][DecoderEmitter] Factor DecoderTableBuilder (PR #155220)

Sergei Barannikov via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 25 02:12:52 PDT 2025


https://github.com/s-barannikov updated https://github.com/llvm/llvm-project/pull/155220

>From c1152943e17a99bf0ac13ff6252c092a1cb9bddc Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <barannikov88 at gmail.com>
Date: Mon, 25 Aug 2025 09:08:01 +0300
Subject: [PATCH 1/2] [TableGen][DecoderEmitter] Remove PredicateNamespace
 (NFC)

There is no target object named Thumb, so there is no need to make
a special case for it.

As part of this change, pass CodeGenTarget instead of DecoderEmitter
to FilterChooser to remove dependency between the latter two.
---
 llvm/utils/TableGen/DecoderEmitter.cpp      | 32 ++++++++-------------
 llvm/utils/TableGen/DisassemblerEmitter.cpp |  5 +---
 llvm/utils/TableGen/TableGenBackends.h      |  3 +-
 3 files changed, 14 insertions(+), 26 deletions(-)

diff --git a/llvm/utils/TableGen/DecoderEmitter.cpp b/llvm/utils/TableGen/DecoderEmitter.cpp
index 4f6eeb3922f9a..773f6a63c675b 100644
--- a/llvm/utils/TableGen/DecoderEmitter.cpp
+++ b/llvm/utils/TableGen/DecoderEmitter.cpp
@@ -340,7 +340,7 @@ class DecoderEmitter {
   SmallDenseMap<unsigned, std::vector<unsigned>> EncodingIDsByHwMode;
 
 public:
-  DecoderEmitter(const RecordKeeper &RK, StringRef PredicateNamespace);
+  explicit DecoderEmitter(const RecordKeeper &RK);
 
   const CodeGenTarget &getTarget() const { return Target; }
 
@@ -370,9 +370,6 @@ class DecoderEmitter {
                                   NamespacesHwModesMap &NamespacesWithHwModes);
 
   void parseInstructionEncodings();
-
-public:
-  StringRef PredicateNamespace;
 };
 
 } // end anonymous namespace
@@ -487,8 +484,7 @@ class FilterChooser {
   /// This field allows us to ignore the extra bits.
   unsigned MaxFilterWidth;
 
-  // Parent emitter
-  const DecoderEmitter *Emitter;
+  const CodeGenTarget &Target;
 
   /// If the selected filter matches multiple encodings, then this is the
   /// starting position and the width of the filtered range.
@@ -524,9 +520,9 @@ class FilterChooser {
   /// Constructs a top-level filter chooser.
   FilterChooser(ArrayRef<InstructionEncoding> Encodings,
                 ArrayRef<unsigned> EncodingIDs, unsigned MaxFilterWidth,
-                const DecoderEmitter *E)
+                const CodeGenTarget &Target)
       : Encodings(Encodings), EncodingIDs(EncodingIDs), Parent(nullptr),
-        MaxFilterWidth(MaxFilterWidth), Emitter(E) {
+        MaxFilterWidth(MaxFilterWidth), Target(Target) {
     // Sort encoding IDs once.
     stable_sort(this->EncodingIDs, LessEncodingIDByWidth(Encodings));
     // Filter width is the width of the smallest encoding.
@@ -542,7 +538,7 @@ class FilterChooser {
                 ArrayRef<unsigned> EncodingIDs, const KnownBits &FilterBits,
                 const FilterChooser &Parent)
       : Encodings(Encodings), EncodingIDs(EncodingIDs), Parent(&Parent),
-        MaxFilterWidth(Parent.MaxFilterWidth), Emitter(Parent.Emitter) {
+        MaxFilterWidth(Parent.MaxFilterWidth), Target(Parent.Target) {
     // Inferior filter choosers are created from sorted array of encoding IDs.
     assert(is_sorted(EncodingIDs, LessEncodingIDByWidth(Encodings)));
     assert(!FilterBits.hasConflict() && "Broken filter");
@@ -1155,8 +1151,7 @@ bool FilterChooser::emitPredicateMatchAux(const Init &Val, bool ParenIfBinOp,
   if (const auto *D = dyn_cast<DefInit>(&Val)) {
     if (!D->getDef()->isSubClassOf("SubtargetFeature"))
       return true;
-    OS << "Bits[" << Emitter->PredicateNamespace << "::" << D->getAsString()
-       << "]";
+    OS << "Bits[" << Target.getName() << "::" << D->getAsString() << "]";
     return false;
   }
   if (const auto *D = dyn_cast<DagInit>(&Val)) {
@@ -1350,7 +1345,7 @@ void FilterChooser::emitSingletonTableEntry(DecoderTableInfo &TableInfo,
                                      : MCD::OPC_TryDecode;
   TableInfo.Table.insertOpcode(DecoderOp);
   const Record *InstDef = Encodings[EncodingID].getInstruction()->TheDef;
-  TableInfo.Table.insertULEB128(Emitter->getTarget().getInstrIntValue(InstDef));
+  TableInfo.Table.insertULEB128(Target.getInstrIntValue(InstDef));
   TableInfo.Table.insertULEB128(DIdx);
 
   if (DecoderOp == MCD::OPC_TryDecode) {
@@ -2502,10 +2497,8 @@ void DecoderEmitter::parseInstructionEncodings() {
   NumEncodings = NumEncodingsSupported + NumEncodingsOmitted;
 }
 
-DecoderEmitter::DecoderEmitter(const RecordKeeper &RK,
-                               StringRef PredicateNamespace)
-    : RK(RK), Target(RK), CGH(Target.getHwModes()),
-      PredicateNamespace(PredicateNamespace) {
+DecoderEmitter::DecoderEmitter(const RecordKeeper &RK)
+    : RK(RK), Target(RK), CGH(Target.getHwModes()) {
   Target.reverseBitsForLittleEndianEncoding();
   parseInstructionEncodings();
 }
@@ -2560,7 +2553,7 @@ namespace {
     auto [DecoderNamespace, HwModeID, Size] = Key;
     const unsigned BitWidth = IsVarLenInst ? MaxInstLen : 8 * Size;
     // Emit the decoder for this (namespace, hwmode, width) combination.
-    FilterChooser FC(Encodings, EncodingIDs, BitWidth, this);
+    FilterChooser FC(Encodings, EncodingIDs, BitWidth, Target);
 
     // The decode table is cleared for each top level decoder function. The
     // predicates and decoders themselves, however, are shared across all
@@ -2603,7 +2596,6 @@ namespace {
   OS << "\n} // namespace\n";
 }
 
-void llvm::EmitDecoder(const RecordKeeper &RK, raw_ostream &OS,
-                       StringRef PredicateNamespace) {
-  DecoderEmitter(RK, PredicateNamespace).run(OS);
+void llvm::EmitDecoder(const RecordKeeper &RK, raw_ostream &OS) {
+  DecoderEmitter(RK).run(OS);
 }
diff --git a/llvm/utils/TableGen/DisassemblerEmitter.cpp b/llvm/utils/TableGen/DisassemblerEmitter.cpp
index 15a11a3a99406..5bc259a5fc8a4 100644
--- a/llvm/utils/TableGen/DisassemblerEmitter.cpp
+++ b/llvm/utils/TableGen/DisassemblerEmitter.cpp
@@ -123,10 +123,7 @@ static void emitDisassembler(const RecordKeeper &Records, raw_ostream &OS) {
     return;
   }
 
-  StringRef PredicateNamespace = Target.getName();
-  if (PredicateNamespace == "Thumb")
-    PredicateNamespace = "ARM";
-  EmitDecoder(Records, OS, PredicateNamespace);
+  EmitDecoder(Records, OS);
 }
 
 cl::OptionCategory DisassemblerEmitterCat("Options for -gen-disassembler");
diff --git a/llvm/utils/TableGen/TableGenBackends.h b/llvm/utils/TableGen/TableGenBackends.h
index d1cbc5d1605d8..6e13864d12e16 100644
--- a/llvm/utils/TableGen/TableGenBackends.h
+++ b/llvm/utils/TableGen/TableGenBackends.h
@@ -64,8 +64,7 @@ class RecordKeeper;
 void EmitMapTable(const RecordKeeper &RK, raw_ostream &OS);
 
 // Defined in DecoderEmitter.cpp
-void EmitDecoder(const RecordKeeper &RK, raw_ostream &OS,
-                 StringRef PredicateNamespace);
+void EmitDecoder(const RecordKeeper &RK, raw_ostream &OS);
 
 } // namespace llvm
 

>From 88e59e7f7c4e4ad038c5b1e47aadf85b96f26dd3 Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <barannikov88 at gmail.com>
Date: Mon, 25 Aug 2025 11:04:37 +0300
Subject: [PATCH 2/2] [TableGen][DecoderEmitter] Factor DecoderTableBuilder

---
 llvm/utils/TableGen/DecoderEmitter.cpp | 163 ++++++++++++++-----------
 1 file changed, 90 insertions(+), 73 deletions(-)

diff --git a/llvm/utils/TableGen/DecoderEmitter.cpp b/llvm/utils/TableGen/DecoderEmitter.cpp
index 773f6a63c675b..37b16cb49911f 100644
--- a/llvm/utils/TableGen/DecoderEmitter.cpp
+++ b/llvm/utils/TableGen/DecoderEmitter.cpp
@@ -466,6 +466,9 @@ enum bitAttr_t {
 /// decide what further remaining bits to look at.
 
 class FilterChooser {
+  // TODO: Unfriend by providing the necessary accessors.
+  friend class DecoderTableBuilder;
+
   // Vector of encodings to choose our filter.
   ArrayRef<InstructionEncoding> Encodings;
 
@@ -578,30 +581,6 @@ class FilterChooser {
   // decoded bits in order to verify that the instruction matches the Opcode.
   std::vector<Island> getIslands(const KnownBits &EncodingBits) const;
 
-  // Emits code to check the Predicates member of an instruction are true.
-  // Returns true if predicate matches were emitted, false otherwise.
-  bool emitPredicateMatch(raw_ostream &OS, unsigned EncodingID) const;
-  bool emitPredicateMatchAux(const Init &Val, bool ParenIfBinOp,
-                             raw_ostream &OS) const;
-
-  bool doesOpcodeNeedPredicate(unsigned EncodingID) const;
-  unsigned getPredicateIndex(DecoderTableInfo &TableInfo, StringRef P) const;
-  void emitPredicateTableEntry(DecoderTableInfo &TableInfo,
-                               unsigned EncodingID) const;
-
-  void emitSoftFailTableEntry(DecoderTableInfo &TableInfo,
-                              unsigned EncodingID) const;
-
-  // Emits table entries to decode the singleton.
-  void emitSingletonTableEntry(DecoderTableInfo &TableInfo,
-                               unsigned EncodingID) const;
-
-  void emitBinaryParser(raw_ostream &OS, indent Indent,
-                        const OperandInfo &OpInfo) const;
-
-  void emitDecoder(raw_ostream &OS, indent Indent, unsigned EncodingID) const;
-  unsigned getDecoderIndex(DecoderSet &Decoders, unsigned EncodingID) const;
-
   // reportRegion is a helper function for filterProcessor to mark a region as
   // eligible for use as a filter region.
   void reportRegion(std::vector<std::unique_ptr<Filter>> &Filters, bitAttr_t RA,
@@ -622,12 +601,48 @@ class FilterChooser {
   void doFilter();
 
 public:
-  /// Emits state machine entries to decode our share of instructions.
-  void emitTableEntries(DecoderTableInfo &TableInfo) const;
-
   void dump() const;
 };
 
+class DecoderTableBuilder {
+  const CodeGenTarget &Target;
+  ArrayRef<InstructionEncoding> Encodings;
+  DecoderTableInfo &TableInfo;
+
+public:
+  DecoderTableBuilder(const CodeGenTarget &Target,
+                      ArrayRef<InstructionEncoding> Encodings,
+                      DecoderTableInfo &TableInfo)
+      : Target(Target), Encodings(Encodings), TableInfo(TableInfo) {}
+
+  void buildTable(const FilterChooser &FC) const;
+
+private:
+  void emitBinaryParser(raw_ostream &OS, indent Indent,
+                        const OperandInfo &OpInfo) const;
+
+  void emitDecoder(raw_ostream &OS, indent Indent, unsigned EncodingID) const;
+
+  unsigned getDecoderIndex(unsigned EncodingID) const;
+
+  unsigned getPredicateIndex(StringRef P) const;
+
+  bool emitPredicateMatchAux(const Init &Val, bool ParenIfBinOp,
+                             raw_ostream &OS) const;
+
+  bool emitPredicateMatch(raw_ostream &OS, unsigned EncodingID) const;
+
+  bool doesOpcodeNeedPredicate(unsigned EncodingID) const;
+
+  void emitPredicateTableEntry(unsigned EncodingID) const;
+
+  void emitSoftFailTableEntry(unsigned EncodingID) const;
+
+  void emitSingletonTableEntry(const FilterChooser &FC) const;
+
+  void emitTableEntries(const FilterChooser &FC) const;
+};
+
 } // end anonymous namespace
 
 ///////////////////////////
@@ -1067,8 +1082,8 @@ FilterChooser::getIslands(const KnownBits &EncodingBits) const {
   return Islands;
 }
 
-void FilterChooser::emitBinaryParser(raw_ostream &OS, indent Indent,
-                                     const OperandInfo &OpInfo) const {
+void DecoderTableBuilder::emitBinaryParser(raw_ostream &OS, indent Indent,
+                                           const OperandInfo &OpInfo) const {
   const std::string &Decoder = OpInfo.Decoder;
 
   bool UseInsertBits = OpInfo.numFields() != 1 || OpInfo.InitValue != 0;
@@ -1103,8 +1118,8 @@ void FilterChooser::emitBinaryParser(raw_ostream &OS, indent Indent,
   }
 }
 
-void FilterChooser::emitDecoder(raw_ostream &OS, indent Indent,
-                                unsigned EncodingID) const {
+void DecoderTableBuilder::emitDecoder(raw_ostream &OS, indent Indent,
+                                      unsigned EncodingID) const {
   const InstructionEncoding &Encoding = Encodings[EncodingID];
 
   // If a custom instruction decoder was specified, use that.
@@ -1122,8 +1137,7 @@ void FilterChooser::emitDecoder(raw_ostream &OS, indent Indent,
       emitBinaryParser(OS, Indent, Op);
 }
 
-unsigned FilterChooser::getDecoderIndex(DecoderSet &Decoders,
-                                        unsigned EncodingID) const {
+unsigned DecoderTableBuilder::getDecoderIndex(unsigned EncodingID) const {
   // Build up the predicate string.
   SmallString<256> Decoder;
   // FIXME: emitDecoder() function can take a buffer directly rather than
@@ -1139,6 +1153,7 @@ unsigned FilterChooser::getDecoderIndex(DecoderSet &Decoders,
   // overkill for now, though.
 
   // Make sure the predicate is in the table.
+  DecoderSet &Decoders = TableInfo.Decoders;
   Decoders.insert(CachedHashString(Decoder));
   // Now figure out the index for when we write out the table.
   DecoderSet::const_iterator P = find(Decoders, Decoder.str());
@@ -1146,8 +1161,9 @@ unsigned FilterChooser::getDecoderIndex(DecoderSet &Decoders,
 }
 
 // If ParenIfBinOp is true, print a surrounding () if Val uses && or ||.
-bool FilterChooser::emitPredicateMatchAux(const Init &Val, bool ParenIfBinOp,
-                                          raw_ostream &OS) const {
+bool DecoderTableBuilder::emitPredicateMatchAux(const Init &Val,
+                                                bool ParenIfBinOp,
+                                                raw_ostream &OS) const {
   if (const auto *D = dyn_cast<DefInit>(&Val)) {
     if (!D->getDef()->isSubClassOf("SubtargetFeature"))
       return true;
@@ -1178,8 +1194,8 @@ bool FilterChooser::emitPredicateMatchAux(const Init &Val, bool ParenIfBinOp,
   return true;
 }
 
-bool FilterChooser::emitPredicateMatch(raw_ostream &OS,
-                                       unsigned EncodingID) const {
+bool DecoderTableBuilder::emitPredicateMatch(raw_ostream &OS,
+                                             unsigned EncodingID) const {
   const ListInit *Predicates =
       Encodings[EncodingID].getRecord()->getValueAsListInit("Predicates");
   bool IsFirstEmission = true;
@@ -1201,7 +1217,7 @@ bool FilterChooser::emitPredicateMatch(raw_ostream &OS,
   return !Predicates->empty();
 }
 
-bool FilterChooser::doesOpcodeNeedPredicate(unsigned EncodingID) const {
+bool DecoderTableBuilder::doesOpcodeNeedPredicate(unsigned EncodingID) const {
   const ListInit *Predicates =
       Encodings[EncodingID].getRecord()->getValueAsListInit("Predicates");
   for (unsigned i = 0; i < Predicates->size(); ++i) {
@@ -1215,8 +1231,7 @@ bool FilterChooser::doesOpcodeNeedPredicate(unsigned EncodingID) const {
   return false;
 }
 
-unsigned FilterChooser::getPredicateIndex(DecoderTableInfo &TableInfo,
-                                          StringRef Predicate) const {
+unsigned DecoderTableBuilder::getPredicateIndex(StringRef Predicate) const {
   // Using the full predicate string as the key value here is a bit
   // heavyweight, but is effective. If the string comparisons become a
   // performance concern, we can implement a mangling of the predicate
@@ -1230,8 +1245,7 @@ unsigned FilterChooser::getPredicateIndex(DecoderTableInfo &TableInfo,
   return (unsigned)(P - TableInfo.Predicates.begin());
 }
 
-void FilterChooser::emitPredicateTableEntry(DecoderTableInfo &TableInfo,
-                                            unsigned EncodingID) const {
+void DecoderTableBuilder::emitPredicateTableEntry(unsigned EncodingID) const {
   if (!doesOpcodeNeedPredicate(EncodingID))
     return;
 
@@ -1244,7 +1258,7 @@ void FilterChooser::emitPredicateTableEntry(DecoderTableInfo &TableInfo,
 
   // Figure out the index into the predicate table for the predicate just
   // computed.
-  unsigned PIdx = getPredicateIndex(TableInfo, PS.str());
+  unsigned PIdx = getPredicateIndex(PS.str());
 
   const MCD::DecoderOps DecoderOp = TableInfo.isOutermostScope()
                                         ? MCD::OPC_CheckPredicateOrFail
@@ -1258,8 +1272,7 @@ void FilterChooser::emitPredicateTableEntry(DecoderTableInfo &TableInfo,
   }
 }
 
-void FilterChooser::emitSoftFailTableEntry(DecoderTableInfo &TableInfo,
-                                           unsigned EncodingID) const {
+void DecoderTableBuilder::emitSoftFailTableEntry(unsigned EncodingID) const {
   const InstructionEncoding &Encoding = Encodings[EncodingID];
   const KnownBits &InstBits = Encoding.getInstBits();
   const APInt &SFBits = Encoding.getSoftFailBits();
@@ -1295,19 +1308,20 @@ void FilterChooser::emitSoftFailTableEntry(DecoderTableInfo &TableInfo,
 }
 
 // Emits table entries to decode the singleton.
-void FilterChooser::emitSingletonTableEntry(DecoderTableInfo &TableInfo,
-                                            unsigned EncodingID) const {
+void DecoderTableBuilder::emitSingletonTableEntry(
+    const FilterChooser &FC) const {
+  unsigned EncodingID = *FC.SingletonEncodingID;
   const InstructionEncoding &Encoding = Encodings[EncodingID];
   KnownBits EncodingBits = Encoding.getMandatoryBits();
 
   // Look for islands of undecoded bits of the singleton.
-  std::vector<Island> Islands = getIslands(EncodingBits);
+  std::vector<FilterChooser::Island> Islands = FC.getIslands(EncodingBits);
 
   // Emit the predicate table entry if one is needed.
-  emitPredicateTableEntry(TableInfo, EncodingID);
+  emitPredicateTableEntry(EncodingID);
 
   // Check any additional encoding fields needed.
-  for (const Island &Ilnd : reverse(Islands)) {
+  for (const FilterChooser::Island &Ilnd : reverse(Islands)) {
     const MCD::DecoderOps DecoderOp = TableInfo.isOutermostScope()
                                           ? MCD::OPC_CheckFieldOrFail
                                           : MCD::OPC_CheckField;
@@ -1326,9 +1340,9 @@ void FilterChooser::emitSingletonTableEntry(DecoderTableInfo &TableInfo,
   }
 
   // Check for soft failure of the match.
-  emitSoftFailTableEntry(TableInfo, EncodingID);
+  emitSoftFailTableEntry(EncodingID);
 
-  unsigned DIdx = getDecoderIndex(TableInfo.Decoders, EncodingID);
+  unsigned DIdx = getDecoderIndex(EncodingID);
 
   // Produce OPC_Decode or OPC_TryDecode opcode based on the information
   // whether the instruction decoder is complete or not. If it is complete
@@ -1646,33 +1660,33 @@ void FilterChooser::dump() const {
   }
 }
 
-void FilterChooser::emitTableEntries(DecoderTableInfo &TableInfo) const {
+void DecoderTableBuilder::emitTableEntries(const FilterChooser &FC) const {
   // If there are other encodings that could match if those with all bits
   // known don't, enter a scope so that they have a chance.
-  if (VariableFC)
+  if (FC.VariableFC)
     TableInfo.pushScope();
 
-  if (SingletonEncodingID) {
-    assert(FilterChooserMap.empty());
+  if (FC.SingletonEncodingID) {
+    assert(FC.FilterChooserMap.empty());
     // There is only one encoding in which all bits in the filtered range are
     // fully defined, but we still need to check if the remaining (unfiltered)
     // bits are valid for this encoding. We also need to check predicates etc.
-    emitSingletonTableEntry(TableInfo, *SingletonEncodingID);
+    emitSingletonTableEntry(FC);
   } else {
     // The general case: emit a switch over the field value.
     DecoderTable &Table = TableInfo.Table;
     Table.insertOpcode(MCD::OPC_ExtractField);
-    Table.insertULEB128(StartBit);
-    Table.insertUInt8(NumBits);
+    Table.insertULEB128(FC.StartBit);
+    Table.insertUInt8(FC.NumBits);
 
     // Emit switch cases for all but the last element.
-    for (const auto &[FilterVal, Delegate] : drop_end(FilterChooserMap)) {
+    for (const auto &[FilterVal, Delegate] : drop_end(FC.FilterChooserMap)) {
       Table.insertOpcode(MCD::OPC_FilterValue);
       Table.insertULEB128(FilterVal);
       size_t FixupPos = Table.insertNumToSkip();
 
       // Emit table entries for this case.
-      Delegate->emitTableEntries(TableInfo);
+      emitTableEntries(*Delegate);
 
       // Patch the previous OPC_FilterValue to fall through to the next case.
       Table.patchNumToSkip(FixupPos, Table.size());
@@ -1680,7 +1694,7 @@ void FilterChooser::emitTableEntries(DecoderTableInfo &TableInfo) const {
 
     // Emit a switch case for the last element. It never falls through;
     // if it doesn't match, we leave the current scope.
-    const auto &[FilterVal, Delegate] = *FilterChooserMap.rbegin();
+    const auto &[FilterVal, Delegate] = *FC.FilterChooserMap.rbegin();
     Table.insertOpcode(!TableInfo.isOutermostScope()
                            ? MCD::OPC_FilterValue
                            : MCD::OPC_FilterValueOrFail);
@@ -1689,15 +1703,24 @@ void FilterChooser::emitTableEntries(DecoderTableInfo &TableInfo) const {
       TableInfo.FixupStack.back().push_back(Table.insertNumToSkip());
 
     // Emit table entries for the last case.
-    Delegate->emitTableEntries(TableInfo);
+    emitTableEntries(*Delegate);
   }
 
-  if (VariableFC) {
+  if (FC.VariableFC) {
     TableInfo.popScope();
-    VariableFC->emitTableEntries(TableInfo);
+    emitTableEntries(*FC.VariableFC);
   }
 }
 
+void DecoderTableBuilder::buildTable(
+    const FilterChooser &FC) const {
+  TableInfo.pushScope();
+  emitTableEntries(FC);
+  TableInfo.popScope();
+  assert(TableInfo.FixupStack.empty() && "Fixup stack phasing error!");
+  TableInfo.Table.insertOpcode(MCD::OPC_Fail);
+}
+
 static std::string findOperandDecoderMethod(const Record *Record) {
   std::string Decoder;
 
@@ -2547,6 +2570,7 @@ namespace {
   }
 
   DecoderTableInfo TableInfo;
+  DecoderTableBuilder TableBuilder(Target, Encodings, TableInfo);
   unsigned OpcodeMask = 0;
 
   for (const auto &[Key, EncodingIDs] : EncMap) {
@@ -2559,14 +2583,7 @@ namespace {
     // predicates and decoders themselves, however, are shared across all
     // decoders to give more opportunities for uniqueing.
     TableInfo.Table.clear();
-    TableInfo.pushScope();
-    FC.emitTableEntries(TableInfo);
-    // Any NumToSkip fixups in the top level scope can resolve to the
-    // OPC_Fail at the end of the table.
-    assert(TableInfo.isOutermostScope() && "fixup stack phasing error!");
-    TableInfo.popScope();
-
-    TableInfo.Table.insertOpcode(MCD::OPC_Fail);
+    TableBuilder.buildTable(FC);
 
     // Print the table to the output stream.
     OpcodeMask |= emitTable(OS, TableInfo.Table, DecoderNamespace, HwModeID,



More information about the llvm-commits mailing list