[llvm] [TableGen] Use bitwise operations to access HwMode ID. (PR #88377)

via llvm-commits llvm-commits at lists.llvm.org
Tue Apr 30 00:40:16 PDT 2024


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

>From 28bc4e4e1ac55c892f692a03a9673f13568aa33f Mon Sep 17 00:00:00 2001
From: z30050559 <zhengwentao3 at huawei.com>
Date: Tue, 26 Mar 2024 15:20:58 +0800
Subject: [PATCH 1/2] [TableGen] Use bitwise operations to access HwMode ID.

1. Bitwise operations are used to access HwMode, allowing for the
   coexistence of HwMode IDs for different features (such as RegInfo
   and EncodingInfo). This will provide better scalability for HwMode.
   Currently, most users utilize HwMode primarily for configuring
   Register-related information, and few use it for configuring Encoding.
   The limited scalability of HwMode has been a significant factor in this
   usage pattern.
2. Sink the HwMode Encodings selection logic down to per instruction level,
   this makes the logic for choosing encodings clearer and provides better
   error messages.
3. Add some HwMode ID conflict detection to the getHwMode() interface.
---
 llvm/include/llvm/MC/MCSubtargetInfo.h        |  18 +-
 llvm/test/TableGen/HwModeBitSet.td            | 162 ++++++++++++++++++
 llvm/test/TableGen/HwModeEncodeDecode3.td     |  78 +++++++--
 llvm/utils/TableGen/CodeEmitterGen.cpp        |  93 +++++++---
 llvm/utils/TableGen/Common/CodeGenHwModes.cpp |   2 +
 llvm/utils/TableGen/SubtargetEmitter.cpp      |  77 ++++++++-
 6 files changed, 380 insertions(+), 50 deletions(-)
 create mode 100644 llvm/test/TableGen/HwModeBitSet.td

diff --git a/llvm/include/llvm/MC/MCSubtargetInfo.h b/llvm/include/llvm/MC/MCSubtargetInfo.h
index f172a799aa3331..70b14665ea9ebd 100644
--- a/llvm/include/llvm/MC/MCSubtargetInfo.h
+++ b/llvm/include/llvm/MC/MCSubtargetInfo.h
@@ -240,7 +240,23 @@ class MCSubtargetInfo {
     return ProcFeatures;
   }
 
-  virtual unsigned getHwMode() const { return 0; }
+  /// HwMode ID will be stored as bits, allowing users to pull the specific
+  /// HwMode ID (like RegInfo HwMode ID) from the bits as needed. This enables
+  /// users to control multiple features with one hwmode (as previously) or use
+  /// different hwmodes to control different features.
+  enum HwModeType {
+    HwMode_Default,   // Return the smallest HwMode ID of current subtarget.
+    HwMode_ValueType, // Return the HwMode ID that controls the ValueType.
+    HwMode_RegInfo,   // Return the HwMode ID that controls the RegSizeInfo and
+                      // SubRegRange.
+    HwMode_EncodingInfo // Return the HwMode ID that controls the EncodingInfo.
+  };
+
+  virtual unsigned getHwModeSet() const { return 0; }
+
+  virtual unsigned getHwMode(enum HwModeType type = HwMode_Default) const {
+    return 0;
+  }
 
   /// Return the cache size in bytes for the given level of cache.
   /// Level is zero-based, so a value of zero means the first level of
diff --git a/llvm/test/TableGen/HwModeBitSet.td b/llvm/test/TableGen/HwModeBitSet.td
new file mode 100644
index 00000000000000..c642906f6f4a88
--- /dev/null
+++ b/llvm/test/TableGen/HwModeBitSet.td
@@ -0,0 +1,162 @@
+// RUN: llvm-tblgen -gen-register-info -register-info-debug -I %p/../../include %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=CHECK-REG
+// RUN: llvm-tblgen -gen-subtarget -I %p/../../include %s -o - 2>&1 | FileCheck %s --check-prefix=CHECK-SUBTARGET
+
+include "llvm/Target/Target.td"
+
+def TestTargetInstrInfo : InstrInfo;
+
+def TestTarget : Target {
+  let InstructionSet = TestTargetInstrInfo;
+}
+
+def TestMode : HwMode<"+feat", []>;
+def TestMode1 : HwMode<"+feat1", []>;
+def TestMode2 : HwMode<"+feat2", []>;
+
+class MyReg<string n>
+  : Register<n> {
+  let Namespace = "Test";
+}
+
+class MyClass<int size, list<ValueType> types, dag registers>
+  : RegisterClass<"Test", types, size, registers> {
+  let Size = size;
+}
+
+def X0 : MyReg<"x0">;
+def X1 : MyReg<"x1">;
+def X2 : MyReg<"x2">;
+def X3 : MyReg<"x3">;
+def X4 : MyReg<"x4">;
+def X5 : MyReg<"x5">;
+def X6 : MyReg<"x6">;
+def X7 : MyReg<"x7">;
+def X8 : MyReg<"x8">;
+def X9 : MyReg<"x9">;
+def X10 : MyReg<"x10">;
+def X11 : MyReg<"x11">;
+def X12 : MyReg<"x12">;
+def X13 : MyReg<"x13">;
+def X14 : MyReg<"x14">;
+def X15 : MyReg<"x15">;
+
+def ValueModeVT : ValueTypeByHwMode<[DefaultMode, TestMode, TestMode1],
+                                    [i32,  i64, f32]>;
+
+let RegInfos = RegInfoByHwMode<[DefaultMode, TestMode],
+                               [RegInfo<32,32,32>, RegInfo<64,64,64>]> in
+def XRegs : MyClass<32, [ValueModeVT], (sequence "X%u", 0, 15)>;
+
+def sub_even : SubRegIndex<32> {
+  let SubRegRanges = SubRegRangeByHwMode<[DefaultMode, TestMode],
+                                         [SubRegRange<32>, SubRegRange<64>]>;
+}
+def sub_odd  : SubRegIndex<32, 32> {
+  let SubRegRanges = SubRegRangeByHwMode<[DefaultMode, TestMode],
+                                         [SubRegRange<32, 32>, SubRegRange<64, 64>]>;
+}
+
+def XPairs : RegisterTuples<[sub_even, sub_odd],
+                            [(decimate (rotl XRegs, 0), 2),
+                             (decimate (rotl XRegs, 1), 2)]>;
+
+let RegInfos = RegInfoByHwMode<[DefaultMode, TestMode],
+                               [RegInfo<64,64,32>, RegInfo<128,128,64>]> in
+def XPairsClass : MyClass<64, [untyped], (add XPairs)>;
+
+// Modes who are not controlling Register related features will be manipulated
+// the same as DefaultMode.
+// CHECK-REG-LABEL: RegisterClass XRegs:
+// CHECK-REG: SpillSize: { Default:32 TestMode:64 TestMode1:32 TestMode2:32 }
+// CHECK-REG: SpillAlignment: { Default:32 TestMode:64 TestMode1:32 TestMode2:32 }
+// CHECK-REG: Regs: X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+
+// CHECK-REG-LABEL: RegisterClass XPairsClass:
+// CHECK-REG: SpillSize: { Default:64 TestMode:128 TestMode1:64 TestMode2:64 }
+// CHECK-REG: SpillAlignment: { Default:32 TestMode:64 TestMode1:32 TestMode2:32 }
+// CHECK-REG: CoveredBySubRegs: 1
+// CHECK-REG: Regs: X0_X1 X2_X3 X4_X5 X6_X7 X8_X9 X10_X11 X12_X13 X14_X15
+
+// CHECK-REG-LABEL: SubRegIndex sub_even:
+// CHECK-REG: Offset: { Default:0 TestMode:0 TestMode1:0 TestMode2:0 }
+// CHECK-REG: Size: { Default:32 TestMode:64 TestMode1:32 TestMode2:32 }
+// CHECK-REG-LABEL: SubRegIndex sub_odd:
+// CHECK-REG: Offset: { Default:32 TestMode:64 TestMode1:32 TestMode2:32 }
+// CHECK-REG: Size: { Default:32 TestMode:64 TestMode1:32 TestMode2:32 }
+
+//============================================================================//
+//--------------------- Encoding/Decoding parts ------------------------------//
+//============================================================================//
+def fooTypeEncDefault : InstructionEncoding {
+  let Size = 8;
+  field bits<64> SoftFail = 0;
+  bits<64> Inst;
+  bits<8> factor;
+  let Inst{7...0} = factor;
+  let Inst{3...2} = 0b10;
+  let Inst{1...0} = 0b00;
+}
+
+def fooTypeEncA : InstructionEncoding {
+  let Size = 4;
+  field bits<32> SoftFail = 0;
+  bits<32> Inst;
+  bits<8> factor;
+  let Inst{7...0} = factor;
+  let Inst{3...2} = 0b11;
+  let Inst{1...0} = 0b00;
+}
+
+
+def foo : Instruction {
+  let OutOperandList = (outs);
+  let InOperandList = (ins i32imm:$factor);
+  let EncodingInfos = EncodingByHwMode<
+    [TestMode2, DefaultMode], [fooTypeEncA, fooTypeEncDefault]
+  >;
+  let AsmString = "foo  $factor";
+}
+
+// CHECK-SUBTARGET-LABEL: unsigned TestTargetGenSubtargetInfo::getHwModeSet() const {
+// CHECK-SUBTARGET:         unsigned Modes = 0;
+// CHECK-SUBTARGET:         if (checkFeatures("+feat")) Modes |= (1 << 0);
+// CHECK-SUBTARGET:         if (checkFeatures("+feat1")) Modes |= (1 << 1);
+// CHECK-SUBTARGET:         if (checkFeatures("+feat2")) Modes |= (1 << 2);
+// CHECK-SUBTARGET:         return Modes;
+// CHECK-SUBTARGET:       }
+// CHECK-SUBTARGET-LABEL: unsigned TestTargetGenSubtargetInfo::getHwMode(enum HwModeType type) const {
+// CHECK-SUBTARGET:         unsigned Modes = getHwModeSet();
+// CHECK-SUBTARGET:         if(!Modes)
+// CHECK-SUBTARGET:           return Modes;
+// CHECK-SUBTARGET:         switch (type) {
+// CHECK-SUBTARGET:         case HwMode_Default:
+// CHECK-SUBTARGET:           return llvm::countr_zero(Modes) + 1;
+// CHECK-SUBTARGET:           break;
+// CHECK-SUBTARGET:         case HwMode_ValueType: {
+// CHECK-SUBTARGET:           Modes &= 3;
+// CHECK-SUBTARGET:           if (!Modes)
+// CHECK-SUBTARGET:             return Modes;
+// CHECK-SUBTARGET:           if (!llvm::has_single_bit<unsigned>(Modes))
+// CHECK-SUBTARGET:             llvm_unreachable("Two or more HwModes for ValueType were found!");
+// CHECK-SUBTARGET:           return llvm::countr_zero(Modes) + 1;
+// CHECK-SUBTARGET:         }
+// CHECK-SUBTARGET:         case HwMode_RegInfo: {
+// CHECK-SUBTARGET:           Modes &= 1;
+// CHECK-SUBTARGET:           if (!Modes)
+// CHECK-SUBTARGET:             return Modes;
+// CHECK-SUBTARGET:           if (!llvm::has_single_bit<unsigned>(Modes))
+// CHECK-SUBTARGET:             llvm_unreachable("Two or more HwModes for RegInfo were found!");
+// CHECK-SUBTARGET:           return llvm::countr_zero(Modes) + 1;
+// CHECK-SUBTARGET:         }
+// CHECK-SUBTARGET:         case HwMode_EncodingInfo: {
+// CHECK-SUBTARGET:           Modes &= 4;
+// CHECK-SUBTARGET:           if (!Modes)
+// CHECK-SUBTARGET:             return Modes;
+// CHECK-SUBTARGET:           if (!llvm::has_single_bit<unsigned>(Modes))
+// CHECK-SUBTARGET:             llvm_unreachable("Two or more HwModes for Encoding were found!");
+// CHECK-SUBTARGET:           return llvm::countr_zero(Modes) + 1;
+// CHECK-SUBTARGET:         }
+// CHECK-SUBTARGET:         }
+// CHECK-SUBTARGET:         return 0; // should not get here
+// CHECK-SUBTARGET:       }
+
diff --git a/llvm/test/TableGen/HwModeEncodeDecode3.td b/llvm/test/TableGen/HwModeEncodeDecode3.td
index 8e0266b2c55af9..bc65d4a1d40d3b 100644
--- a/llvm/test/TableGen/HwModeEncodeDecode3.td
+++ b/llvm/test/TableGen/HwModeEncodeDecode3.td
@@ -160,15 +160,22 @@ def unrelated: Instruction {
 // DECODER-SUPPRESS-O2-DAG: Opcode: fooTypeEncA:baz
 // DECODER-SUPPRESS-O2-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
+// 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
+// ENCODER:         UINT64_C(12),       // baz
 // ENCODER:         UINT64_C(12),       // foo
 // ENCODER:         UINT64_C(2),        // unrelated
 
@@ -178,18 +185,53 @@ def unrelated: Instruction {
 // ENCODER:         UINT64_C(3),        // foo
 // ENCODER:         UINT64_C(2),        // unrelated
 
-// ENCODER:  unsigned HwMode = STI.getHwMode();
-// 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:  };
-
-// ENCODER:     case ::foo: {
-// ENCODER:      switch (HwMode) {
-// ENCODER:      default: llvm_unreachable("Unhandled HwMode");
-// ENCODER:      case 0: {
-// ENCODER:      case 1: {
-// ENCODER:      case 2: {
-
+// ENCODER-LABEL: case ::bar:
+// ENCODER-LABEL: case ::unrelated:
+// ENCODER-NOT: getHwMode
+// ENCODER-LABEL: case ::baz: {
+// ENCODER: unsigned HwMode = STI.getHwMode(MCSubtargetInfo::HwMode_EncodingInfo);
+// 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(MI, MI.getOperand(0), Fixups, STI);
+// ENCODER: op &= UINT64_C(240);
+// ENCODER: Value |= op;
+// ENCODER: break;
+// ENCODER: }
+// ENCODER-LABEL: case ::foo: {
+// ENCODER: unsigned HwMode = STI.getHwMode(MCSubtargetInfo::HwMode_EncodingInfo);
+// ENCODER: HwMode &= 3;
+// ENCODER: switch (HwMode) {
+// ENCODER: default: llvm_unreachable("Unknown hardware mode!"); 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(MI, MI.getOperand(0), Fixups, STI);
+// ENCODER: op &= UINT64_C(240);
+// ENCODER: Value |= op;
+// ENCODER: break;
+// ENCODER: }
+// ENCODER: case 1: {
+// ENCODER: op = getMachineOpValue(MI, MI.getOperand(0), Fixups, STI);
+// ENCODER: op &= UINT64_C(240);
+// ENCODER: Value |= op;
+// ENCODER: break;
+// ENCODER: }
+// ENCODER: case 2: {
+// ENCODER: op = getMachineOpValue(MI, MI.getOperand(0), Fixups, STI);
+// ENCODER: op &= UINT64_C(255);
+// ENCODER: op <<= 8;
+// ENCODER: Value |= op;
+// ENCODER: break;
+// ENCODER: }
diff --git a/llvm/utils/TableGen/CodeEmitterGen.cpp b/llvm/utils/TableGen/CodeEmitterGen.cpp
index a57885f22d7e3e..31a51c19e67c11 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,55 @@ 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(MCSubtargetInfo::HwMode_EncodingInfo);\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 +404,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 +427,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 +532,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/Common/CodeGenHwModes.cpp b/llvm/utils/TableGen/Common/CodeGenHwModes.cpp
index fec74d29c8bbbd..124cfbaf4fb747 100644
--- a/llvm/utils/TableGen/Common/CodeGenHwModes.cpp
+++ b/llvm/utils/TableGen/Common/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/SubtargetEmitter.cpp b/llvm/utils/TableGen/SubtargetEmitter.cpp
index 2e2c57b802ee54..e445c07270c258 100644
--- a/llvm/utils/TableGen/SubtargetEmitter.cpp
+++ b/llvm/utils/TableGen/SubtargetEmitter.cpp
@@ -1781,13 +1781,68 @@ void SubtargetEmitter::EmitHwModeCheck(const std::string &ClassName,
   if (CGH.getNumModeIds() == 1)
     return;
 
-  OS << "unsigned " << ClassName << "::getHwMode() const {\n";
+  // Collect all HwModes and related features defined in the TD files,
+  // and store them in bit format.
+  unsigned ValueTypeModes = 0;
+  unsigned RegInfoModes = 0;
+  unsigned EncodingInfoModes = 0;
+  for (const auto &MS : CGH.getHwModeSelects()) {
+    for (const HwModeSelect::PairType &P : MS.second.Items) {
+      if (P.first == DefaultMode)
+        continue;
+      if (P.second->isSubClassOf("ValueType")) {
+        ValueTypeModes |= (1 << (P.first - 1));
+      } else if (P.second->isSubClassOf("RegInfo") ||
+                 P.second->isSubClassOf("SubRegRange")) {
+        RegInfoModes |= (1 << (P.first - 1));
+      } else if (P.second->isSubClassOf("InstructionEncoding")) {
+        EncodingInfoModes |= (1 << (P.first - 1));
+      }
+    }
+  }
+
+  // Start emitting for getHwModeSet().
+  OS << "unsigned " << ClassName << "::getHwModeSet() const {\n";
+  OS << "  // Collect HwModes 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";
+    OS << "  if (checkFeatures(\"" << HM.Features << "\")) Modes |= (1 << "
+       << (M - 1) << ");\n";
   }
-  OS << "  return 0;\n}\n";
+  OS << "  return Modes;\n}\n";
+  // End emitting for getHwModeSet().
+
+  // Start emitting for getHwMode().
+  OS << "unsigned " << ClassName
+     << "::getHwMode(enum HwModeType type) const {\n";
+  OS << "  unsigned Modes = getHwModeSet();\n";
+  OS << "\n  if(!Modes)\n    return Modes;\n\n";
+  OS << "  switch (type) {\n";
+  OS << "  case HwMode_Default:\n    return llvm::countr_zero(Modes) + 1;\n"
+     << "    break;\n";
+  OS << "  case HwMode_ValueType: {\n    Modes &= " << ValueTypeModes << ";\n"
+     << "    if (!Modes)\n      return Modes;\n"
+     << "    if (!llvm::has_single_bit<unsigned>(Modes))\n"
+     << "      llvm_unreachable(\"Two or more HwModes for ValueType were "
+        "found!\");\n"
+     << "    return llvm::countr_zero(Modes) + 1;\n  }\n";
+  OS << "  case HwMode_RegInfo: {\n    Modes &= " << RegInfoModes << ";\n"
+     << "    if (!Modes)\n      return Modes;\n"
+     << "    if (!llvm::has_single_bit<unsigned>(Modes))\n"
+     << "      llvm_unreachable(\"Two or more HwModes for RegInfo were "
+        "found!\");\n"
+     << "    return llvm::countr_zero(Modes) + 1;\n  }\n";
+  OS << "  case HwMode_EncodingInfo: {\n    Modes &= " << EncodingInfoModes
+     << ";\n"
+     << "    if (!Modes)\n      return Modes;\n"
+     << "    if (!llvm::has_single_bit<unsigned>(Modes))\n"
+     << "      llvm_unreachable(\"Two or more HwModes for Encoding were "
+        "found!\");\n"
+     << "    return llvm::countr_zero(Modes) + 1;\n  }\n";
+  OS << "  }\n";
+  OS << "  return 0; // should not get here\n}\n";
+  // End emitting for getHwMode().
 }
 
 void SubtargetEmitter::emitGetMacroFusions(const std::string &ClassName,
@@ -1876,8 +1931,11 @@ void SubtargetEmitter::emitGenMCSubtargetInfo(raw_ostream &OS) {
      << "    return " << Target << "_MC"
      << "::resolveVariantSchedClassImpl(SchedClass, MI, MCII, CPUID);\n";
   OS << "  }\n";
-  if (TGT.getHwModes().getNumModeIds() > 1)
-    OS << "  unsigned getHwMode() const override;\n";
+  if (TGT.getHwModes().getNumModeIds() > 1) {
+    OS << "  unsigned getHwModeSet() const override;\n";
+    OS << "  unsigned getHwMode(enum HwModeType type = HwMode_Default) const "
+          "override;\n";
+  }
   OS << "};\n";
   EmitHwModeCheck(Target + "GenMCSubtargetInfo", OS);
 }
@@ -2004,8 +2062,11 @@ void SubtargetEmitter::run(raw_ostream &OS) {
      << " unsigned CPUID) const override;\n"
      << "  DFAPacketizer *createDFAPacketizer(const InstrItineraryData *IID)"
      << " const;\n";
-  if (TGT.getHwModes().getNumModeIds() > 1)
-    OS << "  unsigned getHwMode() const override;\n";
+  if (TGT.getHwModes().getNumModeIds() > 1) {
+    OS << "  unsigned getHwModeSet() const override;\n";
+    OS << "  unsigned getHwMode(enum HwModeType type = HwMode_Default) const "
+          "override;\n";
+  }
   if (TGT.hasMacroFusion())
     OS << "  std::vector<MacroFusionPredTy> getMacroFusions() const "
           "override;\n";

>From f544d3ed349a08b0276e7dbef16dcd405135980d Mon Sep 17 00:00:00 2001
From: z30050559 <zhengwentao3 at huawei.com>
Date: Mon, 29 Apr 2024 10:33:36 +0800
Subject: [PATCH 2/2] [TableGen] fix HwMode logic in CodeEmitterGen

1. Since we have obtained HwMode Id from getHwMode() interface,
   we no longer need to perform bit related operations when checking
   the case.
2. When generating the InstBits table, if the instruction does not have the
   HwMode ID corresponding to the current hwmode table, the base encoding
   will be generated based on the encoding defined in the top-level
   Instruction class.
3. Add testcase to test the correctness of hwmode encoding in APInt mode
4. Fixed a bug in the 'getOperandBitOffset' interface: it would check the
   HwMode ID for a instruction but did not preemptively fetch the HwMode ID.
5. Fix up some comments and clean up some codes.
---
 llvm/include/llvm/MC/MCSubtargetInfo.h    |  17 +-
 llvm/test/TableGen/HwModeBitSet.td        |  18 +-
 llvm/test/TableGen/HwModeEncodeAPInt.td   | 241 ++++++++++++++++++++++
 llvm/test/TableGen/HwModeEncodeDecode.td  |   1 +
 llvm/test/TableGen/HwModeEncodeDecode2.td |   1 +
 llvm/test/TableGen/HwModeEncodeDecode3.td | 103 +++++----
 llvm/utils/TableGen/CodeEmitterGen.cpp    |  61 ++----
 llvm/utils/TableGen/SubtargetEmitter.cpp  |  46 ++---
 8 files changed, 373 insertions(+), 115 deletions(-)
 create mode 100644 llvm/test/TableGen/HwModeEncodeAPInt.td

diff --git a/llvm/include/llvm/MC/MCSubtargetInfo.h b/llvm/include/llvm/MC/MCSubtargetInfo.h
index 70b14665ea9ebd..ff76435d60843b 100644
--- a/llvm/include/llvm/MC/MCSubtargetInfo.h
+++ b/llvm/include/llvm/MC/MCSubtargetInfo.h
@@ -240,10 +240,14 @@ class MCSubtargetInfo {
     return ProcFeatures;
   }
 
-  /// HwMode ID will be stored as bits, allowing users to pull the specific
-  /// HwMode ID (like RegInfo HwMode ID) from the bits as needed. This enables
-  /// users to control multiple features with one hwmode (as previously) or use
-  /// different hwmodes to control different features.
+  /// HwMode IDs are stored and accessed in a bit set format, enabling
+  /// users to efficiently retrieve specific IDs, such as the RegInfo
+  /// HwMode ID, from the set as required. Using this approach, various
+  /// types of HwMode IDs can be added to a subtarget to manage different
+  /// attributes within that subtarget, significantly enhancing the
+  /// scalability and usability of HwMode. Moreover, to ensure compatibility,
+  /// this method also supports controlling multiple attributes with a single
+  /// HwMode ID, just as was done previously.
   enum HwModeType {
     HwMode_Default,   // Return the smallest HwMode ID of current subtarget.
     HwMode_ValueType, // Return the HwMode ID that controls the ValueType.
@@ -252,8 +256,13 @@ class MCSubtargetInfo {
     HwMode_EncodingInfo // Return the HwMode ID that controls the EncodingInfo.
   };
 
+  /// Return a bit set containing all HwMode IDs of the current subtarget.
   virtual unsigned getHwModeSet() const { return 0; }
 
+  /// HwMode ID corresponding to the 'type' parameter is retrieved from the
+  /// HwMode bit set of the current subtarget. It’s important to note that if
+  /// the current subtarget possesses two HwMode IDs and both control a single
+  /// attribute (such as RegInfo), this interface will result in an error.
   virtual unsigned getHwMode(enum HwModeType type = HwMode_Default) const {
     return 0;
   }
diff --git a/llvm/test/TableGen/HwModeBitSet.td b/llvm/test/TableGen/HwModeBitSet.td
index c642906f6f4a88..b2de6e8e012c50 100644
--- a/llvm/test/TableGen/HwModeBitSet.td
+++ b/llvm/test/TableGen/HwModeBitSet.td
@@ -1,6 +1,8 @@
+// This is to test the scenario where different HwMode attributes coexist.
 // RUN: llvm-tblgen -gen-register-info -register-info-debug -I %p/../../include %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=CHECK-REG
 // RUN: llvm-tblgen -gen-subtarget -I %p/../../include %s -o - 2>&1 | FileCheck %s --check-prefix=CHECK-SUBTARGET
 
+
 include "llvm/Target/Target.td"
 
 def TestTargetInstrInfo : InstrInfo;
@@ -109,6 +111,7 @@ def fooTypeEncA : InstructionEncoding {
 
 
 def foo : Instruction {
+  bits<32> Inst;
   let OutOperandList = (outs);
   let InOperandList = (ins i32imm:$factor);
   let EncodingInfos = EncodingByHwMode<
@@ -126,37 +129,34 @@ def foo : Instruction {
 // CHECK-SUBTARGET:       }
 // CHECK-SUBTARGET-LABEL: unsigned TestTargetGenSubtargetInfo::getHwMode(enum HwModeType type) const {
 // CHECK-SUBTARGET:         unsigned Modes = getHwModeSet();
-// CHECK-SUBTARGET:         if(!Modes)
+// CHECK-SUBTARGET:         if (!Modes)
 // CHECK-SUBTARGET:           return Modes;
 // CHECK-SUBTARGET:         switch (type) {
 // CHECK-SUBTARGET:         case HwMode_Default:
 // CHECK-SUBTARGET:           return llvm::countr_zero(Modes) + 1;
-// CHECK-SUBTARGET:           break;
-// CHECK-SUBTARGET:         case HwMode_ValueType: {
+// CHECK-SUBTARGET:         case HwMode_ValueType:
 // CHECK-SUBTARGET:           Modes &= 3;
 // CHECK-SUBTARGET:           if (!Modes)
 // CHECK-SUBTARGET:             return Modes;
 // CHECK-SUBTARGET:           if (!llvm::has_single_bit<unsigned>(Modes))
 // CHECK-SUBTARGET:             llvm_unreachable("Two or more HwModes for ValueType were found!");
 // CHECK-SUBTARGET:           return llvm::countr_zero(Modes) + 1;
-// CHECK-SUBTARGET:         }
-// CHECK-SUBTARGET:         case HwMode_RegInfo: {
+// CHECK-SUBTARGET:         case HwMode_RegInfo:
 // CHECK-SUBTARGET:           Modes &= 1;
 // CHECK-SUBTARGET:           if (!Modes)
 // CHECK-SUBTARGET:             return Modes;
 // CHECK-SUBTARGET:           if (!llvm::has_single_bit<unsigned>(Modes))
 // CHECK-SUBTARGET:             llvm_unreachable("Two or more HwModes for RegInfo were found!");
 // CHECK-SUBTARGET:           return llvm::countr_zero(Modes) + 1;
-// CHECK-SUBTARGET:         }
-// CHECK-SUBTARGET:         case HwMode_EncodingInfo: {
+// CHECK-SUBTARGET:         case HwMode_EncodingInfo:
 // CHECK-SUBTARGET:           Modes &= 4;
 // CHECK-SUBTARGET:           if (!Modes)
 // CHECK-SUBTARGET:             return Modes;
 // CHECK-SUBTARGET:           if (!llvm::has_single_bit<unsigned>(Modes))
-// CHECK-SUBTARGET:             llvm_unreachable("Two or more HwModes for Encoding were found!");
+// CHECK-SUBTARGET:             llvm_unreachable("Two or more HwModes for EncodingInfo were found!");
 // CHECK-SUBTARGET:           return llvm::countr_zero(Modes) + 1;
 // CHECK-SUBTARGET:         }
-// CHECK-SUBTARGET:         }
+// CHECK-SUBTARGET:         llvm_unreachable("unexpected HwModeType");
 // CHECK-SUBTARGET:         return 0; // should not get here
 // CHECK-SUBTARGET:       }
 
diff --git a/llvm/test/TableGen/HwModeEncodeAPInt.td b/llvm/test/TableGen/HwModeEncodeAPInt.td
new file mode 100644
index 00000000000000..43ca5edd952a64
--- /dev/null
+++ b/llvm/test/TableGen/HwModeEncodeAPInt.td
@@ -0,0 +1,241 @@
+// This testcase is to test the correctness of HwMode encoding under the 'APInt' Mode.
+// RUN: llvm-tblgen -gen-emitter -I %p/../../include %s | \
+// RUN:     FileCheck %s --check-prefix=ENCODER
+
+include "llvm/Target/Target.td"
+
+def archInstrInfo : InstrInfo { }
+
+def arch : Target {
+  let InstructionSet = archInstrInfo;
+}
+
+def Myi32 : Operand<i32> {
+  let DecoderMethod = "DecodeMyi32";
+}
+
+def HasA : Predicate<"Subtarget->hasA()">;
+def HasB : Predicate<"Subtarget->hasB()">;
+
+def ModeA : HwMode<"+a", [HasA]>; // Mode 1
+def ModeB : HwMode<"+b", [HasB]>; // Mode 2
+def ModeC : HwMode<"+c", []>;     // Mode 3
+
+
+def fooTypeEncDefault : InstructionEncoding {
+  let Size = 16;
+  field bits<128> SoftFail = 0;
+  bits<128> Inst;
+  bits<8> factor;
+  let Inst{127...120} = factor;
+  let Inst{3...2} = 0b10;
+  let Inst{1...0} = 0b00;
+}
+
+def fooTypeEncA : InstructionEncoding {
+  let Size = 16;
+  field bits<128> SoftFail = 0;
+  bits<128> Inst;
+  bits<8> factor;
+  let Inst{119...112} = factor;
+  let Inst{3...2} = 0b11;
+  let Inst{1...0} = 0b00;
+}
+
+def fooTypeEncB : InstructionEncoding {
+  let Size = 16;
+  field bits<128> SoftFail = 0;
+  bits<128> Inst;
+  bits<8> factor;
+  let Inst{119...112} = factor;
+  let Inst{111...110} = 0b11;
+}
+
+def fooTypeEncC : InstructionEncoding {
+  let Size = 16;
+  field bits<128> SoftFail = 0;
+  bits<128> Inst;
+  bits<8> factor;
+  let Inst{31...24} = factor;
+  let Inst{23...21} = 0b110;
+  let Inst{1...0} = 0b11;
+}
+
+// Test for DefaultMode as a selector.
+def foo : Instruction {
+  bits<128> Inst;
+  let OutOperandList = (outs);
+  let InOperandList = (ins i32imm:$factor);
+  let EncodingInfos = EncodingByHwMode<
+  [ModeC, ModeA, ModeB, DefaultMode],
+  [fooTypeEncC, fooTypeEncA, fooTypeEncB, fooTypeEncDefault]>;
+  let AsmString = "foo  $factor";
+}
+
+def bar: Instruction {
+  let OutOperandList = (outs);
+  let InOperandList = (ins i32imm:$factor);
+  let Size = 4;
+  bits<32> Inst;
+  bits<32> SoftFail;
+  bits<8> factor;
+  let Inst{31...24} = factor;
+  let Inst{1...0} = 0b10;
+  let AsmString = "bar  $factor";
+}
+
+def baz : Instruction {
+  let OutOperandList = (outs);
+  let InOperandList = (ins i32imm:$factor);
+  bits<32> Inst;
+  let EncodingInfos = EncodingByHwMode<
+    [ModeB], [fooTypeEncA]
+  >;
+  let AsmString = "foo  $factor";
+}
+
+def unrelated: Instruction {
+  let OutOperandList = (outs);
+  let DecoderNamespace = "Alt";
+  let InOperandList = (ins i32imm:$factor);
+  let Size = 4;
+  bits<32> Inst;
+  bits<32> SoftFail;
+  bits<8> factor;
+  let Inst{31...24} = factor;
+  let Inst{1...0} = 0b10;
+  let AsmString = "unrelated  $factor";
+}
+
+// For 'bar' and 'unrelated', we didn't assign any HwModes for them,
+// they should keep the same in the following four tables.
+// For 'foo' we assigned four HwModes( includes 'DefaultMode' ),
+// it's encodings should be different in the following four tables.
+// For 'baz' we only assigned ModeB for it, so it will be presented
+// as '0' in the tables of ModeA, ModeC and Default Mode.
+// ENCODER-LABEL:   static const uint64_t InstBits[] = {
+// ENCODER:         UINT64_C(2), UINT64_C(0),       // bar
+// ENCODER:         UINT64_C(0), UINT64_C(0),       // baz
+// ENCODER:         UINT64_C(8), UINT64_C(0),       // foo
+// ENCODER:         UINT64_C(2), UINT64_C(0),       // unrelated
+// ENCODER-LABEL:   static const uint64_t InstBits_ModeA[] = {
+// ENCODER:         UINT64_C(2), UINT64_C(0),       // bar
+// ENCODER:         UINT64_C(0), UINT64_C(0),       // baz
+// ENCODER:         UINT64_C(12), UINT64_C(0),      // foo
+// ENCODER:         UINT64_C(2), UINT64_C(0),       // unrelated
+// ENCODER-LABEL:   static const uint64_t InstBits_ModeB[] = {
+// ENCODER:         UINT64_C(2), UINT64_C(0),       // bar
+// ENCODER:         UINT64_C(12), UINT64_C(0),      // baz
+// ENCODER:         UINT64_C(0), UINT64_C(211106232532992),  // foo
+// ENCODER:         UINT64_C(2), UINT64_C(0),       // unrelated
+// ENCODER-LABEL:   static const uint64_t InstBits_ModeC[] = {
+// ENCODER:         UINT64_C(2), UINT64_C(0),      // bar
+// ENCODER:         UINT64_C(0), UINT64_C(0),      // baz
+// ENCODER:         UINT64_C(12582915),  UINT64_C(0),  // foo
+// ENCODER:         UINT64_C(2),  UINT64_C(0),     // unrelated
+
+
+// ENCODER: const uint64_t *InstBitsByHw;
+// ENCODER: const unsigned opcode = MI.getOpcode();
+// ENCODER: if (Scratch.getBitWidth() != 128)
+// ENCODER:   Scratch = Scratch.zext(128);
+// ENCODER: Inst = APInt(128, ArrayRef(InstBits + opcode * 2, 2));
+// ENCODER: APInt &Value = Inst;
+// ENCODER: APInt &op = Scratch;
+// ENCODER: switch (opcode) {
+// ENCODER-LABEL: case ::bar:
+// ENCODER-LABEL: case ::unrelated:
+// ENCODER-NOT: getHwMode
+// ENCODER-LABEL: case ::foo: {
+// ENCODER: unsigned HwMode = STI.getHwMode(MCSubtargetInfo::HwMode_EncodingInfo);
+// ENCODER: switch (HwMode) {
+// ENCODER: default: llvm_unreachable("Unknown hardware mode!"); break;
+// ENCODER: case 0: InstBitsByHw = InstBits; break;
+// ENCODER: case 1: InstBitsByHw = InstBits_ModeA; break;
+// ENCODER: case 2: InstBitsByHw = InstBits_ModeB; break;
+// ENCODER: case 3: InstBitsByHw = InstBits_ModeC; break;
+// ENCODER: };
+// ENCODER: Inst = APInt(128, ArrayRef(InstBitsByHw + opcode * 2, 2));
+// ENCODER: Value = Inst;
+// ENCODER: switch (HwMode) {
+// ENCODER: default: llvm_unreachable("Unhandled HwMode");
+// ENCODER: case 0: {
+// ENCODER: op.clearAllBits();
+// ENCODER: getMachineOpValue(MI, MI.getOperand(0), op, Fixups, STI);
+// ENCODER: Value.insertBits(op.extractBitsAsZExtValue(8, 0), 120, 8);
+// ENCODER: break;
+// ENCODER: }
+// ENCODER: case 1: {
+// ENCODER: op.clearAllBits();
+// ENCODER: getMachineOpValue(MI, MI.getOperand(0), op, Fixups, STI);
+// ENCODER: Value.insertBits(op.extractBitsAsZExtValue(8, 0), 112, 8);
+// ENCODER: break;
+// ENCODER: }
+// ENCODER: case 2: {
+// ENCODER: op.clearAllBits();
+// ENCODER: getMachineOpValue(MI, MI.getOperand(0), op, Fixups, STI);
+// ENCODER: Value.insertBits(op.extractBitsAsZExtValue(8, 0), 112, 8);
+// ENCODER: break;
+// ENCODER: }
+// ENCODER: case 3: {
+// ENCODER: op.clearAllBits();
+// ENCODER: getMachineOpValue(MI, MI.getOperand(0), op, Fixups, STI);
+// ENCODER: Value.insertBits(op.extractBitsAsZExtValue(8, 0), 24, 8);
+// ENCODER: break;
+// ENCODER: }
+// ENCODER-LABEL: case ::baz: {
+// ENCODER: unsigned HwMode = STI.getHwMode(MCSubtargetInfo::HwMode_EncodingInfo);
+// ENCODER: switch (HwMode) {
+// ENCODER: default: llvm_unreachable("Unknown hardware mode!"); break;
+// ENCODER: case 2: InstBitsByHw = InstBits_ModeB; break;
+// ENCODER: };
+// ENCODER: Inst = APInt(128, ArrayRef(InstBitsByHw + opcode * 2, 2));
+// ENCODER: Value = Inst;
+// ENCODER: switch (HwMode) {
+// ENCODER: default: llvm_unreachable("Unhandled HwMode");
+// ENCODER: case 2: {
+// ENCODER: getMachineOpValue(MI, MI.getOperand(0), op, Fixups, STI);
+// ENCODER: Value.insertBits(op.extractBitsAsZExtValue(8, 0), 112, 8);
+// ENCODER: break;
+// ENCODER: }
+
+// ENCODER-LABEL: uint32_t archMCCodeEmitter::getOperandBitOffset
+// ENCODER: switch (MI.getOpcode()) {
+// ENCODER-LABEL: case ::bar:
+// ENCODER-LABEL: case ::unrelated: {
+// ENCODER-NOT: getHwMode
+// ENCODER-LABEL: case ::foo: {
+// ENCODER:   unsigned HwMode = STI.getHwMode(MCSubtargetInfo::HwMode_EncodingInfo);
+// ENCODER:   switch (HwMode) {
+// ENCODER:   default: llvm_unreachable("Unhandled HwMode");
+// ENCODER:   case 0: {
+// ENCODER:   switch (OpNum) {
+// ENCODER:   case 0:
+// ENCODER:     return 120;
+// ENCODER:   }
+// ENCODER:   break;
+// ENCODER:   }
+// ENCODER:   case 1: {
+// ENCODER:   switch (OpNum) {
+// ENCODER:   case 0:
+// ENCODER:     return 112;
+// ENCODER:   }
+// ENCODER:   break;
+// ENCODER:   }
+// ENCODER:   case 2: {
+// ENCODER:   switch (OpNum) {
+// ENCODER:   case 0:
+// ENCODER:     return 112;
+// ENCODER:   }
+// ENCODER:   break;
+// ENCODER:   }
+// ENCODER:   case 3: {
+// ENCODER:   switch (OpNum) {
+// ENCODER:   case 0:
+// ENCODER:     return 24;
+// ENCODER:   }
+// ENCODER:   break;
+// ENCODER:   }
+// ENCODER:   }
+// ENCODER:   break;
+// ENCODER: }
diff --git a/llvm/test/TableGen/HwModeEncodeDecode.td b/llvm/test/TableGen/HwModeEncodeDecode.td
index aaacd7d3f6590a..6e68e9d7024612 100644
--- a/llvm/test/TableGen/HwModeEncodeDecode.td
+++ b/llvm/test/TableGen/HwModeEncodeDecode.td
@@ -41,6 +41,7 @@ def fooTypeEncB : InstructionEncoding {
 
 let OutOperandList = (outs) in {
 def foo : Instruction {
+  bits<32> Inst;
   let InOperandList = (ins i32imm:$factor);
   let EncodingInfos = EncodingByHwMode<
     [ModeA, ModeB], [fooTypeEncA,
diff --git a/llvm/test/TableGen/HwModeEncodeDecode2.td b/llvm/test/TableGen/HwModeEncodeDecode2.td
index cf96dda6c8bf3b..ecf782c0b46ecb 100644
--- a/llvm/test/TableGen/HwModeEncodeDecode2.td
+++ b/llvm/test/TableGen/HwModeEncodeDecode2.td
@@ -45,6 +45,7 @@ def fooTypeEncB : InstructionEncoding {
 
 let OutOperandList = (outs) in {
   def foo : Instruction {
+    bits<32> Inst;
     let InOperandList = (ins i32imm:$factor);
     let EncodingInfos = EncodingByHwMode<
       [ModeA, ModeB], [fooTypeEncA, fooTypeEncB]
diff --git a/llvm/test/TableGen/HwModeEncodeDecode3.td b/llvm/test/TableGen/HwModeEncodeDecode3.td
index bc65d4a1d40d3b..dbeb26013f7abd 100644
--- a/llvm/test/TableGen/HwModeEncodeDecode3.td
+++ b/llvm/test/TableGen/HwModeEncodeDecode3.td
@@ -22,8 +22,9 @@ 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]>; // Mode 1
+def ModeB : HwMode<"+b", [HasB]>; // Mode 2
+def ModeC : HwMode<"+c", []>;     // Mode 3
 
 
 def fooTypeEncDefault : InstructionEncoding {
@@ -55,13 +56,24 @@ def fooTypeEncB : InstructionEncoding {
   let Inst{1...0} = 0b11;
 }
 
+def fooTypeEncC : InstructionEncoding {
+  let Size = 4;
+  field bits<32> SoftFail = 0;
+  bits<32> Inst;
+  bits<8> factor;
+  let Inst{31...24} = factor;
+  let Inst{23...21} = 0b110;
+  let Inst{1...0} = 0b11;
+}
+
 // Test for DefaultMode as a selector.
 def foo : Instruction {
+  bits<32> Inst;
   let OutOperandList = (outs);
   let InOperandList = (ins i32imm:$factor);
   let EncodingInfos = EncodingByHwMode<
-    [ModeA, ModeB, DefaultMode], [fooTypeEncA, fooTypeEncB, fooTypeEncDefault]
-  >;
+  [ModeC, ModeA, ModeB, DefaultMode],
+  [fooTypeEncC, fooTypeEncA, fooTypeEncB, fooTypeEncDefault]>;
   let AsmString = "foo  $factor";
 }
 
@@ -102,9 +114,9 @@ def unrelated: Instruction {
 
 
 // Under default settings, using 'HwMode' to dictate instruction encodings results in
-// significant duplication of DecoderTables. The three tables ‘DecoderTableAlt32’,
-// ‘DecoderTableAlt_ModeA32’, and ‘DecoderTableAlt_ModeB32’ are exact duplicates and
-// could effectively be merged into one.
+// significant duplication of DecoderTables. The four tables ‘DecoderTableAlt32’,
+// ‘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-LABEL: DecoderTable64[] =
@@ -115,6 +127,8 @@ def unrelated: Instruction {
 // DECODER-DAG: Opcode: unrelated
 // DECODER-LABEL: DecoderTableAlt_ModeB32[] =
 // DECODER-DAG: Opcode: unrelated
+// DECODER-LABEL: DecoderTableAlt_ModeC32[] =
+// DECODER-DAG: Opcode: unrelated
 // DECODER-LABEL: DecoderTable_ModeA32[] =
 // DECODER-DAG: Opcode: fooTypeEncA:foo
 // DECODER-DAG: Opcode: bar
@@ -122,9 +136,12 @@ def unrelated: Instruction {
 // DECODER-DAG: Opcode: fooTypeEncB:foo
 // DECODER-DAG: Opcode: fooTypeEncA:baz
 // DECODER-DAG: Opcode: bar
+// DECODER-LABEL: DecoderTable_ModeC32[] =
+// DECODER-DAG: Opcode: fooTypeEncC:foo
+// DECODER-DAG: Opcode: bar
 
 // Under the 'O1' optimization level, unnecessary duplicate tables will be eliminated,
-// reducing the three ‘Alt’ tables down to just one.
+// reducing the four ‘Alt’ tables down to just one.
 // DECODER-SUPPRESS-O1-LABEL: DecoderTable32[] =
 // DECODER-SUPPRESS-O1-DAG: Opcode: bar
 // DECODER-SUPPRESS-O1-LABEL: DecoderTable64[] =
@@ -138,6 +155,9 @@ def unrelated: Instruction {
 // DECODER-SUPPRESS-O1-DAG: Opcode: fooTypeEncB:foo
 // DECODER-SUPPRESS-O1-DAG: Opcode: fooTypeEncA:baz
 // DECODER-SUPPRESS-O1-DAG: Opcode: bar
+// DECODER-SUPPRESS-O1-LABEL: DecoderTable_ModeC32[] =
+// DECODER-SUPPRESS-O1-DAG: Opcode: fooTypeEncC:foo
+// DECODER-SUPPRESS-O1-DAG: Opcode: bar
 
 // Under the 'O2' optimization condition, instructions possessing the 'EncodingByHwMode'
 // attribute will be extracted from their original DecoderNamespace and placed into their
@@ -159,59 +179,48 @@ def unrelated: Instruction {
 // DECODER-SUPPRESS-O2-DAG: Opcode: fooTypeEncB:foo
 // DECODER-SUPPRESS-O2-DAG: Opcode: fooTypeEncA:baz
 // DECODER-SUPPRESS-O2-NOT: Opcode: bar
+// DECODER-SUPPRESS-O2-LABEL: DecoderTable_ModeC32[] =
+// DECODER-SUPPRESS-O2-DAG: Opcode: fooTypeEncC:foo
+// DECODER-SUPPRESS-O2-NOT: Opcode: bar
 
-// 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).
+// For 'bar' and 'unrelated', we didn't assign any HwModes for them,
+// they should keep the same in the following four tables.
+// For 'foo' we assigned four HwModes( includes 'DefaultMode' ),
+// it's encodings should be different in the following four tables.
+// For 'baz' we only assigned ModeB for it, so it will be presented
+// as '0' in the tables of ModeA, ModeC and Default Mode.
 // ENCODER-LABEL:   static const uint64_t InstBits[] = {
 // ENCODER:         UINT64_C(2),        // bar
-// ENCODER:         UINT64_C(12),       // baz
+// ENCODER:         UINT64_C(0),        // 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(12),       // baz
+// ENCODER:         UINT64_C(0),        // baz
 // ENCODER:         UINT64_C(12),       // foo
 // ENCODER:         UINT64_C(2),        // unrelated
-
 // ENCODER-LABEL:   static const uint64_t InstBits_ModeB[] = {
 // ENCODER:         UINT64_C(2),        // bar
 // ENCODER:         UINT64_C(12),       // baz
 // ENCODER:         UINT64_C(3),        // foo
 // ENCODER:         UINT64_C(2),        // unrelated
+// ENCODER-LABEL:   static const uint64_t InstBits_ModeC[] = {
+// ENCODER:         UINT64_C(2),        // bar
+// ENCODER:         UINT64_C(0),        // baz
+// ENCODER:         UINT64_C(12582915), // foo
+// ENCODER:         UINT64_C(2),        // unrelated
 
 // ENCODER-LABEL: case ::bar:
 // ENCODER-LABEL: case ::unrelated:
 // ENCODER-NOT: getHwMode
-// ENCODER-LABEL: case ::baz: {
-// ENCODER: unsigned HwMode = STI.getHwMode(MCSubtargetInfo::HwMode_EncodingInfo);
-// 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(MI, MI.getOperand(0), Fixups, STI);
-// ENCODER: op &= UINT64_C(240);
-// ENCODER: Value |= op;
-// ENCODER: break;
-// ENCODER: }
 // ENCODER-LABEL: case ::foo: {
 // ENCODER: unsigned HwMode = STI.getHwMode(MCSubtargetInfo::HwMode_EncodingInfo);
-// ENCODER: HwMode &= 3;
 // ENCODER: switch (HwMode) {
 // ENCODER: default: llvm_unreachable("Unknown hardware mode!"); break;
 // ENCODER: case 0: InstBitsByHw = InstBits; break;
 // ENCODER: case 1: InstBitsByHw = InstBits_ModeA; break;
 // ENCODER: case 2: InstBitsByHw = InstBits_ModeB; break;
+// ENCODER: case 3: InstBitsByHw = InstBits_ModeC; break;
 // ENCODER: };
 // ENCODER: Value = InstBitsByHw[opcode];
 // ENCODER: switch (HwMode) {
@@ -235,3 +244,25 @@ def unrelated: Instruction {
 // ENCODER: Value |= op;
 // ENCODER: break;
 // ENCODER: }
+// ENCODER: case 3: {
+// ENCODER: op = getMachineOpValue(MI, MI.getOperand(0), Fixups, STI);
+// ENCODER: op &= UINT64_C(255);
+// ENCODER: op <<= 24;
+// ENCODER: Value |= op;
+// ENCODER: break;
+// ENCODER: }
+// ENCODER-LABEL: case ::baz: {
+// ENCODER: unsigned HwMode = STI.getHwMode(MCSubtargetInfo::HwMode_EncodingInfo);
+// 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(MI, MI.getOperand(0), Fixups, STI);
+// ENCODER: op &= UINT64_C(240);
+// ENCODER: Value |= op;
+// ENCODER: break;
+// ENCODER: }
diff --git a/llvm/utils/TableGen/CodeEmitterGen.cpp b/llvm/utils/TableGen/CodeEmitterGen.cpp
index 31a51c19e67c11..3171084131a4d5 100644
--- a/llvm/utils/TableGen/CodeEmitterGen.cpp
+++ b/llvm/utils/TableGen/CodeEmitterGen.cpp
@@ -290,54 +290,44 @@ 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.
+      // Invoke the interface to obtain the HwMode ID controlling the
+      // EncodingInfo for the current subtarget. This interface will
+      // mask off irrelevant HwMode IDs.
       append("      unsigned HwMode = "
              "STI.getHwMode(MCSubtargetInfo::HwMode_EncodingInfo);\n");
-      append("      HwMode &= " + itostr(EncodingHwModesInBits) + ";\n");
-      append("      switch (HwMode) {\n");
-      append("      default: llvm_unreachable(\"Unknown hardware mode!\"); "
-             "break;\n");
+      Case += "      switch (HwMode) {\n";
+      Case += "      default: llvm_unreachable(\"Unknown hardware mode!\"); "
+              "break;\n";
       for (auto &[ModeId, Encoding] : EBM) {
         if (ModeId == DefaultMode) {
-          append("      case " + itostr(DefaultMode) +
-                 ": InstBitsByHw = InstBits");
+          Case +=
+              "      case " + itostr(DefaultMode) + ": InstBitsByHw = InstBits";
         } else {
-          append("      case " + itostr(1 << (ModeId - 1)) +
-                 ": InstBitsByHw = InstBits_" +
-                 std::string(HWM.getMode(ModeId).Name));
+          Case += "      case " + itostr(ModeId) +
+                  ": InstBitsByHw = InstBits_" +
+                  std::string(HWM.getMode(ModeId).Name);
         }
-        append("; break;\n");
+        Case += "; break;\n";
       }
-      append("      };\n");
+      Case += "      };\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");
+        Case += "      Inst = APInt(" + itostr(BitWidth);
+        Case += ", ArrayRef(InstBitsByHw + opcode * " + itostr(NumWords) +
+                ", " + itostr(NumWords);
+        Case += "));\n";
+        Case += "      Value = Inst;\n";
       } else {
-        append("      Value = InstBitsByHw[opcode];\n");
+        Case += "      Value = InstBitsByHw[opcode];\n";
       }
 
       append("      switch (HwMode) {\n");
       append("      default: llvm_unreachable(\"Unhandled HwMode\");\n");
       for (auto &[ModeId, Encoding] : EBM) {
-        if (ModeId == DefaultMode) {
-          append("      case " + itostr(DefaultMode) + ": {\n");
-        } else {
-          append("      case " + itostr(1 << (ModeId - 1)) + ": {\n");
-        }
+        append("      case " + itostr(ModeId) + ": {\n");
         addInstructionCasesForEncoding(R, Encoding, Target, Case,
                                        BitOffsetCase);
         append("      break;\n");
@@ -427,17 +417,8 @@ 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");
diff --git a/llvm/utils/TableGen/SubtargetEmitter.cpp b/llvm/utils/TableGen/SubtargetEmitter.cpp
index e445c07270c258..b6b7641cfb929d 100644
--- a/llvm/utils/TableGen/SubtargetEmitter.cpp
+++ b/llvm/utils/TableGen/SubtargetEmitter.cpp
@@ -1782,7 +1782,7 @@ void SubtargetEmitter::EmitHwModeCheck(const std::string &ClassName,
     return;
 
   // Collect all HwModes and related features defined in the TD files,
-  // and store them in bit format.
+  // and store them as a bit set.
   unsigned ValueTypeModes = 0;
   unsigned RegInfoModes = 0;
   unsigned EncodingInfoModes = 0;
@@ -1803,7 +1803,7 @@ void SubtargetEmitter::EmitHwModeCheck(const std::string &ClassName,
 
   // Start emitting for getHwModeSet().
   OS << "unsigned " << ClassName << "::getHwModeSet() const {\n";
-  OS << "  // Collect HwModes and store them in bits\n";
+  OS << "  // Collect HwModes and store them as a bit set.\n";
   OS << "  unsigned Modes = 0;\n";
   for (unsigned M = 1, NumModes = CGH.getNumModeIds(); M != NumModes; ++M) {
     const HwMode &HM = CGH.getMode(M);
@@ -1813,35 +1813,29 @@ void SubtargetEmitter::EmitHwModeCheck(const std::string &ClassName,
   OS << "  return Modes;\n}\n";
   // End emitting for getHwModeSet().
 
+  auto handlePerMode = [&](std::string ModeType, unsigned ModeInBitSet) {
+    OS << "  case HwMode_" << ModeType << ":\n"
+       << "    Modes &= " << ModeInBitSet << ";\n"
+       << "    if (!Modes)\n      return Modes;\n"
+       << "    if (!llvm::has_single_bit<unsigned>(Modes))\n"
+       << "      llvm_unreachable(\"Two or more HwModes for " << ModeType
+       << " were found!\");\n"
+       << "    return llvm::countr_zero(Modes) + 1;\n";
+  };
+
   // Start emitting for getHwMode().
   OS << "unsigned " << ClassName
      << "::getHwMode(enum HwModeType type) const {\n";
-  OS << "  unsigned Modes = getHwModeSet();\n";
-  OS << "\n  if(!Modes)\n    return Modes;\n\n";
+  OS << "  unsigned Modes = getHwModeSet();\n\n";
+  OS << "  if (!Modes)\n    return Modes;\n\n";
   OS << "  switch (type) {\n";
-  OS << "  case HwMode_Default:\n    return llvm::countr_zero(Modes) + 1;\n"
-     << "    break;\n";
-  OS << "  case HwMode_ValueType: {\n    Modes &= " << ValueTypeModes << ";\n"
-     << "    if (!Modes)\n      return Modes;\n"
-     << "    if (!llvm::has_single_bit<unsigned>(Modes))\n"
-     << "      llvm_unreachable(\"Two or more HwModes for ValueType were "
-        "found!\");\n"
-     << "    return llvm::countr_zero(Modes) + 1;\n  }\n";
-  OS << "  case HwMode_RegInfo: {\n    Modes &= " << RegInfoModes << ";\n"
-     << "    if (!Modes)\n      return Modes;\n"
-     << "    if (!llvm::has_single_bit<unsigned>(Modes))\n"
-     << "      llvm_unreachable(\"Two or more HwModes for RegInfo were "
-        "found!\");\n"
-     << "    return llvm::countr_zero(Modes) + 1;\n  }\n";
-  OS << "  case HwMode_EncodingInfo: {\n    Modes &= " << EncodingInfoModes
-     << ";\n"
-     << "    if (!Modes)\n      return Modes;\n"
-     << "    if (!llvm::has_single_bit<unsigned>(Modes))\n"
-     << "      llvm_unreachable(\"Two or more HwModes for Encoding were "
-        "found!\");\n"
-     << "    return llvm::countr_zero(Modes) + 1;\n  }\n";
+  OS << "  case HwMode_Default:\n    return llvm::countr_zero(Modes) + 1;\n";
+  handlePerMode("ValueType", ValueTypeModes);
+  handlePerMode("RegInfo", RegInfoModes);
+  handlePerMode("EncodingInfo", EncodingInfoModes);
   OS << "  }\n";
-  OS << "  return 0; // should not get here\n}\n";
+  OS << "  llvm_unreachable(\"unexpected HwModeType\");\n"
+     << "  return 0; // should not get here\n}\n";
   // End emitting for getHwMode().
 }
 



More information about the llvm-commits mailing list