[llvm] [TableGen] Compact two-word APInt encoder tables (PR #202630)
David Zbarsky via llvm-commits
llvm-commits at lists.llvm.org
Tue Jun 9 07:00:18 PDT 2026
https://github.com/dzbarsky created https://github.com/llvm/llvm-project/pull/202630
For instruction encodings between 65 and 128 bits without hardware-mode tables, store only the low 64-bit base value in InstBits. Emit the fixed nonzero high word in the existing opcode switch, where opcodes with identical operand code and high bits already share a case.
The common encoding path removes one table load. Encodings wider than two words and hardware-mode encodings retain the existing APInt word array.
For AMDGPU, encoder constants shrink from 708,064 to 354,192 bytes and encoder object sections shrink by 354,280 bytes. A stripped AMDGPU llvm-mc shrinks from 20,900,800 to 20,537,544 bytes, saving 363,256 bytes (1.74%).
CPU measurements show no regression: common instructions improve from 556.0 to 548.3 ms, and instructions with nonzero upper words change from 191.3 to 192.2 ms.
Extend BigEncoder.td to check low-word tables and high-word insertion. Validate BigEncoder.td and HwModeEncodeAPInt.td, compare all 58 nonzero AMDGPU upper-word values, and produce byte-identical tensor, WMMA, and MFMA encodings with the official MC tests.
Work towards #202616
>From 40ae1cd7a18af7657b5076f15073436622c4a8a8 Mon Sep 17 00:00:00 2001
From: David Zbarsky <dzbarsky at gmail.com>
Date: Mon, 8 Jun 2026 23:51:42 -0400
Subject: [PATCH] [TableGen] Compact two-word APInt encoder tables
For instruction encodings between 65 and 128 bits without hardware-mode tables, store only the low 64-bit base value in InstBits. Emit the fixed nonzero high word in the existing opcode switch, where opcodes with identical operand code and high bits already share a case.
The common encoding path removes one table load. Encodings wider than two words and hardware-mode encodings retain the existing APInt word array.
For AMDGPU, encoder constants shrink from 708,064 to 354,192 bytes and encoder object sections shrink by 354,280 bytes. A stripped AMDGPU llvm-mc shrinks from 20,900,800 to 20,537,544 bytes, saving 363,256 bytes (1.74%).
CPU measurements show no regression: common instructions improve from 556.0 to 548.3 ms, and instructions with nonzero upper words change from 191.3 to 192.2 ms.
Extend BigEncoder.td to check low-word tables and high-word insertion. Validate BigEncoder.td and HwModeEncodeAPInt.td, compare all 58 nonzero AMDGPU upper-word values, and produce byte-identical tensor, WMMA, and MFMA encodings with the official MC tests.
---
llvm/test/TableGen/BigEncoder.td | 15 ++++++--
llvm/utils/TableGen/CodeEmitterGen.cpp | 50 ++++++++++++++++++--------
2 files changed, 48 insertions(+), 17 deletions(-)
diff --git a/llvm/test/TableGen/BigEncoder.td b/llvm/test/TableGen/BigEncoder.td
index 9b9d382433508..999acee8b191c 100644
--- a/llvm/test/TableGen/BigEncoder.td
+++ b/llvm/test/TableGen/BigEncoder.td
@@ -21,6 +21,7 @@ def foo : Instruction {
bits<32> factor;
let Inst{7...0} = 0xAA;
let Inst{14...8} = factor{6...0}; // no offset
+ let Inst{64} = 1;
let AsmString = "foo $factor";
field bits<16> SoftFail = 0;
}
@@ -46,12 +47,20 @@ def biz : Instruction {
}
}
+// CHECK: static const uint64_t InstBits[] = {
+// CHECK: UINT64_C(187), // bar
+// CHECK: UINT64_C(204), // biz
+// CHECK: UINT64_C(170), // foo
+// CHECK: };
+// CHECK: Inst = APInt(65, InstBits[TableIndex]);
+
// CHECK-LABEL: case ::biz: {
// CHECK: Value.insertBits(op.extractBitsAsZExtValue(4, 3), 12, 4);
// CHECK-NEXT: Value.insertBits(op.extractBitsAsZExtValue(4, 7), 8, 4);
-// CHECK-LABEL: case ::foo: {
-// CHECK: Value.insertBits(op.extractBitsAsZExtValue(7, 0), 8, 7);
-
// CHECK-LABEL: case ::bar: {
// CHECK: Value.insertBits(op.extractBitsAsZExtValue(8, 3), 8, 8);
+
+// CHECK-LABEL: case ::foo: {
+// CHECK: Value.insertBits(UINT64_C(1), 64, 1);
+// CHECK: Value.insertBits(op.extractBitsAsZExtValue(7, 0), 8, 7);
diff --git a/llvm/utils/TableGen/CodeEmitterGen.cpp b/llvm/utils/TableGen/CodeEmitterGen.cpp
index fadb4ee1142b6..8cdf0c5e4966d 100644
--- a/llvm/utils/TableGen/CodeEmitterGen.cpp
+++ b/llvm/utils/TableGen/CodeEmitterGen.cpp
@@ -79,6 +79,7 @@ class CodeEmitterGen {
unsigned HwMode = DefaultMode);
unsigned BitWidth = 0u;
bool UseAPInt = false;
+ bool CompactTwoWordInstBits = false;
};
} // end anonymous namespace
@@ -361,6 +362,16 @@ static void emitInstBits(raw_ostream &OS, const APInt &Bits) {
OS << ((I > 0) ? ", " : "") << "UINT64_C(" << Bits.getRawData()[I] << ")";
}
+static APInt getInstructionBaseValue(const Record *EncodingDef,
+ unsigned BitWidth) {
+ const BitsInit *BI = EncodingDef->getValueAsBitsInit("Inst");
+ APInt Value(BitWidth, 0);
+ for (unsigned I = 0, E = BI->getNumBits(); I != E; ++I)
+ if (const auto *B = dyn_cast<BitInit>(BI->getBit(I)); B && B->getValue())
+ Value.setBit(I);
+ return Value;
+}
+
void CodeEmitterGen::emitInstructionBaseValues(
raw_ostream &O, ArrayRef<const CodeGenInstruction *> NumberedInstructions,
unsigned HwMode) {
@@ -387,16 +398,12 @@ void CodeEmitterGen::emitInstructionBaseValues(
continue;
}
}
- const BitsInit *BI = EncodingDef->getValueAsBitsInit("Inst");
-
- // Start by filling in fixed values.
- APInt Value(BitWidth, 0);
- for (unsigned I = 0, E = BI->getNumBits(); I != E; ++I) {
- if (const auto *B = dyn_cast<BitInit>(BI->getBit(I)); B && B->getValue())
- Value.setBit(I);
- }
+ APInt Value = getInstructionBaseValue(EncodingDef, BitWidth);
O << " ";
- emitInstBits(O, Value);
+ if (CompactTwoWordInstBits)
+ O << "UINT64_C(" << Value.getRawData()[0] << ")";
+ else
+ emitInstBits(O, Value);
O << "," << '\t' << "// " << R->getName() << "\n";
}
O << " };\n";
@@ -436,6 +443,8 @@ void CodeEmitterGen::run(raw_ostream &O) {
BitWidth = std::max(BitWidth, BI->getNumBits());
}
UseAPInt = BitWidth > 64;
+ CompactTwoWordInstBits =
+ UseAPInt && HwModes.empty() && APInt::getNumWords(BitWidth) == 2;
// Emit function declaration
if (UseAPInt) {
@@ -474,6 +483,14 @@ void CodeEmitterGen::run(raw_ostream &O) {
const Record *R = CGI->TheDef;
auto [Case, BitOffsetCase] = getInstructionCases(R);
+ if (CompactTwoWordInstBits) {
+ APInt BaseValue = getInstructionBaseValue(R, BitWidth);
+ uint64_t HighWord = BaseValue.getRawData()[1];
+ if (HighWord)
+ Case.insert(0, " Value.insertBits(UINT64_C(" + utostr(HighWord) +
+ "), 64, " + utostr(BitWidth - 64) + ");\n");
+ }
+
CaseMap[Case].push_back(Index);
BitOffsetCaseMap[BitOffsetCase].push_back(Index);
}
@@ -496,12 +513,17 @@ void CodeEmitterGen::run(raw_ostream &O) {
// 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"
+ << " Scratch = Scratch.zext(" << BitWidth << ");\n";
+ if (CompactTwoWordInstBits)
+ O << " Inst = APInt(" << BitWidth << ", InstBits[TableIndex]);\n";
+ else {
+ int NumWords = APInt::getNumWords(BitWidth);
+ O << " Inst = APInt(" << BitWidth
+ << ", ArrayRef(InstBits + TableIndex * " << NumWords << ", " << NumWords
+ << "));\n";
+ }
+ O << " APInt &Value = Inst;\n"
<< " APInt &op = Scratch;\n"
<< " switch (opcode) {\n";
} else {
More information about the llvm-commits
mailing list