[llvm] POC decoder namespace coalescing (PR #157753)

Rahul Joshi via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 9 14:48:38 PDT 2025


https://github.com/jurahul created https://github.com/llvm/llvm-project/pull/157753

None

>From 6ca367404b66ccc555cb7d4a025c6f5a94709ef0 Mon Sep 17 00:00:00 2001
From: Rahul Joshi <rjoshi at nvidia.com>
Date: Mon, 8 Sep 2025 19:53:02 -0700
Subject: [PATCH] POC

---
 llvm/include/llvm/Target/Target.td            |  11 +
 llvm/lib/Bitcode/Reader/MetadataLoader.cpp    |   3 -
 llvm/lib/Target/AArch64/AArch64.td            |   5 +-
 .../Disassembler/AArch64Disassembler.cpp      |   2 +-
 llvm/lib/Target/AMDGPU/AMDGPU.td              |   5 +
 .../Disassembler/AMDGPUDisassembler.cpp       |  31 +-
 .../RISCV/Disassembler/RISCVDisassembler.cpp  |   8 +-
 llvm/lib/Target/RISCV/RISCV.td                |   5 +
 llvm/lib/Target/Sparc/SparcInstrInfo.td       |   5 +-
 .../Sparc/sparc-special-registers.txt         |  12 +-
 .../FixedLenDecoderEmitter/conflict.td        |  25 +-
 llvm/utils/TableGen/DecoderEmitter.cpp        | 299 +++++++++++++++---
 12 files changed, 330 insertions(+), 81 deletions(-)

diff --git a/llvm/include/llvm/Target/Target.td b/llvm/include/llvm/Target/Target.td
index 38c3b6064d267..4551321e2bbbd 100644
--- a/llvm/include/llvm/Target/Target.td
+++ b/llvm/include/llvm/Target/Target.td
@@ -1154,6 +1154,17 @@ class InstrInfo {
   //
   // This option is a temporary migration help. It will go away.
   bit guessInstructionProperties = true;
+
+  // Lists of decoder namespaces to coalesce. Each list should consist of one
+  // or more names, and the meaning is that each namespace is coalesced with
+  // the first namespace in the list. And the order of the namespaces in the
+  // list is the attempt order. As an example, if we have
+  //  [ "", "X", "Y" ]
+  //   Then namespaces X and Y are coalesced with "", and the decoding is
+  //   attempted in the order "", then X and then Y.
+  // FIXME: Does this need to be done per HWmode, since the decoder tables
+  // are generated per HW mode.
+  list<list<string>> CoalesceDecoderNamespaces = [[]];
 }
 
 // Standard Pseudo Instructions.
diff --git a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
index a5cedadd30981..532f08926a9e4 100644
--- a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
+++ b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
@@ -58,9 +58,6 @@
 #include <tuple>
 #include <utility>
 #include <vector>
-namespace llvm {
-class Argument;
-}
 
 using namespace llvm;
 
diff --git a/llvm/lib/Target/AArch64/AArch64.td b/llvm/lib/Target/AArch64/AArch64.td
index 86f95488e6bb7..fe63757dc0098 100644
--- a/llvm/lib/Target/AArch64/AArch64.td
+++ b/llvm/lib/Target/AArch64/AArch64.td
@@ -40,7 +40,10 @@ include "AArch64SchedPredExynos.td"
 include "AArch64SchedPredNeoverse.td"
 include "AArch64Combine.td"
 
-def AArch64InstrInfo : InstrInfo;
+def AArch64InstrInfo : InstrInfo {
+  // Coalesce "Fallback" and default "" namespaces.
+  let CoalesceDecoderNamespaces = [["", "Fallback" ]];
+}
 
 //===----------------------------------------------------------------------===//
 // Named operands for MRS/MSR/TLBI/...
diff --git a/llvm/lib/Target/AArch64/Disassembler/AArch64Disassembler.cpp b/llvm/lib/Target/AArch64/Disassembler/AArch64Disassembler.cpp
index 8c1e9f61693fb..a3ab01449893b 100644
--- a/llvm/lib/Target/AArch64/Disassembler/AArch64Disassembler.cpp
+++ b/llvm/lib/Target/AArch64/Disassembler/AArch64Disassembler.cpp
@@ -1608,7 +1608,7 @@ DecodeStatus AArch64Disassembler::getInstruction(MCInst &MI, uint64_t &Size,
   uint32_t Insn =
       (Bytes[3] << 24) | (Bytes[2] << 16) | (Bytes[1] << 8) | (Bytes[0] << 0);
 
-  const uint8_t *Tables[] = {DecoderTable32, DecoderTableFallback32};
+  const uint8_t *Tables[] = {DecoderTable32};
 
   for (const auto *Table : Tables) {
     DecodeStatus Result =
diff --git a/llvm/lib/Target/AMDGPU/AMDGPU.td b/llvm/lib/Target/AMDGPU/AMDGPU.td
index a366db1c580ba..02d29318d0fdb 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPU.td
+++ b/llvm/lib/Target/AMDGPU/AMDGPU.td
@@ -2115,6 +2115,11 @@ def FeatureISAVersion12_Generic: FeatureSet<
 
 def AMDGPUInstrInfo : InstrInfo {
   let guessInstructionProperties = 1;
+  let CoalesceDecoderNamespaces = [
+    ["GFX11", "GFX11_FAKE16"],
+    ["GFX12", "GFX12_FAKE16"],
+    ["GFX1250", "GFX1250_FAKE16"],
+  ];
 }
 
 def AMDGPUAsmParser : AsmParser {
diff --git a/llvm/lib/Target/AMDGPU/Disassembler/AMDGPUDisassembler.cpp b/llvm/lib/Target/AMDGPU/Disassembler/AMDGPUDisassembler.cpp
index b50b2a2e6e23c..f457085e42eb4 100644
--- a/llvm/lib/Target/AMDGPU/Disassembler/AMDGPUDisassembler.cpp
+++ b/llvm/lib/Target/AMDGPU/Disassembler/AMDGPUDisassembler.cpp
@@ -617,18 +617,15 @@ DecodeStatus AMDGPUDisassembler::getInstruction(MCInst &MI, uint64_t &Size,
       std::bitset<96> DecW = eat12Bytes(Bytes);
 
       if (isGFX11() &&
-          tryDecodeInst(DecoderTableGFX1196, DecoderTableGFX11_FAKE1696, MI,
-                        DecW, Address, CS))
+          tryDecodeInst(DecoderTableGFX1196, MI, DecW, Address, CS))
         break;
 
       if (isGFX1250() &&
-          tryDecodeInst(DecoderTableGFX125096, DecoderTableGFX1250_FAKE1696, MI,
-                        DecW, Address, CS))
+          tryDecodeInst(DecoderTableGFX125096, MI, DecW, Address, CS))
         break;
 
       if (isGFX12() &&
-          tryDecodeInst(DecoderTableGFX1296, DecoderTableGFX12_FAKE1696, MI,
-                        DecW, Address, CS))
+          tryDecodeInst(DecoderTableGFX1296, MI, DecW, Address, CS))
         break;
 
       if (isGFX12() &&
@@ -698,18 +695,13 @@ DecodeStatus AMDGPUDisassembler::getInstruction(MCInst &MI, uint64_t &Size,
         break;
 
       if (isGFX1250() &&
-          tryDecodeInst(DecoderTableGFX125064, DecoderTableGFX1250_FAKE1664, MI,
-                        QW, Address, CS))
+          tryDecodeInst(DecoderTableGFX125064, MI, QW, Address, CS))
         break;
 
-      if (isGFX12() &&
-          tryDecodeInst(DecoderTableGFX1264, DecoderTableGFX12_FAKE1664, MI, QW,
-                        Address, CS))
+      if (isGFX12() && tryDecodeInst(DecoderTableGFX1264, MI, QW, Address, CS))
         break;
 
-      if (isGFX11() &&
-          tryDecodeInst(DecoderTableGFX1164, DecoderTableGFX11_FAKE1664, MI, QW,
-                        Address, CS))
+      if (isGFX11() && tryDecodeInst(DecoderTableGFX1164, MI, QW, Address, CS))
         break;
 
       if (isGFX11() &&
@@ -753,19 +745,14 @@ DecodeStatus AMDGPUDisassembler::getInstruction(MCInst &MI, uint64_t &Size,
       if (isGFX10() && tryDecodeInst(DecoderTableGFX1032, MI, DW, Address, CS))
         break;
 
-      if (isGFX11() &&
-          tryDecodeInst(DecoderTableGFX1132, DecoderTableGFX11_FAKE1632, MI, DW,
-                        Address, CS))
+      if (isGFX11() && tryDecodeInst(DecoderTableGFX1132, MI, DW, Address, CS))
         break;
 
       if (isGFX1250() &&
-          tryDecodeInst(DecoderTableGFX125032, DecoderTableGFX1250_FAKE1632, MI,
-                        DW, Address, CS))
+          tryDecodeInst(DecoderTableGFX125032, MI, DW, Address, CS))
         break;
 
-      if (isGFX12() &&
-          tryDecodeInst(DecoderTableGFX1232, DecoderTableGFX12_FAKE1632, MI, DW,
-                        Address, CS))
+      if (isGFX12() && tryDecodeInst(DecoderTableGFX1232, MI, DW, Address, CS))
         break;
     }
 
diff --git a/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp b/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp
index 89df9d82f8780..86ea7bfb5b592 100644
--- a/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp
+++ b/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp
@@ -691,9 +691,9 @@ static constexpr DecoderListEntry DecoderList32[]{
     {DecoderTableXSMT32, XSMTGroup, "SpacemiT extensions"},
     // Standard Extensions
     {DecoderTable32, {}, "standard 32-bit instructions"},
-    {DecoderTableRV32Only32, {}, "RV32-only standard 32-bit instructions"},
-    {DecoderTableZfinx32, {}, "Zfinx (Float in Integer)"},
-    {DecoderTableZdinxRV32Only32, {}, "RV32-only Zdinx (Double in Integer)"},
+    //{DecoderTableRV32Only32, {}, "RV32-only standard 32-bit instructions"},
+    //{DecoderTableZfinx32, {}, "Zfinx (Float in Integer)"},
+    //{DecoderTableZdinxRV32Only32, {}, "RV32-only Zdinx (Double in Integer)"},
 };
 
 namespace {
@@ -743,7 +743,7 @@ static constexpr DecoderListEntry DecoderList16[]{
     // DecoderTableZicfiss16 must be checked before DecoderTable16.
     {DecoderTableZicfiss16, {}, "Zicfiss (Shadow Stack 16-bit)"},
     {DecoderTable16, {}, "standard 16-bit instructions"},
-    {DecoderTableRV32Only16, {}, "RV32-only 16-bit instructions"},
+    //{DecoderTableRV32Only16, {}, "RV32-only 16-bit instructions"},
     // Zc* instructions incompatible with Zcf or Zcd
     {DecoderTableZcOverlap16,
      {},
diff --git a/llvm/lib/Target/RISCV/RISCV.td b/llvm/lib/Target/RISCV/RISCV.td
index b24d8637cb27f..e005878dc1274 100644
--- a/llvm/lib/Target/RISCV/RISCV.td
+++ b/llvm/lib/Target/RISCV/RISCV.td
@@ -85,6 +85,11 @@ include "RISCVPfmCounters.td"
 
 def RISCVInstrInfo : InstrInfo {
   let guessInstructionProperties = 0;
+  let CoalesceDecoderNamespaces = [
+    ["", "RV32Only", "Zfinx", "ZdinxRV32Only"]
+    // ["Zicfiss", "", "RV32Only", "ZcOverlap"], // Need to handle RV32Only mappings.
+  ];
+
 }
 
 def RISCVAsmParser : AsmParser {
diff --git a/llvm/lib/Target/Sparc/SparcInstrInfo.td b/llvm/lib/Target/Sparc/SparcInstrInfo.td
index 53972d6c105a4..8f3779295a93c 100644
--- a/llvm/lib/Target/Sparc/SparcInstrInfo.td
+++ b/llvm/lib/Target/Sparc/SparcInstrInfo.td
@@ -38,7 +38,8 @@ def HasV9   : Predicate<"Subtarget->isV9()">,
 // HasNoV9 - This predicate is true when the target doesn't have V9
 // instructions.  Use of this is just a hack for the isel not having proper
 // costs for V8 instructions that are more expensive than their V9 ones.
-def HasNoV9 : Predicate<"!Subtarget->isV9()">;
+def HasNoV9 : Predicate<"!Subtarget->isV9()">,
+              AssemblerPredicate<(all_of (not FeatureV9))>;
 
 // HasVIS - This is true when the target processor has VIS extensions.
 def HasVIS : Predicate<"Subtarget->isVIS()">,
@@ -756,7 +757,7 @@ let rd = 0 in {
     def STFSRri : F3_2<3, 0b100101, (outs), (ins (MEMri $rs1, $simm13):$addr),
 		   "st %fsr, [$addr]", [], IIC_st>;
   }
-  let mayStore = 1, Defs = [FQ] in {
+  let mayStore = 1, Defs = [FQ], Predicates = [HasNoV9] in {
     def STDFQrr : F3_1<3, 0b100110, (outs), (ins (MEMrr $rs1, $rs2):$addr),
 		   "std %fq, [$addr]", [], IIC_std>;
     def STDFQri : F3_2<3, 0b100110, (outs), (ins (MEMri $rs1, $simm13):$addr),
diff --git a/llvm/test/MC/Disassembler/Sparc/sparc-special-registers.txt b/llvm/test/MC/Disassembler/Sparc/sparc-special-registers.txt
index 56ffb9abd5341..5aaacfdba888a 100644
--- a/llvm/test/MC/Disassembler/Sparc/sparc-special-registers.txt
+++ b/llvm/test/MC/Disassembler/Sparc/sparc-special-registers.txt
@@ -16,22 +16,22 @@
 # CHECK: wr %i0, %i1, %asr15
 0x9f 0x86 0x00 0x19
 
-# CHECK: rd %psr, %i0
+# CHECK-V8: rd %psr, %i0
 0xb1 0x48 0x00 0x00
 
-# CHECK: rd %wim, %i0
+# CHECK-V8: rd %wim, %i0
 0xb1 0x50 0x00 0x00
 
-# CHECK: rd %tbr, %i0
+# CHECK-V8: rd %tbr, %i0
 0xb1 0x58 0x00 0x00
 
-# CHECK: wr %i0, 5, %psr
+# CHECK-V8: wr %i0, 5, %psr
 0x81 0x8e 0x20 0x05
 
-# CHECK: wr %i0, 5, %wim
+# CHECK-V8: wr %i0, 5, %wim
 0x81 0x96 0x20 0x05
 
-# CHECK: wr %i0, 5, %tbr
+# CHECK-V8: wr %i0, 5, %tbr
 0x81 0x9e 0x20 0x05
 
 # CHECK: st %fsr, [%i5]
diff --git a/llvm/test/TableGen/FixedLenDecoderEmitter/conflict.td b/llvm/test/TableGen/FixedLenDecoderEmitter/conflict.td
index 853a68d22d1d9..a1e1b91ecbadd 100644
--- a/llvm/test/TableGen/FixedLenDecoderEmitter/conflict.td
+++ b/llvm/test/TableGen/FixedLenDecoderEmitter/conflict.td
@@ -9,23 +9,40 @@ def MyTarget : Target { let InstructionSet = MyTargetISA; }
 def R0 : Register<"r0"> { let Namespace = "MyTarget"; }
 def GPR32 : RegisterClass<"MyTarget", [i32], 32, (add R0)>;
 
+def X0 : Register<"x0"> { let Namespace = "MyTarget"; }
+def GPR64 : RegisterClass<"MyTarget", [i64], 64, (add X0)>;
+
 class I<dag OOps, dag IOps, list<dag> Pat>
   : Instruction {
   let Namespace = "MyTarget";
   let OutOperandList = OOps;
   let InOperandList = IOps;
   let Pattern = Pat;
+  let Size = 4;
   bits<32> Inst;
   bits<32> SoftFail;
 }
 
+// Assume there isa 2 bit encoding for the dst and src register.
 def A : I<(outs GPR32:$dst), (ins GPR32:$src1), []> {
-  let Size = 4;
-  let Inst{31...0} = 0;
+  bits<2> dst;
+  bits<2> src1;
+  let Inst{31...4} = 0;
+  let Inst{1...0} = dst;
+  let Inst{3...2} = src1;
 }
+
 def B : I<(outs GPR32:$dst), (ins GPR32:$src1), []> {
-  let Size = 4;
-  let Inst{31...0} = 0;
+  bits<2> dst;
+  bits<2> src1;
+  let Inst{31...4} = 0;
+  let Inst{1...0} = dst;
+  let Inst{3...2} = src1;
+}
+
+def C : I<(outs), (ins), []> {
+  let Inst{31...4} = 1;
+  let Inst{3...0} = 0;
 }
 
 // CHECK: Decoding Conflict:
diff --git a/llvm/utils/TableGen/DecoderEmitter.cpp b/llvm/utils/TableGen/DecoderEmitter.cpp
index 95e7408ea88c7..0efc1bfa0b912 100644
--- a/llvm/utils/TableGen/DecoderEmitter.cpp
+++ b/llvm/utils/TableGen/DecoderEmitter.cpp
@@ -28,6 +28,7 @@
 #include "llvm/ADT/Statistic.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSet.h"
 #include "llvm/MC/MCDecoderOps.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/CommandLine.h"
@@ -116,6 +117,12 @@ static cl::opt<bool> IgnoreFullyDefinedOperands(
         "Do not automatically decode operands with no '?' in their encoding."),
     cl::init(false), cl::cat(DisassemblerEmitterCat));
 
+static cl::opt<bool> ResolveConflictsByTryingAll(
+    "resolve-conflicts-try-all",
+    cl::desc(
+        "Resolve decoding conflicts by trying all conflciting instructions."),
+    cl::init(false), cl::cat(DisassemblerEmitterCat));
+
 STATISTIC(NumEncodings, "Number of encodings considered");
 STATISTIC(NumEncodingsLackingDisasm,
           "Number of encodings without disassembler info");
@@ -174,6 +181,36 @@ struct OperandInfo {
   ArrayRef<EncodingField> fields() const { return Fields; }
 };
 
+class CoalescedNamespaces {
+  // Map from a namespace to its coalesced namespace, order info.
+  StringMap<std::pair<StringRef, unsigned>> NSMap;
+
+public:
+  CoalescedNamespaces() {}
+  void addNamespaces(ArrayRef<StringRef> NSList) {
+    if (NSList.size() <= 1)
+      return;
+    StringRef Head = NSList.front();
+    for (const auto &[Idx, NS] : enumerate(NSList)) {
+      if (NSMap.contains(NS))
+        PrintFatalError("Namespace " + NS + " already coalesced");
+      NSMap[NS] = std::make_pair(Head, (unsigned)Idx);
+    }
+  }
+  StringRef getEffectiveNS(StringRef NS) const {
+    auto II = NSMap.find(NS);
+    if (II == NSMap.end())
+      return NS;
+    return II->second.first;
+  }
+  unsigned getNSOrder(StringRef NS) const {
+    auto II = NSMap.find(NS);
+    if (II == NSMap.end())
+      return 0;
+    return II->second.second;
+  }
+};
+
 /// Represents a parsed InstructionEncoding record or a record derived from it.
 class InstructionEncoding {
   /// The Record this encoding originates from.
@@ -187,6 +224,7 @@ class InstructionEncoding {
 
   /// The namespace in which this encoding exists.
   StringRef DecoderNamespace;
+  StringRef EffectiveDecoderNamespace;
 
   /// Known bits of this encoding. This is the value of the `Inst` field
   /// with any variable references replaced with '?'.
@@ -209,8 +247,8 @@ class InstructionEncoding {
   SmallVector<OperandInfo, 16> Operands;
 
 public:
-  InstructionEncoding(const Record *EncodingDef,
-                      const CodeGenInstruction *Inst);
+  InstructionEncoding(const Record *EncodingDef, const CodeGenInstruction *Inst,
+                      const CoalescedNamespaces &NSInfo);
 
   /// Returns the Record this encoding originates from.
   const Record *getRecord() const { return EncodingDef; }
@@ -221,8 +259,12 @@ class InstructionEncoding {
   /// Returns the name of this encoding, for debugging purposes.
   StringRef getName() const { return Name; }
 
-  /// Returns the namespace in which this encoding exists.
+  /// Returns the namespace in which this encoding exists. This always returns
+  /// the effective namespace when coalescing is enabled.
   StringRef getDecoderNamespace() const { return DecoderNamespace; }
+  StringRef getEffectiveDecoderNamespace() const {
+    return EffectiveDecoderNamespace;
+  }
 
   /// Returns the size of this encoding, in bits.
   unsigned getBitWidth() const { return InstBits.getBitWidth(); }
@@ -263,16 +305,26 @@ class InstructionEncoding {
   void parseFixedLenOperands(const BitsInit &Bits);
 };
 
-/// Sorting predicate to sort encoding IDs by encoding width.
+/// Sorting predicate to sort encoding IDs by encoding width. Withing the
+/// same width, sort the decode namespace order.
 class LessEncodingIDByWidth {
   ArrayRef<InstructionEncoding> Encodings;
+  const CoalescedNamespaces &NSInfo;
 
 public:
-  explicit LessEncodingIDByWidth(ArrayRef<InstructionEncoding> Encodings)
-      : Encodings(Encodings) {}
+  explicit LessEncodingIDByWidth(ArrayRef<InstructionEncoding> Encodings,
+                                 const CoalescedNamespaces &NSInfo)
+      : Encodings(Encodings), NSInfo(NSInfo) {}
 
   bool operator()(unsigned ID1, unsigned ID2) const {
-    return Encodings[ID1].getBitWidth() < Encodings[ID2].getBitWidth();
+    const InstructionEncoding &E1 = Encodings[ID1];
+    const InstructionEncoding &E2 = Encodings[ID2];
+
+    unsigned NSOrder1 = NSInfo.getNSOrder(E1.getDecoderNamespace());
+    unsigned NSOrder2 = NSInfo.getNSOrder(E2.getDecoderNamespace());
+
+    return std::tuple(E1.getBitWidth(), NSOrder1) <
+           std::tuple(E2.getBitWidth(), NSOrder2);
   }
 };
 
@@ -357,6 +409,8 @@ class DecoderEmitter {
   /// Encodings IDs for each HwMode. An ID is an index into Encodings.
   SmallDenseMap<unsigned, std::vector<unsigned>> EncodingIDsByHwMode;
 
+  CoalescedNamespaces NSInfo;
+
 public:
   explicit DecoderEmitter(const RecordKeeper &RK);
 
@@ -504,6 +558,11 @@ class FilterChooser {
   /// Also used when there is only one encoding.
   std::optional<unsigned> SingletonEncodingID;
 
+  // AttemptAll = true means we either have > 1 NamespaceChoosers or
+  // we sequentially attempt to decode witth the EncodingIDs.
+  bool AttemptAll = false;
+  std::vector<std::unique_ptr<const FilterChooser>> NamespaceChoosers;
+
   /// If the selected filter matches multiple encodings, and there is
   /// *at least one* encoding in which all bits are known in the filtered range,
   /// then this is the FilterChooser created for the subset of encodings that
@@ -517,6 +576,8 @@ class FilterChooser {
   /// The "field value" here refers to the encoding bits in the filtered range.
   std::map<uint64_t, std::unique_ptr<const FilterChooser>> FilterChooserMap;
 
+  const CoalescedNamespaces &NSInfo;
+
   struct Island {
     unsigned StartBit;
     unsigned NumBits;
@@ -526,10 +587,12 @@ class FilterChooser {
 public:
   /// Constructs a top-level filter chooser.
   FilterChooser(ArrayRef<InstructionEncoding> Encodings,
-                ArrayRef<unsigned> EncodingIDs)
-      : Encodings(Encodings), EncodingIDs(EncodingIDs), Parent(nullptr) {
+                ArrayRef<unsigned> EncodingIDs,
+                const CoalescedNamespaces &NSInfo)
+      : Encodings(Encodings), EncodingIDs(EncodingIDs), Parent(nullptr),
+        NSInfo(NSInfo) {
     // Sort encoding IDs once.
-    stable_sort(this->EncodingIDs, LessEncodingIDByWidth(Encodings));
+    stable_sort(this->EncodingIDs, LessEncodingIDByWidth(Encodings, NSInfo));
     // Filter width is the width of the smallest encoding.
     unsigned FilterWidth = Encodings[this->EncodingIDs.front()].getBitWidth();
     FilterBits = KnownBits(FilterWidth);
@@ -540,9 +603,10 @@ class FilterChooser {
   FilterChooser(ArrayRef<InstructionEncoding> Encodings,
                 ArrayRef<unsigned> EncodingIDs, const KnownBits &FilterBits,
                 const FilterChooser &Parent)
-      : Encodings(Encodings), EncodingIDs(EncodingIDs), Parent(&Parent) {
+      : Encodings(Encodings), EncodingIDs(EncodingIDs), Parent(&Parent),
+        NSInfo(Parent.NSInfo) {
     // Inferior filter choosers are created from sorted array of encoding IDs.
-    assert(is_sorted(EncodingIDs, LessEncodingIDByWidth(Encodings)));
+    assert(is_sorted(EncodingIDs, LessEncodingIDByWidth(Encodings, NSInfo)));
     assert(!FilterBits.hasConflict() && "Broken filter");
     // Filter width is the width of the smallest encoding.
     unsigned FilterWidth = Encodings[EncodingIDs.front()].getBitWidth();
@@ -564,6 +628,8 @@ class FilterChooser {
   /// works with, creating inferior FilterChoosers as necessary.
   void applyFilter(const Filter &F);
 
+  void splitPerNamespace();
+
   /// dumpStack - dumpStack traverses the filter chooser chain and calls
   /// dumpFilterArray on each filter chooser up to the top level one.
   void dumpStack(raw_ostream &OS, indent Indent, unsigned PadToWidth) const;
@@ -591,6 +657,8 @@ class FilterChooser {
   // dump the conflict set to the standard error.
   void doFilter();
 
+  bool hasMultipleNamespaces() const;
+
 public:
   void dump() const;
 };
@@ -679,6 +747,31 @@ void FilterChooser::applyFilter(const Filter &F) {
   NumBits = F.NumBits;
   assert(FilterBits.extractBits(NumBits, StartBit).isUnknown());
 
+  // If we have a mix of fixed and variable encodings, and they have different
+  // namespaces, then split this filter into multiple ones. If its just fixed or
+  // just variable IDs, they should already be ordered in their namespace order.
+  // If we have both, then if we do not split, we may generate a decoding order
+  // as   : f0, f1, f2, v0, v1, v2
+  // whereas the intent was: f0, v0, f1, v1, f2, v2. So achieve this, split
+  // This into sub-filters and apply. If there is just fixed IDs or just
+  // variable IDs. we assume they are sorted in the right order.
+  if (!F.VariableIDs.empty() && !F.FilteredIDs.empty()) {
+    StringSet<> Namespaces;
+    for (unsigned ID : F.VariableIDs)
+      Namespaces.insert(Encodings[ID].getDecoderNamespace());
+    for (const auto &[_, InferiorEncodingIDs] : F.FilteredIDs) {
+      for (unsigned ID : InferiorEncodingIDs)
+        Namespaces.insert(Encodings[ID].getDecoderNamespace());
+    }
+    if (Namespaces.size() > 1) {
+      errs() << "Found multiple namespaces with non-empty variable IDs\n";
+      splitPerNamespace();
+      return;
+    }
+  }
+
+  // Check if we have a mix of namespaces. If so, we need to attempt to decode
+  // them in the order of the namespaces
   if (!F.VariableIDs.empty()) {
     // Delegates to an inferior filter chooser for further processing on this
     // group of instructions whose segment values are variable.
@@ -822,28 +915,55 @@ unsigned DecoderEmitter::emitTable(formatted_raw_ostream &OS,
     case MCD::OPC_FilterValueOrSkip: {
       OS << "  MCD::OPC_FilterValueOrSkip, ";
       // The filter value is ULEB128 encoded.
+      const char *ErrMsg = nullptr;
+      uint64_t FilterVal = decodeULEB128(&*I, nullptr, EndPtr, &ErrMsg);
+      assert(ErrMsg == nullptr && "ULEB128 value too large!");
       emitULEB128(I, OS);
+
       uint32_t NumToSkip = emitNumToSkip(I, OS);
-      emitNumToSkipComment(NumToSkip);
+
+      OS << "// FilterVal = 0x";
+      OS.write_hex(FilterVal);
+      emitNumToSkipComment(NumToSkip, /*InComment=*/true);
       OS << '\n';
       break;
     }
     case MCD::OPC_FilterValue: {
       OS << "  MCD::OPC_FilterValue, ";
+      const char *ErrMsg = nullptr;
+      uint64_t FilterVal = decodeULEB128(&*I, nullptr, EndPtr, &ErrMsg);
+      assert(ErrMsg == nullptr && "ULEB128 value too large!");
       // The filter value is ULEB128 encoded.
       emitULEB128(I, OS);
+      OS << "// FilterVal = 0x";
+      OS.write_hex(FilterVal);
       OS << '\n';
       break;
     }
     case MCD::OPC_CheckField: {
       OS << "  MCD::OPC_CheckField, ";
+
+      const char *ErrMsg = nullptr;
+      unsigned Start = decodeULEB128(&*I, nullptr, EndPtr, &ErrMsg);
+      assert(ErrMsg == nullptr && "ULEB128 value too large!");
       // ULEB128 encoded start value.
       emitULEB128(I, OS);
+
       // 8-bit length.
       unsigned Len = *I++;
       OS << Len << ", ";
+
+      uint64_t FieldVal = decodeULEB128(&*I, nullptr, EndPtr, &ErrMsg);
+      assert(ErrMsg == nullptr && "ULEB128 value too large!");
+
       // ULEB128 encoded field value.
       emitULEB128(I, OS);
+
+      OS << " // Inst{";
+      if (Len > 1)
+        OS << (Start + Len - 1) << "-";
+      OS << Start << "} == 0x";
+      OS.write_hex(FieldVal);
       OS << '\n';
       break;
     }
@@ -861,7 +981,7 @@ unsigned DecoderEmitter::emitTable(formatted_raw_ostream &OS,
       unsigned Opc = decodeULEB128(&*I, nullptr, EndPtr, &ErrMsg);
       assert(ErrMsg == nullptr && "ULEB128 value too large!");
 
-      OS << "  MCD::OPC_" << (IsTry ? "Try" : "") << "Decode, ";
+      OS << "  MCD::OPC_" << (IsTry ? "Try" : "") << "Decode ,";
       emitULEB128(I, OS);
 
       // Decoder index.
@@ -873,11 +993,8 @@ unsigned DecoderEmitter::emitTable(formatted_raw_ostream &OS,
       assert(EncI != OpcodeToEncodingID.end() && "no encoding entry");
       auto EncodingID = EncI->second;
 
-      if (!IsTry) {
-        OS << "// Opcode: " << Encodings[EncodingID].getName()
-           << ", DecodeIdx: " << DecodeIdx << '\n';
-        break;
-      }
+      OS << "// Opcode: " << Encodings[EncodingID].getName()
+         << ", DecodeIdx: " << DecodeIdx;
       OS << '\n';
       break;
     }
@@ -1331,6 +1448,7 @@ void DecoderTableBuilder::emitSingletonTableEntry(
   // can decode it.
   const MCD::DecoderOps DecoderOp =
       Encoding.hasCompleteDecoder() ? MCD::OPC_Decode : MCD::OPC_TryDecode;
+
   TableInfo.Table.insertOpcode(DecoderOp);
   const Record *InstDef = Encodings[EncodingID].getInstruction()->TheDef;
   TableInfo.Table.insertULEB128(Target.getInstrIntValue(InstDef));
@@ -1571,6 +1689,40 @@ std::unique_ptr<Filter> FilterChooser::findBestFilter() const {
   return nullptr;
 }
 
+bool FilterChooser::hasMultipleNamespaces() const {
+  if (EncodingIDs.size() <= 1)
+    return false;
+  const InstructionEncoding &First = Encodings[EncodingIDs.front()];
+  const InstructionEncoding &Last = Encodings[EncodingIDs.back()];
+  return First.getDecoderNamespace() != Last.getDecoderNamespace();
+}
+
+void FilterChooser::splitPerNamespace() {
+  if (!hasMultipleNamespaces())
+    PrintFatalError("Expected use of > 1 decoder namespaces");
+
+  auto GetNSOrder = [&](unsigned EncodingID) -> unsigned {
+    StringRef NS = Encodings[EncodingID].getDecoderNamespace();
+    return NSInfo.getNSOrder(NS);
+  };
+
+  // This splits a filter into per-namespace filter.
+
+  unsigned LastNSOrder = GetNSOrder(EncodingIDs.back());
+  std::vector<unsigned> Counts(LastNSOrder + 1, 0);
+  for (unsigned ID : EncodingIDs)
+    ++Counts[GetNSOrder(ID)];
+  unsigned Start = 0;
+  for (unsigned Count : Counts) {
+    if (Count == 0)
+      continue;
+    ArrayRef<unsigned> NSEncodings = ArrayRef(EncodingIDs).slice(Start, Count);
+    NamespaceChoosers.push_back(std::make_unique<FilterChooser>(
+        Encodings, NSEncodings, FilterBits, *this));
+    Start += Count;
+  }
+}
+
 // Decides on the best configuration of filter(s) to use in order to decode
 // the instructions.  A conflict of instructions may occur, in which case we
 // dump the conflict set to the standard error.
@@ -1589,10 +1741,20 @@ void FilterChooser::doFilter() {
     return;
   }
 
+  if (hasMultipleNamespaces()) {
+    splitPerNamespace();
+    return;
+  }
+
+  if (ResolveConflictsByTryingAll) {
+    AttemptAll = true;
+    return;
+  }
+
   // Print out useful conflict information for postmortem analysis.
   errs() << "Decoding Conflict:\n";
   dump();
-  PrintFatalError("Decoding conflict encountered");
+  // PrintFatalError("Decoding conflict encountered");
 }
 
 void FilterChooser::dump() const {
@@ -1608,7 +1770,11 @@ void FilterChooser::dump() const {
     const InstructionEncoding &Encoding = Encodings[EncodingID];
     errs() << Indent << indent(PadToWidth - Encoding.getBitWidth());
     printKnownBits(errs(), Encoding.getMandatoryBits(), '_');
-    errs() << "  " << Encoding.getName() << '\n';
+    errs() << "  " << Encoding.getName();
+    StringRef NS = Encoding.getDecoderNamespace();
+    if (!NS.empty())
+      errs() << ", DecoderNamespace = " << NS;
+    errs() << '\n';
   }
 }
 
@@ -1617,10 +1783,14 @@ void DecoderTableBuilder::emitTableEntries(const FilterChooser &FC) const {
 
   // If there are other encodings that could match if those with all bits
   // known don't, enter a scope so that they have a chance.
-  size_t FixupLoc = 0;
+  // Note: If the same bit patten can match a known encoding as well as variable
+  // encoding, we first let the fixed encoding take priority over the variable
+  // ones. This is a sort of conflict, call it a soft-conflict which is always
+  // resolved by giving preference to the fixed encoding.
+  size_t VarScopeFixupLoc = 0;
   if (FC.VariableFC) {
     Table.insertOpcode(MCD::OPC_Scope);
-    FixupLoc = Table.insertNumToSkip();
+    VarScopeFixupLoc = Table.insertNumToSkip();
   }
 
   if (FC.SingletonEncodingID) {
@@ -1640,7 +1810,7 @@ void DecoderTableBuilder::emitTableEntries(const FilterChooser &FC) const {
 
     // Emit table entries for the only case.
     emitTableEntries(*Delegate);
-  } else {
+  } else if (FC.FilterChooserMap.size() > 1) {
     // The general case: emit a switch over the field value.
     Table.insertOpcode(MCD::OPC_ExtractField);
     Table.insertULEB128(FC.StartBit);
@@ -1667,10 +1837,35 @@ void DecoderTableBuilder::emitTableEntries(const FilterChooser &FC) const {
 
     // Emit table entries for the last case.
     emitTableEntries(*Delegate);
+  } else if (!FC.NamespaceChoosers.empty()) {
+    errs() << "Attempting NS Choosers " << Table.size() << '\n';
+    FC.dump();
+    for (const auto &Delegate : drop_end(FC.NamespaceChoosers)) {
+      Table.insertOpcode(MCD::OPC_Scope);
+      unsigned FixupLoc = Table.insertNumToSkip();
+      emitTableEntries(*Delegate);
+      Table.patchNumToSkip(FixupLoc, Table.size());
+    }
+    emitTableEntries(*FC.NamespaceChoosers.back());
+  } else if (FC.AttemptAll) {
+    errs() << "Attempting All " << Table.size() << '\n';
+    FC.dump();
+    for (const auto ID : drop_end(FC.EncodingIDs)) {
+      Table.insertOpcode(MCD::OPC_Scope);
+      unsigned FixupLoc = Table.insertNumToSkip();
+      FilterChooser SingletonFC(FC.Encodings, ID, FC.FilterBits, *FC.Parent);
+      emitSingletonTableEntry(SingletonFC);
+      Table.patchNumToSkip(FixupLoc, Table.size());
+    }
+    FilterChooser LastSingletonFC(FC.Encodings, FC.EncodingIDs.back(),
+                                  FC.FilterBits, *FC.Parent);
+    emitSingletonTableEntry(LastSingletonFC);
+  } else {
+    llvm_unreachable("Invalid FilterChooser");
   }
 
   if (FC.VariableFC) {
-    Table.patchNumToSkip(FixupLoc, Table.size());
+    Table.patchNumToSkip(VarScopeFixupLoc, Table.size());
     emitTableEntries(*FC.VariableFC);
   }
 }
@@ -2051,7 +2246,8 @@ void InstructionEncoding::parseFixedLenOperands(const BitsInit &Bits) {
 }
 
 InstructionEncoding::InstructionEncoding(const Record *EncodingDef,
-                                         const CodeGenInstruction *Inst)
+                                         const CodeGenInstruction *Inst,
+                                         const CoalescedNamespaces &NSInfo)
     : EncodingDef(EncodingDef), Inst(Inst) {
   const Record *InstDef = Inst->TheDef;
 
@@ -2061,6 +2257,7 @@ InstructionEncoding::InstructionEncoding(const Record *EncodingDef,
   Name.append(InstDef->getName());
 
   DecoderNamespace = EncodingDef->getValueAsString("DecoderNamespace");
+  EffectiveDecoderNamespace = NSInfo.getEffectiveNS(DecoderNamespace);
   DecoderMethod = EncodingDef->getValueAsString("DecoderMethod");
   if (!DecoderMethod.empty())
     HasCompleteDecoder = EncodingDef->getValueAsBit("hasCompleteDecoder");
@@ -2268,9 +2465,11 @@ static DecodeStatus decodeInstruction(const uint8_t DecodeTable[], MCInst &MI,
       S = decodeToMCInst(DecodeIdx, S, insn, MI, Address, DisAsm, DecodeComplete);
       assert(DecodeComplete);
 
-      LLVM_DEBUG(dbgs() << Loc << ": OPC_Decode: opcode " << Opc
-                   << ", using decoder " << DecodeIdx << ": "
-                   << (S != MCDisassembler::Fail ? "PASS\n" : "FAIL\n"));
+      LLVM_DEBUG({
+        dbgs() << Loc << ": OPC_Decode : opcode " << Opc
+               << ", using decoder " << DecodeIdx << ": "
+               << (S != MCDisassembler::Fail ? "PASS\n" : "FAIL\n");
+      });
       return S;
     })";
   if (HasTryDecode) {
@@ -2295,15 +2494,15 @@ static DecodeStatus decodeInstruction(const uint8_t DecodeTable[], MCInst &MI,
         return S;
       }
       assert(S == MCDisassembler::Fail);
+      // Reset decode status. This also drops a SoftFail status that could be
+      // set before the decode attempt.
+      S = MCDisassembler::Success;
       if (ScopeStack.empty()) {
         LLVM_DEBUG(dbgs() << "FAIL, returning FAIL\n");
         return MCDisassembler::Fail;
       }
       Ptr = ScopeStack.pop_back_val();
       LLVM_DEBUG(dbgs() << "FAIL, continuing at " << Ptr - DecodeTable << '\n');
-      // Reset decode status. This also drops a SoftFail status that could be
-      // set before the decode attempt.
-      S = MCDisassembler::Success;
       break;
     })";
   }
@@ -2339,6 +2538,7 @@ void DecoderEmitter::collectHwModesReferencedForEncodings(
       if (EncodingDef->isSubClassOf("InstructionEncoding")) {
         StringRef DecoderNamespace =
             EncodingDef->getValueAsString("DecoderNamespace");
+        DecoderNamespace = NSInfo.getEffectiveNS(DecoderNamespace);
         NamespacesWithHwModes[DecoderNamespace].insert(HwModeID);
         BV.set(HwModeID);
       }
@@ -2360,7 +2560,8 @@ void DecoderEmitter::handleHwModesUnrelatedEncodings(
     break;
   }
   case SUPPRESSION_LEVEL1: {
-    StringRef DecoderNamespace = Encodings[EncodingID].getDecoderNamespace();
+    StringRef DecoderNamespace =
+        Encodings[EncodingID].getEffectiveDecoderNamespace();
     auto It = NamespacesWithHwModes.find(DecoderNamespace);
     if (It != NamespacesWithHwModes.end()) {
       for (unsigned HwModeID : It->second)
@@ -2445,7 +2646,7 @@ void DecoderEmitter::parseInstructionEncodings() {
           continue;
         }
         unsigned EncodingID = Encodings.size();
-        Encodings.emplace_back(EncodingDef, Inst);
+        Encodings.emplace_back(EncodingDef, Inst, NSInfo);
         EncodingIDsByHwMode[HwModeID].push_back(EncodingID);
       }
       continue; // Ignore encoding specified by Instruction itself.
@@ -2457,7 +2658,7 @@ void DecoderEmitter::parseInstructionEncodings() {
     }
 
     unsigned EncodingID = Encodings.size();
-    Encodings.emplace_back(InstDef, Inst);
+    Encodings.emplace_back(InstDef, Inst, NSInfo);
 
     // This instruction is encoded the same on all HwModes.
     // According to user needs, add it to all, some, or only the default HwMode.
@@ -2480,7 +2681,8 @@ void DecoderEmitter::parseInstructionEncodings() {
       continue;
     }
     unsigned EncodingID = Encodings.size();
-    Encodings.emplace_back(EncodingDef, &Target.getInstruction(InstDef));
+    Encodings.emplace_back(EncodingDef, &Target.getInstruction(InstDef),
+                           NSInfo);
     EncodingIDsByHwMode[DefaultMode].push_back(EncodingID);
   }
 
@@ -2492,6 +2694,25 @@ void DecoderEmitter::parseInstructionEncodings() {
 
 DecoderEmitter::DecoderEmitter(const RecordKeeper &RK)
     : RK(RK), Target(RK), CGH(Target.getHwModes()) {
+  // First parse information about coalesced namespaces.
+  const ListInit *LI = Target.getInstructionSet()->getValueAsListInit(
+      "CoalesceDecoderNamespaces");
+  for (const Init *LE : LI->getElements()) {
+    const ListInit *InnerLI = dyn_cast<ListInit>(LE);
+    if (!InnerLI)
+      PrintFatalError(
+          "CoalesceDecoderNamespaces expected to be a list of list of strings");
+    std::vector<StringRef> Strings;
+    for (const Init *I : InnerLI->getElements()) {
+      if (const auto *SI = dyn_cast<StringInit>(I))
+        Strings.push_back(SI->getValue());
+      else
+        PrintFatalError("CoalesceDecoderNamespaces expected to be a list of "
+                        "list of strings");
+    }
+    NSInfo.addNamespaces(Strings);
+  }
+
   Target.reverseBitsForLittleEndianEncoding();
   parseInstructionEncodings();
 }
@@ -2551,7 +2772,9 @@ template <typename T> constexpr uint32_t InsnBitWidth = 0;
       const InstructionEncoding &Encoding = Encodings[EncodingID];
       const unsigned BitWidth =
           IsVarLenInst ? MaxInstLen : Encoding.getBitWidth();
-      StringRef DecoderNamespace = Encoding.getDecoderNamespace();
+      // For the purpose of bucketing, map the decoder namespace to its
+      // effective one.
+      StringRef DecoderNamespace = Encoding.getEffectiveDecoderNamespace();
       EncMap[BitWidth][{DecoderNamespace, HwModeID}].push_back(EncodingID);
     }
   }
@@ -2575,7 +2798,7 @@ template <typename T> constexpr uint32_t InsnBitWidth = 0;
       auto [DecoderNamespace, HwModeID] = Key;
 
       // Emit the decoder for this (namespace, hwmode, width) combination.
-      FilterChooser FC(Encodings, EncodingIDs);
+      FilterChooser FC(Encodings, EncodingIDs, NSInfo);
 
       // The decode table is cleared for each top level decoder function. The
       // predicates and decoders themselves, however, are shared across



More information about the llvm-commits mailing list