[llvm] [MC][TableGen] Compress generated instruction name tables (PR #202643)
David Zbarsky via llvm-commits
llvm-commits at lists.llvm.org
Tue Jun 9 07:00:57 PDT 2026
https://github.com/dzbarsky created https://github.com/llvm/llvm-project/pull/202643
Generated instruction names account for several MiB in tools that link
multiple targets. The existing suffix-sharing table leaves most repeated
opcode prefixes unshared, particularly for AMDGPU.
Emit names as opcode-ordered, front-coded blocks of 128 entries. Decode
blocks lazily in MCInstrInfo::getName(), publish them with atomics, and
cache 16-bit offsets and 8-bit lengths so warmed lookups avoid strlen.
Keep the legacy MCInstrInfo initializer and preserve the MCInstrInfo
object size. M68k now uses MCInstrInfo::getName() instead of the generated
raw arrays. Materialize names passed to generated std::ostringstream
feature-verifier diagnostics.
In a Release build with AMDGPU, AArch64, RISCV, and X86, stripped sizes
change as follows:
llvm-mc: 32,777,520 -> 30,762,368 bytes (-2,015,152, -6.15%)
llvm-mca: 29,013,728 -> 26,998,544 bytes (-2,015,184, -6.95%)
llvm-objdump: 28,806,984 -> 26,791,832 bytes (-2,015,152, -7.00%)
AMDGPUMCTargetDesc.cpp.o shrinks from 6,762,192 to 5,509,776 bytes
(-1,252,416, -18.52%). Across all 26 generated target tables, static
instruction-name storage falls from 3,373,005 to 887,359 bytes
(-2,485,646, -73.69%).
The stripped downstream all-tools multicall binary decreases from 142,015,464
to 139,804,472 bytes, saving 2,210,992 bytes (1.557%).
A 50,000-run X86 llvm-mc disassembly benchmark has unchanged mean user
CPU time (0.570809 seconds -> 0.570828 seconds, +0.0032%). One thousand
alternating empty AMDGPU llvm-mc invocations improve user CPU time by
2.0% (1.178458 seconds -> 1.154751 seconds). Warmed getName() lookups are
faster in both sequential and randomized focused benchmarks because
decoded lengths avoid strlen.
Validated all 142,869 generated instruction names across every target,
568 MC disassembler tests, the llvm-mca suite (1,380 passed and 2
expected failures), both affected TableGen tests, a concurrent block-decoder
unit test, and downstream Release builds of llc and llvm-mc.
Work towards #202616
>From 1eb62e3323fb67f4b9837ac8a51380c1ec5c7a82 Mon Sep 17 00:00:00 2001
From: David Zbarsky <dzbarsky at gmail.com>
Date: Mon, 8 Jun 2026 16:46:22 -0400
Subject: [PATCH] [MC][TableGen] Compress generated instruction name tables
Generated instruction names account for several MiB in tools that link
multiple targets. The existing suffix-sharing table leaves most repeated
opcode prefixes unshared, particularly for AMDGPU.
Emit names as opcode-ordered, front-coded blocks of 128 entries. Decode
blocks lazily in MCInstrInfo::getName(), publish them with atomics, and
cache 16-bit offsets and 8-bit lengths so warmed lookups avoid strlen.
Keep the legacy MCInstrInfo initializer and preserve the MCInstrInfo
object size. M68k now uses MCInstrInfo::getName() instead of the generated
raw arrays. Materialize names passed to generated std::ostringstream
feature-verifier diagnostics.
In a Release build with AMDGPU, AArch64, RISCV, and X86, stripped sizes
change as follows:
llvm-mc: 32,777,520 -> 30,762,368 bytes (-2,015,152, -6.15%)
llvm-mca: 29,013,728 -> 26,998,544 bytes (-2,015,184, -6.95%)
llvm-objdump: 28,806,984 -> 26,791,832 bytes (-2,015,152, -7.00%)
AMDGPUMCTargetDesc.cpp.o shrinks from 6,762,192 to 5,509,776 bytes
(-1,252,416, -18.52%). Across all 26 generated target tables, static
instruction-name storage falls from 3,373,005 to 887,359 bytes
(-2,485,646, -73.69%).
The stripped downstream all-tools multicall binary decreases from 142,015,464
to 139,804,472 bytes, saving 2,210,992 bytes (1.557%).
A 50,000-run X86 llvm-mc disassembly benchmark has unchanged mean user
CPU time (0.570809 seconds -> 0.570828 seconds, +0.0032%). One thousand
alternating empty AMDGPU llvm-mc invocations improve user CPU time by
2.0% (1.178458 seconds -> 1.154751 seconds). Warmed getName() lookups are
faster in both sequential and randomized focused benchmarks because
decoded lengths avoid strlen.
Validated all 142,869 generated instruction names across every target,
568 MC disassembler tests, the llvm-mca suite (1,380 passed and 2
expected failures), both affected TableGen tests, a concurrent block-decoder
unit test, and downstream Release builds of llc and llvm-mc.
---
llvm/include/llvm/MC/MCInstrInfo.h | 82 +++++++++++++++--
llvm/lib/MC/MCInstrInfo.cpp | 63 +++++++++++++
llvm/lib/Target/M68k/M68kInstrInfo.cpp | 3 +-
llvm/test/TableGen/RegClassByHwMode.td | 2 +-
.../TableGen/target-specialized-pseudos.td | 5 +-
llvm/unittests/MC/CMakeLists.txt | 2 +-
llvm/unittests/MC/MCInstrInfoTest.cpp | 92 +++++++++++++++++++
llvm/utils/TableGen/InstrInfoEmitter.cpp | 91 +++++++++++++-----
8 files changed, 306 insertions(+), 34 deletions(-)
create mode 100644 llvm/unittests/MC/MCInstrInfoTest.cpp
diff --git a/llvm/include/llvm/MC/MCInstrInfo.h b/llvm/include/llvm/MC/MCInstrInfo.h
index 97d5e7a52d1a8..970c24a0326b4 100644
--- a/llvm/include/llvm/MC/MCInstrInfo.h
+++ b/llvm/include/llvm/MC/MCInstrInfo.h
@@ -13,15 +13,62 @@
#ifndef LLVM_MC_MCINSTRINFO_H
#define LLVM_MC_MCINSTRINFO_H
+#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/MC/MCInstrDesc.h"
#include "llvm/Support/Compiler.h"
+#include <atomic>
#include <cassert>
+#include <cstdint>
namespace llvm {
class MCSubtargetInfo;
+/// Lazily decodes a front-coded instruction name table.
+///
+/// Each block stores its decoded byte size followed by one-byte common-prefix
+/// and suffix lengths and the suffix bytes for each name.
+///
+/// Names are decoded in small opcode blocks. Most users never request
+/// instruction names, so they avoid both the decode cost and the memory for
+/// unused blocks.
+class MCInstrNameTable {
+ ArrayRef<uint8_t> CompressedData;
+ ArrayRef<uint32_t> BlockOffsets;
+ std::atomic<const uint16_t *> *DecodedBlocks;
+ unsigned NumNames;
+
+ LLVM_ABI const uint16_t *decodeBlock(unsigned Block) const;
+
+public:
+ static constexpr unsigned BlockSize = 128;
+
+ constexpr MCInstrNameTable(ArrayRef<uint8_t> CompressedData,
+ ArrayRef<uint32_t> BlockOffsets,
+ std::atomic<const uint16_t *> *DecodedBlocks,
+ unsigned NumNames)
+ : CompressedData(CompressedData), BlockOffsets(BlockOffsets),
+ DecodedBlocks(DecodedBlocks), NumNames(NumNames) {}
+
+ StringRef getName(unsigned Opcode) const {
+ assert(Opcode < NumNames && "Invalid opcode!");
+ unsigned Block = Opcode / BlockSize;
+ const uint16_t *Indices =
+ DecodedBlocks[Block].load(std::memory_order_acquire);
+ if (!Indices)
+ Indices = decodeBlock(Block);
+
+ unsigned Remaining = NumNames - Block * BlockSize;
+ unsigned NumBlockNames = Remaining < BlockSize ? Remaining : BlockSize;
+ const uint8_t *Lengths =
+ reinterpret_cast<const uint8_t *>(Indices + NumBlockNames);
+ const char *Data = reinterpret_cast<const char *>(Lengths + NumBlockNames);
+ unsigned Index = Opcode % BlockSize;
+ return StringRef(Data + Indices[Index], Lengths[Index]);
+ }
+};
+
//---------------------------------------------------------------------------
/// Interface to description of machine instruction set.
class MCInstrInfo {
@@ -31,9 +78,11 @@ class MCInstrInfo {
std::string &);
private:
- const MCInstrDesc *LastDesc; // Raw array to allow static init'n
- const unsigned *InstrNameIndices; // Array for name indices in InstrNameData
- const char *InstrNameData; // Instruction name string pool
+ const MCInstrDesc *LastDesc; // Raw array to allow static init'n
+ // Instruction name indices, or an MCInstrNameTable when InstrNameData is
+ // null.
+ const void *InstrNameIndices;
+ const char *InstrNameData; // Instruction name string pool
// Subtarget feature that an instruction is deprecated on, if any
// -1 implies this is not deprecated by any single feature. It may still be
// deprecated due to a "complex" reason, below.
@@ -41,7 +90,7 @@ class MCInstrInfo {
// A complex method to determine if a certain instruction is deprecated or
// not, and return the reason for deprecation.
const ComplexDeprecationPredicate *ComplexDeprecationInfos;
- unsigned NumOpcodes; // Number of entries in the desc array
+ unsigned NumOpcodes; // Number of entries in the desc array
protected:
// Pointer to 2d array [NumHwModes][NumRegClassByHwModes]
@@ -66,6 +115,23 @@ class MCInstrInfo {
NumRegClassByHwModes = NumRegClassByHwMode;
}
+ /// Initialize MCInstrInfo with a compressed instruction name table.
+ /// *DO NOT USE*.
+ void InitMCInstrInfo(const MCInstrDesc *D, const MCInstrNameTable *Names,
+ const uint8_t *DF,
+ const ComplexDeprecationPredicate *CDI, unsigned NO,
+ const int16_t *RCHWTables = nullptr,
+ int16_t NumRegClassByHwMode = 0) {
+ LastDesc = D + NO - 1;
+ InstrNameIndices = Names;
+ InstrNameData = nullptr;
+ DeprecatedFeatures = DF;
+ ComplexDeprecationInfos = CDI;
+ NumOpcodes = NO;
+ RegClassByHwModeTables = RCHWTables;
+ NumRegClassByHwModes = NumRegClassByHwMode;
+ }
+
unsigned getNumOpcodes() const { return NumOpcodes; }
const int16_t *getRegClassByHwModeTable(unsigned ModeId) const {
@@ -96,7 +162,11 @@ class MCInstrInfo {
/// Returns the name for the instructions with the given opcode.
StringRef getName(unsigned Opcode) const {
assert(Opcode < NumOpcodes && "Invalid opcode!");
- return StringRef(&InstrNameData[InstrNameIndices[Opcode]]);
+ if (!InstrNameData)
+ return static_cast<const MCInstrNameTable *>(InstrNameIndices)
+ ->getName(Opcode);
+ return StringRef(&InstrNameData[static_cast<const unsigned *>(
+ InstrNameIndices)[Opcode]]);
}
/// Returns true if a certain instruction is deprecated and if so
@@ -105,6 +175,6 @@ class MCInstrInfo {
std::string &Info) const;
};
-} // End llvm namespace
+} // namespace llvm
#endif
diff --git a/llvm/lib/MC/MCInstrInfo.cpp b/llvm/lib/MC/MCInstrInfo.cpp
index ab63db0405324..08cebbb9d394c 100644
--- a/llvm/lib/MC/MCInstrInfo.cpp
+++ b/llvm/lib/MC/MCInstrInfo.cpp
@@ -9,9 +9,72 @@
#include "llvm/MC/MCInstrInfo.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCSubtargetInfo.h"
+#include <algorithm>
+#include <cstring>
+#include <limits>
+#include <new>
using namespace llvm;
+const uint16_t *MCInstrNameTable::decodeBlock(unsigned Block) const {
+ assert(Block < BlockOffsets.size());
+
+ const uint8_t *ReadPtr = CompressedData.begin() + BlockOffsets[Block];
+ [[maybe_unused]] const uint8_t *BlockEnd =
+ Block + 1 == BlockOffsets.size()
+ ? CompressedData.end()
+ : CompressedData.begin() + BlockOffsets[Block + 1];
+ unsigned NumBlockNames = std::min(BlockSize, NumNames - Block * BlockSize);
+ assert(BlockEnd - ReadPtr >= 2);
+ uint16_t DataSize = ReadPtr[0] | (uint16_t(ReadPtr[1]) << 8);
+ ReadPtr += 2;
+
+ size_t IndexBytes = (sizeof(uint16_t) + sizeof(uint8_t)) * NumBlockNames;
+ void *Storage = ::operator new(IndexBytes + DataSize);
+ auto *Indices = static_cast<uint16_t *>(Storage);
+ auto *Lengths = reinterpret_cast<uint8_t *>(Indices + NumBlockNames);
+ char *Data = reinterpret_cast<char *>(Lengths + NumBlockNames);
+ char *WritePtr = Data;
+ StringRef Previous;
+
+ for (unsigned I = 0; I != NumBlockNames; ++I) {
+ assert(BlockEnd - ReadPtr >= 2);
+ uint8_t PrefixLength = *ReadPtr++;
+ uint8_t SuffixLength = *ReadPtr++;
+ assert(PrefixLength <= Previous.size());
+ assert(SuffixLength <= BlockEnd - ReadPtr);
+
+ char *NameStart = WritePtr;
+ if (PrefixLength) {
+ std::memcpy(WritePtr, Previous.data(), PrefixLength);
+ WritePtr += PrefixLength;
+ }
+ if (SuffixLength) {
+ std::memcpy(WritePtr, ReadPtr, SuffixLength);
+ WritePtr += SuffixLength;
+ ReadPtr += SuffixLength;
+ }
+ *WritePtr++ = '\0';
+
+ assert(NameStart - Data <= std::numeric_limits<uint16_t>::max());
+ Indices[I] = static_cast<uint16_t>(NameStart - Data);
+ Lengths[I] = PrefixLength + SuffixLength;
+ Previous = StringRef(NameStart, Lengths[I]);
+ }
+
+ assert(ReadPtr == BlockEnd);
+ assert(WritePtr == Data + DataSize);
+
+ const uint16_t *Expected = nullptr;
+ if (!DecodedBlocks[Block].compare_exchange_strong(
+ Expected, Indices, std::memory_order_release,
+ std::memory_order_acquire)) {
+ ::operator delete(Storage);
+ return Expected;
+ }
+ return Indices;
+}
+
bool MCInstrInfo::getDeprecatedInfo(MCInst &MI, const MCSubtargetInfo &STI,
std::string &Info) const {
unsigned Opcode = MI.getOpcode();
diff --git a/llvm/lib/Target/M68k/M68kInstrInfo.cpp b/llvm/lib/Target/M68k/M68kInstrInfo.cpp
index 0c4708eb98daa..a2ca41c5a6880 100644
--- a/llvm/lib/Target/M68k/M68kInstrInfo.cpp
+++ b/llvm/lib/Target/M68k/M68kInstrInfo.cpp
@@ -666,8 +666,7 @@ bool M68kInstrInfo::isPCRelRegisterOperandLegal(
// Refer to TargetInstrInfo.h for more information about this function.
const MachineInstr *MI = MO.getParent();
- const unsigned NameIndices = M68kInstrNameIndices[MI->getOpcode()];
- StringRef InstrName(&M68kInstrNameData[NameIndices]);
+ StringRef InstrName = getName(MI->getOpcode());
const unsigned OperandNo = MO.getOperandNo();
// If this machine operand is the 2nd operand, then check
diff --git a/llvm/test/TableGen/RegClassByHwMode.td b/llvm/test/TableGen/RegClassByHwMode.td
index 46aef05ebc9c2..75c6dd01af58e 100644
--- a/llvm/test/TableGen/RegClassByHwMode.td
+++ b/llvm/test/TableGen/RegClassByHwMode.td
@@ -64,7 +64,7 @@ include "Common/RegClassByHwModeCommon.td"
// INSTRINFO-NEXT: };
// INSTRINFO: static inline void InitMyTargetMCInstrInfo(
-// INSTRINFO-NEXT: II->InitMCInstrInfo(MyTargetDescs.Insts, MyTargetInstrNameIndices, MyTargetInstrNameData, nullptr, nullptr, {{[0-9]+}}, &MyTargetRegClassByHwModeTables[0][0], 3);
+// INSTRINFO-NEXT: II->InitMCInstrInfo(MyTargetDescs.Insts, &getMyTargetInstrNameTable(), nullptr, nullptr, {{[0-9]+}}, &MyTargetRegClassByHwModeTables[0][0], 3);
// INSTRINFO: #ifdef GET_INSTRINFO_HEADER
// INSTRINFO-NEXT: #undef GET_INSTRINFO_HEADER
diff --git a/llvm/test/TableGen/target-specialized-pseudos.td b/llvm/test/TableGen/target-specialized-pseudos.td
index bf55926f21983..6395d28aa1b33 100644
--- a/llvm/test/TableGen/target-specialized-pseudos.td
+++ b/llvm/test/TableGen/target-specialized-pseudos.td
@@ -38,8 +38,9 @@
// ALLCASES: /* [[PATCHABLE_TYPED_EVENT_CALL_OP_ENTRY]] */ { -1, 0, MCOI::OPERAND_UNKNOWN, 0 }, { MyTarget::XRegsRegClassID, 0, MCOI::OPERAND_REGISTER, 0 }, { -1, 0, MCOI::OPERAND_UNKNOWN, 0 },
-// CHECK: const char MyTargetInstrNameData[] = {
-// CHECK: /* {{[0-9]+}} */ "LOAD_STACK_GUARD\000"
+// CHECK: static constexpr uint8_t MyTargetInstrNameData[] = {
+// CHECK: static constexpr uint32_t MyTargetInstrNameBlockOffsets[] = {
+// CHECK: const MCInstrNameTable &getMyTargetInstrNameTable() {
include "llvm/Target/Target.td"
diff --git a/llvm/unittests/MC/CMakeLists.txt b/llvm/unittests/MC/CMakeLists.txt
index 4881888f03742..8c096e0250a5d 100644
--- a/llvm/unittests/MC/CMakeLists.txt
+++ b/llvm/unittests/MC/CMakeLists.txt
@@ -19,9 +19,9 @@ add_llvm_unittest(MCTests
DwarfDebugFrameCIE.cpp
DwarfLineTables.cpp
DwarfLineTableHeaders.cpp
+ MCInstrInfoTest.cpp
MCInstPrinter.cpp
StringTableBuilderTest.cpp
TargetRegistry.cpp
MCDisassemblerTest.cpp
)
-
diff --git a/llvm/unittests/MC/MCInstrInfoTest.cpp b/llvm/unittests/MC/MCInstrInfoTest.cpp
new file mode 100644
index 0000000000000..abb17a5c93d90
--- /dev/null
+++ b/llvm/unittests/MC/MCInstrInfoTest.cpp
@@ -0,0 +1,92 @@
+//===- MCInstrInfoTest.cpp - MCInstrInfo 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/MCInstrInfo.h"
+#include "llvm/Config/llvm-config.h"
+#include "gtest/gtest.h"
+#include <algorithm>
+#include <array>
+#include <atomic>
+#include <cstdint>
+#include <new>
+#include <string>
+#include <thread>
+#include <vector>
+
+using namespace llvm;
+
+namespace {
+
+TEST(MCInstrInfoTest, FrontCodedInstructionNames) {
+ constexpr unsigned NumNames = MCInstrNameTable::BlockSize + 2;
+ constexpr unsigned NumBlocks = 2;
+
+ std::vector<std::string> Names;
+ Names.reserve(NumNames);
+ for (unsigned I = 0; I != NumNames; ++I)
+ Names.push_back("COMMON_INSTRUCTION_PREFIX_" + std::to_string(I));
+
+ std::vector<uint8_t> CompressedData;
+ std::vector<uint32_t> BlockOffsets;
+
+ for (unsigned Start = 0; Start < NumNames;
+ Start += MCInstrNameTable::BlockSize) {
+ unsigned End = std::min(Start + MCInstrNameTable::BlockSize, NumNames);
+ BlockOffsets.push_back(CompressedData.size());
+
+ unsigned DataSize = 0;
+ for (unsigned I = Start; I != End; ++I)
+ DataSize += Names[I].size() + 1;
+ CompressedData.push_back(DataSize);
+ CompressedData.push_back(DataSize >> 8);
+
+ StringRef Previous;
+ for (unsigned I = Start; I != End; ++I) {
+ StringRef Name = Names[I];
+ unsigned PrefixLength = 0;
+ while (PrefixLength != Previous.size() && PrefixLength != Name.size() &&
+ Previous[PrefixLength] == Name[PrefixLength])
+ ++PrefixLength;
+ CompressedData.push_back(PrefixLength);
+ CompressedData.push_back(Name.size() - PrefixLength);
+ CompressedData.insert(CompressedData.end(),
+ Name.bytes_begin() + PrefixLength,
+ Name.bytes_end());
+ Previous = Name;
+ }
+ }
+
+ std::array<std::atomic<const uint16_t *>, NumBlocks> DecodedBlocks;
+ for (auto &Block : DecodedBlocks)
+ Block.store(nullptr, std::memory_order_relaxed);
+ MCInstrNameTable Table(CompressedData, BlockOffsets, DecodedBlocks.data(),
+ NumNames);
+
+#if LLVM_ENABLE_THREADS
+ std::atomic<bool> Correct{true};
+ std::array<std::thread, 8> Threads;
+ for (auto &Thread : Threads)
+ Thread = std::thread([&] {
+ for (unsigned I = 0; I != NumNames; ++I)
+ if (Table.getName(I) != Names[I])
+ Correct.store(false, std::memory_order_relaxed);
+ });
+ for (auto &Thread : Threads)
+ Thread.join();
+ EXPECT_TRUE(Correct.load(std::memory_order_relaxed));
+#endif
+
+ for (unsigned I = 0; I != NumNames; ++I)
+ EXPECT_EQ(Table.getName(I), Names[I]);
+
+ for (auto &Block : DecodedBlocks)
+ ::operator delete(const_cast<uint16_t *>(Block.load()));
+}
+
+} // namespace
diff --git a/llvm/utils/TableGen/InstrInfoEmitter.cpp b/llvm/utils/TableGen/InstrInfoEmitter.cpp
index 08526cc2a72bf..a8a30ff20b659 100644
--- a/llvm/utils/TableGen/InstrInfoEmitter.cpp
+++ b/llvm/utils/TableGen/InstrInfoEmitter.cpp
@@ -11,7 +11,6 @@
//
//===----------------------------------------------------------------------===//
-#include "Basic/SequenceToOffsetTable.h"
#include "Common/CodeGenDAGPatterns.h"
#include "Common/CodeGenInstruction.h"
#include "Common/CodeGenRegisters.h"
@@ -37,6 +36,7 @@
#include <cassert>
#include <cstdint>
#include <iterator>
+#include <limits>
#include <map>
#include <string>
#include <utility>
@@ -835,8 +835,8 @@ void InstrInfoEmitter::emitFeatureVerifier(raw_ostream &OS,
<< " RequiredFeatures;\n"
<< " if (MissingFeatures.any()) {\n"
<< " std::ostringstream Msg;\n"
- << " Msg << \"Attempting to emit \" << &" << Target.getName()
- << "InstrNameData[" << Target.getName() << "InstrNameIndices[Opcode]]\n"
+ << " Msg << \"Attempting to emit \" << get" << Target.getName()
+ << "InstrNameTable().getName(Opcode).str()\n"
<< " << \" instruction but the \";\n"
<< " for (unsigned i = 0, e = MissingFeatures.size(); i != e; ++i)\n"
<< " if (MissingFeatures.test(i))\n"
@@ -1006,12 +1006,8 @@ void InstrInfoEmitter::run(raw_ostream &OS) {
OS << "extern const " << TargetName << "InstrTable " << TargetName
<< "Descs = {\n {\n";
- SequenceToOffsetTable<StringRef> InstrNames;
unsigned Num = NumberedInstructions.size();
for (const CodeGenInstruction *Inst : reverse(NumberedInstructions)) {
- // Keep a list of the instruction names.
- InstrNames.add(Inst->getName());
-
auto OverrideEntry = TargetSpecializedPseudoInsts.find(Inst);
if (OverrideEntry != TargetSpecializedPseudoInsts.end())
Inst = OverrideEntry->second;
@@ -1046,19 +1042,71 @@ void InstrInfoEmitter::run(raw_ostream &OS) {
// Emit the array of instruction names.
Timer.startTimer("Emit instruction names");
- InstrNames.layout();
- InstrNames.emitStringLiteralDef(OS, Twine("extern const char ") +
- TargetName + "InstrNameData[]");
- OS << "extern const unsigned " << TargetName << "InstrNameIndices[] = {";
- Num = 0;
- for (const CodeGenInstruction *Inst : NumberedInstructions) {
- // Newline every eight entries.
- if (Num % 8 == 0)
+ constexpr unsigned InstrNameBlockSize = 128;
+ std::string CompressedInstrNames;
+ std::vector<uint32_t> InstrNameBlockOffsets;
+
+ for (unsigned Start = 0; Start < NumberedInstructions.size();
+ Start += InstrNameBlockSize) {
+ unsigned End = std::min<unsigned>(Start + InstrNameBlockSize,
+ NumberedInstructions.size());
+ InstrNameBlockOffsets.push_back(CompressedInstrNames.size());
+
+ unsigned BlockDataSize = 0;
+ for (unsigned I = Start; I != End; ++I) {
+ size_t NameSize = NumberedInstructions[I]->getName().size();
+ if (NameSize > std::numeric_limits<uint8_t>::max())
+ PrintFatalError(NumberedInstructions[I]->TheDef,
+ "instruction name is too long");
+ BlockDataSize += NameSize + 1;
+ }
+ if (BlockDataSize > std::numeric_limits<uint16_t>::max())
+ PrintFatalError("instruction name block is too large");
+ CompressedInstrNames.push_back(BlockDataSize);
+ CompressedInstrNames.push_back(BlockDataSize >> 8);
+
+ StringRef PreviousName;
+ for (unsigned I = Start; I != End; ++I) {
+ StringRef Name = NumberedInstructions[I]->getName();
+ unsigned PrefixLength = 0;
+ while (PrefixLength != PreviousName.size() &&
+ PrefixLength != Name.size() &&
+ PreviousName[PrefixLength] == Name[PrefixLength])
+ ++PrefixLength;
+
+ CompressedInstrNames.push_back(PrefixLength);
+ CompressedInstrNames.push_back(Name.size() - PrefixLength);
+ CompressedInstrNames.append(Name.drop_front(PrefixLength));
+ PreviousName = Name;
+ }
+ }
+
+ OS << "static constexpr uint8_t " << TargetName << "InstrNameData[] = {";
+ for (auto [I, Byte] : enumerate(CompressedInstrNames)) {
+ if (I % 16 == 0)
OS << "\n ";
- OS << InstrNames.get(Inst->getName()) << "U, ";
- ++Num;
+ OS << unsigned(uint8_t(Byte)) << ", ";
}
OS << "\n};\n\n";
+ OS << "static constexpr uint32_t " << TargetName
+ << "InstrNameBlockOffsets[] = {";
+ for (auto [I, Offset] : enumerate(InstrNameBlockOffsets)) {
+ if (I % 8 == 0)
+ OS << "\n ";
+ OS << Offset << "U, ";
+ }
+ OS << "\n};\n\n";
+ OS << "static std::atomic<const uint16_t *> " << TargetName
+ << "InstrNameBlocks[" << InstrNameBlockOffsets.size() << "] = {};\n\n";
+ OS << "static constexpr MCInstrNameTable " << TargetName << "InstrNames(\n"
+ << " " << TargetName << "InstrNameData, " << TargetName
+ << "InstrNameBlockOffsets, " << TargetName << "InstrNameBlocks, "
+ << NumberedInstructions.size() << ");\n\n";
+ OS << "const MCInstrNameTable &get" << TargetName << "InstrNameTable() {\n"
+ << " static_assert(MCInstrNameTable::BlockSize == "
+ << InstrNameBlockSize << ");\n"
+ << " return " << TargetName << "InstrNames;\n"
+ << "}\n\n";
if (HasDeprecationFeatures) {
OS << "extern const uint8_t " << TargetName
@@ -1135,7 +1183,7 @@ void InstrInfoEmitter::run(raw_ostream &OS) {
OS << "static inline void Init" << TargetName
<< "MCInstrInfo(MCInstrInfo *II) {\n";
OS << " II->InitMCInstrInfo(" << TargetName << "Descs.Insts, "
- << TargetName << "InstrNameIndices, " << TargetName << "InstrNameData, ";
+ << "&get" << TargetName << "InstrNameTable(), ";
if (HasDeprecationFeatures)
OS << TargetName << "InstrDeprecationFeatures, ";
else
@@ -1209,8 +1257,7 @@ void InstrInfoEmitter::run(raw_ostream &OS) {
NamespaceEmitter LlvmNS(OS, "llvm");
OS << "extern const " << TargetName << "InstrTable " << TargetName
<< "Descs;\n";
- OS << "extern const unsigned " << TargetName << "InstrNameIndices[];\n";
- OS << "extern const char " << TargetName << "InstrNameData[];\n";
+ OS << "const MCInstrNameTable &get" << TargetName << "InstrNameTable();\n";
if (HasDeprecationFeatures)
OS << "extern const uint8_t " << TargetName
@@ -1232,8 +1279,8 @@ void InstrInfoEmitter::run(raw_ostream &OS) {
"RegInfo)]";
OS << ") {\n"
- << " InitMCInstrInfo(" << TargetName << "Descs.Insts, " << TargetName
- << "InstrNameIndices, " << TargetName << "InstrNameData, ";
+ << " InitMCInstrInfo(" << TargetName << "Descs.Insts, "
+ << "&get" << TargetName << "InstrNameTable(), ";
if (HasDeprecationFeatures)
OS << TargetName << "InstrDeprecationFeatures, ";
else
More information about the llvm-commits
mailing list