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

Sergei Barannikov via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 17 16:54:15 PDT 2025


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

>From 9fefd91bfa077109b87be35c2e66c7322b4f048c 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 1/8] tmp

---
 llvm/include/llvm/MC/MCDecoderOps.h    |   18 +-
 llvm/utils/TableGen/DecoderEmitter.cpp | 1150 ++++++++++++++----------
 2 files changed, 658 insertions(+), 510 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 681f4cc9614eb..f46c17e90c6bd 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,51 +1085,34 @@ 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");
       break;
     }
-    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");
-      break;
-    }
-    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) {
-        Ptr = SkipTo;
-        LLVM_DEBUG(dbgs() << "continuing at " << Ptr - DecodeTable << '\n');
+      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;
       }
-      break;
-    }
-    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) {
+      if (FieldValue == CaseValue) {
+        LLVM_DEBUG(dbgs() << Loc << ": OPC_SwitchField(" << Start << ", " << Len
+                          << "): " << FieldValue << '\n');
+      } else {
         if (ScopeStack.empty()) {
           LLVM_DEBUG(dbgs() << "returning Fail\n");
           return MCDisassembler::Fail;
@@ -1705,6 +1230,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,
@@ -1872,6 +2010,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);
@@ -1943,7 +2096,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) {
@@ -1957,6 +2109,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.
@@ -1964,12 +2119,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 a4e45bd59aa55a225d44c9981b3caab6c86b8f26 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 2/8] 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 93079e01eb0d12aec5a9d72b2df5de0af91aa3bb 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 3/8] 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 f46c17e90c6bd..3a9a0a7342e29 100644
--- a/llvm/utils/TableGen/DecoderEmitter.cpp
+++ b/llvm/utils/TableGen/DecoderEmitter.cpp
@@ -1255,7 +1255,7 @@ class DecoderTreeNode {
   KindTy Kind;
 };
 
-class CheckAnyNode : public DecoderTreeNode {
+class CheckManyNode : public DecoderTreeNode {
   SmallVector<std::unique_ptr<DecoderTreeNode>, 0> Children;
 
   static const DecoderTreeNode *
@@ -1263,11 +1263,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,
@@ -1286,35 +1287,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 {
@@ -1499,8 +1479,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;
 }
@@ -1524,15 +1504,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;
 }
@@ -1576,13 +1556,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()));
   }
@@ -1595,12 +1579,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);
@@ -1616,18 +1601,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 a5dd2abb0c33cf0df2403d3d932f4fefe8fb2373 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 4/8] 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 3a9a0a7342e29..b44608152896e 100644
--- a/llvm/utils/TableGen/DecoderEmitter.cpp
+++ b/llvm/utils/TableGen/DecoderEmitter.cpp
@@ -1399,18 +1399,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 {
@@ -1454,8 +1455,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>();
 
@@ -1485,34 +1486,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 e68a5473456680e27b79031bed7cb9e0820f071e 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 5/8] 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 b44608152896e..a1675eccc5eec 100644
--- a/llvm/utils/TableGen/DecoderEmitter.cpp
+++ b/llvm/utils/TableGen/DecoderEmitter.cpp
@@ -1565,11 +1565,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 af1211ff209c6538c5ba4177ecb561f6f069318b 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 6/8] 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 a1675eccc5eec..3f22d5080874a 100644
--- a/llvm/utils/TableGen/DecoderEmitter.cpp
+++ b/llvm/utils/TableGen/DecoderEmitter.cpp
@@ -1399,7 +1399,7 @@ class DecoderTreeBuilder {
       : Target(Target), Encodings(Encodings) {}
 
   std::unique_ptr<DecoderTreeNode> buildTree(const FilterChooser &FC) {
-    return convertFilterChooser(FC);
+    return convertFilterChooser(&FC);
   }
 
 private:
@@ -1411,7 +1411,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 {
@@ -1493,27 +1493,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 4916fe55d9914f0a4dbbf3adb8bf44c0cfeff063 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 7/8] 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 3f22d5080874a..425e10824c5a0 100644
--- a/llvm/utils/TableGen/DecoderEmitter.cpp
+++ b/llvm/utils/TableGen/DecoderEmitter.cpp
@@ -1564,6 +1564,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.
@@ -1672,6 +1675,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));
@@ -1682,7 +1692,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 2d49938c2a8be4ed10f2392d01cc0665c24a776a 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 8/8] 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 425e10824c5a0..41c27e0d72170 100644
--- a/llvm/utils/TableGen/DecoderEmitter.cpp
+++ b/llvm/utils/TableGen/DecoderEmitter.cpp
@@ -1682,6 +1682,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));



More information about the llvm-commits mailing list