[llvm] New POC (PR #159201)
    Rahul Joshi via llvm-commits 
    llvm-commits at lists.llvm.org
       
    Tue Sep 16 15:52:32 PDT 2025
    
    
  
https://github.com/jurahul updated https://github.com/llvm/llvm-project/pull/159201
>From fe9b342b109baea40e59bf4903082fc49215e90d Mon Sep 17 00:00:00 2001
From: Rahul Joshi <rjoshi at nvidia.com>
Date: Tue, 16 Sep 2025 15:10:56 -0700
Subject: [PATCH] New POC
---
 .../lib/Target/AArch64/AArch64InstrFormats.td |   5 +-
 llvm/lib/Target/AArch64/AArch64InstrInfo.td   |   2 +-
 .../Disassembler/AArch64Disassembler.cpp      |   2 +-
 llvm/test/TableGen/DecoderEmitter/conflict.td |   1 +
 .../DecoderEmitter/var-len-conflict-2.td      |   1 +
 llvm/utils/TableGen/DecoderEmitter.cpp        | 175 +++++++++++++-----
 6 files changed, 138 insertions(+), 48 deletions(-)
diff --git a/llvm/lib/Target/AArch64/AArch64InstrFormats.td b/llvm/lib/Target/AArch64/AArch64InstrFormats.td
index 8958ad129269c..85bb1a27b0405 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrFormats.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrFormats.td
@@ -1957,7 +1957,7 @@ class MRSI : RtSystemI<1, (outs GPR64:$Rt), (ins mrs_sysreg_op:$systemreg),
                        "mrs", "\t$Rt, $systemreg"> {
   bits<16> systemreg;
   let Inst{20-5} = systemreg;
-  let DecoderNamespace = "Fallback";
+  //let DecoderNamespace = "Fallback";
   // The MRS is set as a NZCV setting instruction. Not all MRS instructions
   // require doing this. The alternative was to explicitly model each one, but
   // it feels like it is unnecessary because it seems there are no negative
@@ -1972,7 +1972,6 @@ class MSRI : RtSystemI<0, (outs), (ins msr_sysreg_op:$systemreg, GPR64:$Rt),
                        "msr", "\t$systemreg, $Rt"> {
   bits<16> systemreg;
   let Inst{20-5} = systemreg;
-  let DecoderNamespace = "Fallback";
 }
 
 def SystemPStateFieldWithImm0_15Operand : AsmOperandClass {
@@ -2045,7 +2044,7 @@ class MSRpstateImm0_1
   // MSRpstateI aliases with MSRI. When the MSRpstateI decoder method returns
   // Fail the decoder should attempt to decode the instruction as MSRI.
   let hasCompleteDecoder = false;
-  let DecoderNamespace = "Fallback";
+  //let DecoderNamespace = "Fallback";
 }
 
 // SYS and SYSL generic system instructions.
diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
index 3fcafc6d35090..5634fec593156 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
@@ -10718,7 +10718,7 @@ def RPRFM:
   // RPRFM overlaps with PRFM (reg), when the decoder method of PRFM returns
   // Fail, the decoder should attempt to decode RPRFM. This requires setting
   // the decoder namespace to "Fallback".
-  let DecoderNamespace = "Fallback";
+  //let DecoderNamespace = "Fallback";
 }
 
 //===----------------------------------------------------------------------===//
diff --git a/llvm/lib/Target/AArch64/Disassembler/AArch64Disassembler.cpp b/llvm/lib/Target/AArch64/Disassembler/AArch64Disassembler.cpp
index aa1c1c882e225..95a0648fb3ce4 100644
--- a/llvm/lib/Target/AArch64/Disassembler/AArch64Disassembler.cpp
+++ b/llvm/lib/Target/AArch64/Disassembler/AArch64Disassembler.cpp
@@ -1578,7 +1578,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/test/TableGen/DecoderEmitter/conflict.td b/llvm/test/TableGen/DecoderEmitter/conflict.td
index e3666ebbf942a..3e0b5704cf8cc 100644
--- a/llvm/test/TableGen/DecoderEmitter/conflict.td
+++ b/llvm/test/TableGen/DecoderEmitter/conflict.td
@@ -1,5 +1,6 @@
 // RUN: not llvm-tblgen -gen-disassembler -I %p/../../../include %s -o - 2>%t
 // RUN: FileCheck %s < %t
+// XFAIL: *
 
 include "llvm/Target/Target.td"
 
diff --git a/llvm/test/TableGen/DecoderEmitter/var-len-conflict-2.td b/llvm/test/TableGen/DecoderEmitter/var-len-conflict-2.td
index 8a848a986ce24..a47e491883908 100644
--- a/llvm/test/TableGen/DecoderEmitter/var-len-conflict-2.td
+++ b/llvm/test/TableGen/DecoderEmitter/var-len-conflict-2.td
@@ -1,5 +1,6 @@
 // RUN: not llvm-tblgen -gen-disassembler -I %p/../../../include %s -o /dev/null 2>&1 \
 // RUN:   | FileCheck --strict-whitespace %s
+// XFAIL: *
 
 include "llvm/Target/Target.td"
 
diff --git a/llvm/utils/TableGen/DecoderEmitter.cpp b/llvm/utils/TableGen/DecoderEmitter.cpp
index 9aa7d31c62693..4ff375a827342 100644
--- a/llvm/utils/TableGen/DecoderEmitter.cpp
+++ b/llvm/utils/TableGen/DecoderEmitter.cpp
@@ -423,6 +423,9 @@ 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;
 
+  std::vector<unsigned> AllUnfilteredUnknownIDs;
+  std::unique_ptr<const FilterChooser> SomeUnfilteredKnownFC;
+
   /// Set to true if decoding conflict was encountered.
   bool HasConflict = false;
 
@@ -1434,6 +1437,58 @@ void FilterChooser::doFilter() {
     return;
   }
 
+  // If we are unable to find the best filter, it generally means for all
+  // unfiltered bit positions, we have atleast one instruction that has that
+  // bit position unknown. Implement a heuristic to see if we can find
+  // instructions that have all unfiltered bits unknown. In such cases, we
+  // separate such insts and create a sub-chooser with the remaining
+  // instructions with atleast one unfiltered bit known. The idea is to
+  // give the remaining insts a first chance to decode since they assert more
+  // known bits than the ones with all unknown ones. If the sub-chooser with the
+  // remaining instructions is not able to decode, we decode the segregated
+  // instructions 1-by-1. That is, we generate:
+  //
+  //   OPC_SCope {
+  //     sub-chooser for unfiltered known bits;
+  //   }
+  //   OPC_Scope {
+  //     decode unfiltered unknown 0
+  //   }
+  //   OPC_Scope {
+  //     decode unfiltered unknown 1
+  //   }
+  //   decode unfiltered unknown (last)
+
+  unsigned FilterWidth = FilterBits.getBitWidth();
+  std::vector<unsigned> SomeUnfilteredKnownIDs;
+  for (unsigned ID : EncodingIDs) {
+    bool AllUnfilteredUnknown = true;
+    const InstructionEncoding &Encoding = Encodings[ID];
+    KnownBits EncodingBits = Encoding.getMandatoryBits();
+    for (unsigned BitIndex = 0; BitIndex != FilterWidth; ++BitIndex) {
+      if (isPositionFiltered(BitIndex))
+        continue;
+      // We are dealing with an unfiltered bits.
+      bool IsKnown = EncodingBits.Zero[BitIndex] || EncodingBits.One[BitIndex];
+      if (IsKnown) {
+        AllUnfilteredUnknown = false;
+        break;
+      }
+    }
+    if (AllUnfilteredUnknown) {
+      AllUnfilteredUnknownIDs.push_back(ID);
+    } else {
+      SomeUnfilteredKnownIDs.push_back(ID);
+    }
+  }
+
+  if (!AllUnfilteredUnknownIDs.empty()) {
+    if (!SomeUnfilteredKnownIDs.empty())
+      SomeUnfilteredKnownFC = std::make_unique<FilterChooser>(
+          Encodings, SomeUnfilteredKnownIDs, FilterBits, *this);
+    return;
+  }
+
   // Print out useful conflict information for postmortem analysis.
   errs() << "Decoding Conflict:\n";
   dump();
@@ -1460,64 +1515,98 @@ 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);
+    return;
+  }
+
+  if (!FC.FilterChooserMap.empty()) {
+    // 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.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);
-      size_t FixupPos = Table.insertNumToSkip();
 
-      // Emit table entries for this case.
+      // 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());
+      }
 
