diff options
author | Rahul Joshi <rjoshi@nvidia.com> | 2025-06-24 18:49:05 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-06-24 18:49:05 -0700 |
commit | ed5f8f238d68811ad5eabcdb0248bcd65ab81960 (patch) | |
tree | 5e031fb5b2e65d57875886e7072f82ee2da94b2a /llvm/utils/TableGen/DecoderEmitter.cpp | |
parent | f608716b098687f67c981f96d1adff324e41e486 (diff) | |
download | llvm-ed5f8f238d68811ad5eabcdb0248bcd65ab81960.zip llvm-ed5f8f238d68811ad5eabcdb0248bcd65ab81960.tar.gz llvm-ed5f8f238d68811ad5eabcdb0248bcd65ab81960.tar.bz2 |
[LLVM][DecoderEmitter] Add option to use function table in decodeToMCInst (#144814)
Add option `use-fn-table-in-decode-to-mcinst` to use a table of function
pointers instead of a switch case in the generated `decodeToMCInst`
function.
When the number of switch cases in this function is large, the generated
code takes a long time to compile in release builds. Using a table of
function pointers instead improves the compile time significantly (~3x
speedup in compiling the code in a downstream target). This option will
allow targets to opt into this mode if they desire for better build
times.
Tested with `check-llvm-mc` with the option enabled by default.
Diffstat (limited to 'llvm/utils/TableGen/DecoderEmitter.cpp')
-rw-r--r-- | llvm/utils/TableGen/DecoderEmitter.cpp | 93 |
1 files changed, 69 insertions, 24 deletions
diff --git a/llvm/utils/TableGen/DecoderEmitter.cpp b/llvm/utils/TableGen/DecoderEmitter.cpp index 2e8ff2a..af25975 100644 --- a/llvm/utils/TableGen/DecoderEmitter.cpp +++ b/llvm/utils/TableGen/DecoderEmitter.cpp @@ -83,6 +83,14 @@ static cl::opt<bool> LargeTable( "in the table instead of the default 16 bits."), cl::init(false), cl::cat(DisassemblerEmitterCat)); +static cl::opt<bool> UseFnTableInDecodetoMCInst( + "use-fn-table-in-decode-to-mcinst", + cl::desc( + "Use a table of function pointers instead of a switch case in the\n" + "generated `decodeToMCInst` function. Helps improve compile time\n" + "of the generated code."), + cl::init(false), cl::cat(DisassemblerEmitterCat)); + STATISTIC(NumEncodings, "Number of encodings considered"); STATISTIC(NumEncodingsLackingDisasm, "Number of encodings without disassembler info"); @@ -1066,31 +1074,67 @@ void DecoderEmitter::emitPredicateFunction(formatted_raw_ostream &OS, void DecoderEmitter::emitDecoderFunction(formatted_raw_ostream &OS, DecoderSet &Decoders, indent Indent) const { - // The decoder function is just a big switch statement based on the - // input decoder index. - OS << Indent << "template <typename InsnType>\n"; - OS << Indent << "static DecodeStatus decodeToMCInst(DecodeStatus S," - << " unsigned Idx, InsnType insn, MCInst &MI,\n"; - OS << Indent << " uint64_t " - << "Address, const MCDisassembler *Decoder, bool &DecodeComplete) {\n"; - Indent += 2; - OS << Indent << "DecodeComplete = true;\n"; + // The decoder function is just a big switch statement or a table of function + // pointers based on the input decoder index. + // TODO: When InsnType is large, using uint64_t limits all fields to 64 bits // It would be better for emitBinaryParser to use a 64-bit tmp whenever // possible but fall back to an InsnType-sized tmp for truly large fields. - OS << Indent - << "using TmpType = " - "std::conditional_t<std::is_integral<InsnType>::" - "value, InsnType, uint64_t>;\n"; - OS << Indent << "TmpType tmp;\n"; - OS << Indent << "switch (Idx) {\n"; - OS << Indent << "default: llvm_unreachable(\"Invalid index!\");\n"; - for (const auto &[Index, Decoder] : enumerate(Decoders)) { - OS << Indent << "case " << Index << ":\n"; - OS << Decoder; - OS << Indent + 2 << "return S;\n"; + StringRef TmpTypeDecl = + "using TmpType = std::conditional_t<std::is_integral<InsnType>::value, " + "InsnType, uint64_t>;\n"; + StringRef DecodeParams = + "DecodeStatus S, InsnType insn, MCInst &MI, uint64_t Address, const " + "MCDisassembler *Decoder, bool &DecodeComplete"; + + if (UseFnTableInDecodetoMCInst) { + // Emit a function for each case first. + for (const auto &[Index, Decoder] : enumerate(Decoders)) { + OS << Indent << "template <typename InsnType>\n"; + OS << Indent << "DecodeStatus decodeFn" << Index << "(" << DecodeParams + << ") {\n"; + Indent += 2; + OS << Indent << TmpTypeDecl; + OS << Indent << "[[maybe_unused]] TmpType tmp;\n"; + OS << Decoder; + OS << Indent << "return S;\n"; + Indent -= 2; + OS << Indent << "}\n\n"; + } + } + + OS << Indent << "// Handling " << Decoders.size() << " cases.\n"; + OS << Indent << "template <typename InsnType>\n"; + OS << Indent << "static DecodeStatus decodeToMCInst(unsigned Idx, " + << DecodeParams << ") {\n"; + Indent += 2; + OS << Indent << "DecodeComplete = true;\n"; + + if (UseFnTableInDecodetoMCInst) { + // Build a table of function pointers. + OS << Indent << "using DecodeFnTy = DecodeStatus (*)(" << DecodeParams + << ");\n"; + OS << Indent << "static constexpr DecodeFnTy decodeFnTable[] = {\n"; + for (size_t Index : llvm::seq(Decoders.size())) + OS << Indent + 2 << "decodeFn" << Index << ",\n"; + OS << Indent << "};\n"; + OS << Indent << "if (Idx >= " << Decoders.size() << ")\n"; + OS << Indent + 2 << "llvm_unreachable(\"Invalid index!\");\n"; + OS << Indent + << "return decodeFnTable[Idx](S, insn, MI, Address, Decoder, " + "DecodeComplete);\n"; + } else { + OS << Indent << TmpTypeDecl; + OS << Indent << "TmpType tmp;\n"; + OS << Indent << "switch (Idx) {\n"; + OS << Indent << "default: llvm_unreachable(\"Invalid index!\");\n"; + for (const auto &[Index, Decoder] : enumerate(Decoders)) { + OS << Indent << "case " << Index << ":\n"; + OS << Decoder; + OS << Indent + 2 << "return S;\n"; + } + OS << Indent << "}\n"; } - OS << Indent << "}\n"; Indent -= 2; OS << Indent << "}\n"; } @@ -1267,7 +1311,8 @@ std::pair<unsigned, bool> FilterChooser::getDecoderIndex(DecoderSet &Decoders, // FIXME: emitDecoder() function can take a buffer directly rather than // a stream. raw_svector_ostream S(Decoder); - bool HasCompleteDecoder = emitDecoder(S, indent(4), Opc); + indent Indent(UseFnTableInDecodetoMCInst ? 2 : 4); + bool HasCompleteDecoder = emitDecoder(S, Indent, Opc); // Using the full decoder string as the key value here is a bit // heavyweight, but is effective. If the string comparisons become a @@ -2371,7 +2416,7 @@ static DecodeStatus decodeInstruction(const uint8_t DecodeTable[], MCInst &MI, << " makeUp(insn, Len);"; } OS << R"( - S = decodeToMCInst(S, DecodeIdx, insn, MI, Address, DisAsm, DecodeComplete); + S = decodeToMCInst(DecodeIdx, S, insn, MI, Address, DisAsm, DecodeComplete); assert(DecodeComplete); LLVM_DEBUG(dbgs() << Loc << ": OPC_Decode: opcode " << Opc @@ -2393,7 +2438,7 @@ static DecodeStatus decodeInstruction(const uint8_t DecodeTable[], MCInst &MI, MCInst TmpMI; TmpMI.setOpcode(Opc); bool DecodeComplete; - S = decodeToMCInst(S, DecodeIdx, insn, TmpMI, Address, DisAsm, DecodeComplete); + S = decodeToMCInst(DecodeIdx, S, insn, TmpMI, Address, DisAsm, DecodeComplete); LLVM_DEBUG(dbgs() << Loc << ": OPC_TryDecode: opcode " << Opc << ", using decoder " << DecodeIdx << ": "); |