[llvm] [MC][TableGen] Pack MCInstrDesc fields into 24 bytes (PR #202647)

David Zbarsky via llvm-commits llvm-commits at lists.llvm.org
Tue Jun 9 07:01:14 PDT 2026


https://github.com/dzbarsky created https://github.com/llvm/llvm-project/pull/202647

MCInstrDesc stores several generated values in separate 16-bit and 32-bit fields, which leaves padding between the 64-bit Flags member and pointer members. Pack operand and definition counts into one uint16_t, encode operand-constraint size as size plus one so zero denotes no constraints, and store scheduling class, implicit-use count, implicit-def count, and encoding size in bounded bit fields.

Add TableGen range diagnostics for every packed field and update target callers to use the MCInstrDesc accessors. Add static_assert(sizeof(MCInstrDesc) == 24) and a unit test covering the packed accessors so the layout cannot regress silently.

On the current-main arm64 release build, the multicall binary shrinks from 139,804,472 to 138,912,840 bytes, saving 891,632 bytes (0.638%). llvm-mca shrinks from 33,570,728 to 33,488,152 bytes, saving 82,576 bytes (0.246%).

Alternating 12-pair Clang compilations of tramp3d changed mean user CPU from 5.3912 to 5.3851 seconds (-0.11%). Alternating 20-pair llvm-mca instruction-table runs changed mean user CPU from 0.027864 to 0.027879 seconds (+0.05%). Validate MCInstrDescTest.PackedFields, InstrInfoEmitterErrors.td, and a complete current-main MCTests build.

Work towards #202616

>From 07d8ede94ce44c361cef69a7b947554822806a19 Mon Sep 17 00:00:00 2001
From: David Zbarsky <dzbarsky at gmail.com>
Date: Mon, 8 Jun 2026 20:45:49 -0400
Subject: [PATCH] [MC][TableGen] Pack MCInstrDesc fields into 24 bytes

MCInstrDesc stores several generated values in separate 16-bit and 32-bit fields, which leaves padding between the 64-bit Flags member and pointer members. Pack operand and definition counts into one uint16_t, encode operand-constraint size as size plus one so zero denotes no constraints, and store scheduling class, implicit-use count, implicit-def count, and encoding size in bounded bit fields.

Add TableGen range diagnostics for every packed field and update target callers to use the MCInstrDesc accessors. Add static_assert(sizeof(MCInstrDesc) == 24) and a unit test covering the packed accessors so the layout cannot regress silently.

On the current-main arm64 release build, the multicall binary shrinks from 139,804,472 to 138,912,840 bytes, saving 891,632 bytes (0.638%). llvm-mca shrinks from 33,570,728 to 33,488,152 bytes, saving 82,576 bytes (0.246%).

Alternating 12-pair Clang compilations of tramp3d changed mean user CPU from 5.3912 to 5.3851 seconds (-0.11%). Alternating 20-pair llvm-mca instruction-table runs changed mean user CPU from 0.027864 to 0.027879 seconds (+0.05%). Validate MCInstrDescTest.PackedFields, InstrInfoEmitterErrors.td, and a complete current-main MCTests build.
---
 llvm/include/llvm/MC/MCInstrDesc.h            | 63 ++++++++++++++-----
 llvm/lib/MC/MCInstrDesc.cpp                   |  4 +-
 .../AMDGPU/AsmParser/AMDGPUAsmParser.cpp      |  4 +-
 llvm/lib/Target/AMDGPU/SIInstrInfo.cpp        |  8 +--
 llvm/lib/Target/AMDGPU/SIInstrInfo.h          |  4 +-
 .../Target/AMDGPU/Utils/AMDGPUBaseInfo.cpp    |  6 +-
 .../lib/Target/ARM/AsmParser/ARMAsmParser.cpp |  8 +--
 .../ARM/Disassembler/ARMDisassembler.cpp      |  4 +-
 .../ARM/MCTargetDesc/ARMMCTargetDesc.cpp      |  2 +-
 llvm/lib/Target/Mips/MipsSEInstrInfo.cpp      |  2 +-
 .../MCTargetDesc/WebAssemblyInstPrinter.cpp   |  2 +-
 .../WebAssembly/WebAssemblyMCInstLower.cpp    |  5 +-
 llvm/lib/Target/X86/X86InstrInfo.cpp          |  2 +-
 llvm/test/TableGen/InstrInfoEmitterErrors.td  | 20 ++++++
 llvm/unittests/MC/CMakeLists.txt              |  2 +-
 llvm/unittests/MC/MCInstrDescTest.cpp         | 41 ++++++++++++
 llvm/utils/TableGen/InstrInfoEmitter.cpp      | 31 ++++++++-
 17 files changed, 163 insertions(+), 45 deletions(-)
 create mode 100644 llvm/test/TableGen/InstrInfoEmitterErrors.td
 create mode 100644 llvm/unittests/MC/MCInstrDescTest.cpp

diff --git a/llvm/include/llvm/MC/MCInstrDesc.h b/llvm/include/llvm/MC/MCInstrDesc.h
index da65487ce9c65..239bff6023a83 100644
--- a/llvm/include/llvm/MC/MCInstrDesc.h
+++ b/llvm/include/llvm/MC/MCInstrDesc.h
@@ -188,6 +188,7 @@ enum Flag {
   Trap,
   VariadicOpsAreDefs,
   Authenticated,
+  NumFlags,
 };
 } // namespace MCID
 
