[llvm] [TableGen][DecoderEmitter] Rework table construction/emission (PR #155889)

Sergei Barannikov via llvm-commits llvm-commits at lists.llvm.org
Fri Sep 19 09:28:04 PDT 2025


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

>From e8fe951808a66d54d725122de9755a2eeb2cbae2 Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <barannikov88 at gmail.com>
Date: Tue, 16 Sep 2025 06:55:34 +0300
Subject: [PATCH 01/21] tmp

---
 llvm/include/llvm/MC/MCDecoderOps.h    |   18 +-
 llvm/utils/TableGen/DecoderEmitter.cpp | 1150 ++++++++++++++----------
 2 files changed, 659 insertions(+), 509 deletions(-)

diff --git a/llvm/include/llvm/MC/MCDecoderOps.h b/llvm/include/llvm/MC/MCDecoderOps.h
index 5afc0387f561f..43013640c9d78 100644
--- a/llvm/include/llvm/MC/MCDecoderOps.h
+++ b/llvm/include/llvm/MC/MCDecoderOps.h
@@ -13,18 +13,14 @@
 namespace llvm::MCD {
 
 // Disassembler state machine opcodes.
-// nts_t is either uint16_t or uint24_t based on whether large decoder table is
-// enabled.
 enum DecoderOps {
-  OPC_Scope = 1,         // OPC_Scope(nts_t NumToSkip)
-  OPC_ExtractField,      // OPC_ExtractField(uleb128 Start, uint8_t Len)
-  OPC_FilterValueOrSkip, // OPC_FilterValueOrSkip(uleb128 Val, nts_t NumToSkip)
-  OPC_FilterValue,       // OPC_FilterValue(uleb128 Val)
-  OPC_CheckField,        // OPC_CheckField(uleb128 Start, uint8_t Len,
-                         //                uleb128 Val)
-  OPC_CheckPredicate,    // OPC_CheckPredicate(uleb128 PIdx)
-  OPC_Decode,            // OPC_Decode(uleb128 Opcode, uleb128 DIdx)
-  OPC_SoftFail,          // OPC_SoftFail(uleb128 PMask, uleb128 NMask)
+  OPC_Scope = 1,      // OPC_Scope(uleb128 Size)
+  OPC_SwitchField,    // OPC_SwitchField(uleb128 Start, uint8_t Len,
+                      //                 [uleb128 Val, uleb128 Size]...)
+  OPC_CheckField,     // OPC_CheckField(uleb128 Start, uint8_t Len, uleb128 Val)
+  OPC_CheckPredicate, // OPC_CheckPredicate(uleb128 PIdx)
+  OPC_Decode,         // OPC_Decode(uleb128 Opcode, uleb128 DIdx)
+  OPC_SoftFail,       // OPC_SoftFail(uleb128 PMask, uleb128 NMask)
 };
 
 } // namespace llvm::MCD
diff --git a/llvm/utils/TableGen/DecoderEmitter.cpp b/llvm/utils/TableGen/DecoderEmitter.cpp
index 083d78c4986f0..85ab92eb3644f 100644
--- a/llvm/utils/TableGen/DecoderEmitter.cpp
+++ b/llvm/utils/TableGen/DecoderEmitter.cpp
@@ -30,7 +30,6 @@
 #include "llvm/ADT/Statistic.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringRef.h"
-#include "llvm/MC/MCDecoderOps.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Debug.h"
@@ -56,7 +55,6 @@
 #include <vector>
 
 using namespace llvm;
-using namespace llvm::MCD;
 
 #define DEBUG_TYPE "decoder-emitter"
 
@@ -84,12 +82,6 @@ static cl::opt<SuppressLevel> DecoderEmitterSuppressDuplicates(
             "significantly reducing Table Duplications")),
     cl::init(SUPPRESSION_DISABLE), cl::cat(DisassemblerEmitterCat));
 
