[llvm] [TableGen] Split large printInstruction() for NVPTX to avoid compiler limits (PR #218530)
Malavika Samak via llvm-commits
llvm-commits at lists.llvm.org
Mon Sep 28 13:50:58 PDT 2026
https://github.com/malavikasamak updated https://github.com/llvm/llvm-project/pull/218530
>From 4d7981dc82bd831374056781df259747e1bb690c Mon Sep 17 00:00:00 2001
From: Malavika Samak <mmalavika at nvidia.com>
Date: Wed, 2 Sep 2026 13:54:11 -0700
Subject: [PATCH 1/2] [TableGen] Emit function-pointer table for AsmWriter
overflow instructions
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Problem
-------
For targets with many instructions (NVPTX in particular), the overflow switch
emitted inside printInstruction() for operand sequences that cannot be encoded
in the table-driven bit-packing path can grow large enough to trigger:
- MSVC C1001/C1053 ICE on AArch64 (function body too large)
- R_RISCV_JAL relocation truncated to fit (JAL ±1 MB range exceeded)
Solution
--------
Move each unique overflow operand sequence into a small static printPattern_N
function and dispatch through a const function-pointer table indexed by opcode,
replacing the switch entirely:
- Add a `Receiver` parameter to `AsmWriterOperand::getCode()` so that code
emitted inside static free functions calls `P->printOperand(...)` rather
than the member function directly.
- `EmitOpcodePatternTable()`: collects overflow instructions, deduplicates
identical operand sequences into shared `printPattern_N` statics, and emits
a `<Target>InstPrinters[]` const function-pointer table covering every
opcode. Non-overflow opcodes point to a no-op `printPattern_None`.
- `EmitOpcodePatternDispatch()`: replaces the overflow switch with a single
unconditional indirect call through the table, keeping printInstruction()
small regardless of instruction-set size.
The mechanism triggers for any target with overflow instructions, so it is not
NVPTX-specific. A follow-up can generalize the threshold-based triggering to
any target that exceeds a function-size limit.
---
llvm/include/llvm/Target/Target.td | 7 +
llvm/lib/Target/NVPTX/NVPTX.td | 1 +
llvm/utils/TableGen/AsmWriterEmitter.cpp | 151 ++++++++++++++++---
llvm/utils/TableGen/Common/AsmWriterInst.cpp | 5 +-
llvm/utils/TableGen/Common/AsmWriterInst.h | 5 +-
5 files changed, 149 insertions(+), 20 deletions(-)
diff --git a/llvm/include/llvm/Target/Target.td b/llvm/include/llvm/Target/Target.td
index 53139e9a2bfea..0c969a4cb351e 100644
--- a/llvm/include/llvm/Target/Target.td
+++ b/llvm/include/llvm/Target/Target.td
@@ -2060,6 +2060,13 @@ class AsmWriter {
// will specify which alternative to use. For example "{x|y|z}" with Variant
// == 1, will expand to "y".
int Variant = 0;
+
+ // UseFunctionPointerTable - If set to 1, emit a function-pointer table
+ // instead of a switch statement for instructions that overflow the 64-bit
+ // table-driven printer. This keeps printInstruction() small, avoiding
+ // MSVC C1001/C1053 ICEs and R_RISCV_JAL relocation-range overflows on
+ // targets with large instruction sets.
+ bit UseFunctionPointerTable = 0;
}
def DefaultAsmWriter : AsmWriter;
diff --git a/llvm/lib/Target/NVPTX/NVPTX.td b/llvm/lib/Target/NVPTX/NVPTX.td
index f9be7928ad102..67982d0589637 100644
--- a/llvm/lib/Target/NVPTX/NVPTX.td
+++ b/llvm/lib/Target/NVPTX/NVPTX.td
@@ -140,6 +140,7 @@ def NVPTXInstrInfo : InstrInfo {
def NVPTXAsmWriter : AsmWriter {
int PassSubtarget = 1;
+ bit UseFunctionPointerTable = 1;
}
def NVPTX : Target {
diff --git a/llvm/utils/TableGen/AsmWriterEmitter.cpp b/llvm/utils/TableGen/AsmWriterEmitter.cpp
index f3e04bb0f0849..62834fa321f36 100644
--- a/llvm/utils/TableGen/AsmWriterEmitter.cpp
+++ b/llvm/utils/TableGen/AsmWriterEmitter.cpp
@@ -91,6 +91,16 @@ class AsmWriterEmitter {
std::vector<std::vector<unsigned>> &InstIdxs,
std::vector<unsigned> &InstOpsUsed,
bool PassSubtarget) const;
+
+ // Emit a flat function-pointer table covering all opcodes. Each unique
+ // operand-printing sequence in the overflow set becomes one printPattern_N
+ // static helper; instructions sharing a sequence share the same helper.
+ // The table is indexed directly by getOpcode(), so dispatch is a single
+ // unconditional indirect call with no switch or branch.
+ void EmitOpcodePatternTable(raw_ostream &O, StringRef TargetName,
+ StringRef ClassName, bool PassSubtarget);
+ void EmitOpcodePatternDispatch(raw_ostream &O, StringRef TargetName,
+ StringRef ClassName, bool PassSubtarget);
};
} // end anonymous namespace
@@ -510,6 +520,21 @@ void AsmWriterEmitter::EmitPrintInstruction(
const Record *AsmWriter = Target.getAsmWriter();
StringRef ClassName = AsmWriter->getValueAsString("AsmWriterClassName");
bool PassSubtarget = AsmWriter->getValueAsInt("PassSubtarget");
+ bool UseFnPtrTable = AsmWriter->getValueAsBit("UseFunctionPointerTable");
+
+ // Delete instructions with no operand info left so that the emptiness check
+ // below only considers overflow instructions, and EmitOpcodePatternTable does
+ // not need to filter them again.
+ llvm::erase_if(Instructions,
+ [](AsmWriterInst &Inst) { return Inst.Operands.empty(); });
+
+ // When opt-in: pre-emit static printPattern_N helpers and function-pointer
+ // table before printInstruction(). A single indirect call in
+ // printInstruction() then replaces the overflow switch, keeping the function
+ // body small and avoiding MSVC C1001/C1053 ICEs and R_RISCV_JAL
+ // relocation-range overflows that arise on large instruction sets.
+ if (UseFnPtrTable && !Instructions.empty())
+ EmitOpcodePatternTable(O, Target.getName(), ClassName, PassSubtarget);
// This function has some huge switch statements that causing excessive
// compile time in LLVM profile instrumenation build. This print function
@@ -575,33 +600,125 @@ void AsmWriterEmitter::EmitPrintInstruction(
BitsLeft -= NumBits;
}
- // Okay, delete instructions with no operand info left.
- llvm::erase_if(Instructions,
- [](AsmWriterInst &Inst) { return Inst.Operands.empty(); });
-
- // Because this is a vector, we want to emit from the end. Reverse all of the
- // elements in the vector.
- std::reverse(Instructions.begin(), Instructions.end());
-
- std::vector<InstructionGroup> Groups = groupInstructions(Instructions);
-
// Now that we've emitted all of the operand info that fit into 64 bits, emit
// information for those instructions that are left. This is a less dense
// encoding, but we expect the main 64-bit table to handle the majority of
// instructions.
- if (!Groups.empty()) {
- // Find the opcode # of inline asm.
- O << " switch (MI->getOpcode()) {\n";
- O << " default: llvm_unreachable(\"Unexpected opcode.\");\n";
- for (const InstructionGroup &G : Groups)
- emitInstructions(G, O, PassSubtarget);
+ if (!Instructions.empty()) {
+ if (UseFnPtrTable) {
+ // Single unconditional indirect call; the function-pointer table was
+ // emitted above, before printInstruction().
+ EmitOpcodePatternDispatch(O, Target.getName(), ClassName, PassSubtarget);
+ } else {
+ // Because this is a vector, we want to emit from the end. Reverse all
+ // of the elements in the vector.
+ std::reverse(Instructions.begin(), Instructions.end());
- O << " }\n";
+ std::vector<InstructionGroup> Groups = groupInstructions(Instructions);
+
+ O << " switch (MI->getOpcode()) {\n";
+ O << " default: llvm_unreachable(\"Unexpected opcode.\");\n";
+ for (const InstructionGroup &G : Groups)
+ emitInstructions(G, O, PassSubtarget);
+
+ O << " }\n";
+ }
}
O << "}\n";
}
+void AsmWriterEmitter::EmitOpcodePatternTable(raw_ostream &O,
+ StringRef TargetName,
+ StringRef ClassName,
+ bool PassSubtarget) {
+ assert(!Instructions.empty() && "caller should have checked HasOverflow");
+
+ // Serialize each instruction's complete operand sequence into a string key.
+ // Instructions with identical keys share one printPattern_N function.
+ // getCode() with Receiver="P->" turns member calls like printOperand(...)
+ // into P->printOperand(...), which is correct for a static free function.
+ using PatternBody = std::vector<std::string>;
+ std::map<PatternBody, unsigned> PatternMap;
+ SmallVector<PatternBody, 64> Patterns;
+
+ // Table: index by CGIIndex, value = pattern index (~0U for non-overflow).
+ std::vector<unsigned> OpcodeToPattern(NumberedInstructions.size(), ~0U);
+
+ for (const AsmWriterInst &AWI : Instructions) {
+ PatternBody Body;
+ for (const AsmWriterOperand &Op : AWI.Operands)
+ Body.push_back(Op.getCode(PassSubtarget, "P->"));
+ auto [It, Inserted] = PatternMap.emplace(Body, Patterns.size());
+ if (Inserted)
+ Patterns.push_back(Body);
+ OpcodeToPattern[AWI.CGIIndex] = It->second;
+ }
+
+ std::string FullClassName = (TargetName + ClassName).str();
+ // Include ClassName in the table name so multiple AsmWriter variants in the
+ // same TU (e.g. AArch64GenAsmWriter.inc + AArch64GenAsmWriter1.inc) don't
+ // produce conflicting variable declarations.
+ std::string PtrTableName = FullClassName + "Printers";
+ std::string ParamList = " " + FullClassName +
+ " *, const MCInst *,\n"
+ " uint64_t," +
+ (PassSubtarget ? " const MCSubtargetInfo &," : "") +
+ " raw_ostream &";
+
+ // printPattern_None: no-op for instructions fully handled by table-driven
+ // path.
+ O << "static void printPattern_None(\n" << ParamList << ") {}\n\n";
+
+ // One printPattern_N per unique operand sequence.
+ for (unsigned PIdx = 0, E = Patterns.size(); PIdx < E; ++PIdx) {
+ O << "static void printPattern_" << PIdx << "(\n"
+ << " " << FullClassName << " *P, const MCInst *MI,\n"
+ << " uint64_t Address,";
+ if (PassSubtarget)
+ O << " const MCSubtargetInfo &STI,";
+ O << " raw_ostream &O) {\n";
+ for (const std::string &Line : Patterns[PIdx])
+ O << " " << Line << "\n";
+ O << "}\n\n";
+ }
+
+ // Function-pointer table indexed by opcode, covering the full opcode range.
+ // Non-overflow opcodes → printPattern_None.
+ // Overflow opcodes → their deduplicated printPattern_N.
+ O << "static void (*const " << PtrTableName << "[])(\n"
+ << ParamList << ") = {\n";
+ for (unsigned i = 0, E = NumberedInstructions.size(); i < E; ++i) {
+ unsigned PIdx = OpcodeToPattern[i];
+ if (PIdx == ~0U)
+ O << " &printPattern_None";
+ else
+ O << " &printPattern_" << PIdx;
+ if (i + 1 < E)
+ O << ",";
+ O << "\t// " << NumberedInstructions[i]->getName() << "\n";
+ }
+ O << "};\n\n";
+
+ LLVM_DEBUG(dbgs() << "[AsmWriter] " << TargetName << ": "
+ << OpcodeInsts.size() << " overflow instructions -> "
+ << Patterns.size() << " unique patterns\n");
+}
+
+void AsmWriterEmitter::EmitOpcodePatternDispatch(raw_ostream &O,
+ StringRef TargetName,
+ StringRef ClassName,
+ bool PassSubtarget) {
+ // Single unconditional indirect call; no switch, no branch.
+ // The compiler cannot inline through a const function pointer, so
+ // printInstruction() itself stays small regardless of instruction-set size.
+ std::string PtrTableName = (TargetName + ClassName + "Printers").str();
+ O << " " << PtrTableName << "[MI->getOpcode()](this, MI, Address, ";
+ if (PassSubtarget)
+ O << "STI, ";
+ O << "O);\n";
+}
+
static void
emitRegisterNameString(raw_ostream &O, StringRef AltName,
const std::deque<CodeGenRegister> &Registers) {
diff --git a/llvm/utils/TableGen/Common/AsmWriterInst.cpp b/llvm/utils/TableGen/Common/AsmWriterInst.cpp
index 97430a1eec8a1..4afdb67bd7844 100644
--- a/llvm/utils/TableGen/Common/AsmWriterInst.cpp
+++ b/llvm/utils/TableGen/Common/AsmWriterInst.cpp
@@ -20,7 +20,8 @@ using namespace llvm;
static bool isIdentChar(char C) { return isAlnum(C) || C == '_'; }
-std::string AsmWriterOperand::getCode(bool PassSubtarget) const {
+std::string AsmWriterOperand::getCode(bool PassSubtarget,
+ StringRef Receiver) const {
if (OperandType == isLiteralTextOperand) {
if (Str.size() == 1)
return "O << '" + Str + "';";
@@ -30,7 +31,7 @@ std::string AsmWriterOperand::getCode(bool PassSubtarget) const {
if (OperandType == isLiteralStatementOperand)
return Str;
- std::string Result = Str + "(MI";
+ std::string Result = Receiver.str() + Str + "(MI";
if (PCRel)
Result += ", Address";
if (MIOpNo != ~0U)
diff --git a/llvm/utils/TableGen/Common/AsmWriterInst.h b/llvm/utils/TableGen/Common/AsmWriterInst.h
index 26745a8459570..fed7825fbfe65 100644
--- a/llvm/utils/TableGen/Common/AsmWriterInst.h
+++ b/llvm/utils/TableGen/Common/AsmWriterInst.h
@@ -16,6 +16,7 @@
#ifndef LLVM_UTILS_TABLEGEN_COMMON_ASMWRITERINST_H
#define LLVM_UTILS_TABLEGEN_COMMON_ASMWRITERINST_H
+#include "llvm/ADT/StringRef.h"
#include <string>
#include <vector>
@@ -74,7 +75,9 @@ struct AsmWriterOperand {
}
/// getCode - Return the code that prints this operand.
- std::string getCode(bool PassSubtarget) const;
+ /// Receiver, if non-empty, is prepended to member-function calls (e.g.
+ /// "P->" turns "printOperand(...)" into "P->printOperand(...)").
+ std::string getCode(bool PassSubtarget, StringRef Receiver = "") const;
};
class AsmWriterInst {
>From d8d4f7c086b5e22b4b42bb7c35af4cb97e6b5fa9 Mon Sep 17 00:00:00 2001
From: Malavika Samak <mmalavika at nvidia.com>
Date: Mon, 28 Sep 2026 12:05:09 -0700
Subject: [PATCH 2/2] [TableGen] Replace pattern-function dispatch with
bytecode interpreter
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Replace the EmitOpcodePatternTable/EmitOpcodePatternDispatch
(printPattern_N static helpers + direct-call switch) approach with a
two-table bytecode interpreter:
OpcodeToOffset[] (uint32_t) — maps opcode → start offset in program
OverflowProgram[] (uint16_t) — concatenated bytecode sequences
Each sequence is a list of statement indices terminated by index 1
(return). The interpreter loop in printInstruction() executes the
sequence for the current opcode using a switch on statement index.
Advantages over the direct-call switch approach:
- No static helper functions emitted before printInstruction()
- Statements execute directly inside printInstruction(), so local
variables (StringTable etc.) are in scope without IIFE hacks
- uint16_t statement indices support >254 unique statements
- Plain integer tables introduce no relocations
Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>
---
llvm/utils/TableGen/AsmWriterEmitter.cpp | 184 +++++++++++------------
1 file changed, 90 insertions(+), 94 deletions(-)
diff --git a/llvm/utils/TableGen/AsmWriterEmitter.cpp b/llvm/utils/TableGen/AsmWriterEmitter.cpp
index 62834fa321f36..6905ee69b9a7b 100644
--- a/llvm/utils/TableGen/AsmWriterEmitter.cpp
+++ b/llvm/utils/TableGen/AsmWriterEmitter.cpp
@@ -92,15 +92,11 @@ class AsmWriterEmitter {
std::vector<unsigned> &InstOpsUsed,
bool PassSubtarget) const;
- // Emit a flat function-pointer table covering all opcodes. Each unique
- // operand-printing sequence in the overflow set becomes one printPattern_N
- // static helper; instructions sharing a sequence share the same helper.
- // The table is indexed directly by getOpcode(), so dispatch is a single
- // unconditional indirect call with no switch or branch.
- void EmitOpcodePatternTable(raw_ostream &O, StringRef TargetName,
- StringRef ClassName, bool PassSubtarget);
- void EmitOpcodePatternDispatch(raw_ostream &O, StringRef TargetName,
- StringRef ClassName, bool PassSubtarget);
+ // Bytecode interpreter state for overflow instructions.
+ std::vector<std::string> BytecodeStmts;
+ void EmitOverflowBytecodeTables(raw_ostream &O, StringRef ClassName,
+ bool PassSubtarget);
+ void EmitOverflowBytecodeInterpreter(raw_ostream &O, bool PassSubtarget);
};
} // end anonymous namespace
@@ -523,18 +519,15 @@ void AsmWriterEmitter::EmitPrintInstruction(
bool UseFnPtrTable = AsmWriter->getValueAsBit("UseFunctionPointerTable");
// Delete instructions with no operand info left so that the emptiness check
- // below only considers overflow instructions, and EmitOpcodePatternTable does
- // not need to filter them again.
+ // below only considers overflow instructions.
llvm::erase_if(Instructions,
[](AsmWriterInst &Inst) { return Inst.Operands.empty(); });
- // When opt-in: pre-emit static printPattern_N helpers and function-pointer
- // table before printInstruction(). A single indirect call in
- // printInstruction() then replaces the overflow switch, keeping the function
- // body small and avoiding MSVC C1001/C1053 ICEs and R_RISCV_JAL
- // relocation-range overflows that arise on large instruction sets.
+ // When opt-in: emit two plain-integer tables before printInstruction() and
+ // replace the overflow switch with a small bytecode interpreter loop, keeping
+ // the function body small and avoiding MSVC C1001/C1053 ICEs.
if (UseFnPtrTable && !Instructions.empty())
- EmitOpcodePatternTable(O, Target.getName(), ClassName, PassSubtarget);
+ EmitOverflowBytecodeTables(O, ClassName, PassSubtarget);
// This function has some huge switch statements that causing excessive
// compile time in LLVM profile instrumenation build. This print function
@@ -605,10 +598,8 @@ void AsmWriterEmitter::EmitPrintInstruction(
// encoding, but we expect the main 64-bit table to handle the majority of
// instructions.
if (!Instructions.empty()) {
- if (UseFnPtrTable) {
- // Single unconditional indirect call; the function-pointer table was
- // emitted above, before printInstruction().
- EmitOpcodePatternDispatch(O, Target.getName(), ClassName, PassSubtarget);
+ if (UseFnPtrTable && BytecodeStmts.size() > 2) {
+ EmitOverflowBytecodeInterpreter(O, PassSubtarget);
} else {
// Because this is a vector, we want to emit from the end. Reverse all
// of the elements in the vector.
@@ -628,95 +619,100 @@ void AsmWriterEmitter::EmitPrintInstruction(
O << "}\n";
}
-void AsmWriterEmitter::EmitOpcodePatternTable(raw_ostream &O,
- StringRef TargetName,
- StringRef ClassName,
- bool PassSubtarget) {
- assert(!Instructions.empty() && "caller should have checked HasOverflow");
+void AsmWriterEmitter::EmitOverflowBytecodeTables(raw_ostream &O,
+ StringRef ClassName,
+ bool PassSubtarget) {
+ if (Instructions.empty())
+ return;
+
+ StringRef TargetName = Target.getName();
- // Serialize each instruction's complete operand sequence into a string key.
- // Instructions with identical keys share one printPattern_N function.
- // getCode() with Receiver="P->" turns member calls like printOperand(...)
- // into P->printOperand(...), which is correct for a static free function.
- using PatternBody = std::vector<std::string>;
- std::map<PatternBody, unsigned> PatternMap;
- SmallVector<PatternBody, 64> Patterns;
+ // Assign uint16_t indices to unique atomic operand-print statements.
+ // Index 0 = unexpected-opcode sentinel, 1 = return terminator, 2+ = real.
+ BytecodeStmts.clear();
+ BytecodeStmts.push_back("llvm_unreachable(\"Unexpected opcode.\");");
+ BytecodeStmts.push_back("return;");
+ std::map<std::string, uint16_t> StmtMap;
- // Table: index by CGIIndex, value = pattern index (~0U for non-overflow).
- std::vector<unsigned> OpcodeToPattern(NumberedInstructions.size(), ~0U);
+ auto GetStmtIdx = [&](const std::string &Code) -> uint16_t {
+ auto [It, Inserted] = StmtMap.emplace(Code, (uint16_t)BytecodeStmts.size());
+ if (Inserted)
+ BytecodeStmts.push_back(Code);
+ return It->second;
+ };
+
+ // Build bytecode sequences and concatenate into OverflowProgram.
+ // Offset 0 holds the unexpected-opcode sentinel; non-overflow opcodes map
+ // there so the interpreter fires llvm_unreachable if ever reached.
+ using Sequence = std::vector<uint16_t>;
+ std::map<Sequence, uint32_t> SeqToOffset;
+ std::vector<uint16_t> Program = {0}; // sentinel at offset 0
+ std::vector<uint32_t> OpcodeToOffset(NumberedInstructions.size(), 0);
for (const AsmWriterInst &AWI : Instructions) {
- PatternBody Body;
+ Sequence Seq;
for (const AsmWriterOperand &Op : AWI.Operands)
- Body.push_back(Op.getCode(PassSubtarget, "P->"));
- auto [It, Inserted] = PatternMap.emplace(Body, Patterns.size());
+ Seq.push_back(GetStmtIdx(Op.getCode(PassSubtarget)));
+ Seq.push_back(1); // terminator
+
+ auto [It, Inserted] = SeqToOffset.emplace(Seq, (uint32_t)Program.size());
if (Inserted)
- Patterns.push_back(Body);
- OpcodeToPattern[AWI.CGIIndex] = It->second;
+ Program.insert(Program.end(), Seq.begin(), Seq.end());
+ OpcodeToOffset[AWI.CGIIndex] = It->second;
}
- std::string FullClassName = (TargetName + ClassName).str();
- // Include ClassName in the table name so multiple AsmWriter variants in the
- // same TU (e.g. AArch64GenAsmWriter.inc + AArch64GenAsmWriter1.inc) don't
- // produce conflicting variable declarations.
- std::string PtrTableName = FullClassName + "Printers";
- std::string ParamList = " " + FullClassName +
- " *, const MCInst *,\n"
- " uint64_t," +
- (PassSubtarget ? " const MCSubtargetInfo &," : "") +
- " raw_ostream &";
-
- // printPattern_None: no-op for instructions fully handled by table-driven
- // path.
- O << "static void printPattern_None(\n" << ParamList << ") {}\n\n";
-
- // One printPattern_N per unique operand sequence.
- for (unsigned PIdx = 0, E = Patterns.size(); PIdx < E; ++PIdx) {
- O << "static void printPattern_" << PIdx << "(\n"
- << " " << FullClassName << " *P, const MCInst *MI,\n"
- << " uint64_t Address,";
- if (PassSubtarget)
- O << " const MCSubtargetInfo &STI,";
- O << " raw_ostream &O) {\n";
- for (const std::string &Line : Patterns[PIdx])
- O << " " << Line << "\n";
- O << "}\n\n";
- }
+ // Emit OpcodeToOffset table.
+ O << "// Maps each opcode to its starting index in " << TargetName
+ << "OverflowProgram.\n"
+ << "// " << NumberedInstructions.size() << " entries ("
+ << Instructions.size() << " overflow instructions, " << SeqToOffset.size()
+ << " unique sequences).\n"
+ << "static const uint32_t " << TargetName << "OpcodeToOffset[] = {\n";
+ for (unsigned I = 0; I < NumberedInstructions.size(); ++I)
+ O << " " << OpcodeToOffset[I] << ",\t// "
+ << NumberedInstructions[I]->getName() << "\n";
+ O << "};\n\n";
- // Function-pointer table indexed by opcode, covering the full opcode range.
- // Non-overflow opcodes → printPattern_None.
- // Overflow opcodes → their deduplicated printPattern_N.
- O << "static void (*const " << PtrTableName << "[])(\n"
- << ParamList << ") = {\n";
- for (unsigned i = 0, E = NumberedInstructions.size(); i < E; ++i) {
- unsigned PIdx = OpcodeToPattern[i];
- if (PIdx == ~0U)
- O << " &printPattern_None";
+ // Emit OverflowProgram bytecode table with offset annotations at sequence
+ // boundaries.
+ std::set<uint32_t> Boundaries;
+ for (auto &[Seq, Off] : SeqToOffset)
+ Boundaries.insert(Off);
+ O << "// Concatenated operand-printing bytecode sequences (terminated by "
+ "1).\n"
+ << "// " << SeqToOffset.size() << " unique sequences, " << Program.size()
+ << " uint16_t entries total.\n"
+ << "static const uint16_t " << TargetName << "OverflowProgram[] = {\n";
+ for (unsigned I = 0; I < Program.size(); ++I) {
+ if (Boundaries.count(I))
+ O << " /* " << I << " */ " << (unsigned)Program[I] << ",\n";
else
- O << " &printPattern_" << PIdx;
- if (i + 1 < E)
- O << ",";
- O << "\t// " << NumberedInstructions[i]->getName() << "\n";
+ O << " " << (unsigned)Program[I] << ",\n";
}
O << "};\n\n";
LLVM_DEBUG(dbgs() << "[AsmWriter] " << TargetName << ": "
- << OpcodeInsts.size() << " overflow instructions -> "
- << Patterns.size() << " unique patterns\n");
+ << Instructions.size() << " overflow instructions -> "
+ << SeqToOffset.size() << " unique sequences, "
+ << BytecodeStmts.size() << " unique statements\n");
}
-void AsmWriterEmitter::EmitOpcodePatternDispatch(raw_ostream &O,
- StringRef TargetName,
- StringRef ClassName,
- bool PassSubtarget) {
- // Single unconditional indirect call; no switch, no branch.
- // The compiler cannot inline through a const function pointer, so
- // printInstruction() itself stays small regardless of instruction-set size.
- std::string PtrTableName = (TargetName + ClassName + "Printers").str();
- O << " " << PtrTableName << "[MI->getOpcode()](this, MI, Address, ";
- if (PassSubtarget)
- O << "STI, ";
- O << "O);\n";
+void AsmWriterEmitter::EmitOverflowBytecodeInterpreter(raw_ostream &O,
+ bool PassSubtarget) {
+ StringRef TargetName = Target.getName();
+ // Execute the bytecode sequence for this opcode until the return terminator.
+ O << " for (uint32_t Idx = " << TargetName
+ << "OpcodeToOffset[MI->getOpcode()];; ++Idx) {\n"
+ << " switch (" << TargetName << "OverflowProgram[Idx]) {\n"
+ << " default: llvm_unreachable(\"Unexpected bytecode command.\");\n";
+ for (unsigned I = 0; I < BytecodeStmts.size(); ++I) {
+ O << " case " << I << ":\n"
+ << " " << BytecodeStmts[I] << "\n";
+ // "return;" exits printInstruction(); no break needed.
+ if (BytecodeStmts[I] != "return;")
+ O << " break;\n";
+ }
+ O << " }\n }\n";
}
static void
More information about the llvm-commits
mailing list