//===-- PostfixExpression.cpp ---------------------------------------------===// // // 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 // //===----------------------------------------------------------------------===// // // This file implements support for postfix expressions found in several symbol // file formats, and their conversion to DWARF. // //===----------------------------------------------------------------------===// #include "lldb/Symbol/PostfixExpression.h" #include "lldb/Core/dwarf.h" #include "lldb/Utility/Stream.h" #include "llvm/ADT/StringExtras.h" using namespace lldb_private; using namespace lldb_private::postfix; using namespace lldb_private::dwarf; static llvm::Optional GetBinaryOpType(llvm::StringRef token) { if (token.size() != 1) return llvm::None; switch (token[0]) { case '@': return BinaryOpNode::Align; case '-': return BinaryOpNode::Minus; case '+': return BinaryOpNode::Plus; } return llvm::None; } static llvm::Optional GetUnaryOpType(llvm::StringRef token) { if (token == "^") return UnaryOpNode::Deref; return llvm::None; } Node *postfix::ParseOneExpression(llvm::StringRef expr, llvm::BumpPtrAllocator &alloc) { llvm::SmallVector stack; llvm::StringRef token; while (std::tie(token, expr) = getToken(expr), !token.empty()) { if (auto op_type = GetBinaryOpType(token)) { // token is binary operator if (stack.size() < 2) return nullptr; Node *right = stack.pop_back_val(); Node *left = stack.pop_back_val(); stack.push_back(MakeNode(alloc, *op_type, *left, *right)); continue; } if (auto op_type = GetUnaryOpType(token)) { // token is unary operator if (stack.empty()) return nullptr; Node *operand = stack.pop_back_val(); stack.push_back(MakeNode(alloc, *op_type, *operand)); continue; } int64_t value; if (to_integer(token, value, 10)) { // token is integer literal stack.push_back(MakeNode(alloc, value)); continue; } stack.push_back(MakeNode(alloc, token)); } if (stack.size() != 1) return nullptr; return stack.back(); } std::vector> postfix::ParseFPOProgram(llvm::StringRef prog, llvm::BumpPtrAllocator &alloc) { llvm::SmallVector exprs; prog.split(exprs, '='); if (exprs.empty() || !exprs.back().trim().empty()) return {}; exprs.pop_back(); std::vector> result; for (llvm::StringRef expr : exprs) { llvm::StringRef lhs; std::tie(lhs, expr) = getToken(expr); Node *rhs = ParseOneExpression(expr, alloc); if (!rhs) return {}; result.emplace_back(lhs, rhs); } return result; } namespace { class SymbolResolver : public Visitor { public: SymbolResolver(llvm::function_ref replacer) : m_replacer(replacer) {} using Visitor::Dispatch; private: bool Visit(BinaryOpNode &binary, Node *&) override { return Dispatch(binary.Left()) && Dispatch(binary.Right()); } bool Visit(InitialValueNode &, Node *&) override { return true; } bool Visit(IntegerNode &, Node *&) override { return true; } bool Visit(RegisterNode &, Node *&) override { return true; } bool Visit(SymbolNode &symbol, Node *&ref) override { if (Node *replacement = m_replacer(symbol)) { ref = replacement; if (replacement != &symbol) return Dispatch(ref); return true; } return false; } bool Visit(UnaryOpNode &unary, Node *&) override { return Dispatch(unary.Operand()); } llvm::function_ref m_replacer; }; class DWARFCodegen : public Visitor<> { public: DWARFCodegen(Stream &stream) : m_out_stream(stream) {} using Visitor<>::Dispatch; private: void Visit(BinaryOpNode &binary, Node *&) override; void Visit(InitialValueNode &val, Node *&) override; void Visit(IntegerNode &integer, Node *&) override { m_out_stream.PutHex8(DW_OP_consts); m_out_stream.PutSLEB128(integer.GetValue()); ++m_stack_depth; } void Visit(RegisterNode ®, Node *&) override; void Visit(SymbolNode &symbol, Node *&) override { llvm_unreachable("Symbols should have been resolved by now!"); } void Visit(UnaryOpNode &unary, Node *&) override; Stream &m_out_stream; /// The number keeping track of the evaluation stack depth at any given /// moment. Used for implementing InitialValueNodes. We start with /// m_stack_depth = 1, assuming that the initial value is already on the /// stack. This initial value will be the value of all InitialValueNodes. If /// the expression does not contain InitialValueNodes, then m_stack_depth is /// not used, and the generated expression will run correctly even without an /// initial value. size_t m_stack_depth = 1; }; } // 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; } --m_stack_depth; // Two pops, one push. } void DWARFCodegen::Visit(InitialValueNode &, Node *&) { // We never go below the initial stack, so we can pick the initial value from // the bottom of the stack at any moment. assert(m_stack_depth >= 1); m_out_stream.PutHex8(DW_OP_pick); m_out_stream.PutHex8(m_stack_depth - 1); ++m_stack_depth; } 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); ++m_stack_depth; } void DWARFCodegen::Visit(UnaryOpNode &unary, Node *&) { Dispatch(unary.Operand()); switch (unary.GetOpType()) { case UnaryOpNode::Deref: m_out_stream.PutHex8(DW_OP_deref); break; } // Stack depth unchanged. } bool postfix::ResolveSymbols( Node *&node, llvm::function_ref replacer) { return SymbolResolver(replacer).Dispatch(node); } void postfix::ToDWARF(Node &node, Stream &stream) { Node *ptr = &node; DWARFCodegen(stream).Dispatch(ptr); }