[LLVM][MC][CodeEmitterGen] Reduce various InstBits table sizes (#156213)

Change various `InstBits` tables have an entry only for non-pseudo
target instructions and adjust the indexing into these tables
accordingly.

Some minor refactoring related to this:
- Use early return after handling variable length encodings
- Reduce the scope of anonymous namespace to just the class declaration.

Example reductions in these table sizes for some targets:

```
Target      FirstSupportedOpcode         Reduction in size
AMDGPU                     10813         10813 * 16 = 168KB
RISCV                      12051         12051 * 8 = 94KB
```
This commit is contained in:
Rahul Joshi 2025-09-10 05:36:51 -07:00 committed by GitHub
parent 79e9317893
commit acea1f512e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 185 additions and 177 deletions

View File

@ -114,32 +114,38 @@ def unrelated: Instruction {
// 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-NEXT: UINT64_C(2), UINT64_C(0), // bar
// ENCODER-NEXT: UINT64_C(0), UINT64_C(0), // baz
// ENCODER-NEXT: UINT64_C(8), UINT64_C(0), // foo
// ENCODER-NEXT: UINT64_C(2), UINT64_C(0), // unrelated
// ENCODER-NEXT: };
// 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-NEXT: UINT64_C(2), UINT64_C(0), // bar
// ENCODER-NEXT: UINT64_C(0), UINT64_C(0), // baz
// ENCODER-NEXT: UINT64_C(12), UINT64_C(0), // foo
// ENCODER-NEXT: UINT64_C(2), UINT64_C(0), // unrelated
// ENCODER-NEXT: };
// 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-NEXT: UINT64_C(2), UINT64_C(0), // bar
// ENCODER-NEXT: UINT64_C(12), UINT64_C(0), // baz
// ENCODER-NEXT: UINT64_C(0), UINT64_C(211106232532992), // foo
// ENCODER-NEXT: UINT64_C(2), UINT64_C(0), // unrelated
// ENCODER-NEXT: };
// 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-NEXT: UINT64_C(2), UINT64_C(0), // bar
// ENCODER-NEXT: UINT64_C(0), UINT64_C(0), // baz
// ENCODER-NEXT: UINT64_C(12582915), UINT64_C(0), // foo
// ENCODER-NEXT: UINT64_C(2), UINT64_C(0), // unrelated
// ENCODER-NEXT: };
// ENCODER: const uint64_t *InstBitsByHw;
// ENCODER: constexpr unsigned FirstSupportedOpcode
// ENCODER: const unsigned opcode = MI.getOpcode();
// ENCODER: if (opcode < FirstSupportedOpcode)
// ENCODER: unsigned TableIndex = opcode - FirstSupportedOpcode
// ENCODER: if (Scratch.getBitWidth() != 128)
// ENCODER: Scratch = Scratch.zext(128);
// ENCODER: Inst = APInt(128, ArrayRef(InstBits + opcode * 2, 2));
// ENCODER: Inst = APInt(128, ArrayRef(InstBits + TableIndex * 2, 2));
// ENCODER: APInt &Value = Inst;
// ENCODER: APInt &op = Scratch;
// ENCODER: switch (opcode) {
@ -155,7 +161,7 @@ def unrelated: Instruction {
// 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: Inst = APInt(128, ArrayRef(InstBitsByHw + TableIndex * 2, 2));
// ENCODER: Value = Inst;
// ENCODER: switch (HwMode) {
// ENCODER: default: llvm_unreachable("Unhandled HwMode");
@ -189,7 +195,7 @@ def unrelated: Instruction {
// 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: Inst = APInt(128, ArrayRef(InstBitsByHw + TableIndex * 2, 2));
// ENCODER: Value = Inst;
// ENCODER: switch (HwMode) {
// ENCODER: default: llvm_unreachable("Unhandled HwMode");

View File

@ -189,25 +189,29 @@ def unrelated: Instruction {
// 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(0), // baz
// ENCODER: UINT64_C(8), // foo
// ENCODER: UINT64_C(2), // unrelated
// ENCODER-NEXT: UINT64_C(2), // bar
// ENCODER-NEXT: UINT64_C(0), // baz
// ENCODER-NEXT: UINT64_C(8), // foo
// ENCODER-NEXT: UINT64_C(2), // unrelated
// ENCODER-NEXT: };
// ENCODER-LABEL: static const uint64_t InstBits_ModeA[] = {
// ENCODER: UINT64_C(2), // bar
// ENCODER: UINT64_C(0), // baz
// ENCODER: UINT64_C(12), // foo
// ENCODER: UINT64_C(2), // unrelated
// ENCODER-NEXT: UINT64_C(2), // bar
// ENCODER-NEXT: UINT64_C(0), // baz
// ENCODER-NEXT: UINT64_C(12), // foo
// ENCODER-NEXT: UINT64_C(2), // unrelated
// ENCODER-NEXT: };
// 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-NEXT: UINT64_C(2), // bar
// ENCODER-NEXT: UINT64_C(12), // baz
// ENCODER-NEXT: UINT64_C(3), // foo
// ENCODER-NEXT: UINT64_C(2), // unrelated
// ENCODER-NEXT: };
// 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-NEXT: UINT64_C(2), // bar
// ENCODER-NEXT: UINT64_C(0), // baz
// ENCODER-NEXT: UINT64_C(12582915), // foo
// ENCODER-NEXT: UINT64_C(2), // unrelated
// ENCODER-NEXT: };
// ENCODER-LABEL: case ::bar:
// ENCODER-LABEL: case ::unrelated:
@ -221,7 +225,7 @@ def unrelated: Instruction {
// ENCODER: case 2: InstBitsByHw = InstBits_ModeB; break;
// ENCODER: case 3: InstBitsByHw = InstBits_ModeC; break;
// ENCODER: };
// ENCODER: Value = InstBitsByHw[opcode];
// ENCODER: Value = InstBitsByHw[TableIndex];
// ENCODER: switch (HwMode) {
// ENCODER: default: llvm_unreachable("Unhandled HwMode");
// ENCODER: case 0: {
@ -256,7 +260,7 @@ def unrelated: Instruction {
// ENCODER: default: llvm_unreachable("Unknown hardware mode!"); break;
// ENCODER: case 2: InstBitsByHw = InstBits_ModeB; break;
// ENCODER: };
// ENCODER: Value = InstBitsByHw[opcode];
// ENCODER: Value = InstBitsByHw[TableIndex];
// ENCODER: switch (HwMode) {
// ENCODER: default: llvm_unreachable("Unhandled HwMode");
// ENCODER: case 2: {

View File

@ -31,6 +31,7 @@
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/TableGen/Error.h"
#include "llvm/TableGen/Record.h"
@ -78,6 +79,8 @@ private:
bool UseAPInt = false;
};
} // end anonymous namespace
// If the VarBitInit at position 'bit' matches the specified variable then
// return the variable bit position. Otherwise return -1.
int CodeEmitterGen::getVariableBit(const std::string &VarName,
@ -311,12 +314,12 @@ CodeEmitterGen::getInstructionCases(const Record *R,
if (UseAPInt) {
int NumWords = APInt::getNumWords(BitWidth);
Case += " Inst = APInt(" + itostr(BitWidth);
Case += ", ArrayRef(InstBitsByHw + opcode * " + itostr(NumWords) + ", " +
itostr(NumWords);
Case += ", ArrayRef(InstBitsByHw + TableIndex * " + itostr(NumWords) +
", " + itostr(NumWords);
Case += "));\n";
Case += " Value = Inst;\n";
} else {
Case += " Value = InstBitsByHw[opcode];\n";
Case += " Value = InstBitsByHw[TableIndex];\n";
}
Append(" switch (HwMode) {\n");
@ -396,15 +399,6 @@ void CodeEmitterGen::emitInstructionBaseValues(
for (const CodeGenInstruction *CGI : NumberedInstructions) {
const Record *R = CGI->TheDef;
if (R->getValueAsString("Namespace") == "TargetOpcode" ||
R->getValueAsBit("isPseudo")) {
O << " ";
emitInstBits(O, APInt(BitWidth, 0));
O << ",\n";
continue;
}
const Record *EncodingDef = R;
if (const Record *RV = R->getValueAsOptionalDef("EncodingInfos")) {
EncodingInfoByHwMode EBM(RV, HWM);
@ -461,138 +455,142 @@ void CodeEmitterGen::run(raw_ostream &O) {
// For little-endian instruction bit encodings, reverse the bit order
Target.reverseBitsForLittleEndianEncoding();
ArrayRef<const CodeGenInstruction *> NumberedInstructions =
Target.getInstructions();
ArrayRef<const CodeGenInstruction *> EncodedInstructions =
Target.getTargetNonPseudoInstructions();
if (Target.hasVariableLengthEncodings()) {
emitVarLenCodeEmitter(Records, O);
} else {
const CodeGenHwModes &HWM = Target.getHwModes();
// The set of HwModes used by instruction encodings.
std::set<unsigned> HwModes;
BitWidth = 0;
for (const CodeGenInstruction *CGI : NumberedInstructions) {
const Record *R = CGI->TheDef;
if (R->getValueAsString("Namespace") == "TargetOpcode" ||
R->getValueAsBit("isPseudo"))
continue;
if (const Record *RV = R->getValueAsOptionalDef("EncodingInfos")) {
EncodingInfoByHwMode EBM(RV, HWM);
for (const auto &[Key, Value] : EBM) {
const BitsInit *BI = Value->getValueAsBitsInit("Inst");
BitWidth = std::max(BitWidth, BI->getNumBits());
HwModes.insert(Key);
}
continue;
}
const BitsInit *BI = R->getValueAsBitsInit("Inst");
BitWidth = std::max(BitWidth, BI->getNumBits());
}
UseAPInt = BitWidth > 64;
// Emit function declaration
if (UseAPInt) {
O << "void " << Target.getName()
<< "MCCodeEmitter::getBinaryCodeForInstr(const MCInst &MI,\n"
<< " SmallVectorImpl<MCFixup> &Fixups,\n"
<< " APInt &Inst,\n"
<< " APInt &Scratch,\n"
<< " const MCSubtargetInfo &STI) const {\n";
} else {
O << "uint64_t " << Target.getName();
O << "MCCodeEmitter::getBinaryCodeForInstr(const MCInst &MI,\n"
<< " SmallVectorImpl<MCFixup> &Fixups,\n"
<< " const MCSubtargetInfo &STI) const {\n";
}
// Emit instruction base values
emitInstructionBaseValues(O, NumberedInstructions, Target, DefaultMode);
if (!HwModes.empty()) {
// Emit table for instrs whose encodings are controlled by HwModes.
for (unsigned HwMode : HwModes) {
if (HwMode == DefaultMode)
continue;
emitInstructionBaseValues(O, NumberedInstructions, Target, HwMode);
}
// This pointer will be assigned to the HwMode table later.
O << " const uint64_t *InstBitsByHw;\n";
}
// Map to accumulate all the cases.
std::map<std::string, std::vector<std::string>> CaseMap;
std::map<std::string, std::vector<std::string>> BitOffsetCaseMap;
// Construct all cases statement for each opcode
for (const Record *R : Records.getAllDerivedDefinitions("Instruction")) {
if (R->getValueAsString("Namespace") == "TargetOpcode" ||
R->getValueAsBit("isPseudo"))
continue;
std::string InstName =
(R->getValueAsString("Namespace") + "::" + R->getName()).str();
std::string Case, BitOffsetCase;
std::tie(Case, BitOffsetCase) = getInstructionCases(R, Target);
CaseMap[Case].push_back(InstName);
BitOffsetCaseMap[BitOffsetCase].push_back(std::move(InstName));
}
// Emit initial function code
if (UseAPInt) {
int NumWords = APInt::getNumWords(BitWidth);
O << " const unsigned opcode = MI.getOpcode();\n"
<< " if (Scratch.getBitWidth() != " << BitWidth << ")\n"
<< " Scratch = Scratch.zext(" << BitWidth << ");\n"
<< " Inst = APInt(" << BitWidth << ", ArrayRef(InstBits + opcode * "
<< NumWords << ", " << NumWords << "));\n"
<< " APInt &Value = Inst;\n"
<< " APInt &op = Scratch;\n"
<< " switch (opcode) {\n";
} else {
O << " const unsigned opcode = MI.getOpcode();\n"
<< " uint64_t Value = InstBits[opcode];\n"
<< " uint64_t op = 0;\n"
<< " (void)op; // suppress warning\n"
<< " switch (opcode) {\n";
}
// Emit each case statement
emitCaseMap(O, CaseMap);
// Default case: unhandled opcode
O << " default:\n"
<< " std::string msg;\n"
<< " raw_string_ostream Msg(msg);\n"
<< " Msg << \"Not supported instr: \" << MI;\n"
<< " report_fatal_error(Msg.str().c_str());\n"
<< " }\n";
if (UseAPInt)
O << " Inst = Value;\n";
else
O << " return Value;\n";
O << "}\n\n";
O << "#ifdef GET_OPERAND_BIT_OFFSET\n"
<< "#undef GET_OPERAND_BIT_OFFSET\n\n"
<< "uint32_t " << Target.getName()
<< "MCCodeEmitter::getOperandBitOffset(const MCInst &MI,\n"
<< " unsigned OpNum,\n"
<< " const MCSubtargetInfo &STI) const {\n"
<< " switch (MI.getOpcode()) {\n";
emitCaseMap(O, BitOffsetCaseMap);
O << " }\n"
<< " std::string msg;\n"
<< " raw_string_ostream Msg(msg);\n"
<< " Msg << \"Not supported instr[opcode]: \" << MI << \"[\" << OpNum "
"<< \"]\";\n"
<< " report_fatal_error(Msg.str().c_str());\n"
<< "}\n\n"
<< "#endif // GET_OPERAND_BIT_OFFSET\n\n";
return;
}
}
const CodeGenHwModes &HWM = Target.getHwModes();
// The set of HwModes used by instruction encodings.
std::set<unsigned> HwModes;
BitWidth = 0;
for (const CodeGenInstruction *CGI : EncodedInstructions) {
const Record *R = CGI->TheDef;
if (const Record *RV = R->getValueAsOptionalDef("EncodingInfos")) {
EncodingInfoByHwMode EBM(RV, HWM);
for (const auto &[Key, Value] : EBM) {
const BitsInit *BI = Value->getValueAsBitsInit("Inst");
BitWidth = std::max(BitWidth, BI->getNumBits());
HwModes.insert(Key);
}
continue;
}
const BitsInit *BI = R->getValueAsBitsInit("Inst");
BitWidth = std::max(BitWidth, BI->getNumBits());
}
UseAPInt = BitWidth > 64;
} // end anonymous namespace
// Emit function declaration
if (UseAPInt) {
O << "void " << Target.getName()
<< "MCCodeEmitter::getBinaryCodeForInstr(const MCInst &MI,\n"
<< " SmallVectorImpl<MCFixup> &Fixups,\n"
<< " APInt &Inst,\n"
<< " APInt &Scratch,\n"
<< " const MCSubtargetInfo &STI) const {\n";
} else {
O << "uint64_t " << Target.getName();
O << "MCCodeEmitter::getBinaryCodeForInstr(const MCInst &MI,\n"
<< " SmallVectorImpl<MCFixup> &Fixups,\n"
<< " const MCSubtargetInfo &STI) const {\n";
}
// Emit instruction base values
emitInstructionBaseValues(O, EncodedInstructions, Target, DefaultMode);
if (!HwModes.empty()) {
// Emit table for instrs whose encodings are controlled by HwModes.
for (unsigned HwMode : HwModes) {
if (HwMode == DefaultMode)
continue;
emitInstructionBaseValues(O, EncodedInstructions, Target, HwMode);
}
// This pointer will be assigned to the HwMode table later.
O << " const uint64_t *InstBitsByHw;\n";
}
// Map to accumulate all the cases.
std::map<std::string, std::vector<std::string>> CaseMap;
std::map<std::string, std::vector<std::string>> BitOffsetCaseMap;
// Construct all cases statement for each opcode
for (const CodeGenInstruction *CGI : EncodedInstructions) {
const Record *R = CGI->TheDef;
std::string InstName =
(R->getValueAsString("Namespace") + "::" + R->getName()).str();
std::string Case, BitOffsetCase;
std::tie(Case, BitOffsetCase) = getInstructionCases(R, Target);
CaseMap[Case].push_back(InstName);
BitOffsetCaseMap[BitOffsetCase].push_back(std::move(InstName));
}
unsigned FirstSupportedOpcode = EncodedInstructions.front()->EnumVal;
O << " constexpr unsigned FirstSupportedOpcode = " << FirstSupportedOpcode
<< ";\n";
O << R"(
const unsigned opcode = MI.getOpcode();
if (opcode < FirstSupportedOpcode) {
std::string msg;
raw_string_ostream Msg(msg);
Msg << "Unsupported instruction: " << MI;
report_fatal_error(Msg.str().c_str());
}
unsigned TableIndex = opcode - FirstSupportedOpcode;
)";
// Emit initial function code
if (UseAPInt) {
int NumWords = APInt::getNumWords(BitWidth);
O << " if (Scratch.getBitWidth() != " << BitWidth << ")\n"
<< " Scratch = Scratch.zext(" << BitWidth << ");\n"
<< " Inst = APInt(" << BitWidth << ", ArrayRef(InstBits + TableIndex * "
<< NumWords << ", " << NumWords << "));\n"
<< " APInt &Value = Inst;\n"
<< " APInt &op = Scratch;\n"
<< " switch (opcode) {\n";
} else {
O << " uint64_t Value = InstBits[TableIndex];\n"
<< " uint64_t op = 0;\n"
<< " (void)op; // suppress warning\n"
<< " switch (opcode) {\n";
}
// Emit each case statement
emitCaseMap(O, CaseMap);
// Default case: unhandled opcode.
O << " default:\n"
<< " std::string msg;\n"
<< " raw_string_ostream Msg(msg);\n"
<< " Msg << \"Not supported instr: \" << MI;\n"
<< " report_fatal_error(Msg.str().c_str());\n"
<< " }\n";
if (UseAPInt)
O << " Inst = Value;\n";
else
O << " return Value;\n";
O << "}\n\n";
O << "#ifdef GET_OPERAND_BIT_OFFSET\n"
<< "#undef GET_OPERAND_BIT_OFFSET\n\n"
<< "uint32_t " << Target.getName()
<< "MCCodeEmitter::getOperandBitOffset(const MCInst &MI,\n"
<< " unsigned OpNum,\n"
<< " const MCSubtargetInfo &STI) const {\n"
<< " switch (MI.getOpcode()) {\n";
emitCaseMap(O, BitOffsetCaseMap);
O << " }\n"
<< " std::string msg;\n"
<< " raw_string_ostream Msg(msg);\n"
<< " Msg << \"Not supported instr[opcode]: \" << MI << \"[\" << OpNum "
"<< \"]\";\n"
<< " report_fatal_error(Msg.str().c_str());\n"
<< "}\n\n"
<< "#endif // GET_OPERAND_BIT_OFFSET\n\n";
}
static TableGen::Emitter::OptClass<CodeEmitterGen>
X("gen-emitter", "Generate machine code emitter");