[llvm] [TableGen] fix tlbgen for EncodingByHwMode (PR #84906)

via llvm-commits llvm-commits at lists.llvm.org
Fri Mar 15 03:37:18 PDT 2024


https://github.com/superZWT123 updated https://github.com/llvm/llvm-project/pull/84906

>From 629b8fdef10031ee5f5588e10487e1e364ac81ec Mon Sep 17 00:00:00 2001
From: z30050559 <z30050559 at huawei.com>
Date: Tue, 12 Mar 2024 10:16:52 +0800
Subject: [PATCH] [TableGen] fix tlbgen for EncodingByHwMode

By using 'HwMode', we can let one instruction have different encodings. But
there are some defects in the original TableGen logic in llvm framework.
1. If we use HwModes to control one instruction's encodings, there will
generate multiple copies of the original DecoderTables to where the
instruction belongs. But for instructions who are not controlled
by HwModes, tablegen still generate multiple copies of DecoderTables for
them, which is an absolutely redundant behavior.
2. If we use HwModes to control one instruction's encodings, when get binary
code for instruction, framework will check all subtargets' HwModes,
but we only set HwModes for subtargets whose instructions' encodings conflict,
those subtargets without HwModes will cause 'llvm_unreachable'.
3. RegInfoByHwMode and EncodingByHwModes cannot coexist, and resolve it by store
HwModes Id in bits.
4. Fix DefaultMode logic for EncodingByHwMode in Decoder and CodeEmitter.
---
 llvm/test/TableGen/HwModeEncodeDecode2.td   |  25 +----
 llvm/test/TableGen/HwModeEncodeDecode3.td   | 100 ++++++++++++--------
 llvm/utils/TableGen/CodeEmitterGen.cpp      |  92 +++++++++++++-----
 llvm/utils/TableGen/CodeGenHwModes.cpp      |   2 +
 llvm/utils/TableGen/DecoderEmitter.cpp      |  67 ++++++++-----
 llvm/utils/TableGen/DisassemblerEmitter.cpp |   2 -
 llvm/utils/TableGen/RegisterInfoEmitter.cpp |  42 +++++++-
 llvm/utils/TableGen/SubtargetEmitter.cpp    |   9 +-
 8 files changed, 224 insertions(+), 115 deletions(-)

diff --git a/llvm/test/TableGen/HwModeEncodeDecode2.td b/llvm/test/TableGen/HwModeEncodeDecode2.td
index 5159501d8148eb..72f705793cc494 100644
--- a/llvm/test/TableGen/HwModeEncodeDecode2.td
+++ b/llvm/test/TableGen/HwModeEncodeDecode2.td
@@ -1,7 +1,5 @@
 // RUN: llvm-tblgen -gen-disassembler -I %p/../../include %s | \
 // RUN:     FileCheck %s --check-prefix=DECODER
-// RUN: llvm-tblgen -gen-disassembler --suppress-per-hwmode-duplicates -I \
-// RUN:     %p/../../include %s | FileCheck %s --check-prefix=DECODER-SUPPRESS
 
 // Test duplicate table suppression for per-HwMode decoders.
 
@@ -76,10 +74,8 @@ let OutOperandList = (outs) in {
     let AsmString = "foo  $factor";
   }
 
-  // Encoding not overridden, different namespace:
-  // In the default case, this instruction is duplicated into two Alt decoder
-  // tables (ModeA and ModeB).
-  // In the suppressed case, this instruction appears in a single decoder table.
+  // Instructions whose DecoderNamespace doesn't contain valid
+  // EncodingInfos attribute, should remain the same as before.
   def unrelated: Instruction {
     let DecoderNamespace = "Alt";
     let InOperandList = (ins i32imm:$factor);
@@ -93,9 +89,7 @@ let OutOperandList = (outs) in {
   }
 }
 
-// DECODER-LABEL: DecoderTableAlt_ModeA32[] =
-// DECODER-DAG: Opcode: unrelated
-// DECODER-LABEL: DecoderTableAlt_ModeB32[] =
+// DECODER-LABEL: DecoderTableAlt32[] =
 // DECODER-DAG: Opcode: unrelated
 // DECODER-LABEL: DecoderTable_ModeA32[] =
 // DECODER-DAG: Opcode: fooTypeEncA:foo
@@ -104,16 +98,3 @@ let OutOperandList = (outs) in {
 // DECODER-DAG: Opcode: fooTypeEncB:foo
 // DECODER-DAG: Opcode: fooTypeEncA:baz
 // DECODER-DAG: Opcode: bar
-
-
-// DECODER-SUPPRESS-LABEL: DecoderTableAlt_AllModes32[] =
-// DECODER-SUPPRESS-DAG: Opcode: unrelated
-// DECODER-SUPPRESS-LABEL: DecoderTable_AllModes32[] =
-// DECODER-SUPPRESS-DAG: Opcode: bar
-// DECODER-SUPPRESS-LABEL: DecoderTable_ModeA32[] =
-// DECODER-SUPPRESS-DAG: Opcode: fooTypeEncA:foo
-// DECODER-SUPPRESS-NOT: Opcode: bar
-// DECODER-SUPPRESS-LABEL: DecoderTable_ModeB32[] =
-// DECODER-SUPPRESS-DAG: Opcode: fooTypeEncB:foo
-// DECODER-SUPPRESS-DAG: Opcode: fooTypeEncA:baz
-// DECODER-SUPPRESS-NOT: Opcode: bar
diff --git a/llvm/test/TableGen/HwModeEncodeDecode3.td b/llvm/test/TableGen/HwModeEncodeDecode3.td
index 406e52d25be706..517ec6df10fee6 100644
--- a/llvm/test/TableGen/HwModeEncodeDecode3.td
+++ b/llvm/test/TableGen/HwModeEncodeDecode3.td
@@ -2,8 +2,6 @@
 // RUN:     FileCheck %s --check-prefix=ENCODER
 // RUN: llvm-tblgen -gen-disassembler -I %p/../../include %s | \
 // RUN:     FileCheck %s --check-prefix=DECODER
-// RUN: llvm-tblgen -gen-disassembler --suppress-per-hwmode-duplicates -I \
-// RUN:     %p/../../include %s | FileCheck %s --check-prefix=DECODER-SUPPRESS
 
 include "llvm/Target/Target.td"
 
@@ -20,8 +18,8 @@ def Myi32 : Operand<i32> {
 def HasA : Predicate<"Subtarget->hasA()">;
 def HasB : Predicate<"Subtarget->hasB()">;
 
-def ModeA : HwMode<"+a", [HasA]>;
-def ModeB : HwMode<"+b", [HasB]>;
+def ModeA : HwMode<"+a", [HasA]>; // hwmode ID 1
+def ModeB : HwMode<"+b", [HasB]>; // hwmode ID 2
 
 
 def fooTypeEncDefault : InstructionEncoding {
@@ -98,17 +96,24 @@ def unrelated: Instruction {
   let AsmString = "unrelated  $factor";
 }
 
+// DecoderTable64 is assigned with 'DefaultMode',
+// the table name should remain the same as before.
+// DECODER-LABEL: DecoderTable64[] =
+// DECODER-DAG: Opcode: fooTypeEncDefault:foo
 
-// DECODER-LABEL: DecoderTableAlt_DefaultMode32[] =
-// DECODER-DAG: Opcode: unrelated
-// DECODER-LABEL: DecoderTableAlt_ModeA32[] =
-// DECODER-DAG: Opcode: unrelated
-// DECODER-LABEL: DecoderTableAlt_ModeB32[] =
+// DecoderTable without any valid HwModes
+// should not have any suffix in table name.
+// The table should remain the same as before.
+// DECODER-LABEL: DecoderTableAlt32[] =
 // DECODER-DAG: Opcode: unrelated
-// DECODER-LABEL: DecoderTable_DefaultMode32[] =
-// DECODER-DAG: Opcode: bar
-// DECODER-LABEL: DecoderTable_DefaultMode64[] =
-// DECODER-DAG: Opcode: fooTypeEncDefault:foo
+// DECODER-NOT: DecoderTableAlt_ModeA32[]
+// DECODER-NOT: DecoderTableAlt_ModeB32[]
+
+// The DecoderTable32 contains two valid hwmodes, we will
+// generate two tables corresponding to these hwmodes.
+// Still want to say we didn't assign 'DefaultMode' to DecoderTable32,
+// instead we assign 'DefaultMode' to DecoderTable64, so
+// DecoderTable32 won't appear here.
 // DECODER-LABEL: DecoderTable_ModeA32[] =
 // DECODER-DAG: Opcode: fooTypeEncA:foo
 // DECODER-DAG: Opcode: bar
@@ -118,30 +123,24 @@ def unrelated: Instruction {
 // DECODER-DAG: Opcode: bar
 
 
-// DECODER-SUPPRESS-LABEL: DecoderTableAlt_AllModes32[] =
-// DECODER-SUPPRESS-DAG: Opcode: unrelated
-// DECODER-SUPPRESS-LABEL: DecoderTable_AllModes32[] =
-// DECODER-SUPPRESS-DAG: Opcode: bar
-// DECODER-SUPPRESS-LABEL: DecoderTable_DefaultMode64[] =
-// DECODER-SUPPRESS-NOT: Opcode: bar
-// DECODER-SUPPRESS-DAG: Opcode: fooTypeEncDefault:foo
-// DECODER-SUPPRESS-LABEL: DecoderTable_ModeA32[] =
-// DECODER-SUPPRESS-DAG: Opcode: fooTypeEncA:foo
-// DECODER-SUPPRESS-NOT: Opcode: bar
-// DECODER-SUPPRESS-LABEL: DecoderTable_ModeB32[] =
-// DECODER-SUPPRESS-DAG: Opcode: fooTypeEncB:foo
-// DECODER-SUPPRESS-DAG: Opcode: fooTypeEncA:baz
-// DECODER-SUPPRESS-NOT: Opcode: bar
-
-// ENCODER-LABEL:   static const uint64_t InstBits_DefaultMode[] = {
+// For 'bar' and 'unrelated', we didn't assign any hwmodes for them,
+// they should keep the same in the following three tables.
+// For 'foo' we assigned three hwmodes(includes 'DefaultMode')
+// it's encodings should be different in the following three tables.
+// For 'baz' we only assigned ModeB for it, to avoid empty encoding
+// we assigned the encoding of ModeB to ModeA and DefaultMode(Even though
+// they will not be used).
+// ENCODER-LABEL:   static const uint64_t InstBits[] = {
 // ENCODER:         UINT64_C(2),        // bar
-// ENCODER:         UINT64_C(0),        // baz
+// To avoid empty encoding, we choose the encoding of ModeB for baz here
+// ENCODER:         UINT64_C(12),        // baz
 // ENCODER:         UINT64_C(8),        // foo
 // ENCODER:         UINT64_C(2),        // unrelated
 
 // ENCODER-LABEL:   static const uint64_t InstBits_ModeA[] = {
 // ENCODER:         UINT64_C(2),        // bar
-// ENCODER:         UINT64_C(0),        // baz
+// To avoid empty encoding, we choose the encoding of ModeB for baz here
+// ENCODER:         UINT64_C(12),        // baz
 // ENCODER:         UINT64_C(12),       // foo
 // ENCODER:         UINT64_C(2),        // unrelated
 
@@ -151,18 +150,39 @@ def unrelated: Instruction {
 // ENCODER:         UINT64_C(3),        // foo
 // ENCODER:         UINT64_C(2),        // unrelated
 
+// Sink the logic of hwmodes selection to per instruction level.
+// ENCODER-LABEL: case ::bar:
+// ENCODER: op = getMachineOpValue
+// ENCODER-LABEL: case ::baz: {
+// ENCODER:  unsigned HwMode = STI.getHwMode();
+// ENCODER:  HwMode &= 2;
+// ENCODER:  switch (HwMode) {
+// ENCODER:  default: llvm_unreachable("Unknown hardware mode!"); break;
+// ENCODER:  case 2: InstBitsByHw = InstBits_ModeB; break;
+// ENCODER:  };
+// ENCODER:  Value = InstBitsByHw[opcode];
+// ENCODER:  switch (HwMode) {
+// ENCODER:  default: llvm_unreachable("Unhandled HwMode");
+// ENCODER:  case 2: {
+// ENCODER:  op = getMachineOpValue
+// ENCODER-LABEL: case ::foo: {
 // ENCODER:  unsigned HwMode = STI.getHwMode();
+// ENCODER:  HwMode &= 3;
 // ENCODER:  switch (HwMode) {
 // ENCODER:  default: llvm_unreachable("Unknown hardware mode!"); break;
-// ENCODER:  case 0: InstBits = InstBits_DefaultMode; break;
-// ENCODER:  case 1: InstBits = InstBits_ModeA; break;
-// ENCODER:  case 2: InstBits = InstBits_ModeB; break;
+// ENCODER:  case 0: InstBitsByHw = InstBits; break;
+// ENCODER:  case 1: InstBitsByHw = InstBits_ModeA; break;
+// ENCODER:  case 2: InstBitsByHw = InstBits_ModeB; break;
 // ENCODER:  };
+// ENCODER:  Value = InstBitsByHw[opcode];
+// ENCODER:  switch (HwMode) {
+// ENCODER:  default: llvm_unreachable("Unhandled HwMode");
+// ENCODER:  case 0: {
+// ENCODER:  op = getMachineOpValue
+// ENCODER:  case 1: {
+// ENCODER:  op = getMachineOpValue
+// ENCODER:  case 2: {
+// ENCODER:  op = getMachineOpValue
+
 
-// ENCODER:     case ::foo: {
-// ENCODER:      switch (HwMode) {
-// ENCODER:      default: llvm_unreachable("Unhandled HwMode");
-// ENCODER:      case 0: {
-// ENCODER:      case 1: {
-// ENCODER:      case 2: {
 
diff --git a/llvm/utils/TableGen/CodeEmitterGen.cpp b/llvm/utils/TableGen/CodeEmitterGen.cpp
index 9194c13ccdcb08..bdfb40ef885ee9 100644
--- a/llvm/utils/TableGen/CodeEmitterGen.cpp
+++ b/llvm/utils/TableGen/CodeEmitterGen.cpp
@@ -68,7 +68,7 @@ class CodeEmitterGen {
 
   void emitInstructionBaseValues(
       raw_ostream &o, ArrayRef<const CodeGenInstruction *> NumberedInstructions,
-      CodeGenTarget &Target, int HwMode = -1);
+      CodeGenTarget &Target, unsigned HwMode = DefaultMode);
   void
   emitCaseMap(raw_ostream &o,
               const std::map<std::string, std::vector<std::string>> &CaseMap);
@@ -281,7 +281,7 @@ std::pair<std::string, std::string>
 CodeEmitterGen::getInstructionCases(Record *R, CodeGenTarget &Target) {
   std::string Case, BitOffsetCase;
 
-  auto append = [&](const char *S) {
+  auto append = [&](const std::string &S) {
     Case += S;
     BitOffsetCase += S;
   };
@@ -290,11 +290,54 @@ CodeEmitterGen::getInstructionCases(Record *R, CodeGenTarget &Target) {
     if (auto *DI = dyn_cast_or_null<DefInit>(RV->getValue())) {
       const CodeGenHwModes &HWM = Target.getHwModes();
       EncodingInfoByHwMode EBM(DI->getDef(), HWM);
+      unsigned EncodingHwModesInBits = DefaultMode;
+      for (auto &[ModeId, Encoding] : EBM) {
+        // DefaultMode is 0, skip it.
+        if (ModeId != DefaultMode)
+          EncodingHwModesInBits |= (1 << (ModeId - 1));
+      }
+
+      // Get HwModes for this Instr by bitwise AND operations,
+      // and find the table to which this instr and hwmode belong.
+      append("      unsigned HwMode = STI.getHwMode();\n");
+      append("      HwMode &= " + itostr(EncodingHwModesInBits) + ";\n");
+      append("      switch (HwMode) {\n");
+      append("      default: llvm_unreachable(\"Unknown hardware mode!\"); "
+             "break;\n");
+      for (auto &[ModeId, Encoding] : EBM) {
+        if (ModeId == DefaultMode) {
+          append("      case " + itostr(DefaultMode) +
+                 ": InstBitsByHw = InstBits");
+        } else {
+          append("      case " + itostr(1 << (ModeId - 1)) +
+                 ": InstBitsByHw = InstBits_" +
+                 std::string(HWM.getMode(ModeId).Name));
+        }
+        append("; break;\n");
+      }
+      append("      };\n");
+
+      // We need to remodify the 'Inst' value from the table we found above.
+      if (UseAPInt) {
+        int NumWords = APInt::getNumWords(BitWidth);
+        append("      Inst = APInt(" + itostr(BitWidth));
+        append(", ArrayRef(InstBitsByHw + opcode * " + itostr(NumWords) + ", " +
+               itostr(NumWords));
+        append("));\n");
+        append("      Value = Inst;\n");
+      } else {
+        append("      Value = InstBitsByHw[opcode];\n");
+      }
+
       append("      switch (HwMode) {\n");
       append("      default: llvm_unreachable(\"Unhandled HwMode\");\n");
-      for (auto &KV : EBM) {
-        append(("      case " + itostr(KV.first) + ": {\n").c_str());
-        addInstructionCasesForEncoding(R, KV.second, Target, Case,
+      for (auto &[ModeId, Encoding] : EBM) {
+        if (ModeId == DefaultMode) {
+          append("      case " + itostr(DefaultMode) + ": {\n");
+        } else {
+          append("      case " + itostr(1 << (ModeId - 1)) + ": {\n");
+        }
+        addInstructionCasesForEncoding(R, Encoding, Target, Case,
                                        BitOffsetCase);
         append("      break;\n");
         append("      }\n");
@@ -360,9 +403,9 @@ static void emitInstBits(raw_ostream &OS, const APInt &Bits) {
 
 void CodeEmitterGen::emitInstructionBaseValues(
     raw_ostream &o, ArrayRef<const CodeGenInstruction *> NumberedInstructions,
-    CodeGenTarget &Target, int HwMode) {
+    CodeGenTarget &Target, unsigned HwMode) {
   const CodeGenHwModes &HWM = Target.getHwModes();
-  if (HwMode == -1)
+  if (HwMode == DefaultMode)
     o << "  static const uint64_t InstBits[] = {\n";
   else
     o << "  static const uint64_t InstBits_"
@@ -383,8 +426,17 @@ void CodeEmitterGen::emitInstructionBaseValues(
     if (const RecordVal *RV = R->getValue("EncodingInfos")) {
       if (auto *DI = dyn_cast_or_null<DefInit>(RV->getValue())) {
         EncodingInfoByHwMode EBM(DI->getDef(), HWM);
-        if (EBM.hasMode(HwMode))
+        if (EBM.hasMode(HwMode)) {
           EncodingDef = EBM.get(HwMode);
+        } else {
+          // If this Instr dosen't have this HwMode, just choose
+          // the encoding from the first HwMode. Otherwise, the encoding
+          // info would be empty.
+          for (auto &[ModeId, Encoding] : EBM) {
+            EncodingDef = Encoding;
+            break;
+          }
+        }
       }
     }
     BitsInit *BI = EncodingDef->getValueAsBitsInit("Inst");
@@ -479,23 +531,17 @@ void CodeEmitterGen::run(raw_ostream &o) {
     }
 
     // Emit instruction base values
-    if (HwModes.empty()) {
-      emitInstructionBaseValues(o, NumberedInstructions, Target, -1);
-    } else {
-      for (unsigned HwMode : HwModes)
-        emitInstructionBaseValues(o, NumberedInstructions, Target, (int)HwMode);
-    }
-
+    emitInstructionBaseValues(o, NumberedInstructions, Target, DefaultMode);
     if (!HwModes.empty()) {
-      o << "  const uint64_t *InstBits;\n";
-      o << "  unsigned HwMode = STI.getHwMode();\n";
-      o << "  switch (HwMode) {\n";
-      o << "  default: llvm_unreachable(\"Unknown hardware mode!\"); break;\n";
-      for (unsigned I : HwModes) {
-        o << "  case " << I << ": InstBits = InstBits_"
-          << HWM.getModeName(I, /*IncludeDefault=*/true) << "; break;\n";
+      // Emit table for instrs whose encodings are controlled by HwModes.
+      for (unsigned HwMode : HwModes) {
+        if (HwMode == DefaultMode)
+          continue;
+        emitInstructionBaseValues(o, NumberedInstructions, Target, HwMode);
       }
-      o << "  };\n";
+
+      // This pointer will be assigned to the HwMode table later.
+      o << "  const uint64_t *InstBitsByHw;\n";
     }
 
     // Map to accumulate all the cases.
diff --git a/llvm/utils/TableGen/CodeGenHwModes.cpp b/llvm/utils/TableGen/CodeGenHwModes.cpp
index fec74d29c8bbbd..124cfbaf4fb747 100644
--- a/llvm/utils/TableGen/CodeGenHwModes.cpp
+++ b/llvm/utils/TableGen/CodeGenHwModes.cpp
@@ -74,6 +74,8 @@ CodeGenHwModes::CodeGenHwModes(RecordKeeper &RK) : Records(RK) {
     ModeIds.insert(std::pair(R, Modes.size()));
   }
 
+  assert(Modes.size() <= 32 && "number of HwModes exceeds maximum of 32");
+
   for (Record *R : Records.getAllDerivedDefinitions("HwModeSelect")) {
     auto P = ModeSelects.emplace(std::pair(R, HwModeSelect(R, *this)));
     assert(P.second);
diff --git a/llvm/utils/TableGen/DecoderEmitter.cpp b/llvm/utils/TableGen/DecoderEmitter.cpp
index 88f24523813828..313fc5a3ba31c9 100644
--- a/llvm/utils/TableGen/DecoderEmitter.cpp
+++ b/llvm/utils/TableGen/DecoderEmitter.cpp
@@ -52,13 +52,6 @@ using namespace llvm;
 
 #define DEBUG_TYPE "decoder-emitter"
 
-extern cl::OptionCategory DisassemblerEmitterCat;
-
-cl::opt<bool> DecoderEmitterSuppressDuplicates(
-    "suppress-per-hwmode-duplicates",
-    cl::desc("Suppress duplication of instrs into per-HwMode decoder tables"),
-    cl::init(false), cl::cat(DisassemblerEmitterCat));
-
 namespace {
 
 STATISTIC(NumEncodings, "Number of encodings considered");
@@ -128,6 +121,8 @@ struct EncodingIDAndOpcode {
 };
 
 using EncodingIDsVec = std::vector<EncodingIDAndOpcode>;
+using NamespacesHwModesMap =
+    std::map<std::string, std::map<StringRef, unsigned>>;
 
 raw_ostream &operator<<(raw_ostream &OS, const EncodingAndInst &Value) {
   if (Value.EncodingDef != Value.Inst->TheDef)
@@ -2451,14 +2446,23 @@ static void emitCheck(formatted_raw_ostream &OS) {
 
 // Collect all HwModes referenced by the target for encoding purposes,
 // returning a vector of corresponding names.
-static void
-collectHwModesReferencedForEncodings(const CodeGenHwModes &HWM,
-                                     std::vector<StringRef> &Names) {
+static void collectHwModesReferencedForEncodings(
+    const CodeGenHwModes &HWM, std::vector<StringRef> &Names,
+    NamespacesHwModesMap &NamespacesWithHwModes) {
   SmallBitVector BV(HWM.getNumModeIds());
   for (const auto &MS : HWM.getHwModeSelects()) {
     for (const HwModeSelect::PairType &P : MS.second.Items) {
-      if (P.second->isSubClassOf("InstructionEncoding"))
+      if (P.second->isSubClassOf("InstructionEncoding")) {
+        std::string DecoderNamespace =
+            std::string(P.second->getValueAsString("DecoderNamespace"));
+        if (P.first == DefaultMode) {
+          NamespacesWithHwModes[DecoderNamespace][""] = 1;
+        } else {
+          NamespacesWithHwModes[DecoderNamespace][HWM.getMode(P.first).Name] =
+              1;
+        }
         BV.set(P.first);
+      }
     }
   }
   transform(BV.set_bits(), std::back_inserter(Names), [&HWM](const int &M) {
@@ -2489,10 +2493,12 @@ void DecoderEmitter::run(raw_ostream &o) {
   // Parameterize the decoders based on namespace and instruction width.
 
   // First, collect all encoding-related HwModes referenced by the target.
+  // Also collect the namespaces which contain valid hwmodes.
   // If HwModeNames is empty, add the empty string so we always have one HwMode.
   const CodeGenHwModes &HWM = Target.getHwModes();
   std::vector<StringRef> HwModeNames;
-  collectHwModesReferencedForEncodings(HWM, HwModeNames);
+  NamespacesHwModesMap NamespacesWithHwModes;
+  collectHwModesReferencedForEncodings(HWM, HwModeNames, NamespacesWithHwModes);
   if (HwModeNames.empty())
     HwModeNames.push_back("");
 
@@ -2503,21 +2509,34 @@ void DecoderEmitter::run(raw_ostream &o) {
     if (const RecordVal *RV = InstDef->getValue("EncodingInfos")) {
       if (DefInit *DI = dyn_cast_or_null<DefInit>(RV->getValue())) {
         EncodingInfoByHwMode EBM(DI->getDef(), HWM);
-        for (auto &KV : EBM)
-          NumberedEncodings.emplace_back(
-              KV.second, NumberedInstruction,
-              HWM.getModeName(KV.first, /*IncludeDefault=*/true));
+        for (auto &[ModeId, Encoding] : EBM) {
+          // DecoderTable with DefaultMode should not have any suffix
+          if (ModeId == DefaultMode) {
+            NumberedEncodings.emplace_back(Encoding, NumberedInstruction, "");
+          } else {
+            NumberedEncodings.emplace_back(Encoding, NumberedInstruction,
+                                           HWM.getMode(ModeId).Name);
+          }
+        }
         continue;
       }
     }
-    // This instruction is encoded the same on all HwModes. Emit it for all
-    // HwModes by default, otherwise leave it in a single common table.
-    if (DecoderEmitterSuppressDuplicates) {
-      NumberedEncodings.emplace_back(InstDef, NumberedInstruction, "AllModes");
-    } else {
-      for (StringRef HwModeName : HwModeNames)
-        NumberedEncodings.emplace_back(InstDef, NumberedInstruction,
-                                       HwModeName);
+    // This instruction is encoded the same on all HwModes.
+    // If this instruction shares a DecoderNamespace with instructions who have
+    // the encodingByHwMode attribute, emit it for all HwModes. Otherwise,
+    // only emit encoding once.
+    std::string DecoderNamespace = std::string(
+        NumberedInstruction->TheDef->getValueAsString("DecoderNamespace"));
+    for (StringRef HwModeName : HwModeNames) {
+      if (NamespacesWithHwModes.count(DecoderNamespace) > 0) {
+        if (NamespacesWithHwModes[DecoderNamespace].count(HwModeName) > 0) {
+          NumberedEncodings.emplace_back(InstDef, NumberedInstruction,
+                                         HwModeName);
+        }
+      } else {
+        NumberedEncodings.emplace_back(InstDef, NumberedInstruction, "");
+        break;
+      }
     }
   }
   for (const auto &NumberedAlias :
diff --git a/llvm/utils/TableGen/DisassemblerEmitter.cpp b/llvm/utils/TableGen/DisassemblerEmitter.cpp
index 2d653af4d3025e..ae6a8ef22bc8ca 100644
--- a/llvm/utils/TableGen/DisassemblerEmitter.cpp
+++ b/llvm/utils/TableGen/DisassemblerEmitter.cpp
@@ -131,7 +131,5 @@ static void EmitDisassembler(RecordKeeper &Records, raw_ostream &OS) {
   EmitDecoder(Records, OS, PredicateNamespace);
 }
 
-cl::OptionCategory DisassemblerEmitterCat("Options for -gen-disassembler");
-
 static TableGen::Emitter::Opt X("gen-disassembler", EmitDisassembler,
                                 "Generate disassembler");
diff --git a/llvm/utils/TableGen/RegisterInfoEmitter.cpp b/llvm/utils/TableGen/RegisterInfoEmitter.cpp
index d074e31c624588..aee668e6b9ddce 100644
--- a/llvm/utils/TableGen/RegisterInfoEmitter.cpp
+++ b/llvm/utils/TableGen/RegisterInfoEmitter.cpp
@@ -1640,6 +1640,46 @@ void RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, CodeGenTarget &Target,
 
   EmitRegMappingTables(OS, Regs, true);
 
+  // By using bitwise AND operations, we collect the HwModes
+  // who are controlling the RegInfos.
+  unsigned RegHwModesInBits = 0;
+  for (const auto &RC : RegisterClasses) {
+    Record *Def = RC.getDef();
+    if (!Def)
+      continue;
+
+    if (const RecordVal *RV = Def->getValue("RegInfos")) {
+      if (DefInit *DI = dyn_cast_or_null<DefInit>(RV->getValue())) {
+        RegSizeInfoByHwMode RSI(DI->getDef(), CGH);
+        for (auto &[ModeId, RegInfo] : RSI) {
+          // Default mode is 0, and skip it.
+          if (ModeId)
+            RegHwModesInBits |= (1 << (ModeId - 1));
+        }
+      }
+    }
+  }
+
+  OS << "static unsigned getRegInfosHwModeId(unsigned HwModesInBits) {\n"
+     << "  HwModesInBits &= " << RegHwModesInBits << ";\n"
+     << "  unsigned RegsHwModeId = 0;\n"
+     << "\n"
+     << "  for (unsigned Idx = 1; Idx <= 32; Idx ++) {\n"
+     << "    if (HwModesInBits & (1 << (Idx - 1))) {\n"
+     << "      // One subtarget can only own one regs HwMode\n"
+     << "      if (RegsHwModeId != 0) {\n"
+     << "        llvm_unreachable(\"One subtarget only can own one kind of "
+        "regs hwmode\");\n"
+     << "      } else {\n"
+     << "        RegsHwModeId = Idx;\n"
+     << "        // Don't break, continue checking\n"
+     << "      }\n"
+     << "    }\n"
+     << "  }\n"
+     << "\n"
+     << "  return RegsHwModeId;\n"
+     << "}\n\n";
+
   OS << ClassName << "::\n"
      << ClassName
      << "(unsigned RA, unsigned DwarfFlavour, unsigned EHFlavour,\n"
@@ -1649,7 +1689,7 @@ void RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, CodeGenTarget &Target,
      << "             SubRegIndexNameTable, SubRegIndexLaneMaskTable,\n"
      << "             ";
   printMask(OS, RegBank.CoveringLanes);
-  OS << ", RegClassInfos, VTLists, HwMode) {\n"
+  OS << ", RegClassInfos, VTLists, getRegInfosHwModeId(HwMode)) {\n"
      << "  InitMCRegisterInfo(" << TargetName << "RegDesc, " << Regs.size() + 1
      << ", RA, PC,\n                     " << TargetName
      << "MCRegisterClasses, " << RegisterClasses.size() << ",\n"
diff --git a/llvm/utils/TableGen/SubtargetEmitter.cpp b/llvm/utils/TableGen/SubtargetEmitter.cpp
index d350d7de139f6e..0aeb8025b2aad2 100644
--- a/llvm/utils/TableGen/SubtargetEmitter.cpp
+++ b/llvm/utils/TableGen/SubtargetEmitter.cpp
@@ -1782,12 +1782,15 @@ void SubtargetEmitter::EmitHwModeCheck(const std::string &ClassName,
     return;
 
   OS << "unsigned " << ClassName << "::getHwMode() const {\n";
+  OS << "  // Collect Hw Modes and store them in bits\n";
+  OS << "  unsigned Modes = 0;\n";
   for (unsigned M = 1, NumModes = CGH.getNumModeIds(); M != NumModes; ++M) {
     const HwMode &HM = CGH.getMode(M);
-    OS << "  if (checkFeatures(\"" << HM.Features << "\")) return " << M
-       << ";\n";
+    // Since mode Id >= 1, So -1 is safe here
+    OS << "  if (checkFeatures(\"" << HM.Features << "\")) Modes |= (1 << "
+       << (M - 1) << ");\n";
   }
-  OS << "  return 0;\n}\n";
+  OS << "  return Modes;\n}\n";
 }
 
 void SubtargetEmitter::emitGetMacroFusions(const std::string &ClassName,



More information about the llvm-commits mailing list