[llvm] [TableGen] Emit Debug code for dynamic bits(eg: instruction operands) in DecoderEmitter (PR #194699)

Prerona Chaudhuri via llvm-commits llvm-commits at lists.llvm.org
Fri May 1 08:11:06 PDT 2026


https://github.com/pchaudhuri-nv updated https://github.com/llvm/llvm-project/pull/194699

>From b85b7e82834516a961386295e4f826a769a3494c Mon Sep 17 00:00:00 2001
From: pchaudhuri-nv <pchaudhuri at nvidia.com>
Date: Tue, 28 Apr 2026 18:23:25 +0000
Subject: [PATCH 1/2] [TableGen] Emit LLVM_DEBUG for per-operand decode in
 DecoderEmitter

Generated disassembler code now logs PASS/FAIL for each operand decode
and whole-instruction decode methods (emitCheckWithDbgMsg). Update
DecoderEmitter and RegClassByHwMode TableGen tests. Add an AArch64 MC
lit test exercising -debug-only=aarch64-disassembler.

Made-with: Cursor
---
 .../AArch64/aarch64-disassembler-debug.txt    | 24 ++++++
 .../TableGen/DecoderEmitter/VarLenDecoder.td  | 30 ++++++--
 .../DecoderEmitter/operand-decoder.td         |  5 +-
 .../DecoderEmitter/trydecode-emission.td      |  8 +-
 .../DecoderEmitter/trydecode-emission2.td     | 16 +++-
 .../DecoderEmitter/trydecode-emission3.td     |  9 ++-
 .../DecoderEmitter/trydecode-emission4.td     |  8 +-
 llvm/test/TableGen/RegClassByHwMode.td        | 42 ++++++++--
 llvm/utils/TableGen/DecoderEmitter.cpp        | 77 ++++++++++++++++---
 9 files changed, 190 insertions(+), 29 deletions(-)
 create mode 100644 llvm/test/MC/Disassembler/AArch64/aarch64-disassembler-debug.txt

diff --git a/llvm/test/MC/Disassembler/AArch64/aarch64-disassembler-debug.txt b/llvm/test/MC/Disassembler/AArch64/aarch64-disassembler-debug.txt
new file mode 100644
index 0000000000000..2cbccfbb59c2b
--- /dev/null
+++ b/llvm/test/MC/Disassembler/AArch64/aarch64-disassembler-debug.txt
@@ -0,0 +1,24 @@
+# REQUIRES: asserts
+# RUN: llvm-mc -triple=aarch64 -mattr=+v8a -disassemble -debug-only=aarch64-disassembler < %s 2>&1 | FileCheck %s
+#
+# Exercises LLVM_DEBUG output from DecoderEmitter-generated AArch64 disassembly:
+# outer OPC_Decode lifecycle, per-operand field(...) checks, and whole-instruction
+# DecoderMethod (DecodeAddSubImmShift).
+
+# BLR x0 — per-operand DecodeSimpleRegisterClass with field(5,5)
+# CHECK: OPC_Decode: attempting to decode Opcode = {{[0-9]+}} .
+# CHECK: OPC_Decode for Rn: field(5,5) using DecodeSimpleRegisterClass<AArch64::GPR64RegClassID, 0, 32> : PASS
+# CHECK: OPC_Decode: opcode {{[0-9]+}} using decoder {{[0-9]+}}: PASS, decoding complete
+
+# add x5, x7, #1638 — custom insn decoder from DecoderEmitter::emitDecoderMethodOpcDebug
+# CHECK: OPC_Decode: attempting to decode Opcode = {{[0-9]+}} .
+# CHECK: OPC_Decode for ADDXri: insn using DecodeAddSubImmShift : PASS
+# CHECK: OPC_Decode: opcode {{[0-9]+}} using decoder {{[0-9]+}}: PASS, decoding complete
+
+# nop — decode completes without per-operand / insn-method OPC_Decode lines
+# CHECK: OPC_Decode: attempting to decode Opcode = {{[0-9]+}} .
+# CHECK: OPC_Decode: opcode {{[0-9]+}} using decoder {{[0-9]+}}: PASS, decoding complete
+
+0x0 0x0 0x3f 0xd6
+0xe5 0x98 0x19 0x91
+0x1f 0x20 0x3 0xd5
diff --git a/llvm/test/TableGen/DecoderEmitter/VarLenDecoder.td b/llvm/test/TableGen/DecoderEmitter/VarLenDecoder.td
index faac5cba4579f..697e159cfb0ac 100644
--- a/llvm/test/TableGen/DecoderEmitter/VarLenDecoder.td
+++ b/llvm/test/TableGen/DecoderEmitter/VarLenDecoder.td
@@ -65,17 +65,37 @@ def FOO32 : MyVarInst<MemOp32> {
 
 // CHECK:      case 0:
 // CHECK-NEXT: tmp = fieldFromInstruction(insn, 8, 3);
-// CHECK-NEXT: if (!Check(S, DecodeRegClassRegisterClass(MI, tmp, Address, Decoder))) { return MCDisassembler::Fail; }
+// CHECK-NEXT: if (!Check(S, DecodeRegClassRegisterClass(MI, tmp, Address, Decoder))) {
+// CHECK-NEXT:   LLVM_DEBUG(dbgs() << "OPC_Decode for opd: field(8,3) using DecodeRegClassRegisterClass : FAIL" << '\n');
+// CHECK-NEXT:   return MCDisassembler::Fail;
+// CHECK-NEXT: }
+// CHECK-NEXT: LLVM_DEBUG(dbgs() << "OPC_Decode for opd: field(8,3) using DecodeRegClassRegisterClass : PASS" << '\n');
 // CHECK-NEXT: tmp = fieldFromInstruction(insn, 0, 3);
-// CHECK-NEXT: if (!Check(S, DecodeRegClassRegisterClass(MI, tmp, Address, Decoder))) { return MCDisassembler::Fail; }
+// CHECK-NEXT: if (!Check(S, DecodeRegClassRegisterClass(MI, tmp, Address, Decoder))) {
+// CHECK-NEXT:   LLVM_DEBUG(dbgs() << "OPC_Decode for opd: field(0,3) using DecodeRegClassRegisterClass : FAIL" << '\n');
+// CHECK-NEXT:   return MCDisassembler::Fail;
+// CHECK-NEXT: }
+// CHECK-NEXT: LLVM_DEBUG(dbgs() << "OPC_Decode for opd: field(0,3) using DecodeRegClassRegisterClass : PASS" << '\n');
 // CHECK-NEXT: tmp = fieldFromInstruction(insn, 11, 16);
-// CHECK-NEXT: if (!Check(S, myCustomDecoder(MI, tmp, Address, Decoder))) { return MCDisassembler::Fail; }
+// CHECK-NEXT: if (!Check(S, myCustomDecoder(MI, tmp, Address, Decoder))) {
+// CHECK-NEXT:   LLVM_DEBUG(dbgs() << "OPC_Decode for opd: field(11,16) using myCustomDecoder : FAIL" << '\n');
+// CHECK-NEXT:   return MCDisassembler::Fail;
+// CHECK-NEXT: }
+// CHECK-NEXT: LLVM_DEBUG(dbgs() << "OPC_Decode for opd: field(11,16) using myCustomDecoder : PASS" << '\n');
 // CHECK-NEXT: return S;
 // CHECK-NEXT: case 1:
 // CHECK-NEXT: tmp = fieldFromInstruction(insn, 8, 3);
-// CHECK-NEXT: if (!Check(S, DecodeRegClassRegisterClass(MI, tmp, Address, Decoder))) { return MCDisassembler::Fail; }
+// CHECK-NEXT: if (!Check(S, DecodeRegClassRegisterClass(MI, tmp, Address, Decoder))) {
+// CHECK-NEXT:   LLVM_DEBUG(dbgs() << "OPC_Decode for opd: field(8,3) using DecodeRegClassRegisterClass : FAIL" << '\n');
+// CHECK-NEXT:   return MCDisassembler::Fail;
+// CHECK-NEXT: }
+// CHECK-NEXT: LLVM_DEBUG(dbgs() << "OPC_Decode for opd: field(8,3) using DecodeRegClassRegisterClass : PASS" << '\n');
 // CHECK-NEXT: tmp = fieldFromInstruction(insn, 0, 3);
-// CHECK-NEXT: if (!Check(S, myCustomDecoder(MI, tmp, Address, Decoder))) { return MCDisassembler::Fail; }
+// CHECK-NEXT: if (!Check(S, myCustomDecoder(MI, tmp, Address, Decoder))) {
+// CHECK-NEXT:   LLVM_DEBUG(dbgs() << "OPC_Decode for opd: field(0,3) using myCustomDecoder : FAIL" << '\n');
+// CHECK-NEXT:   return MCDisassembler::Fail;
+// CHECK-NEXT: }
+// CHECK-NEXT: LLVM_DEBUG(dbgs() << "OPC_Decode for opd: field(0,3) using myCustomDecoder : PASS" << '\n');
 // CHECK-NEXT: tmp = 0x0;
 // CHECK-NEXT: tmp |= fieldFromInstruction(insn, 11, 16) << 16;
 // CHECK-NEXT: tmp |= fieldFromInstruction(insn, 27, 16);
diff --git a/llvm/test/TableGen/DecoderEmitter/operand-decoder.td b/llvm/test/TableGen/DecoderEmitter/operand-decoder.td
index c6ec2ee1a4db4..35e4288d42076 100644
--- a/llvm/test/TableGen/DecoderEmitter/operand-decoder.td
+++ b/llvm/test/TableGen/DecoderEmitter/operand-decoder.td
@@ -12,8 +12,11 @@ def MyTarget : Target {
 }
 
 // CHECK-LABEL: case 0:
-// CHECK-NEXT:    if (!Check(S, DecodeRCRegisterClass(MI, Decoder)))
+// CHECK-NEXT:    if (!Check(S, DecodeRCRegisterClass(MI, Decoder))) {
+// CHECK-NEXT:      LLVM_DEBUG(dbgs() << "OPC_Decode for op0: bits<0> using DecodeRCRegisterClass : FAIL" << '\n');
 // CHECK-NEXT:      return MCDisassembler::Fail;
+// CHECK-NEXT:    }
+// CHECK-NEXT:    LLVM_DEBUG(dbgs() << "OPC_Decode for op0: bits<0> using DecodeRCRegisterClass : PASS" << '\n');
 // CHECK-NEXT:    tmp = fieldFromInstruction(insn, 2, 4);
 // CHECK-NEXT:    MI.addOperand(MCOperand::createImm(tmp));
 // CHECK-NEXT:    tmp = 0x0;
diff --git a/llvm/test/TableGen/DecoderEmitter/trydecode-emission.td b/llvm/test/TableGen/DecoderEmitter/trydecode-emission.td
index a39f26a6e715a..2f772dfb9e750 100644
--- a/llvm/test/TableGen/DecoderEmitter/trydecode-emission.td
+++ b/llvm/test/TableGen/DecoderEmitter/trydecode-emission.td
@@ -43,4 +43,10 @@ def InstB : TestInstruction {
 // CHECK-NEXT:                                 // 14: }
 // CHECK-NEXT: };
 
-// CHECK: if (!Check(S, DecodeInstB(MI, insn, Address, Decoder))) { DecodeComplete = false; return MCDisassembler::Fail; }
+// CHECK-LABEL: case 0:
+// CHECK-NEXT:    if (!Check(S, DecodeInstB(MI, insn, Address, Decoder))) {
+// CHECK-NEXT:      LLVM_DEBUG(dbgs() << "OPC_Decode for InstB: insn using DecodeInstB : FAIL" << '\n');
+// CHECK-NEXT:      DecodeComplete = false;
+// CHECK-NEXT:      return MCDisassembler::Fail;
+// CHECK-NEXT:    }
+// CHECK-NEXT:    LLVM_DEBUG(dbgs() << "OPC_Decode for InstB: insn using DecodeInstB : PASS" << '\n');
diff --git a/llvm/test/TableGen/DecoderEmitter/trydecode-emission2.td b/llvm/test/TableGen/DecoderEmitter/trydecode-emission2.td
index 40d68061bfc0d..48f33c1d5d586 100644
--- a/llvm/test/TableGen/DecoderEmitter/trydecode-emission2.td
+++ b/llvm/test/TableGen/DecoderEmitter/trydecode-emission2.td
@@ -42,5 +42,17 @@ def InstB : TestInstruction {
 // CHECK-NEXT:                                 // 22: }
 // CHECK-NEXT: };
 
-// CHECK: if (!Check(S, DecodeInstB(MI, insn, Address, Decoder))) { DecodeComplete = false; return MCDisassembler::Fail; }
-// CHECK: if (!Check(S, DecodeInstA(MI, insn, Address, Decoder))) { DecodeComplete = false; return MCDisassembler::Fail; }
+// CHECK-LABEL: case 0:
+// CHECK-NEXT:    if (!Check(S, DecodeInstB(MI, insn, Address, Decoder))) {
+// CHECK-NEXT:      LLVM_DEBUG(dbgs() << "OPC_Decode for InstB: insn using DecodeInstB : FAIL" << '\n');
+// CHECK-NEXT:      DecodeComplete = false;
+// CHECK-NEXT:      return MCDisassembler::Fail;
+// CHECK-NEXT:    }
+// CHECK-NEXT:    LLVM_DEBUG(dbgs() << "OPC_Decode for InstB: insn using DecodeInstB : PASS" << '\n');
+// CHECK-LABEL: case 1:
+// CHECK-NEXT:    if (!Check(S, DecodeInstA(MI, insn, Address, Decoder))) {
+// CHECK-NEXT:      LLVM_DEBUG(dbgs() << "OPC_Decode for InstA: insn using DecodeInstA : FAIL" << '\n');
+// CHECK-NEXT:      DecodeComplete = false;
+// CHECK-NEXT:      return MCDisassembler::Fail;
+// CHECK-NEXT:    }
+// CHECK-NEXT:    LLVM_DEBUG(dbgs() << "OPC_Decode for InstA: insn using DecodeInstA : PASS" << '\n');
diff --git a/llvm/test/TableGen/DecoderEmitter/trydecode-emission3.td b/llvm/test/TableGen/DecoderEmitter/trydecode-emission3.td
index 2e2d1ddb8b8f6..7dc5381fc5ff5 100644
--- a/llvm/test/TableGen/DecoderEmitter/trydecode-emission3.td
+++ b/llvm/test/TableGen/DecoderEmitter/trydecode-emission3.td
@@ -44,4 +44,11 @@ def InstB : TestInstruction {
 // CHECK-NEXT:                                 // 14: }
 // CHECK-NEXT: };
 
-// CHECK: if (!Check(S, DecodeInstBOp(MI, tmp, Address, Decoder))) { DecodeComplete = false; return MCDisassembler::Fail; }
+// CHECK-LABEL: case 0:
+// CHECK-NEXT:    tmp = fieldFromInstruction(insn, 0, 2);
+// CHECK-NEXT:    if (!Check(S, DecodeInstBOp(MI, tmp, Address, Decoder))) {
+// CHECK-NEXT:      LLVM_DEBUG(dbgs() << "OPC_Decode for op: field(0,2) using DecodeInstBOp : FAIL" << '\n');
+// CHECK-NEXT:      DecodeComplete = false;
+// CHECK-NEXT:      return MCDisassembler::Fail;
+// CHECK-NEXT:    }
+// CHECK-NEXT:    LLVM_DEBUG(dbgs() << "OPC_Decode for op: field(0,2) using DecodeInstBOp : PASS" << '\n');
diff --git a/llvm/test/TableGen/DecoderEmitter/trydecode-emission4.td b/llvm/test/TableGen/DecoderEmitter/trydecode-emission4.td
index bb0f658a037e8..b7a50ee0d8751 100644
--- a/llvm/test/TableGen/DecoderEmitter/trydecode-emission4.td
+++ b/llvm/test/TableGen/DecoderEmitter/trydecode-emission4.td
@@ -42,4 +42,10 @@ def InstB : TestInstruction {
 // CHECK-NEXT:                                  // 16: }
 // CHECK-NEXT: };
 
-// CHECK: if (!Check(S, DecodeInstB(MI, insn, Address, Decoder))) { DecodeComplete = false; return MCDisassembler::Fail; }
+// CHECK-LABEL: case 0:
+// CHECK-NEXT:    if (!Check(S, DecodeInstB(MI, insn, Address, Decoder))) {
+// CHECK-NEXT:      LLVM_DEBUG(dbgs() << "OPC_Decode for InstB: insn using DecodeInstB : FAIL" << '\n');
+// CHECK-NEXT:      DecodeComplete = false;
+// CHECK-NEXT:      return MCDisassembler::Fail;
+// CHECK-NEXT:    }
+// CHECK-NEXT:    LLVM_DEBUG(dbgs() << "OPC_Decode for InstB: insn using DecodeInstB : PASS" << '\n');
diff --git a/llvm/test/TableGen/RegClassByHwMode.td b/llvm/test/TableGen/RegClassByHwMode.td
index 4f0a71e8bbf71..bd559d1aff882 100644
--- a/llvm/test/TableGen/RegClassByHwMode.td
+++ b/llvm/test/TableGen/RegClassByHwMode.td
@@ -185,19 +185,49 @@ include "Common/RegClassByHwModeCommon.td"
 // DISASM: static DecodeStatus decodeToMCInst(
 // DISASM:   switch (Idx) {
 // DISASM: case 0:
-// DISASM: if (!Check(S, DecodeYRegs_EvenIfRequiredRegClassByHwMode(MI, tmp, Address, Decoder))) { return MCDisassembler::Fail; }
-// DISASM: if (!Check(S, DecodeXRegs_EvenIfRequiredRegClassByHwMode(MI, tmp, Address, Decoder))) { return MCDisassembler::Fail; }
+// DISASM: tmp = fieldFromInstruction(insn, 0, 3);
+// DISASM: if (!Check(S, DecodeYRegs_EvenIfRequiredRegClassByHwMode(MI, tmp, Address, Decoder))) {
+// DISASM: LLVM_DEBUG(dbgs() << "OPC_Decode for dst: field(0,3) using DecodeYRegs_EvenIfRequiredRegClassByHwMode : FAIL" << '\n');
+// DISASM: return MCDisassembler::Fail;
+// DISASM: }
+// DISASM: LLVM_DEBUG(dbgs() << "OPC_Decode for dst: field(0,3) using DecodeYRegs_EvenIfRequiredRegClassByHwMode : PASS" << '\n');
+// DISASM: tmp = fieldFromInstruction(insn, 3, 2);
+// DISASM: if (!Check(S, DecodeXRegs_EvenIfRequiredRegClassByHwMode(MI, tmp, Address, Decoder))) {
+// DISASM: LLVM_DEBUG(dbgs() << "OPC_Decode for src: field(3,2) using DecodeXRegs_EvenIfRequiredRegClassByHwMode : FAIL" << '\n');
+// DISASM: return MCDisassembler::Fail;
+// DISASM: }
+// DISASM: LLVM_DEBUG(dbgs() << "OPC_Decode for src: field(3,2) using DecodeXRegs_EvenIfRequiredRegClassByHwMode : PASS" << '\n');
 
 // DISASM: case 1:
-// DISASM: if (!Check(S, DecodeXRegs_EvenIfRequiredRegClassByHwMode(MI, tmp, Address, Decoder))) { return MCDisassembler::Fail; }
+// DISASM: tmp = fieldFromInstruction(insn, 3, 2);
+// DISASM: if (!Check(S, DecodeXRegs_EvenIfRequiredRegClassByHwMode(MI, tmp, Address, Decoder))) {
+// DISASM: LLVM_DEBUG(dbgs() << "OPC_Decode for src: field(3,2) using DecodeXRegs_EvenIfRequiredRegClassByHwMode : FAIL" << '\n');
+// DISASM: return MCDisassembler::Fail;
+// DISASM: }
+// DISASM: LLVM_DEBUG(dbgs() << "OPC_Decode for src: field(3,2) using DecodeXRegs_EvenIfRequiredRegClassByHwMode : PASS" << '\n');
 
 // DISASM: case 2:
-// DISASM: if (!Check(S, DecodeXRegs_EvenRegisterClass(MI, tmp, Address, Decoder))) { return MCDisassembler::Fail; }
+// DISASM: tmp = fieldFromInstruction(insn, 3, 2);
+// DISASM: if (!Check(S, DecodeXRegs_EvenRegisterClass(MI, tmp, Address, Decoder))) {
+// DISASM: LLVM_DEBUG(dbgs() << "OPC_Decode for src: field(3,2) using DecodeXRegs_EvenRegisterClass : FAIL" << '\n');
+// DISASM: return MCDisassembler::Fail;
+// DISASM: }
+// DISASM: LLVM_DEBUG(dbgs() << "OPC_Decode for src: field(3,2) using DecodeXRegs_EvenRegisterClass : PASS" << '\n');
 // DISASM: case 3:
-// DISASM: if (!Check(S, DecodeXRegsRegisterClass(MI, tmp, Address, Decoder))) { return MCDisassembler::Fail; }
+// DISASM: tmp = fieldFromInstruction(insn, 3, 2);
+// DISASM: if (!Check(S, DecodeXRegsRegisterClass(MI, tmp, Address, Decoder))) {
+// DISASM: LLVM_DEBUG(dbgs() << "OPC_Decode for src: field(3,2) using DecodeXRegsRegisterClass : FAIL" << '\n');
+// DISASM: return MCDisassembler::Fail;
+// DISASM: }
+// DISASM: LLVM_DEBUG(dbgs() << "OPC_Decode for src: field(3,2) using DecodeXRegsRegisterClass : PASS" << '\n');
 
 // DISASM: case 4:
-// DISASM: if (!Check(S, YEvenIfRequiredCustomDecoder(MI, tmp, Address, Decoder))) { return MCDisassembler::Fail; }
+// DISASM: tmp = fieldFromInstruction(insn, 3, 2);
+// DISASM: if (!Check(S, YEvenIfRequiredCustomDecoder(MI, tmp, Address, Decoder))) {
+// DISASM: LLVM_DEBUG(dbgs() << "OPC_Decode for src: field(3,2) using YEvenIfRequiredCustomDecoder : FAIL" << '\n');
+// DISASM: return MCDisassembler::Fail;
+// DISASM: }
+// DISASM: LLVM_DEBUG(dbgs() << "OPC_Decode for src: field(3,2) using YEvenIfRequiredCustomDecoder : PASS" << '\n');
 
 
 // ISEL-SDAG: MatcherTable
diff --git a/llvm/utils/TableGen/DecoderEmitter.cpp b/llvm/utils/TableGen/DecoderEmitter.cpp
index 664c3009ff504..e279ac7c5fe77 100644
--- a/llvm/utils/TableGen/DecoderEmitter.cpp
+++ b/llvm/utils/TableGen/DecoderEmitter.cpp
@@ -683,6 +683,63 @@ static std::vector<EncodingIsland> getIslands(const KnownBits &EncodingBits,
   return Islands;
 }
 
+static std::string getOperandFieldDescForDebug(const OperandInfo &OpInfo) {
+  if (OpInfo.Fields.empty() && !OpInfo.InitValue)
+    return "bits<0>";
+  if (OpInfo.fields().empty())
+    return "imm";
+  std::string S;
+  raw_string_ostream OS(S);
+  ListSeparator LS(" + ");
+  for (const auto &EF : OpInfo.fields())
+    OS << LS << formatv("field({},{})", EF.Base, EF.Width);
+  return S;
+}
+
+/// Emit `if (!Check(...)) { LLVM_DEBUG FAIL; ... } LLVM_DEBUG PASS` for a
+/// decoder call. \p CheckCall is the full expression inside Check(S, …).
+static void emitCheckWithDbgMsg(raw_ostream &OS, indent Indent,
+                                const Twine &CheckCall, const Twine &DebugMsg,
+                                bool SetDecodeCompleteOnFail) {
+  OS << Indent << "if (!Check(S, " << CheckCall << ")) {\n";
+  OS << Indent << "  LLVM_DEBUG(dbgs() << \"";
+  OS << DebugMsg;
+  OS << " : FAIL\" << '\\n');\n";
+  if (SetDecodeCompleteOnFail)
+    OS << Indent << "  DecodeComplete = false;\n";
+  OS << Indent << "  return MCDisassembler::Fail;\n";
+  OS << Indent << "}\n";
+  OS << Indent << "LLVM_DEBUG(dbgs() << \"";
+  OS << DebugMsg;
+  OS << " : PASS\" << '\\n');\n";
+}
+
+/// Operand-level decoder (emitBinaryParser).
+static void emitDecoderCheckWithOpcDebug(raw_ostream &OS, indent Indent,
+                                         const OperandInfo &OpInfo,
+                                         const Twine &CheckCall) {
+  StringRef OpName = OpInfo.Name.empty() ? "opd" : OpInfo.Name;
+  std::string FieldDesc = getOperandFieldDescForDebug(OpInfo);
+  StringRef DecoderName = OpInfo.Decoder;
+
+  emitCheckWithDbgMsg(
+      OS, Indent, CheckCall,
+      formatv("OPC_Decode for {}: {} using {}", OpName, FieldDesc, DecoderName),
+      !OpInfo.HasCompleteDecoder);
+}
+
+/// Whole-instruction \p DecoderMethod. Used when the encoding
+/// has a custom \p DecoderMethod (whole-instruction decoder).
+static void emitDecoderMethodOpcDebug(raw_ostream &OS, indent Indent,
+                                      const InstructionEncoding &Encoding,
+                                      StringRef DecoderMethod) {
+  StringRef EncName = Encoding.getName();
+  emitCheckWithDbgMsg(
+      OS, Indent, formatv("{}(MI, insn, Address, Decoder)", DecoderMethod),
+      formatv("OPC_Decode for {}: insn using {}", EncName, DecoderMethod),
+      !Encoding.hasCompleteDecoder());
+}
+
 static void emitBinaryParser(raw_ostream &OS, indent Indent,
                              const InstructionEncoding &Encoding,
                              const OperandInfo &OpInfo) {
@@ -702,8 +759,8 @@ static void emitBinaryParser(raw_ostream &OS, indent Indent,
     // The operand has no encoding, so the corresponding argument is omitted.
     // This avoids confusion and allows the function to be overloaded if the
     // operand does have an encoding in other instructions.
-    OS << Indent << "if (!Check(S, " << OpInfo.Decoder << "(MI, Decoder)))\n"
-       << Indent << "  return MCDisassembler::Fail;\n";
+    emitDecoderCheckWithOpcDebug(OS, Indent, OpInfo,
+                                 formatv("{}(MI, Decoder)", OpInfo.Decoder));
     return;
   }
 
@@ -738,10 +795,8 @@ static void emitBinaryParser(raw_ostream &OS, indent Indent,
 
   StringRef Decoder = OpInfo.Decoder;
   if (!Decoder.empty()) {
-    OS << Indent << "if (!Check(S, " << Decoder
-       << "(MI, tmp, Address, Decoder))) { "
-       << (OpInfo.HasCompleteDecoder ? "" : "DecodeComplete = false; ")
-       << "return MCDisassembler::Fail; }\n";
+    emitDecoderCheckWithOpcDebug(
+        OS, Indent, OpInfo, formatv("{}(MI, tmp, Address, Decoder)", Decoder));
   } else {
     OS << Indent << "MI.addOperand(MCOperand::createImm(tmp));\n";
   }
@@ -755,10 +810,7 @@ static std::string getDecoderString(const InstructionEncoding &Encoding) {
   // If a custom instruction decoder was specified, use that.
   StringRef DecoderMethod = Encoding.getDecoderMethod();
   if (!DecoderMethod.empty()) {
-    OS << Indent << "if (!Check(S, " << DecoderMethod
-       << "(MI, insn, Address, Decoder))) { "
-       << (Encoding.hasCompleteDecoder() ? "" : "DecodeComplete = false; ")
-       << "return MCDisassembler::Fail; }\n";
+    emitDecoderMethodOpcDebug(OS, Indent, Encoding, DecoderMethod);
   } else {
     for (const OperandInfo &Op : Encoding.getOperands())
       emitBinaryParser(OS, Indent, Encoding, Op);
@@ -1181,11 +1233,12 @@ static DecodeStatus decodeInstruction(const uint8_t DecodeTable[], MCInst &MI,
        << "      makeUp(insn, Len);";
   }
   OS << R"(
+      LLVM_DEBUG(dbgs() << "OPC_Decode: attempting to decode Opcode = " << Opc << " .\n");
       S = decodeToMCInst(DecodeIdx, S, insn, MI, Address, DisAsm,
                          DecodeComplete);
       LLVM_DEBUG(dbgs() << Loc << ": OPC_Decode: opcode " << Opc
-                        << ", using decoder " << DecodeIdx << ": "
-                        << (S ? "PASS, " : "FAIL, "));
+                        << " using decoder " << DecodeIdx
+                        << ": " << (S ? "PASS, " : "FAIL, "));
 
       if (DecodeComplete) {
         LLVM_DEBUG(dbgs() << "decoding complete\n");

>From 2846cc68d4c57edc93112e0cb92317a4a6100634 Mon Sep 17 00:00:00 2001
From: pchaudhuri-nv <pchaudhuri at nvidia.com>
Date: Fri, 1 May 2026 15:09:35 +0000
Subject: [PATCH 2/2] [TableGen] Reduce decoder debug bloat via shared dbgFail
 helper

Refactor the per-operand decode debug emission introduced in the previous
commit so it expands to a call into a shared `dbgFail()` helper rather
than inlining `dbgs() << ...` at every check site. The helper returns
`MCDisassembler::Fail` itself, with an optional `bool *DecodeComplete`
parameter that, when non-null, clears the flag on the fail path. This
lets each emitted check site shrink from a 4-5 line
`if { LLVM_DEBUG; [DecodeComplete = false;] return Fail; }` block down
to a single `if (\!Check(...)) return dbgFail("...");` line, cutting
both `.inc` line count and the amount of duplicated text the C++
compiler has to chew through. The short helper name (`dbgFail` rather
than something like `debugDecodeFail`) shaves ~24 KB off the AArch64
`.inc` and ~50 KB off the AMDGPU `.inc` since the symbol appears at
every emitted fail site (~3,000 sites for AArch64, ~5,000 for AMDGPU).

Also drop two debug prints that don't carry diagnostic value: the
PASS-side per-operand log (FAIL is the only message that helps when
chasing a decoder bug) and the per-attempt "attempting to decode
Opcode" log (redundant with the existing post-decode PASS/FAIL line).
Tests updated to match the new emission.

Final code-bloat impact (entire branch vs main, AArch64 + AMDGPU
disassembler):

Debug + Asserts (CMAKE_BUILD_TYPE=Debug, LLVM_ENABLE_ASSERTIONS=ON)

  +---------+------------+------------+------------+-------------------+
  | Target  | Artifact   |       main |     branch |             Delta |
  +---------+------------+------------+------------+-------------------+
  | AArch64 | .inc bytes |  4,025,238 |  4,318,247 |  +293,009 (+7.3%) |
  | AArch64 | .inc lines |     53,131 |     54,611 |            +1,480 |
  | AArch64 | .a bytes   |  1,592,944 |  1,903,576 | +310,632 (+19.5%) |
  | AArch64 | .o .text   |    379,244 |    508,442 | +129,198 (+34.1%) |
  | AMDGPU  | .inc bytes | 13,208,072 | 13,558,010 |  +349,938 (+2.6%) |
  | AMDGPU  | .inc lines |    176,202 |    176,690 |              +488 |
  | AMDGPU  | .a bytes   |  8,007,582 |  8,333,326 |  +325,744 (+4.1%) |
  | AMDGPU  | .o .text   |  1,608,517 |  1,723,160 |  +114,643 (+7.1%) |
  +---------+------------+------------+------------+-------------------+

Release + Asserts (CMAKE_BUILD_TYPE=Release, LLVM_ENABLE_ASSERTIONS=ON)

  +---------+------------+------------+------------+--------------------+
  | Target  | Artifact   |       main |     branch |              Delta |
  +---------+------------+------------+------------+--------------------+
  | AArch64 | .inc bytes |  4,025,238 |  4,318,247 |   +293,009 (+7.3%) |
  | AArch64 | .a bytes   |    474,080 |  1,055,376 | +581,296 (+122.6%) |
  | AArch64 | .o .text   |    252,674 |    551,093 | +298,419 (+118.1%) |
  | AMDGPU  | .inc bytes | 13,208,072 | 13,558,010 |   +349,938 (+2.6%) |
  | AMDGPU  | .a bytes   |  1,881,442 |  2,547,682 |  +666,240 (+35.4%) |
  | AMDGPU  | .o .text   |  1,059,270 |  1,403,399 |  +344,129 (+32.5%) |
  +---------+------------+------------+------------+--------------------+

The remaining bloat is dominated by the per-fail-path string literal
embedded at every emitted check site (one per operand). It can be made
opt-in by gating the verbose emission behind an existing build flag
(e.g. `EXPENSIVE_CHECKS`) if the residual size cost matters for a
shipped configuration.

Co-Authored-By: Claude Opus 4.7 <noreply at anthropic.com>
---
 .../AArch64/aarch64-disassembler-debug.txt    |  5 ---
 .../TableGen/DecoderEmitter/VarLenDecoder.td  | 30 +++----------
 .../DecoderEmitter/operand-decoder.td         |  6 +--
 .../DecoderEmitter/trydecode-emission.td      |  7 +--
 .../DecoderEmitter/trydecode-emission2.td     | 14 +-----
 .../DecoderEmitter/trydecode-emission3.td     |  7 +--
 .../DecoderEmitter/trydecode-emission4.td     |  7 +--
 llvm/test/TableGen/RegClassByHwMode.td        | 44 ++++++-------------
 llvm/utils/TableGen/DecoderEmitter.cpp        | 28 ++++++------
 9 files changed, 40 insertions(+), 108 deletions(-)

diff --git a/llvm/test/MC/Disassembler/AArch64/aarch64-disassembler-debug.txt b/llvm/test/MC/Disassembler/AArch64/aarch64-disassembler-debug.txt
index 2cbccfbb59c2b..e2ea86cb6c346 100644
--- a/llvm/test/MC/Disassembler/AArch64/aarch64-disassembler-debug.txt
+++ b/llvm/test/MC/Disassembler/AArch64/aarch64-disassembler-debug.txt
@@ -6,17 +6,12 @@
 # DecoderMethod (DecodeAddSubImmShift).
 
 # BLR x0 — per-operand DecodeSimpleRegisterClass with field(5,5)
-# CHECK: OPC_Decode: attempting to decode Opcode = {{[0-9]+}} .
-# CHECK: OPC_Decode for Rn: field(5,5) using DecodeSimpleRegisterClass<AArch64::GPR64RegClassID, 0, 32> : PASS
 # CHECK: OPC_Decode: opcode {{[0-9]+}} using decoder {{[0-9]+}}: PASS, decoding complete
 
 # add x5, x7, #1638 — custom insn decoder from DecoderEmitter::emitDecoderMethodOpcDebug
-# CHECK: OPC_Decode: attempting to decode Opcode = {{[0-9]+}} .
-# CHECK: OPC_Decode for ADDXri: insn using DecodeAddSubImmShift : PASS
 # CHECK: OPC_Decode: opcode {{[0-9]+}} using decoder {{[0-9]+}}: PASS, decoding complete
 
 # nop — decode completes without per-operand / insn-method OPC_Decode lines
-# CHECK: OPC_Decode: attempting to decode Opcode = {{[0-9]+}} .
 # CHECK: OPC_Decode: opcode {{[0-9]+}} using decoder {{[0-9]+}}: PASS, decoding complete
 
 0x0 0x0 0x3f 0xd6
diff --git a/llvm/test/TableGen/DecoderEmitter/VarLenDecoder.td b/llvm/test/TableGen/DecoderEmitter/VarLenDecoder.td
index 697e159cfb0ac..6624f08a6f704 100644
--- a/llvm/test/TableGen/DecoderEmitter/VarLenDecoder.td
+++ b/llvm/test/TableGen/DecoderEmitter/VarLenDecoder.td
@@ -65,37 +65,17 @@ def FOO32 : MyVarInst<MemOp32> {
 
 // CHECK:      case 0:
 // CHECK-NEXT: tmp = fieldFromInstruction(insn, 8, 3);
-// CHECK-NEXT: if (!Check(S, DecodeRegClassRegisterClass(MI, tmp, Address, Decoder))) {
-// CHECK-NEXT:   LLVM_DEBUG(dbgs() << "OPC_Decode for opd: field(8,3) using DecodeRegClassRegisterClass : FAIL" << '\n');
-// CHECK-NEXT:   return MCDisassembler::Fail;
-// CHECK-NEXT: }
-// CHECK-NEXT: LLVM_DEBUG(dbgs() << "OPC_Decode for opd: field(8,3) using DecodeRegClassRegisterClass : PASS" << '\n');
+// CHECK-NEXT: if (!Check(S, DecodeRegClassRegisterClass(MI, tmp, Address, Decoder))) return dbgFail("OPC_Decode for opd: field(8,3) using DecodeRegClassRegisterClass");
 // CHECK-NEXT: tmp = fieldFromInstruction(insn, 0, 3);
-// CHECK-NEXT: if (!Check(S, DecodeRegClassRegisterClass(MI, tmp, Address, Decoder))) {
-// CHECK-NEXT:   LLVM_DEBUG(dbgs() << "OPC_Decode for opd: field(0,3) using DecodeRegClassRegisterClass : FAIL" << '\n');
-// CHECK-NEXT:   return MCDisassembler::Fail;
-// CHECK-NEXT: }
-// CHECK-NEXT: LLVM_DEBUG(dbgs() << "OPC_Decode for opd: field(0,3) using DecodeRegClassRegisterClass : PASS" << '\n');
+// CHECK-NEXT: if (!Check(S, DecodeRegClassRegisterClass(MI, tmp, Address, Decoder))) return dbgFail("OPC_Decode for opd: field(0,3) using DecodeRegClassRegisterClass");
 // CHECK-NEXT: tmp = fieldFromInstruction(insn, 11, 16);
-// CHECK-NEXT: if (!Check(S, myCustomDecoder(MI, tmp, Address, Decoder))) {
-// CHECK-NEXT:   LLVM_DEBUG(dbgs() << "OPC_Decode for opd: field(11,16) using myCustomDecoder : FAIL" << '\n');
-// CHECK-NEXT:   return MCDisassembler::Fail;
-// CHECK-NEXT: }
-// CHECK-NEXT: LLVM_DEBUG(dbgs() << "OPC_Decode for opd: field(11,16) using myCustomDecoder : PASS" << '\n');
+// CHECK-NEXT: if (!Check(S, myCustomDecoder(MI, tmp, Address, Decoder))) return dbgFail("OPC_Decode for opd: field(11,16) using myCustomDecoder");
 // CHECK-NEXT: return S;
 // CHECK-NEXT: case 1:
 // CHECK-NEXT: tmp = fieldFromInstruction(insn, 8, 3);
-// CHECK-NEXT: if (!Check(S, DecodeRegClassRegisterClass(MI, tmp, Address, Decoder))) {
-// CHECK-NEXT:   LLVM_DEBUG(dbgs() << "OPC_Decode for opd: field(8,3) using DecodeRegClassRegisterClass : FAIL" << '\n');
-// CHECK-NEXT:   return MCDisassembler::Fail;
-// CHECK-NEXT: }
-// CHECK-NEXT: LLVM_DEBUG(dbgs() << "OPC_Decode for opd: field(8,3) using DecodeRegClassRegisterClass : PASS" << '\n');
+// CHECK-NEXT: if (!Check(S, DecodeRegClassRegisterClass(MI, tmp, Address, Decoder))) return dbgFail("OPC_Decode for opd: field(8,3) using DecodeRegClassRegisterClass");
 // CHECK-NEXT: tmp = fieldFromInstruction(insn, 0, 3);
-// CHECK-NEXT: if (!Check(S, myCustomDecoder(MI, tmp, Address, Decoder))) {
-// CHECK-NEXT:   LLVM_DEBUG(dbgs() << "OPC_Decode for opd: field(0,3) using myCustomDecoder : FAIL" << '\n');
-// CHECK-NEXT:   return MCDisassembler::Fail;
-// CHECK-NEXT: }
-// CHECK-NEXT: LLVM_DEBUG(dbgs() << "OPC_Decode for opd: field(0,3) using myCustomDecoder : PASS" << '\n');
+// CHECK-NEXT: if (!Check(S, myCustomDecoder(MI, tmp, Address, Decoder))) return dbgFail("OPC_Decode for opd: field(0,3) using myCustomDecoder");
 // CHECK-NEXT: tmp = 0x0;
 // CHECK-NEXT: tmp |= fieldFromInstruction(insn, 11, 16) << 16;
 // CHECK-NEXT: tmp |= fieldFromInstruction(insn, 27, 16);
diff --git a/llvm/test/TableGen/DecoderEmitter/operand-decoder.td b/llvm/test/TableGen/DecoderEmitter/operand-decoder.td
index 35e4288d42076..4049e8a5acd5c 100644
--- a/llvm/test/TableGen/DecoderEmitter/operand-decoder.td
+++ b/llvm/test/TableGen/DecoderEmitter/operand-decoder.td
@@ -12,11 +12,7 @@ def MyTarget : Target {
 }
 
 // CHECK-LABEL: case 0:
-// CHECK-NEXT:    if (!Check(S, DecodeRCRegisterClass(MI, Decoder))) {
-// CHECK-NEXT:      LLVM_DEBUG(dbgs() << "OPC_Decode for op0: bits<0> using DecodeRCRegisterClass : FAIL" << '\n');
-// CHECK-NEXT:      return MCDisassembler::Fail;
-// CHECK-NEXT:    }
-// CHECK-NEXT:    LLVM_DEBUG(dbgs() << "OPC_Decode for op0: bits<0> using DecodeRCRegisterClass : PASS" << '\n');
+// CHECK-NEXT:    if (!Check(S, DecodeRCRegisterClass(MI, Decoder))) return dbgFail("OPC_Decode for op0: bits<0> using DecodeRCRegisterClass");
 // CHECK-NEXT:    tmp = fieldFromInstruction(insn, 2, 4);
 // CHECK-NEXT:    MI.addOperand(MCOperand::createImm(tmp));
 // CHECK-NEXT:    tmp = 0x0;
diff --git a/llvm/test/TableGen/DecoderEmitter/trydecode-emission.td b/llvm/test/TableGen/DecoderEmitter/trydecode-emission.td
index 2f772dfb9e750..2e730cb5a3f08 100644
--- a/llvm/test/TableGen/DecoderEmitter/trydecode-emission.td
+++ b/llvm/test/TableGen/DecoderEmitter/trydecode-emission.td
@@ -44,9 +44,4 @@ def InstB : TestInstruction {
 // CHECK-NEXT: };
 
 // CHECK-LABEL: case 0:
-// CHECK-NEXT:    if (!Check(S, DecodeInstB(MI, insn, Address, Decoder))) {
-// CHECK-NEXT:      LLVM_DEBUG(dbgs() << "OPC_Decode for InstB: insn using DecodeInstB : FAIL" << '\n');
-// CHECK-NEXT:      DecodeComplete = false;
-// CHECK-NEXT:      return MCDisassembler::Fail;
-// CHECK-NEXT:    }
-// CHECK-NEXT:    LLVM_DEBUG(dbgs() << "OPC_Decode for InstB: insn using DecodeInstB : PASS" << '\n');
+// CHECK-NEXT:    if (!Check(S, DecodeInstB(MI, insn, Address, Decoder))) return dbgFail("OPC_Decode for InstB: insn using DecodeInstB", &DecodeComplete);
diff --git a/llvm/test/TableGen/DecoderEmitter/trydecode-emission2.td b/llvm/test/TableGen/DecoderEmitter/trydecode-emission2.td
index 48f33c1d5d586..fd7973c25bc50 100644
--- a/llvm/test/TableGen/DecoderEmitter/trydecode-emission2.td
+++ b/llvm/test/TableGen/DecoderEmitter/trydecode-emission2.td
@@ -43,16 +43,6 @@ def InstB : TestInstruction {
 // CHECK-NEXT: };
 
 // CHECK-LABEL: case 0:
-// CHECK-NEXT:    if (!Check(S, DecodeInstB(MI, insn, Address, Decoder))) {
-// CHECK-NEXT:      LLVM_DEBUG(dbgs() << "OPC_Decode for InstB: insn using DecodeInstB : FAIL" << '\n');
-// CHECK-NEXT:      DecodeComplete = false;
-// CHECK-NEXT:      return MCDisassembler::Fail;
-// CHECK-NEXT:    }
-// CHECK-NEXT:    LLVM_DEBUG(dbgs() << "OPC_Decode for InstB: insn using DecodeInstB : PASS" << '\n');
+// CHECK-NEXT:    if (!Check(S, DecodeInstB(MI, insn, Address, Decoder))) return dbgFail("OPC_Decode for InstB: insn using DecodeInstB", &DecodeComplete);
 // CHECK-LABEL: case 1:
-// CHECK-NEXT:    if (!Check(S, DecodeInstA(MI, insn, Address, Decoder))) {
-// CHECK-NEXT:      LLVM_DEBUG(dbgs() << "OPC_Decode for InstA: insn using DecodeInstA : FAIL" << '\n');
-// CHECK-NEXT:      DecodeComplete = false;
-// CHECK-NEXT:      return MCDisassembler::Fail;
-// CHECK-NEXT:    }
-// CHECK-NEXT:    LLVM_DEBUG(dbgs() << "OPC_Decode for InstA: insn using DecodeInstA : PASS" << '\n');
+// CHECK-NEXT:    if (!Check(S, DecodeInstA(MI, insn, Address, Decoder))) return dbgFail("OPC_Decode for InstA: insn using DecodeInstA", &DecodeComplete);
diff --git a/llvm/test/TableGen/DecoderEmitter/trydecode-emission3.td b/llvm/test/TableGen/DecoderEmitter/trydecode-emission3.td
index 7dc5381fc5ff5..062807b9cb581 100644
--- a/llvm/test/TableGen/DecoderEmitter/trydecode-emission3.td
+++ b/llvm/test/TableGen/DecoderEmitter/trydecode-emission3.td
@@ -46,9 +46,4 @@ def InstB : TestInstruction {
 
 // CHECK-LABEL: case 0:
 // CHECK-NEXT:    tmp = fieldFromInstruction(insn, 0, 2);
-// CHECK-NEXT:    if (!Check(S, DecodeInstBOp(MI, tmp, Address, Decoder))) {
-// CHECK-NEXT:      LLVM_DEBUG(dbgs() << "OPC_Decode for op: field(0,2) using DecodeInstBOp : FAIL" << '\n');
-// CHECK-NEXT:      DecodeComplete = false;
-// CHECK-NEXT:      return MCDisassembler::Fail;
-// CHECK-NEXT:    }
-// CHECK-NEXT:    LLVM_DEBUG(dbgs() << "OPC_Decode for op: field(0,2) using DecodeInstBOp : PASS" << '\n');
+// CHECK-NEXT:    if (!Check(S, DecodeInstBOp(MI, tmp, Address, Decoder))) return dbgFail("OPC_Decode for op: field(0,2) using DecodeInstBOp", &DecodeComplete);
diff --git a/llvm/test/TableGen/DecoderEmitter/trydecode-emission4.td b/llvm/test/TableGen/DecoderEmitter/trydecode-emission4.td
index b7a50ee0d8751..f989b47738347 100644
--- a/llvm/test/TableGen/DecoderEmitter/trydecode-emission4.td
+++ b/llvm/test/TableGen/DecoderEmitter/trydecode-emission4.td
@@ -43,9 +43,4 @@ def InstB : TestInstruction {
 // CHECK-NEXT: };
 
 // CHECK-LABEL: case 0:
-// CHECK-NEXT:    if (!Check(S, DecodeInstB(MI, insn, Address, Decoder))) {
-// CHECK-NEXT:      LLVM_DEBUG(dbgs() << "OPC_Decode for InstB: insn using DecodeInstB : FAIL" << '\n');
-// CHECK-NEXT:      DecodeComplete = false;
-// CHECK-NEXT:      return MCDisassembler::Fail;
-// CHECK-NEXT:    }
-// CHECK-NEXT:    LLVM_DEBUG(dbgs() << "OPC_Decode for InstB: insn using DecodeInstB : PASS" << '\n');
+// CHECK-NEXT:    if (!Check(S, DecodeInstB(MI, insn, Address, Decoder))) return dbgFail("OPC_Decode for InstB: insn using DecodeInstB", &DecodeComplete);
diff --git a/llvm/test/TableGen/RegClassByHwMode.td b/llvm/test/TableGen/RegClassByHwMode.td
index bd559d1aff882..5a66c16bb321b 100644
--- a/llvm/test/TableGen/RegClassByHwMode.td
+++ b/llvm/test/TableGen/RegClassByHwMode.td
@@ -144,6 +144,14 @@ include "Common/RegClassByHwModeCommon.td"
 
 
 
+// DISASM{LITERAL}: [[maybe_unused]] DecodeStatus dbgFail(const char *Msg,
+// DISASM-NEXT:                                       bool *DecodeComplete = nullptr) {
+// DISASM-NEXT:   LLVM_DEBUG(dbgs() << Msg << " : FAIL\n");
+// DISASM-NEXT:   if (DecodeComplete)
+// DISASM-NEXT:     *DecodeComplete = false;
+// DISASM-NEXT:   return MCDisassembler::Fail;
+// DISASM-NEXT: }
+
 // DISASM{LITERAL}: [[maybe_unused]]
 // DISASM-NEXT: static DecodeStatus DecodeMyPtrRCRegClassByHwMode(MCInst &Inst, unsigned Imm, uint64_t Addr, const MCDisassembler *Decoder) {
 // DISASM-NEXT:   switch (Decoder->getSubtargetInfo().getHwMode(MCSubtargetInfo::HwMode_RegInfo)) {
@@ -186,48 +194,24 @@ include "Common/RegClassByHwModeCommon.td"
 // DISASM:   switch (Idx) {
 // DISASM: case 0:
 // DISASM: tmp = fieldFromInstruction(insn, 0, 3);
-// DISASM: if (!Check(S, DecodeYRegs_EvenIfRequiredRegClassByHwMode(MI, tmp, Address, Decoder))) {
-// DISASM: LLVM_DEBUG(dbgs() << "OPC_Decode for dst: field(0,3) using DecodeYRegs_EvenIfRequiredRegClassByHwMode : FAIL" << '\n');
-// DISASM: return MCDisassembler::Fail;
-// DISASM: }
-// DISASM: LLVM_DEBUG(dbgs() << "OPC_Decode for dst: field(0,3) using DecodeYRegs_EvenIfRequiredRegClassByHwMode : PASS" << '\n');
+// DISASM: if (!Check(S, DecodeYRegs_EvenIfRequiredRegClassByHwMode(MI, tmp, Address, Decoder))) return dbgFail("OPC_Decode for dst: field(0,3) using DecodeYRegs_EvenIfRequiredRegClassByHwMode");
 // DISASM: tmp = fieldFromInstruction(insn, 3, 2);
-// DISASM: if (!Check(S, DecodeXRegs_EvenIfRequiredRegClassByHwMode(MI, tmp, Address, Decoder))) {
-// DISASM: LLVM_DEBUG(dbgs() << "OPC_Decode for src: field(3,2) using DecodeXRegs_EvenIfRequiredRegClassByHwMode : FAIL" << '\n');
-// DISASM: return MCDisassembler::Fail;
-// DISASM: }
-// DISASM: LLVM_DEBUG(dbgs() << "OPC_Decode for src: field(3,2) using DecodeXRegs_EvenIfRequiredRegClassByHwMode : PASS" << '\n');
+// DISASM: if (!Check(S, DecodeXRegs_EvenIfRequiredRegClassByHwMode(MI, tmp, Address, Decoder))) return dbgFail("OPC_Decode for src: field(3,2) using DecodeXRegs_EvenIfRequiredRegClassByHwMode");
 
 // DISASM: case 1:
 // DISASM: tmp = fieldFromInstruction(insn, 3, 2);
-// DISASM: if (!Check(S, DecodeXRegs_EvenIfRequiredRegClassByHwMode(MI, tmp, Address, Decoder))) {
-// DISASM: LLVM_DEBUG(dbgs() << "OPC_Decode for src: field(3,2) using DecodeXRegs_EvenIfRequiredRegClassByHwMode : FAIL" << '\n');
-// DISASM: return MCDisassembler::Fail;
-// DISASM: }
-// DISASM: LLVM_DEBUG(dbgs() << "OPC_Decode for src: field(3,2) using DecodeXRegs_EvenIfRequiredRegClassByHwMode : PASS" << '\n');
+// DISASM: if (!Check(S, DecodeXRegs_EvenIfRequiredRegClassByHwMode(MI, tmp, Address, Decoder))) return dbgFail("OPC_Decode for src: field(3,2) using DecodeXRegs_EvenIfRequiredRegClassByHwMode");
 
 // DISASM: case 2:
 // DISASM: tmp = fieldFromInstruction(insn, 3, 2);
-// DISASM: if (!Check(S, DecodeXRegs_EvenRegisterClass(MI, tmp, Address, Decoder))) {
-// DISASM: LLVM_DEBUG(dbgs() << "OPC_Decode for src: field(3,2) using DecodeXRegs_EvenRegisterClass : FAIL" << '\n');
-// DISASM: return MCDisassembler::Fail;
-// DISASM: }
-// DISASM: LLVM_DEBUG(dbgs() << "OPC_Decode for src: field(3,2) using DecodeXRegs_EvenRegisterClass : PASS" << '\n');
+// DISASM: if (!Check(S, DecodeXRegs_EvenRegisterClass(MI, tmp, Address, Decoder))) return dbgFail("OPC_Decode for src: field(3,2) using DecodeXRegs_EvenRegisterClass");
 // DISASM: case 3:
 // DISASM: tmp = fieldFromInstruction(insn, 3, 2);
-// DISASM: if (!Check(S, DecodeXRegsRegisterClass(MI, tmp, Address, Decoder))) {
-// DISASM: LLVM_DEBUG(dbgs() << "OPC_Decode for src: field(3,2) using DecodeXRegsRegisterClass : FAIL" << '\n');
-// DISASM: return MCDisassembler::Fail;
-// DISASM: }
-// DISASM: LLVM_DEBUG(dbgs() << "OPC_Decode for src: field(3,2) using DecodeXRegsRegisterClass : PASS" << '\n');
+// DISASM: if (!Check(S, DecodeXRegsRegisterClass(MI, tmp, Address, Decoder))) return dbgFail("OPC_Decode for src: field(3,2) using DecodeXRegsRegisterClass");
 
 // DISASM: case 4:
 // DISASM: tmp = fieldFromInstruction(insn, 3, 2);
-// DISASM: if (!Check(S, YEvenIfRequiredCustomDecoder(MI, tmp, Address, Decoder))) {
-// DISASM: LLVM_DEBUG(dbgs() << "OPC_Decode for src: field(3,2) using YEvenIfRequiredCustomDecoder : FAIL" << '\n');
-// DISASM: return MCDisassembler::Fail;
-// DISASM: }
-// DISASM: LLVM_DEBUG(dbgs() << "OPC_Decode for src: field(3,2) using YEvenIfRequiredCustomDecoder : PASS" << '\n');
+// DISASM: if (!Check(S, YEvenIfRequiredCustomDecoder(MI, tmp, Address, Decoder))) return dbgFail("OPC_Decode for src: field(3,2) using YEvenIfRequiredCustomDecoder");
 
 
 // ISEL-SDAG: MatcherTable
diff --git a/llvm/utils/TableGen/DecoderEmitter.cpp b/llvm/utils/TableGen/DecoderEmitter.cpp
index e279ac7c5fe77..6879e4017828a 100644
--- a/llvm/utils/TableGen/DecoderEmitter.cpp
+++ b/llvm/utils/TableGen/DecoderEmitter.cpp
@@ -696,22 +696,17 @@ static std::string getOperandFieldDescForDebug(const OperandInfo &OpInfo) {
   return S;
 }
 
-/// Emit `if (!Check(...)) { LLVM_DEBUG FAIL; ... } LLVM_DEBUG PASS` for a
-/// decoder call. \p CheckCall is the full expression inside Check(S, …).
+/// Emit `if (!Check(...)) return dbgFail("..."[, &DecodeComplete]);` for a
+/// decoder call. The helper does the LLVM_DEBUG + return so each call site
+/// stays one line and the macro expansion is shared, not duplicated per site.
 static void emitCheckWithDbgMsg(raw_ostream &OS, indent Indent,
                                 const Twine &CheckCall, const Twine &DebugMsg,
                                 bool SetDecodeCompleteOnFail) {
-  OS << Indent << "if (!Check(S, " << CheckCall << ")) {\n";
-  OS << Indent << "  LLVM_DEBUG(dbgs() << \"";
-  OS << DebugMsg;
-  OS << " : FAIL\" << '\\n');\n";
+  OS << Indent << "if (!Check(S, " << CheckCall << ")) return dbgFail(\""
+     << DebugMsg << "\"";
   if (SetDecodeCompleteOnFail)
-    OS << Indent << "  DecodeComplete = false;\n";
-  OS << Indent << "  return MCDisassembler::Fail;\n";
-  OS << Indent << "}\n";
-  OS << Indent << "LLVM_DEBUG(dbgs() << \"";
-  OS << DebugMsg;
-  OS << " : PASS\" << '\\n');\n";
+    OS << ", &DecodeComplete";
+  OS << ");\n";
 }
 
 /// Operand-level decoder (emitBinaryParser).
@@ -1233,7 +1228,6 @@ static DecodeStatus decodeInstruction(const uint8_t DecodeTable[], MCInst &MI,
        << "      makeUp(insn, Len);";
   }
   OS << R"(
-      LLVM_DEBUG(dbgs() << "OPC_Decode: attempting to decode Opcode = " << Opc << " .\n");
       S = decodeToMCInst(DecodeIdx, S, insn, MI, Address, DisAsm,
                          DecodeComplete);
       LLVM_DEBUG(dbgs() << Loc << ": OPC_Decode: opcode " << Opc
@@ -1570,6 +1564,14 @@ namespace {
 // on their usage.
 template <typename T> constexpr uint32_t InsnBitWidth = 0;
 
+[[maybe_unused]] DecodeStatus dbgFail(const char *Msg,
+                                      bool *DecodeComplete = nullptr) {
+  LLVM_DEBUG(dbgs() << Msg << " : FAIL\n");
+  if (DecodeComplete)
+    *DecodeComplete = false;
+  return MCDisassembler::Fail;
+}
+
 )";
 
   // Do extra bookkeeping for variable-length encodings.



More information about the llvm-commits mailing list