diff --git a/lldb/include/lldb/Core/Disassembler.h b/lldb/include/lldb/Core/Disassembler.h index 5de314109b0cc..a7922d4a4f703 100644 --- a/lldb/include/lldb/Core/Disassembler.h +++ b/lldb/include/lldb/Core/Disassembler.h @@ -570,24 +570,40 @@ class Disassembler : public std::enable_shared_from_this, const Disassembler &operator=(const Disassembler &) = delete; }; +/// Structured data for a single variable annotation. +struct VariableAnnotation { + std::string variable_name; + /// Location description (e.g., "r15", "undef", "const_0"). + std::string location_description; + /// Whether variable is live at this instruction. + bool is_live; + /// Register numbering scheme for location interpretation. + lldb::RegisterKind register_kind; + /// Where this annotation is valid. + std::optional address_range; + /// Source file where variable was declared. + std::optional decl_file; + /// Line number where variable was declared. + std::optional decl_line; + /// Variable's type name. + std::optional type_name; +}; + /// Tracks live variable annotations across instructions and produces /// per-instruction "events" like `name = RDI` or `name = `. class VariableAnnotator { - struct VarState { - /// Display name. - std::string name; - /// Last printed location (empty means ). - std::string last_loc; - }; // Live state from the previous instruction, keyed by Variable::GetID(). - llvm::DenseMap m_live_vars; + llvm::DenseMap m_live_vars; public: /// Compute annotation strings for a single instruction and update /// `m_live_vars`. Returns only the events that should be printed *at this /// instruction*. std::vector Annotate(Instruction &inst); + + /// Returns structured data for all variables relevant at this instruction. + std::vector AnnotateStructured(Instruction &inst); }; } // namespace lldb_private diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp index f2eb887986bfb..117c13a1233cd 100644 --- a/lldb/source/Core/Disassembler.cpp +++ b/lldb/source/Core/Disassembler.cpp @@ -286,6 +286,9 @@ bool Disassembler::ElideMixedSourceAndDisassemblyLine( return false; } +static constexpr const llvm::StringLiteral kUndefLocation = "undef"; +static constexpr const llvm::StringLiteral kUndefLocationFormatted = ""; + // 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 @@ -300,16 +303,43 @@ bool Disassembler::ElideMixedSourceAndDisassemblyLine( // disassembled instruction stream, similar to how debug information // enhances source-level debugging. std::vector VariableAnnotator::Annotate(Instruction &inst) { + std::vector structured_annotations = + AnnotateStructured(inst); + std::vector events; + events.reserve(structured_annotations.size()); + + for (const auto &annotation : structured_annotations) { + const llvm::StringRef location = + (annotation.location_description == kUndefLocation + ? llvm::StringRef(kUndefLocationFormatted) + : llvm::StringRef(annotation.location_description)); + + const auto display_string = + llvm::formatv("{0} = {1}", annotation.variable_name, location).str(); + + events.push_back(std::move(display_string)); + } + + return events; +} + +std::vector +VariableAnnotator::AnnotateStructured(Instruction &inst) { + std::vector annotations; auto module_sp = inst.GetAddress().GetModule(); - // If we lost module context, everything becomes . + // If we lost module context, mark all live variables as UndefLocation. if (!module_sp) { - for (const auto &KV : m_live_vars) - events.emplace_back(llvm::formatv("{0} = ", KV.second.name).str()); + for (const auto &KV : m_live_vars) { + VariableAnnotation annotation_entity = KV.second; + annotation_entity.is_live = false; + annotation_entity.location_description = kUndefLocation; + annotations.push_back(std::move(annotation_entity)); + } m_live_vars.clear(); - return events; + return annotations; } // Resolve function/block at this *file* address. @@ -319,10 +349,14 @@ std::vector VariableAnnotator::Annotate(Instruction &inst) { if (!module_sp->ResolveSymbolContextForAddress(iaddr, mask, sc) || !sc.function) { // No function context: everything dies here. - for (const auto &KV : m_live_vars) - events.emplace_back(llvm::formatv("{0} = ", KV.second.name).str()); + for (const auto &KV : m_live_vars) { + VariableAnnotation annotation_entity = KV.second; + annotation_entity.is_live = false; + annotation_entity.location_description = kUndefLocation; + annotations.push_back(std::move(annotation_entity)); + } m_live_vars.clear(); - return events; + return annotations; } // Collect in-scope variables for this instruction into current_vars. @@ -349,7 +383,7 @@ std::vector VariableAnnotator::Annotate(Instruction &inst) { // Prefer "register-only" output when we have an ABI. opts.PrintRegisterOnly = static_cast(abi_sp); - llvm::DenseMap current_vars; + llvm::DenseMap current_vars; for (size_t i = 0, e = var_list.GetSize(); i != e; ++i) { lldb::VariableSP v = var_list.GetVariableAtIndex(i); @@ -376,8 +410,26 @@ std::vector VariableAnnotator::Annotate(Instruction &inst) { if (loc.empty()) continue; - current_vars.try_emplace(v->GetID(), - VarState{std::string(name), std::string(loc)}); + std::optional decl_file; + std::optional decl_line; + std::optional type_name; + + const Declaration &decl = v->GetDeclaration(); + if (decl.GetFile()) { + decl_file = decl.GetFile().GetFilename().AsCString(); + if (decl.GetLine() > 0) + decl_line = decl.GetLine(); + } + + if (Type *type = v->GetType()) + if (const char *type_str = type->GetName().AsCString()) + type_name = type_str; + + current_vars.try_emplace( + v->GetID(), + VariableAnnotation{std::string(name), std::string(loc), true, + entry.expr->GetRegisterKind(), entry.file_range, + decl_file, decl_line, type_name}); } // Diff m_live_vars → current_vars. @@ -387,24 +439,31 @@ std::vector VariableAnnotator::Annotate(Instruction &inst) { auto it = m_live_vars.find(KV.first); if (it == m_live_vars.end()) { // Newly live. - events.emplace_back( - llvm::formatv("{0} = {1}", KV.second.name, KV.second.last_loc).str()); - } else if (it->second.last_loc != KV.second.last_loc) { + VariableAnnotation annotation_entity = KV.second; + annotation_entity.is_live = true; + annotations.push_back(std::move(annotation_entity)); + } else if (it->second.location_description != + KV.second.location_description) { // Location changed. - events.emplace_back( - llvm::formatv("{0} = {1}", KV.second.name, KV.second.last_loc).str()); + VariableAnnotation annotation_entity = KV.second; + annotation_entity.is_live = true; + annotations.push_back(std::move(annotation_entity)); } } - // 2) Ends: anything that was live but is not in current_vars becomes . - for (const auto &KV : m_live_vars) { - if (!current_vars.count(KV.first)) - events.emplace_back(llvm::formatv("{0} = ", KV.second.name).str()); - } + // 2) Ends: anything that was live but is not in current_vars becomes + // UndefLocation. + for (const auto &KV : m_live_vars) + if (!current_vars.count(KV.first)) { + auto annotation_entity = KV.second; + annotation_entity.is_live = false; + annotation_entity.location_description = kUndefLocation; + annotations.push_back(std::move(annotation_entity)); + } // Commit new state. m_live_vars = std::move(current_vars); - return events; + return annotations; } void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,