https://github.com/enferex updated https://github.com/llvm/llvm-project/pull/216314
>From 704fcd95bac0059dcd10d95b82e2a3b90b925b0d Mon Sep 17 00:00:00 2001 From: Matt Davis <[email protected]> Date: Thu, 13 Aug 2026 18:36:19 +0000 Subject: [PATCH 1/5] [DWARF] Add DW_OP_NVIDIA_mux vendor extension DW_OP_NVIDIA_mux (0xeb) takes a 1-byte unsigned selector, giving NVIDIA an extension space behind a single opcode in the crowded DW_OP_lo_user..DW_OP_hi_user range. The selector is opaque to LLVM; no NVIDIA operation semantics are defined here. Assissted-by: LLM --- lldb/source/Expression/DWARFExpression.cpp | 1 + llvm/include/llvm/BinaryFormat/Dwarf.def | 7 ++++ llvm/include/llvm/BinaryFormat/Dwarf.h | 1 + .../DWARF/LowLevel/DWARFExpression.cpp | 2 + llvm/unittests/BinaryFormat/DwarfTest.cpp | 11 ++++++ .../DWARFExpressionCompactPrinterTest.cpp | 39 +++++++++++++++++++ 6 files changed, 61 insertions(+) diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp index e62b6945dc3ed..4eb5271f57e73 100644 --- a/lldb/source/Expression/DWARFExpression.cpp +++ b/lldb/source/Expression/DWARFExpression.cpp @@ -214,6 +214,7 @@ GetOpcodeDataSize(const DataExtractor &data, const lldb::offset_t data_offset, case DW_OP_HP_unmod_range: case DW_OP_HP_tls: case DW_OP_INTEL_bit_piece: + case DW_OP_NVIDIA_mux: case DW_OP_WASM_location: case DW_OP_WASM_location_int: case DW_OP_APPLE_uninit: diff --git a/llvm/include/llvm/BinaryFormat/Dwarf.def b/llvm/include/llvm/BinaryFormat/Dwarf.def index 32195aae562c3..885216bdc2de9 100644 --- a/llvm/include/llvm/BinaryFormat/Dwarf.def +++ b/llvm/include/llvm/BinaryFormat/Dwarf.def @@ -920,6 +920,13 @@ HANDLE_DW_OP(0xe5, HP_unmod_range, -1, -1, 0, HP) HANDLE_DW_OP(0xe6, HP_tls, -1, -1, 0, HP) HANDLE_DW_OP(0xe8, INTEL_bit_piece, -1, -1, 0, INTEL) +// Extension for NVIDIA. DW_OP_NVIDIA_mux acts as an extension multiplexer: its +// single 1-byte unsigned operand selects an NVIDIA-specific operation. This +// keeps the cost of the whole NVIDIA extension family at one encoding in the +// scarce DW_OP_lo_user..DW_OP_hi_user range. The selector value is opaque to +// LLVM; no NVIDIA operation semantics are defined here. +HANDLE_DW_OP(0xeb, NVIDIA_mux, 1, -1, 0, NVIDIA) + // Extensions for WebAssembly. HANDLE_DW_OP(0xed, WASM_location, -1, -1, 0, WASM) HANDLE_DW_OP(0xee, WASM_location_int, -1, -1, 0, WASM) diff --git a/llvm/include/llvm/BinaryFormat/Dwarf.h b/llvm/include/llvm/BinaryFormat/Dwarf.h index 46124019206f2..ee7031b239479 100644 --- a/llvm/include/llvm/BinaryFormat/Dwarf.h +++ b/llvm/include/llvm/BinaryFormat/Dwarf.h @@ -83,6 +83,7 @@ enum LLVMConstants : uint32_t { DWARF_VENDOR_HP, DWARF_VENDOR_IBM, DWARF_VENDOR_INTEL, + DWARF_VENDOR_NVIDIA, DWARF_VENDOR_PGI, DWARF_VENDOR_SUN, DWARF_VENDOR_UPC, diff --git a/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFExpression.cpp b/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFExpression.cpp index 61bd6fcb65bfa..654fb459feb37 100644 --- a/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFExpression.cpp +++ b/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFExpression.cpp @@ -103,6 +103,8 @@ static std::vector<Desc> getOpDescriptions() { Descriptions[DW_OP_GNU_entry_value] = Desc(Op::Dwarf4, Op::SizeLEB); Descriptions[DW_OP_GNU_implicit_pointer] = Desc(Op::Dwarf4, Op::SizeRefAddr, Op::SignedSizeLEB); + // The operand is an opaque 8-bit selector for an NVIDIA-specific operation. + Descriptions[DW_OP_NVIDIA_mux] = Desc(Op::Dwarf5, Op::Size1); // This Description acts as a marker that getSubOpDesc must be called // to fetch the final Description for the operation. Each such final // Description must share the same first SizeSubOpLEB operand. diff --git a/llvm/unittests/BinaryFormat/DwarfTest.cpp b/llvm/unittests/BinaryFormat/DwarfTest.cpp index c7e47a15852fd..5975484bd4b19 100644 --- a/llvm/unittests/BinaryFormat/DwarfTest.cpp +++ b/llvm/unittests/BinaryFormat/DwarfTest.cpp @@ -48,6 +48,9 @@ TEST(DwarfTest, getOperationEncoding) { EXPECT_EQ(DW_OP_deref, getOperationEncoding("DW_OP_deref")); EXPECT_EQ(DW_OP_bit_piece, getOperationEncoding("DW_OP_bit_piece")); + // A vendor extension. + EXPECT_EQ(DW_OP_NVIDIA_mux, getOperationEncoding("DW_OP_NVIDIA_mux")); + // Invalid ops. EXPECT_EQ(0u, getOperationEncoding("DW_OP_otherthings")); EXPECT_EQ(0u, getOperationEncoding("other")); @@ -65,6 +68,14 @@ TEST(DwarfTest, SubOperationEncoding) { getSubOperationEncoding(DW_OP_LLVM_user, "DW_OP_LLVM_nop")); } +TEST(DwarfTest, OperationEncodingString) { + // A vendor extension. + EXPECT_EQ("DW_OP_NVIDIA_mux", OperationEncodingString(DW_OP_NVIDIA_mux)); + + // Unassigned encodings in the vendor range shouldn't be stringified. + EXPECT_EQ(StringRef(), OperationEncodingString(DW_OP_hi_user)); +} + TEST(DwarfTest, LanguageStringOnInvalid) { // This is invalid, so it shouldn't be stringified. EXPECT_EQ(StringRef(), LanguageString(0)); diff --git a/llvm/unittests/DebugInfo/DWARF/DWARFExpressionCompactPrinterTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFExpressionCompactPrinterTest.cpp index e328f0ffa8809..a853a22360c95 100644 --- a/llvm/unittests/DebugInfo/DWARF/DWARFExpressionCompactPrinterTest.cpp +++ b/llvm/unittests/DebugInfo/DWARF/DWARFExpressionCompactPrinterTest.cpp @@ -208,6 +208,45 @@ TEST_F(DWARFExpressionCompactPrinterTest, Test_OP_LLVM_user_unknown_subop) { "DW_OP_LLVM_form_aspace_address (2)>"); } +// DW_OP_NVIDIA_mux carries an opaque 8-bit selector, so the compact printer +// cannot know its stack effect and must bail out naming the opcode. +TEST_F(DWARFExpressionCompactPrinterTest, Test_OP_NVIDIA_mux) { + TestExprPrinterFailure({DW_OP_NVIDIA_mux, 0xa5}, + "<unknown op DW_OP_NVIDIA_mux (235)>"); +} + +// The selector is a fixed 1-byte operand, so the full printer must consume +// exactly one byte and print its value. +TEST(NVIDIAMux, Full_DW_OP_NVIDIA_mux) { + const uint8_t Enc[] = {DW_OP_NVIDIA_mux, 0xa5}; + + std::string Result; + raw_string_ostream OS(Result); + DataExtractor DE(Enc, true); + DWARFExpression Expr(DE, 8); + + DIDumpOptions DumpOpts; + printDwarfExpression(&Expr, OS, DumpOpts, nullptr); + + EXPECT_EQ(OS.str(), "DW_OP_NVIDIA_mux 0xa5"); +} + +// A trailing operation must still decode, proving the selector advanced the +// offset by exactly one byte. +TEST(NVIDIAMux, Full_DW_OP_NVIDIA_mux_TrailingOp) { + const uint8_t Enc[] = {DW_OP_NVIDIA_mux, 0xa5, DW_OP_stack_value}; + + std::string Result; + raw_string_ostream OS(Result); + DataExtractor DE(Enc, true); + DWARFExpression Expr(DE, 8); + + DIDumpOptions DumpOpts; + printDwarfExpression(&Expr, OS, DumpOpts, nullptr); + + EXPECT_EQ(OS.str(), "DW_OP_NVIDIA_mux 0xa5, DW_OP_stack_value"); +} + // NVPTX packs virtual register names into DWARF register numbers, so compact // printing without a callback must recover the name and return true. TEST(NVPTXPackedRegister, Compact_DW_OP_regx_NoMRI) { >From 8ad3f7fd1209302226129bb4dacb76ec00f1999c Mon Sep 17 00:00:00 2001 From: Matt Davis <[email protected]> Date: Mon, 17 Aug 2026 22:05:49 +0000 Subject: [PATCH 2/5] [DWARF] Move the NVIDIA mux under DW_OP_LLVM_user Assisted-by: LLM --- lldb/source/Expression/DWARFExpression.cpp | 1 - llvm/include/llvm/BinaryFormat/Dwarf.def | 13 ++++---- llvm/include/llvm/BinaryFormat/Dwarf.h | 1 - .../DWARF/LowLevel/DWARFExpression.cpp | 5 +-- llvm/unittests/BinaryFormat/DwarfTest.cpp | 11 ------- .../DWARFExpressionCompactPrinterTest.cpp | 32 ++++++++++--------- 6 files changed, 26 insertions(+), 37 deletions(-) diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp index 4eb5271f57e73..e62b6945dc3ed 100644 --- a/lldb/source/Expression/DWARFExpression.cpp +++ b/lldb/source/Expression/DWARFExpression.cpp @@ -214,7 +214,6 @@ GetOpcodeDataSize(const DataExtractor &data, const lldb::offset_t data_offset, case DW_OP_HP_unmod_range: case DW_OP_HP_tls: case DW_OP_INTEL_bit_piece: - case DW_OP_NVIDIA_mux: case DW_OP_WASM_location: case DW_OP_WASM_location_int: case DW_OP_APPLE_uninit: diff --git a/llvm/include/llvm/BinaryFormat/Dwarf.def b/llvm/include/llvm/BinaryFormat/Dwarf.def index 885216bdc2de9..84fb1b97152d4 100644 --- a/llvm/include/llvm/BinaryFormat/Dwarf.def +++ b/llvm/include/llvm/BinaryFormat/Dwarf.def @@ -920,13 +920,6 @@ HANDLE_DW_OP(0xe5, HP_unmod_range, -1, -1, 0, HP) HANDLE_DW_OP(0xe6, HP_tls, -1, -1, 0, HP) HANDLE_DW_OP(0xe8, INTEL_bit_piece, -1, -1, 0, INTEL) -// Extension for NVIDIA. DW_OP_NVIDIA_mux acts as an extension multiplexer: its -// single 1-byte unsigned operand selects an NVIDIA-specific operation. This -// keeps the cost of the whole NVIDIA extension family at one encoding in the -// scarce DW_OP_lo_user..DW_OP_hi_user range. The selector value is opaque to -// LLVM; no NVIDIA operation semantics are defined here. -HANDLE_DW_OP(0xeb, NVIDIA_mux, 1, -1, 0, NVIDIA) - // Extensions for WebAssembly. HANDLE_DW_OP(0xed, WASM_location, -1, -1, 0, WASM) HANDLE_DW_OP(0xee, WASM_location_int, -1, -1, 0, WASM) @@ -970,6 +963,12 @@ HANDLE_DW_OP_LLVM_USEROP(0x0009, aspace_bregx) HANDLE_DW_OP_LLVM_USEROP(0x000a, piece_end) HANDLE_DW_OP_LLVM_USEROP(0x000b, extend) HANDLE_DW_OP_LLVM_USEROP(0x000c, select_bit_piece) +// The DW_OP_LLVM_NVIDIA_mux operation takes a single ULEB128 operand that +// selects an NVIDIA-specific operation. It acts as a second-level multiplexer, +// giving NVIDIA a private encoding space without requiring every NVIDIA +// operation to be registered here. The selector is opaque to LLVM; no NVIDIA +// operation semantics are defined here. +HANDLE_DW_OP_LLVM_USEROP(0x000d, NVIDIA_mux) // DWARF languages. HANDLE_DW_LANG(0x0001, C89, 0, 2, DWARF) diff --git a/llvm/include/llvm/BinaryFormat/Dwarf.h b/llvm/include/llvm/BinaryFormat/Dwarf.h index ee7031b239479..46124019206f2 100644 --- a/llvm/include/llvm/BinaryFormat/Dwarf.h +++ b/llvm/include/llvm/BinaryFormat/Dwarf.h @@ -83,7 +83,6 @@ enum LLVMConstants : uint32_t { DWARF_VENDOR_HP, DWARF_VENDOR_IBM, DWARF_VENDOR_INTEL, - DWARF_VENDOR_NVIDIA, DWARF_VENDOR_PGI, DWARF_VENDOR_SUN, DWARF_VENDOR_UPC, diff --git a/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFExpression.cpp b/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFExpression.cpp index 654fb459feb37..06bfd55da4582 100644 --- a/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFExpression.cpp +++ b/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFExpression.cpp @@ -103,8 +103,6 @@ static std::vector<Desc> getOpDescriptions() { Descriptions[DW_OP_GNU_entry_value] = Desc(Op::Dwarf4, Op::SizeLEB); Descriptions[DW_OP_GNU_implicit_pointer] = Desc(Op::Dwarf4, Op::SizeRefAddr, Op::SignedSizeLEB); - // The operand is an opaque 8-bit selector for an NVIDIA-specific operation. - Descriptions[DW_OP_NVIDIA_mux] = Desc(Op::Dwarf5, Op::Size1); // This Description acts as a marker that getSubOpDesc must be called // to fetch the final Description for the operation. Each such final // Description must share the same first SizeSubOpLEB operand. @@ -149,6 +147,9 @@ static std::vector<Desc> getSubOpDescriptions() { Desc(Op::Dwarf5, Op::SizeSubOpLEB, Op::SizeLEB, Op::SizeLEB); Descriptions[DW_OP_LLVM_select_bit_piece] = Desc(Op::Dwarf5, Op::SizeSubOpLEB, Op::SizeLEB, Op::SizeLEB); + // The operand is an opaque selector for an NVIDIA-specific operation. + Descriptions[DW_OP_LLVM_NVIDIA_mux] = + Desc(Op::Dwarf5, Op::SizeSubOpLEB, Op::SizeLEB); return Descriptions; } diff --git a/llvm/unittests/BinaryFormat/DwarfTest.cpp b/llvm/unittests/BinaryFormat/DwarfTest.cpp index 5975484bd4b19..c7e47a15852fd 100644 --- a/llvm/unittests/BinaryFormat/DwarfTest.cpp +++ b/llvm/unittests/BinaryFormat/DwarfTest.cpp @@ -48,9 +48,6 @@ TEST(DwarfTest, getOperationEncoding) { EXPECT_EQ(DW_OP_deref, getOperationEncoding("DW_OP_deref")); EXPECT_EQ(DW_OP_bit_piece, getOperationEncoding("DW_OP_bit_piece")); - // A vendor extension. - EXPECT_EQ(DW_OP_NVIDIA_mux, getOperationEncoding("DW_OP_NVIDIA_mux")); - // Invalid ops. EXPECT_EQ(0u, getOperationEncoding("DW_OP_otherthings")); EXPECT_EQ(0u, getOperationEncoding("other")); @@ -68,14 +65,6 @@ TEST(DwarfTest, SubOperationEncoding) { getSubOperationEncoding(DW_OP_LLVM_user, "DW_OP_LLVM_nop")); } -TEST(DwarfTest, OperationEncodingString) { - // A vendor extension. - EXPECT_EQ("DW_OP_NVIDIA_mux", OperationEncodingString(DW_OP_NVIDIA_mux)); - - // Unassigned encodings in the vendor range shouldn't be stringified. - EXPECT_EQ(StringRef(), OperationEncodingString(DW_OP_hi_user)); -} - TEST(DwarfTest, LanguageStringOnInvalid) { // This is invalid, so it shouldn't be stringified. EXPECT_EQ(StringRef(), LanguageString(0)); diff --git a/llvm/unittests/DebugInfo/DWARF/DWARFExpressionCompactPrinterTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFExpressionCompactPrinterTest.cpp index a853a22360c95..6c5cd6f9fc388 100644 --- a/llvm/unittests/DebugInfo/DWARF/DWARFExpressionCompactPrinterTest.cpp +++ b/llvm/unittests/DebugInfo/DWARF/DWARFExpressionCompactPrinterTest.cpp @@ -208,17 +208,17 @@ TEST_F(DWARFExpressionCompactPrinterTest, Test_OP_LLVM_user_unknown_subop) { "DW_OP_LLVM_form_aspace_address (2)>"); } -// DW_OP_NVIDIA_mux carries an opaque 8-bit selector, so the compact printer -// cannot know its stack effect and must bail out naming the opcode. -TEST_F(DWARFExpressionCompactPrinterTest, Test_OP_NVIDIA_mux) { - TestExprPrinterFailure({DW_OP_NVIDIA_mux, 0xa5}, - "<unknown op DW_OP_NVIDIA_mux (235)>"); +// DW_OP_LLVM_NVIDIA_mux carries an opaque selector, so the compact printer +// cannot know its stack effect and must bail out naming both opcodes. +TEST_F(DWARFExpressionCompactPrinterTest, Test_OP_LLVM_NVIDIA_mux) { + TestExprPrinterFailure({DW_OP_LLVM_user, DW_OP_LLVM_NVIDIA_mux, 0xa5, 0x01}, + "<unknown op DW_OP_LLVM_user (233) subop " + "DW_OP_LLVM_NVIDIA_mux (13)>"); } -// The selector is a fixed 1-byte operand, so the full printer must consume -// exactly one byte and print its value. -TEST(NVIDIAMux, Full_DW_OP_NVIDIA_mux) { - const uint8_t Enc[] = {DW_OP_NVIDIA_mux, 0xa5}; +// The selector is a ULEB128 operand, so 165 encodes as two bytes. +TEST(NVIDIAMux, Full_DW_OP_LLVM_NVIDIA_mux) { + const uint8_t Enc[] = {DW_OP_LLVM_user, DW_OP_LLVM_NVIDIA_mux, 0xa5, 0x01}; std::string Result; raw_string_ostream OS(Result); @@ -228,13 +228,14 @@ TEST(NVIDIAMux, Full_DW_OP_NVIDIA_mux) { DIDumpOptions DumpOpts; printDwarfExpression(&Expr, OS, DumpOpts, nullptr); - EXPECT_EQ(OS.str(), "DW_OP_NVIDIA_mux 0xa5"); + EXPECT_EQ(OS.str(), "DW_OP_LLVM_user DW_OP_LLVM_NVIDIA_mux 0xa5"); } -// A trailing operation must still decode, proving the selector advanced the -// offset by exactly one byte. -TEST(NVIDIAMux, Full_DW_OP_NVIDIA_mux_TrailingOp) { - const uint8_t Enc[] = {DW_OP_NVIDIA_mux, 0xa5, DW_OP_stack_value}; +// A trailing operation must still decode, proving the selector consumed +// the bytes of its ULEB128 operand encoding. +TEST(NVIDIAMux, Full_DW_OP_LLVM_NVIDIA_mux_TrailingOp) { + const uint8_t Enc[] = {DW_OP_LLVM_user, DW_OP_LLVM_NVIDIA_mux, 0xa5, 0x01, + DW_OP_stack_value}; std::string Result; raw_string_ostream OS(Result); @@ -244,7 +245,8 @@ TEST(NVIDIAMux, Full_DW_OP_NVIDIA_mux_TrailingOp) { DIDumpOptions DumpOpts; printDwarfExpression(&Expr, OS, DumpOpts, nullptr); - EXPECT_EQ(OS.str(), "DW_OP_NVIDIA_mux 0xa5, DW_OP_stack_value"); + EXPECT_EQ(OS.str(), + "DW_OP_LLVM_user DW_OP_LLVM_NVIDIA_mux 0xa5, DW_OP_stack_value"); } // NVPTX packs virtual register names into DWARF register numbers, so compact >From 428ca402f158cfb39916474030f9a87fff8835e0 Mon Sep 17 00:00:00 2001 From: Matt Davis <[email protected]> Date: Thu, 20 Aug 2026 21:36:11 +0000 Subject: [PATCH 3/5] Clean up a comment in DWARFExpressionCompactPrinterTest.cpp --- .../DebugInfo/DWARF/DWARFExpressionCompactPrinterTest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/unittests/DebugInfo/DWARF/DWARFExpressionCompactPrinterTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFExpressionCompactPrinterTest.cpp index 6c5cd6f9fc388..2ac3c69a3d1fd 100644 --- a/llvm/unittests/DebugInfo/DWARF/DWARFExpressionCompactPrinterTest.cpp +++ b/llvm/unittests/DebugInfo/DWARF/DWARFExpressionCompactPrinterTest.cpp @@ -232,7 +232,7 @@ TEST(NVIDIAMux, Full_DW_OP_LLVM_NVIDIA_mux) { } // A trailing operation must still decode, proving the selector consumed -// the bytes of its ULEB128 operand encoding. +// the bytes of its ULEB128 encoding. TEST(NVIDIAMux, Full_DW_OP_LLVM_NVIDIA_mux_TrailingOp) { const uint8_t Enc[] = {DW_OP_LLVM_user, DW_OP_LLVM_NVIDIA_mux, 0xa5, 0x01, DW_OP_stack_value}; >From e682e20405a705878c5cf15f083510cf475400bc Mon Sep 17 00:00:00 2001 From: Matt Davis <[email protected]> Date: Fri, 21 Aug 2026 17:48:15 +0000 Subject: [PATCH 4/5] [DWARF] Refuse to decode an unknown DW_OP_LLVM_NVIDIA_mux selector The ULEB128 selector operand of DW_OP_LLVM_NVIDIA_mux names an NVIDIA specific operation, and the number and type of the operands that follow it are implied by that operation. Treating the selector as the whole of the operation assumed it is always followed by nothing, which would mis-parse every later operation in the expression once a selector carrying operands exists. No NVIDIA operation is known here, so where such an operation ends cannot be determined. Read the selector and then stop decoding, the same way an unrecognized DW_OP_WASM_location kind is handled: llvm-dwarfdump reports <decoding error> and dumps the remaining bytes rather than parsing them from a guessed offset. A build that knows a selector can decode its operands in the new NvidiaMuxArg case. The compact printer is unaffected. It reads the sub-opcode before decoding fails, so it still names both operations before bailing out. --- llvm/include/llvm/BinaryFormat/Dwarf.def | 8 ++++++-- .../DebugInfo/DWARF/LowLevel/DWARFExpression.h | 5 +++++ .../DWARF/LowLevel/DWARFExpression.cpp | 13 +++++++++++-- .../DWARF/DWARFExpressionCompactPrinterTest.cpp | 17 +++++++++-------- 4 files changed, 31 insertions(+), 12 deletions(-) diff --git a/llvm/include/llvm/BinaryFormat/Dwarf.def b/llvm/include/llvm/BinaryFormat/Dwarf.def index 84fb1b97152d4..bbfe70016ac4d 100644 --- a/llvm/include/llvm/BinaryFormat/Dwarf.def +++ b/llvm/include/llvm/BinaryFormat/Dwarf.def @@ -966,8 +966,12 @@ HANDLE_DW_OP_LLVM_USEROP(0x000c, select_bit_piece) // The DW_OP_LLVM_NVIDIA_mux operation takes a single ULEB128 operand that // selects an NVIDIA-specific operation. It acts as a second-level multiplexer, // giving NVIDIA a private encoding space without requiring every NVIDIA -// operation to be registered here. The selector is opaque to LLVM; no NVIDIA -// operation semantics are defined here. +// operation to be registered here. +// +// The number and type of the operands that follow the selector are implied by +// it, so an unrecognized selector cannot be skipped. No NVIDIA operation is +// known here, so an expression using one is reported as undecodable rather +// than parsed from a guessed offset. HANDLE_DW_OP_LLVM_USEROP(0x000d, NVIDIA_mux) // DWARF languages. diff --git a/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFExpression.h b/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFExpression.h index fcc6f3b1f7397..b9fd2ef29e892 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFExpression.h +++ b/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFExpression.h @@ -47,6 +47,11 @@ class DWARFExpression { /// for the first operand of an operation. SizeSubOpLEB = 9, WasmLocationArg = 30, + /// The operand is a ULEB128 encoded selector naming an NVIDIA specific + /// operation. The number and type of any operands that follow are + /// implied by the selector, so an unrecognized one cannot be skipped + /// and stops decoding. + NvidiaMuxArg = 31, SignBit = 0x80, SignedSize1 = SignBit | Size1, SignedSize2 = SignBit | Size2, diff --git a/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFExpression.cpp b/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFExpression.cpp index 06bfd55da4582..a0b5c42fa78cb 100644 --- a/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFExpression.cpp +++ b/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFExpression.cpp @@ -147,9 +147,8 @@ static std::vector<Desc> getSubOpDescriptions() { Desc(Op::Dwarf5, Op::SizeSubOpLEB, Op::SizeLEB, Op::SizeLEB); Descriptions[DW_OP_LLVM_select_bit_piece] = Desc(Op::Dwarf5, Op::SizeSubOpLEB, Op::SizeLEB, Op::SizeLEB); - // The operand is an opaque selector for an NVIDIA-specific operation. Descriptions[DW_OP_LLVM_NVIDIA_mux] = - Desc(Op::Dwarf5, Op::SizeSubOpLEB, Op::SizeLEB); + Desc(Op::Dwarf5, Op::SizeSubOpLEB, Op::NvidiaMuxArg); return Descriptions; } @@ -223,6 +222,16 @@ bool DWARFExpression::Operation::extract(DataExtractor Data, case Operation::BaseTypeRef: Operands[Operand] = Data.getULEB128(&Offset); break; + case Operation::NvidiaMuxArg: + assert(Operand == 1); + Operands[Operand] = Data.getULEB128(&Offset); + // The selector names an NVIDIA specific operation, and the number and + // type of the operands that follow it are implied by that operation. + // No NVIDIA operation is known here, so where this operation ends is + // unknown and anything after it would be parsed from the wrong offset. + // Refuse to decode rather than mis-parse the rest of the expression. + // A build that knows a selector can decode its operands here. + return false; case Operation::WasmLocationArg: assert(Operand == 1); switch (Operands[0]) { diff --git a/llvm/unittests/DebugInfo/DWARF/DWARFExpressionCompactPrinterTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFExpressionCompactPrinterTest.cpp index 2ac3c69a3d1fd..cde594abcb1cc 100644 --- a/llvm/unittests/DebugInfo/DWARF/DWARFExpressionCompactPrinterTest.cpp +++ b/llvm/unittests/DebugInfo/DWARF/DWARFExpressionCompactPrinterTest.cpp @@ -216,8 +216,9 @@ TEST_F(DWARFExpressionCompactPrinterTest, Test_OP_LLVM_NVIDIA_mux) { "DW_OP_LLVM_NVIDIA_mux (13)>"); } -// The selector is a ULEB128 operand, so 165 encodes as two bytes. -TEST(NVIDIAMux, Full_DW_OP_LLVM_NVIDIA_mux) { +// No NVIDIA operation is known here, so the selector cannot be resolved and +// decoding must stop rather than guess how long the operation is. +TEST(NVIDIAMux, Full_DW_OP_LLVM_NVIDIA_mux_UnknownSelector) { const uint8_t Enc[] = {DW_OP_LLVM_user, DW_OP_LLVM_NVIDIA_mux, 0xa5, 0x01}; std::string Result; @@ -228,12 +229,13 @@ TEST(NVIDIAMux, Full_DW_OP_LLVM_NVIDIA_mux) { DIDumpOptions DumpOpts; printDwarfExpression(&Expr, OS, DumpOpts, nullptr); - EXPECT_EQ(OS.str(), "DW_OP_LLVM_user DW_OP_LLVM_NVIDIA_mux 0xa5"); + EXPECT_EQ(OS.str(), "<decoding error> e9 0d a5 01"); } -// A trailing operation must still decode, proving the selector consumed -// the bytes of its ULEB128 encoding. -TEST(NVIDIAMux, Full_DW_OP_LLVM_NVIDIA_mux_TrailingOp) { +// An unknown selector may imply operands of its own, so nothing after it can +// be located. A trailing operation must be reported as undecoded bytes rather +// than parsed from a guessed offset. +TEST(NVIDIAMux, Full_DW_OP_LLVM_NVIDIA_mux_TrailingOpNotDecoded) { const uint8_t Enc[] = {DW_OP_LLVM_user, DW_OP_LLVM_NVIDIA_mux, 0xa5, 0x01, DW_OP_stack_value}; @@ -245,8 +247,7 @@ TEST(NVIDIAMux, Full_DW_OP_LLVM_NVIDIA_mux_TrailingOp) { DIDumpOptions DumpOpts; printDwarfExpression(&Expr, OS, DumpOpts, nullptr); - EXPECT_EQ(OS.str(), - "DW_OP_LLVM_user DW_OP_LLVM_NVIDIA_mux 0xa5, DW_OP_stack_value"); + EXPECT_EQ(OS.str(), "<decoding error> e9 0d a5 01 9f"); } // NVPTX packs virtual register names into DWARF register numbers, so compact >From ca5ba8372d97e80c3d86f9261439e8d8147a0f10 Mon Sep 17 00:00:00 2001 From: Matt Davis <[email protected]> Date: Tue, 25 Aug 2026 11:22:41 +0000 Subject: [PATCH 5/5] [DWARF] Test malformed DW_OP_LLVM_NVIDIA_mux selectors Cover a selector whose ULEB128 encoding runs off the end of the expression, and one that is missing entirely. Both are reported as undecodable rather than accepted as a complete operation. Also, update comments in LowLevel/DWARFExpression.h --- .../DWARF/LowLevel/DWARFExpression.h | 9 ++++-- .../DWARFExpressionCompactPrinterTest.cpp | 31 +++++++++++++++++++ 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFExpression.h b/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFExpression.h index b9fd2ef29e892..2bf448fb21708 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFExpression.h +++ b/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFExpression.h @@ -27,9 +27,12 @@ class DWARFExpression { /// This class represents an Operation in the Expression. /// - /// An Operation can be in Error state (check with isError()). This - /// means that it couldn't be decoded successfully and if it is the - /// case, all others fields contain undefined values. + /// An Operation can be in Error state (check with isError()). This means + /// that it couldn't be decoded successfully. Some fields stay valid so that + /// a caller can report or copy the bytes it could not decode: getCode(), + /// getDescription(), getEndOffset(), which is the offset the operation + /// started at, and in some cases getSubCode(). The remaining operand values + /// are undefined. class Operation { public: /// Size and signedness of expression operations' operands. diff --git a/llvm/unittests/DebugInfo/DWARF/DWARFExpressionCompactPrinterTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFExpressionCompactPrinterTest.cpp index cde594abcb1cc..49d02a4004c1c 100644 --- a/llvm/unittests/DebugInfo/DWARF/DWARFExpressionCompactPrinterTest.cpp +++ b/llvm/unittests/DebugInfo/DWARF/DWARFExpressionCompactPrinterTest.cpp @@ -250,6 +250,37 @@ TEST(NVIDIAMux, Full_DW_OP_LLVM_NVIDIA_mux_TrailingOpNotDecoded) { EXPECT_EQ(OS.str(), "<decoding error> e9 0d a5 01 9f"); } +// A selector whose ULEB128 encoding runs off the end of the expression must +// be reported, not silently accepted as a complete operation. +TEST(NVIDIAMux, Full_DW_OP_LLVM_NVIDIA_mux_TruncatedSelector) { + const uint8_t Enc[] = {DW_OP_LLVM_user, DW_OP_LLVM_NVIDIA_mux, 0xa5}; + + std::string Result; + raw_string_ostream OS(Result); + DataExtractor DE(Enc, true); + DWARFExpression Expr(DE, 8); + + DIDumpOptions DumpOpts; + printDwarfExpression(&Expr, OS, DumpOpts, nullptr); + + EXPECT_EQ(OS.str(), "<decoding error> e9 0d a5"); +} + +// The selector may also be missing entirely. +TEST(NVIDIAMux, Full_DW_OP_LLVM_NVIDIA_mux_MissingSelector) { + const uint8_t Enc[] = {DW_OP_LLVM_user, DW_OP_LLVM_NVIDIA_mux}; + + std::string Result; + raw_string_ostream OS(Result); + DataExtractor DE(Enc, true); + DWARFExpression Expr(DE, 8); + + DIDumpOptions DumpOpts; + printDwarfExpression(&Expr, OS, DumpOpts, nullptr); + + EXPECT_EQ(OS.str(), "<decoding error> e9 0d"); +} + // NVPTX packs virtual register names into DWARF register numbers, so compact // printing without a callback must recover the name and return true. TEST(NVPTXPackedRegister, Compact_DW_OP_regx_NoMRI) { _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
