Skip to content

Conversation

@s-barannikov
Copy link
Contributor

@s-barannikov s-barannikov commented Aug 20, 2025

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 table construction time, making the code harder to follow.

This change moves this logic to the InstructionEncoding constructor.

@llvmbot
Copy link
Member

llvmbot commented Aug 20, 2025

@llvm/pr-subscribers-tablegen

Author: Sergei Barannikov (s-barannikov)

Changes

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, making the code harder to follow.

This change moves this logic to the InstructionEncoding constructor.


Full diff: https://github.com/llvm/llvm-project/pull/154644.diff

1 Files Affected:

  • (modified) llvm/utils/TableGen/DecoderEmitter.cpp (+38-38)
diff --git a/llvm/utils/TableGen/DecoderEmitter.cpp b/llvm/utils/TableGen/DecoderEmitter.cpp
index 8fa44bcc7b964..62903a10b26b2 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;
@@ -169,6 +170,16 @@ class InstructionEncoding {
       HasCompleteDecoder = EncodingDef->getValueAsBit("hasCompleteDecoder");
 
     populateEncoding();
+
+    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;
+      });
+    }
   }
 
   /// Returns the Record this encoding originates from.
@@ -187,11 +198,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; }
@@ -672,12 +681,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);
@@ -1226,7 +1234,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;
 
@@ -1252,65 +1260,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
@@ -1322,7 +1321,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 ||.
@@ -1522,8 +1521,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
@@ -1534,10 +1532,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));

@s-barannikov s-barannikov force-pushed the tablegen/decoder/operands-complete-decoder branch from 494dffb to 23d55e1 Compare August 21, 2025 06:15
…r (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.
@s-barannikov s-barannikov force-pushed the tablegen/decoder/operands-complete-decoder branch from 23d55e1 to 5794f99 Compare August 21, 2025 21:05
@s-barannikov
Copy link
Contributor Author

Rebased to resolve conflicts

@s-barannikov s-barannikov enabled auto-merge (squash) August 21, 2025 21:26
@s-barannikov s-barannikov merged commit 2421929 into llvm:main Aug 21, 2025
9 checks passed
@s-barannikov s-barannikov deleted the tablegen/decoder/operands-complete-decoder branch August 21, 2025 21:36
@llvm-ci
Copy link
Collaborator

llvm-ci commented Aug 21, 2025

LLVM Buildbot has detected a new failure on builder lldb-x86_64-debian running on lldb-x86_64-debian while building llvm at step 6 "test".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/162/builds/29483

Here is the relevant piece of the build log for the reference
Step 6 (test) failure: build (failure)
...
PASS: lldb-api :: tools/lldb-dap/stackTraceMissingFunctionName/TestDAP_stackTraceMissingFunctionName.py (258 of 3150)
PASS: lldb-api :: tools/lldb-dap/locations/TestDAP_locations.py (259 of 3150)
PASS: lldb-api :: functionalities/breakpoint/cpp/TestCPPBreakpointLocations.py (260 of 3150)
PASS: lldb-api :: functionalities/gdb_remote_client/TestTargetXMLArch.py (261 of 3150)
PASS: lldb-api :: functionalities/breakpoint/breakpoint_by_line_and_column/TestBreakpointByLineAndColumn.py (262 of 3150)
PASS: lldb-api :: functionalities/tail_call_frames/unambiguous_sequence/TestUnambiguousTailCalls.py (263 of 3150)
PASS: lldb-api :: tools/lldb-dap/exception/cpp/TestDAP_exception_cpp.py (264 of 3150)
PASS: lldb-api :: lang/cpp/thunk/TestThunk.py (265 of 3150)
PASS: lldb-api :: functionalities/tail_call_frames/ambiguous_tail_call_seq2/TestAmbiguousTailCallSeq2.py (266 of 3150)
PASS: lldb-api :: tools/lldb-dap/stackTraceDisassemblyDisplay/TestDAP_stackTraceDisassemblyDisplay.py (267 of 3150)
FAIL: lldb-api :: tools/lldb-dap/output/TestDAP_output.py (268 of 3150)
******************** TEST 'lldb-api :: tools/lldb-dap/output/TestDAP_output.py' FAILED ********************
Script:
--
/usr/bin/python3 /home/worker/2.0.1/lldb-x86_64-debian/llvm-project/lldb/test/API/dotest.py -u CXXFLAGS -u CFLAGS --env LLVM_LIBS_DIR=/home/worker/2.0.1/lldb-x86_64-debian/build/./lib --env LLVM_INCLUDE_DIR=/home/worker/2.0.1/lldb-x86_64-debian/build/include --env LLVM_TOOLS_DIR=/home/worker/2.0.1/lldb-x86_64-debian/build/./bin --arch x86_64 --build-dir /home/worker/2.0.1/lldb-x86_64-debian/build/lldb-test-build.noindex --lldb-module-cache-dir /home/worker/2.0.1/lldb-x86_64-debian/build/lldb-test-build.noindex/module-cache-lldb/lldb-api --clang-module-cache-dir /home/worker/2.0.1/lldb-x86_64-debian/build/lldb-test-build.noindex/module-cache-clang/lldb-api --executable /home/worker/2.0.1/lldb-x86_64-debian/build/./bin/lldb --compiler /home/worker/2.0.1/lldb-x86_64-debian/build/./bin/clang --dsymutil /home/worker/2.0.1/lldb-x86_64-debian/build/./bin/dsymutil --make /usr/bin/gmake --llvm-tools-dir /home/worker/2.0.1/lldb-x86_64-debian/build/./bin --lldb-obj-root /home/worker/2.0.1/lldb-x86_64-debian/build/tools/lldb --lldb-libs-dir /home/worker/2.0.1/lldb-x86_64-debian/build/./lib --cmake-build-type Release -t /home/worker/2.0.1/lldb-x86_64-debian/llvm-project/lldb/test/API/tools/lldb-dap/output -p TestDAP_output.py
--
Exit Code: 1

Command Output (stdout):
--
lldb version 22.0.0git (https://github.com/llvm/llvm-project.git revision 2421929ca62af916fa68aeb9463f5a2a9f8fd99d)
  clang revision 2421929ca62af916fa68aeb9463f5a2a9f8fd99d
  llvm revision 2421929ca62af916fa68aeb9463f5a2a9f8fd99d
Skipping the following test categories: ['libc++', 'msvcstl', 'dsym', 'gmodules', 'debugserver', 'objc']

--
Command Output (stderr):
--
Change dir to: /home/worker/2.0.1/lldb-x86_64-debian/llvm-project/lldb/test/API/tools/lldb-dap/output
runCmd: settings clear --all

output: 

runCmd: settings set symbols.enable-external-lookup false

output: 

runCmd: settings set target.inherit-tcc true

output: 

runCmd: settings set target.disable-aslr false

output: 

runCmd: settings set target.detach-on-error false

output: 


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants