[llvm] [TableGen][DecoderEmitter] Add option to emit type-specialized code (PR #146593)
Sergei Barannikov via llvm-commits
llvm-commits at lists.llvm.org
Tue Aug 19 04:50:25 PDT 2025
================
@@ -2574,52 +2776,140 @@ namespace {
}
}
+ bool GenerateTemplated =
+ Target.getInstructionSet()->getValueAsBit("GenerateTemplatedDecoder");
+
+ // For variable instruction, we emit a instruction length table to let the
+ // decoder know how long the instructions are. You can see example usage in
+ // M68k's disassembler.
+ if (IsVarLenInst) {
+ if (GenerateTemplated)
+ PrintFatalError(
+ "Templated decoder not needed for variable length instruction");
+ emitInstrLenTable(OS, InstrLen);
+ }
+
DecoderTableInfo TableInfo;
+ bool HasCheckPredicate = false;
unsigned OpcodeMask = 0;
- for (const auto &[Key, EncodingIDs] : EncMap) {
- auto [DecoderNamespace, HwModeID, Size] = Key;
- const unsigned BitWidth = IsVarLenInst ? MaxInstLen : 8 * Size;
- // Emit the decoder for this (namespace, hwmode, width) combination.
- FilterChooser FC(Encodings, EncodingIDs, Operands, BitWidth, this);
-
- // The decode table is cleared for each top level decoder function. The
- // predicates and decoders themselves, however, are shared across all
- // decoders to give more opportunities for uniqueing.
- TableInfo.Table.clear();
- TableInfo.pushScope();
- FC.emitTableEntries(TableInfo);
- // Any NumToSkip fixups in the top level scope can resolve to the
- // OPC_Fail at the end of the table.
- assert(TableInfo.isOutermostScope() && "fixup stack phasing error!");
- TableInfo.popScope();
- TableInfo.Table.push_back(MCD::OPC_Fail);
+ // Helper lambda to emit the decoder code for a given instruction Bitwidth
+ // and associated C++ type. If `Bitwidth` is 0 (and CPPType is empty) it will
+ // generate templated decoder code.
+ auto emitDecoder = [&](const CPPType &Type, StringRef Suffix) {
+ // Reset the Decoders for each non-templated type.
+ TableInfo.Decoders.clear();
+
+ const bool IsTemplate = Type.Kind == CPPType::TemplateTy;
+ if (!IsTemplate) {
+ OS << "// ------------------------------------------------------------\n";
+ OS << "// Decoder tables for " << Type.Bitwidth << "-bit instructions.\n";
+ OS << "// Using C++ type `" << Type.getName() << "` for payload.\n\n";
+ }
- // Print the table to the output stream.
- OpcodeMask |= emitTable(OS, TableInfo.Table, DecoderNamespace, HwModeID,
- BitWidth, EncodingIDs);
- }
+ for (const auto &[Key, EncodingIDs] : EncMap) {
+ auto [DecoderNamespace, HwModeID, Size] = Key;
+ const unsigned Bitwidth = IsVarLenInst ? MaxInstLen : 8 * Size;
- // For variable instruction, we emit a instruction length table
- // to let the decoder know how long the instructions are.
- // You can see example usage in M68k's disassembler.
- if (IsVarLenInst)
- emitInstrLenTable(OS, InstrLen);
+ if (!IsTemplate && Bitwidth != Type.Bitwidth)
+ continue;
- const bool HasCheckPredicate =
- OpcodeMask &
- ((1 << MCD::OPC_CheckPredicate) | (1 << MCD::OPC_CheckPredicateOrFail));
+ // Emit the decoder table for this (namespace, hwmode, width) combination.
+ FilterChooser FC(Encodings, EncodingIDs, Operands, Bitwidth, this);
+
+ // The decode table is cleared for each top level decoder table generated.
+ //
+ // The decoders themselves are shared across all decoder tables for a
+ // given instuction bitwidth, to give more opporuninity for uniqueing.
+ // Generally, decoders across different instruction do not have much
+ // uniqueing opportunity, and we generate a different decode function
+ // for each bitwidth, so we clear the decoders themselves for each
+ // bitwidth (at the start of `emitDecoder`).
+ //
+ // Predicates are shared across all decoders to give more opportunities
+ // for uniqueing.
+ TableInfo.Table.clear();
+ TableInfo.pushScope();
+ FC.emitTableEntries(TableInfo);
+ // Any NumToSkip fixups in the top level scope can resolve to the
+ // OPC_Fail at the end of the table.
+ assert(TableInfo.isOutermostScope() && "fixup stack phasing error!");
+ TableInfo.popScope();
+ TableInfo.Table.push_back(MCD::OPC_Fail);
+
+ // Print the table to the output stream.
+ OpcodeMask |= emitTable(OS, TableInfo.Table, DecoderNamespace, HwModeID,
+ Bitwidth, EncodingIDs);
+ }
+
+ // Emit the decoder function for this Bitwidth.
+ emitDecoderFunction(OS, TableInfo.Decoders, Type, Suffix);
+
+ HasCheckPredicate |= OpcodeMask & ((1 << MCD::OPC_CheckPredicate) |
+ (1 << MCD::OPC_CheckPredicateOrFail));
+ };
+
+ if (GenerateTemplated) {
+ // Generate the tempated variaant of `fieldFromInstruction`.
+ emitFieldFromInstruction(
+ OS, /*GenerateIntType=*/true, /*GenerateBitsetType=*/false,
+ /*GenerateAPIntType=*/false, /*GenerateTemplateType=*/true);
+ const CPPType Type(0, false);
+ emitDecoder(Type, /*Suffix=*/"");
+ emitDecodeInstruction(OS, IsVarLenInst, OpcodeMask, Type, /*Suffix=*/"",
+ /*IsImplFnuction=*/false);
+ } else {
+ // Collect all allowed Bitwidths for instructions and keep track of the
+ // variants of `fieldFromInstruction` we need to generate.
+ SmallSet<unsigned, 4> InstrBitwidths;
+ bool GenerateIntType = false;
+ bool GenerateBitsetType = false;
+ for (const auto &[Key, _] : EncMap) {
+ unsigned Size = std::get<2>(Key);
+ const unsigned Bitwidth = IsVarLenInst ? MaxInstLen : 8 * Size;
+ InstrBitwidths.insert(Bitwidth);
+ const CPPType::TypeKind Kind = CPPType(Bitwidth, IsVarLenInst).Kind;
+ GenerateIntType |= Kind == CPPType::UIntTy;
+ GenerateBitsetType |= Kind == CPPType::BitsetTy;
+ }
+ assert((!IsVarLenInst || InstrBitwidths.size() == 1) &&
----------------
s-barannikov wrote:
It may also make sense to add a separate `else if (isVarLenInst)`.
https://github.com/llvm/llvm-project/pull/146593
More information about the llvm-commits
mailing list