| //===-- PdbFPOProgramToDWARFExpression.cpp ----------------------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "PdbFPOProgramToDWARFExpression.h" |
| #include "CodeViewRegisterMapping.h" |
| |
| #include "lldb/Core/StreamBuffer.h" |
| #include "lldb/Symbol/PostfixExpression.h" |
| #include "lldb/Utility/LLDBAssert.h" |
| #include "lldb/Utility/Stream.h" |
| #include "llvm/ADT/DenseMap.h" |
| |
| #include "llvm/ADT/StringExtras.h" |
| #include "llvm/DebugInfo/CodeView/CodeView.h" |
| #include "llvm/DebugInfo/CodeView/EnumTables.h" |
| |
| using namespace lldb; |
| using namespace lldb_private; |
| using namespace lldb_private::postfix; |
| |
| static uint32_t ResolveLLDBRegisterNum(llvm::StringRef reg_name, llvm::Triple::ArchType arch_type) { |
| // lookup register name to get lldb register number |
| llvm::codeview::CPUType cpu_type; |
| switch (arch_type) { |
| case llvm::Triple::ArchType::aarch64: |
| cpu_type = llvm::codeview::CPUType::ARM64; |
| break; |
| |
| default: |
| cpu_type = llvm::codeview::CPUType::X64; |
| break; |
| } |
| |
| llvm::ArrayRef<llvm::EnumEntry<uint16_t>> register_names = |
| llvm::codeview::getRegisterNames(cpu_type); |
| auto it = llvm::find_if( |
| register_names, |
| [®_name](const llvm::EnumEntry<uint16_t> ®ister_entry) { |
| return reg_name.compare_lower(register_entry.Name) == 0; |
| }); |
| |
| if (it == register_names.end()) |
| return LLDB_INVALID_REGNUM; |
| |
| auto reg_id = static_cast<llvm::codeview::RegisterId>(it->Value); |
| return npdb::GetLLDBRegisterNumber(arch_type, reg_id); |
| } |
| |
| static bool ParseFPOSingleAssignmentProgram(llvm::StringRef program, |
| llvm::BumpPtrAllocator &alloc, |
| llvm::StringRef ®ister_name, |
| Node *&ast) { |
| // lvalue of assignment is always first token |
| // rvalue program goes next |
| std::tie(register_name, program) = getToken(program); |
| if (register_name.empty()) |
| return false; |
| |
| ast = Parse(program, alloc); |
| return ast != nullptr; |
| } |
| |
| static Node *ParseFPOProgram(llvm::StringRef program, |
| llvm::StringRef register_name, |
| llvm::Triple::ArchType arch_type, |
| llvm::BumpPtrAllocator &alloc) { |
| llvm::DenseMap<llvm::StringRef, Node *> dependent_programs; |
| |
| size_t cur = 0; |
| while (true) { |
| size_t assign_index = program.find('=', cur); |
| if (assign_index == llvm::StringRef::npos) { |
| llvm::StringRef tail = program.slice(cur, llvm::StringRef::npos); |
| if (!tail.trim().empty()) { |
| // missing assign operator |
| return nullptr; |
| } |
| break; |
| } |
| llvm::StringRef assignment_program = program.slice(cur, assign_index); |
| |
| llvm::StringRef lvalue_name; |
| Node *rvalue_ast = nullptr; |
| if (!ParseFPOSingleAssignmentProgram(assignment_program, alloc, lvalue_name, |
| rvalue_ast)) { |
| return nullptr; |
| } |
| |
| lldbassert(rvalue_ast); |
| |
| // Emplace valid dependent subtrees to make target assignment independent |
| // from predecessors. Resolve all other SymbolNodes as registers. |
| bool success = |
| ResolveSymbols(rvalue_ast, [&](SymbolNode &symbol) -> Node * { |
| if (Node *node = dependent_programs.lookup(symbol.GetName())) |
| return node; |
| uint32_t reg_num = |
| ResolveLLDBRegisterNum(symbol.GetName().drop_front(1), arch_type); |
| |
| if (reg_num == LLDB_INVALID_REGNUM) |
| return nullptr; |
| |
| return MakeNode<RegisterNode>(alloc, reg_num); |
| }); |
| if (!success) |
| return nullptr; |
| |
| if (lvalue_name == register_name) { |
| // found target assignment program - no need to parse further |
| return rvalue_ast; |
| } |
| |
| dependent_programs[lvalue_name] = rvalue_ast; |
| cur = assign_index + 1; |
| } |
| |
| return nullptr; |
| } |
| |
| bool lldb_private::npdb::TranslateFPOProgramToDWARFExpression( |
| llvm::StringRef program, llvm::StringRef register_name, |
| llvm::Triple::ArchType arch_type, Stream &stream) { |
| llvm::BumpPtrAllocator node_alloc; |
| Node *target_program = |
| ParseFPOProgram(program, register_name, arch_type, node_alloc); |
| if (target_program == nullptr) { |
| return false; |
| } |
| |
| ToDWARF(*target_program, stream); |
| return true; |
| } |