//===-- 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> register_names = llvm::codeview::getRegisterNames(cpu_type); auto it = llvm::find_if( register_names, [®_name](const llvm::EnumEntry ®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(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 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(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; }