diff --git a/lldb/include/lldb/Core/Disassembler.h b/lldb/include/lldb/Core/Disassembler.h index 50a5d87835844..cefddcc20fac2 100644 --- a/lldb/include/lldb/Core/Disassembler.h +++ b/lldb/include/lldb/Core/Disassembler.h @@ -159,7 +159,8 @@ class Instruction { const SymbolContext *sym_ctx, const SymbolContext *prev_sym_ctx, const FormatEntity::Entry *disassembly_addr_format, - size_t max_address_text_size); + size_t max_address_text_size, + bool enable_rich_annotations = false); virtual bool DoesBranch() = 0; @@ -169,7 +170,7 @@ class Instruction { virtual bool IsAuthenticated() = 0; - bool CanSetBreakpoint (); + bool CanSetBreakpoint(); virtual size_t Decode(const Disassembler &disassembler, const DataExtractor &data, @@ -282,7 +283,7 @@ std::function FetchImmOp(int64_t &imm); std::function MatchOpType(Instruction::Operand::Type type); -} +} // namespace OperandMatchers class InstructionList { public: @@ -316,20 +317,19 @@ class InstructionList { /// @param[in] ignore_calls /// It true, then fine the first branch instruction that isn't /// a function call (a branch that calls and returns to the next - /// instruction). If false, find the instruction index of any + /// instruction). If false, find the instruction index of any /// branch in the list. - /// + /// /// @param[out] found_calls - /// If non-null, this will be set to true if any calls were found in + /// If non-null, this will be set to true if any calls were found in /// extending the range. - /// + /// /// @return /// The instruction index of the first branch that is at or past - /// \a start. Returns UINT32_MAX if no matching branches are + /// \a start. Returns UINT32_MAX if no matching branches are /// found. //------------------------------------------------------------------ - uint32_t GetIndexOfNextBranchInstruction(uint32_t start, - bool ignore_calls, + uint32_t GetIndexOfNextBranchInstruction(uint32_t start, bool ignore_calls, bool *found_calls) const; uint32_t GetIndexOfInstructionAtLoadAddress(lldb::addr_t load_addr, @@ -445,10 +445,11 @@ class Disassembler : public std::enable_shared_from_this, const ExecutionContext &exe_ctx, const Address &start, Limit limit, bool mixed_source_and_assembly, uint32_t num_mixed_context_lines, uint32_t options, - Stream &strm); + Stream &strm, bool enable_rich_annotations = false); static bool Disassemble(Debugger &debugger, const ArchSpec &arch, - StackFrame &frame, Stream &strm); + StackFrame &frame, Stream &strm, + bool enable_rich_annotations = false); // Constructors and Destructors Disassembler(const ArchSpec &arch, const char *flavor); @@ -458,7 +459,7 @@ class Disassembler : public std::enable_shared_from_this, const ExecutionContext &exe_ctx, bool mixed_source_and_assembly, uint32_t num_mixed_context_lines, uint32_t options, - Stream &strm); + Stream &strm, bool enable_rich_annotations = false); size_t ParseInstructions(Target &target, Address address, Limit limit, Stream *error_strm_ptr, diff --git a/lldb/include/lldb/Expression/DWARFExpression.h b/lldb/include/lldb/Expression/DWARFExpression.h index 8fcc5d37b91c9..1f8464cf5cf85 100644 --- a/lldb/include/lldb/Expression/DWARFExpression.h +++ b/lldb/include/lldb/Expression/DWARFExpression.h @@ -159,7 +159,8 @@ class DWARFExpression { return data.GetByteSize() > 0; } - void DumpLocation(Stream *s, lldb::DescriptionLevel level, ABI *abi) const; + void DumpLocation(Stream *s, lldb::DescriptionLevel level, ABI *abi, + llvm::DIDumpOptions options = {}) const; bool MatchesOperand(StackFrame &frame, const Instruction::Operand &op) const; diff --git a/lldb/include/lldb/Target/StackFrame.h b/lldb/include/lldb/Target/StackFrame.h index 3f51c9a7f22f0..a970743f0fa5b 100644 --- a/lldb/include/lldb/Target/StackFrame.h +++ b/lldb/include/lldb/Target/StackFrame.h @@ -321,7 +321,7 @@ class StackFrame : public ExecutionContextScope, /// /// \return /// C string with the assembly instructions for this function. - const char *Disassemble(); + const char *Disassemble(bool enable_rich_annotations = false); /// Print a description of this frame using the provided frame format. /// diff --git a/lldb/source/Commands/CommandObjectDisassemble.cpp b/lldb/source/Commands/CommandObjectDisassemble.cpp index 70e687e19ac6d..5b7249be80a3e 100644 --- a/lldb/source/Commands/CommandObjectDisassemble.cpp +++ b/lldb/source/Commands/CommandObjectDisassemble.cpp @@ -154,6 +154,10 @@ Status CommandObjectDisassemble::CommandOptions::SetOptionValue( } } break; + case 'R': // --rich + enable_rich_annotations = true; + break; + case '\x01': force = true; break; @@ -180,6 +184,7 @@ void CommandObjectDisassemble::CommandOptions::OptionParsingStarting( end_addr = LLDB_INVALID_ADDRESS; symbol_containing_addr = LLDB_INVALID_ADDRESS; raw = false; + enable_rich_annotations = false; plugin_name.clear(); Target *target = @@ -550,7 +555,8 @@ void CommandObjectDisassemble::DoExecute(Args &command, cpu_string, features_string, m_exe_ctx, cur_range.GetBaseAddress(), limit, m_options.show_mixed, m_options.show_mixed ? m_options.num_lines_context : 0, options, - result.GetOutputStream())) { + result.GetOutputStream(), + /*enable_rich_annotations=*/m_options.enable_rich_annotations)) { result.SetStatus(eReturnStatusSuccessFinishResult); } else { if (m_options.symbol_containing_addr != LLDB_INVALID_ADDRESS) { diff --git a/lldb/source/Commands/CommandObjectDisassemble.h b/lldb/source/Commands/CommandObjectDisassemble.h index 4fbcd72d1c042..caaeabc15593d 100644 --- a/lldb/source/Commands/CommandObjectDisassemble.h +++ b/lldb/source/Commands/CommandObjectDisassemble.h @@ -78,6 +78,7 @@ class CommandObjectDisassemble : public CommandObjectParsed { // in SetOptionValue if anything the selects a location is set. lldb::addr_t symbol_containing_addr = 0; bool force = false; + bool enable_rich_annotations = false; }; CommandObjectDisassemble(CommandInterpreter &interpreter); diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td index acb741081cac3..15e03a2a3056b 100644 --- a/lldb/source/Commands/Options.td +++ b/lldb/source/Commands/Options.td @@ -361,6 +361,8 @@ let Command = "disassemble" in { Desc<"Disassemble function containing this address.">; def disassemble_options_force : Option<"force", "\\x01">, Groups<[2,3,4,5,7]>, Desc<"Force disassembly of large functions.">; + def disassemble_options_rich : Option<"rich", "R">, + Desc<"Enable rich disassembly annotations for this invocation.">; } let Command = "diagnostics dump" in { diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp index e0a7d69345706..bc27aec6eafac 100644 --- a/lldb/source/Core/Disassembler.cpp +++ b/lldb/source/Core/Disassembler.cpp @@ -26,7 +26,10 @@ #include "lldb/Symbol/Function.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/Symbol/VariableList.h" #include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" #include "lldb/Target/SectionLoadList.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/Target.h" @@ -165,14 +168,12 @@ Disassembler::DisassembleBytes(const ArchSpec &arch, const char *plugin_name, return disasm_sp; } -bool Disassembler::Disassemble(Debugger &debugger, const ArchSpec &arch, - const char *plugin_name, const char *flavor, - const char *cpu, const char *features, - const ExecutionContext &exe_ctx, - const Address &address, Limit limit, - bool mixed_source_and_assembly, - uint32_t num_mixed_context_lines, - uint32_t options, Stream &strm) { +bool Disassembler::Disassemble( + Debugger &debugger, const ArchSpec &arch, const char *plugin_name, + const char *flavor, const char *cpu, const char *features, + const ExecutionContext &exe_ctx, const Address &address, Limit limit, + bool mixed_source_and_assembly, uint32_t num_mixed_context_lines, + uint32_t options, Stream &strm, bool enable_rich_annotations) { if (!exe_ctx.GetTargetPtr()) return false; @@ -187,9 +188,10 @@ bool Disassembler::Disassemble(Debugger &debugger, const ArchSpec &arch, if (bytes_disassembled == 0) return false; - disasm_sp->PrintInstructions(debugger, arch, exe_ctx, - mixed_source_and_assembly, - num_mixed_context_lines, options, strm); + disasm_sp->PrintInstructions( + debugger, arch, exe_ctx, mixed_source_and_assembly, + num_mixed_context_lines, options, strm, + /*enable_rich_annotations=*/enable_rich_annotations); return true; } @@ -284,7 +286,8 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch, const ExecutionContext &exe_ctx, bool mixed_source_and_assembly, uint32_t num_mixed_context_lines, - uint32_t options, Stream &strm) { + uint32_t options, Stream &strm, + bool enable_rich_annotations) { // We got some things disassembled... size_t num_instructions_found = GetInstructionList().GetSize(); @@ -542,7 +545,7 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch, (options & eOptionShowControlFlowKind) != 0; inst->Dump(&strm, max_opcode_byte_size, true, show_bytes, show_control_flow_kind, &exe_ctx, &sc, &prev_sc, nullptr, - address_text_size); + address_text_size, enable_rich_annotations); strm.EOL(); } else { break; @@ -551,7 +554,8 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch, } bool Disassembler::Disassemble(Debugger &debugger, const ArchSpec &arch, - StackFrame &frame, Stream &strm) { + StackFrame &frame, Stream &strm, + bool enable_rich_annotations) { constexpr const char *plugin_name = nullptr; constexpr const char *flavor = nullptr; constexpr const char *cpu = nullptr; @@ -638,7 +642,8 @@ void Instruction::Dump(lldb_private::Stream *s, uint32_t max_opcode_byte_size, const SymbolContext *sym_ctx, const SymbolContext *prev_sym_ctx, const FormatEntity::Entry *disassembly_addr_format, - size_t max_address_text_size) { + size_t max_address_text_size, + bool enable_rich_annotations) { size_t opcode_column_width = 7; const size_t operand_column_width = 25; @@ -704,6 +709,104 @@ void Instruction::Dump(lldb_private::Stream *s, uint32_t max_opcode_byte_size, ss.FillLastLineToColumn(opcode_pos + opcode_column_width, ' '); ss.PutCString(mnemonics); + // Add rich variable location annotations to the disassembly output. + // + // For each instruction, this block attempts to resolve in-scope variables + // and determine if the current PC falls within their + // DWARF location entry. If so, it prints a simplified annotation using the + // variable name and its resolved location (e.g., "var = reg; " ). + // + // Annotations are only included if the variable has a valid DWARF location + // entry, and the location string is non-empty after filtering. Decoding + // errors and DWARF opcodes are intentionally omitted to keep the output + // concise and user-friendly. + // + // The goal is to give users helpful live variable hints alongside the + // disassembled instruction stream, similar to how debug information + // enhances source-level debugging. + + const size_t annotation_column = 150; + + auto annotate_variables = [&]() { + StackFrame *frame = exe_ctx->GetFramePtr(); + TargetSP target_sp = exe_ctx->GetTargetSP(); + if (!frame || !target_sp) + return; + + addr_t current_pc = m_address.GetLoadAddress(target_sp.get()); + addr_t original_pc = + frame->GetFrameCodeAddress().GetLoadAddress(target_sp.get()); + + if (!frame->ChangePC(current_pc)) + return; + + VariableListSP var_list_sp = frame->GetInScopeVariableList(true); + if (!var_list_sp) + return; + + SymbolContext sc = frame->GetSymbolContext(eSymbolContextFunction); + addr_t func_load_addr = LLDB_INVALID_ADDRESS; + if (sc.function) + func_load_addr = + sc.function->GetAddress().GetLoadAddress(target_sp.get()); + + // Only annotate if the current disassembly line is short enough + // to keep annotations aligned past the desired annotation_column. + if (ss.GetSizeOfLastLine() >= annotation_column) + return; + + std::vector annotations; + + for (const VariableSP &var_sp : *var_list_sp) { + if (!var_sp) + continue; + + const char *name = var_sp->GetName().AsCString(); + auto &expr_list = var_sp->LocationExpressionList(); + if (!expr_list.IsValid()) + continue; + + // Handle std::optional. + if (auto entryOrErr = expr_list.GetExpressionEntryAtAddress( + func_load_addr, current_pc)) { + auto entry = *entryOrErr; + // Check if entry has a file_range, and filter on address if so. + if (!entry.file_range || entry.file_range->ContainsFileAddress( + (current_pc - func_load_addr) + + expr_list.GetFuncFileAddress())) { + + StreamString loc_str; + ABI *abi = exe_ctx->GetProcessPtr()->GetABI().get(); + llvm::DIDumpOptions opts; + opts.ShowAddresses = false; + opts.PrintRegisterOnly = + true; // <-- important: suppress DW_OP_... annotations, etc. + + entry.expr->DumpLocation(&loc_str, eDescriptionLevelBrief, abi, opts); + + // Only include if not empty. + llvm::StringRef loc_clean = + llvm::StringRef(loc_str.GetString()).trim(); + if (!loc_clean.empty()) { + annotations.push_back(llvm::formatv("{0} = {1}", name, loc_clean)); + } + } + } + } + + if (!annotations.empty()) { + ss.FillLastLineToColumn(annotation_column, ' '); + ss.PutCString(" ; "); + ss.PutCString(llvm::join(annotations, ", ")); + } + + frame->ChangePC(original_pc); + }; + + if (enable_rich_annotations && exe_ctx && exe_ctx->GetFramePtr()) { + annotate_variables(); + } + if (!m_comment.empty()) { ss.FillLastLineToColumn( opcode_pos + opcode_column_width + operand_column_width, ' '); @@ -724,9 +827,7 @@ bool Instruction::DumpEmulation(const ArchSpec &arch) { return false; } -bool Instruction::CanSetBreakpoint () { - return !HasDelaySlot(); -} +bool Instruction::CanSetBreakpoint() { return !HasDelaySlot(); } bool Instruction::HasDelaySlot() { // Default is false. @@ -1073,10 +1174,8 @@ void InstructionList::Append(lldb::InstructionSP &inst_sp) { m_instructions.push_back(inst_sp); } -uint32_t -InstructionList::GetIndexOfNextBranchInstruction(uint32_t start, - bool ignore_calls, - bool *found_calls) const { +uint32_t InstructionList::GetIndexOfNextBranchInstruction( + uint32_t start, bool ignore_calls, bool *found_calls) const { size_t num_instructions = m_instructions.size(); uint32_t next_branch = UINT32_MAX; diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp index ed4e4e4e341a3..df56bcf5eb43e 100644 --- a/lldb/source/Expression/DWARFExpression.cpp +++ b/lldb/source/Expression/DWARFExpression.cpp @@ -67,7 +67,8 @@ void DWARFExpression::UpdateValue(uint64_t const_value, } void DWARFExpression::DumpLocation(Stream *s, lldb::DescriptionLevel level, - ABI *abi) const { + ABI *abi, + llvm::DIDumpOptions options) const { auto *MCRegInfo = abi ? &abi->GetMCRegisterInfo() : nullptr; auto GetRegName = [&MCRegInfo](uint64_t DwarfRegNum, bool IsEH) -> llvm::StringRef { @@ -79,10 +80,9 @@ void DWARFExpression::DumpLocation(Stream *s, lldb::DescriptionLevel level, return llvm::StringRef(RegName); return {}; }; - llvm::DIDumpOptions DumpOpts; - DumpOpts.GetNameForDWARFReg = GetRegName; + options.GetNameForDWARFReg = GetRegName; llvm::DWARFExpression E(m_data.GetAsLLVM(), m_data.GetAddressByteSize()); - llvm::printDwarfExpression(&E, s->AsRawOstream(), DumpOpts, nullptr); + llvm::printDwarfExpression(&E, s->AsRawOstream(), options, nullptr); } RegisterKind DWARFExpression::GetRegisterKind() const { return m_reg_kind; } diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp index d97a814952186..0aa93e635b866 100644 --- a/lldb/source/Target/StackFrame.cpp +++ b/lldb/source/Target/StackFrame.cpp @@ -262,15 +262,20 @@ bool StackFrame::ChangePC(addr_t pc) { return true; } -const char *StackFrame::Disassemble() { +const char *StackFrame::Disassemble(bool enable_rich_annotations) { std::lock_guard guard(m_mutex); - if (!m_disassembly.Empty()) - return m_disassembly.GetData(); + + // Keep the existing cache only for the plain (non-rich) path. + if (!enable_rich_annotations) { + if (!m_disassembly.Empty()) + return m_disassembly.GetData(); + } ExecutionContext exe_ctx(shared_from_this()); if (Target *target = exe_ctx.GetTargetPtr()) { - Disassembler::Disassemble(target->GetDebugger(), target->GetArchitecture(), - *this, m_disassembly); + Disassembler::Disassemble( + target->GetDebugger(), target->GetArchitecture(), *this, m_disassembly, + /*enable_rich_annotations=*/enable_rich_annotations); } return m_disassembly.Empty() ? nullptr : m_disassembly.GetData(); @@ -438,10 +443,10 @@ VariableList *StackFrame::GetVariableList(bool get_file_globals, const bool get_child_variables = true; const bool can_create = true; const bool stop_if_child_block_is_inlined_function = true; - frame_block->AppendBlockVariables(can_create, get_child_variables, - stop_if_child_block_is_inlined_function, - [](Variable *v) { return true; }, - m_variable_list_sp.get()); + frame_block->AppendBlockVariables( + can_create, get_child_variables, + stop_if_child_block_is_inlined_function, + [](Variable *v) { return true; }, m_variable_list_sp.get()); } } @@ -1225,10 +1230,12 @@ StackFrame::GetValueObjectForFrameVariable(const VariableSP &variable_sp, VariableList *var_list = GetVariableList(true, nullptr); if (var_list) { // Make sure the variable is a frame variable - const uint32_t var_idx = var_list->FindIndexForVariable(variable_sp.get()); + const uint32_t var_idx = + var_list->FindIndexForVariable(variable_sp.get()); const uint32_t num_variables = var_list->GetSize(); if (var_idx < num_variables) { - valobj_sp = m_variable_list_value_objects.GetValueObjectAtIndex(var_idx); + valobj_sp = + m_variable_list_value_objects.GetValueObjectAtIndex(var_idx); if (!valobj_sp) { if (m_variable_list_value_objects.GetSize() < num_variables) m_variable_list_value_objects.Resize(num_variables); @@ -1762,11 +1769,9 @@ lldb::ValueObjectSP DoGuessValueAt(StackFrame &frame, ConstString reg, if (clobbered_reg_matcher(operands[0])) { origin_operand = &operands[1]; - } - else if (clobbered_reg_matcher(operands[1])) { + } else if (clobbered_reg_matcher(operands[1])) { origin_operand = &operands[0]; - } - else { + } else { continue; } @@ -1792,8 +1797,7 @@ lldb::ValueObjectSP DoGuessValueAt(StackFrame &frame, ConstString reg, if (!source_path) { continue; } - source_path = - GetValueForDereferincingOffset(frame, source_path, offset); + source_path = GetValueForDereferincingOffset(frame, source_path, offset); } if (source_path) { @@ -1803,7 +1807,7 @@ lldb::ValueObjectSP DoGuessValueAt(StackFrame &frame, ConstString reg, return ValueObjectSP(); } -} +} // namespace lldb::ValueObjectSP StackFrame::GuessValueForRegisterAndOffset(ConstString reg, int64_t offset) { diff --git a/lldb/test/API/functionalities/rich-disassembler/Makefile b/lldb/test/API/functionalities/rich-disassembler/Makefile new file mode 100644 index 0000000000000..ae3330e632a0e --- /dev/null +++ b/lldb/test/API/functionalities/rich-disassembler/Makefile @@ -0,0 +1,6 @@ + +# CXX_SOURCES := a_loop_with_local_variable.c b_multiple_stack_variables.c c_variable_passed_to_another_function.c d_original_example.c e_control_flow_edge.c +C_SOURCES := a_loop_with_local_variable.c b_multiple_stack_variables.c c_variable_passed_to_another_function.c d_original_example.c e_control_flow_edge.c + + +include Makefile.rules diff --git a/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py b/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py new file mode 100644 index 0000000000000..4bad4a4f4986b --- /dev/null +++ b/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py @@ -0,0 +1,34 @@ +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * +import lldb + + +class TestRichDisassembler(TestBase): + def test_d_original_example_O1(self): + """ + Tests disassembler output for d_original_example.c built with -O1, + using the CLI with --rich for enabled annotations. + """ + self.build( + dictionary={"C_SOURCES": "d_original_example.c", "CFLAGS_EXTRAS": "-g -O1"} + ) + exe = self.getBuildArtifact("a.out") + target = self.dbg.CreateTarget(exe) + self.assertTrue(target) + + bp = target.BreakpointCreateByName("main") + self.assertGreater(bp.GetNumLocations(), 0) + + process = target.LaunchSimple(None, None, self.get_process_working_directory()) + self.assertTrue(process, "Failed to launch process") + self.assertEqual(process.GetState(), lldb.eStateStopped) + + # Run the CLI command and read output from self.res + self.runCmd("disassemble --rich -f", check=True) + out = self.res.GetOutput() + print(out) + + self.assertIn("argc = ", out) + self.assertIn("argv = ", out) + self.assertIn("i = ", out) + self.assertNotIn("", out) diff --git a/lldb/test/API/functionalities/rich-disassembler/d_original_example.c b/lldb/test/API/functionalities/rich-disassembler/d_original_example.c new file mode 100644 index 0000000000000..1c864753a0220 --- /dev/null +++ b/lldb/test/API/functionalities/rich-disassembler/d_original_example.c @@ -0,0 +1,7 @@ +#include + +int main(int argc, char **argv) { + for (int i = 1; i < argc; ++i) + puts(argv[i]); + return 0; +} diff --git a/llvm/include/llvm/DebugInfo/DIContext.h b/llvm/include/llvm/DebugInfo/DIContext.h index 0347f90c236d1..e7e87bbfebf38 100644 --- a/llvm/include/llvm/DebugInfo/DIContext.h +++ b/llvm/include/llvm/DebugInfo/DIContext.h @@ -209,6 +209,7 @@ struct DIDumpOptions { bool IsEH = false; bool DumpNonSkeleton = false; bool ShowAggregateErrors = false; + bool PrintRegisterOnly = false; std::string JsonErrSummaryFile; std::function GetNameForDWARFReg; diff --git a/llvm/lib/DebugInfo/DWARF/DWARFExpressionPrinter.cpp b/llvm/lib/DebugInfo/DWARF/DWARFExpressionPrinter.cpp index ebcd4dda50488..3622ff3a886a1 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFExpressionPrinter.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFExpressionPrinter.cpp @@ -47,14 +47,16 @@ static void prettyPrintBaseTypeRef(DWARFUnit *U, raw_ostream &OS, static bool printOp(const DWARFExpression::Operation *Op, raw_ostream &OS, DIDumpOptions DumpOpts, const DWARFExpression *Expr, DWARFUnit *U) { - if (Op->isError()) { + if (Op->isError() && !DumpOpts.PrintRegisterOnly) { OS << ""; return false; } - StringRef Name = OperationEncodingString(Op->getCode()); - assert(!Name.empty() && "DW_OP has no name!"); - OS << Name; + if (!DumpOpts.PrintRegisterOnly) { + StringRef Name = OperationEncodingString(Op->getCode()); + assert(!Name.empty() && "DW_OP has no name!"); + OS << Name; + } if ((Op->getCode() >= DW_OP_breg0 && Op->getCode() <= DW_OP_breg31) || (Op->getCode() >= DW_OP_reg0 && Op->getCode() <= DW_OP_reg31) || @@ -64,48 +66,51 @@ static bool printOp(const DWARFExpression::Operation *Op, raw_ostream &OS, Op->getRawOperands())) return true; - for (unsigned Operand = 0; Operand < Op->getDescription().Op.size(); - ++Operand) { - unsigned Size = Op->getDescription().Op[Operand]; - unsigned Signed = Size & DWARFExpression::Operation::SignBit; + if (!DumpOpts.PrintRegisterOnly) { + for (unsigned Operand = 0; Operand < Op->getDescription().Op.size(); + ++Operand) { + unsigned Size = Op->getDescription().Op[Operand]; + unsigned Signed = Size & DWARFExpression::Operation::SignBit; - if (Size == DWARFExpression::Operation::SizeSubOpLEB) { - StringRef SubName = - SubOperationEncodingString(Op->getCode(), Op->getRawOperand(Operand)); - assert(!SubName.empty() && "DW_OP SubOp has no name!"); - OS << " " << SubName; - } else if (Size == DWARFExpression::Operation::BaseTypeRef && U) { - // For DW_OP_convert the operand may be 0 to indicate that conversion to - // the generic type should be done. The same holds for DW_OP_reinterpret, - // which is currently not supported. - if (Op->getCode() == DW_OP_convert && Op->getRawOperand(Operand) == 0) - OS << " 0x0"; - else - prettyPrintBaseTypeRef(U, OS, DumpOpts, Op->getRawOperands(), Operand); - } else if (Size == DWARFExpression::Operation::WasmLocationArg) { - assert(Operand == 1); - switch (Op->getRawOperand(0)) { - case 0: - case 1: - case 2: - case 3: // global as uint32 - case 4: - OS << format(" 0x%" PRIx64, Op->getRawOperand(Operand)); - break; - default: - assert(false); + if (Size == DWARFExpression::Operation::SizeSubOpLEB) { + StringRef SubName = SubOperationEncodingString( + Op->getCode(), Op->getRawOperand(Operand)); + assert(!SubName.empty() && "DW_OP SubOp has no name!"); + OS << " " << SubName; + } else if (Size == DWARFExpression::Operation::BaseTypeRef && U) { + // For DW_OP_convert the operand may be 0 to indicate that conversion to + // the generic type should be done. The same holds for + // DW_OP_reinterpret, which is currently not supported. + if (Op->getCode() == DW_OP_convert && Op->getRawOperand(Operand) == 0) + OS << " 0x0"; + else + prettyPrintBaseTypeRef(U, OS, DumpOpts, Op->getRawOperands(), + Operand); + } else if (Size == DWARFExpression::Operation::WasmLocationArg) { + assert(Operand == 1); + switch (Op->getRawOperand(0)) { + case 0: + case 1: + case 2: + case 3: // global as uint32 + case 4: + OS << format(" 0x%" PRIx64, Op->getRawOperand(Operand)); + break; + default: + assert(false); + } + } else if (Size == DWARFExpression::Operation::SizeBlock) { + uint64_t Offset = Op->getRawOperand(Operand); + for (unsigned i = 0; i < Op->getRawOperand(Operand - 1); ++i) + OS << format(" 0x%02x", + static_cast(Expr->getData()[Offset++])); + } else { + if (Signed) + OS << format(" %+" PRId64, (int64_t)Op->getRawOperand(Operand)); + else if (Op->getCode() != DW_OP_entry_value && + Op->getCode() != DW_OP_GNU_entry_value) + OS << format(" 0x%" PRIx64, Op->getRawOperand(Operand)); } - } else if (Size == DWARFExpression::Operation::SizeBlock) { - uint64_t Offset = Op->getRawOperand(Operand); - for (unsigned i = 0; i < Op->getRawOperand(Operand - 1); ++i) - OS << format(" 0x%02x", - static_cast(Expr->getData()[Offset++])); - } else { - if (Signed) - OS << format(" %+" PRId64, (int64_t)Op->getRawOperand(Operand)); - else if (Op->getCode() != DW_OP_entry_value && - Op->getCode() != DW_OP_GNU_entry_value) - OS << format(" 0x%" PRIx64, Op->getRawOperand(Operand)); } } return true; @@ -120,29 +125,30 @@ void printDwarfExpression(const DWARFExpression *E, raw_ostream &OS, for (auto &Op : *E) { DumpOpts.IsEH = IsEH; - if (!printOp(&Op, OS, DumpOpts, E, U)) { + if (!printOp(&Op, OS, DumpOpts, E, U) && !DumpOpts.PrintRegisterOnly) { uint64_t FailOffset = Op.getEndOffset(); while (FailOffset < E->getData().size()) OS << format(" %02x", static_cast(E->getData()[FailOffset++])); return; } + if (!DumpOpts.PrintRegisterOnly) { + if (Op.getCode() == DW_OP_entry_value || + Op.getCode() == DW_OP_GNU_entry_value) { + OS << "("; + EntryValExprSize = Op.getRawOperand(0); + EntryValStartOffset = Op.getEndOffset(); + continue; + } - if (Op.getCode() == DW_OP_entry_value || - Op.getCode() == DW_OP_GNU_entry_value) { - OS << "("; - EntryValExprSize = Op.getRawOperand(0); - EntryValStartOffset = Op.getEndOffset(); - continue; - } + if (EntryValExprSize) { + EntryValExprSize -= Op.getEndOffset() - EntryValStartOffset; + if (EntryValExprSize == 0) + OS << ")"; + } - if (EntryValExprSize) { - EntryValExprSize -= Op.getEndOffset() - EntryValStartOffset; - if (EntryValExprSize == 0) - OS << ")"; + if (Op.getEndOffset() < E->getData().size()) + OS << ", "; } - - if (Op.getEndOffset() < E->getData().size()) - OS << ", "; } }