[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