[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