| //===-- 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" | |
| #include <optional> | |
| using namespace lldb_private; | |
| using namespace lldb_private::postfix; | |
| using namespace llvm::dwarf; | |
| static std::optional<BinaryOpNode::OpType> | |
| GetBinaryOpType(llvm::StringRef token) { | |
| if (token.size() != 1) | |
| return std::nullopt; | |
| switch (token[0]) { | |
| case '@': | |
| return BinaryOpNode::Align; | |
| case '-': | |
| return BinaryOpNode::Minus; | |
| case '+': | |
| return BinaryOpNode::Plus; | |
| } | |
| return std::nullopt; | |
| } | |
| static std::optional<UnaryOpNode::OpType> | |
| GetUnaryOpType(llvm::StringRef token) { | |
| if (token == "^") | |
| return UnaryOpNode::Deref; | |
| return std::nullopt; | |
| } | |
| Node *postfix::ParseOneExpression(llvm::StringRef expr, | |
| llvm::BumpPtrAllocator &alloc) { | |
| llvm::SmallVector<Node *, 4> 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<BinaryOpNode>(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<UnaryOpNode>(alloc, *op_type, *operand)); | |
| continue; | |
| } | |
| int64_t value; | |
| if (to_integer(token, value, 10)) { | |
| // token is integer literal | |
| stack.push_back(MakeNode<IntegerNode>(alloc, value)); | |
| continue; | |
| } | |
| stack.push_back(MakeNode<SymbolNode>(alloc, token)); | |
| } | |
| if (stack.size() != 1) | |
| return nullptr; | |
| return stack.back(); | |
| } | |
| std::vector<std::pair<llvm::StringRef, Node *>> | |
| postfix::ParseFPOProgram(llvm::StringRef prog, llvm::BumpPtrAllocator &alloc) { | |
| llvm::SmallVector<llvm::StringRef, 4> exprs; | |
| prog.split(exprs, '='); | |
| if (exprs.empty() || !exprs.back().trim().empty()) | |
| return {}; | |
| exprs.pop_back(); | |
| std::vector<std::pair<llvm::StringRef, Node *>> 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<bool> { | |
| public: | |
| SymbolResolver(llvm::function_ref<Node *(SymbolNode &symbol)> replacer) | |
| : m_replacer(replacer) {} | |
| using Visitor<bool>::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<Node *(SymbolNode &symbol)> 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<Node *(SymbolNode &)> replacer) { | |
| return SymbolResolver(replacer).Dispatch(node); | |
| } | |
| void postfix::ToDWARF(Node &node, Stream &stream) { | |
| Node *ptr = &node; | |
| DWARFCodegen(stream).Dispatch(ptr); | |
| } |