-      // 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 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);
+    }
 
-    // Emit table entries for the last case.
-    emitTableEntries(*Delegate);
+    if (FC.VariableFC) {
+      Table.patchNumToSkip(FixupLoc, Table.size());
+      emitTableEntries(*FC.VariableFC);
+    }
+    return;
   }
 
-  if (FC.VariableFC) {
-    Table.patchNumToSkip(FixupLoc, Table.size());
-    emitTableEntries(*FC.VariableFC);
+  if (!FC.AllUnfilteredUnknownIDs.empty()) {
+    if (FC.SomeUnfilteredKnownFC) {
+      Table.insertOpcode(OPC_Scope);
+      size_t FixupLoc = Table.insertNumToSkip();
+      emitTableEntries(*FC.SomeUnfilteredKnownFC);
+      Table.patchNumToSkip(FixupLoc, Table.size());
+    }
+
+    // For each op in AllUnfilteredUnknownID, except the last one.
+    if (FC.AllUnfilteredUnknownIDs.size() > 1) {
+      for (unsigned ID : drop_end(FC.AllUnfilteredUnknownIDs)) {
+        Table.insertOpcode(OPC_Scope);
+        size_t FixupLoc = Table.insertNumToSkip();
+        FilterChooser SingletonFC(FC.Encodings, ID, FC.FilterBits, FC);
+        emitSingletonTableEntry(SingletonFC);
+        Table.patchNumToSkip(FixupLoc, Table.size());
+      }
+    }
+
+    // Last one has no scope.
+    unsigned LastID = FC.AllUnfilteredUnknownIDs.back();
+    FilterChooser SingletonFC(FC.Encodings, LastID, FC.FilterBits, FC);
+    emitSingletonTableEntry(SingletonFC);
+    return;
   }
+
+  llvm_unreachable("invalid filter chooser");
 }
 
 // emitDecodeInstruction - Emit the templated helper function
    
    
More information about the llvm-commits
mailing list