1 | //===-- PostfixExpression.cpp ---------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This file implements support for postfix expressions found in several symbol |
10 | // file formats, and their conversion to DWARF. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "lldb/Symbol/PostfixExpression.h" |
15 | #include "lldb/Core/dwarf.h" |
16 | #include "lldb/Utility/Stream.h" |
17 | #include "llvm/ADT/StringExtras.h" |
18 | #include <optional> |
19 | |
20 | using namespace lldb_private; |
21 | using namespace lldb_private::postfix; |
22 | using namespace lldb_private::dwarf; |
23 | |
24 | static std::optional<BinaryOpNode::OpType> |
25 | GetBinaryOpType(llvm::StringRef token) { |
26 | if (token.size() != 1) |
27 | return std::nullopt; |
28 | switch (token[0]) { |
29 | case '@': |
30 | return BinaryOpNode::Align; |
31 | case '-': |
32 | return BinaryOpNode::Minus; |
33 | case '+': |
34 | return BinaryOpNode::Plus; |
35 | } |
36 | return std::nullopt; |
37 | } |
38 | |
39 | static std::optional<UnaryOpNode::OpType> |
40 | GetUnaryOpType(llvm::StringRef token) { |
41 | if (token == "^" ) |
42 | return UnaryOpNode::Deref; |
43 | return std::nullopt; |
44 | } |
45 | |
46 | Node *postfix::ParseOneExpression(llvm::StringRef expr, |
47 | llvm::BumpPtrAllocator &alloc) { |
48 | llvm::SmallVector<Node *, 4> stack; |
49 | |
50 | llvm::StringRef token; |
51 | while (std::tie(args&: token, args&: expr) = getToken(Source: expr), !token.empty()) { |
52 | if (auto op_type = GetBinaryOpType(token)) { |
53 | // token is binary operator |
54 | if (stack.size() < 2) |
55 | return nullptr; |
56 | |
57 | Node *right = stack.pop_back_val(); |
58 | Node *left = stack.pop_back_val(); |
59 | stack.push_back(Elt: MakeNode<BinaryOpNode>(alloc, args&: *op_type, args&: *left, args&: *right)); |
60 | continue; |
61 | } |
62 | |
63 | if (auto op_type = GetUnaryOpType(token)) { |
64 | // token is unary operator |
65 | if (stack.empty()) |
66 | return nullptr; |
67 | |
68 | Node *operand = stack.pop_back_val(); |
69 | stack.push_back(Elt: MakeNode<UnaryOpNode>(alloc, args&: *op_type, args&: *operand)); |
70 | continue; |
71 | } |
72 | |
73 | int64_t value; |
74 | if (to_integer(S: token, Num&: value, Base: 10)) { |
75 | // token is integer literal |
76 | stack.push_back(Elt: MakeNode<IntegerNode>(alloc, args&: value)); |
77 | continue; |
78 | } |
79 | |
80 | stack.push_back(Elt: MakeNode<SymbolNode>(alloc, args&: token)); |
81 | } |
82 | |
83 | if (stack.size() != 1) |
84 | return nullptr; |
85 | |
86 | return stack.back(); |
87 | } |
88 | |
89 | std::vector<std::pair<llvm::StringRef, Node *>> |
90 | postfix::ParseFPOProgram(llvm::StringRef prog, llvm::BumpPtrAllocator &alloc) { |
91 | llvm::SmallVector<llvm::StringRef, 4> exprs; |
92 | prog.split(A&: exprs, Separator: '='); |
93 | if (exprs.empty() || !exprs.back().trim().empty()) |
94 | return {}; |
95 | exprs.pop_back(); |
96 | |
97 | std::vector<std::pair<llvm::StringRef, Node *>> result; |
98 | for (llvm::StringRef expr : exprs) { |
99 | llvm::StringRef lhs; |
100 | std::tie(args&: lhs, args&: expr) = getToken(Source: expr); |
101 | Node *rhs = ParseOneExpression(expr, alloc); |
102 | if (!rhs) |
103 | return {}; |
104 | result.emplace_back(args&: lhs, args&: rhs); |
105 | } |
106 | return result; |
107 | } |
108 | |
109 | namespace { |
110 | class SymbolResolver : public Visitor<bool> { |
111 | public: |
112 | SymbolResolver(llvm::function_ref<Node *(SymbolNode &symbol)> replacer) |
113 | : m_replacer(replacer) {} |
114 | |
115 | using Visitor<bool>::Dispatch; |
116 | |
117 | private: |
118 | bool Visit(BinaryOpNode &binary, Node *&) override { |
119 | return Dispatch(node&: binary.Left()) && Dispatch(node&: binary.Right()); |
120 | } |
121 | |
122 | bool Visit(InitialValueNode &, Node *&) override { return true; } |
123 | bool Visit(IntegerNode &, Node *&) override { return true; } |
124 | bool Visit(RegisterNode &, Node *&) override { return true; } |
125 | |
126 | bool Visit(SymbolNode &symbol, Node *&ref) override { |
127 | if (Node *replacement = m_replacer(symbol)) { |
128 | ref = replacement; |
129 | if (replacement != &symbol) |
130 | return Dispatch(node&: ref); |
131 | return true; |
132 | } |
133 | return false; |
134 | } |
135 | |
136 | bool Visit(UnaryOpNode &unary, Node *&) override { |
137 | return Dispatch(node&: unary.Operand()); |
138 | } |
139 | |
140 | llvm::function_ref<Node *(SymbolNode &symbol)> m_replacer; |
141 | }; |
142 | |
143 | class DWARFCodegen : public Visitor<> { |
144 | public: |
145 | DWARFCodegen(Stream &stream) : m_out_stream(stream) {} |
146 | |
147 | using Visitor<>::Dispatch; |
148 | |
149 | private: |
150 | void Visit(BinaryOpNode &binary, Node *&) override; |
151 | |
152 | void Visit(InitialValueNode &val, Node *&) override; |
153 | |
154 | void Visit(IntegerNode &integer, Node *&) override { |
155 | m_out_stream.PutHex8(uvalue: DW_OP_consts); |
156 | m_out_stream.PutSLEB128(uval: integer.GetValue()); |
157 | ++m_stack_depth; |
158 | } |
159 | |
160 | void Visit(RegisterNode ®, Node *&) override; |
161 | |
162 | void Visit(SymbolNode &symbol, Node *&) override { |
163 | llvm_unreachable("Symbols should have been resolved by now!" ); |
164 | } |
165 | |
166 | void Visit(UnaryOpNode &unary, Node *&) override; |
167 | |
168 | Stream &m_out_stream; |
169 | |
170 | /// The number keeping track of the evaluation stack depth at any given |
171 | /// moment. Used for implementing InitialValueNodes. We start with |
172 | /// m_stack_depth = 1, assuming that the initial value is already on the |
173 | /// stack. This initial value will be the value of all InitialValueNodes. If |
174 | /// the expression does not contain InitialValueNodes, then m_stack_depth is |
175 | /// not used, and the generated expression will run correctly even without an |
176 | /// initial value. |
177 | size_t m_stack_depth = 1; |
178 | }; |
179 | } // namespace |
180 | |
181 | void DWARFCodegen::Visit(BinaryOpNode &binary, Node *&) { |
182 | Dispatch(node&: binary.Left()); |
183 | Dispatch(node&: binary.Right()); |
184 | |
185 | switch (binary.GetOpType()) { |
186 | case BinaryOpNode::Plus: |
187 | m_out_stream.PutHex8(uvalue: DW_OP_plus); |
188 | // NOTE: can be optimized by using DW_OP_plus_uconst opcpode |
189 | // if right child node is constant value |
190 | break; |
191 | case BinaryOpNode::Minus: |
192 | m_out_stream.PutHex8(uvalue: DW_OP_minus); |
193 | break; |
194 | case BinaryOpNode::Align: |
195 | // emit align operator a @ b as |
196 | // a & ~(b - 1) |
197 | // NOTE: implicitly assuming that b is power of 2 |
198 | m_out_stream.PutHex8(uvalue: DW_OP_lit1); |
199 | m_out_stream.PutHex8(uvalue: DW_OP_minus); |
200 | m_out_stream.PutHex8(uvalue: DW_OP_not); |
201 | |
202 | m_out_stream.PutHex8(uvalue: DW_OP_and); |
203 | break; |
204 | } |
205 | --m_stack_depth; // Two pops, one push. |
206 | } |
207 | |
208 | void DWARFCodegen::Visit(InitialValueNode &, Node *&) { |
209 | // We never go below the initial stack, so we can pick the initial value from |
210 | // the bottom of the stack at any moment. |
211 | assert(m_stack_depth >= 1); |
212 | m_out_stream.PutHex8(uvalue: DW_OP_pick); |
213 | m_out_stream.PutHex8(uvalue: m_stack_depth - 1); |
214 | ++m_stack_depth; |
215 | } |
216 | |
217 | void DWARFCodegen::Visit(RegisterNode ®, Node *&) { |
218 | uint32_t reg_num = reg.GetRegNum(); |
219 | assert(reg_num != LLDB_INVALID_REGNUM); |
220 | |
221 | if (reg_num > 31) { |
222 | m_out_stream.PutHex8(uvalue: DW_OP_bregx); |
223 | m_out_stream.PutULEB128(uval: reg_num); |
224 | } else |
225 | m_out_stream.PutHex8(uvalue: DW_OP_breg0 + reg_num); |
226 | |
227 | m_out_stream.PutSLEB128(uval: 0); |
228 | ++m_stack_depth; |
229 | } |
230 | |
231 | void DWARFCodegen::Visit(UnaryOpNode &unary, Node *&) { |
232 | Dispatch(node&: unary.Operand()); |
233 | |
234 | switch (unary.GetOpType()) { |
235 | case UnaryOpNode::Deref: |
236 | m_out_stream.PutHex8(uvalue: DW_OP_deref); |
237 | break; |
238 | } |
239 | // Stack depth unchanged. |
240 | } |
241 | |
242 | bool postfix::ResolveSymbols( |
243 | Node *&node, llvm::function_ref<Node *(SymbolNode &)> replacer) { |
244 | return SymbolResolver(replacer).Dispatch(node); |
245 | } |
246 | |
247 | void postfix::ToDWARF(Node &node, Stream &stream) { |
248 | Node *ptr = &node; |
249 | DWARFCodegen(stream).Dispatch(node&: ptr); |
250 | } |
251 | |