Index: include/lldb/Symbol/PostfixExpression.h =================================================================== --- include/lldb/Symbol/PostfixExpression.h +++ include/lldb/Symbol/PostfixExpression.h @@ -19,6 +19,9 @@ #include "llvm/Support/Casting.h" namespace lldb_private { + +class Stream; + namespace postfix { /// The base class for all nodes in the parsed postfix tree. @@ -174,6 +177,45 @@ } }; +/// A specialized visitor class for clients that want to resolve (replace) +/// SymbolNodes in the AST. It implements reasonable defaults for all Visit +/// functions, and presents a simpler replacement interface. +class SymbolResolver : public Visitor { +public: + /// Perform SymbolNode replacement using the given Resolver class. Returns + /// true if all SymbolNodes have been sucessfully replaced. (Successfulness + /// is determined by the result of the Replace function.) + template + static bool Resolve(Node *&node, Args &&... args) { + static_assert(std::is_base_of::value, ""); + return ResolverT(std::forward(args)...).Dispatch(node); + } + +protected: + bool Visit(BinaryOpNode &binary, Node *&ref) override { + return Dispatch(binary.Left()) && Dispatch(binary.Right()); + } + + bool Visit(IntegerNode &integer, Node *&) override { return true; } + bool Visit(RegisterNode ®, Node *&) override { return true; } + + bool Visit(SymbolNode &symbol, Node *&ref) override { + if (Node *replacement = Replace(symbol)) { + ref = replacement; + return true; + } + return false; + } + + bool Visit(UnaryOpNode &unary, Node *&ref) override { + return Dispatch(unary.Operand()); + } + + /// Return the node which should be used to replace the given SymbolNode, or + /// nullptr, if unable to replace/resolve. + virtual Node *Replace(SymbolNode &symbol) = 0; +}; + template inline T *MakeNode(llvm::BumpPtrAllocator &alloc, Args &&... args) { static_assert(std::is_trivially_destructible::value, @@ -185,6 +227,10 @@ /// provided allocator. Node *Parse(llvm::StringRef expr, llvm::BumpPtrAllocator &alloc); +/// Serialize the given expression tree as DWARF. The result is written into the +/// given stream. The AST should not contain any SymbolNodes. +void ToDWARF(Node &node, Stream &stream); + } // namespace postfix } // namespace lldb_private Index: source/Plugins/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpression.cpp =================================================================== --- source/Plugins/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpression.cpp +++ source/Plugins/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpression.cpp @@ -10,7 +10,6 @@ #include "CodeViewRegisterMapping.h" #include "lldb/Core/StreamBuffer.h" -#include "lldb/Core/dwarf.h" #include "lldb/Symbol/PostfixExpression.h" #include "lldb/Utility/LLDBAssert.h" #include "lldb/Utility/Stream.h" @@ -26,79 +25,42 @@ namespace { -class FPOProgramASTVisitorMergeDependent : public Visitor<> { +class FPOProgramASTVisitorMergeDependent : public SymbolResolver { public: - void Visit(BinaryOpNode &binary, Node *&) override { - Dispatch(binary.Left()); - Dispatch(binary.Right()); - } - - void Visit(UnaryOpNode &unary, Node *&) override { - Dispatch(unary.Operand()); - } - - void Visit(RegisterNode &, Node *&) override {} - void Visit(IntegerNode &, Node *&) override {} - void Visit(SymbolNode &symbol, Node *&ref) override; - - static void - Merge(const llvm::DenseMap &dependent_programs, - Node *&ast) { - FPOProgramASTVisitorMergeDependent(dependent_programs).Dispatch(ast); - } - -private: FPOProgramASTVisitorMergeDependent( const llvm::DenseMap &dependent_programs) : m_dependent_programs(dependent_programs) {} +private: + Node *Replace(SymbolNode &symbol) override; + const llvm::DenseMap &m_dependent_programs; }; -void FPOProgramASTVisitorMergeDependent::Visit(SymbolNode &symbol, Node *&ref) { +Node *FPOProgramASTVisitorMergeDependent::Replace(SymbolNode &symbol) { auto it = m_dependent_programs.find(symbol.GetName()); if (it == m_dependent_programs.end()) - return; + return &symbol; - ref = it->second; - Dispatch(ref); + Node *result = it->second; + Dispatch(result); // Recursively process the result. + return result; // And return it. } -class FPOProgramASTVisitorResolveRegisterRefs : public Visitor { +class FPOProgramASTVisitorResolveRegisterRefs : public SymbolResolver { public: - static bool - Resolve(const llvm::DenseMap &dependent_programs, - llvm::Triple::ArchType arch_type, llvm::BumpPtrAllocator &alloc, - Node *&ast) { - return FPOProgramASTVisitorResolveRegisterRefs(dependent_programs, - arch_type, alloc) - .Dispatch(ast); - } - - bool Visit(BinaryOpNode &binary, Node *&) override { - return Dispatch(binary.Left()) && Dispatch(binary.Right()); - } - - bool Visit(UnaryOpNode &unary, Node *&) override { - return Dispatch(unary.Operand()); - } - - bool Visit(RegisterNode &, Node *&) override { return true; } - - bool Visit(IntegerNode &, Node *&) override { return true; } - - bool Visit(SymbolNode &symbol, Node *&ref) override; - -private: FPOProgramASTVisitorResolveRegisterRefs( const llvm::DenseMap &dependent_programs, llvm::Triple::ArchType arch_type, llvm::BumpPtrAllocator &alloc) - : m_dependent_programs(dependent_programs), m_arch_type(arch_type), - m_alloc(alloc) {} + : m_alloc(alloc), m_dependent_programs(dependent_programs), + m_arch_type(arch_type) {} + +private: + Node *Replace(SymbolNode &symbol) override; + llvm::BumpPtrAllocator &m_alloc; const llvm::DenseMap &m_dependent_programs; llvm::Triple::ArchType m_arch_type; - llvm::BumpPtrAllocator &m_alloc; }; static uint32_t ResolveLLDBRegisterNum(llvm::StringRef reg_name, llvm::Triple::ArchType arch_type) { @@ -118,98 +80,21 @@ return npdb::GetLLDBRegisterNumber(arch_type, reg_id); } -bool FPOProgramASTVisitorResolveRegisterRefs::Visit(SymbolNode &symbol, - Node *&ref) { +Node *FPOProgramASTVisitorResolveRegisterRefs::Replace(SymbolNode &symbol) { // Look up register reference as lvalue in preceding assignments. auto it = m_dependent_programs.find(symbol.GetName()); if (it != m_dependent_programs.end()) { // Dependent programs are handled elsewhere. - return true; + return &symbol; } uint32_t reg_num = ResolveLLDBRegisterNum(symbol.GetName().drop_front(1), m_arch_type); if (reg_num == LLDB_INVALID_REGNUM) - return false; - - ref = MakeNode(m_alloc, reg_num); - return true; -} - -class FPOProgramASTVisitorDWARFCodegen : public Visitor<> { -public: - static void Emit(Stream &stream, Node *&ast) { - FPOProgramASTVisitorDWARFCodegen(stream).Dispatch(ast); - } - - void Visit(RegisterNode ®, Node *&); - void Visit(BinaryOpNode &binary, Node *&); - void Visit(UnaryOpNode &unary, Node *&); - void Visit(SymbolNode &symbol, Node *&) { - llvm_unreachable("Symbols should have been resolved by now!"); - } - void Visit(IntegerNode &integer, Node *&); - -private: - FPOProgramASTVisitorDWARFCodegen(Stream &stream) : m_out_stream(stream) {} - - Stream &m_out_stream; -}; - -void FPOProgramASTVisitorDWARFCodegen::Visit(RegisterNode ®, Node *&) { - uint32_t reg_num = reg.GetRegNum(); - lldbassert(reg_num != LLDB_INVALID_REGNUM); - - if (reg_num > 31) { - m_out_stream.PutHex8(DW_OP_bregx); - m_out_stream.PutULEB128(reg_num); - } else - m_out_stream.PutHex8(DW_OP_breg0 + reg_num); - - m_out_stream.PutSLEB128(0); -} - -void FPOProgramASTVisitorDWARFCodegen::Visit(IntegerNode &integer, Node *&) { - uint32_t value = integer.GetValue(); - m_out_stream.PutHex8(DW_OP_constu); - m_out_stream.PutULEB128(value); -} - -void FPOProgramASTVisitorDWARFCodegen::Visit(BinaryOpNode &binary, Node *&) { - Dispatch(binary.Left()); - Dispatch(binary.Right()); - - switch (binary.GetOpType()) { - case BinaryOpNode::Plus: - m_out_stream.PutHex8(DW_OP_plus); - // NOTE: can be optimized by using DW_OP_plus_uconst opcpode - // if right child node is constant value - break; - case BinaryOpNode::Minus: - m_out_stream.PutHex8(DW_OP_minus); - break; - case BinaryOpNode::Align: - // emit align operator a @ b as - // a & ~(b - 1) - // NOTE: implicitly assuming that b is power of 2 - m_out_stream.PutHex8(DW_OP_lit1); - m_out_stream.PutHex8(DW_OP_minus); - m_out_stream.PutHex8(DW_OP_not); - - m_out_stream.PutHex8(DW_OP_and); - break; - } -} - -void FPOProgramASTVisitorDWARFCodegen::Visit(UnaryOpNode &unary, Node *&) { - Dispatch(unary.Operand()); + return nullptr; - switch (unary.GetOpType()) { - case UnaryOpNode::Deref: - m_out_stream.PutHex8(DW_OP_deref); - break; - } + return MakeNode(m_alloc, reg_num); } } // namespace @@ -257,8 +142,8 @@ lldbassert(rvalue_ast); // check & resolve assignment program - if (!FPOProgramASTVisitorResolveRegisterRefs::Resolve( - dependent_programs, arch_type, alloc, rvalue_ast)) + if (!SymbolResolver::Resolve( + rvalue_ast, dependent_programs, arch_type, alloc)) return nullptr; if (lvalue_name == register_name) { @@ -266,7 +151,8 @@ // emplace valid dependent subtrees to make target assignment independent // from predecessors - FPOProgramASTVisitorMergeDependent::Merge(dependent_programs, rvalue_ast); + SymbolResolver::Resolve( + rvalue_ast, dependent_programs); return rvalue_ast; } @@ -288,6 +174,6 @@ return false; } - FPOProgramASTVisitorDWARFCodegen::Emit(stream, target_program); + ToDWARF(*target_program, stream); return true; } Index: source/Symbol/PostfixExpression.cpp =================================================================== --- source/Symbol/PostfixExpression.cpp +++ source/Symbol/PostfixExpression.cpp @@ -12,6 +12,8 @@ //===----------------------------------------------------------------------===// #include "lldb/Symbol/PostfixExpression.h" +#include "lldb/Core/dwarf.h" +#include "lldb/Utility/Stream.h" #include "llvm/ADT/StringExtras.h" using namespace lldb_private; @@ -80,3 +82,84 @@ return stack.back(); } + +namespace { +class DWARFCodegen : public Visitor<> { +public: + DWARFCodegen(Stream &stream) : m_out_stream(stream) {} + + using Visitor<>::Dispatch; + +private: + void Visit(BinaryOpNode &binary, Node *&); + + void Visit(IntegerNode &integer, Node *&) { + m_out_stream.PutHex8(DW_OP_constu); + m_out_stream.PutULEB128(integer.GetValue()); + } + + void Visit(RegisterNode ®, Node *&); + + void Visit(SymbolNode &symbol, Node *&) { + llvm_unreachable("Symbols should have been resolved by now!"); + } + + void Visit(UnaryOpNode &unary, Node *&); + + Stream &m_out_stream; +}; +} // namespace + +void DWARFCodegen::Visit(BinaryOpNode &binary, Node *&) { + Dispatch(binary.Left()); + Dispatch(binary.Right()); + + switch (binary.GetOpType()) { + case BinaryOpNode::Plus: + m_out_stream.PutHex8(DW_OP_plus); + // NOTE: can be optimized by using DW_OP_plus_uconst opcpode + // if right child node is constant value + break; + case BinaryOpNode::Minus: + m_out_stream.PutHex8(DW_OP_minus); + break; + case BinaryOpNode::Align: + // emit align operator a @ b as + // a & ~(b - 1) + // NOTE: implicitly assuming that b is power of 2 + m_out_stream.PutHex8(DW_OP_lit1); + m_out_stream.PutHex8(DW_OP_minus); + m_out_stream.PutHex8(DW_OP_not); + + m_out_stream.PutHex8(DW_OP_and); + break; + } +} + +void DWARFCodegen::Visit(RegisterNode ®, Node *&) { + uint32_t reg_num = reg.GetRegNum(); + assert(reg_num != LLDB_INVALID_REGNUM); + + if (reg_num > 31) { + m_out_stream.PutHex8(DW_OP_bregx); + m_out_stream.PutULEB128(reg_num); + } else + m_out_stream.PutHex8(DW_OP_breg0 + reg_num); + + m_out_stream.PutSLEB128(0); +} + +void DWARFCodegen::Visit(UnaryOpNode &unary, Node *&) { + Dispatch(unary.Operand()); + + switch (unary.GetOpType()) { + case UnaryOpNode::Deref: + m_out_stream.PutHex8(DW_OP_deref); + break; + } +} + +void postfix::ToDWARF(Node &node, Stream &stream) { + Node *ptr = &node; + DWARFCodegen(stream).Dispatch(ptr); +} Index: unittests/Symbol/PostfixExpressionTest.cpp =================================================================== --- unittests/Symbol/PostfixExpressionTest.cpp +++ unittests/Symbol/PostfixExpressionTest.cpp @@ -7,6 +7,9 @@ //===----------------------------------------------------------------------===// #include "lldb/Symbol/PostfixExpression.h" +#include "lldb/Expression/DWARFExpression.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/StreamString.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/raw_ostream.h" #include "gtest/gtest.h" @@ -95,3 +98,66 @@ EXPECT_EQ("nullptr", ParseAndStringify("1 2")); EXPECT_EQ("nullptr", ParseAndStringify("")); } + +namespace { +class SimpleResolver : public SymbolResolver { +public: + SimpleResolver(llvm::BumpPtrAllocator &alloc) : m_alloc(alloc) {} + +private: + Node *Replace(SymbolNode &symbol) override { + uint32_t num; + if (to_integer(symbol.GetName().drop_front(), num)) + return MakeNode(m_alloc, num); + return nullptr; + } + + llvm::BumpPtrAllocator &m_alloc; +}; +} // namespace + +static std::string ParseAndGenerateDWARF(llvm::StringRef expr) { + llvm::BumpPtrAllocator alloc; + Node *ast = Parse(expr, alloc); + if (!ast) + return "Parse failed."; + if (!SymbolResolver::Resolve(ast, alloc)) + return "Resolution failed."; + + const size_t addr_size = 4; + StreamString dwarf(Stream::eBinary, addr_size, lldb::eByteOrderLittle); + ToDWARF(*ast, dwarf); + + // print dwarf expression to comparable textual representation + DataExtractor extractor(dwarf.GetData(), dwarf.GetSize(), + lldb::eByteOrderLittle, addr_size); + + StreamString result; + if (!DWARFExpression::PrintDWARFExpression(result, extractor, addr_size, + /*dwarf_ref_size*/ 4, + /*location_expression*/ false)) { + return "DWARF printing failed."; + } + + return result.GetString(); +} + +TEST(PostfixExpression, ToDWARF) { + EXPECT_EQ("DW_OP_constu 0x0", ParseAndGenerateDWARF("0")); + + EXPECT_EQ("DW_OP_breg1 +0", ParseAndGenerateDWARF("R1")); + + EXPECT_EQ("DW_OP_bregx 65 0", ParseAndGenerateDWARF("R65")); + + EXPECT_EQ("DW_OP_constu 0x4, DW_OP_constu 0x5, DW_OP_plus ", + ParseAndGenerateDWARF("4 5 +")); + + EXPECT_EQ("DW_OP_constu 0x4, DW_OP_constu 0x5, DW_OP_minus ", + ParseAndGenerateDWARF("4 5 -")); + + EXPECT_EQ("DW_OP_constu 0x4, DW_OP_deref ", ParseAndGenerateDWARF("4 ^")); + + EXPECT_EQ("DW_OP_breg6 +0, DW_OP_constu 0x80, DW_OP_lit1 " + ", DW_OP_minus , DW_OP_not , DW_OP_and ", + ParseAndGenerateDWARF("R6 128 @")); +} Index: unittests/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpressionTests.cpp =================================================================== --- unittests/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpressionTests.cpp +++ unittests/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpressionTests.cpp @@ -53,36 +53,10 @@ result_dwarf_expression.GetString().data()); } -TEST(PDBFPOProgramToDWARFExpressionTests, SingleAssignmentConst) { - CheckValidProgramTranslation("$T0 0 = ", "$T0", "DW_OP_constu 0x0"); -} - TEST(PDBFPOProgramToDWARFExpressionTests, SingleAssignmentRegisterRef) { CheckValidProgramTranslation("$T0 $ebp = ", "$T0", "DW_OP_breg6 +0"); } -TEST(PDBFPOProgramToDWARFExpressionTests, SingleAssignmentExpressionPlus) { - CheckValidProgramTranslation("$T0 $ebp 4 + = ", "$T0", - "DW_OP_breg6 +0, DW_OP_constu 0x4, DW_OP_plus "); -} - -TEST(PDBFPOProgramToDWARFExpressionTests, SingleAssignmentExpressionDeref) { - CheckValidProgramTranslation("$T0 $ebp ^ = ", "$T0", - "DW_OP_breg6 +0, DW_OP_deref "); -} - -TEST(PDBFPOProgramToDWARFExpressionTests, SingleAssignmentExpressionMinus) { - CheckValidProgramTranslation( - "$T0 $ebp 4 - = ", "$T0", - "DW_OP_breg6 +0, DW_OP_constu 0x4, DW_OP_minus "); -} - -TEST(PDBFPOProgramToDWARFExpressionTests, SingleAssignmentExpressionAlign) { - CheckValidProgramTranslation("$T0 $ebp 128 @ = ", "$T0", - "DW_OP_breg6 +0, DW_OP_constu 0x80, DW_OP_lit1 " - ", DW_OP_minus , DW_OP_not , DW_OP_and "); -} - TEST(PDBFPOProgramToDWARFExpressionTests, MultipleIndependentAssignments) { CheckValidProgramTranslation("$T1 1 = $T0 0 =", "$T0", "DW_OP_constu 0x0"); }