[llvm] [TableGen][DecoderEmitter] Infer encoding's HasCompleteDecoder earlier (NFCI) (PR #154644)

Sergei Barannikov via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 21 14:05:26 PDT 2025


https://github.com/s-barannikov updated https://github.com/llvm/llvm-project/pull/154644

>From 5794f992552ecb37afcc41fab61fa92d81b89805 Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <barannikov88 at gmail.com>
Date: Thu, 21 Aug 2025 01:38:38 +0300
Subject: [PATCH] [TableGen][DecoderEmitter] Infer encoding's
 HasCompleteDecoder earlier (NFCI)

If an encoding has a custom decoder, the decoder is assumed to be
"complete" (always succeed) if hasCompleteDecoder field is true.
We determine this when constructing InstructionEncoding.

If the decoder for an encoding is *generated*, it always succeeds
if none of the operand decoders can fail. The latter is determined
based on the value of operands' DecoderMethod/hasCompleteDecoder.
This happens late, at emission time, complicating the code.

This change moves this logic to the InstructionEncoding constructor.
---
 llvm/utils/TableGen/DecoderEmitter.cpp | 76 +++++++++++++-------------
 1 file changed, 38 insertions(+), 38 deletions(-)

diff --git a/llvm/utils/TableGen/DecoderEmitter.cpp b/llvm/utils/TableGen/DecoderEmitter.cpp
index 95522030300a0..47804255ffb0d 100644
--- a/llvm/utils/TableGen/DecoderEmitter.cpp
+++ b/llvm/utils/TableGen/DecoderEmitter.cpp
@@ -147,9 +147,10 @@ class InstructionEncoding {
   /// meaning the decoder is generated.
   StringRef DecoderMethod;
 
-  /// Whether the custom decoding function always succeeds. Should not be used
-  /// if the decoder is generated.
-  bool HasCompleteDecoder = true;
+  /// Whether the custom decoding function always succeeds. If a custom decoder
+  /// function is specified, the value is taken from the target description,
+  /// otherwise it is inferred.
+  bool HasCompleteDecoder;
 
   /// Information about the operands' contribution to this encoding.
   SmallVector<OperandInfo, 16> Operands;
@@ -174,11 +175,9 @@ class InstructionEncoding {
   /// if the decoder is generated.
   StringRef getDecoderMethod() const { return DecoderMethod; }
 
-  /// Returns whether the custom decoding function always succeeds.
-  bool hasCompleteDecoder() const {
-    assert(!DecoderMethod.empty());
-    return HasCompleteDecoder;
-  }
+  /// Returns whether the decoder (either generated or specified by the user)
+  /// always succeeds.
+  bool hasCompleteDecoder() const { return HasCompleteDecoder; }
 
   /// Returns information about the operands' contribution to this encoding.
   ArrayRef<OperandInfo> getOperands() const { return Operands; }
@@ -658,12 +657,11 @@ class FilterChooser {
   void emitSingletonTableEntry(DecoderTableInfo &TableInfo,
                                const Filter &Best) const;
 
-  bool emitBinaryParser(raw_ostream &OS, indent Indent,
+  void emitBinaryParser(raw_ostream &OS, indent Indent,
                         const OperandInfo &OpInfo) const;
 
-  bool emitDecoder(raw_ostream &OS, indent Indent, unsigned EncodingID) const;
-  std::pair<unsigned, bool> getDecoderIndex(DecoderSet &Decoders,
-                                            unsigned EncodingID) const;
+  void emitDecoder(raw_ostream &OS, indent Indent, unsigned EncodingID) const;
+  unsigned getDecoderIndex(DecoderSet &Decoders, unsigned EncodingID) const;
 
   // Assign a single filter and run with it.
   void runSingleFilter(unsigned startBit, unsigned numBit);
@@ -1212,7 +1210,7 @@ FilterChooser::getIslands(const insn_t &Insn) const {
   return Islands;
 }
 
-bool FilterChooser::emitBinaryParser(raw_ostream &OS, indent Indent,
+void FilterChooser::emitBinaryParser(raw_ostream &OS, indent Indent,
                                      const OperandInfo &OpInfo) const {
   const std::string &Decoder = OpInfo.Decoder;
 
@@ -1238,65 +1236,56 @@ bool FilterChooser::emitBinaryParser(raw_ostream &OS, indent Indent,
     OS << ";\n";
   }
 
-  bool OpHasCompleteDecoder;
   if (!Decoder.empty()) {
-    OpHasCompleteDecoder = OpInfo.HasCompleteDecoder;
     OS << Indent << "if (!Check(S, " << Decoder
        << "(MI, tmp, Address, Decoder))) { "
-       << (OpHasCompleteDecoder ? "" : "DecodeComplete = false; ")
+       << (OpInfo.HasCompleteDecoder ? "" : "DecodeComplete = false; ")
        << "return MCDisassembler::Fail; }\n";
   } else {
-    OpHasCompleteDecoder = true;
     OS << Indent << "MI.addOperand(MCOperand::createImm(tmp));\n";
   }
-  return OpHasCompleteDecoder;
 }
 
-bool FilterChooser::emitDecoder(raw_ostream &OS, indent Indent,
+void FilterChooser::emitDecoder(raw_ostream &OS, indent Indent,
                                 unsigned EncodingID) const {
   const InstructionEncoding &Encoding = Encodings[EncodingID];
 
   // If a custom instruction decoder was specified, use that.
   StringRef DecoderMethod = Encoding.getDecoderMethod();
   if (!DecoderMethod.empty()) {
-    bool HasCompleteDecoder = Encoding.hasCompleteDecoder();
     OS << Indent << "if (!Check(S, " << DecoderMethod
        << "(MI, insn, Address, Decoder))) { "
-       << (HasCompleteDecoder ? "" : "DecodeComplete = false; ")
+       << (Encoding.hasCompleteDecoder() ? "" : "DecodeComplete = false; ")
        << "return MCDisassembler::Fail; }\n";
-    return HasCompleteDecoder;
+    return;
   }
 
-  bool HasCompleteDecoder = true;
   for (const OperandInfo &Op : Encoding.getOperands()) {
     // FIXME: This is broken. If there is an operand that doesn't contribute
     //   to the encoding, we generate the same code as if the decoder method
     //   was specified on the encoding. And then we stop, ignoring the rest
     //   of the operands. M68k disassembler experiences this.
     if (Op.numFields() == 0 && !Op.Decoder.empty()) {
-      HasCompleteDecoder = Op.HasCompleteDecoder;
       OS << Indent << "if (!Check(S, " << Op.Decoder
          << "(MI, insn, Address, Decoder))) { "
-         << (HasCompleteDecoder ? "" : "DecodeComplete = false; ")
+         << (Op.HasCompleteDecoder ? "" : "DecodeComplete = false; ")
          << "return MCDisassembler::Fail; }\n";
       break;
     }
 
-    HasCompleteDecoder &= emitBinaryParser(OS, Indent, Op);
+    emitBinaryParser(OS, Indent, Op);
   }
-  return HasCompleteDecoder;
 }
 
-std::pair<unsigned, bool>
-FilterChooser::getDecoderIndex(DecoderSet &Decoders,
-                               unsigned EncodingID) const {
+unsigned FilterChooser::getDecoderIndex(DecoderSet &Decoders,
+                                        unsigned EncodingID) const {
   // Build up the predicate string.
   SmallString<256> Decoder;
   // FIXME: emitDecoder() function can take a buffer directly rather than
   // a stream.
   raw_svector_ostream S(Decoder);
   indent Indent(UseFnTableInDecodeToMCInst ? 2 : 4);
-  bool HasCompleteDecoder = emitDecoder(S, Indent, EncodingID);
+  emitDecoder(S, Indent, EncodingID);
 
   // Using the full decoder string as the key value here is a bit
   // heavyweight, but is effective. If the string comparisons become a
@@ -1308,7 +1297,7 @@ FilterChooser::getDecoderIndex(DecoderSet &Decoders,
   Decoders.insert(CachedHashString(Decoder));
   // Now figure out the index for when we write out the table.
   DecoderSet::const_iterator P = find(Decoders, Decoder.str());
-  return {(unsigned)(P - Decoders.begin()), HasCompleteDecoder};
+  return std::distance(Decoders.begin(), P);
 }
 
 // If ParenIfBinOp is true, print a surrounding () if Val uses && or ||.
@@ -1508,8 +1497,7 @@ void FilterChooser::emitSingletonTableEntry(DecoderTableInfo &TableInfo,
   // Check for soft failure of the match.
   emitSoftFailTableEntry(TableInfo, EncodingID);
 
-  auto [DIdx, HasCompleteDecoder] =
-      getDecoderIndex(TableInfo.Decoders, EncodingID);
+  unsigned DIdx = getDecoderIndex(TableInfo.Decoders, EncodingID);
 
   // Produce OPC_Decode or OPC_TryDecode opcode based on the information
   // whether the instruction decoder is complete or not. If it is complete
@@ -1520,10 +1508,12 @@ void FilterChooser::emitSingletonTableEntry(DecoderTableInfo &TableInfo,
   // decoder method indicates that additional processing should be done to see
   // if there is any other instruction that also matches the bitpattern and
   // can decode it.
-  const uint8_t DecoderOp = HasCompleteDecoder ? MCD::OPC_Decode
-                                               : (TableInfo.isOutermostScope()
-                                                      ? MCD::OPC_TryDecodeOrFail
-                                                      : MCD::OPC_TryDecode);
+  const InstructionEncoding &Encoding = Encodings[EncodingID];
+  const uint8_t DecoderOp =
+      Encoding.hasCompleteDecoder()
+          ? MCD::OPC_Decode
+          : (TableInfo.isOutermostScope() ? MCD::OPC_TryDecodeOrFail
+                                          : MCD::OPC_TryDecode);
   TableInfo.Table.push_back(DecoderOp);
   const Record *InstDef = Encodings[EncodingID].getInstruction()->TheDef;
   TableInfo.Table.insertULEB128(Emitter->getTarget().getInstrIntValue(InstDef));
@@ -2124,6 +2114,16 @@ InstructionEncoding::InstructionEncoding(const Record *EncodingDef,
     if (DecoderMethod.empty())
       parseFixedLenOperands(*BI);
   }
+
+  if (DecoderMethod.empty()) {
+    // A generated decoder is always successful if none of the operand
+    // decoders can fail (all are always successful).
+    HasCompleteDecoder = all_of(Operands, [](const OperandInfo &Op) {
+      // By default, a generated operand decoder is assumed to always succeed.
+      // This can be overridden by the user.
+      return Op.Decoder.empty() || Op.HasCompleteDecoder;
+    });
+  }
 }
 
 // emitFieldFromInstruction - Emit the templated helper function



More information about the llvm-commits mailing list