-static cl::opt<bool> LargeTable(
-    "large-decoder-table",
-    cl::desc("Use large decoder table format. This uses 24 bits for offset\n"
-             "in the table instead of the default 16 bits."),
-    cl::init(false), cl::cat(DisassemblerEmitterCat));
-
 static cl::opt<bool> UseFnTableInDecodeToMCInst(
     "use-fn-table-in-decode-to-mcinst",
     cl::desc(
@@ -127,8 +119,6 @@ STATISTIC(NumInstructions, "Number of instructions considered");
 STATISTIC(NumEncodingsSupported, "Number of encodings supported");
 STATISTIC(NumEncodingsOmitted, "Number of encodings omitted");
 
-static unsigned getNumToSkipInBytes() { return LargeTable ? 3 : 2; }
-
 /// Similar to KnownBits::print(), but allows you to specify a character to use
 /// to print unknown bits.
 static void printKnownBits(raw_ostream &OS, const KnownBits &Bits,
@@ -163,67 +153,7 @@ class LessEncodingIDByWidth {
 using PredicateSet = SetVector<CachedHashString>;
 using DecoderSet = SetVector<CachedHashString>;
 
-class DecoderTable {
-public:
-  DecoderTable() { Data.reserve(16384); }
-
-  void clear() { Data.clear(); }
-  size_t size() const { return Data.size(); }
-  const uint8_t *data() const { return Data.data(); }
-
-  using const_iterator = std::vector<uint8_t>::const_iterator;
-  const_iterator begin() const { return Data.begin(); }
-  const_iterator end() const { return Data.end(); }
-
-  /// Inserts a state machine opcode into the table.
-  void insertOpcode(DecoderOps Opcode) { Data.push_back(Opcode); }
-
-  /// Inserts a uint8 encoded value into the table.
-  void insertUInt8(unsigned Value) {
-    assert(isUInt<8>(Value));
-    Data.push_back(Value);
-  }
-
-  /// Inserts a ULEB128 encoded value into the table.
-  void insertULEB128(uint64_t Value) {
-    // Encode and emit the value to filter against.
-    uint8_t Buffer[16];
-    unsigned Len = encodeULEB128(Value, Buffer);
-    Data.insert(Data.end(), Buffer, Buffer + Len);
-  }
-
-  // Insert space for `NumToSkip` and return the position
-  // in the table for patching.
-  size_t insertNumToSkip() {
-    size_t Size = Data.size();
-    Data.insert(Data.end(), getNumToSkipInBytes(), 0);
-    return Size;
-  }
-
-  void patchNumToSkip(size_t FixupIdx, uint32_t DestIdx) {
-    // Calculate the distance from the byte following the fixup entry byte
-    // to the destination. The Target is calculated from after the
-    // `getNumToSkipInBytes()`-byte NumToSkip entry itself, so subtract
-    // `getNumToSkipInBytes()` from the displacement here to account for that.
-    assert(DestIdx >= FixupIdx + getNumToSkipInBytes() &&
-           "Expecting a forward jump in the decoding table");
-    uint32_t Delta = DestIdx - FixupIdx - getNumToSkipInBytes();
-    if (!isUIntN(8 * getNumToSkipInBytes(), Delta))
-      PrintFatalError(
-          "disassembler decoding table too large, try --large-decoder-table");
-
-    Data[FixupIdx] = static_cast<uint8_t>(Delta);
-    Data[FixupIdx + 1] = static_cast<uint8_t>(Delta >> 8);
-    if (getNumToSkipInBytes() == 3)
-      Data[FixupIdx + 2] = static_cast<uint8_t>(Delta >> 16);
-  }
-
-private:
-  std::vector<uint8_t> Data;
-};
-
 struct DecoderTableInfo {
-  DecoderTable Table;
   PredicateSet Predicates;
   DecoderSet Decoders;
   bool HasCheckPredicate;
@@ -252,6 +182,8 @@ struct DecoderTableInfo {
 
 using NamespacesHwModesMap = std::map<StringRef, std::set<unsigned>>;
 
+class DecoderTreeNode;
+
 class DecoderEmitter {
   const RecordKeeper &RK;
   CodeGenTarget Target;
@@ -271,7 +203,7 @@ class DecoderEmitter {
   // Emit the decoder state machine table.
   void emitTable(formatted_raw_ostream &OS, DecoderTableInfo &TableInfo,
                  StringRef Namespace, unsigned HwModeID, unsigned BitWidth,
-                 ArrayRef<unsigned> EncodingIDs) const;
+                 const DecoderTreeNode *Tree) const;
   void emitInstrLenTable(formatted_raw_ostream &OS,
                          ArrayRef<unsigned> InstrLen) const;
   void emitPredicateFunction(formatted_raw_ostream &OS,
@@ -388,7 +320,7 @@ enum bitAttr_t {
 
 class FilterChooser {
   // TODO: Unfriend by providing the necessary accessors.
-  friend class DecoderTableBuilder;
+  friend class DecoderTreeBuilder;
 
   // Vector of encodings to choose our filter.
   ArrayRef<InstructionEncoding> Encodings;
@@ -500,35 +432,6 @@ class FilterChooser {
   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, unsigned BitWidth) const {
-    // When specializing decoders per bit width, each decoder table will begin
-    // with the bitwidth for that table.
-    if (SpecializeDecodersPerBitwidth)
-      TableInfo.Table.insertULEB128(BitWidth);
-    emitTableEntries(FC);
-  }
-
-private:
-  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
 
 ///////////////////////////
@@ -605,219 +508,6 @@ unsigned Filter::usefulness() const {
 //                              //
 //////////////////////////////////
 
-static StringRef getDecoderOpName(DecoderOps Op) {
-#define CASE(OP)                                                               \
-  case OP:                                                                     \
-    return #OP
-  switch (Op) {
-    CASE(OPC_Scope);
-    CASE(OPC_ExtractField);
-    CASE(OPC_FilterValueOrSkip);
-    CASE(OPC_FilterValue);
-    CASE(OPC_CheckField);
-    CASE(OPC_CheckPredicate);
-    CASE(OPC_Decode);
-    CASE(OPC_SoftFail);
-  }
-#undef CASE
-  llvm_unreachable("Unknown decoder op");
-}
-
-// Emit the decoder state machine table.
-void DecoderEmitter::emitTable(formatted_raw_ostream &OS,
-                               DecoderTableInfo &TableInfo, StringRef Namespace,
-                               unsigned HwModeID, unsigned BitWidth,
-                               ArrayRef<unsigned> EncodingIDs) const {
-  // We'll need to be able to map from a decoded opcode into the corresponding
-  // EncodingID for this specific combination of BitWidth and Namespace. This
-  // is used below to index into Encodings.
-  DenseMap<unsigned, unsigned> OpcodeToEncodingID;
-  OpcodeToEncodingID.reserve(EncodingIDs.size());
-  for (unsigned EncodingID : EncodingIDs) {
-    const Record *InstDef = Encodings[EncodingID].getInstruction()->TheDef;
-    OpcodeToEncodingID[Target.getInstrIntValue(InstDef)] = EncodingID;
-  }
-
-  OS << "static const uint8_t DecoderTable" << Namespace;
-  if (HwModeID != DefaultMode)
-    OS << '_' << Target.getHwModes().getModeName(HwModeID);
-  OS << BitWidth << "[" << TableInfo.Table.size() << "] = {\n";
-
-  // Emit ULEB128 encoded value to OS, returning the number of bytes emitted.
-  auto EmitULEB128 = [](DecoderTable::const_iterator &I,
-                        formatted_raw_ostream &OS) {
-    while (*I >= 128)
-      OS << (unsigned)*I++ << ", ";
-    OS << (unsigned)*I++ << ", ";
-  };
-
-  // Emit `getNumToSkipInBytes()`-byte numtoskip value to OS, returning the
-  // NumToSkip value.
-  auto EmitNumToSkip = [](DecoderTable::const_iterator &I,
-                          formatted_raw_ostream &OS) {
-    uint8_t Byte = *I++;
-    uint32_t NumToSkip = Byte;
-    OS << (unsigned)Byte << ", ";
-    Byte = *I++;
-    OS << (unsigned)Byte << ", ";
-    NumToSkip |= Byte << 8;
-    if (getNumToSkipInBytes() == 3) {
-      Byte = *I++;
-      OS << (unsigned)(Byte) << ", ";
-      NumToSkip |= Byte << 16;
-    }
-    return NumToSkip;
-  };
-
-  // FIXME: We may be able to use the NumToSkip values to recover
-  // appropriate indentation levels.
-  DecoderTable &Table = TableInfo.Table;
-  DecoderTable::const_iterator I = Table.begin();
-  DecoderTable::const_iterator E = Table.end();
-  const uint8_t *const EndPtr = Table.data() + Table.size();
-
-  auto EmitPos = [&OS](uint32_t Pos) {
-    constexpr uint32_t StartColumn = 12;
-    OS << "/* " << Pos << " */";
-    OS.PadToColumn(StartColumn);
-  };
-
-  auto StartComment = [&OS]() {
-    constexpr uint32_t CommentColumn = 52;
-    OS.PadToColumn(CommentColumn);
-    OS << "// ";
-  };
-
-  auto EmitNumToSkipComment = [&](uint32_t NumToSkip) {
-    uint32_t Index = (I - Table.begin()) + NumToSkip;
-    OS << "skip to " << Index;
-  };
-
-  // The first entry when specializing decoders per bitwidth is the bitwidth.
-  // This will be used for additional checks in `decodeInstruction`.
-  if (SpecializeDecodersPerBitwidth) {
-    EmitPos(0);
-    EmitULEB128(I, OS);
-    StartComment();
-    OS << "Bitwidth " << BitWidth << '\n';
-  }
-
-  auto DecodeAndEmitULEB128 = [EndPtr,
-                               &EmitULEB128](DecoderTable::const_iterator &I,
-                                             formatted_raw_ostream &OS) {
-    const char *ErrMsg = nullptr;
-    uint64_t Value = decodeULEB128(&*I, nullptr, EndPtr, &ErrMsg);
-    assert(ErrMsg == nullptr && "ULEB128 value too large!");
-
-    EmitULEB128(I, OS);
-    return Value;
-  };
-
-  while (I != E) {
-    assert(I < E && "incomplete decode table entry!");
-
-    uint32_t Pos = I - Table.begin();
-    EmitPos(Pos);
-    const uint8_t DecoderOp = *I++;
-    OS << getDecoderOpName(static_cast<DecoderOps>(DecoderOp)) << ", ";
-    switch (DecoderOp) {
-    default:
-      PrintFatalError("Invalid decode table opcode: " + Twine((int)DecoderOp) +
-                      " at index " + Twine(Pos));
-    case OPC_Scope: {
-      uint32_t NumToSkip = EmitNumToSkip(I, OS);
-      StartComment();
-      uint32_t Index = (I - Table.begin()) + NumToSkip;
-      OS << "end scope at " << Index;
-      break;
-    }
-    case OPC_ExtractField: {
-      // ULEB128 encoded start value.
-      unsigned Start = DecodeAndEmitULEB128(I, OS);
-      unsigned Len = *I++;
-      OS << Len << ',';
-      StartComment();
-      OS << "Field = Inst{";
-      if (Len > 1)
-        OS << (Start + Len - 1) << '-';
-      OS << Start << '}';
-      break;
-    }
-    case OPC_FilterValueOrSkip: {
-      // The filter value is ULEB128 encoded.
-      uint64_t FilterVal = DecodeAndEmitULEB128(I, OS);
-      uint32_t NumToSkip = EmitNumToSkip(I, OS);
-      StartComment();
-      OS << "if Field != " << format_hex(FilterVal, 0) << ' ';
-      EmitNumToSkipComment(NumToSkip);
-      break;
-    }
-    case OPC_FilterValue: {
-      // The filter value is ULEB128 encoded.
-      uint64_t FilterVal = DecodeAndEmitULEB128(I, OS);
-
-      StartComment();
-      OS << "if Field != " << format_hex(FilterVal, 0) << " pop scope";
-      break;
-    }
-    case OPC_CheckField: {
-      // ULEB128 encoded start value.
-      unsigned Start = DecodeAndEmitULEB128(I, OS);
-
-      // 8-bit length.
-      unsigned Len = *I++;
-      OS << Len << ", ";
-
-      // ULEB128 encoded field value.
-      uint64_t FieldVal = DecodeAndEmitULEB128(I, OS);
-
-      StartComment();
-      OS << "if Inst{";
-      if (Len > 1)
-        OS << (Start + Len - 1) << '-';
-      OS << Start << "} != " << format_hex(FieldVal, 0) << " pop scope";
-      break;
-    }
-    case OPC_CheckPredicate: {
-      unsigned PIdx = DecodeAndEmitULEB128(I, OS);
-      StartComment();
-      OS << "if !checkPredicate(" << PIdx << ") pop scope";
-      break;
-    }
-    case OPC_Decode: {
-      // Decode the Opcode value.
-      unsigned Opc = DecodeAndEmitULEB128(I, OS);
-
-      // Decoder index.
-      unsigned DecodeIdx = DecodeAndEmitULEB128(I, OS);
-
-      auto EncI = OpcodeToEncodingID.find(Opc);
-      assert(EncI != OpcodeToEncodingID.end() && "no encoding entry");
-      auto EncodingID = EncI->second;
-
-      StartComment();
-      OS << "Opcode: " << Encodings[EncodingID].getName()
-         << ", DecodeIdx: " << DecodeIdx;
-      break;
-    }
-    case OPC_SoftFail: {
-      // Decode the positive mask.
-      uint64_t PositiveMask = DecodeAndEmitULEB128(I, OS);
-
-      // Decode the negative mask.
-      uint64_t NegativeMask = DecodeAndEmitULEB128(I, OS);
-
-      StartComment();
-      OS << "positive mask: " << format_hex(PositiveMask, 0)
-         << "negative mask: " << format_hex(NegativeMask, 0);
-      break;
-    }
-    }
-    OS << '\n';
-  }
-  OS << "};\n\n";
-}
-
 void DecoderEmitter::emitInstrLenTable(formatted_raw_ostream &OS,
                                        ArrayRef<unsigned> InstrLen) const {
   OS << "static const uint8_t InstrLenTable[] = {\n";
@@ -1078,81 +768,6 @@ static std::string getPredicateString(const InstructionEncoding &Encoding,
   return Predicate;
 }
 
-void DecoderTableBuilder::emitPredicateTableEntry(unsigned EncodingID) const {
-  const InstructionEncoding &Encoding = Encodings[EncodingID];
-  std::string Predicate = getPredicateString(Encoding, Target.getName());
-  if (Predicate.empty())
-    return;
-
-  // 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
-  // data easily enough with a map back to the actual string. That's
-  // overkill for now, though.
-  TableInfo.insertPredicate(Predicate);
-  unsigned PredicateIndex = TableInfo.getPredicateIndex(Predicate);
-
-  TableInfo.Table.insertOpcode(OPC_CheckPredicate);
-  TableInfo.Table.insertULEB128(PredicateIndex);
-  TableInfo.HasCheckPredicate = true;
-}
-
-void DecoderTableBuilder::emitSoftFailTableEntry(unsigned EncodingID) const {
-  const InstructionEncoding &Encoding = Encodings[EncodingID];
-  const KnownBits &InstBits = Encoding.getInstBits();
-  const APInt &SoftFailMask = Encoding.getSoftFailMask();
-
-  if (SoftFailMask.isZero())
-    return;
-
-  APInt PositiveMask = InstBits.Zero & SoftFailMask;
-  APInt NegativeMask = InstBits.One & SoftFailMask;
-
-  TableInfo.Table.insertOpcode(OPC_SoftFail);
-  TableInfo.Table.insertULEB128(PositiveMask.getZExtValue());
-  TableInfo.Table.insertULEB128(NegativeMask.getZExtValue());
-  TableInfo.HasSoftFail = true;
-}
-
-// Emits table entries to decode the singleton.
-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<EncodingIsland> Islands = getIslands(EncodingBits, FC.FilterBits);
-
-  // Emit the predicate table entry if one is needed.
-  emitPredicateTableEntry(EncodingID);
-
-  // Check any additional encoding fields needed.
-  for (const EncodingIsland &Island : reverse(Islands)) {
-    TableInfo.Table.insertOpcode(OPC_CheckField);
-    TableInfo.Table.insertULEB128(Island.StartBit);
-    TableInfo.Table.insertUInt8(Island.NumBits);
-    TableInfo.Table.insertULEB128(Island.FieldVal);
-  }
-
-  // Check for soft failure of the match.
-  emitSoftFailTableEntry(EncodingID);
-
-  // Using the full decoder 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
-  // data easily enough with a map back to the actual string. That's
-  // overkill for now, though.
-  std::string Decoder = getDecoderString(Encoding);
-  TableInfo.insertDecoder(Decoder);
-  unsigned DecoderIndex = TableInfo.getDecoderIndex(Decoder);
-
-  TableInfo.Table.insertOpcode(MCD::OPC_Decode);
-  const Record *InstDef = Encodings[EncodingID].getInstruction()->TheDef;
-  TableInfo.Table.insertULEB128(Target.getInstrIntValue(InstDef));
-  TableInfo.Table.insertULEB128(DecoderIndex);
-}
-
 std::unique_ptr<Filter>
 FilterChooser::findBestFilter(ArrayRef<bitAttr_t> BitAttrs, bool AllowMixed,
                               bool Greedy) const {
@@ -1429,83 +1044,11 @@ void FilterChooser::dump() const {
   }
 }
 
-void DecoderTableBuilder::emitTableEntries(const FilterChooser &FC) const {
-  DecoderTable &Table = TableInfo.Table;
-
-  // 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.
-  size_t FixupLoc = 0;
-  if (FC.VariableFC) {
-    Table.insertOpcode(OPC_Scope);
-    FixupLoc = Table.insertNumToSkip();
-  }
-
-  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(FC);
-  } else if (FC.FilterChooserMap.size() == 1) {
-    // If there is only one possible field value, emit a combined OPC_CheckField
-    // instead of OPC_ExtractField + OPC_FilterValue.
-    const auto &[FilterVal, Delegate] = *FC.FilterChooserMap.begin();
-    Table.insertOpcode(OPC_CheckField);
-    Table.insertULEB128(FC.StartBit);
-    Table.insertUInt8(FC.NumBits);
-    Table.insertULEB128(FilterVal);
-
-    // Emit table entries for the only case.
-    emitTableEntries(*Delegate);
-  } else {
-    // The general case: emit a switch over the field value.
-    Table.insertOpcode(OPC_ExtractField);
-    Table.insertULEB128(FC.StartBit);
-    Table.insertUInt8(FC.NumBits);
-
-    // Emit switch cases for all but the last element.
-    for (const auto &[FilterVal, Delegate] : drop_end(FC.FilterChooserMap)) {
-      Table.insertOpcode(OPC_FilterValueOrSkip);
-      Table.insertULEB128(FilterVal);
-      size_t FixupPos = Table.insertNumToSkip();
-
-      // Emit table entries for this case.
-      emitTableEntries(*Delegate);
-
-      // Patch the previous FilterValueOrSkip to fall through to the next case.
-      Table.patchNumToSkip(FixupPos, Table.size());
-    }
-
-    // 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] = *FC.FilterChooserMap.rbegin();
-    Table.insertOpcode(OPC_FilterValue);
-    Table.insertULEB128(FilterVal);
-
-    // Emit table entries for the last case.
-    emitTableEntries(*Delegate);
-  }
-
-  if (FC.VariableFC) {
-    Table.patchNumToSkip(FixupLoc, Table.size());
-    emitTableEntries(*FC.VariableFC);
-  }
-}
-
 // emitDecodeInstruction - Emit the templated helper function
 // decodeInstruction().
 static void emitDecodeInstruction(formatted_raw_ostream &OS, bool IsVarLenInst,
                                   const DecoderTableInfo &TableInfo) {
   OS << R"(
-static unsigned decodeNumToSkip(const uint8_t *&Ptr) {
-  unsigned NumToSkip = *Ptr++;
-  NumToSkip |= (*Ptr++) << 8;
-)";
-  if (getNumToSkipInBytes() == 3)
-    OS << "  NumToSkip |= (*Ptr++) << 16;\n";
-  OS << R"(  return NumToSkip;
-}
-
 template <typename InsnType>
 static DecodeStatus decodeInstruction(const uint8_t DecodeTable[], MCInst &MI,
                                       InsnType insn, uint64_t Address,
@@ -1532,7 +1075,6 @@ static DecodeStatus decodeInstruction(const uint8_t DecodeTable[], MCInst &MI,
 
   OS << R"(
   SmallVector<const uint8_t *, 8> ScopeStack;
-  uint64_t CurFieldValue = 0;
   DecodeStatus S = MCDisassembler::Success;
   while (true) {
     ptrdiff_t Loc = Ptr - DecodeTable;
@@ -1543,50 +1085,35 @@ static DecodeStatus decodeInstruction(const uint8_t DecodeTable[], MCInst &MI,
              << (int)DecoderOp << '\n';
       return MCDisassembler::Fail;
     case OPC_Scope: {
-      unsigned NumToSkip = decodeNumToSkip(Ptr);
+      unsigned NumToSkip = decodeULEB128AndIncUnsafe(Ptr);
       const uint8_t *SkipTo = Ptr + NumToSkip;
       ScopeStack.push_back(SkipTo);
       LLVM_DEBUG(dbgs() << Loc << ": OPC_Scope(" << SkipTo - DecodeTable
                         << ")\n");
       continue;
     }
-    case OPC_ExtractField: {
+    case OPC_SwitchField: {
       // Decode the start value.
       unsigned Start = decodeULEB128AndIncUnsafe(Ptr);
       unsigned Len = *Ptr++;)";
   if (IsVarLenInst)
     OS << "\n      makeUp(insn, Start + Len);";
   OS << R"(
-      CurFieldValue = fieldFromInstruction(insn, Start, Len);
-      LLVM_DEBUG(dbgs() << Loc << ": OPC_ExtractField(" << Start << ", "
-                   << Len << "): " << CurFieldValue << "\n");
-      continue;
-    }
-    case OPC_FilterValueOrSkip: {
-      // Decode the field value.
-      uint64_t Val = decodeULEB128AndIncUnsafe(Ptr);
-      bool Failed = Val != CurFieldValue;
-      unsigned NumToSkip = decodeNumToSkip(Ptr);
-      const uint8_t *SkipTo = Ptr + NumToSkip;
-
-      LLVM_DEBUG(dbgs() << Loc << ": OPC_FilterValueOrSkip(" << Val << ", "
-                        << SkipTo - DecodeTable << ") "
-                        << (Failed ? "FAIL, " : "PASS\n"));
-      if (!Failed)
-        continue;
-      Ptr = SkipTo;
-      LLVM_DEBUG(dbgs() << "continuing at " << Ptr - DecodeTable << '\n');
-      continue;
-    }
-    case OPC_FilterValue: {
-      // Decode the field value.
-      uint64_t Val = decodeULEB128AndIncUnsafe(Ptr);
-      bool Failed = Val != CurFieldValue;
-
-      LLVM_DEBUG(dbgs() << Loc << ": OPC_FilterValue(" << Val << ") "
-                        << (Failed ? "FAIL, " : "PASS\n"));
-      if (!Failed)
+      uint64_t FieldValue = fieldFromInstruction(insn, Start, Len);
+      uint64_t CaseValue;
+      unsigned CaseSize;
+      while (true) {
+        CaseValue = decodeULEB128AndIncUnsafe(Ptr);
+        CaseSize = decodeULEB128AndIncUnsafe(Ptr);
+        if (FieldValue == CaseValue || !CaseSize)
+          break;
+        Ptr += CaseSize;
+      }
+      if (FieldValue == CaseValue) {
+        LLVM_DEBUG(dbgs() << Loc << ": OPC_SwitchField(" << Start << ", " << Len
+                          << "): " << FieldValue << '\n');
         continue;
+      }
       break;
     }
     case OPC_CheckField: {
@@ -1684,6 +1211,619 @@ static DecodeStatus decodeInstruction(const uint8_t DecodeTable[], MCInst &MI,
 )";
 }
 
+namespace {
+
+class DecoderTreeNode {
+public:
+  virtual ~DecoderTreeNode() = default;
+
+  enum KindTy {
+    CheckAny,
+    CheckAll,
+    CheckField,
+    SwitchField,
+    CheckPredicate,
+    SoftFail,
+    Decode,
+  };
+
+  KindTy getKind() const { return Kind; }
+
+protected:
+  explicit DecoderTreeNode(KindTy Kind) : Kind(Kind) {}
+
+private:
+  KindTy Kind;
+};
+
+class CheckAnyNode : public DecoderTreeNode {
+  SmallVector<std::unique_ptr<DecoderTreeNode>, 0> Children;
+
+  static const DecoderTreeNode *
+  mapElement(decltype(Children)::const_reference Element) {
+    return Element.get();
+  }
+
+public:
+  CheckAnyNode() : DecoderTreeNode(CheckAny) {}
+
+  void addChild(std::unique_ptr<DecoderTreeNode> N) {
+    Children.push_back(std::move(N));
+  }
+
+  using child_iterator = mapped_iterator<decltype(Children)::const_iterator,
+                                         decltype(&mapElement)>;
+
+  child_iterator child_begin() const {
+    return child_iterator(Children.begin(), mapElement);
+  }
+
+  child_iterator child_end() const {
+    return child_iterator(Children.end(), mapElement);
+  }
+
+  iterator_range<child_iterator> children() const {
+    return make_range(child_begin(), child_end());
+  }
+};
+
+class CheckAllNode : public DecoderTreeNode {
+  SmallVector<std::unique_ptr<DecoderTreeNode>, 0> Children;
+
+  static const DecoderTreeNode *
+  mapElement(decltype(Children)::const_reference Element) {
+    return Element.get();
+  }
+
+public:
+  CheckAllNode() : DecoderTreeNode(CheckAll) {}
+
+  void addChild(std::unique_ptr<DecoderTreeNode> Child) {
+    Children.push_back(std::move(Child));
+  }
+
+  using child_iterator = mapped_iterator<decltype(Children)::const_iterator,
+                                         decltype(&mapElement)>;
+
+  child_iterator child_begin() const {
+    return child_iterator(Children.begin(), mapElement);
+  }
+
+  child_iterator child_end() const {
+    return child_iterator(Children.end(), mapElement);
+  }
+
+  iterator_range<child_iterator> children() const {
+    return make_range(child_begin(), child_end());
+  }
+};
+
+class CheckFieldNode : public DecoderTreeNode {
+  unsigned StartBit;
+  unsigned NumBits;
+  uint64_t Value;
+
+public:
+  CheckFieldNode(unsigned StartBit, unsigned NumBits, uint64_t Value)
+      : DecoderTreeNode(CheckField), StartBit(StartBit), NumBits(NumBits),
+        Value(Value) {}
+
+  unsigned getStartBit() const { return StartBit; }
+
+  unsigned getNumBits() const { return NumBits; }
+
+  uint64_t getValue() const { return Value; }
+};
+
+class SwitchFieldNode : public DecoderTreeNode {
+  unsigned StartBit;
+  unsigned NumBits;
+  std::map<uint64_t, std::unique_ptr<DecoderTreeNode>> Cases;
+
+  static std::pair<uint64_t, const DecoderTreeNode *>
+  mapElement(decltype(Cases)::const_reference Element) {
+    return std::pair(Element.first, Element.second.get());
+  }
+
+public:
+  SwitchFieldNode(unsigned StartBit, unsigned NumBits)
+      : DecoderTreeNode(SwitchField), StartBit(StartBit), NumBits(NumBits) {}
+
+  void addCase(uint64_t Value, std::unique_ptr<DecoderTreeNode> N) {
+    Cases.try_emplace(Value, std::move(N));
+  }
+
+  unsigned getStartBit() const { return StartBit; }
+
+  unsigned getNumBits() const { return NumBits; }
+
+  using case_iterator =
+      mapped_iterator<decltype(Cases)::const_iterator, decltype(&mapElement)>;
+
+  case_iterator case_begin() const {
+    return case_iterator(Cases.begin(), mapElement);
+  }
+
+  case_iterator case_end() const {
+    return case_iterator(Cases.end(), mapElement);
+  }
+
+  iterator_range<case_iterator> cases() const {
+    return make_range(case_begin(), case_end());
+  }
+};
+
+class CheckPredicateNode : public DecoderTreeNode {
+  std::string PredicateString;
+
+public:
+  explicit CheckPredicateNode(std::string PredicateString)
+      : DecoderTreeNode(CheckPredicate),
+        PredicateString(std::move(PredicateString)) {}
+
+  StringRef getPredicateString() const { return PredicateString; }
+};
+
+class SoftFailNode : public DecoderTreeNode {
+  uint64_t PositiveMask, NegativeMask;
+
+public:
+  SoftFailNode(uint64_t PositiveMask, uint64_t NegativeMask)
+      : DecoderTreeNode(SoftFail), PositiveMask(PositiveMask),
+        NegativeMask(NegativeMask) {}
+
+  uint64_t getPositiveMask() const { return PositiveMask; }
+  uint64_t getNegativeMask() const { return NegativeMask; }
+};
+
+class DecodeNode : public DecoderTreeNode {
+  const InstructionEncoding &Encoding;
+  std::string DecoderString;
+
+public:
+  DecodeNode(const InstructionEncoding &Encoding, std::string DecoderString)
+      : DecoderTreeNode(Decode), Encoding(Encoding),
+        DecoderString(std::move(DecoderString)) {}
+
+  const InstructionEncoding &getEncoding() const { return Encoding; }
+
+  StringRef getDecoderString() const { return DecoderString; }
+};
+
+class DecoderTreeBuilder {
+  const CodeGenTarget &Target;
+  ArrayRef<InstructionEncoding> Encodings;
+
+public:
+  DecoderTreeBuilder(const CodeGenTarget &Target,
+                     ArrayRef<InstructionEncoding> Encodings)
+      : Target(Target), Encodings(Encodings) {}
+
+  std::unique_ptr<DecoderTreeNode> buildTree(const FilterChooser &FC) {
+    return buildCheckAnyNode(FC);
+  }
+
+private:
+  std::unique_ptr<DecoderTreeNode>
+  buildTerminalNode(unsigned EncodingID, const KnownBits &FilterBits);
+
+  std::unique_ptr<DecoderTreeNode> buildCheckAllOrSwitchNode(
+      unsigned StartBit, unsigned NumBits,
+      const std::map<uint64_t, std::unique_ptr<const FilterChooser>> &FCMap);
+
+  std::unique_ptr<DecoderTreeNode> buildCheckAnyNode(const FilterChooser &FC);
+};
+
+class DecoderTableEmitter {
+  DecoderTableInfo &TableInfo;
+  formatted_raw_ostream OS;
+  unsigned IndexWidth;
+  unsigned CurrentIndex;
+  unsigned CommentIndex;
+
+public:
+  DecoderTableEmitter(DecoderTableInfo &TableInfo, raw_ostream &OS)
+      : TableInfo(TableInfo), OS(OS) {}
+
+  void emitTable(StringRef TableName, unsigned BitWidth,
+                 const DecoderTreeNode *Root);
+
+private:
+  void analyzeNode(const DecoderTreeNode *Node) const;
+
+  unsigned computeNodeSize(const DecoderTreeNode *Node) const;
+  unsigned computeTableSize(const DecoderTreeNode *Root,
+                            unsigned BitWidth) const;
+
+  void emitStartLine();
+  void emitOpcode(StringRef Name);
+  void emitByte(uint8_t Val);
+  void emitUInt8(unsigned Val);
+  void emitULEB128(uint64_t Val);
+  formatted_raw_ostream &emitComment(indent Indent);
+
+  void emitCheckAnyNode(const CheckAnyNode *N, indent Indent);
+  void emitCheckAllNode(const CheckAllNode *N, indent Indent);
+  void emitSwitchFieldNode(const SwitchFieldNode *N, indent Indent);
+  void emitCheckFieldNode(const CheckFieldNode *N, indent Indent);
+  void emitCheckPredicateNode(const CheckPredicateNode *N, indent Indent);
+  void emitSoftFailNode(const SoftFailNode *N, indent Indent);
+  void emitDecodeNode(const DecodeNode *N, indent Indent);
+  void emitNode(const DecoderTreeNode *N, indent Indent);
+};
+
+} // namespace
+
+std::unique_ptr<DecoderTreeNode>
+DecoderTreeBuilder::buildTerminalNode(unsigned EncodingID,
+                                      const KnownBits &FilterBits) {
+  const InstructionEncoding &Encoding = Encodings[EncodingID];
+  auto N = std::make_unique<CheckAllNode>();
+
+  std::string Predicate = getPredicateString(Encoding, Target.getName());
+  if (!Predicate.empty())
+    N->addChild(std::make_unique<CheckPredicateNode>(std::move(Predicate)));
+
+  std::vector<EncodingIsland> Islands =
+      getIslands(Encoding.getMandatoryBits(), FilterBits);
+  for (const EncodingIsland &Island : reverse(Islands)) {
+    N->addChild(std::make_unique<CheckFieldNode>(
+        Island.StartBit, Island.NumBits, Island.FieldVal));
+  }
+
+  const KnownBits &InstBits = Encoding.getInstBits();
+  const APInt &SoftFailMask = Encoding.getSoftFailMask();
+  if (!SoftFailMask.isZero()) {
+    APInt PositiveMask = InstBits.Zero & SoftFailMask;
+    APInt NegativeMask = InstBits.One & SoftFailMask;
+    N->addChild(std::make_unique<SoftFailNode>(PositiveMask.getZExtValue(),
+                                               NegativeMask.getZExtValue()));
+  }
+
+  std::string DecoderIndex = getDecoderString(Encoding);
+  N->addChild(std::make_unique<DecodeNode>(Encoding, DecoderIndex));
+
+  return N;
+}
+
+std::unique_ptr<DecoderTreeNode> DecoderTreeBuilder::buildCheckAllOrSwitchNode(
+    unsigned StartBit, unsigned NumBits,
+    const std::map<uint64_t, std::unique_ptr<const FilterChooser>> &FCMap) {
+  if (FCMap.size() == 1) {
+    const auto &[FieldVal, ChildFC] = *FCMap.begin();
+    auto N = std::make_unique<CheckAllNode>();
+    N->addChild(std::make_unique<CheckFieldNode>(StartBit, NumBits, FieldVal));
+    N->addChild(buildCheckAnyNode(*ChildFC));
+    return N;
+  }
+  auto N = std::make_unique<SwitchFieldNode>(StartBit, NumBits);
+  for (const auto &[FieldVal, ChildFC] : FCMap)
+    N->addCase(FieldVal, buildCheckAnyNode(*ChildFC));
+  return N;
+}
+
+std::unique_ptr<DecoderTreeNode>
+DecoderTreeBuilder::buildCheckAnyNode(const FilterChooser &FC) {
+  auto N = std::make_unique<CheckAnyNode>();
+  if (FC.SingletonEncodingID) {
+    N->addChild(buildTerminalNode(*FC.SingletonEncodingID, FC.FilterBits));
+  } else {
+    N->addChild(buildCheckAllOrSwitchNode(FC.StartBit, FC.NumBits,
+                                          FC.FilterChooserMap));
+  }
+  if (FC.VariableFC) {
+    N->addChild(buildCheckAnyNode(*FC.VariableFC));
+  }
+
+  return N;
+}
+
+void DecoderTableEmitter::analyzeNode(const DecoderTreeNode *Node) const {
+  switch (Node->getKind()) {
+  case DecoderTreeNode::CheckAny: {
+    const auto *N = static_cast<const CheckAnyNode *>(Node);
+    for (const DecoderTreeNode *Child : N->children())
+      analyzeNode(Child);
+    break;
+  }
+  case DecoderTreeNode::CheckAll: {
+    const auto *N = static_cast<const CheckAllNode *>(Node);
+    for (const DecoderTreeNode *Child : N->children())
+      analyzeNode(Child);
+    break;
+  }
+  case DecoderTreeNode::CheckField:
+    break;
+  case DecoderTreeNode::SwitchField: {
+    const auto *N = static_cast<const SwitchFieldNode *>(Node);
+    for (const DecoderTreeNode *Child : make_second_range(N->cases()))
+      analyzeNode(Child);
+    break;
+  }
+  case DecoderTreeNode::CheckPredicate: {
+    const auto *N = static_cast<const CheckPredicateNode *>(Node);
+    TableInfo.insertPredicate(N->getPredicateString());
+    break;
+  }
+  case DecoderTreeNode::SoftFail:
+    break;
+  case DecoderTreeNode::Decode: {
+    const auto *N = static_cast<const DecodeNode *>(Node);
+    TableInfo.insertDecoder(N->getDecoderString());
+    break;
+  }
+  }
+}
+
+unsigned
+DecoderTableEmitter::computeNodeSize(const DecoderTreeNode *Node) const {
+  switch (Node->getKind()) {
+  case DecoderTreeNode::CheckAny: {
+    const auto *N = static_cast<const CheckAnyNode *>(Node);
+    unsigned Size = 0;
+    for (const DecoderTreeNode *Child : drop_end(N->children())) {
+      unsigned ChildSize = computeNodeSize(Child);
+      Size += 1 + getULEB128Size(ChildSize) + ChildSize;
+    }
+    return Size + computeNodeSize(*std::prev(N->child_end()));
+  }
+  case DecoderTreeNode::CheckAll: {
+    const auto *N = static_cast<const CheckAllNode *>(Node);
+    unsigned Size = 0;
+    for (const DecoderTreeNode *Child : N->children())
+      Size += computeNodeSize(Child);
+    return Size;
+  }
+  case DecoderTreeNode::CheckField: {
+    const auto *N = static_cast<const CheckFieldNode *>(Node);
+    return 1 + getULEB128Size(N->getStartBit()) + 1 +
+           getULEB128Size(N->getValue());
+  }
+  case DecoderTreeNode::SwitchField: {
+    const auto *N = static_cast<const SwitchFieldNode *>(Node);
+    unsigned Size = 1 + getULEB128Size(N->getStartBit()) + 1;
+
+    for (auto [Val, Child] : drop_end(N->cases())) {
+      unsigned ChildSize = computeNodeSize(Child);
+      Size += getULEB128Size(Val) + getULEB128Size(ChildSize) + ChildSize;
+    }
+
+    auto [Val, Child] = *std::prev(N->case_end());
+    unsigned ChildSize = computeNodeSize(Child);
+    Size += getULEB128Size(Val) + getULEB128Size(0) + ChildSize;
+    return Size;
+  }
+  case DecoderTreeNode::CheckPredicate: {
+    const auto *N = static_cast<const CheckPredicateNode *>(Node);
+    unsigned PredicateIndex =
+        TableInfo.getPredicateIndex(N->getPredicateString());
+    return 1 + getULEB128Size(PredicateIndex);
+  }
+  case DecoderTreeNode::SoftFail: {
+    const auto *N = static_cast<const SoftFailNode *>(Node);
+    return 1 + getULEB128Size(N->getPositiveMask()) +
+           getULEB128Size(N->getNegativeMask());
+  }
+  case DecoderTreeNode::Decode: {
+    const auto *N = static_cast<const DecodeNode *>(Node);
+    unsigned InstOpcode = N->getEncoding().getInstruction()->EnumVal;
+    unsigned DecoderIndex = TableInfo.getDecoderIndex(N->getDecoderString());
+    return 1 + getULEB128Size(InstOpcode) + getULEB128Size(DecoderIndex);
+  }
+  }
+  llvm_unreachable("Unknown node kind");
+}
+
+unsigned DecoderTableEmitter::computeTableSize(const DecoderTreeNode *Root,
+                                               unsigned BitWidth) const {
+  unsigned Size = 0;
+  if (SpecializeDecodersPerBitwidth)
+    Size += getULEB128Size(BitWidth);
+  Size += computeNodeSize(Root);
+  return Size;
+}
+
+void DecoderTableEmitter::emitStartLine() {
+  CommentIndex = CurrentIndex;
+  OS.indent(2);
+}
+
+void DecoderTableEmitter::emitOpcode(StringRef Name) {
+  emitStartLine();
+  OS << Name << ", ";
+  ++CurrentIndex;
+}
+
+void DecoderTableEmitter::emitByte(uint8_t Val) {
+  OS << static_cast<unsigned>(Val) << ", ";
+  ++CurrentIndex;
+}
+
+void DecoderTableEmitter::emitUInt8(unsigned Val) {
+  assert(isUInt<8>(Val));
+  emitByte(Val);
+}
+
+void DecoderTableEmitter::emitULEB128(uint64_t Val) {
+  while (Val >= 0x80) {
+    emitByte((Val & 0x7F) | 0x80);
+    Val >>= 7;
+  }
+  emitByte(Val);
+}
+
+formatted_raw_ostream &DecoderTableEmitter::emitComment(indent Indent) {
+  constexpr unsigned CommentColumn = 45;
+  if (OS.getColumn() > CommentColumn)
+    OS << '\n';
+  OS.PadToColumn(CommentColumn);
+  OS << "// " << format_decimal(CommentIndex, IndexWidth) << ": " << Indent;
+  return OS;
+}
+
+void DecoderTableEmitter::emitCheckAnyNode(const CheckAnyNode *N,
+                                           indent Indent) {
+  for (const DecoderTreeNode *Child : drop_end(N->children())) {
+    emitOpcode("OPC_Scope");
+    emitULEB128(computeNodeSize(Child));
+
+    emitComment(Indent) << "{\n";
+    emitNode(Child, Indent + 1);
+    emitComment(Indent) << "}\n";
+  }
+
+  const DecoderTreeNode *Child = *std::prev(N->child_end());
+  emitNode(Child, Indent);
+}
+
+void DecoderTableEmitter::emitCheckAllNode(const CheckAllNode *N,
+                                           indent Indent) {
+  for (const DecoderTreeNode *Child : N->children())
+    emitNode(Child, Indent);
+}
+
+void DecoderTableEmitter::emitSwitchFieldNode(const SwitchFieldNode *N,
+                                              indent Indent) {
+  unsigned LSB = N->getStartBit();
+  unsigned Width = N->getNumBits();
+  unsigned MSB = LSB + Width - 1;
+
+  emitOpcode("OPC_SwitchField");
+  emitULEB128(LSB);
+  emitUInt8(Width);
+
+  emitComment(Indent) << "switch Inst[" << MSB << ':' << LSB << "] {\n";
+
+  for (auto [Val, Child] : drop_end(N->cases())) {
+    emitStartLine();
+    emitULEB128(Val);
+    emitULEB128(computeNodeSize(Child));
+
+    emitComment(Indent) << "case " << format_hex(Val, 0) << ": {\n";
+    emitNode(Child, Indent + 1);
+    emitComment(Indent) << "}\n";
+  }
+
+  auto [Val, Child] = *std::prev(N->case_end());
+  emitStartLine();
+  emitULEB128(Val);
+  emitULEB128(0);
+
+  emitComment(Indent) << "case " << format_hex(Val, 0) << ": {\n";
+  emitNode(Child, Indent + 1);
+  emitComment(Indent) << "}\n";
+
+  emitComment(Indent) << "} // switch Inst[" << MSB << ':' << LSB << "]\n";
+}
+
+void DecoderTableEmitter::emitCheckFieldNode(const CheckFieldNode *N,
+                                             indent Indent) {
+  unsigned LSB = N->getStartBit();
+  unsigned Width = N->getNumBits();
+  unsigned MSB = LSB + Width - 1;
+  uint64_t Val = N->getValue();
+
+  emitOpcode("OPC_CheckField");
+  emitULEB128(LSB);
+  emitUInt8(Width);
+  emitULEB128(Val);
+
+  emitComment(Indent);
+  OS << "check Inst[" << MSB << ':' << LSB << "] == " << format_hex(Val, 0)
+     << '\n';
+}
+
+void DecoderTableEmitter::emitCheckPredicateNode(const CheckPredicateNode *N,
+                                                 indent Indent) {
+  unsigned PredicateIndex =
+      TableInfo.getPredicateIndex(N->getPredicateString());
+
+  emitOpcode("OPC_CheckPredicate");
+  emitULEB128(PredicateIndex);
+  TableInfo.HasCheckPredicate = true;
+
+  emitComment(Indent) << "check predicate " << PredicateIndex << "\n";
+}
+
+void DecoderTableEmitter::emitSoftFailNode(const SoftFailNode *N,
+                                           indent Indent) {
+  uint64_t PositiveMask = N->getPositiveMask();
+  uint64_t NegativeMask = N->getNegativeMask();
+
+  emitOpcode("OPC_SoftFail");
+  emitULEB128(PositiveMask);
+  emitULEB128(NegativeMask);
+  TableInfo.HasSoftFail = true;
+
+  emitComment(Indent) << "check softfail";
+  OS << " pos=" << format_hex(PositiveMask, 10);
+  OS << " neg=" << format_hex(NegativeMask, 10) << '\n';
+}
+
+void DecoderTableEmitter::emitDecodeNode(const DecodeNode *N, indent Indent) {
+  const InstructionEncoding &Encoding = N->getEncoding();
+  unsigned InstOpcode = Encoding.getInstruction()->EnumVal;
+  unsigned DecoderIndex = TableInfo.getDecoderIndex(N->getDecoderString());
+
+  emitOpcode("OPC_Decode");
+  emitULEB128(InstOpcode);
+  emitULEB128(DecoderIndex);
+
+  emitComment(Indent) << "decode to " << Encoding.getName() << " using decoder "
+                      << DecoderIndex << '\n';
+}
+
+void DecoderTableEmitter::emitNode(const DecoderTreeNode *N, indent Indent) {
+  switch (N->getKind()) {
+  case DecoderTreeNode::CheckAny:
+    return emitCheckAnyNode(static_cast<const CheckAnyNode *>(N), Indent);
+  case DecoderTreeNode::CheckAll:
+    return emitCheckAllNode(static_cast<const CheckAllNode *>(N), Indent);
+  case DecoderTreeNode::SwitchField:
+    return emitSwitchFieldNode(static_cast<const SwitchFieldNode *>(N), Indent);
+  case DecoderTreeNode::CheckField:
+    return emitCheckFieldNode(static_cast<const CheckFieldNode *>(N), Indent);
+  case DecoderTreeNode::CheckPredicate:
+    return emitCheckPredicateNode(static_cast<const CheckPredicateNode *>(N),
+                                  Indent);
+  case DecoderTreeNode::SoftFail:
+    return emitSoftFailNode(static_cast<const SoftFailNode *>(N), Indent);
+  case DecoderTreeNode::Decode:
+    return emitDecodeNode(static_cast<const DecodeNode *>(N), Indent);
+  }
+  llvm_unreachable("Unknown node kind");
+}
+
+void DecoderTableEmitter::emitTable(StringRef TableName, unsigned BitWidth,
+                                    const DecoderTreeNode *Root) {
+  analyzeNode(Root);
+
+  unsigned TableSize = computeTableSize(Root, BitWidth);
+  OS << "static const uint8_t " << TableName << "[" << TableSize << "] = {\n";
+
+  // Calculate the number of decimal places for table indices.
+  // This is simply log10 of the table size.
+  IndexWidth = 1;
+  for (unsigned S = TableSize; S /= 10;)
+    ++IndexWidth;
+
+  CurrentIndex = 0;
+
+  // When specializing decoders per bit width, each decoder table will begin
+  // with the bitwidth for that table.
+  if (SpecializeDecodersPerBitwidth) {
+    emitStartLine();
+    emitULEB128(BitWidth);
+    emitComment(indent(0)) << "BitWidth " << BitWidth << '\n';
+  }
+
+  emitNode(Root, indent(0));
+  assert(CurrentIndex == TableSize &&
+         "The size of the emitted table differs from the calculated one");
+
+  OS << "};\n";
+}
+
 /// Collects all HwModes referenced by the target for encoding purposes.
 void DecoderEmitter::collectHwModesReferencedForEncodings(
     std::vector<unsigned> &HwModeIDs,
@@ -1851,6 +1991,21 @@ DecoderEmitter::DecoderEmitter(const RecordKeeper &RK)
   parseInstructionEncodings();
 }
 
+// Emit the decoder state machine table.
+void DecoderEmitter::emitTable(formatted_raw_ostream &OS,
+                               DecoderTableInfo &TableInfo, StringRef Namespace,
+                               unsigned HwModeID, unsigned BitWidth,
+                               const DecoderTreeNode *Tree) const {
+  SmallString<32> TableName("DecoderTable");
+  TableName.append(Namespace);
+  if (HwModeID != DefaultMode)
+    TableName.append({"_", Target.getHwModes().getModeName(HwModeID)});
+  TableName.append(utostr(BitWidth));
+
+  DecoderTableEmitter TableEmitter(TableInfo, OS);
+  TableEmitter.emitTable(TableName, BitWidth, Tree);
+}
+
 // Emits disassembler code for instruction decoding.
 void DecoderEmitter::run(raw_ostream &o) const {
   formatted_raw_ostream OS(o);
@@ -1922,7 +2077,6 @@ template <typename T> constexpr uint32_t InsnBitWidth = 0;
   // Entries in `EncMap` are already sorted by bitwidth. So bucketing per
   // bitwidth can be done on-the-fly as we iterate over the map.
   DecoderTableInfo TableInfo{};
-  DecoderTableBuilder TableBuilder(Target, Encodings, TableInfo);
 
   bool HasConflict = false;
   for (const auto &[BitWidth, BWMap] : EncMap) {
@@ -1936,6 +2090,9 @@ template <typename T> constexpr uint32_t InsnBitWidth = 0;
       if (HasConflict)
         continue;
 
+      DecoderTreeBuilder TreeBuilder(Target, Encodings);
+      std::unique_ptr<DecoderTreeNode> Tree = TreeBuilder.buildTree(FC);
+
       // The decode table is cleared for each top level decoder function. The
       // predicates and decoders themselves, however, are shared across
       // different decoders to give more opportunities for uniqueing.
@@ -1943,12 +2100,9 @@ template <typename T> constexpr uint32_t InsnBitWidth = 0;
       //    across all decoder tables for a given bitwidth, else they are shared
       //    across all decoder tables.
       //  - predicates are shared across all decoder tables.
-      TableInfo.Table.clear();
-      TableBuilder.buildTable(FC, BitWidth);
-
       // Print the table to the output stream.
       emitTable(OS, TableInfo, DecoderNamespace, HwModeID, BitWidth,
-                EncodingIDs);
+                Tree.get());
     }
 
     // Each BitWidth get's its own decoders and decoder function if

>From 7ccd80c6335c1b8f36319312084340cc9f9787c7 Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <barannikov88 at gmail.com>
Date: Tue, 16 Sep 2025 20:53:31 +0300
Subject: [PATCH 02/21] update tests

---
 .../DecoderEmitterBitwidthSpecialization.td   | 44 ++++++------
 .../TableGen/DecoderEmitter/VarLenDecoder.td  | 28 ++++----
 .../DecoderEmitter/additional-encoding.td     | 54 +++++++++------
 .../TableGen/DecoderEmitter/big-filter.td     | 20 +++---
 .../DecoderEmitter/trydecode-emission.td      | 31 ++-------
 .../DecoderEmitter/trydecode-emission2.td     | 30 +++-----
 .../DecoderEmitter/trydecode-emission3.td     | 22 ++----
 .../DecoderEmitter/trydecode-emission4.td     | 23 ++-----
 .../DecoderEmitter/var-len-conflict-1.td      | 19 ++----
 llvm/test/TableGen/HwModeEncodeDecode.td      | 10 +--
 llvm/test/TableGen/HwModeEncodeDecode2.td     | 28 ++++----
 llvm/test/TableGen/HwModeEncodeDecode3.td     | 68 +++++++++----------
 12 files changed, 168 insertions(+), 209 deletions(-)

diff --git a/llvm/test/TableGen/DecoderEmitter/DecoderEmitterBitwidthSpecialization.td b/llvm/test/TableGen/DecoderEmitter/DecoderEmitterBitwidthSpecialization.td
index 71b0c99675baa..5f3e2a8d2d7df 100644
--- a/llvm/test/TableGen/DecoderEmitter/DecoderEmitterBitwidthSpecialization.td
+++ b/llvm/test/TableGen/DecoderEmitter/DecoderEmitterBitwidthSpecialization.td
@@ -84,14 +84,14 @@ def Inst3 : Instruction16Bit<3> {
 // In the default case, we emit a single decodeToMCinst function and DecodeIdx
 // is shared across all bitwidths.
 
-// CHECK-DEFAULT-LABEL: DecoderTable8[25]
-// CHECK-DEFAULT:         DecodeIdx: 0
-// CHECK-DEFAULT:         DecodeIdx: 1
+// CHECK-DEFAULT-LABEL: DecoderTable8
+// CHECK-DEFAULT:         using decoder 0
+// CHECK-DEFAULT:         using decoder 1
 // CHECK-DEFAULT:       };
 
-// CHECK-DEFAULT-LABEL: DecoderTable16[25]
-// CHECK-DEFAULT:         DecodeIdx: 2
-// CHECK-DEFAULT:         DecodeIdx: 3
+// CHECK-DEFAULT-LABEL: DecoderTable16
+// CHECK-DEFAULT:         using decoder 2
+// CHECK-DEFAULT:         using decoder 3
 // CHECK-DEFAULT:       };
 
 // CHECK-DEFAULT-LABEL: template <typename InsnType>
@@ -105,10 +105,10 @@ def Inst3 : Instruction16Bit<3> {
 // When we specialize per bitwidth, we emit 2 decodeToMCInst functions and
 // DecodeIdx is assigned per bit width.
 
-// CHECK-SPECIALIZE-NO-TABLE-LABEL:   DecoderTable8[26]
-// CHECK-SPECIALIZE-NO-TABLE:           /* 0 */ 8, // Bitwidth 8
-// CHECK-SPECIALIZE-NO-TABLE:           DecodeIdx: 0
-// CHECK-SPECIALIZE-NO-TABLE:           DecodeIdx: 1
+// CHECK-SPECIALIZE-NO-TABLE-LABEL:   DecoderTable8
+// CHECK-SPECIALIZE-NO-TABLE:           8, // 0: BitWidth 8
+// CHECK-SPECIALIZE-NO-TABLE:           using decoder 0
+// CHECK-SPECIALIZE-NO-TABLE:           using decoder 1
 // CHECK-SPECIALIZE-NO-TABLE:         };
 
 // CHECK-SPECIALIZE-NO-TABLE-LABEL:   template <typename InsnType>
@@ -117,10 +117,10 @@ def Inst3 : Instruction16Bit<3> {
 // CHECK-SPECIALIZE-NO-TABLE:           case 0
 // CHECK-SPECIALIZE-NO-TABLE:           case 1
 
-// CHECK-SPECIALIZE-NO-TABLE-LABEL:   DecoderTable16[26]
-// CHECK-SPECIALIZE-NO-TABLE:           /* 0 */ 16, // Bitwidth 16
-// CHECK-SPECIALIZE-NO-TABLE:           DecodeIdx: 0
-// CHECK-SPECIALIZE-NO-TABLE:           DecodeIdx: 1
+// CHECK-SPECIALIZE-NO-TABLE-LABEL:   DecoderTable16
+// CHECK-SPECIALIZE-NO-TABLE:           16, // 0: BitWidth 16
+// CHECK-SPECIALIZE-NO-TABLE:           using decoder 0
+// CHECK-SPECIALIZE-NO-TABLE:           using decoder 1
 // CHECK-SPECIALIZE-NO-TABLE:         };
 
 // CHECK-SPECIALIZE-NO-TABLE-LABEL:   template <typename InsnType>
@@ -138,10 +138,10 @@ def Inst3 : Instruction16Bit<3> {
 // Per bitwidth specialization with function table.
 
 // 8 bit deccoder table, functions, and function table.
-// CHECK-SPECIALIZE-TABLE-LABEL:    DecoderTable8[26]
-// CHECK-SPECIALIZE-TABLE:           /* 0 */ 8, // Bitwidth 8
-// CHECK-SPECIALIZE-TABLE:            DecodeIdx: 0
-// CHECK-SPECIALIZE-TABLE:            DecodeIdx: 1
+// CHECK-SPECIALIZE-TABLE-LABEL:    DecoderTable8
+// CHECK-SPECIALIZE-TABLE:            8, // 0: BitWidth 8
+// CHECK-SPECIALIZE-TABLE:            using decoder 0
+// CHECK-SPECIALIZE-TABLE:            using decoder 1
 // CHECK-SPECIALIZE-TABLE:          };
 
 // CHECK-SPECIALIZE-TABLE-LABEL:    template <typename InsnType>
@@ -161,10 +161,10 @@ def Inst3 : Instruction16Bit<3> {
 // CHECK-SPECIALIZE-TABLE-NEXT:     };
 
 // 16 bit deccoder table, functions, and function table.
-// CHECK-SPECIALIZE-TABLE-LABEL:    DecoderTable16[26]
-// CHECK-SPECIALIZE-TABLE:            /* 0 */ 16, // Bitwidth 16
-// CHECK-SPECIALIZE-TABLE:            DecodeIdx: 0
-// CHECK-SPECIALIZE-TABLE:            DecodeIdx: 1
+// CHECK-SPECIALIZE-TABLE-LABEL:    DecoderTable16
+// CHECK-SPECIALIZE-TABLE:            16, // 0: BitWidth 16
+// CHECK-SPECIALIZE-TABLE:            using decoder 0
+// CHECK-SPECIALIZE-TABLE:            using decoder 1
 // CHECK-SPECIALIZE-TABLE:          };
 
 // CHECK-SPECIALIZE-TABLE-LABEL:    template <typename InsnType>
diff --git a/llvm/test/TableGen/DecoderEmitter/VarLenDecoder.td b/llvm/test/TableGen/DecoderEmitter/VarLenDecoder.td
index d046c1a192111..faac5cba4579f 100644
--- a/llvm/test/TableGen/DecoderEmitter/VarLenDecoder.td
+++ b/llvm/test/TableGen/DecoderEmitter/VarLenDecoder.td
@@ -1,5 +1,4 @@
-// RUN: llvm-tblgen -gen-disassembler -I %p/../../../include %s | FileCheck %s --check-prefixes=CHECK,CHECK-SMALL
-// RUN: llvm-tblgen -gen-disassembler --large-decoder-table -I %p/../../../include %s | FileCheck %s --check-prefixes=CHECK,CHECK-LARGE
+// RUN: llvm-tblgen -gen-disassembler -I %p/../../../include %s | FileCheck %s --check-prefixes=CHECK
 
 include "llvm/Target/Target.td"
 
@@ -53,19 +52,16 @@ def FOO32 : MyVarInst<MemOp32> {
 // CHECK-NEXT: 43,
 // CHECK-NEXT: };
 
-// CHECK-SMALL:      /* 0 */       OPC_ExtractField, 3, 5,                // Field = Inst{7-3}
-// CHECK-SMALL-NEXT: /* 3 */       OPC_FilterValueOrSkip, 8, 4, 0,        // if Field != 0x8 skip to 11
-// CHECK-SMALL-NEXT: /* 7 */       OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 0, // Opcode: FOO16
-// CHECK-SMALL-NEXT: /* 11 */      OPC_FilterValue, 9,                    // if Field != 0x9 pop scope
-// CHECK-SMALL-NEXT: /* 13 */      OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 1, // Opcode: FOO32
-// CHECK-SMALL-NEXT: };
-
-// CHECK-LARGE:      /* 0 */       OPC_ExtractField, 3, 5,                // Field = Inst{7-3}
-// CHECK-LARGE-NEXT: /* 3 */       OPC_FilterValueOrSkip, 8, 4, 0, 0,     // if Field != 0x8 skip to 12
-// CHECK-LARGE-NEXT: /* 8 */       OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 0, // Opcode: FOO16
-// CHECK-LARGE-NEXT: /* 12 */      OPC_FilterValue, 9,                    // if Field != 0x9 pop scope
-// CHECK-LARGE-NEXT: /* 14 */      OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 1, // Opcode: FOO32
-// CHECK-LARGE-NEXT: };
+// CHECK-LABEL: static const uint8_t DecoderTable43[15] = {
+// CHECK-NEXT:    OPC_SwitchField, 3, 5,       //  0: switch Inst[7:3] {
+// CHECK-NEXT:    8, 4,                        //  3: case 0x8: {
+// CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 0, //  5:  decode to FOO16 using decoder 0
+// CHECK-NEXT:                                 //  5: }
+// CHECK-NEXT:    9, 0,                        //  9: case 0x9: {
+// CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 1, // 11:  decode to FOO32 using decoder 1
+// CHECK-NEXT:                                 // 11: }
+// CHECK-NEXT:                                 // 11: } // switch Inst[7:3]
+// CHECK-NEXT:  };
 
 // CHECK:      case 0:
 // CHECK-NEXT: tmp = fieldFromInstruction(insn, 8, 3);
@@ -86,7 +82,7 @@ def FOO32 : MyVarInst<MemOp32> {
 // CHECK-NEXT: MI.addOperand(MCOperand::createImm(tmp));
 // CHECK-NEXT: return S;
 
-// CHECK-LABEL: case OPC_ExtractField: {
+// CHECK-LABEL: case OPC_SwitchField: {
 // CHECK: makeUp(insn, Start + Len);
 
 // CHECK-LABEL: case OPC_CheckField: {
diff --git a/llvm/test/TableGen/DecoderEmitter/additional-encoding.td b/llvm/test/TableGen/DecoderEmitter/additional-encoding.td
index e3ef572bfa7f8..0869a5ca1a46d 100644
--- a/llvm/test/TableGen/DecoderEmitter/additional-encoding.td
+++ b/llvm/test/TableGen/DecoderEmitter/additional-encoding.td
@@ -30,28 +30,38 @@ class I<dag out_ops, dag in_ops> : Instruction {
   let OutOperandList = out_ops;
 }
 
-// CHECK:      /* 0 */  OPC_ExtractField, 12, 4,               // Field = Inst{15-12}
-// CHECK-NEXT: /* 3 */  OPC_FilterValueOrSkip, 0, 15, 0,       // if Field != 0x0 skip to 22
-// CHECK-NEXT: /* 7 */  OPC_Scope, 8, 0,                       // end scope at 18
-// CHECK-NEXT: /* 10 */ OPC_CheckField, 6, 6, 0,               // if Inst{11-6} != 0x0
-// CHECK-NEXT: /* 14 */ OPC_Decode, {{[0-9]+}}, 2, 0,          // Opcode: {{.*}}:NOP, DecodeIdx: 0
-// CHECK-NEXT: /* 18 */ OPC_Decode, {{[0-9]+}}, 2, 1,          // Opcode: SHIFT0, DecodeIdx: 1
-// CHECK-NEXT: /* 22 */ OPC_FilterValueOrSkip, 1, 15, 0,       // if Field != 0x1 skip to 41
-// CHECK-NEXT: /* 26 */ OPC_Scope, 8, 0,                       // end scope at 37
-// CHECK-NEXT: /* 29 */ OPC_CheckField, 6, 6, 0,               // if Inst{11-6} != 0x0
-// CHECK-NEXT: /* 33 */ OPC_Decode, {{[0-9]+}}, 2, 0,          // Opcode: {{.*}}:NOP, DecodeIdx: 0
-// CHECK-NEXT: /* 37 */ OPC_Decode, {{[0-9]+}}, 2, 1,          // Opcode: SHIFT1, DecodeIdx: 1
-// CHECK-NEXT: /* 41 */ OPC_FilterValueOrSkip, 2, 15, 0,       // if Field != 0x2 skip to 60
-// CHECK-NEXT: /* 45 */ OPC_Scope, 8, 0,                       // end scope at 56
-// CHECK-NEXT: /* 48 */ OPC_CheckField, 6, 6, 0,               // if Inst{11-6} != 0x0
-// CHECK-NEXT: /* 52 */ OPC_Decode, {{[0-9]+}}, 2, 0,          // Opcode: {{.*}}:NOP, DecodeIdx: 0
-// CHECK-NEXT: /* 56 */ OPC_Decode, {{[0-9]+}}, 2, 1,          // Opcode: SHIFT2, DecodeIdx: 1
-// CHECK-NEXT: /* 60 */ OPC_FilterValue, 3,                    // if Field != 0x3
-// CHECK-NEXT: /* 62 */ OPC_Scope, 8, 0,                       // end scope at 73
-// CHECK-NEXT: /* 65 */ OPC_CheckField, 6, 6, 0,               // if Inst{11-6} != 0x0
-// CHECK-NEXT: /* 69 */ OPC_Decode, {{[0-9]+}}, 2, 0,          // Opcode: {{.*}}:NOP, DecodeIdx: 0
-// CHECK-NEXT: /* 73 */ OPC_Decode, {{[0-9]+}}, 2, 1,          // Opcode: SHIFT3, DecodeIdx: 1
-
+// CHECK-LABEL: static const uint8_t DecoderTable16[67] = {
+// CHECK-NEXT:    OPC_SwitchField, 12, 4,      //  0: switch Inst[15:12] {
+// CHECK-NEXT:    0, 14,                       //  3: case 0x0: {
+// CHECK-NEXT:    OPC_Scope, 8,                //  5:  {
+// CHECK-NEXT:    OPC_CheckField, 6, 6, 0,     //  7:   check Inst[11:6] == 0x0
+// CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 0, // 11:   decode to NOP using decoder 0
+// CHECK-NEXT:                                 // 11:  }
+// CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 1, // 15:  decode to SHIFT0 using decoder 1
+// CHECK-NEXT:                                 // 15: }
+// CHECK-NEXT:    1, 14,                       // 19: case 0x1: {
+// CHECK-NEXT:    OPC_Scope, 8,                // 21:  {
+// CHECK-NEXT:    OPC_CheckField, 6, 6, 0,     // 23:   check Inst[11:6] == 0x0
+// CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 0, // 27:   decode to {{.*}}:NOP using decoder 0
+// CHECK-NEXT:                                 // 27:  }
+// CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 1, // 31:  decode to SHIFT1 using decoder 1
+// CHECK-NEXT:                                 // 31: }
+// CHECK-NEXT:    2, 14,                       // 35: case 0x2: {
+// CHECK-NEXT:    OPC_Scope, 8,                // 37:  {
+// CHECK-NEXT:    OPC_CheckField, 6, 6, 0,     // 39:   check Inst[11:6] == 0x0
+// CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 0, // 43:   decode to {{.*}}:NOP using decoder 0
+// CHECK-NEXT:                                 // 43:  }
+// CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 1, // 47:  decode to SHIFT2 using decoder 1
+// CHECK-NEXT:                                 // 47: }
+// CHECK-NEXT:    3, 0,                        // 51: case 0x3: {
+// CHECK-NEXT:    OPC_Scope, 8,                // 53:  {
+// CHECK-NEXT:    OPC_CheckField, 6, 6, 0,     // 55:   check Inst[11:6] == 0x0
+// CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 0, // 59:   decode to {{.*}}:NOP using decoder 0
+// CHECK-NEXT:                                 // 59:  }
+// CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 1, // 63:  decode to SHIFT3 using decoder 1
+// CHECK-NEXT:                                 // 63: }
+// CHECK-NEXT:                                 // 63: } // switch Inst[15:12]
+// CHECK-NEXT:  };
 
 class SHIFT<bits<2> opc> : I<(outs), (ins ShAmtOp:$shamt)>, EncSHIFT<opc>;
 def SHIFT0 : SHIFT<0>;
diff --git a/llvm/test/TableGen/DecoderEmitter/big-filter.td b/llvm/test/TableGen/DecoderEmitter/big-filter.td
index 87aa7f814c3f3..fa516ad5665a5 100644
--- a/llvm/test/TableGen/DecoderEmitter/big-filter.td
+++ b/llvm/test/TableGen/DecoderEmitter/big-filter.td
@@ -11,14 +11,18 @@ class I : Instruction {
 
 // Check that a 64-bit filter with all bits set does not confuse DecoderEmitter.
 //
-// CHECK-LABEL: static const uint8_t DecoderTable128[34] = {
-// CHECK-NEXT:  /* 0 */  OPC_ExtractField, 0, 64,        // Field = Inst{63-0}
-// CHECK-NEXT:  /* 3 */  OPC_FilterValueOrSkip, 1, 8, 0, // if Field != 0x1 skip to 15
-// CHECK-NEXT:  /* 7 */  OPC_CheckField, 127, 1, 1,      // if Inst{127} != 0x1
-// CHECK-NEXT:  /* 11 */ OPC_Decode, {{[0-9]+}}, 2, 0,   // Opcode: I2, DecodeIdx: 0
-// CHECK-NEXT:  /* 15 */ OPC_FilterValue, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1, // if Field != 0xffffffffffffffff
-// CHECK-NEXT:  /* 26 */ OPC_CheckField, 127, 1, 0,      // if Inst{127} != 0x0
-// CHECK-NEXT:  /* 30 */ OPC_Decode, {{[0-9]+}}, 2, 0,   // Opcode: I1, DecodeIdx: 0
+// CHECK-LABEL: static const uint8_t DecoderTable128[32] = {
+// CHECK-NEXT:    OPC_SwitchField, 0, 64,      //  0: switch Inst[63:0] {
+// CHECK-NEXT:    1, 8,                        //  3: case 0x1: {
+// CHECK-NEXT:    OPC_CheckField, 127, 1, 1,   //  5:  check Inst[127:127] == 0x1
+// CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 0, //  9:  decode to I2 using decoder 0
+// CHECK-NEXT:                                 //  9: }
+// CHECK-NEXT:    255, 255, 255, 255, 255, 255, 255, 255, 255, 1, 0,
+// CHECK-NEXT:                                 // 13: case 0xffffffffffffffff: {
+// CHECK-NEXT:    OPC_CheckField, 127, 1, 0,   // 24:  check Inst[127:127] == 0x0
+// CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 0, // 28:  decode to I1 using decoder 0
+// CHECK-NEXT:                                 // 28: }
+// CHECK-NEXT:                                 // 28: } // switch Inst[63:0]
 // CHECK-NEXT:  };
 
 def I1 : I {
diff --git a/llvm/test/TableGen/DecoderEmitter/trydecode-emission.td b/llvm/test/TableGen/DecoderEmitter/trydecode-emission.td
index b04ba2b4a6f5b..d6b1557d94bf4 100644
--- a/llvm/test/TableGen/DecoderEmitter/trydecode-emission.td
+++ b/llvm/test/TableGen/DecoderEmitter/trydecode-emission.td
@@ -1,5 +1,4 @@
 // RUN: llvm-tblgen -gen-disassembler -I %p/../../../include %s | FileCheck %s
-// RUN: llvm-tblgen -gen-disassembler --large-decoder-table -I %p/../../../include %s | FileCheck %s --check-prefix=CHECK-LARGE
 
 // Check that if decoding of an instruction fails and the instruction does not
 // have a complete decoder method that can determine if the bitpattern is valid
@@ -34,29 +33,13 @@ def InstB : TestInstruction {
   let hasCompleteDecoder = 0;
 }
 
-// CHECK:      /* 0 */       OPC_CheckField, 4, 4, 0,                   // if Inst{7-4} != 0x0
-// CHECK-NEXT: /* 4 */       OPC_Scope, 8, 0,                           // end scope at 15
-// CHECK-NEXT: /* 7 */       OPC_CheckField, 2, 2, 0,                   // if Inst{3-2} != 0x0
-// CHECK-NEXT: /* 11 */      OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 0,     // Opcode: InstB, DecodeIdx: 0
-// CHECK-NEXT: /* 15 */      OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 1,     // Opcode: InstA, DecodeIdx: 1
+// CHECK-LABEL: static const uint8_t DecoderTable8[18] = {
+// CHECK-NEXT:    OPC_CheckField, 4, 4, 0,     //  0: check Inst[7:4] == 0x0
+// CHECK-NEXT:    OPC_Scope, 8,                //  4: {
+// CHECK-NEXT:    OPC_CheckField, 2, 2, 0,     //  6:  check Inst[3:2] == 0x0
+// CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 0, // 10:  decode to InstB using decoder 0
+// CHECK-NEXT:                                 // 10: }
+// CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 1, // 14: decode to InstA using decoder 1
 // CHECK-NEXT: };
 
 // CHECK: if (!Check(S, DecodeInstB(MI, insn, Address, Decoder))) { DecodeComplete = false; return MCDisassembler::Fail; }
-
-// CHECK:       unsigned NumToSkip = *Ptr++;
-// CHECK-NEXT:  NumToSkip |= (*Ptr++) << 8;
-// CHECK-NEXT:  return NumToSkip;
-
-// CHECK-LARGE:      /* 0 */       OPC_CheckField, 4, 4, 0,                  // if Inst{7-4} != 0x0
-// CHECK-LARGE-NEXT: /* 4 */       OPC_Scope, 8, 0, 0,                       // end scope at 16
-// CHECK-LARGE-NEXT: /* 8 */       OPC_CheckField, 2, 2, 0,                  // if Inst{3-2} != 0x0
-// CHECK-LARGE-NEXT: /* 12 */      OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 0,    // Opcode: InstB, DecodeIdx: 0
-// CHECK-LARGE-NEXT: /* 16 */      OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 1,    // Opcode: InstA, DecodeIdx: 1
-// CHECK-LARGE-NEXT: };
-
-// CHECK-LARGE: if (!Check(S, DecodeInstB(MI, insn, Address, Decoder))) { DecodeComplete = false; return MCDisassembler::Fail; }
-
-// CHECK-LARGE:       unsigned NumToSkip = *Ptr++;
-// CHECK-LARGE-NEXT:  NumToSkip |= (*Ptr++) << 8;
-// CHECK-LARGE-NEXT:  NumToSkip |= (*Ptr++) << 16;
-// CHECK-LARGE-NEXT:  return NumToSkip;
diff --git a/llvm/test/TableGen/DecoderEmitter/trydecode-emission2.td b/llvm/test/TableGen/DecoderEmitter/trydecode-emission2.td
index 7fd26fffd28b7..896e62c08e4f5 100644
--- a/llvm/test/TableGen/DecoderEmitter/trydecode-emission2.td
+++ b/llvm/test/TableGen/DecoderEmitter/trydecode-emission2.td
@@ -1,5 +1,4 @@
 // RUN: llvm-tblgen -gen-disassembler -I %p/../../../include %s | FileCheck %s
-// RUN: llvm-tblgen -gen-disassembler --large-decoder-table -I %p/../../../include %s | FileCheck %s --check-prefix=CHECK-LARGE
 
 include "llvm/Target/Target.td"
 
@@ -31,25 +30,16 @@ def InstB : TestInstruction {
   let hasCompleteDecoder = 0;
 }
 
-// CHECK:      /* 0 */       OPC_CheckField, 2, 1, 0,
-// CHECK-NEXT: /* 4 */       OPC_CheckField, 5, 3, 0,
-// CHECK-NEXT: /* 8 */       OPC_Scope, 8, 0, // end scope at 19
-// CHECK-NEXT: /* 11 */      OPC_CheckField, 0, 2, 3,
-// CHECK-NEXT: /* 15 */      OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 0,
-// CHECK-NEXT: /* 19 */      OPC_CheckField, 3, 2, 0,
-// CHECK-NEXT: /* 23 */      OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 1,
+// CHECK-LABEL: static const uint8_t DecoderTable8[26] = {
+// CHECK-NEXT:    OPC_CheckField, 2, 1, 0,     //  0: check Inst[2:2] == 0x0
+// CHECK-NEXT:    OPC_CheckField, 5, 3, 0,     //  4: check Inst[7:5] == 0x0
+// CHECK-NEXT:    OPC_Scope, 8,                //  8: {
+// CHECK-NEXT:    OPC_CheckField, 0, 2, 3,     // 10:  check Inst[1:0] == 0x3
+// CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 0, // 14:  decode to InstB using decoder 0
+// CHECK-NEXT:                                 // 14: }
+// CHECK-NEXT:    OPC_CheckField, 3, 2, 0,     // 18: check Inst[4:3] == 0x0
+// CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 1, // 22: decode to InstA using decoder 1
+// CHECK-NEXT: };
 
 // CHECK: if (!Check(S, DecodeInstB(MI, insn, Address, Decoder))) { DecodeComplete = false; return MCDisassembler::Fail; }
 // CHECK: if (!Check(S, DecodeInstA(MI, insn, Address, Decoder))) { DecodeComplete = false; return MCDisassembler::Fail; }
-
-// CHECK-LARGE:      /* 0 */       OPC_CheckField, 2, 1, 0,
-// CHECK-LARGE-NEXT: /* 4 */       OPC_CheckField, 5, 3, 0,
-// CHECK-LARGE-NEXT: /* 8 */       OPC_Scope, 8, 0, 0, // end scope at 20
-// CHECK-LARGE-NEXT: /* 12 */      OPC_CheckField, 0, 2, 3,
-// CHECK-LARGE-NEXT: /* 16 */      OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 0,
-// CHECK-LARGE-NEXT: /* 20 */      OPC_CheckField, 3, 2, 0,
-// CHECK-LARGE-NEXT: /* 24 */      OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 1,
-// CHECK-LARGE-NEXT: };
-
-// CHECK-LARGE: if (!Check(S, DecodeInstB(MI, insn, Address, Decoder))) { DecodeComplete = false; return MCDisassembler::Fail; }
-// CHECK-LARGE: if (!Check(S, DecodeInstA(MI, insn, Address, Decoder))) { DecodeComplete = false; return MCDisassembler::Fail; }
diff --git a/llvm/test/TableGen/DecoderEmitter/trydecode-emission3.td b/llvm/test/TableGen/DecoderEmitter/trydecode-emission3.td
index c884d6b8a93cc..1ff2b6072891c 100644
--- a/llvm/test/TableGen/DecoderEmitter/trydecode-emission3.td
+++ b/llvm/test/TableGen/DecoderEmitter/trydecode-emission3.td
@@ -1,5 +1,4 @@
 // RUN: llvm-tblgen -gen-disassembler  -I %p/../../../include %s | FileCheck %s
-// RUN: llvm-tblgen -gen-disassembler --large-decoder-table -I %p/../../../include %s | FileCheck %s --check-prefix=CHECK-LARGE
 
 include "llvm/Target/Target.td"
 
@@ -35,20 +34,13 @@ def InstB : TestInstruction {
   let AsmString = "InstB";
 }
 
-// CHECK:      /* 0 */       OPC_CheckField, 4, 4, 0,
-// CHECK-NEXT: /* 4 */       OPC_Scope, 8, 0, // end scope at 15
-// CHECK-NEXT: /* 7 */       OPC_CheckField, 2, 2, 0,
-// CHECK-NEXT: /* 11 */      OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 0,
-// CHECK-NEXT: /* 15 */      OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 1, // Opcode: InstA, DecodeIdx: 1
+// CHECK-LABEL: static const uint8_t DecoderTable8[18] = {
+// CHECK-NEXT:    OPC_CheckField, 4, 4, 0,     //  0: check Inst[7:4] == 0x0
+// CHECK-NEXT:    OPC_Scope, 8,                //  4: {
+// CHECK-NEXT:    OPC_CheckField, 2, 2, 0,     //  6:  check Inst[3:2] == 0x0
+// CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 0, // 10:  decode to InstB using decoder 0
+// CHECK-NEXT:                                 // 10: }
+// CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 1, // 14: decode to InstA using decoder 1
 // CHECK-NEXT: };
 
 // CHECK: if (!Check(S, DecodeInstBOp(MI, tmp, Address, Decoder))) { DecodeComplete = false; return MCDisassembler::Fail; }
-
-// CHECK-LARGE:      /* 0 */       OPC_CheckField, 4, 4, 0,
-// CHECK-LARGE-NEXT: /* 4 */       OPC_Scope, 8, 0, 0, // end scope at 16
-// CHECK-LARGE-NEXT: /* 8 */       OPC_CheckField, 2, 2, 0,
-// CHECK-LARGE-NEXT: /* 12 */      OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 0,
-// CHECK-LARGE-NEXT: /* 16 */      OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 1, // Opcode: InstA, DecodeIdx: 1
-// CHECK-LARGE-NEXT: };
-
-// CHECK-LARGE: if (!Check(S, DecodeInstBOp(MI, tmp, Address, Decoder))) { DecodeComplete = false; return MCDisassembler::Fail; }
diff --git a/llvm/test/TableGen/DecoderEmitter/trydecode-emission4.td b/llvm/test/TableGen/DecoderEmitter/trydecode-emission4.td
index 2ff160cda2ec5..6bfedc071f115 100644
--- a/llvm/test/TableGen/DecoderEmitter/trydecode-emission4.td
+++ b/llvm/test/TableGen/DecoderEmitter/trydecode-emission4.td
@@ -1,5 +1,4 @@
 // RUN: llvm-tblgen -gen-disassembler  -I %p/../../../include %s | FileCheck %s
-// RUN: llvm-tblgen -gen-disassembler --large-decoder-table -I %p/../../../include %s | FileCheck %s --check-prefix=CHECK-LARGE
 
 // Test for OPC_ExtractField/OPC_CheckField with start bit > 255.
 // These large start values may arise for architectures with long instruction
@@ -33,21 +32,13 @@ def InstB : TestInstruction {
   let hasCompleteDecoder = 0;
 }
 
-// CHECK:      /* 0 */       OPC_CheckField, 250, 3, 4, 0,
-// CHECK-NEXT: /* 5 */       OPC_Scope, 9, 0, // end scope at 17
-// CHECK-NEXT: /* 8 */       OPC_CheckField, 248, 3, 2, 0,
-// CHECK-NEXT: /* 13 */      OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 0,
-// CHECK-NEXT: /* 17 */      OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 1, // Opcode: InstA, DecodeIdx: 1
+// CHECK-LABEL: static const uint8_t DecoderTable512[20] = {
+// CHECK-NEXT:    OPC_CheckField, 250, 3, 4, 0, //  0: check Inst[509:506] == 0x0
+// CHECK-NEXT:    OPC_Scope, 9,                 //  5: {
+// CHECK-NEXT:    OPC_CheckField, 248, 3, 2, 0, //  7:  check Inst[505:504] == 0x0
+// CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 0,  // 12:  decode to InstB using decoder 0
+// CHECK-NEXT:                                  // 12: }
+// CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 1,  // 16: decode to InstA using decoder 1
 // CHECK-NEXT: };
 
 // CHECK: if (!Check(S, DecodeInstB(MI, insn, Address, Decoder))) { DecodeComplete = false; return MCDisassembler::Fail; }
-
-
-// CHECK-LARGE:      /* 0 */       OPC_CheckField, 250, 3, 4, 0,
-// CHECK-LARGE-NEXT: /* 5 */       OPC_Scope, 9, 0, 0, // end scope at 18
-// CHECK-LARGE-NEXT: /* 9 */       OPC_CheckField, 248, 3, 2, 0,
-// CHECK-LARGE-NEXT: /* 14 */      OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 0,
-// CHECK-LARGE-NEXT: /* 18 */      OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 1, // Opcode: InstA, DecodeIdx: 1
-// CHECK-LARGE-NEXT: };
-
-// CHECK-LARGE: if (!Check(S, DecodeInstB(MI, insn, Address, Decoder))) { DecodeComplete = false; return MCDisassembler::Fail; }
diff --git a/llvm/test/TableGen/DecoderEmitter/var-len-conflict-1.td b/llvm/test/TableGen/DecoderEmitter/var-len-conflict-1.td
index b18d28a9f5136..50e2d6d97f721 100644
--- a/llvm/test/TableGen/DecoderEmitter/var-len-conflict-1.td
+++ b/llvm/test/TableGen/DecoderEmitter/var-len-conflict-1.td
@@ -18,19 +18,12 @@ class I : Instruction {
 // 00000001 ________  I16_1
 // 00000010 ________  I16_2
 
-// CHECK:      /* 0 */  OPC_Scope, 17, 0,               // end scope at 20
-// CHECK-NEXT: /* 3 */  OPC_ExtractField, 0, 1,         // Field = Inst{0}
-// CHECK-NEXT: /* 6 */  OPC_FilterValueOrSkip, 0, 4, 0, // if Field != 0x0 skip to 14
-// CHECK-NEXT: /* 10 */ OPC_Decode, {{[0-9]+}}, 2, 0,   // Opcode: I8_0, DecodeIdx: 0
-// CHECK-NEXT: /* 14 */ OPC_FilterValue, 1,             // if Field != 0x1
-// CHECK-NEXT: /* 16 */ OPC_Decode, {{[0-9]+}}, 2, 0,   // Opcode: I8_1, DecodeIdx: 0
-// CHECK-NEXT: /* 20 */ OPC_ExtractField, 8, 8,         // Field = Inst{15-8}
-// CHECK-NEXT: /* 23 */ OPC_FilterValueOrSkip, 0, 4, 0, // if Field != 0x0 skip to 31
-// CHECK-NEXT: /* 27 */ OPC_Decode, {{[0-9]+}}, 2, 1,   // Opcode: I16_0, DecodeIdx: 1
-// CHECK-NEXT: /* 31 */ OPC_FilterValueOrSkip, 1, 4, 0, // if Field != 0x1 skip to 39
-// CHECK-NEXT: /* 35 */ OPC_Decode, {{[0-9]+}}, 2, 1,   // Opcode: I16_1, DecodeIdx: 1
-// CHECK-NEXT: /* 39 */ OPC_FilterValue, 2,             // if Field != 0x2
-// CHECK-NEXT: /* 41 */ OPC_Decode, {{[0-9]+}}, 2, 1,   // Opcode: I16_2, DecodeIdx: 1
+// CHECK: switch Inst[0:0]
+// CHECK: decode to I8_0
+// CHECK: decode to I8_1
+// CHECK: switch Inst[15:8]
+// CHECK: decode to I16_0
+// CHECK: decode to I16_1
 
 def I8_0  : I { dag Inst = (descend (operand "$op", 7), 0b0); }
 def I8_1  : I { dag Inst = (descend (operand "$op", 7), 0b1); }
diff --git a/llvm/test/TableGen/HwModeEncodeDecode.td b/llvm/test/TableGen/HwModeEncodeDecode.td
index 6df5fd46297bf..7badc895d5f50 100644
--- a/llvm/test/TableGen/HwModeEncodeDecode.td
+++ b/llvm/test/TableGen/HwModeEncodeDecode.td
@@ -71,12 +71,12 @@ def baz : Instruction {
 }
 
 // DECODER-LABEL: DecoderTable_ModeA32
-// DECODER-DAG: Opcode: fooTypeEncA:foo
-// DECODER-DAG: Opcode: bar
+// DECODER-DAG: decode to fooTypeEncA:foo
+// DECODER-DAG: decode to bar
 // DECODER-LABEL: DecoderTable_ModeB32
-// DECODER-DAG: Opcode: fooTypeEncB:foo
-// DECODER-DAG: Opcode: fooTypeEncA:baz
-// DECODER-DAG: Opcode: bar
+// DECODER-DAG: decode to fooTypeEncA:baz
+// DECODER-DAG: decode to bar
+// DECODER-DAG: decode to fooTypeEncB:foo
 
 // ENCODER-LABEL:   static const uint64_t InstBits_ModeA[] = {
 // ENCODER:         UINT64_C(2),        // bar
diff --git a/llvm/test/TableGen/HwModeEncodeDecode2.td b/llvm/test/TableGen/HwModeEncodeDecode2.td
index 6431a9bd69a55..e1a5ccffec722 100644
--- a/llvm/test/TableGen/HwModeEncodeDecode2.td
+++ b/llvm/test/TableGen/HwModeEncodeDecode2.td
@@ -94,25 +94,25 @@ let OutOperandList = (outs) in {
 }
 
 // DECODER-LABEL: DecoderTable_ModeA32
-// DECODER-DAG: Opcode: fooTypeEncA:foo
-// DECODER-DAG: Opcode: bar
+// DECODER-DAG: decode to fooTypeEncA:foo
+// DECODER-DAG: decode to bar
 // DECODER-LABEL: DecoderTable_ModeB32
-// DECODER-DAG: Opcode: fooTypeEncB:foo
-// DECODER-DAG: Opcode: fooTypeEncA:baz
-// DECODER-DAG: Opcode: bar
+// DECODER-DAG: decode to fooTypeEncB:foo
+// DECODER-DAG: decode to fooTypeEncA:baz
+// DECODER-DAG: decode to bar
 // DECODER-LABEL: DecoderTableAlt_ModeA32
-// DECODER-DAG: Opcode: unrelated
+// DECODER-DAG: decode to unrelated
 // DECODER-LABEL: DecoderTableAlt_ModeB32
-// DECODER-DAG: Opcode: unrelated
+// DECODER-DAG: decode to unrelated
 
 // DECODER-SUPPRESS-LABEL: DecoderTable32
-// DECODER-SUPPRESS-DAG: Opcode: bar
+// DECODER-SUPPRESS-DAG: decode to bar
 // DECODER-SUPPRESS-LABEL: DecoderTable_ModeA32
-// DECODER-SUPPRESS-DAG: Opcode: fooTypeEncA:foo
-// DECODER-SUPPRESS-NOT: Opcode: bar
+// DECODER-SUPPRESS-DAG: decode to fooTypeEncA:foo
+// DECODER-SUPPRESS-NOT: decode to bar
 // DECODER-SUPPRESS-LABEL: DecoderTable_ModeB32
-// DECODER-SUPPRESS-DAG: Opcode: fooTypeEncB:foo
-// DECODER-SUPPRESS-DAG: Opcode: fooTypeEncA:baz
-// DECODER-SUPPRESS-NOT: Opcode: bar
+// DECODER-SUPPRESS-DAG: decode to fooTypeEncB:foo
+// DECODER-SUPPRESS-DAG: decode to fooTypeEncA:baz
+// DECODER-SUPPRESS-NOT: decode to bar
 // DECODER-SUPPRESS-LABEL: DecoderTableAlt32
-// DECODER-SUPPRESS-DAG: Opcode: unrelated
+// DECODER-SUPPRESS-DAG: decode to unrelated
diff --git a/llvm/test/TableGen/HwModeEncodeDecode3.td b/llvm/test/TableGen/HwModeEncodeDecode3.td
index 3893216f70e01..36c65214d2719 100644
--- a/llvm/test/TableGen/HwModeEncodeDecode3.td
+++ b/llvm/test/TableGen/HwModeEncodeDecode3.td
@@ -117,46 +117,46 @@ def unrelated: Instruction {
 // ‘DecoderTableAlt_ModeA32’, ‘DecoderTableAlt_ModeB32’ and 'DecoderTable_ModeC32' are
 // exact duplicates and could effectively be merged into one.
 // DECODER-LABEL: DecoderTable32
-// DECODER-DAG: Opcode: bar
+// DECODER-DAG: decode to bar
 // DECODER-LABEL: DecoderTable_ModeA32
-// DECODER-DAG: Opcode: fooTypeEncA:foo
-// DECODER-DAG: Opcode: bar
+// DECODER-DAG: decode to fooTypeEncA:foo
+// DECODER-DAG: decode to bar
 // DECODER-LABEL: DecoderTable_ModeB32
-// DECODER-DAG: Opcode: fooTypeEncB:foo
-// DECODER-DAG: Opcode: fooTypeEncA:baz
-// DECODER-DAG: Opcode: bar
+// DECODER-DAG: decode to fooTypeEncB:foo
+// DECODER-DAG: decode to fooTypeEncA:baz
+// DECODER-DAG: decode to bar
 // DECODER-LABEL: DecoderTable_ModeC32
-// DECODER-DAG: Opcode: fooTypeEncC:foo
-// DECODER-DAG: Opcode: bar
+// DECODER-DAG: decode to fooTypeEncC:foo
+// DECODER-DAG: decode to bar
 // DECODER-LABEL: DecoderTableAlt32
-// DECODER-DAG: Opcode: unrelated
+// DECODER-DAG: decode to unrelated
 // DECODER-LABEL: DecoderTableAlt_ModeA32
-// DECODER-DAG: Opcode: unrelated
+// DECODER-DAG: decode to unrelated
 // DECODER-LABEL: DecoderTableAlt_ModeB32
-// DECODER-DAG: Opcode: unrelated
+// DECODER-DAG: decode to unrelated
 // DECODER-LABEL: DecoderTableAlt_ModeC32
-// DECODER-DAG: Opcode: unrelated
+// DECODER-DAG: decode to unrelated
 // DECODER-LABEL: DecoderTable64
-// DECODER-DAG: Opcode: fooTypeEncDefault:foo
+// DECODER-DAG: decode to fooTypeEncDefault:foo
 
 // Under the 'O1' optimization level, unnecessary duplicate tables will be eliminated,
 // reducing the four ‘Alt’ tables down to just one.
 // DECODER-SUPPRESS-O1-LABEL: DecoderTable32
-// DECODER-SUPPRESS-O1-DAG: Opcode: bar
+// DECODER-SUPPRESS-O1-DAG: decode to bar
 // DECODER-SUPPRESS-O1-LABEL: DecoderTable_ModeA32
-// DECODER-SUPPRESS-O1-DAG: Opcode: fooTypeEncA:foo
-// DECODER-SUPPRESS-O1-DAG: Opcode: bar
+// DECODER-SUPPRESS-O1-DAG: decode to fooTypeEncA:foo
+// DECODER-SUPPRESS-O1-DAG: decode to bar
 // DECODER-SUPPRESS-O1-LABEL: DecoderTable_ModeB32
-// DECODER-SUPPRESS-O1-DAG: Opcode: fooTypeEncB:foo
-// DECODER-SUPPRESS-O1-DAG: Opcode: fooTypeEncA:baz
-// DECODER-SUPPRESS-O1-DAG: Opcode: bar
+// DECODER-SUPPRESS-O1-DAG: decode to fooTypeEncB:foo
+// DECODER-SUPPRESS-O1-DAG: decode to fooTypeEncA:baz
+// DECODER-SUPPRESS-O1-DAG: decode to bar
 // DECODER-SUPPRESS-O1-LABEL: DecoderTable_ModeC32
-// DECODER-SUPPRESS-O1-DAG: Opcode: fooTypeEncC:foo
-// DECODER-SUPPRESS-O1-DAG: Opcode: bar
+// DECODER-SUPPRESS-O1-DAG: decode to fooTypeEncC:foo
+// DECODER-SUPPRESS-O1-DAG: decode to bar
 // DECODER-SUPPRESS-O1-LABEL: DecoderTableAlt32
-// DECODER-SUPPRESS-O1-DAG: Opcode: unrelated
+// DECODER-SUPPRESS-O1-DAG: decode to unrelated
 // DECODER-SUPPRESS-O1-LABEL: DecoderTable64
-// DECODER-SUPPRESS-O1-DAG: Opcode: fooTypeEncDefault:foo
+// DECODER-SUPPRESS-O1-DAG: decode to fooTypeEncDefault:foo
 
 // Under the 'O2' optimization condition, instructions possessing the 'EncodingByHwMode'
 // attribute will be extracted from their original DecoderNamespace and placed into their
@@ -165,22 +165,22 @@ def unrelated: Instruction {
 // approach will significantly reduce instruction redundancy, but it necessitates users to thoroughly
 // consider the interplay between HwMode and DecoderNamespace for their instructions.
 // DECODER-SUPPRESS-O2-LABEL: DecoderTable32
-// DECODER-SUPPRESS-O2-DAG: Opcode: bar
+// DECODER-SUPPRESS-O2-DAG: decode to bar
 // DECODER-SUPPRESS-O2-LABEL: DecoderTable_ModeA32
-// DECODER-SUPPRESS-O2-DAG: Opcode: fooTypeEncA:foo
-// DECODER-SUPPRESS-O2-NOT: Opcode: bar
+// DECODER-SUPPRESS-O2-DAG: decode to fooTypeEncA:foo
+// DECODER-SUPPRESS-O2-NOT: decode to bar
 // DECODER-SUPPRESS-O2-LABEL: DecoderTable_ModeB32
-// DECODER-SUPPRESS-O2-DAG: Opcode: fooTypeEncB:foo
-// DECODER-SUPPRESS-O2-DAG: Opcode: fooTypeEncA:baz
-// DECODER-SUPPRESS-O2-NOT: Opcode: bar
+// DECODER-SUPPRESS-O2-DAG: decode to fooTypeEncB:foo
+// DECODER-SUPPRESS-O2-DAG: decode to fooTypeEncA:baz
+// DECODER-SUPPRESS-O2-NOT: decode to bar
 // DECODER-SUPPRESS-O2-LABEL: DecoderTable_ModeC32
-// DECODER-SUPPRESS-O2-DAG: Opcode: fooTypeEncC:foo
-// DECODER-SUPPRESS-O2-NOT: Opcode: bar
+// DECODER-SUPPRESS-O2-DAG: decode to fooTypeEncC:foo
+// DECODER-SUPPRESS-O2-NOT: decode to bar
 // DECODER-SUPPRESS-O2-LABEL: DecoderTableAlt32
-// DECODER-SUPPRESS-O2-DAG: Opcode: unrelated
+// DECODER-SUPPRESS-O2-DAG: decode to unrelated
 // DECODER-SUPPRESS-O2-LABEL: DecoderTable64
-// DECODER-SUPPRESS-O2-NOT: Opcode: bar
-// DECODER-SUPPRESS-O2-DAG: Opcode: fooTypeEncDefault:foo
+// DECODER-SUPPRESS-O2-NOT: decode to bar
+// DECODER-SUPPRESS-O2-DAG: decode to fooTypeEncDefault:foo
 
 // For 'bar' and 'unrelated', we didn't assign any HwModes for them,
 // they should keep the same in the following four tables.

>From 2d3301c8d185036e70adb481175b20892670e28a Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <barannikov88 at gmail.com>
Date: Wed, 17 Sep 2025 22:36:40 +0300
Subject: [PATCH 03/21] Address review comments

* correct variable name
* remove redundant braces
* document integer literals in computeNodeSize
* add a common base class for CheckAllNode and CheckAnyNode
---
 llvm/utils/TableGen/DecoderEmitter.cpp | 76 +++++++++++---------------
 1 file changed, 31 insertions(+), 45 deletions(-)

diff --git a/llvm/utils/TableGen/DecoderEmitter.cpp b/llvm/utils/TableGen/DecoderEmitter.cpp
index 85ab92eb3644f..53db88ceeb099 100644
--- a/llvm/utils/TableGen/DecoderEmitter.cpp
+++ b/llvm/utils/TableGen/DecoderEmitter.cpp
@@ -1236,7 +1236,7 @@ class DecoderTreeNode {
   KindTy Kind;
 };
 
-class CheckAnyNode : public DecoderTreeNode {
+class CheckManyNode : public DecoderTreeNode {
   SmallVector<std::unique_ptr<DecoderTreeNode>, 0> Children;
 
   static const DecoderTreeNode *
@@ -1244,11 +1244,12 @@ class CheckAnyNode : public DecoderTreeNode {
     return Element.get();
   }
 
-public:
-  CheckAnyNode() : DecoderTreeNode(CheckAny) {}
+protected:
+  explicit CheckManyNode(KindTy Kind) : DecoderTreeNode(Kind) {}
 
-  void addChild(std::unique_ptr<DecoderTreeNode> N) {
-    Children.push_back(std::move(N));
+public:
+  void addChild(std::unique_ptr<DecoderTreeNode> Child) {
+    Children.push_back(std::move(Child));
   }
 
   using child_iterator = mapped_iterator<decltype(Children)::const_iterator,
@@ -1267,35 +1268,14 @@ class CheckAnyNode : public DecoderTreeNode {
   }
 };
 
-class CheckAllNode : public DecoderTreeNode {
-  SmallVector<std::unique_ptr<DecoderTreeNode>, 0> Children;
-
-  static const DecoderTreeNode *
-  mapElement(decltype(Children)::const_reference Element) {
-    return Element.get();
-  }
-
+class CheckAnyNode : public CheckManyNode {
 public:
-  CheckAllNode() : DecoderTreeNode(CheckAll) {}
-
-  void addChild(std::unique_ptr<DecoderTreeNode> Child) {
-    Children.push_back(std::move(Child));
-  }
-
-  using child_iterator = mapped_iterator<decltype(Children)::const_iterator,
-                                         decltype(&mapElement)>;
-
-  child_iterator child_begin() const {
-    return child_iterator(Children.begin(), mapElement);
-  }
-
-  child_iterator child_end() const {
-    return child_iterator(Children.end(), mapElement);
-  }
+  CheckAnyNode() : CheckManyNode(CheckAny) {}
+};
 
-  iterator_range<child_iterator> children() const {
-    return make_range(child_begin(), child_end());
-  }
+class CheckAllNode : public CheckManyNode {
+public:
+  CheckAllNode() : CheckManyNode(CheckAll) {}
 };
 
 class CheckFieldNode : public DecoderTreeNode {
@@ -1480,8 +1460,8 @@ DecoderTreeBuilder::buildTerminalNode(unsigned EncodingID,
                                                NegativeMask.getZExtValue()));
   }
 
-  std::string DecoderIndex = getDecoderString(Encoding);
-  N->addChild(std::make_unique<DecodeNode>(Encoding, DecoderIndex));
+  std::string DecoderString = getDecoderString(Encoding);
+  N->addChild(std::make_unique<DecodeNode>(Encoding, std::move(DecoderString)));
 
   return N;
 }
@@ -1505,15 +1485,15 @@ std::unique_ptr<DecoderTreeNode> DecoderTreeBuilder::buildCheckAllOrSwitchNode(
 std::unique_ptr<DecoderTreeNode>
 DecoderTreeBuilder::buildCheckAnyNode(const FilterChooser &FC) {
   auto N = std::make_unique<CheckAnyNode>();
-  if (FC.SingletonEncodingID) {
+
+  if (FC.SingletonEncodingID)
     N->addChild(buildTerminalNode(*FC.SingletonEncodingID, FC.FilterBits));
-  } else {
+  else
     N->addChild(buildCheckAllOrSwitchNode(FC.StartBit, FC.NumBits,
                                           FC.FilterChooserMap));
-  }
-  if (FC.VariableFC) {
+
+  if (FC.VariableFC)
     N->addChild(buildCheckAnyNode(*FC.VariableFC));
-  }
 
   return N;
 }
@@ -1557,13 +1537,17 @@ void DecoderTableEmitter::analyzeNode(const DecoderTreeNode *Node) const {
 
 unsigned
 DecoderTableEmitter::computeNodeSize(const DecoderTreeNode *Node) const {
+  // To make the arithmetic below clearer.
+  static constexpr unsigned OpcodeSize = 1;
+  static constexpr unsigned FieldWidthSize = 1;
+
   switch (Node->getKind()) {
   case DecoderTreeNode::CheckAny: {
     const auto *N = static_cast<const CheckAnyNode *>(Node);
     unsigned Size = 0;
     for (const DecoderTreeNode *Child : drop_end(N->children())) {
       unsigned ChildSize = computeNodeSize(Child);
-      Size += 1 + getULEB128Size(ChildSize) + ChildSize;
+      Size += OpcodeSize + getULEB128Size(ChildSize) + ChildSize;
     }
     return Size + computeNodeSize(*std::prev(N->child_end()));
   }
@@ -1576,12 +1560,13 @@ DecoderTableEmitter::computeNodeSize(const DecoderTreeNode *Node) const {
   }
   case DecoderTreeNode::CheckField: {
     const auto *N = static_cast<const CheckFieldNode *>(Node);
-    return 1 + getULEB128Size(N->getStartBit()) + 1 +
+    return OpcodeSize + getULEB128Size(N->getStartBit()) + FieldWidthSize +
            getULEB128Size(N->getValue());
   }
   case DecoderTreeNode::SwitchField: {
     const auto *N = static_cast<const SwitchFieldNode *>(Node);
-    unsigned Size = 1 + getULEB128Size(N->getStartBit()) + 1;
+    unsigned Size =
+        OpcodeSize + getULEB128Size(N->getStartBit()) + FieldWidthSize;
 
     for (auto [Val, Child] : drop_end(N->cases())) {
       unsigned ChildSize = computeNodeSize(Child);
@@ -1597,18 +1582,19 @@ DecoderTableEmitter::computeNodeSize(const DecoderTreeNode *Node) const {
     const auto *N = static_cast<const CheckPredicateNode *>(Node);
     unsigned PredicateIndex =
         TableInfo.getPredicateIndex(N->getPredicateString());
-    return 1 + getULEB128Size(PredicateIndex);
+    return OpcodeSize + getULEB128Size(PredicateIndex);
   }
   case DecoderTreeNode::SoftFail: {
     const auto *N = static_cast<const SoftFailNode *>(Node);
-    return 1 + getULEB128Size(N->getPositiveMask()) +
+    return OpcodeSize + getULEB128Size(N->getPositiveMask()) +
            getULEB128Size(N->getNegativeMask());
   }
   case DecoderTreeNode::Decode: {
     const auto *N = static_cast<const DecodeNode *>(Node);
     unsigned InstOpcode = N->getEncoding().getInstruction()->EnumVal;
     unsigned DecoderIndex = TableInfo.getDecoderIndex(N->getDecoderString());
-    return 1 + getULEB128Size(InstOpcode) + getULEB128Size(DecoderIndex);
+    return OpcodeSize + getULEB128Size(InstOpcode) +
+           getULEB128Size(DecoderIndex);
   }
   }
   llvm_unreachable("Unknown node kind");

>From 61a26b22e4a8a0b3241e5be3913822c688acca26 Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <barannikov88 at gmail.com>
Date: Wed, 17 Sep 2025 23:13:00 +0300
Subject: [PATCH 04/21] Rename DecoderTreeBuilder methods

The former names were hints for myself, give the methods more
appropriate names now.
---
 llvm/utils/TableGen/DecoderEmitter.cpp | 29 +++++++++++++-------------
 1 file changed, 15 insertions(+), 14 deletions(-)

diff --git a/llvm/utils/TableGen/DecoderEmitter.cpp b/llvm/utils/TableGen/DecoderEmitter.cpp
index 53db88ceeb099..bc00322758a1f 100644
--- a/llvm/utils/TableGen/DecoderEmitter.cpp
+++ b/llvm/utils/TableGen/DecoderEmitter.cpp
@@ -1380,18 +1380,19 @@ class DecoderTreeBuilder {
       : Target(Target), Encodings(Encodings) {}
 
   std::unique_ptr<DecoderTreeNode> buildTree(const FilterChooser &FC) {
-    return buildCheckAnyNode(FC);
+    return convertFilterChooser(FC);
   }
 
 private:
   std::unique_ptr<DecoderTreeNode>
-  buildTerminalNode(unsigned EncodingID, const KnownBits &FilterBits);
+  convertSingleton(unsigned EncodingID, const KnownBits &FilterBits);
 
-  std::unique_ptr<DecoderTreeNode> buildCheckAllOrSwitchNode(
+  std::unique_ptr<DecoderTreeNode> convertFilterChooserMap(
       unsigned StartBit, unsigned NumBits,
       const std::map<uint64_t, std::unique_ptr<const FilterChooser>> &FCMap);
 
-  std::unique_ptr<DecoderTreeNode> buildCheckAnyNode(const FilterChooser &FC);
+  std::unique_ptr<DecoderTreeNode>
+  convertFilterChooser(const FilterChooser &FC);
 };
 
 class DecoderTableEmitter {
@@ -1435,8 +1436,8 @@ class DecoderTableEmitter {
 } // namespace
 
 std::unique_ptr<DecoderTreeNode>
-DecoderTreeBuilder::buildTerminalNode(unsigned EncodingID,
-                                      const KnownBits &FilterBits) {
+DecoderTreeBuilder::convertSingleton(unsigned EncodingID,
+                                     const KnownBits &FilterBits) {
   const InstructionEncoding &Encoding = Encodings[EncodingID];
   auto N = std::make_unique<CheckAllNode>();
 
@@ -1466,34 +1467,34 @@ DecoderTreeBuilder::buildTerminalNode(unsigned EncodingID,
   return N;
 }
 
-std::unique_ptr<DecoderTreeNode> DecoderTreeBuilder::buildCheckAllOrSwitchNode(
+std::unique_ptr<DecoderTreeNode> DecoderTreeBuilder::convertFilterChooserMap(
     unsigned StartBit, unsigned NumBits,
     const std::map<uint64_t, std::unique_ptr<const FilterChooser>> &FCMap) {
   if (FCMap.size() == 1) {
     const auto &[FieldVal, ChildFC] = *FCMap.begin();
     auto N = std::make_unique<CheckAllNode>();
     N->addChild(std::make_unique<CheckFieldNode>(StartBit, NumBits, FieldVal));
-    N->addChild(buildCheckAnyNode(*ChildFC));
+    N->addChild(convertFilterChooser(*ChildFC));
     return N;
   }
   auto N = std::make_unique<SwitchFieldNode>(StartBit, NumBits);
   for (const auto &[FieldVal, ChildFC] : FCMap)
-    N->addCase(FieldVal, buildCheckAnyNode(*ChildFC));
+    N->addCase(FieldVal, convertFilterChooser(*ChildFC));
   return N;
 }
 
 std::unique_ptr<DecoderTreeNode>
-DecoderTreeBuilder::buildCheckAnyNode(const FilterChooser &FC) {
+DecoderTreeBuilder::convertFilterChooser(const FilterChooser &FC) {
   auto N = std::make_unique<CheckAnyNode>();
 
   if (FC.SingletonEncodingID)
-    N->addChild(buildTerminalNode(*FC.SingletonEncodingID, FC.FilterBits));
+    N->addChild(convertSingleton(*FC.SingletonEncodingID, FC.FilterBits));
   else
-    N->addChild(buildCheckAllOrSwitchNode(FC.StartBit, FC.NumBits,
-                                          FC.FilterChooserMap));
+    N->addChild(
+        convertFilterChooserMap(FC.StartBit, FC.NumBits, FC.FilterChooserMap));
 
   if (FC.VariableFC)
-    N->addChild(buildCheckAnyNode(*FC.VariableFC));
+    N->addChild(convertFilterChooser(*FC.VariableFC));
 
   return N;
 }

>From 8352f03d3604a0d0fc8ca25c274281b4bad7ffee Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <barannikov88 at gmail.com>
Date: Thu, 18 Sep 2025 00:16:56 +0300
Subject: [PATCH 05/21] Clarify size computation of CheckAny node

---
 llvm/utils/TableGen/DecoderEmitter.cpp | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/llvm/utils/TableGen/DecoderEmitter.cpp b/llvm/utils/TableGen/DecoderEmitter.cpp
index bc00322758a1f..27afbdae2f518 100644
--- a/llvm/utils/TableGen/DecoderEmitter.cpp
+++ b/llvm/utils/TableGen/DecoderEmitter.cpp
@@ -1546,11 +1546,14 @@ DecoderTableEmitter::computeNodeSize(const DecoderTreeNode *Node) const {
   case DecoderTreeNode::CheckAny: {
     const auto *N = static_cast<const CheckAnyNode *>(Node);
     unsigned Size = 0;
+    // All children except the last one are preceded by OPC_Scope opcode and
+    // the size of the child.
     for (const DecoderTreeNode *Child : drop_end(N->children())) {
       unsigned ChildSize = computeNodeSize(Child);
       Size += OpcodeSize + getULEB128Size(ChildSize) + ChildSize;
     }
-    return Size + computeNodeSize(*std::prev(N->child_end()));
+    const DecoderTreeNode *Child = *std::prev(N->child_end());
+    return Size + computeNodeSize(Child);
   }
   case DecoderTreeNode::CheckAll: {
     const auto *N = static_cast<const CheckAllNode *>(Node);

>From cebaaf86c1b9a91f206ac1b3c65d736640ff6e39 Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <barannikov88 at gmail.com>
Date: Thu, 18 Sep 2025 01:10:40 +0300
Subject: [PATCH 06/21] Avoid recursion when converting FilterChooser

Recursively converting FilterChooser results in "chained" CheckAny
nodes: the last child of each CheckAny node is another CheckAny node,
and each of them has no more than two children.

Avoid recursion and create single CheckAny node with multiple children.

There is no difference in output because the last child of CheckAny
node is printed unindented.
---
 llvm/utils/TableGen/DecoderEmitter.cpp | 26 +++++++++++++-------------
 1 file changed, 13 insertions(+), 13 deletions(-)

diff --git a/llvm/utils/TableGen/DecoderEmitter.cpp b/llvm/utils/TableGen/DecoderEmitter.cpp
index 27afbdae2f518..2a527691163a1 100644
--- a/llvm/utils/TableGen/DecoderEmitter.cpp
+++ b/llvm/utils/TableGen/DecoderEmitter.cpp
@@ -1380,7 +1380,7 @@ class DecoderTreeBuilder {
       : Target(Target), Encodings(Encodings) {}
 
   std::unique_ptr<DecoderTreeNode> buildTree(const FilterChooser &FC) {
-    return convertFilterChooser(FC);
+    return convertFilterChooser(&FC);
   }
 
 private:
@@ -1392,7 +1392,7 @@ class DecoderTreeBuilder {
       const std::map<uint64_t, std::unique_ptr<const FilterChooser>> &FCMap);
 
   std::unique_ptr<DecoderTreeNode>
-  convertFilterChooser(const FilterChooser &FC);
+  convertFilterChooser(const FilterChooser *FC);
 };
 
 class DecoderTableEmitter {
@@ -1474,27 +1474,27 @@ std::unique_ptr<DecoderTreeNode> DecoderTreeBuilder::convertFilterChooserMap(
     const auto &[FieldVal, ChildFC] = *FCMap.begin();
     auto N = std::make_unique<CheckAllNode>();
     N->addChild(std::make_unique<CheckFieldNode>(StartBit, NumBits, FieldVal));
-    N->addChild(convertFilterChooser(*ChildFC));
+    N->addChild(convertFilterChooser(ChildFC.get()));
     return N;
   }
   auto N = std::make_unique<SwitchFieldNode>(StartBit, NumBits);
   for (const auto &[FieldVal, ChildFC] : FCMap)
-    N->addCase(FieldVal, convertFilterChooser(*ChildFC));
+    N->addCase(FieldVal, convertFilterChooser(ChildFC.get()));
   return N;
 }
 
 std::unique_ptr<DecoderTreeNode>
-DecoderTreeBuilder::convertFilterChooser(const FilterChooser &FC) {
+DecoderTreeBuilder::convertFilterChooser(const FilterChooser *FC) {
   auto N = std::make_unique<CheckAnyNode>();
 
-  if (FC.SingletonEncodingID)
-    N->addChild(convertSingleton(*FC.SingletonEncodingID, FC.FilterBits));
-  else
-    N->addChild(
-        convertFilterChooserMap(FC.StartBit, FC.NumBits, FC.FilterChooserMap));
-
-  if (FC.VariableFC)
-    N->addChild(convertFilterChooser(*FC.VariableFC));
+  do {
+    if (FC->SingletonEncodingID)
+      N->addChild(convertSingleton(*FC->SingletonEncodingID, FC->FilterBits));
+    else
+      N->addChild(convertFilterChooserMap(FC->StartBit, FC->NumBits,
+                                          FC->FilterChooserMap));
+    FC = FC->VariableFC.get();
+  } while (FC);
 
   return N;
 }

>From 9836fb5f1397400339a332b540eb2cfb32b608ca Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <barannikov88 at gmail.com>
Date: Thu, 18 Sep 2025 01:54:03 +0300
Subject: [PATCH 07/21] Print the last child of CheckNode indented

It should be indented like other children, because otherwise the output
looks as if it was a sibling, and those have different semantics.

Add a workaround for single-child CheckAny node not currently being
optimized. This compensates extra nesting caused by the above change.
---
 .../DecoderEmitter/additional-encoding.td        | 16 ++++++++++++----
 .../DecoderEmitter/trydecode-emission.td         |  4 +++-
 .../DecoderEmitter/trydecode-emission2.td        |  6 ++++--
 .../DecoderEmitter/trydecode-emission3.td        |  4 +++-
 .../DecoderEmitter/trydecode-emission4.td        |  4 +++-
 llvm/utils/TableGen/DecoderEmitter.cpp           | 14 +++++++++++++-
 6 files changed, 38 insertions(+), 10 deletions(-)

diff --git a/llvm/test/TableGen/DecoderEmitter/additional-encoding.td b/llvm/test/TableGen/DecoderEmitter/additional-encoding.td
index 0869a5ca1a46d..b0d8a583af7b7 100644
--- a/llvm/test/TableGen/DecoderEmitter/additional-encoding.td
+++ b/llvm/test/TableGen/DecoderEmitter/additional-encoding.td
@@ -37,28 +37,36 @@ class I<dag out_ops, dag in_ops> : Instruction {
 // CHECK-NEXT:    OPC_CheckField, 6, 6, 0,     //  7:   check Inst[11:6] == 0x0
 // CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 0, // 11:   decode to NOP using decoder 0
 // CHECK-NEXT:                                 // 11:  }
-// CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 1, // 15:  decode to SHIFT0 using decoder 1
+// CHECK-NEXT:                                 // 11:  {
+// CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 1, // 15:   decode to SHIFT0 using decoder 1
+// CHECK-NEXT:                                 // 15:  }
 // CHECK-NEXT:                                 // 15: }
 // CHECK-NEXT:    1, 14,                       // 19: case 0x1: {
 // CHECK-NEXT:    OPC_Scope, 8,                // 21:  {
 // CHECK-NEXT:    OPC_CheckField, 6, 6, 0,     // 23:   check Inst[11:6] == 0x0
 // CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 0, // 27:   decode to {{.*}}:NOP using decoder 0
 // CHECK-NEXT:                                 // 27:  }
-// CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 1, // 31:  decode to SHIFT1 using decoder 1
+// CHECK-NEXT:                                 // 27:  {
+// CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 1, // 31:   decode to SHIFT1 using decoder 1
+// CHECK-NEXT:                                 // 31:  }
 // CHECK-NEXT:                                 // 31: }
 // CHECK-NEXT:    2, 14,                       // 35: case 0x2: {
 // CHECK-NEXT:    OPC_Scope, 8,                // 37:  {
 // CHECK-NEXT:    OPC_CheckField, 6, 6, 0,     // 39:   check Inst[11:6] == 0x0
 // CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 0, // 43:   decode to {{.*}}:NOP using decoder 0
 // CHECK-NEXT:                                 // 43:  }
-// CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 1, // 47:  decode to SHIFT2 using decoder 1
+// CHECK-NEXT:                                 // 43:  {
+// CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 1, // 47:   decode to SHIFT2 using decoder 1
+// CHECK-NEXT:                                 // 47:  }
 // CHECK-NEXT:                                 // 47: }
 // CHECK-NEXT:    3, 0,                        // 51: case 0x3: {
 // CHECK-NEXT:    OPC_Scope, 8,                // 53:  {
 // CHECK-NEXT:    OPC_CheckField, 6, 6, 0,     // 55:   check Inst[11:6] == 0x0
 // CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 0, // 59:   decode to {{.*}}:NOP using decoder 0
 // CHECK-NEXT:                                 // 59:  }
-// CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 1, // 63:  decode to SHIFT3 using decoder 1
+// CHECK-NEXT:                                 // 59:  {
+// CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 1, // 63:   decode to SHIFT3 using decoder 1
+// CHECK-NEXT:                                 // 63:  }
 // CHECK-NEXT:                                 // 63: }
 // CHECK-NEXT:                                 // 63: } // switch Inst[15:12]
 // CHECK-NEXT:  };
diff --git a/llvm/test/TableGen/DecoderEmitter/trydecode-emission.td b/llvm/test/TableGen/DecoderEmitter/trydecode-emission.td
index d6b1557d94bf4..34d494fb522ed 100644
--- a/llvm/test/TableGen/DecoderEmitter/trydecode-emission.td
+++ b/llvm/test/TableGen/DecoderEmitter/trydecode-emission.td
@@ -39,7 +39,9 @@ def InstB : TestInstruction {
 // CHECK-NEXT:    OPC_CheckField, 2, 2, 0,     //  6:  check Inst[3:2] == 0x0
 // CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 0, // 10:  decode to InstB using decoder 0
 // CHECK-NEXT:                                 // 10: }
-// CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 1, // 14: decode to InstA using decoder 1
+// CHECK-NEXT:                                 // 10: {
+// CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 1, // 14:  decode to InstA using decoder 1
+// CHECK-NEXT:                                 // 14: }
 // CHECK-NEXT: };
 
 // CHECK: if (!Check(S, DecodeInstB(MI, insn, Address, Decoder))) { DecodeComplete = false; return MCDisassembler::Fail; }
diff --git a/llvm/test/TableGen/DecoderEmitter/trydecode-emission2.td b/llvm/test/TableGen/DecoderEmitter/trydecode-emission2.td
index 896e62c08e4f5..50477543bafd0 100644
--- a/llvm/test/TableGen/DecoderEmitter/trydecode-emission2.td
+++ b/llvm/test/TableGen/DecoderEmitter/trydecode-emission2.td
@@ -37,8 +37,10 @@ def InstB : TestInstruction {
 // CHECK-NEXT:    OPC_CheckField, 0, 2, 3,     // 10:  check Inst[1:0] == 0x3
 // CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 0, // 14:  decode to InstB using decoder 0
 // CHECK-NEXT:                                 // 14: }
-// CHECK-NEXT:    OPC_CheckField, 3, 2, 0,     // 18: check Inst[4:3] == 0x0
-// CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 1, // 22: decode to InstA using decoder 1
+// CHECK-NEXT:                                 // 14: {
+// CHECK-NEXT:    OPC_CheckField, 3, 2, 0,     // 18:  check Inst[4:3] == 0x0
+// CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 1, // 22:  decode to InstA using decoder 1
+// CHECK-NEXT:                                 // 22: }
 // CHECK-NEXT: };
 
 // CHECK: if (!Check(S, DecodeInstB(MI, insn, Address, Decoder))) { DecodeComplete = false; return MCDisassembler::Fail; }
diff --git a/llvm/test/TableGen/DecoderEmitter/trydecode-emission3.td b/llvm/test/TableGen/DecoderEmitter/trydecode-emission3.td
index 1ff2b6072891c..1c83b6567a2ee 100644
--- a/llvm/test/TableGen/DecoderEmitter/trydecode-emission3.td
+++ b/llvm/test/TableGen/DecoderEmitter/trydecode-emission3.td
@@ -40,7 +40,9 @@ def InstB : TestInstruction {
 // CHECK-NEXT:    OPC_CheckField, 2, 2, 0,     //  6:  check Inst[3:2] == 0x0
 // CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 0, // 10:  decode to InstB using decoder 0
 // CHECK-NEXT:                                 // 10: }
-// CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 1, // 14: decode to InstA using decoder 1
+// CHECK-NEXT:                                 // 10: {
+// CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 1, // 14:  decode to InstA using decoder 1
+// CHECK-NEXT:                                 // 14: }
 // CHECK-NEXT: };
 
 // CHECK: if (!Check(S, DecodeInstBOp(MI, tmp, Address, Decoder))) { DecodeComplete = false; return MCDisassembler::Fail; }
diff --git a/llvm/test/TableGen/DecoderEmitter/trydecode-emission4.td b/llvm/test/TableGen/DecoderEmitter/trydecode-emission4.td
index 6bfedc071f115..7a93556be55d6 100644
--- a/llvm/test/TableGen/DecoderEmitter/trydecode-emission4.td
+++ b/llvm/test/TableGen/DecoderEmitter/trydecode-emission4.td
@@ -38,7 +38,9 @@ def InstB : TestInstruction {
 // CHECK-NEXT:    OPC_CheckField, 248, 3, 2, 0, //  7:  check Inst[505:504] == 0x0
 // CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 0,  // 12:  decode to InstB using decoder 0
 // CHECK-NEXT:                                  // 12: }
-// CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 1,  // 16: decode to InstA using decoder 1
+// CHECK-NEXT:                                  // 12: {
+// CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 1,  // 16:  decode to InstA using decoder 1
+// CHECK-NEXT:                                  // 16: }
 // CHECK-NEXT: };
 
 // CHECK: if (!Check(S, DecodeInstB(MI, insn, Address, Decoder))) { DecodeComplete = false; return MCDisassembler::Fail; }
diff --git a/llvm/utils/TableGen/DecoderEmitter.cpp b/llvm/utils/TableGen/DecoderEmitter.cpp
index 2a527691163a1..4830974b451ee 100644
--- a/llvm/utils/TableGen/DecoderEmitter.cpp
+++ b/llvm/utils/TableGen/DecoderEmitter.cpp
@@ -1545,6 +1545,9 @@ DecoderTableEmitter::computeNodeSize(const DecoderTreeNode *Node) const {
   switch (Node->getKind()) {
   case DecoderTreeNode::CheckAny: {
     const auto *N = static_cast<const CheckAnyNode *>(Node);
+    // Pretend the node was optimized. See the comment in emitCheckAnyNode.
+    if (range_size(N->children()) == 1)
+      return computeNodeSize(*N->child_begin());
     unsigned Size = 0;
     // All children except the last one are preceded by OPC_Scope opcode and
     // the size of the child.
@@ -1653,6 +1656,13 @@ formatted_raw_ostream &DecoderTableEmitter::emitComment(indent Indent) {
 
 void DecoderTableEmitter::emitCheckAnyNode(const CheckAnyNode *N,
                                            indent Indent) {
+  // TODO: Single-child CheckAny node should be optimized out. For now,
+  //   pretend this is the case and print the single child unindented.
+  if (range_size(N->children()) == 1) {
+    emitNode(*N->child_begin(), Indent);
+    return;
+  }
+
   for (const DecoderTreeNode *Child : drop_end(N->children())) {
     emitOpcode("OPC_Scope");
     emitULEB128(computeNodeSize(Child));
@@ -1663,7 +1673,9 @@ void DecoderTableEmitter::emitCheckAnyNode(const CheckAnyNode *N,
   }
 
   const DecoderTreeNode *Child = *std::prev(N->child_end());
-  emitNode(Child, Indent);
+  emitComment(Indent) << "{\n";
+  emitNode(Child, Indent + 1);
+  emitComment(Indent) << "}\n";
 }
 
 void DecoderTableEmitter::emitCheckAllNode(const CheckAllNode *N,

>From bc4b239517fcdc7a6a914bd2b630c2befbc97fd8 Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <barannikov88 at gmail.com>
Date: Thu, 18 Sep 2025 02:39:46 +0300
Subject: [PATCH 08/21] Add a TODO with potential CheckAny optimization

---
 llvm/utils/TableGen/DecoderEmitter.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/llvm/utils/TableGen/DecoderEmitter.cpp b/llvm/utils/TableGen/DecoderEmitter.cpp
index 4830974b451ee..b2597c7523a6f 100644
--- a/llvm/utils/TableGen/DecoderEmitter.cpp
+++ b/llvm/utils/TableGen/DecoderEmitter.cpp
@@ -1663,6 +1663,11 @@ void DecoderTableEmitter::emitCheckAnyNode(const CheckAnyNode *N,
     return;
   }
 
+  // TODO: Emit single OPC_Scope and check at runtime where the scope ends.
+  //   Not that it would save much space: OPC_Scope means we've resolved
+  //   a conflict, and there aren't many of them. AMDGPU is an exception,
+  //   it has ~3.5K OPC_Scope opcodes, but most, if not all scopes contain
+  //   only two children.
   for (const DecoderTreeNode *Child : drop_end(N->children())) {
     emitOpcode("OPC_Scope");
     emitULEB128(computeNodeSize(Child));

>From 547a6aab63ccb4c7b7815a8ff900f1a31d16af9c Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <barannikov88 at gmail.com>
Date: Thu, 18 Sep 2025 03:38:08 +0300
Subject: [PATCH 09/21] Revert "Add a TODO with potential CheckAny
 optimization"

This reverts commit 2d49938c2a8be4ed10f2392d01cc0665c24a776a.

Implemented suggestion results in increased table sizes on average.
The number of elements in scopes is too small for the suggestion to be
beneficial.
---
 llvm/utils/TableGen/DecoderEmitter.cpp | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/llvm/utils/TableGen/DecoderEmitter.cpp b/llvm/utils/TableGen/DecoderEmitter.cpp
index b2597c7523a6f..4830974b451ee 100644
--- a/llvm/utils/TableGen/DecoderEmitter.cpp
+++ b/llvm/utils/TableGen/DecoderEmitter.cpp
@@ -1663,11 +1663,6 @@ void DecoderTableEmitter::emitCheckAnyNode(const CheckAnyNode *N,
     return;
   }
 
-  // TODO: Emit single OPC_Scope and check at runtime where the scope ends.
-  //   Not that it would save much space: OPC_Scope means we've resolved
-  //   a conflict, and there aren't many of them. AMDGPU is an exception,
-  //   it has ~3.5K OPC_Scope opcodes, but most, if not all scopes contain
-  //   only two children.
   for (const DecoderTreeNode *Child : drop_end(N->children())) {
     emitOpcode("OPC_Scope");
     emitULEB128(computeNodeSize(Child));

>From 1722ed093b9cea45bf3ffbd5edb4eddb239e4cb4 Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <barannikov88 at gmail.com>
Date: Thu, 18 Sep 2025 15:42:59 +0300
Subject: [PATCH 10/21] Extract DecoderTreeNode/DecoderTableEmitter into
 separate h/cpp files

Hopefully, logical separation simplifies the review.
I was going to do this in the next patch anyway.
---
 llvm/utils/TableGen/CMakeLists.txt          |   2 +
 llvm/utils/TableGen/DecoderEmitter.cpp      | 562 +-------------------
 llvm/utils/TableGen/DecoderTableEmitter.cpp | 342 ++++++++++++
 llvm/utils/TableGen/DecoderTableEmitter.h   |  89 ++++
 llvm/utils/TableGen/DecoderTree.cpp         |  14 +
 llvm/utils/TableGen/DecoderTree.h           | 181 +++++++
 6 files changed, 632 insertions(+), 558 deletions(-)
 create mode 100644 llvm/utils/TableGen/DecoderTableEmitter.cpp
 create mode 100644 llvm/utils/TableGen/DecoderTableEmitter.h
 create mode 100644 llvm/utils/TableGen/DecoderTree.cpp
 create mode 100644 llvm/utils/TableGen/DecoderTree.h

diff --git a/llvm/utils/TableGen/CMakeLists.txt b/llvm/utils/TableGen/CMakeLists.txt
index 67291214c14e6..e4784faaa2671 100644
--- a/llvm/utils/TableGen/CMakeLists.txt
+++ b/llvm/utils/TableGen/CMakeLists.txt
@@ -43,6 +43,8 @@ add_tablegen(llvm-tblgen LLVM
   DAGISelMatcherGen.cpp
   DAGISelMatcherOpt.cpp
   DecoderEmitter.cpp
+  DecoderTableEmitter.cpp
+  DecoderTree.cpp
   DFAEmitter.cpp
   DFAPacketizerEmitter.cpp
   DisassemblerEmitter.cpp
diff --git a/llvm/utils/TableGen/DecoderEmitter.cpp b/llvm/utils/TableGen/DecoderEmitter.cpp
index 4830974b451ee..6f6c95e593748 100644
--- a/llvm/utils/TableGen/DecoderEmitter.cpp
+++ b/llvm/utils/TableGen/DecoderEmitter.cpp
@@ -18,14 +18,14 @@
 #include "Common/InstructionEncoding.h"
 #include "Common/SubtargetFeatureInfo.h"
 #include "Common/VarLenCodeEmitterGen.h"
+#include "DecoderTableEmitter.h"
+#include "DecoderTree.h"
 #include "TableGenBackends.h"
 #include "llvm/ADT/APInt.h"
 #include "llvm/ADT/ArrayRef.h"
-#include "llvm/ADT/CachedHashString.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SetVector.h"
 #include "llvm/ADT/SmallBitVector.h"
-#include "llvm/ADT/SmallSet.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/Statistic.h"
 #include "llvm/ADT/StringExtras.h"
@@ -38,7 +38,6 @@
 #include "llvm/Support/FormatVariadic.h"
 #include "llvm/Support/FormattedStream.h"
 #include "llvm/Support/KnownBits.h"
-#include "llvm/Support/LEB128.h"
 #include "llvm/Support/MathExtras.h"
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/TableGen/Error.h"
@@ -150,40 +149,8 @@ class LessEncodingIDByWidth {
   }
 };
 
-using PredicateSet = SetVector<CachedHashString>;
-using DecoderSet = SetVector<CachedHashString>;
-
-struct DecoderTableInfo {
-  PredicateSet Predicates;
-  DecoderSet Decoders;
-  bool HasCheckPredicate;
-  bool HasSoftFail;
-
-  void insertPredicate(StringRef Predicate) {
-    Predicates.insert(CachedHashString(Predicate));
-  }
-
-  void insertDecoder(StringRef Decoder) {
-    Decoders.insert(CachedHashString(Decoder));
-  }
-
-  unsigned getPredicateIndex(StringRef Predicate) const {
-    auto I = find(Predicates, Predicate);
-    assert(I != Predicates.end());
-    return std::distance(Predicates.begin(), I);
-  }
-
-  unsigned getDecoderIndex(StringRef Decoder) const {
-    auto I = find(Decoders, Decoder);
-    assert(I != Decoders.end());
-    return std::distance(Decoders.begin(), I);
-  }
-};
-
 using NamespacesHwModesMap = std::map<StringRef, std::set<unsigned>>;
 
-class DecoderTreeNode;
-
 class DecoderEmitter {
   const RecordKeeper &RK;
   CodeGenTarget Target;
@@ -1213,163 +1180,6 @@ static DecodeStatus decodeInstruction(const uint8_t DecodeTable[], MCInst &MI,
 
 namespace {
 
-class DecoderTreeNode {
-public:
-  virtual ~DecoderTreeNode() = default;
-
-  enum KindTy {
-    CheckAny,
-    CheckAll,
-    CheckField,
-    SwitchField,
-    CheckPredicate,
-    SoftFail,
-    Decode,
-  };
-
-  KindTy getKind() const { return Kind; }
-
-protected:
-  explicit DecoderTreeNode(KindTy Kind) : Kind(Kind) {}
-
-private:
-  KindTy Kind;
-};
-
-class CheckManyNode : public DecoderTreeNode {
-  SmallVector<std::unique_ptr<DecoderTreeNode>, 0> Children;
-
-  static const DecoderTreeNode *
-  mapElement(decltype(Children)::const_reference Element) {
-    return Element.get();
-  }
-
-protected:
-  explicit CheckManyNode(KindTy Kind) : DecoderTreeNode(Kind) {}
-
-public:
-  void addChild(std::unique_ptr<DecoderTreeNode> Child) {
-    Children.push_back(std::move(Child));
-  }
-
-  using child_iterator = mapped_iterator<decltype(Children)::const_iterator,
-                                         decltype(&mapElement)>;
-
-  child_iterator child_begin() const {
-    return child_iterator(Children.begin(), mapElement);
-  }
-
-  child_iterator child_end() const {
-    return child_iterator(Children.end(), mapElement);
-  }
-
-  iterator_range<child_iterator> children() const {
-    return make_range(child_begin(), child_end());
-  }
-};
-
-class CheckAnyNode : public CheckManyNode {
-public:
-  CheckAnyNode() : CheckManyNode(CheckAny) {}
-};
-
-class CheckAllNode : public CheckManyNode {
-public:
-  CheckAllNode() : CheckManyNode(CheckAll) {}
-};
-
-class CheckFieldNode : public DecoderTreeNode {
-  unsigned StartBit;
-  unsigned NumBits;
-  uint64_t Value;
-
-public:
-  CheckFieldNode(unsigned StartBit, unsigned NumBits, uint64_t Value)
-      : DecoderTreeNode(CheckField), StartBit(StartBit), NumBits(NumBits),
-        Value(Value) {}
-
-  unsigned getStartBit() const { return StartBit; }
-
-  unsigned getNumBits() const { return NumBits; }
-
-  uint64_t getValue() const { return Value; }
-};
-
-class SwitchFieldNode : public DecoderTreeNode {
-  unsigned StartBit;
-  unsigned NumBits;
-  std::map<uint64_t, std::unique_ptr<DecoderTreeNode>> Cases;
-
-  static std::pair<uint64_t, const DecoderTreeNode *>
-  mapElement(decltype(Cases)::const_reference Element) {
-    return std::pair(Element.first, Element.second.get());
-  }
-
-public:
-  SwitchFieldNode(unsigned StartBit, unsigned NumBits)
-      : DecoderTreeNode(SwitchField), StartBit(StartBit), NumBits(NumBits) {}
-
-  void addCase(uint64_t Value, std::unique_ptr<DecoderTreeNode> N) {
-    Cases.try_emplace(Value, std::move(N));
-  }
-
-  unsigned getStartBit() const { return StartBit; }
-
-  unsigned getNumBits() const { return NumBits; }
-
-  using case_iterator =
-      mapped_iterator<decltype(Cases)::const_iterator, decltype(&mapElement)>;
-
-  case_iterator case_begin() const {
-    return case_iterator(Cases.begin(), mapElement);
-  }
-
-  case_iterator case_end() const {
-    return case_iterator(Cases.end(), mapElement);
-  }
-
-  iterator_range<case_iterator> cases() const {
-    return make_range(case_begin(), case_end());
-  }
-};
-
-class CheckPredicateNode : public DecoderTreeNode {
-  std::string PredicateString;
-
-public:
-  explicit CheckPredicateNode(std::string PredicateString)
-      : DecoderTreeNode(CheckPredicate),
-        PredicateString(std::move(PredicateString)) {}
-
-  StringRef getPredicateString() const { return PredicateString; }
-};
-
-class SoftFailNode : public DecoderTreeNode {
-  uint64_t PositiveMask, NegativeMask;
-
-public:
-  SoftFailNode(uint64_t PositiveMask, uint64_t NegativeMask)
-      : DecoderTreeNode(SoftFail), PositiveMask(PositiveMask),
-        NegativeMask(NegativeMask) {}
-
-  uint64_t getPositiveMask() const { return PositiveMask; }
-  uint64_t getNegativeMask() const { return NegativeMask; }
-};
-
-class DecodeNode : public DecoderTreeNode {
-  const InstructionEncoding &Encoding;
-  std::string DecoderString;
-
-public:
-  DecodeNode(const InstructionEncoding &Encoding, std::string DecoderString)
-      : DecoderTreeNode(Decode), Encoding(Encoding),
-        DecoderString(std::move(DecoderString)) {}
-
-  const InstructionEncoding &getEncoding() const { return Encoding; }
-
-  StringRef getDecoderString() const { return DecoderString; }
-};
-
 class DecoderTreeBuilder {
   const CodeGenTarget &Target;
   ArrayRef<InstructionEncoding> Encodings;
@@ -1395,44 +1205,6 @@ class DecoderTreeBuilder {
   convertFilterChooser(const FilterChooser *FC);
 };
 
-class DecoderTableEmitter {
-  DecoderTableInfo &TableInfo;
-  formatted_raw_ostream OS;
-  unsigned IndexWidth;
-  unsigned CurrentIndex;
-  unsigned CommentIndex;
-
-public:
-  DecoderTableEmitter(DecoderTableInfo &TableInfo, raw_ostream &OS)
-      : TableInfo(TableInfo), OS(OS) {}
-
-  void emitTable(StringRef TableName, unsigned BitWidth,
-                 const DecoderTreeNode *Root);
-
-private:
-  void analyzeNode(const DecoderTreeNode *Node) const;
-
-  unsigned computeNodeSize(const DecoderTreeNode *Node) const;
-  unsigned computeTableSize(const DecoderTreeNode *Root,
-                            unsigned BitWidth) const;
-
-  void emitStartLine();
-  void emitOpcode(StringRef Name);
-  void emitByte(uint8_t Val);
-  void emitUInt8(unsigned Val);
-  void emitULEB128(uint64_t Val);
-  formatted_raw_ostream &emitComment(indent Indent);
-
-  void emitCheckAnyNode(const CheckAnyNode *N, indent Indent);
-  void emitCheckAllNode(const CheckAllNode *N, indent Indent);
-  void emitSwitchFieldNode(const SwitchFieldNode *N, indent Indent);
-  void emitCheckFieldNode(const CheckFieldNode *N, indent Indent);
-  void emitCheckPredicateNode(const CheckPredicateNode *N, indent Indent);
-  void emitSoftFailNode(const SoftFailNode *N, indent Indent);
-  void emitDecodeNode(const DecodeNode *N, indent Indent);
-  void emitNode(const DecoderTreeNode *N, indent Indent);
-};
-
 } // namespace
 
 std::unique_ptr<DecoderTreeNode>
@@ -1499,333 +1271,6 @@ DecoderTreeBuilder::convertFilterChooser(const FilterChooser *FC) {
   return N;
 }
 
-void DecoderTableEmitter::analyzeNode(const DecoderTreeNode *Node) const {
-  switch (Node->getKind()) {
-  case DecoderTreeNode::CheckAny: {
-    const auto *N = static_cast<const CheckAnyNode *>(Node);
-    for (const DecoderTreeNode *Child : N->children())
-      analyzeNode(Child);
-    break;
-  }
-  case DecoderTreeNode::CheckAll: {
-    const auto *N = static_cast<const CheckAllNode *>(Node);
-    for (const DecoderTreeNode *Child : N->children())
-      analyzeNode(Child);
-    break;
-  }
-  case DecoderTreeNode::CheckField:
-    break;
-  case DecoderTreeNode::SwitchField: {
-    const auto *N = static_cast<const SwitchFieldNode *>(Node);
-    for (const DecoderTreeNode *Child : make_second_range(N->cases()))
-      analyzeNode(Child);
-    break;
-  }
-  case DecoderTreeNode::CheckPredicate: {
-    const auto *N = static_cast<const CheckPredicateNode *>(Node);
-    TableInfo.insertPredicate(N->getPredicateString());
-    break;
-  }
-  case DecoderTreeNode::SoftFail:
-    break;
-  case DecoderTreeNode::Decode: {
-    const auto *N = static_cast<const DecodeNode *>(Node);
-    TableInfo.insertDecoder(N->getDecoderString());
-    break;
-  }
-  }
-}
-
-unsigned
-DecoderTableEmitter::computeNodeSize(const DecoderTreeNode *Node) const {
-  // To make the arithmetic below clearer.
-  static constexpr unsigned OpcodeSize = 1;
-  static constexpr unsigned FieldWidthSize = 1;
-
-  switch (Node->getKind()) {
-  case DecoderTreeNode::CheckAny: {
-    const auto *N = static_cast<const CheckAnyNode *>(Node);
-    // Pretend the node was optimized. See the comment in emitCheckAnyNode.
-    if (range_size(N->children()) == 1)
-      return computeNodeSize(*N->child_begin());
-    unsigned Size = 0;
-    // All children except the last one are preceded by OPC_Scope opcode and
-    // the size of the child.
-    for (const DecoderTreeNode *Child : drop_end(N->children())) {
-      unsigned ChildSize = computeNodeSize(Child);
-      Size += OpcodeSize + getULEB128Size(ChildSize) + ChildSize;
-    }
-    const DecoderTreeNode *Child = *std::prev(N->child_end());
-    return Size + computeNodeSize(Child);
-  }
-  case DecoderTreeNode::CheckAll: {
-    const auto *N = static_cast<const CheckAllNode *>(Node);
-    unsigned Size = 0;
-    for (const DecoderTreeNode *Child : N->children())
-      Size += computeNodeSize(Child);
-    return Size;
-  }
-  case DecoderTreeNode::CheckField: {
-    const auto *N = static_cast<const CheckFieldNode *>(Node);
-    return OpcodeSize + getULEB128Size(N->getStartBit()) + FieldWidthSize +
-           getULEB128Size(N->getValue());
-  }
-  case DecoderTreeNode::SwitchField: {
-    const auto *N = static_cast<const SwitchFieldNode *>(Node);
-    unsigned Size =
-        OpcodeSize + getULEB128Size(N->getStartBit()) + FieldWidthSize;
-
-    for (auto [Val, Child] : drop_end(N->cases())) {
-      unsigned ChildSize = computeNodeSize(Child);
-      Size += getULEB128Size(Val) + getULEB128Size(ChildSize) + ChildSize;
-    }
-
-    auto [Val, Child] = *std::prev(N->case_end());
-    unsigned ChildSize = computeNodeSize(Child);
-    Size += getULEB128Size(Val) + getULEB128Size(0) + ChildSize;
-    return Size;
-  }
-  case DecoderTreeNode::CheckPredicate: {
-    const auto *N = static_cast<const CheckPredicateNode *>(Node);
-    unsigned PredicateIndex =
-        TableInfo.getPredicateIndex(N->getPredicateString());
-    return OpcodeSize + getULEB128Size(PredicateIndex);
-  }
-  case DecoderTreeNode::SoftFail: {
-    const auto *N = static_cast<const SoftFailNode *>(Node);
-    return OpcodeSize + getULEB128Size(N->getPositiveMask()) +
-           getULEB128Size(N->getNegativeMask());
-  }
-  case DecoderTreeNode::Decode: {
-    const auto *N = static_cast<const DecodeNode *>(Node);
-    unsigned InstOpcode = N->getEncoding().getInstruction()->EnumVal;
-    unsigned DecoderIndex = TableInfo.getDecoderIndex(N->getDecoderString());
-    return OpcodeSize + getULEB128Size(InstOpcode) +
-           getULEB128Size(DecoderIndex);
-  }
-  }
-  llvm_unreachable("Unknown node kind");
-}
-
-unsigned DecoderTableEmitter::computeTableSize(const DecoderTreeNode *Root,
-                                               unsigned BitWidth) const {
-  unsigned Size = 0;
-  if (SpecializeDecodersPerBitwidth)
-    Size += getULEB128Size(BitWidth);
-  Size += computeNodeSize(Root);
-  return Size;
-}
-
-void DecoderTableEmitter::emitStartLine() {
-  CommentIndex = CurrentIndex;
-  OS.indent(2);
-}
-
-void DecoderTableEmitter::emitOpcode(StringRef Name) {
-  emitStartLine();
-  OS << Name << ", ";
-  ++CurrentIndex;
-}
-
-void DecoderTableEmitter::emitByte(uint8_t Val) {
-  OS << static_cast<unsigned>(Val) << ", ";
-  ++CurrentIndex;
-}
-
-void DecoderTableEmitter::emitUInt8(unsigned Val) {
-  assert(isUInt<8>(Val));
-  emitByte(Val);
-}
-
-void DecoderTableEmitter::emitULEB128(uint64_t Val) {
-  while (Val >= 0x80) {
-    emitByte((Val & 0x7F) | 0x80);
-    Val >>= 7;
-  }
-  emitByte(Val);
-}
-
-formatted_raw_ostream &DecoderTableEmitter::emitComment(indent Indent) {
-  constexpr unsigned CommentColumn = 45;
-  if (OS.getColumn() > CommentColumn)
-    OS << '\n';
-  OS.PadToColumn(CommentColumn);
-  OS << "// " << format_decimal(CommentIndex, IndexWidth) << ": " << Indent;
-  return OS;
-}
-
-void DecoderTableEmitter::emitCheckAnyNode(const CheckAnyNode *N,
-                                           indent Indent) {
-  // TODO: Single-child CheckAny node should be optimized out. For now,
-  //   pretend this is the case and print the single child unindented.
-  if (range_size(N->children()) == 1) {
-    emitNode(*N->child_begin(), Indent);
-    return;
-  }
-
-  for (const DecoderTreeNode *Child : drop_end(N->children())) {
-    emitOpcode("OPC_Scope");
-    emitULEB128(computeNodeSize(Child));
-
-    emitComment(Indent) << "{\n";
-    emitNode(Child, Indent + 1);
-    emitComment(Indent) << "}\n";
-  }
-
-  const DecoderTreeNode *Child = *std::prev(N->child_end());
-  emitComment(Indent) << "{\n";
-  emitNode(Child, Indent + 1);
-  emitComment(Indent) << "}\n";
-}
-
-void DecoderTableEmitter::emitCheckAllNode(const CheckAllNode *N,
-                                           indent Indent) {
-  for (const DecoderTreeNode *Child : N->children())
-    emitNode(Child, Indent);
-}
-
-void DecoderTableEmitter::emitSwitchFieldNode(const SwitchFieldNode *N,
-                                              indent Indent) {
-  unsigned LSB = N->getStartBit();
-  unsigned Width = N->getNumBits();
-  unsigned MSB = LSB + Width - 1;
-
-  emitOpcode("OPC_SwitchField");
-  emitULEB128(LSB);
-  emitUInt8(Width);
-
-  emitComment(Indent) << "switch Inst[" << MSB << ':' << LSB << "] {\n";
-
-  for (auto [Val, Child] : drop_end(N->cases())) {
-    emitStartLine();
-    emitULEB128(Val);
-    emitULEB128(computeNodeSize(Child));
-
-    emitComment(Indent) << "case " << format_hex(Val, 0) << ": {\n";
-    emitNode(Child, Indent + 1);
-    emitComment(Indent) << "}\n";
-  }
-
-  auto [Val, Child] = *std::prev(N->case_end());
-  emitStartLine();
-  emitULEB128(Val);
-  emitULEB128(0);
-
-  emitComment(Indent) << "case " << format_hex(Val, 0) << ": {\n";
-  emitNode(Child, Indent + 1);
-  emitComment(Indent) << "}\n";
-
-  emitComment(Indent) << "} // switch Inst[" << MSB << ':' << LSB << "]\n";
-}
-
-void DecoderTableEmitter::emitCheckFieldNode(const CheckFieldNode *N,
-                                             indent Indent) {
-  unsigned LSB = N->getStartBit();
-  unsigned Width = N->getNumBits();
-  unsigned MSB = LSB + Width - 1;
-  uint64_t Val = N->getValue();
-
-  emitOpcode("OPC_CheckField");
-  emitULEB128(LSB);
-  emitUInt8(Width);
-  emitULEB128(Val);
-
-  emitComment(Indent);
-  OS << "check Inst[" << MSB << ':' << LSB << "] == " << format_hex(Val, 0)
-     << '\n';
-}
-
-void DecoderTableEmitter::emitCheckPredicateNode(const CheckPredicateNode *N,
-                                                 indent Indent) {
-  unsigned PredicateIndex =
-      TableInfo.getPredicateIndex(N->getPredicateString());
-
-  emitOpcode("OPC_CheckPredicate");
-  emitULEB128(PredicateIndex);
-  TableInfo.HasCheckPredicate = true;
-
-  emitComment(Indent) << "check predicate " << PredicateIndex << "\n";
-}
-
-void DecoderTableEmitter::emitSoftFailNode(const SoftFailNode *N,
-                                           indent Indent) {
-  uint64_t PositiveMask = N->getPositiveMask();
-  uint64_t NegativeMask = N->getNegativeMask();
-
-  emitOpcode("OPC_SoftFail");
-  emitULEB128(PositiveMask);
-  emitULEB128(NegativeMask);
-  TableInfo.HasSoftFail = true;
-
-  emitComment(Indent) << "check softfail";
-  OS << " pos=" << format_hex(PositiveMask, 10);
-  OS << " neg=" << format_hex(NegativeMask, 10) << '\n';
-}
-
-void DecoderTableEmitter::emitDecodeNode(const DecodeNode *N, indent Indent) {
-  const InstructionEncoding &Encoding = N->getEncoding();
-  unsigned InstOpcode = Encoding.getInstruction()->EnumVal;
-  unsigned DecoderIndex = TableInfo.getDecoderIndex(N->getDecoderString());
-
-  emitOpcode("OPC_Decode");
-  emitULEB128(InstOpcode);
-  emitULEB128(DecoderIndex);
-
-  emitComment(Indent) << "decode to " << Encoding.getName() << " using decoder "
-                      << DecoderIndex << '\n';
-}
-
-void DecoderTableEmitter::emitNode(const DecoderTreeNode *N, indent Indent) {
-  switch (N->getKind()) {
-  case DecoderTreeNode::CheckAny:
-    return emitCheckAnyNode(static_cast<const CheckAnyNode *>(N), Indent);
-  case DecoderTreeNode::CheckAll:
-    return emitCheckAllNode(static_cast<const CheckAllNode *>(N), Indent);
-  case DecoderTreeNode::SwitchField:
-    return emitSwitchFieldNode(static_cast<const SwitchFieldNode *>(N), Indent);
-  case DecoderTreeNode::CheckField:
-    return emitCheckFieldNode(static_cast<const CheckFieldNode *>(N), Indent);
-  case DecoderTreeNode::CheckPredicate:
-    return emitCheckPredicateNode(static_cast<const CheckPredicateNode *>(N),
-                                  Indent);
-  case DecoderTreeNode::SoftFail:
-    return emitSoftFailNode(static_cast<const SoftFailNode *>(N), Indent);
-  case DecoderTreeNode::Decode:
-    return emitDecodeNode(static_cast<const DecodeNode *>(N), Indent);
-  }
-  llvm_unreachable("Unknown node kind");
-}
-
-void DecoderTableEmitter::emitTable(StringRef TableName, unsigned BitWidth,
-                                    const DecoderTreeNode *Root) {
-  analyzeNode(Root);
-
-  unsigned TableSize = computeTableSize(Root, BitWidth);
-  OS << "static const uint8_t " << TableName << "[" << TableSize << "] = {\n";
-
-  // Calculate the number of decimal places for table indices.
-  // This is simply log10 of the table size.
-  IndexWidth = 1;
-  for (unsigned S = TableSize; S /= 10;)
-    ++IndexWidth;
-
-  CurrentIndex = 0;
-
-  // When specializing decoders per bit width, each decoder table will begin
-  // with the bitwidth for that table.
-  if (SpecializeDecodersPerBitwidth) {
-    emitStartLine();
-    emitULEB128(BitWidth);
-    emitComment(indent(0)) << "BitWidth " << BitWidth << '\n';
-  }
-
-  emitNode(Root, indent(0));
-  assert(CurrentIndex == TableSize &&
-         "The size of the emitted table differs from the calculated one");
-
-  OS << "};\n";
-}
-
 /// Collects all HwModes referenced by the target for encoding purposes.
 void DecoderEmitter::collectHwModesReferencedForEncodings(
     std::vector<unsigned> &HwModeIDs,
@@ -2005,7 +1450,8 @@ void DecoderEmitter::emitTable(formatted_raw_ostream &OS,
   TableName.append(utostr(BitWidth));
 
   DecoderTableEmitter TableEmitter(TableInfo, OS);
-  TableEmitter.emitTable(TableName, BitWidth, Tree);
+  TableEmitter.emitTable(TableName,
+                         SpecializeDecodersPerBitwidth ? BitWidth : 0, Tree);
 }
 
 // Emits disassembler code for instruction decoding.
diff --git a/llvm/utils/TableGen/DecoderTableEmitter.cpp b/llvm/utils/TableGen/DecoderTableEmitter.cpp
new file mode 100644
index 0000000000000..f6e60eaad1e20
--- /dev/null
+++ b/llvm/utils/TableGen/DecoderTableEmitter.cpp
@@ -0,0 +1,342 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "DecoderTableEmitter.h"
+#include "Common/CodeGenInstruction.h"
+#include "Common/InstructionEncoding.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/LEB128.h"
+
+using namespace llvm;
+
+void DecoderTableEmitter::analyzeNode(const DecoderTreeNode *Node) const {
+  switch (Node->getKind()) {
+  case DecoderTreeNode::CheckAny: {
+    const auto *N = static_cast<const CheckAnyNode *>(Node);
+    for (const DecoderTreeNode *Child : N->children())
+      analyzeNode(Child);
+    break;
+  }
+  case DecoderTreeNode::CheckAll: {
+    const auto *N = static_cast<const CheckAllNode *>(Node);
+    for (const DecoderTreeNode *Child : N->children())
+      analyzeNode(Child);
+    break;
+  }
+  case DecoderTreeNode::CheckField:
+    break;
+  case DecoderTreeNode::SwitchField: {
+    const auto *N = static_cast<const SwitchFieldNode *>(Node);
+    for (const DecoderTreeNode *Child : make_second_range(N->cases()))
+      analyzeNode(Child);
+    break;
+  }
+  case DecoderTreeNode::CheckPredicate: {
+    const auto *N = static_cast<const CheckPredicateNode *>(Node);
+    TableInfo.insertPredicate(N->getPredicateString());
+    break;
+  }
+  case DecoderTreeNode::SoftFail:
+    break;
+  case DecoderTreeNode::Decode: {
+    const auto *N = static_cast<const DecodeNode *>(Node);
+    TableInfo.insertDecoder(N->getDecoderString());
+    break;
+  }
+  }
+}
+
+unsigned
+DecoderTableEmitter::computeNodeSize(const DecoderTreeNode *Node) const {
+  // To make the arithmetic below clearer.
+  static constexpr unsigned OpcodeSize = 1;
+  static constexpr unsigned FieldWidthSize = 1;
+
+  switch (Node->getKind()) {
+  case DecoderTreeNode::CheckAny: {
+    const auto *N = static_cast<const CheckAnyNode *>(Node);
+    // Pretend the node was optimized. See the comment in emitCheckAnyNode.
+    if (range_size(N->children()) == 1)
+      return computeNodeSize(*N->child_begin());
+    unsigned Size = 0;
+    // All children except the last one are preceded by OPC_Scope opcode and
+    // the size of the child.
+    for (const DecoderTreeNode *Child : drop_end(N->children())) {
+      unsigned ChildSize = computeNodeSize(Child);
+      Size += OpcodeSize + getULEB128Size(ChildSize) + ChildSize;
+    }
+    const DecoderTreeNode *Child = *std::prev(N->child_end());
+    return Size + computeNodeSize(Child);
+  }
+  case DecoderTreeNode::CheckAll: {
+    const auto *N = static_cast<const CheckAllNode *>(Node);
+    unsigned Size = 0;
+    for (const DecoderTreeNode *Child : N->children())
+      Size += computeNodeSize(Child);
+    return Size;
+  }
+  case DecoderTreeNode::CheckField: {
+    const auto *N = static_cast<const CheckFieldNode *>(Node);
+    return OpcodeSize + getULEB128Size(N->getStartBit()) + FieldWidthSize +
+           getULEB128Size(N->getValue());
+  }
+  case DecoderTreeNode::SwitchField: {
+    const auto *N = static_cast<const SwitchFieldNode *>(Node);
+    unsigned Size =
+        OpcodeSize + getULEB128Size(N->getStartBit()) + FieldWidthSize;
+
+    for (auto [Val, Child] : drop_end(N->cases())) {
+      unsigned ChildSize = computeNodeSize(Child);
+      Size += getULEB128Size(Val) + getULEB128Size(ChildSize) + ChildSize;
+    }
+
+    auto [Val, Child] = *std::prev(N->case_end());
+    unsigned ChildSize = computeNodeSize(Child);
+    Size += getULEB128Size(Val) + getULEB128Size(0) + ChildSize;
+    return Size;
+  }
+  case DecoderTreeNode::CheckPredicate: {
+    const auto *N = static_cast<const CheckPredicateNode *>(Node);
+    unsigned PredicateIndex =
+        TableInfo.getPredicateIndex(N->getPredicateString());
+    return OpcodeSize + getULEB128Size(PredicateIndex);
+  }
+  case DecoderTreeNode::SoftFail: {
+    const auto *N = static_cast<const SoftFailNode *>(Node);
+    return OpcodeSize + getULEB128Size(N->getPositiveMask()) +
+           getULEB128Size(N->getNegativeMask());
+  }
+  case DecoderTreeNode::Decode: {
+    const auto *N = static_cast<const DecodeNode *>(Node);
+    unsigned InstOpcode = N->getEncoding().getInstruction()->EnumVal;
+    unsigned DecoderIndex = TableInfo.getDecoderIndex(N->getDecoderString());
+    return OpcodeSize + getULEB128Size(InstOpcode) +
+           getULEB128Size(DecoderIndex);
+  }
+  }
+  llvm_unreachable("Unknown node kind");
+}
+
+unsigned DecoderTableEmitter::computeTableSize(const DecoderTreeNode *Root,
+                                               unsigned BitWidth) const {
+  unsigned Size = 0;
+  if (BitWidth)
+    Size += getULEB128Size(BitWidth);
+  Size += computeNodeSize(Root);
+  return Size;
+}
+
+void DecoderTableEmitter::emitStartLine() {
+  CommentIndex = CurrentIndex;
+  OS.indent(2);
+}
+
+void DecoderTableEmitter::emitOpcode(StringRef Name) {
+  emitStartLine();
+  OS << Name << ", ";
+  ++CurrentIndex;
+}
+
+void DecoderTableEmitter::emitByte(uint8_t Val) {
+  OS << static_cast<unsigned>(Val) << ", ";
+  ++CurrentIndex;
+}
+
+void DecoderTableEmitter::emitUInt8(unsigned Val) {
+  assert(isUInt<8>(Val));
+  emitByte(Val);
+}
+
+void DecoderTableEmitter::emitULEB128(uint64_t Val) {
+  while (Val >= 0x80) {
+    emitByte((Val & 0x7F) | 0x80);
+    Val >>= 7;
+  }
+  emitByte(Val);
+}
+
+formatted_raw_ostream &DecoderTableEmitter::emitComment(indent Indent) {
+  constexpr unsigned CommentColumn = 45;
+  if (OS.getColumn() > CommentColumn)
+    OS << '\n';
+  OS.PadToColumn(CommentColumn);
+  OS << "// " << format_decimal(CommentIndex, IndexWidth) << ": " << Indent;
+  return OS;
+}
+
+void DecoderTableEmitter::emitCheckAnyNode(const CheckAnyNode *N,
+                                           indent Indent) {
+  // TODO: Single-child CheckAny node should be optimized out. For now,
+  //   pretend this is the case and print the single child unindented.
+  if (range_size(N->children()) == 1) {
+    emitNode(*N->child_begin(), Indent);
+    return;
+  }
+
+  for (const DecoderTreeNode *Child : drop_end(N->children())) {
+    emitOpcode("OPC_Scope");
+    emitULEB128(computeNodeSize(Child));
+
+    emitComment(Indent) << "{\n";
+    emitNode(Child, Indent + 1);
+    emitComment(Indent) << "}\n";
+  }
+
+  const DecoderTreeNode *Child = *std::prev(N->child_end());
+  emitComment(Indent) << "{\n";
+  emitNode(Child, Indent + 1);
+  emitComment(Indent) << "}\n";
+}
+
+void DecoderTableEmitter::emitCheckAllNode(const CheckAllNode *N,
+                                           indent Indent) {
+  for (const DecoderTreeNode *Child : N->children())
+    emitNode(Child, Indent);
+}
+
+void DecoderTableEmitter::emitSwitchFieldNode(const SwitchFieldNode *N,
+                                              indent Indent) {
+  unsigned LSB = N->getStartBit();
+  unsigned Width = N->getNumBits();
+  unsigned MSB = LSB + Width - 1;
+
+  emitOpcode("OPC_SwitchField");
+  emitULEB128(LSB);
+  emitUInt8(Width);
+
+  emitComment(Indent) << "switch Inst[" << MSB << ':' << LSB << "] {\n";
+
+  for (auto [Val, Child] : drop_end(N->cases())) {
+    emitStartLine();
+    emitULEB128(Val);
+    emitULEB128(computeNodeSize(Child));
+
+    emitComment(Indent) << "case " << format_hex(Val, 0) << ": {\n";
+    emitNode(Child, Indent + 1);
+    emitComment(Indent) << "}\n";
+  }
+
+  auto [Val, Child] = *std::prev(N->case_end());
+  emitStartLine();
+  emitULEB128(Val);
+  emitULEB128(0);
+
+  emitComment(Indent) << "case " << format_hex(Val, 0) << ": {\n";
+  emitNode(Child, Indent + 1);
+  emitComment(Indent) << "}\n";
+
+  emitComment(Indent) << "} // switch Inst[" << MSB << ':' << LSB << "]\n";
+}
+
+void DecoderTableEmitter::emitCheckFieldNode(const CheckFieldNode *N,
+                                             indent Indent) {
+  unsigned LSB = N->getStartBit();
+  unsigned Width = N->getNumBits();
+  unsigned MSB = LSB + Width - 1;
+  uint64_t Val = N->getValue();
+
+  emitOpcode("OPC_CheckField");
+  emitULEB128(LSB);
+  emitUInt8(Width);
+  emitULEB128(Val);
+
+  emitComment(Indent);
+  OS << "check Inst[" << MSB << ':' << LSB << "] == " << format_hex(Val, 0)
+     << '\n';
+}
+
+void DecoderTableEmitter::emitCheckPredicateNode(const CheckPredicateNode *N,
+                                                 indent Indent) {
+  unsigned PredicateIndex =
+      TableInfo.getPredicateIndex(N->getPredicateString());
+
+  emitOpcode("OPC_CheckPredicate");
+  emitULEB128(PredicateIndex);
+  TableInfo.HasCheckPredicate = true;
+
+  emitComment(Indent) << "check predicate " << PredicateIndex << "\n";
+}
+
+void DecoderTableEmitter::emitSoftFailNode(const SoftFailNode *N,
+                                           indent Indent) {
+  uint64_t PositiveMask = N->getPositiveMask();
+  uint64_t NegativeMask = N->getNegativeMask();
+
+  emitOpcode("OPC_SoftFail");
+  emitULEB128(PositiveMask);
+  emitULEB128(NegativeMask);
+  TableInfo.HasSoftFail = true;
+
+  emitComment(Indent) << "check softfail";
+  OS << " pos=" << format_hex(PositiveMask, 10);
+  OS << " neg=" << format_hex(NegativeMask, 10) << '\n';
+}
+
+void DecoderTableEmitter::emitDecodeNode(const DecodeNode *N, indent Indent) {
+  const InstructionEncoding &Encoding = N->getEncoding();
+  unsigned InstOpcode = Encoding.getInstruction()->EnumVal;
+  unsigned DecoderIndex = TableInfo.getDecoderIndex(N->getDecoderString());
+
+  emitOpcode("OPC_Decode");
+  emitULEB128(InstOpcode);
+  emitULEB128(DecoderIndex);
+
+  emitComment(Indent) << "decode to " << Encoding.getName() << " using decoder "
+                      << DecoderIndex << '\n';
+}
+
+void DecoderTableEmitter::emitNode(const DecoderTreeNode *N, indent Indent) {
+  switch (N->getKind()) {
+  case DecoderTreeNode::CheckAny:
+    return emitCheckAnyNode(static_cast<const CheckAnyNode *>(N), Indent);
+  case DecoderTreeNode::CheckAll:
+    return emitCheckAllNode(static_cast<const CheckAllNode *>(N), Indent);
+  case DecoderTreeNode::SwitchField:
+    return emitSwitchFieldNode(static_cast<const SwitchFieldNode *>(N), Indent);
+  case DecoderTreeNode::CheckField:
+    return emitCheckFieldNode(static_cast<const CheckFieldNode *>(N), Indent);
+  case DecoderTreeNode::CheckPredicate:
+    return emitCheckPredicateNode(static_cast<const CheckPredicateNode *>(N),
+                                  Indent);
+  case DecoderTreeNode::SoftFail:
+    return emitSoftFailNode(static_cast<const SoftFailNode *>(N), Indent);
+  case DecoderTreeNode::Decode:
+    return emitDecodeNode(static_cast<const DecodeNode *>(N), Indent);
+  }
+  llvm_unreachable("Unknown node kind");
+}
+
+void DecoderTableEmitter::emitTable(StringRef TableName, unsigned BitWidth,
+                                    const DecoderTreeNode *Root) {
+  analyzeNode(Root);
+
+  unsigned TableSize = computeTableSize(Root, BitWidth);
+  OS << "static const uint8_t " << TableName << "[" << TableSize << "] = {\n";
+
+  // Calculate the number of decimal places for table indices.
+  // This is simply log10 of the table size.
+  IndexWidth = 1;
+  for (unsigned S = TableSize; S /= 10;)
+    ++IndexWidth;
+
+  CurrentIndex = 0;
+
+  // When specializing decoders per bit width, each decoder table will begin
+  // with the bitwidth for that table.
+  if (BitWidth) {
+    emitStartLine();
+    emitULEB128(BitWidth);
+    emitComment(indent(0)) << "BitWidth " << BitWidth << '\n';
+  }
+
+  emitNode(Root, indent(0));
+  assert(CurrentIndex == TableSize &&
+         "The size of the emitted table differs from the calculated one");
+
+  OS << "};\n";
+}
diff --git a/llvm/utils/TableGen/DecoderTableEmitter.h b/llvm/utils/TableGen/DecoderTableEmitter.h
new file mode 100644
index 0000000000000..44ed86b5c6ddc
--- /dev/null
+++ b/llvm/utils/TableGen/DecoderTableEmitter.h
@@ -0,0 +1,89 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_TABLEGEN_DECODERTABLEEMITTER_H
+#define LLVM_UTILS_TABLEGEN_DECODERTABLEEMITTER_H
+
+#include "DecoderTree.h"
+#include "llvm/ADT/CachedHashString.h"
+#include "llvm/ADT/SetVector.h"
+#include "llvm/Support/FormattedStream.h"
+
+namespace llvm {
+
+using PredicateSet = SetVector<CachedHashString>;
+using DecoderSet = SetVector<CachedHashString>;
+
+struct DecoderTableInfo {
+  PredicateSet Predicates;
+  DecoderSet Decoders;
+  bool HasCheckPredicate;
+  bool HasSoftFail;
+
+  void insertPredicate(StringRef Predicate) {
+    Predicates.insert(CachedHashString(Predicate));
+  }
+
+  void insertDecoder(StringRef Decoder) {
+    Decoders.insert(CachedHashString(Decoder));
+  }
+
+  unsigned getPredicateIndex(StringRef Predicate) const {
+    auto I = find(Predicates, Predicate);
+    assert(I != Predicates.end());
+    return std::distance(Predicates.begin(), I);
+  }
+
+  unsigned getDecoderIndex(StringRef Decoder) const {
+    auto I = find(Decoders, Decoder);
+    assert(I != Decoders.end());
+    return std::distance(Decoders.begin(), I);
+  }
+};
+
+class DecoderTableEmitter {
+  DecoderTableInfo &TableInfo;
+  formatted_raw_ostream OS;
+  unsigned IndexWidth;
+  unsigned CurrentIndex;
+  unsigned CommentIndex;
+
+public:
+  DecoderTableEmitter(DecoderTableInfo &TableInfo, raw_ostream &OS)
+      : TableInfo(TableInfo), OS(OS) {}
+
+  void emitTable(StringRef TableName, unsigned BitWidth,
+                 const DecoderTreeNode *Root);
+
+private:
+  void analyzeNode(const DecoderTreeNode *Node) const;
+
+  unsigned computeNodeSize(const DecoderTreeNode *Node) const;
+  unsigned computeTableSize(const DecoderTreeNode *Root,
+                            unsigned BitWidth) const;
+
+  void emitStartLine();
+  void emitOpcode(StringRef Name);
+  void emitByte(uint8_t Val);
+  void emitUInt8(unsigned Val);
+  void emitULEB128(uint64_t Val);
+  formatted_raw_ostream &emitComment(indent Indent);
+
+  void emitCheckAnyNode(const CheckAnyNode *N, indent Indent);
+  void emitCheckAllNode(const CheckAllNode *N, indent Indent);
+  void emitSwitchFieldNode(const SwitchFieldNode *N, indent Indent);
+  void emitCheckFieldNode(const CheckFieldNode *N, indent Indent);
+  void emitCheckPredicateNode(const CheckPredicateNode *N, indent Indent);
+  void emitSoftFailNode(const SoftFailNode *N, indent Indent);
+  void emitDecodeNode(const DecodeNode *N, indent Indent);
+  void emitNode(const DecoderTreeNode *N, indent Indent);
+};
+
+} // namespace llvm
+
+#endif // LLVM_UTILS_TABLEGEN_DECODERTABLEEMITTER_H
diff --git a/llvm/utils/TableGen/DecoderTree.cpp b/llvm/utils/TableGen/DecoderTree.cpp
new file mode 100644
index 0000000000000..aa7389e6dd9f3
--- /dev/null
+++ b/llvm/utils/TableGen/DecoderTree.cpp
@@ -0,0 +1,14 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "DecoderTree.h"
+
+using namespace llvm;
+
+// Pin the vtable to this file.
+DecoderTreeNode::~DecoderTreeNode() = default;
diff --git a/llvm/utils/TableGen/DecoderTree.h b/llvm/utils/TableGen/DecoderTree.h
new file mode 100644
index 0000000000000..a7102c3809d0f
--- /dev/null
+++ b/llvm/utils/TableGen/DecoderTree.h
@@ -0,0 +1,181 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_TABLEGEN_DECODERTREE_H
+#define LLVM_UTILS_TABLEGEN_DECODERTREE_H
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include <map>
+#include <memory>
+
+namespace llvm {
+
+class InstructionEncoding;
+
+class DecoderTreeNode {
+public:
+  virtual ~DecoderTreeNode();
+
+  enum KindTy {
+    CheckAny,
+    CheckAll,
+    CheckField,
+    SwitchField,
+    CheckPredicate,
+    SoftFail,
+    Decode,
+  };
+
+  KindTy getKind() const { return Kind; }
+
+protected:
+  explicit DecoderTreeNode(KindTy Kind) : Kind(Kind) {}
+
+private:
+  KindTy Kind;
+};
+
+class CheckManyNode : public DecoderTreeNode {
+  SmallVector<std::unique_ptr<DecoderTreeNode>, 0> Children;
+
+  static const DecoderTreeNode *
+  mapElement(decltype(Children)::const_reference Element) {
+    return Element.get();
+  }
+
+protected:
+  explicit CheckManyNode(KindTy Kind) : DecoderTreeNode(Kind) {}
+
+public:
+  void addChild(std::unique_ptr<DecoderTreeNode> Child) {
+    Children.push_back(std::move(Child));
+  }
+
+  using child_iterator = mapped_iterator<decltype(Children)::const_iterator,
+                                         decltype(&mapElement)>;
+
+  child_iterator child_begin() const {
+    return child_iterator(Children.begin(), mapElement);
+  }
+
+  child_iterator child_end() const {
+    return child_iterator(Children.end(), mapElement);
+  }
+
+  iterator_range<child_iterator> children() const {
+    return make_range(child_begin(), child_end());
+  }
+};
+
+class CheckAnyNode : public CheckManyNode {
+public:
+  CheckAnyNode() : CheckManyNode(CheckAny) {}
+};
+
+class CheckAllNode : public CheckManyNode {
+public:
+  CheckAllNode() : CheckManyNode(CheckAll) {}
+};
+
+class CheckFieldNode : public DecoderTreeNode {
+  unsigned StartBit;
+  unsigned NumBits;
+  uint64_t Value;
+
+public:
+  CheckFieldNode(unsigned StartBit, unsigned NumBits, uint64_t Value)
+      : DecoderTreeNode(CheckField), StartBit(StartBit), NumBits(NumBits),
+        Value(Value) {}
+
+  unsigned getStartBit() const { return StartBit; }
+
+  unsigned getNumBits() const { return NumBits; }
+
+  uint64_t getValue() const { return Value; }
+};
+
+class SwitchFieldNode : public DecoderTreeNode {
+  unsigned StartBit;
+  unsigned NumBits;
+  std::map<uint64_t, std::unique_ptr<DecoderTreeNode>> Cases;
+
+  static std::pair<uint64_t, const DecoderTreeNode *>
+  mapElement(decltype(Cases)::const_reference Element) {
+    return std::pair(Element.first, Element.second.get());
+  }
+
+public:
+  SwitchFieldNode(unsigned StartBit, unsigned NumBits)
+      : DecoderTreeNode(SwitchField), StartBit(StartBit), NumBits(NumBits) {}
+
+  void addCase(uint64_t Value, std::unique_ptr<DecoderTreeNode> N) {
+    Cases.try_emplace(Value, std::move(N));
+  }
+
+  unsigned getStartBit() const { return StartBit; }
+
+  unsigned getNumBits() const { return NumBits; }
+
+  using case_iterator =
+      mapped_iterator<decltype(Cases)::const_iterator, decltype(&mapElement)>;
+
+  case_iterator case_begin() const {
+    return case_iterator(Cases.begin(), mapElement);
+  }
+
+  case_iterator case_end() const {
+    return case_iterator(Cases.end(), mapElement);
+  }
+
+  iterator_range<case_iterator> cases() const {
+    return make_range(case_begin(), case_end());
+  }
+};
+
+class CheckPredicateNode : public DecoderTreeNode {
+  std::string PredicateString;
+
+public:
+  explicit CheckPredicateNode(std::string PredicateString)
+      : DecoderTreeNode(CheckPredicate),
+        PredicateString(std::move(PredicateString)) {}
+
+  StringRef getPredicateString() const { return PredicateString; }
+};
+
+class SoftFailNode : public DecoderTreeNode {
+  uint64_t PositiveMask, NegativeMask;
+
+public:
+  SoftFailNode(uint64_t PositiveMask, uint64_t NegativeMask)
+      : DecoderTreeNode(SoftFail), PositiveMask(PositiveMask),
+        NegativeMask(NegativeMask) {}
+
+  uint64_t getPositiveMask() const { return PositiveMask; }
+  uint64_t getNegativeMask() const { return NegativeMask; }
+};
+
+class DecodeNode : public DecoderTreeNode {
+  const InstructionEncoding &Encoding;
+  std::string DecoderString;
+
+public:
+  DecodeNode(const InstructionEncoding &Encoding, std::string DecoderString)
+      : DecoderTreeNode(Decode), Encoding(Encoding),
+        DecoderString(std::move(DecoderString)) {}
+
+  const InstructionEncoding &getEncoding() const { return Encoding; }
+
+  StringRef getDecoderString() const { return DecoderString; }
+};
+
+} // namespace llvm
+
+#endif // LLVM_UTILS_TABLEGEN_DECODERTREE_H

>From 8d903dba6f68fbf4e45fd87cbed582beb900b2ba Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <barannikov88 at gmail.com>
Date: Thu, 18 Sep 2025 15:53:06 +0300
Subject: [PATCH 11/21] Change `auto` to `const_iterator` to suppress
 clang-tidy warnings

---
 llvm/utils/TableGen/DecoderTableEmitter.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/utils/TableGen/DecoderTableEmitter.h b/llvm/utils/TableGen/DecoderTableEmitter.h
index 44ed86b5c6ddc..1eb1d17210c2c 100644
--- a/llvm/utils/TableGen/DecoderTableEmitter.h
+++ b/llvm/utils/TableGen/DecoderTableEmitter.h
@@ -34,13 +34,13 @@ struct DecoderTableInfo {
   }
 
   unsigned getPredicateIndex(StringRef Predicate) const {
-    auto I = find(Predicates, Predicate);
+    PredicateSet::const_iterator I = find(Predicates, Predicate);
     assert(I != Predicates.end());
     return std::distance(Predicates.begin(), I);
   }
 
   unsigned getDecoderIndex(StringRef Decoder) const {
-    auto I = find(Decoders, Decoder);
+    DecoderSet::const_iterator I = find(Decoders, Decoder);
     assert(I != Decoders.end());
     return std::distance(Decoders.begin(), I);
   }

>From 3036d9629e764afe6b2e7903b4b32bf3b7a7c608 Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <barannikov88 at gmail.com>
Date: Thu, 18 Sep 2025 16:21:54 +0300
Subject: [PATCH 12/21] Move/update comments, minor refactoring

---
 llvm/utils/TableGen/DecoderEmitter.cpp | 77 ++++++++++++--------------
 1 file changed, 34 insertions(+), 43 deletions(-)

diff --git a/llvm/utils/TableGen/DecoderEmitter.cpp b/llvm/utils/TableGen/DecoderEmitter.cpp
index 6f6c95e593748..54cad9a68d11f 100644
--- a/llvm/utils/TableGen/DecoderEmitter.cpp
+++ b/llvm/utils/TableGen/DecoderEmitter.cpp
@@ -167,10 +167,6 @@ class DecoderEmitter {
 
   const CodeGenTarget &getTarget() const { return Target; }
 
-  // Emit the decoder state machine table.
-  void emitTable(formatted_raw_ostream &OS, DecoderTableInfo &TableInfo,
-                 StringRef Namespace, unsigned HwModeID, unsigned BitWidth,
-                 const DecoderTreeNode *Tree) const;
   void emitInstrLenTable(formatted_raw_ostream &OS,
                          ArrayRef<unsigned> InstrLen) const;
   void emitPredicateFunction(formatted_raw_ostream &OS,
@@ -1189,9 +1185,7 @@ class DecoderTreeBuilder {
                      ArrayRef<InstructionEncoding> Encodings)
       : Target(Target), Encodings(Encodings) {}
 
-  std::unique_ptr<DecoderTreeNode> buildTree(const FilterChooser &FC) {
-    return convertFilterChooser(&FC);
-  }
+  std::unique_ptr<DecoderTreeNode> buildTree(ArrayRef<unsigned> EncodingIDs);
 
 private:
   std::unique_ptr<DecoderTreeNode>
@@ -1271,6 +1265,14 @@ DecoderTreeBuilder::convertFilterChooser(const FilterChooser *FC) {
   return N;
 }
 
+std::unique_ptr<DecoderTreeNode>
+DecoderTreeBuilder::buildTree(ArrayRef<unsigned> EncodingIDs) {
+  FilterChooser FC(Encodings, EncodingIDs);
+  if (FC.hasConflict())
+    return nullptr;
+  return convertFilterChooser(&FC);
+}
+
 /// Collects all HwModes referenced by the target for encoding purposes.
 void DecoderEmitter::collectHwModesReferencedForEncodings(
     std::vector<unsigned> &HwModeIDs,
@@ -1438,22 +1440,6 @@ DecoderEmitter::DecoderEmitter(const RecordKeeper &RK)
   parseInstructionEncodings();
 }
 
-// Emit the decoder state machine table.
-void DecoderEmitter::emitTable(formatted_raw_ostream &OS,
-                               DecoderTableInfo &TableInfo, StringRef Namespace,
-                               unsigned HwModeID, unsigned BitWidth,
-                               const DecoderTreeNode *Tree) const {
-  SmallString<32> TableName("DecoderTable");
-  TableName.append(Namespace);
-  if (HwModeID != DefaultMode)
-    TableName.append({"_", Target.getHwModes().getModeName(HwModeID)});
-  TableName.append(utostr(BitWidth));
-
-  DecoderTableEmitter TableEmitter(TableInfo, OS);
-  TableEmitter.emitTable(TableName,
-                         SpecializeDecodersPerBitwidth ? BitWidth : 0, Tree);
-}
-
 // Emits disassembler code for instruction decoding.
 void DecoderEmitter::run(raw_ostream &o) const {
   formatted_raw_ostream OS(o);
@@ -1522,35 +1508,40 @@ template <typename T> constexpr uint32_t InsnBitWidth = 0;
     PrintFatalError(
         "Cannot specialize decoders for variable length instuctions");
 
-  // Entries in `EncMap` are already sorted by bitwidth. So bucketing per
-  // bitwidth can be done on-the-fly as we iterate over the map.
   DecoderTableInfo TableInfo{};
+  DecoderTreeBuilder TreeBuilder(Target, Encodings);
+  DecoderTableEmitter TableEmitter(TableInfo, OS);
 
+  // Emit a table for each (namespace, hwmode, bitwidth) combination.
+  // Predicates and decoders are shared across the tables to provide more
+  // opportunities for uniqueness. If SpecializeDecodersPerBitwidth is enabled,
+  // decoders are shared across all tables for a given bitwidth, else they are
+  // shared across all tables. Predicates are always shared across all tables.
+  //
+  // Entries in `EncMap` are already sorted by bitwidth. So bucketing per
+  // bitwidth can be done on-the-fly as we iterate over the map.
   bool HasConflict = false;
   for (const auto &[BitWidth, BWMap] : EncMap) {
     for (const auto &[Key, EncodingIDs] : BWMap) {
       auto [DecoderNamespace, HwModeID] = Key;
 
-      // Emit the decoder for this (namespace, hwmode, width) combination.
-      FilterChooser FC(Encodings, EncodingIDs);
-      HasConflict |= FC.hasConflict();
-      // Skip emitting table entries if a conflict has been detected.
-      if (HasConflict)
+      std::unique_ptr<DecoderTreeNode> Tree =
+          TreeBuilder.buildTree(EncodingIDs);
+
+      // Skip emitting the table if a conflict has been detected.
+      if (!Tree) {
+        HasConflict = true;
         continue;
+      }
+
+      // Form the table name.
+      SmallString<32> TableName({"DecoderTable", DecoderNamespace});
+      if (HwModeID != DefaultMode)
+        TableName.append({"_", Target.getHwModes().getModeName(HwModeID)});
+      TableName.append(utostr(BitWidth));
 
-      DecoderTreeBuilder TreeBuilder(Target, Encodings);
-      std::unique_ptr<DecoderTreeNode> Tree = TreeBuilder.buildTree(FC);
-
-      // The decode table is cleared for each top level decoder function. The
-      // predicates and decoders themselves, however, are shared across
-      // different decoders to give more opportunities for uniqueing.
-      //  - If `SpecializeDecodersPerBitwidth` is enabled, decoders are shared
-      //    across all decoder tables for a given bitwidth, else they are shared
-      //    across all decoder tables.
-      //  - predicates are shared across all decoder tables.
-      // Print the table to the output stream.
-      emitTable(OS, TableInfo, DecoderNamespace, HwModeID, BitWidth,
-                Tree.get());
+      TableEmitter.emitTable(
+          TableName, SpecializeDecodersPerBitwidth ? BitWidth : 0, Tree.get());
     }
 
     // Each BitWidth get's its own decoders and decoder function if

>From 9905b7e580f2338a7b1254d1cf5d7b4a4d2a0a1f Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <barannikov88 at gmail.com>
Date: Thu, 18 Sep 2025 19:29:18 +0300
Subject: [PATCH 13/21] Print CheckAny as a sequence of try-else, fix SoftFail
 output

This reduces the vertical size of the output and should make it clearer.
Drop "check" when printing SoftFail because this opcode cannot fail.
---
 .../DecoderEmitter/additional-encoding.td     | 20 ++++++++-----------
 .../DecoderEmitter/trydecode-emission.td      |  5 ++---
 .../DecoderEmitter/trydecode-emission2.td     |  5 ++---
 .../DecoderEmitter/trydecode-emission3.td     |  5 ++---
 .../DecoderEmitter/trydecode-emission4.td     |  5 ++---
 llvm/utils/TableGen/DecoderTableEmitter.cpp   |  9 +++++----
 6 files changed, 21 insertions(+), 28 deletions(-)

diff --git a/llvm/test/TableGen/DecoderEmitter/additional-encoding.td b/llvm/test/TableGen/DecoderEmitter/additional-encoding.td
index b0d8a583af7b7..2652407191a57 100644
--- a/llvm/test/TableGen/DecoderEmitter/additional-encoding.td
+++ b/llvm/test/TableGen/DecoderEmitter/additional-encoding.td
@@ -33,38 +33,34 @@ class I<dag out_ops, dag in_ops> : Instruction {
 // CHECK-LABEL: static const uint8_t DecoderTable16[67] = {
 // CHECK-NEXT:    OPC_SwitchField, 12, 4,      //  0: switch Inst[15:12] {
 // CHECK-NEXT:    0, 14,                       //  3: case 0x0: {
-// CHECK-NEXT:    OPC_Scope, 8,                //  5:  {
+// CHECK-NEXT:    OPC_Scope, 8,                //  5:  try {
 // CHECK-NEXT:    OPC_CheckField, 6, 6, 0,     //  7:   check Inst[11:6] == 0x0
 // CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 0, // 11:   decode to NOP using decoder 0
-// CHECK-NEXT:                                 // 11:  }
-// CHECK-NEXT:                                 // 11:  {
+// CHECK-NEXT:                                 // 11:  } else try {
 // CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 1, // 15:   decode to SHIFT0 using decoder 1
 // CHECK-NEXT:                                 // 15:  }
 // CHECK-NEXT:                                 // 15: }
 // CHECK-NEXT:    1, 14,                       // 19: case 0x1: {
-// CHECK-NEXT:    OPC_Scope, 8,                // 21:  {
+// CHECK-NEXT:    OPC_Scope, 8,                // 21:  try {
 // CHECK-NEXT:    OPC_CheckField, 6, 6, 0,     // 23:   check Inst[11:6] == 0x0
 // CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 0, // 27:   decode to {{.*}}:NOP using decoder 0
-// CHECK-NEXT:                                 // 27:  }
-// CHECK-NEXT:                                 // 27:  {
+// CHECK-NEXT:                                 // 27:  } else try {
 // CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 1, // 31:   decode to SHIFT1 using decoder 1
 // CHECK-NEXT:                                 // 31:  }
 // CHECK-NEXT:                                 // 31: }
 // CHECK-NEXT:    2, 14,                       // 35: case 0x2: {
-// CHECK-NEXT:    OPC_Scope, 8,                // 37:  {
+// CHECK-NEXT:    OPC_Scope, 8,                // 37:  try {
 // CHECK-NEXT:    OPC_CheckField, 6, 6, 0,     // 39:   check Inst[11:6] == 0x0
 // CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 0, // 43:   decode to {{.*}}:NOP using decoder 0
-// CHECK-NEXT:                                 // 43:  }
-// CHECK-NEXT:                                 // 43:  {
+// CHECK-NEXT:                                 // 43:  } else try {
 // CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 1, // 47:   decode to SHIFT2 using decoder 1
 // CHECK-NEXT:                                 // 47:  }
 // CHECK-NEXT:                                 // 47: }
 // CHECK-NEXT:    3, 0,                        // 51: case 0x3: {
-// CHECK-NEXT:    OPC_Scope, 8,                // 53:  {
+// CHECK-NEXT:    OPC_Scope, 8,                // 53:  try {
 // CHECK-NEXT:    OPC_CheckField, 6, 6, 0,     // 55:   check Inst[11:6] == 0x0
 // CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 0, // 59:   decode to {{.*}}:NOP using decoder 0
-// CHECK-NEXT:                                 // 59:  }
-// CHECK-NEXT:                                 // 59:  {
+// CHECK-NEXT:                                 // 59:  } else try {
 // CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 1, // 63:   decode to SHIFT3 using decoder 1
 // CHECK-NEXT:                                 // 63:  }
 // CHECK-NEXT:                                 // 63: }
diff --git a/llvm/test/TableGen/DecoderEmitter/trydecode-emission.td b/llvm/test/TableGen/DecoderEmitter/trydecode-emission.td
index 34d494fb522ed..a39f26a6e715a 100644
--- a/llvm/test/TableGen/DecoderEmitter/trydecode-emission.td
+++ b/llvm/test/TableGen/DecoderEmitter/trydecode-emission.td
@@ -35,11 +35,10 @@ def InstB : TestInstruction {
 
 // CHECK-LABEL: static const uint8_t DecoderTable8[18] = {
 // CHECK-NEXT:    OPC_CheckField, 4, 4, 0,     //  0: check Inst[7:4] == 0x0
-// CHECK-NEXT:    OPC_Scope, 8,                //  4: {
+// CHECK-NEXT:    OPC_Scope, 8,                //  4: try {
 // CHECK-NEXT:    OPC_CheckField, 2, 2, 0,     //  6:  check Inst[3:2] == 0x0
 // CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 0, // 10:  decode to InstB using decoder 0
-// CHECK-NEXT:                                 // 10: }
-// CHECK-NEXT:                                 // 10: {
+// CHECK-NEXT:                                 // 10: } else try {
 // CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 1, // 14:  decode to InstA using decoder 1
 // CHECK-NEXT:                                 // 14: }
 // CHECK-NEXT: };
diff --git a/llvm/test/TableGen/DecoderEmitter/trydecode-emission2.td b/llvm/test/TableGen/DecoderEmitter/trydecode-emission2.td
index 50477543bafd0..0a172d07622ab 100644
--- a/llvm/test/TableGen/DecoderEmitter/trydecode-emission2.td
+++ b/llvm/test/TableGen/DecoderEmitter/trydecode-emission2.td
@@ -33,11 +33,10 @@ def InstB : TestInstruction {
 // CHECK-LABEL: static const uint8_t DecoderTable8[26] = {
 // CHECK-NEXT:    OPC_CheckField, 2, 1, 0,     //  0: check Inst[2:2] == 0x0
 // CHECK-NEXT:    OPC_CheckField, 5, 3, 0,     //  4: check Inst[7:5] == 0x0
-// CHECK-NEXT:    OPC_Scope, 8,                //  8: {
+// CHECK-NEXT:    OPC_Scope, 8,                //  8: try {
 // CHECK-NEXT:    OPC_CheckField, 0, 2, 3,     // 10:  check Inst[1:0] == 0x3
 // CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 0, // 14:  decode to InstB using decoder 0
-// CHECK-NEXT:                                 // 14: }
-// CHECK-NEXT:                                 // 14: {
+// CHECK-NEXT:                                 // 14: } else try {
 // CHECK-NEXT:    OPC_CheckField, 3, 2, 0,     // 18:  check Inst[4:3] == 0x0
 // CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 1, // 22:  decode to InstA using decoder 1
 // CHECK-NEXT:                                 // 22: }
diff --git a/llvm/test/TableGen/DecoderEmitter/trydecode-emission3.td b/llvm/test/TableGen/DecoderEmitter/trydecode-emission3.td
index 1c83b6567a2ee..2e2d1ddb8b8f6 100644
--- a/llvm/test/TableGen/DecoderEmitter/trydecode-emission3.td
+++ b/llvm/test/TableGen/DecoderEmitter/trydecode-emission3.td
@@ -36,11 +36,10 @@ def InstB : TestInstruction {
 
 // CHECK-LABEL: static const uint8_t DecoderTable8[18] = {
 // CHECK-NEXT:    OPC_CheckField, 4, 4, 0,     //  0: check Inst[7:4] == 0x0
-// CHECK-NEXT:    OPC_Scope, 8,                //  4: {
+// CHECK-NEXT:    OPC_Scope, 8,                //  4: try {
 // CHECK-NEXT:    OPC_CheckField, 2, 2, 0,     //  6:  check Inst[3:2] == 0x0
 // CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 0, // 10:  decode to InstB using decoder 0
-// CHECK-NEXT:                                 // 10: }
-// CHECK-NEXT:                                 // 10: {
+// CHECK-NEXT:                                 // 10: } else try {
 // CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 1, // 14:  decode to InstA using decoder 1
 // CHECK-NEXT:                                 // 14: }
 // CHECK-NEXT: };
diff --git a/llvm/test/TableGen/DecoderEmitter/trydecode-emission4.td b/llvm/test/TableGen/DecoderEmitter/trydecode-emission4.td
index 7a93556be55d6..bb0f658a037e8 100644
--- a/llvm/test/TableGen/DecoderEmitter/trydecode-emission4.td
+++ b/llvm/test/TableGen/DecoderEmitter/trydecode-emission4.td
@@ -34,11 +34,10 @@ def InstB : TestInstruction {
 
 // CHECK-LABEL: static const uint8_t DecoderTable512[20] = {
 // CHECK-NEXT:    OPC_CheckField, 250, 3, 4, 0, //  0: check Inst[509:506] == 0x0
-// CHECK-NEXT:    OPC_Scope, 9,                 //  5: {
+// CHECK-NEXT:    OPC_Scope, 9,                 //  5: try {
 // CHECK-NEXT:    OPC_CheckField, 248, 3, 2, 0, //  7:  check Inst[505:504] == 0x0
 // CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 0,  // 12:  decode to InstB using decoder 0
-// CHECK-NEXT:                                  // 12: }
-// CHECK-NEXT:                                  // 12: {
+// CHECK-NEXT:                                  // 12: } else try {
 // CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 1,  // 16:  decode to InstA using decoder 1
 // CHECK-NEXT:                                  // 16: }
 // CHECK-NEXT: };
diff --git a/llvm/utils/TableGen/DecoderTableEmitter.cpp b/llvm/utils/TableGen/DecoderTableEmitter.cpp
index f6e60eaad1e20..69762c4692d0c 100644
--- a/llvm/utils/TableGen/DecoderTableEmitter.cpp
+++ b/llvm/utils/TableGen/DecoderTableEmitter.cpp
@@ -178,17 +178,18 @@ void DecoderTableEmitter::emitCheckAnyNode(const CheckAnyNode *N,
     return;
   }
 
+  ListSeparator LS("} else ");
   for (const DecoderTreeNode *Child : drop_end(N->children())) {
     emitOpcode("OPC_Scope");
     emitULEB128(computeNodeSize(Child));
 
-    emitComment(Indent) << "{\n";
+    emitComment(Indent) << LS << "try {\n";
     emitNode(Child, Indent + 1);
-    emitComment(Indent) << "}\n";
   }
 
   const DecoderTreeNode *Child = *std::prev(N->child_end());
-  emitComment(Indent) << "{\n";
+
+  emitComment(Indent) << LS << "try {\n";
   emitNode(Child, Indent + 1);
   emitComment(Indent) << "}\n";
 }
@@ -272,7 +273,7 @@ void DecoderTableEmitter::emitSoftFailNode(const SoftFailNode *N,
   emitULEB128(NegativeMask);
   TableInfo.HasSoftFail = true;
 
-  emitComment(Indent) << "check softfail";
+  emitComment(Indent) << "softfail";
   OS << " pos=" << format_hex(PositiveMask, 10);
   OS << " neg=" << format_hex(NegativeMask, 10) << '\n';
 }

>From 1892e3fcbb851fed5faff4b32fa7d9853e5c0c34 Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <barannikov88 at gmail.com>
Date: Thu, 18 Sep 2025 19:29:40 +0300
Subject: [PATCH 14/21] Print single-bit bit range in more compact form

---
 .../TableGen/DecoderEmitter/big-filter.td     |  4 +--
 .../DecoderEmitter/trydecode-emission2.td     |  2 +-
 .../DecoderEmitter/var-len-conflict-1.td      |  2 +-
 llvm/utils/TableGen/DecoderTableEmitter.cpp   | 28 +++++++++++++++----
 llvm/utils/TableGen/DecoderTableEmitter.h     |  2 +-
 5 files changed, 27 insertions(+), 11 deletions(-)

diff --git a/llvm/test/TableGen/DecoderEmitter/big-filter.td b/llvm/test/TableGen/DecoderEmitter/big-filter.td
index fa516ad5665a5..168692b6dd1ac 100644
--- a/llvm/test/TableGen/DecoderEmitter/big-filter.td
+++ b/llvm/test/TableGen/DecoderEmitter/big-filter.td
@@ -14,12 +14,12 @@ class I : Instruction {
 // CHECK-LABEL: static const uint8_t DecoderTable128[32] = {
 // CHECK-NEXT:    OPC_SwitchField, 0, 64,      //  0: switch Inst[63:0] {
 // CHECK-NEXT:    1, 8,                        //  3: case 0x1: {
-// CHECK-NEXT:    OPC_CheckField, 127, 1, 1,   //  5:  check Inst[127:127] == 0x1
+// CHECK-NEXT:    OPC_CheckField, 127, 1, 1,   //  5:  check Inst[127] == 0x1
 // CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 0, //  9:  decode to I2 using decoder 0
 // CHECK-NEXT:                                 //  9: }
 // CHECK-NEXT:    255, 255, 255, 255, 255, 255, 255, 255, 255, 1, 0,
 // CHECK-NEXT:                                 // 13: case 0xffffffffffffffff: {
-// CHECK-NEXT:    OPC_CheckField, 127, 1, 0,   // 24:  check Inst[127:127] == 0x0
+// CHECK-NEXT:    OPC_CheckField, 127, 1, 0,   // 24:  check Inst[127] == 0x0
 // CHECK-NEXT:    OPC_Decode, {{[0-9, ]+}}, 0, // 28:  decode to I1 using decoder 0
 // CHECK-NEXT:                                 // 28: }
 // CHECK-NEXT:                                 // 28: } // switch Inst[63:0]
diff --git a/llvm/test/TableGen/DecoderEmitter/trydecode-emission2.td b/llvm/test/TableGen/DecoderEmitter/trydecode-emission2.td
index 0a172d07622ab..40d68061bfc0d 100644
--- a/llvm/test/TableGen/DecoderEmitter/trydecode-emission2.td
+++ b/llvm/test/TableGen/DecoderEmitter/trydecode-emission2.td
@@ -31,7 +31,7 @@ def InstB : TestInstruction {
 }
 
 // CHECK-LABEL: static const uint8_t DecoderTable8[26] = {
-// CHECK-NEXT:    OPC_CheckField, 2, 1, 0,     //  0: check Inst[2:2] == 0x0
+// CHECK-NEXT:    OPC_CheckField, 2, 1, 0,     //  0: check Inst[2] == 0x0
 // CHECK-NEXT:    OPC_CheckField, 5, 3, 0,     //  4: check Inst[7:5] == 0x0
 // CHECK-NEXT:    OPC_Scope, 8,                //  8: try {
 // CHECK-NEXT:    OPC_CheckField, 0, 2, 3,     // 10:  check Inst[1:0] == 0x3
diff --git a/llvm/test/TableGen/DecoderEmitter/var-len-conflict-1.td b/llvm/test/TableGen/DecoderEmitter/var-len-conflict-1.td
index 50e2d6d97f721..ae585d7d2c62c 100644
--- a/llvm/test/TableGen/DecoderEmitter/var-len-conflict-1.td
+++ b/llvm/test/TableGen/DecoderEmitter/var-len-conflict-1.td
@@ -18,7 +18,7 @@ class I : Instruction {
 // 00000001 ________  I16_1
 // 00000010 ________  I16_2
 
-// CHECK: switch Inst[0:0]
+// CHECK: switch Inst[0]
 // CHECK: decode to I8_0
 // CHECK: decode to I8_1
 // CHECK: switch Inst[15:8]
diff --git a/llvm/utils/TableGen/DecoderTableEmitter.cpp b/llvm/utils/TableGen/DecoderTableEmitter.cpp
index 69762c4692d0c..2a468a68da346 100644
--- a/llvm/utils/TableGen/DecoderTableEmitter.cpp
+++ b/llvm/utils/TableGen/DecoderTableEmitter.cpp
@@ -160,7 +160,7 @@ void DecoderTableEmitter::emitULEB128(uint64_t Val) {
   emitByte(Val);
 }
 
-formatted_raw_ostream &DecoderTableEmitter::emitComment(indent Indent) {
+raw_ostream &DecoderTableEmitter::emitComment(indent Indent) {
   constexpr unsigned CommentColumn = 45;
   if (OS.getColumn() > CommentColumn)
     OS << '\n';
@@ -169,6 +169,23 @@ formatted_raw_ostream &DecoderTableEmitter::emitComment(indent Indent) {
   return OS;
 }
 
+namespace {
+
+/// Helper class for printing bit ranges.
+struct BitRange {
+  unsigned MSB, LSB;
+
+  friend raw_ostream &operator<<(raw_ostream &OS, BitRange R) {
+    if (R.MSB == R.LSB)
+      OS << '[' << R.LSB << ']';
+    else
+      OS << '[' << R.MSB << ':' << R.LSB << ']';
+    return OS;
+  }
+};
+
+} // namespace
+
 void DecoderTableEmitter::emitCheckAnyNode(const CheckAnyNode *N,
                                            indent Indent) {
   // TODO: Single-child CheckAny node should be optimized out. For now,
@@ -210,7 +227,7 @@ void DecoderTableEmitter::emitSwitchFieldNode(const SwitchFieldNode *N,
   emitULEB128(LSB);
   emitUInt8(Width);
 
-  emitComment(Indent) << "switch Inst[" << MSB << ':' << LSB << "] {\n";
+  emitComment(Indent) << "switch Inst" << BitRange{MSB, LSB} << " {\n";
 
   for (auto [Val, Child] : drop_end(N->cases())) {
     emitStartLine();
@@ -231,7 +248,7 @@ void DecoderTableEmitter::emitSwitchFieldNode(const SwitchFieldNode *N,
   emitNode(Child, Indent + 1);
   emitComment(Indent) << "}\n";
 
-  emitComment(Indent) << "} // switch Inst[" << MSB << ':' << LSB << "]\n";
+  emitComment(Indent) << "} // switch Inst" << BitRange{MSB, LSB} << "\n";
 }
 
 void DecoderTableEmitter::emitCheckFieldNode(const CheckFieldNode *N,
@@ -246,9 +263,8 @@ void DecoderTableEmitter::emitCheckFieldNode(const CheckFieldNode *N,
   emitUInt8(Width);
   emitULEB128(Val);
 
-  emitComment(Indent);
-  OS << "check Inst[" << MSB << ':' << LSB << "] == " << format_hex(Val, 0)
-     << '\n';
+  emitComment(Indent) << "check Inst" << BitRange{MSB, LSB}
+                      << " == " << format_hex(Val, 0) << '\n';
 }
 
 void DecoderTableEmitter::emitCheckPredicateNode(const CheckPredicateNode *N,
diff --git a/llvm/utils/TableGen/DecoderTableEmitter.h b/llvm/utils/TableGen/DecoderTableEmitter.h
index 1eb1d17210c2c..4eeb91e2b39ae 100644
--- a/llvm/utils/TableGen/DecoderTableEmitter.h
+++ b/llvm/utils/TableGen/DecoderTableEmitter.h
@@ -72,7 +72,7 @@ class DecoderTableEmitter {
   void emitByte(uint8_t Val);
   void emitUInt8(unsigned Val);
   void emitULEB128(uint64_t Val);
-  formatted_raw_ostream &emitComment(indent Indent);
+  raw_ostream &emitComment(indent Indent);
 
   void emitCheckAnyNode(const CheckAnyNode *N, indent Indent);
   void emitCheckAllNode(const CheckAllNode *N, indent Indent);

>From 71a404e7959baaf65ee82520c599afc7d90f0878 Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <barannikov88 at gmail.com>
Date: Thu, 18 Sep 2025 19:30:06 +0300
Subject: [PATCH 15/21] Rename CommentIndex -> LineStartIndex, comment
 index-related members

---
 llvm/utils/TableGen/DecoderTableEmitter.cpp |  4 ++--
 llvm/utils/TableGen/DecoderTableEmitter.h   | 11 ++++++++++-
 2 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/llvm/utils/TableGen/DecoderTableEmitter.cpp b/llvm/utils/TableGen/DecoderTableEmitter.cpp
index 2a468a68da346..764090315519a 100644
--- a/llvm/utils/TableGen/DecoderTableEmitter.cpp
+++ b/llvm/utils/TableGen/DecoderTableEmitter.cpp
@@ -132,7 +132,7 @@ unsigned DecoderTableEmitter::computeTableSize(const DecoderTreeNode *Root,
 }
 
 void DecoderTableEmitter::emitStartLine() {
-  CommentIndex = CurrentIndex;
+  LineStartIndex = CurrentIndex;
   OS.indent(2);
 }
 
@@ -165,7 +165,7 @@ raw_ostream &DecoderTableEmitter::emitComment(indent Indent) {
   if (OS.getColumn() > CommentColumn)
     OS << '\n';
   OS.PadToColumn(CommentColumn);
-  OS << "// " << format_decimal(CommentIndex, IndexWidth) << ": " << Indent;
+  OS << "// " << format_decimal(LineStartIndex, IndexWidth) << ": " << Indent;
   return OS;
 }
 
diff --git a/llvm/utils/TableGen/DecoderTableEmitter.h b/llvm/utils/TableGen/DecoderTableEmitter.h
index 4eeb91e2b39ae..1ca9647d7aed3 100644
--- a/llvm/utils/TableGen/DecoderTableEmitter.h
+++ b/llvm/utils/TableGen/DecoderTableEmitter.h
@@ -49,9 +49,18 @@ struct DecoderTableInfo {
 class DecoderTableEmitter {
   DecoderTableInfo &TableInfo;
   formatted_raw_ostream OS;
+
+  /// The number of positions occupied by the index in the output. Used to
+  /// right-align indices and left-align the text that follows them.
   unsigned IndexWidth;
+
+  /// The current position in the output stream. After the table is emitted,
+  /// this is its size.
   unsigned CurrentIndex;
-  unsigned CommentIndex;
+
+  /// The index of the first byte of the table row. Used as a label in the
+  /// comment following the row.
+  unsigned LineStartIndex;
 
 public:
   DecoderTableEmitter(DecoderTableInfo &TableInfo, raw_ostream &OS)

>From bc91cb2ee1d2714b0f4ab0a3ec01a8f69983281f Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <barannikov88 at gmail.com>
Date: Thu, 18 Sep 2025 19:41:54 +0300
Subject: [PATCH 16/21] Add a TODO comment to emitCheckAllNode

---
 llvm/utils/TableGen/DecoderTableEmitter.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/llvm/utils/TableGen/DecoderTableEmitter.cpp b/llvm/utils/TableGen/DecoderTableEmitter.cpp
index 764090315519a..a3caf573b49c4 100644
--- a/llvm/utils/TableGen/DecoderTableEmitter.cpp
+++ b/llvm/utils/TableGen/DecoderTableEmitter.cpp
@@ -213,6 +213,9 @@ void DecoderTableEmitter::emitCheckAnyNode(const CheckAnyNode *N,
 
 void DecoderTableEmitter::emitCheckAllNode(const CheckAllNode *N,
                                            indent Indent) {
+  // TODO: Single-child CheckAll should be optimized out.
+  // TODO: Nested CheckAll nodes should be flattened.
+  // TODO: Sibling CheckAll and other Check* nodes should be merged together.
   for (const DecoderTreeNode *Child : N->children())
     emitNode(Child, Indent);
 }

>From 2f68c1af049fdfda0f2422c4fead40512d6ed826 Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <barannikov88 at gmail.com>
Date: Thu, 18 Sep 2025 20:13:37 +0300
Subject: [PATCH 17/21] Explain why the last child of CheckAny/SwitchField is
 emitted differently

---
 llvm/utils/TableGen/DecoderTableEmitter.cpp | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/llvm/utils/TableGen/DecoderTableEmitter.cpp b/llvm/utils/TableGen/DecoderTableEmitter.cpp
index a3caf573b49c4..379d4ba5a2f50 100644
--- a/llvm/utils/TableGen/DecoderTableEmitter.cpp
+++ b/llvm/utils/TableGen/DecoderTableEmitter.cpp
@@ -204,6 +204,8 @@ void DecoderTableEmitter::emitCheckAnyNode(const CheckAnyNode *N,
     emitNode(Child, Indent + 1);
   }
 
+  // Don't emit OPC_Scope for the last child so that we leave the current scope
+  // if it fails. Otherwise, we would need some kind of OPC_LeaveScope opcode.
   const DecoderTreeNode *Child = *std::prev(N->child_end());
 
   emitComment(Indent) << LS << "try {\n";
@@ -242,6 +244,10 @@ void DecoderTableEmitter::emitSwitchFieldNode(const SwitchFieldNode *N,
     emitComment(Indent) << "}\n";
   }
 
+  // Don't emit the size of the last child and instead emit a sentinel value,
+  // which tells the interpreter that this is the last case. The interpreter
+  // doesn't need to know its size because SwitchField node never falls through
+  // (we either successfully decode an instruction, or leave the current scope).
   auto [Val, Child] = *std::prev(N->case_end());
   emitStartLine();
   emitULEB128(Val);

>From 38c5af0732e0fc61f988ae9f771895480223deb4 Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <barannikov88 at gmail.com>
Date: Thu, 18 Sep 2025 21:50:40 +0300
Subject: [PATCH 18/21] Move predicate/decoder unification to tree construction
 stage

---
 llvm/utils/TableGen/DecoderEmitter.cpp      | 34 +++++++-------
 llvm/utils/TableGen/DecoderTableEmitter.cpp | 49 ++-------------------
 llvm/utils/TableGen/DecoderTableEmitter.h   | 33 +-------------
 llvm/utils/TableGen/DecoderTree.h           | 44 ++++++++++++++----
 4 files changed, 58 insertions(+), 102 deletions(-)

diff --git a/llvm/utils/TableGen/DecoderEmitter.cpp b/llvm/utils/TableGen/DecoderEmitter.cpp
index 54cad9a68d11f..bbca3dca3f415 100644
--- a/llvm/utils/TableGen/DecoderEmitter.cpp
+++ b/llvm/utils/TableGen/DecoderEmitter.cpp
@@ -1177,13 +1177,14 @@ static DecodeStatus decodeInstruction(const uint8_t DecodeTable[], MCInst &MI,
 namespace {
 
 class DecoderTreeBuilder {
+  DecoderContext &Ctx;
   const CodeGenTarget &Target;
   ArrayRef<InstructionEncoding> Encodings;
 
 public:
-  DecoderTreeBuilder(const CodeGenTarget &Target,
+  DecoderTreeBuilder(DecoderContext &Ctx, const CodeGenTarget &Target,
                      ArrayRef<InstructionEncoding> Encodings)
-      : Target(Target), Encodings(Encodings) {}
+      : Ctx(Ctx), Target(Target), Encodings(Encodings) {}
 
   std::unique_ptr<DecoderTreeNode> buildTree(ArrayRef<unsigned> EncodingIDs);
 
@@ -1208,8 +1209,10 @@ DecoderTreeBuilder::convertSingleton(unsigned EncodingID,
   auto N = std::make_unique<CheckAllNode>();
 
   std::string Predicate = getPredicateString(Encoding, Target.getName());
-  if (!Predicate.empty())
-    N->addChild(std::make_unique<CheckPredicateNode>(std::move(Predicate)));
+  if (!Predicate.empty()) {
+    unsigned PredicateIndex = Ctx.getPredicateIndex(Predicate);
+    N->addChild(std::make_unique<CheckPredicateNode>(PredicateIndex));
+  }
 
   std::vector<EncodingIsland> Islands =
       getIslands(Encoding.getMandatoryBits(), FilterBits);
@@ -1227,8 +1230,8 @@ DecoderTreeBuilder::convertSingleton(unsigned EncodingID,
                                                NegativeMask.getZExtValue()));
   }
 
-  std::string DecoderString = getDecoderString(Encoding);
-  N->addChild(std::make_unique<DecodeNode>(Encoding, std::move(DecoderString)));
+  unsigned DecoderIndex = Ctx.getDecoderIndex(getDecoderString(Encoding));
+  N->addChild(std::make_unique<DecodeNode>(Encoding, DecoderIndex));
 
   return N;
 }
@@ -1508,16 +1511,13 @@ template <typename T> constexpr uint32_t InsnBitWidth = 0;
     PrintFatalError(
         "Cannot specialize decoders for variable length instuctions");
 
-  DecoderTableInfo TableInfo{};
-  DecoderTreeBuilder TreeBuilder(Target, Encodings);
+  DecoderContext Ctx;
+  DecoderTreeBuilder TreeBuilder(Ctx, Target, Encodings);
+
+  DecoderTableInfo TableInfo;
   DecoderTableEmitter TableEmitter(TableInfo, OS);
 
   // Emit a table for each (namespace, hwmode, bitwidth) combination.
-  // Predicates and decoders are shared across the tables to provide more
-  // opportunities for uniqueness. If SpecializeDecodersPerBitwidth is enabled,
-  // decoders are shared across all tables for a given bitwidth, else they are
-  // shared across all tables. Predicates are always shared across all tables.
-  //
   // Entries in `EncMap` are already sorted by bitwidth. So bucketing per
   // bitwidth can be done on-the-fly as we iterate over the map.
   bool HasConflict = false;
@@ -1547,8 +1547,8 @@ template <typename T> constexpr uint32_t InsnBitWidth = 0;
     // Each BitWidth get's its own decoders and decoder function if
     // SpecializeDecodersPerBitwidth is enabled.
     if (SpecializeDecodersPerBitwidth) {
-      emitDecoderFunction(OS, TableInfo.Decoders, BitWidth);
-      TableInfo.Decoders.clear();
+      emitDecoderFunction(OS, Ctx.Decoders, BitWidth);
+      Ctx.Decoders.clear();
     }
   }
 
@@ -1558,11 +1558,11 @@ template <typename T> constexpr uint32_t InsnBitWidth = 0;
   // Emit the decoder function for the last bucket. This will also emit the
   // single decoder function if SpecializeDecodersPerBitwidth = false.
   if (!SpecializeDecodersPerBitwidth)
-    emitDecoderFunction(OS, TableInfo.Decoders, 0);
+    emitDecoderFunction(OS, Ctx.Decoders, 0);
 
   // Emit the predicate function.
   if (TableInfo.HasCheckPredicate)
-    emitPredicateFunction(OS, TableInfo.Predicates);
+    emitPredicateFunction(OS, Ctx.Predicates);
 
   // Emit the main entry point for the decoder, decodeInstruction().
   emitDecodeInstruction(OS, IsVarLenInst, TableInfo);
diff --git a/llvm/utils/TableGen/DecoderTableEmitter.cpp b/llvm/utils/TableGen/DecoderTableEmitter.cpp
index 379d4ba5a2f50..7017f790c59f3 100644
--- a/llvm/utils/TableGen/DecoderTableEmitter.cpp
+++ b/llvm/utils/TableGen/DecoderTableEmitter.cpp
@@ -14,43 +14,6 @@
 
 using namespace llvm;
 
-void DecoderTableEmitter::analyzeNode(const DecoderTreeNode *Node) const {
-  switch (Node->getKind()) {
-  case DecoderTreeNode::CheckAny: {
-    const auto *N = static_cast<const CheckAnyNode *>(Node);
-    for (const DecoderTreeNode *Child : N->children())
-      analyzeNode(Child);
-    break;
-  }
-  case DecoderTreeNode::CheckAll: {
-    const auto *N = static_cast<const CheckAllNode *>(Node);
-    for (const DecoderTreeNode *Child : N->children())
-      analyzeNode(Child);
-    break;
-  }
-  case DecoderTreeNode::CheckField:
-    break;
-  case DecoderTreeNode::SwitchField: {
-    const auto *N = static_cast<const SwitchFieldNode *>(Node);
-    for (const DecoderTreeNode *Child : make_second_range(N->cases()))
-      analyzeNode(Child);
-    break;
-  }
-  case DecoderTreeNode::CheckPredicate: {
-    const auto *N = static_cast<const CheckPredicateNode *>(Node);
-    TableInfo.insertPredicate(N->getPredicateString());
-    break;
-  }
-  case DecoderTreeNode::SoftFail:
-    break;
-  case DecoderTreeNode::Decode: {
-    const auto *N = static_cast<const DecodeNode *>(Node);
-    TableInfo.insertDecoder(N->getDecoderString());
-    break;
-  }
-  }
-}
-
 unsigned
 DecoderTableEmitter::computeNodeSize(const DecoderTreeNode *Node) const {
   // To make the arithmetic below clearer.
@@ -102,8 +65,7 @@ DecoderTableEmitter::computeNodeSize(const DecoderTreeNode *Node) const {
   }
   case DecoderTreeNode::CheckPredicate: {
     const auto *N = static_cast<const CheckPredicateNode *>(Node);
-    unsigned PredicateIndex =
-        TableInfo.getPredicateIndex(N->getPredicateString());
+    unsigned PredicateIndex = N->getPredicateIndex();
     return OpcodeSize + getULEB128Size(PredicateIndex);
   }
   case DecoderTreeNode::SoftFail: {
@@ -114,7 +76,7 @@ DecoderTableEmitter::computeNodeSize(const DecoderTreeNode *Node) const {
   case DecoderTreeNode::Decode: {
     const auto *N = static_cast<const DecodeNode *>(Node);
     unsigned InstOpcode = N->getEncoding().getInstruction()->EnumVal;
-    unsigned DecoderIndex = TableInfo.getDecoderIndex(N->getDecoderString());
+    unsigned DecoderIndex = N->getDecoderIndex();
     return OpcodeSize + getULEB128Size(InstOpcode) +
            getULEB128Size(DecoderIndex);
   }
@@ -278,8 +240,7 @@ void DecoderTableEmitter::emitCheckFieldNode(const CheckFieldNode *N,
 
 void DecoderTableEmitter::emitCheckPredicateNode(const CheckPredicateNode *N,
                                                  indent Indent) {
-  unsigned PredicateIndex =
-      TableInfo.getPredicateIndex(N->getPredicateString());
+  unsigned PredicateIndex = N->getPredicateIndex();
 
   emitOpcode("OPC_CheckPredicate");
   emitULEB128(PredicateIndex);
@@ -306,7 +267,7 @@ void DecoderTableEmitter::emitSoftFailNode(const SoftFailNode *N,
 void DecoderTableEmitter::emitDecodeNode(const DecodeNode *N, indent Indent) {
   const InstructionEncoding &Encoding = N->getEncoding();
   unsigned InstOpcode = Encoding.getInstruction()->EnumVal;
-  unsigned DecoderIndex = TableInfo.getDecoderIndex(N->getDecoderString());
+  unsigned DecoderIndex = N->getDecoderIndex();
 
   emitOpcode("OPC_Decode");
   emitULEB128(InstOpcode);
@@ -339,8 +300,6 @@ void DecoderTableEmitter::emitNode(const DecoderTreeNode *N, indent Indent) {
 
 void DecoderTableEmitter::emitTable(StringRef TableName, unsigned BitWidth,
                                     const DecoderTreeNode *Root) {
-  analyzeNode(Root);
-
   unsigned TableSize = computeTableSize(Root, BitWidth);
   OS << "static const uint8_t " << TableName << "[" << TableSize << "] = {\n";
 
diff --git a/llvm/utils/TableGen/DecoderTableEmitter.h b/llvm/utils/TableGen/DecoderTableEmitter.h
index 1ca9647d7aed3..22e7f9042a6d9 100644
--- a/llvm/utils/TableGen/DecoderTableEmitter.h
+++ b/llvm/utils/TableGen/DecoderTableEmitter.h
@@ -10,40 +10,13 @@
 #define LLVM_UTILS_TABLEGEN_DECODERTABLEEMITTER_H
 
 #include "DecoderTree.h"
-#include "llvm/ADT/CachedHashString.h"
-#include "llvm/ADT/SetVector.h"
 #include "llvm/Support/FormattedStream.h"
 
 namespace llvm {
 
-using PredicateSet = SetVector<CachedHashString>;
-using DecoderSet = SetVector<CachedHashString>;
-
 struct DecoderTableInfo {
-  PredicateSet Predicates;
-  DecoderSet Decoders;
-  bool HasCheckPredicate;
-  bool HasSoftFail;
-
-  void insertPredicate(StringRef Predicate) {
-    Predicates.insert(CachedHashString(Predicate));
-  }
-
-  void insertDecoder(StringRef Decoder) {
-    Decoders.insert(CachedHashString(Decoder));
-  }
-
-  unsigned getPredicateIndex(StringRef Predicate) const {
-    PredicateSet::const_iterator I = find(Predicates, Predicate);
-    assert(I != Predicates.end());
-    return std::distance(Predicates.begin(), I);
-  }
-
-  unsigned getDecoderIndex(StringRef Decoder) const {
-    DecoderSet::const_iterator I = find(Decoders, Decoder);
-    assert(I != Decoders.end());
-    return std::distance(Decoders.begin(), I);
-  }
+  bool HasCheckPredicate = false;
+  bool HasSoftFail = false;
 };
 
 class DecoderTableEmitter {
@@ -70,8 +43,6 @@ class DecoderTableEmitter {
                  const DecoderTreeNode *Root);
 
 private:
-  void analyzeNode(const DecoderTreeNode *Node) const;
-
   unsigned computeNodeSize(const DecoderTreeNode *Node) const;
   unsigned computeTableSize(const DecoderTreeNode *Root,
                             unsigned BitWidth) const;
diff --git a/llvm/utils/TableGen/DecoderTree.h b/llvm/utils/TableGen/DecoderTree.h
index a7102c3809d0f..7c09025468e59 100644
--- a/llvm/utils/TableGen/DecoderTree.h
+++ b/llvm/utils/TableGen/DecoderTree.h
@@ -9,7 +9,9 @@
 #ifndef LLVM_UTILS_TABLEGEN_DECODERTREE_H
 #define LLVM_UTILS_TABLEGEN_DECODERTREE_H
 
+#include "llvm/ADT/CachedHashString.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SetVector.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
 #include <map>
@@ -19,6 +21,31 @@ namespace llvm {
 
 class InstructionEncoding;
 
+using PredicateSet = SetVector<CachedHashString>;
+using DecoderSet = SetVector<CachedHashString>;
+
+/// Context shared across decoder trees.
+/// Predicates and decoders are shared across decoder trees to provide more
+/// opportunities for uniqueness. If SpecializeDecodersPerBitwidth is enabled,
+/// decoders are shared across all trees for a given bitwidth, else they are
+/// shared across all trees. Predicates are always shared across all trees.
+struct DecoderContext {
+  PredicateSet Predicates;
+  DecoderSet Decoders;
+
+  unsigned getPredicateIndex(StringRef Predicate) {
+    Predicates.insert(CachedHashString(Predicate));
+    PredicateSet::const_iterator I = find(Predicates, Predicate);
+    return std::distance(Predicates.begin(), I);
+  }
+
+  unsigned getDecoderIndex(StringRef Decoder) {
+    Decoders.insert(CachedHashString(Decoder));
+    DecoderSet::const_iterator I = find(Decoders, Decoder);
+    return std::distance(Decoders.begin(), I);
+  }
+};
+
 class DecoderTreeNode {
 public:
   virtual ~DecoderTreeNode();
@@ -140,14 +167,13 @@ class SwitchFieldNode : public DecoderTreeNode {
 };
 
 class CheckPredicateNode : public DecoderTreeNode {
-  std::string PredicateString;
+  unsigned PredicateIndex;
 
 public:
-  explicit CheckPredicateNode(std::string PredicateString)
-      : DecoderTreeNode(CheckPredicate),
-        PredicateString(std::move(PredicateString)) {}
+  explicit CheckPredicateNode(unsigned PredicateIndex)
+      : DecoderTreeNode(CheckPredicate), PredicateIndex(PredicateIndex) {}
 
-  StringRef getPredicateString() const { return PredicateString; }
+  unsigned getPredicateIndex() const { return PredicateIndex; }
 };
 
 class SoftFailNode : public DecoderTreeNode {
@@ -164,16 +190,16 @@ class SoftFailNode : public DecoderTreeNode {
 
 class DecodeNode : public DecoderTreeNode {
   const InstructionEncoding &Encoding;
-  std::string DecoderString;
+  unsigned DecoderIndex;
 
 public:
-  DecodeNode(const InstructionEncoding &Encoding, std::string DecoderString)
+  DecodeNode(const InstructionEncoding &Encoding, unsigned DecoderIndex)
       : DecoderTreeNode(Decode), Encoding(Encoding),
-        DecoderString(std::move(DecoderString)) {}
+        DecoderIndex(DecoderIndex) {}
 
   const InstructionEncoding &getEncoding() const { return Encoding; }
 
-  StringRef getDecoderString() const { return DecoderString; }
+  unsigned getDecoderIndex() const { return DecoderIndex; }
 };
 
 } // namespace llvm

>From d7b6fcfb0afd1d2354d2ead9d8b370056ab912a8 Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <barannikov88 at gmail.com>
Date: Fri, 19 Sep 2025 00:50:19 +0300
Subject: [PATCH 19/21] Really add a comment

---
 llvm/utils/TableGen/DecoderTableEmitter.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/llvm/utils/TableGen/DecoderTableEmitter.cpp b/llvm/utils/TableGen/DecoderTableEmitter.cpp
index 7017f790c59f3..c8128977a5d44 100644
--- a/llvm/utils/TableGen/DecoderTableEmitter.cpp
+++ b/llvm/utils/TableGen/DecoderTableEmitter.cpp
@@ -58,6 +58,8 @@ DecoderTableEmitter::computeNodeSize(const DecoderTreeNode *Node) const {
       Size += getULEB128Size(Val) + getULEB128Size(ChildSize) + ChildSize;
     }
 
+    // The last child is emitted with sentinel value 0 instead of the size.
+    // See the comment in emitSwitchFieldNode.
     auto [Val, Child] = *std::prev(N->case_end());
     unsigned ChildSize = computeNodeSize(Child);
     Size += getULEB128Size(Val) + getULEB128Size(0) + ChildSize;

>From 3ed446e7eb62edf28d36c541a3042294073e224c Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <barannikov88 at gmail.com>
Date: Fri, 19 Sep 2025 01:15:43 +0300
Subject: [PATCH 20/21] Remove InstructionEncoding from DecodeNode

We only need encoding name and instruction opcode.
---
 llvm/utils/TableGen/DecoderEmitter.cpp      |  3 ++-
 llvm/utils/TableGen/DecoderTableEmitter.cpp | 15 ++++++---------
 llvm/utils/TableGen/DecoderTree.h           | 13 ++++++++-----
 3 files changed, 16 insertions(+), 15 deletions(-)

diff --git a/llvm/utils/TableGen/DecoderEmitter.cpp b/llvm/utils/TableGen/DecoderEmitter.cpp
index bbca3dca3f415..54c66150771ad 100644
--- a/llvm/utils/TableGen/DecoderEmitter.cpp
+++ b/llvm/utils/TableGen/DecoderEmitter.cpp
@@ -1231,7 +1231,8 @@ DecoderTreeBuilder::convertSingleton(unsigned EncodingID,
   }
 
   unsigned DecoderIndex = Ctx.getDecoderIndex(getDecoderString(Encoding));
-  N->addChild(std::make_unique<DecodeNode>(Encoding, DecoderIndex));
+  N->addChild(std::make_unique<DecodeNode>(
+      Encoding.getName(), Encoding.getInstruction()->EnumVal, DecoderIndex));
 
   return N;
 }
diff --git a/llvm/utils/TableGen/DecoderTableEmitter.cpp b/llvm/utils/TableGen/DecoderTableEmitter.cpp
index c8128977a5d44..bd587faaa405b 100644
--- a/llvm/utils/TableGen/DecoderTableEmitter.cpp
+++ b/llvm/utils/TableGen/DecoderTableEmitter.cpp
@@ -7,8 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "DecoderTableEmitter.h"
-#include "Common/CodeGenInstruction.h"
-#include "Common/InstructionEncoding.h"
+#include "llvm/ADT/StringExtras.h"
 #include "llvm/Support/Format.h"
 #include "llvm/Support/LEB128.h"
 
@@ -77,10 +76,8 @@ DecoderTableEmitter::computeNodeSize(const DecoderTreeNode *Node) const {
   }
   case DecoderTreeNode::Decode: {
     const auto *N = static_cast<const DecodeNode *>(Node);
-    unsigned InstOpcode = N->getEncoding().getInstruction()->EnumVal;
-    unsigned DecoderIndex = N->getDecoderIndex();
-    return OpcodeSize + getULEB128Size(InstOpcode) +
-           getULEB128Size(DecoderIndex);
+    return OpcodeSize + getULEB128Size(N->getInstOpcode()) +
+           getULEB128Size(N->getDecoderIndex());
   }
   }
   llvm_unreachable("Unknown node kind");
@@ -267,15 +264,15 @@ void DecoderTableEmitter::emitSoftFailNode(const SoftFailNode *N,
 }
 
 void DecoderTableEmitter::emitDecodeNode(const DecodeNode *N, indent Indent) {
-  const InstructionEncoding &Encoding = N->getEncoding();
-  unsigned InstOpcode = Encoding.getInstruction()->EnumVal;
+  StringRef EncodingName = N->getEncodingName();
+  unsigned InstOpcode = N->getInstOpcode();
   unsigned DecoderIndex = N->getDecoderIndex();
 
   emitOpcode("OPC_Decode");
   emitULEB128(InstOpcode);
   emitULEB128(DecoderIndex);
 
-  emitComment(Indent) << "decode to " << Encoding.getName() << " using decoder "
+  emitComment(Indent) << "decode to " << EncodingName << " using decoder "
                       << DecoderIndex << '\n';
 }
 
diff --git a/llvm/utils/TableGen/DecoderTree.h b/llvm/utils/TableGen/DecoderTree.h
index 7c09025468e59..e90127b1e79c6 100644
--- a/llvm/utils/TableGen/DecoderTree.h
+++ b/llvm/utils/TableGen/DecoderTree.h
@@ -189,15 +189,18 @@ class SoftFailNode : public DecoderTreeNode {
 };
 
 class DecodeNode : public DecoderTreeNode {
-  const InstructionEncoding &Encoding;
+  StringRef EncodingName;
+  unsigned InstOpcode;
   unsigned DecoderIndex;
 
 public:
-  DecodeNode(const InstructionEncoding &Encoding, unsigned DecoderIndex)
-      : DecoderTreeNode(Decode), Encoding(Encoding),
-        DecoderIndex(DecoderIndex) {}
+  DecodeNode(StringRef EncodingName, unsigned InstOpcode, unsigned DecoderIndex)
+      : DecoderTreeNode(Decode), EncodingName(EncodingName),
+        InstOpcode(InstOpcode), DecoderIndex(DecoderIndex) {}
 
-  const InstructionEncoding &getEncoding() const { return Encoding; }
+  StringRef getEncodingName() const { return EncodingName; }
+
+  unsigned getInstOpcode() const { return InstOpcode; }
 
   unsigned getDecoderIndex() const { return DecoderIndex; }
 };

>From 18fafdc9f78d9cf67f84e266bf2d538df22eaf27 Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <barannikov88 at gmail.com>
Date: Fri, 19 Sep 2025 08:36:55 +0300
Subject: [PATCH 21/21] Document tree nodes

---
 llvm/utils/TableGen/DecoderTree.h | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/llvm/utils/TableGen/DecoderTree.h b/llvm/utils/TableGen/DecoderTree.h
index e90127b1e79c6..91d70a8393d86 100644
--- a/llvm/utils/TableGen/DecoderTree.h
+++ b/llvm/utils/TableGen/DecoderTree.h
@@ -69,6 +69,7 @@ class DecoderTreeNode {
   KindTy Kind;
 };
 
+/// Common base class for nodes with multiple children.
 class CheckManyNode : public DecoderTreeNode {
   SmallVector<std::unique_ptr<DecoderTreeNode>, 0> Children;
 
@@ -101,16 +102,22 @@ class CheckManyNode : public DecoderTreeNode {
   }
 };
 
+/// Executes child nodes one by one until one of them succeeds or all fail.
+/// The node fails if all child nodes fail. It never succeeds, because if a
+/// child node succeeds, it does not return.
 class CheckAnyNode : public CheckManyNode {
 public:
   CheckAnyNode() : CheckManyNode(CheckAny) {}
 };
 
+/// Executes child nodes one by one until one of them fails all all succeed.
+/// The node fails if any of the child nodes fails.
 class CheckAllNode : public CheckManyNode {
 public:
   CheckAllNode() : CheckManyNode(CheckAll) {}
 };
 
+/// Checks the value of encoding bits in the specified range.
 class CheckFieldNode : public DecoderTreeNode {
   unsigned StartBit;
   unsigned NumBits;
@@ -128,6 +135,10 @@ class CheckFieldNode : public DecoderTreeNode {
   uint64_t getValue() const { return Value; }
 };
 
+/// Switch based on the value of encoding bits in the specified range.
+/// If the value of the bits in the range doesn't match any of the cases,
+/// the node fails. This is semantically equivalent to CheckAny node where
+/// every child is a CheckField node, but is faster.
 class SwitchFieldNode : public DecoderTreeNode {
   unsigned StartBit;
   unsigned NumBits;
@@ -166,6 +177,7 @@ class SwitchFieldNode : public DecoderTreeNode {
   }
 };
 
+/// Checks that the instruction to be decoded has its predicates satisfied.
 class CheckPredicateNode : public DecoderTreeNode {
   unsigned PredicateIndex;
 
@@ -176,6 +188,8 @@ class CheckPredicateNode : public DecoderTreeNode {
   unsigned getPredicateIndex() const { return PredicateIndex; }
 };
 
+/// Checks if the encoding bits are correct w.r.t. SoftFail semantics.
+/// This is the only node that can never fail.
 class SoftFailNode : public DecoderTreeNode {
   uint64_t PositiveMask, NegativeMask;
 
@@ -188,6 +202,7 @@ class SoftFailNode : public DecoderTreeNode {
   uint64_t getNegativeMask() const { return NegativeMask; }
 };
 
+/// Attempts to decode the specified encoding as the specified instruction.
 class DecodeNode : public DecoderTreeNode {
   StringRef EncodingName;
   unsigned InstOpcode;



More information about the llvm-commits mailing list