@@ -203,23 +204,43 @@ class MCInstrDesc {
   // the <Target>Insts table because they rely on knowing their own address to
   // find other information elsewhere in the same table.
 
-  uint32_t Opcode;         // The opcode number.
-  uint16_t NumOperands;    // Num of args (may be more if variable_ops)
-  uint8_t NumDefs;         // Num of args that are definitions
-  uint8_t Size;            // Number of bytes in encoding.
-  uint16_t SchedClass;     // enum identifying instr sched class
-  uint8_t NumImplicitUses; // Num of regs implicitly used
-  uint8_t NumImplicitDefs; // Num of regs implicitly defined
-  uint16_t OpInfoOffset;   // Offset to info about operands
-  uint16_t ImplicitOffset; // Offset to start of implicit op list
-  uint64_t Flags;          // Flags identifying machine instr class
-  uint64_t TSFlags;        // Target Specific Flag values
+  uint64_t TSFlags; // Target-specific flag values.
+  uint64_t Flags : 41; // Flags identifying machine instruction classes.
+  uint64_t Opcode : 16; // The opcode number.
+  uint64_t EncodedSize : 7; // Half the encoded size, or 127 for size 3.
+
+  // Operand counts 126 through 128 are unused. Use 126 and 127 to encode
+  // counts 129 and 130.
+  uint64_t EncodedNumOperands : 7;
+  // Definition count 127 is unused. Use it to encode 128 definitions.
+  uint64_t EncodedNumDefs : 7;
+  uint64_t SchedClass : 13;     // enum identifying instr sched class
+  uint64_t NumImplicitUses : 6; // Num of regs implicitly used
+  uint64_t NumImplicitDefs : 6; // Num of regs implicitly defined
+  uint64_t OpInfoOffset : 15;   // Offset to info about operands
+  uint64_t ImplicitOffset : 10; // Offset to start of implicit op list
+
+  constexpr MCInstrDesc(uint32_t Opcode = 0, uint16_t NumOperands = 0,
+                        uint8_t NumDefs = 0, uint8_t Size = 0,
+                        uint16_t SchedClass = 0,
+                        uint8_t NumImplicitUses = 0,
+                        uint8_t NumImplicitDefs = 0,
+                        uint16_t OpInfoOffset = 0,
+                        uint16_t ImplicitOffset = 0, uint64_t Flags = 0,
+                        uint64_t TSFlags = 0)
+      : TSFlags(TSFlags), Flags(Flags), Opcode(Opcode),
+        EncodedSize(Size == 3 ? 127 : Size / 2),
+        EncodedNumOperands(NumOperands >= 129 ? NumOperands - 3 : NumOperands),
+        EncodedNumDefs(NumDefs == 128 ? 127 : NumDefs),
+        SchedClass(SchedClass), NumImplicitUses(NumImplicitUses),
+        NumImplicitDefs(NumImplicitDefs), OpInfoOffset(OpInfoOffset),
+        ImplicitOffset(ImplicitOffset) {}
 
   /// Returns the value of the specified operand constraint if
   /// it is present. Returns -1 if it is not present.
   int getOperandConstraint(unsigned OpNum,
                            MCOI::OperandConstraint Constraint) const {
-    if (OpNum < NumOperands &&
+    if (OpNum < getNumOperands() &&
         (operands()[OpNum].Constraints & (1 << Constraint))) {
       unsigned ValuePos = 4 + Constraint * 4;
       return (int)(operands()[OpNum].Constraints >> ValuePos) & 0x0f;
@@ -235,18 +256,23 @@ class MCInstrDesc {
   /// instructions may have additional operands at the end of the list, and note
   /// that the machine instruction may include implicit register def/uses as
   /// well.
-  unsigned getNumOperands() const { return NumOperands; }
+  unsigned getNumOperands() const {
+    return EncodedNumOperands >= 126 ? EncodedNumOperands + 3
+                                     : EncodedNumOperands;
+  }
 
   ArrayRef<MCOperandInfo> operands() const {
     auto OpInfo = reinterpret_cast<const MCOperandInfo *>(this + Opcode + 1);
-    return ArrayRef(OpInfo + OpInfoOffset, NumOperands);
+    return ArrayRef(OpInfo + OpInfoOffset, getNumOperands());
   }
 
   /// Return the number of MachineOperands that are register
   /// definitions.  Register definitions always occur at the start of the
   /// machine operand list.  This is the number of "outs" in the .td file,
   /// and does not include implicit defs.
-  unsigned getNumDefs() const { return NumDefs; }
+  unsigned getNumDefs() const {
+    return EncodedNumDefs == 127 ? 128 : EncodedNumDefs;
+  }
 
   /// Return flags of this instruction.
   uint64_t getFlags() const { return Flags; }
@@ -604,7 +630,9 @@ class MCInstrDesc {
 
   /// Return the number of bytes in the encoding of this instruction,
   /// or zero if the encoding size cannot be known from the opcode.
-  unsigned getSize() const { return Size; }
+  unsigned getSize() const {
+    return EncodedSize == 127 ? 3 : EncodedSize * 2;
+  }
 
   /// Find the index of the first operand in the
   /// operand list that is used to represent the predicate. It returns -1 if
@@ -629,6 +657,9 @@ class MCInstrDesc {
                                 const MCRegisterInfo &RI) const;
 };
 
+static_assert(MCID::NumFlags <= 41);
+static_assert(sizeof(MCInstrDesc) == 24);
+
 } // end namespace llvm
 
 #endif
diff --git a/llvm/lib/MC/MCInstrDesc.cpp b/llvm/lib/MC/MCInstrDesc.cpp
index 99cdb44168ec6..d6f8e61308a82 100644
--- a/llvm/lib/MC/MCInstrDesc.cpp
+++ b/llvm/lib/MC/MCInstrDesc.cpp
@@ -39,12 +39,12 @@ bool MCInstrDesc::hasImplicitDefOfPhysReg(MCRegister Reg,
 
 bool MCInstrDesc::hasExplicitDefOfPhysReg(const MCInst &MI, MCRegister Reg,
                                           const MCRegisterInfo &RI) const {
-  for (int i = 0, e = NumDefs; i != e; ++i)
+  for (int i = 0, e = getNumDefs(); i != e; ++i)
     if (MI.getOperand(i).isReg() && MI.getOperand(i).getReg() &&
         RI.isSubRegisterEq(Reg, MI.getOperand(i).getReg()))
       return true;
   if (variadicOpsAreDefs())
-    for (int i = NumOperands - 1, e = MI.getNumOperands(); i != e; ++i)
+    for (int i = getNumOperands() - 1, e = MI.getNumOperands(); i != e; ++i)
       if (MI.getOperand(i).isReg() &&
           RI.isSubRegisterEq(Reg, MI.getOperand(i).getReg()))
         return true;
diff --git a/llvm/lib/Target/AMDGPU/AsmParser/AMDGPUAsmParser.cpp b/llvm/lib/Target/AMDGPU/AsmParser/AMDGPUAsmParser.cpp
index 2a9a31e732a6e..e2c3919da7370 100644
--- a/llvm/lib/Target/AMDGPU/AsmParser/AMDGPUAsmParser.cpp
+++ b/llvm/lib/Target/AMDGPU/AsmParser/AMDGPUAsmParser.cpp
@@ -1388,7 +1388,7 @@ class AMDGPUAsmParser : public MCTargetAsmParser {
 
   /// Get size of register operand
   unsigned getRegOperandSize(const MCInstrDesc &Desc, unsigned OpNo) const {
-    assert(OpNo < Desc.NumOperands);
+    assert(OpNo < Desc.getNumOperands());
     int16_t RCID = MII.getOpRegClassID(Desc.operands()[OpNo], HwMode);
     return getRegBitWidth(RCID) / 8;
   }
@@ -9497,7 +9497,7 @@ static bool isRegOrImmWithInputMods(const MCInstrDesc &Desc, unsigned OpNum) {
       // 1. This operand is input modifiers
       Desc.operands()[OpNum].OperandType == AMDGPU::OPERAND_INPUT_MODS
       // 2. This is not last operand
-      && Desc.NumOperands > (OpNum + 1)
+      && Desc.getNumOperands() > (OpNum + 1)
       // 3. Next operand is register class
       && Desc.operands()[OpNum + 1].RegClass != -1
       // 4. Next register is not tied to any other operand
diff --git a/llvm/lib/Target/AMDGPU/SIInstrInfo.cpp b/llvm/lib/Target/AMDGPU/SIInstrInfo.cpp
index ef9d184555bd6..d8095259bb45f 100644
--- a/llvm/lib/Target/AMDGPU/SIInstrInfo.cpp
+++ b/llvm/lib/Target/AMDGPU/SIInstrInfo.cpp
@@ -257,8 +257,8 @@ bool SIInstrInfo::areLoadsFromSameBasePtr(SDNode *Load0, SDNode *Load1,
     // getNamedOperandIdx returns the index for MachineInstrs.  Since they
     // include the output in the operand list, but SDNodes don't, we need to
     // subtract the index by one.
-    Offset0Idx -= get(Opc0).NumDefs;
-    Offset1Idx -= get(Opc1).NumDefs;
+    Offset0Idx -= get(Opc0).getNumDefs();
+    Offset1Idx -= get(Opc1).getNumDefs();
     Offset0 = Load0->getConstantOperandVal(Offset0Idx);
     Offset1 = Load1->getConstantOperandVal(Offset1Idx);
     return true;
@@ -314,8 +314,8 @@ bool SIInstrInfo::areLoadsFromSameBasePtr(SDNode *Load0, SDNode *Load1,
     // getNamedOperandIdx returns the index for MachineInstrs.  Since they
     // include the output in the operand list, but SDNodes don't, we need to
     // subtract the index by one.
-    OffIdx0 -= get(Opc0).NumDefs;
-    OffIdx1 -= get(Opc1).NumDefs;
+    OffIdx0 -= get(Opc0).getNumDefs();
+    OffIdx1 -= get(Opc1).getNumDefs();
 
     SDValue Off0 = Load0->getOperand(OffIdx0);
     SDValue Off1 = Load1->getOperand(OffIdx1);
diff --git a/llvm/lib/Target/AMDGPU/SIInstrInfo.h b/llvm/lib/Target/AMDGPU/SIInstrInfo.h
index 831aa9ebb8435..c2053a1213eca 100644
--- a/llvm/lib/Target/AMDGPU/SIInstrInfo.h
+++ b/llvm/lib/Target/AMDGPU/SIInstrInfo.h
@@ -1317,7 +1317,7 @@ class SIInstrInfo final : public AMDGPUGenInstrInfo {
                         const MachineOperand &DefMO) const {
     assert(UseMO.getParent() == &MI);
     int OpIdx = UseMO.getOperandNo();
-    if (OpIdx >= MI.getDesc().NumOperands)
+    if (OpIdx >= MI.getDesc().getNumOperands())
       return false;
 
     return isInlineConstant(DefMO, MI.getDesc().operands()[OpIdx]);
@@ -1332,7 +1332,7 @@ class SIInstrInfo final : public AMDGPUGenInstrInfo {
 
   bool isInlineConstant(const MachineInstr &MI, unsigned OpIdx,
                         int64_t ImmVal) const {
-    if (OpIdx >= MI.getDesc().NumOperands)
+    if (OpIdx >= MI.getDesc().getNumOperands())
       return false;
 
     if (isCopyInstr(MI)) {
diff --git a/llvm/lib/Target/AMDGPU/Utils/AMDGPUBaseInfo.cpp b/llvm/lib/Target/AMDGPU/Utils/AMDGPUBaseInfo.cpp
index 33df51e8a7e07..e8915d1f331ef 100644
--- a/llvm/lib/Target/AMDGPU/Utils/AMDGPUBaseInfo.cpp
+++ b/llvm/lib/Target/AMDGPU/Utils/AMDGPUBaseInfo.cpp
@@ -2868,14 +2868,14 @@ bool isInlineValue(MCRegister Reg) {
 #undef MAP_REG2REG
 
 bool isKImmOperand(const MCInstrDesc &Desc, unsigned OpNo) {
-  assert(OpNo < Desc.NumOperands);
+  assert(OpNo < Desc.getNumOperands());
   unsigned OpType = Desc.operands()[OpNo].OperandType;
   return OpType >= AMDGPU::OPERAND_KIMM_FIRST &&
          OpType <= AMDGPU::OPERAND_KIMM_LAST;
 }
 
 bool isSISrcFPOperand(const MCInstrDesc &Desc, unsigned OpNo) {
-  assert(OpNo < Desc.NumOperands);
+  assert(OpNo < Desc.getNumOperands());
   unsigned OpType = Desc.operands()[OpNo].OperandType;
   switch (OpType) {
   case AMDGPU::OPERAND_REG_IMM_FP32:
@@ -2898,7 +2898,7 @@ bool isSISrcFPOperand(const MCInstrDesc &Desc, unsigned OpNo) {
 }
 
 bool isSISrcInlinableOperand(const MCInstrDesc &Desc, unsigned OpNo) {
-  assert(OpNo < Desc.NumOperands);
+  assert(OpNo < Desc.getNumOperands());
   unsigned OpType = Desc.operands()[OpNo].OperandType;
   return (OpType >= AMDGPU::OPERAND_REG_INLINE_C_FIRST &&
           OpType <= AMDGPU::OPERAND_REG_INLINE_C_LAST) ||
diff --git a/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp b/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp
index a4ac7f61713e0..860dd924a3a20 100644
--- a/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp
+++ b/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp
@@ -7636,7 +7636,7 @@ bool ARMAsmParser::validateLDRDSTRD(MCInst &Inst, const OperandVector &Operands,
 }
 
 static int findFirstVectorPredOperandIdx(const MCInstrDesc &MCID) {
-  for (unsigned i = 0; i < MCID.NumOperands; ++i) {
+  for (unsigned i = 0; i < MCID.getNumOperands(); ++i) {
     if (ARM::isVpred(MCID.operands()[i].OperandType))
       return i;
   }
@@ -11199,11 +11199,11 @@ unsigned ARMAsmParser::checkTargetMatchPredicate(MCInst &Inst) {
   if (MCID.TSFlags & ARMII::ThumbArithFlagSetting) {
     assert(MCID.hasOptionalDef() &&
            "optionally flag setting instruction missing optional def operand");
-    assert(MCID.NumOperands == Inst.getNumOperands() &&
+    assert(MCID.getNumOperands() == Inst.getNumOperands() &&
            "operand count mismatch!");
     bool IsCPSR = false;
     // Check if the instruction has CPSR set.
-    for (unsigned OpNo = 0; OpNo < MCID.NumOperands; ++OpNo) {
+    for (unsigned OpNo = 0; OpNo < MCID.getNumOperands(); ++OpNo) {
       if (MCID.operands()[OpNo].isOptionalDef() &&
           Inst.getOperand(OpNo).isReg() &&
           Inst.getOperand(OpNo).getReg() == ARM::CPSR)
@@ -11290,7 +11290,7 @@ unsigned ARMAsmParser::checkTargetMatchPredicate(MCInst &Inst) {
     break;
   }
 
-  for (unsigned I = 0; I < MCID.NumOperands; ++I)
+  for (unsigned I = 0; I < MCID.getNumOperands(); ++I)
     if (MCID.operands()[I].RegClass == ARM::rGPRRegClassID) {
       // rGPRRegClass excludes PC, and also excluded SP before ARMv8
       const auto &Op = Inst.getOperand(I);
diff --git a/llvm/lib/Target/ARM/Disassembler/ARMDisassembler.cpp b/llvm/lib/Target/ARM/Disassembler/ARMDisassembler.cpp
index 2fa07ee611d49..45dd019a37f67 100644
--- a/llvm/lib/Target/ARM/Disassembler/ARMDisassembler.cpp
+++ b/llvm/lib/Target/ARM/Disassembler/ARMDisassembler.cpp
@@ -6242,7 +6242,7 @@ DecodeStatus ARMDisassembler::getARMInstruction(MCInst &MI, uint64_t &Size,
 
 bool ARMDisassembler::isVectorPredicable(const MCInst &MI) const {
   const MCInstrDesc &MCID = MCII->get(MI.getOpcode());
-  for (unsigned i = 0; i < MCID.NumOperands; ++i) {
+  for (unsigned i = 0; i < MCID.getNumOperands(); ++i) {
     if (ARM::isVpred(MCID.operands()[i].OperandType))
       return true;
   }
@@ -6332,7 +6332,7 @@ void ARMDisassembler::UpdateThumbPredicate(DecodeStatus &S, MCInst &MI) const {
   const MCInstrDesc &MCID = MCII->get(MI.getOpcode());
   ArrayRef<MCOperandInfo> OpInfo = MCID.operands();
   MCInst::iterator I = MI.begin();
-  unsigned short NumOps = MCID.NumOperands;
+  unsigned short NumOps = MCID.getNumOperands();
   for (unsigned i = 0; i < NumOps; ++i, ++I) {
     if (OpInfo[i].isPredicate() ) {
       if (CC != ARMCC::AL && !MCID.isPredicable())
diff --git a/llvm/lib/Target/ARM/MCTargetDesc/ARMMCTargetDesc.cpp b/llvm/lib/Target/ARM/MCTargetDesc/ARMMCTargetDesc.cpp
index 467fdbfd0e05b..b9089814211d9 100644
--- a/llvm/lib/Target/ARM/MCTargetDesc/ARMMCTargetDesc.cpp
+++ b/llvm/lib/Target/ARM/MCTargetDesc/ARMMCTargetDesc.cpp
@@ -572,7 +572,7 @@ std::optional<uint64_t> ARMMCInstrAnalysis::evaluateMemoryOperandAddress(
     return std::nullopt;
 
   // Find the memory addressing operand in the instruction.
-  unsigned OpIndex = Desc.NumDefs;
+  unsigned OpIndex = Desc.getNumDefs();
   while (OpIndex < Desc.getNumOperands() &&
          Desc.operands()[OpIndex].OperandType != MCOI::OPERAND_MEMORY)
     ++OpIndex;
diff --git a/llvm/lib/Target/Mips/MipsSEInstrInfo.cpp b/llvm/lib/Target/Mips/MipsSEInstrInfo.cpp
index 6c59975a1b501..f8049a92deabd 100644
--- a/llvm/lib/Target/Mips/MipsSEInstrInfo.cpp
+++ b/llvm/lib/Target/Mips/MipsSEInstrInfo.cpp
@@ -772,7 +772,7 @@ std::pair<bool, bool>
 MipsSEInstrInfo::compareOpndSize(unsigned Opc,
                                  const MachineFunction &MF) const {
   const MCInstrDesc &Desc = get(Opc);
-  assert(Desc.NumOperands == 2 && "Unary instruction expected.");
+  assert(Desc.getNumOperands() == 2 && "Unary instruction expected.");
   const MipsRegisterInfo *RI = &getRegisterInfo();
   unsigned DstRegSize = RI->getRegSizeInBits(*getRegClass(Desc, 0));
   unsigned SrcRegSize = RI->getRegSizeInBits(*getRegClass(Desc, 1));
diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp
index 8cee0788def0a..dd700ba965cdc 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp
@@ -280,7 +280,7 @@ void WebAssemblyInstPrinter::printInst(const MCInst *MI, uint64_t Address,
 
     // Annotate any control flow label references.
 
-    unsigned NumFixedOperands = Desc.NumOperands;
+    unsigned NumFixedOperands = Desc.getNumOperands();
     SmallSet<uint64_t, 8> Printed;
     for (unsigned I = 0, E = MI->getNumOperands(); I < E; ++I) {
       // See if this operand denotes a basic block target.
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp
index e48283aadb437..faf43140e57cd 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp
@@ -234,7 +234,8 @@ void WebAssemblyMCInstLower::lower(const MachineInstr *MI,
       // Currently this is the only way that CImmediates show up so panic if we
       // get confused.
       unsigned DescIndex = I - NumVariadicDefs;
-      assert(DescIndex < Desc.NumOperands && "unexpected CImmediate operand");
+      assert(DescIndex < Desc.getNumOperands() &&
+             "unexpected CImmediate operand");
       auto Operands = Desc.operands();
       const MCOperandInfo &Info = Operands[DescIndex];
       assert(Info.OperandType == WebAssembly::OPERAND_TYPEINDEX &&
@@ -245,7 +246,7 @@ void WebAssemblyMCInstLower::lower(const MachineInstr *MI,
     }
     case MachineOperand::MO_Immediate: {
       unsigned DescIndex = I - NumVariadicDefs;
-      if (DescIndex < Desc.NumOperands) {
+      if (DescIndex < Desc.getNumOperands()) {
         auto Operands = Desc.operands();
         const MCOperandInfo &Info = Operands[DescIndex];
         // Replace type index placeholder with actual type index. The type index
diff --git a/llvm/lib/Target/X86/X86InstrInfo.cpp b/llvm/lib/Target/X86/X86InstrInfo.cpp
index 15d2e10aa0f08..c9f064a889542 100644
--- a/llvm/lib/Target/X86/X86InstrInfo.cpp
+++ b/llvm/lib/Target/X86/X86InstrInfo.cpp
@@ -8764,7 +8764,7 @@ bool X86InstrInfo::unfoldMemoryOperand(
   MachineFunction &MF = DAG.getMachineFunction();
   const TargetRegisterInfo &TRI = *MF.getSubtarget().getRegisterInfo();
   const TargetRegisterClass *RC = getRegClass(MCID, Index);
-  unsigned NumDefs = MCID.NumDefs;
+  unsigned NumDefs = MCID.getNumDefs();
   std::vector<SDValue> AddrOps;
   std::vector<SDValue> BeforeOps;
   std::vector<SDValue> AfterOps;
diff --git a/llvm/test/TableGen/InstrInfoEmitterErrors.td b/llvm/test/TableGen/InstrInfoEmitterErrors.td
new file mode 100644
index 0000000000000..32ea972c28a53
--- /dev/null
+++ b/llvm/test/TableGen/InstrInfoEmitterErrors.td
@@ -0,0 +1,20 @@
+// RUN: not llvm-tblgen -gen-instr-info -I %p/../../include %s 2>&1 | FileCheck %s
+
+include "llvm/Target/Target.td"
+
+def Reg : Register<"reg">;
+def Regs : RegisterClass<"test", [i32], 0, (add Reg)>;
+
+def TestInstrInfo : InstrInfo;
+def TestTarget : Target {
+  let InstructionSet = TestInstrInfo;
+}
+
+// CHECK: error: instruction size cannot be encoded in 7 bits
+def InvalidSize : Instruction {
+  let Namespace = "Test";
+  let OutOperandList = (outs);
+  let InOperandList = (ins);
+  let AsmString = "";
+  let Size = 254;
+}
diff --git a/llvm/unittests/MC/CMakeLists.txt b/llvm/unittests/MC/CMakeLists.txt
index 4881888f03742..4b133b57cfdae 100644
--- a/llvm/unittests/MC/CMakeLists.txt
+++ b/llvm/unittests/MC/CMakeLists.txt
@@ -20,8 +20,8 @@ add_llvm_unittest(MCTests
   DwarfLineTables.cpp
   DwarfLineTableHeaders.cpp
   MCInstPrinter.cpp
+  MCInstrDescTest.cpp
   StringTableBuilderTest.cpp
   TargetRegistry.cpp
   MCDisassemblerTest.cpp
   )
-
diff --git a/llvm/unittests/MC/MCInstrDescTest.cpp b/llvm/unittests/MC/MCInstrDescTest.cpp
new file mode 100644
index 0000000000000..7228614945e1e
--- /dev/null
+++ b/llvm/unittests/MC/MCInstrDescTest.cpp
@@ -0,0 +1,41 @@
+//===- MCInstrDescTest.cpp - MCInstrDesc unit tests -----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/MC/MCInstrDesc.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+namespace {
+
+TEST(MCInstrDescTest, PackedFields) {
+  for (unsigned NumOperands : {0U, 1U, 125U, 129U, 130U}) {
+    for (unsigned NumDefs : {0U, 1U, 126U, 128U}) {
+      for (unsigned Size : {0U, 2U, 3U, 4U, 252U}) {
+        MCInstrDesc Desc(65535, NumOperands, NumDefs, Size, 8191, 63, 63,
+                         32767, 1023,
+                         (1ULL << MCID::Authenticated) | 1, UINT64_MAX);
+
+        EXPECT_EQ(Desc.getOpcode(), 65535U);
+        EXPECT_EQ(Desc.getNumOperands(), NumOperands);
+        EXPECT_EQ(Desc.getNumDefs(), NumDefs);
+        EXPECT_EQ(Desc.getSize(), Size);
+        EXPECT_EQ(Desc.getSchedClass(), 8191U);
+        EXPECT_EQ(Desc.NumImplicitUses, 63U);
+        EXPECT_EQ(Desc.NumImplicitDefs, 63U);
+        EXPECT_EQ(Desc.OpInfoOffset, 32767U);
+        EXPECT_EQ(Desc.ImplicitOffset, 1023U);
+        EXPECT_TRUE(Desc.isPreISelOpcode());
+        EXPECT_TRUE(Desc.isAuthenticated());
+        EXPECT_EQ(Desc.TSFlags, UINT64_MAX);
+      }
+    }
+  }
+}
+
+} // namespace
diff --git a/llvm/utils/TableGen/InstrInfoEmitter.cpp b/llvm/utils/TableGen/InstrInfoEmitter.cpp
index 08526cc2a72bf..f18f1766046ee 100644
--- a/llvm/utils/TableGen/InstrInfoEmitter.cpp
+++ b/llvm/utils/TableGen/InstrInfoEmitter.cpp
@@ -950,6 +950,8 @@ void InstrInfoEmitter::run(raw_ostream &OS) {
       ImplicitListSize += ImplicitOps.size();
     }
   }
+  if (!isUInt<10>(ImplicitListSize))
+    PrintFatalError("implicit operand table does not fit in 10-bit offsets");
 
   {
     IfGuardEmitter IfGuard(
@@ -1002,7 +1004,10 @@ void InstrInfoEmitter::run(raw_ostream &OS) {
        << "InstrTable::Padding) % sizeof(MCOperandInfo) == 0);\n";
     OS << "static constexpr unsigned " << TargetName << "OpInfoBase = (sizeof "
        << TargetName << "InstrTable::ImplicitOps + sizeof " << TargetName
-       << "InstrTable::Padding) / sizeof(MCOperandInfo);\n\n";
+       << "InstrTable::Padding) / sizeof(MCOperandInfo);\n";
+    OS << "static_assert(" << TargetName << "OpInfoBase + " << OperandInfoSize
+       << " <= (1U << 15), "
+          "\"operand info table does not fit in 15-bit offsets\");\n\n";
 
     OS << "extern const " << TargetName << "InstrTable " << TargetName
        << "Descs = {\n  {\n";
@@ -1295,10 +1300,30 @@ void InstrInfoEmitter::emitRecord(
     DefOperands = Opnd.MIOperandNo + Opnd.MINumOperands;
   }
 
+  int64_t Size = Inst.TheDef->getValueAsInt("Size");
+  unsigned SchedClass = SchedModels.getSchedClassIdx(Inst);
+  if (!isUInt<16>(Num))
+    PrintFatalError(Inst.TheDef, "instruction opcode does not fit in 16 bits");
+  if (MinOperands > 125 && MinOperands != 129 && MinOperands != 130)
+    PrintFatalError(Inst.TheDef,
+                    "instruction operand count cannot be encoded in 7 bits");
+  if (DefOperands > 126 && DefOperands != 128)
+    PrintFatalError(Inst.TheDef,
+                    "instruction definition count cannot be encoded in 7 bits");
+  if (Size != 3 && (Size < 0 || Size > 252 || Size % 2 != 0))
+    PrintFatalError(Inst.TheDef,
+                    "instruction size cannot be encoded in 7 bits");
+  if (!isUInt<13>(SchedClass))
+    PrintFatalError(Inst.TheDef,
+                    "instruction scheduling class does not fit in 13 bits");
+  if (!isUInt<6>(Inst.ImplicitUses.size()) ||
+      !isUInt<6>(Inst.ImplicitDefs.size()))
+    PrintFatalError(Inst.TheDef,
+                    "implicit register count does not fit in 6 bits");
+
   OS << "    { ";
   OS << Num << ",\t" << MinOperands << ",\t" << DefOperands << ",\t"
-     << Inst.TheDef->getValueAsInt("Size") << ",\t"
-     << SchedModels.getSchedClassIdx(Inst) << ",\t";
+     << Size << ",\t" << SchedClass << ",\t";
 
   const CodeGenTarget &Target = CDP.getTargetInfo();
 



More information about the llvm-commits mailing list