LLDB mainline
FormatterBytecode.cpp
Go to the documentation of this file.
1//===-- FormatterBytecode.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#include "FormatterBytecode.h"
12#include "llvm/ADT/StringExtras.h"
13#include "llvm/Support/DataExtractor.h"
14#include "llvm/Support/Format.h"
15#include "llvm/Support/FormatProviders.h"
16#include "llvm/Support/FormatVariadicDetails.h"
17
18using namespace lldb;
19namespace lldb_private {
20
22 switch (op) {
23#define DEFINE_OPCODE(OP, MNEMONIC, NAME) \
24 case OP: { \
25 const char *s = MNEMONIC; \
26 return s ? s : #NAME; \
27 }
28#include "FormatterBytecode.def"
29#undef DEFINE_SIGNATURE
30 }
31 return llvm::utostr(op);
32}
33
35 switch (sel) {
36#define DEFINE_SELECTOR(ID, NAME) \
37 case ID: \
38 return "@" #NAME;
39#include "FormatterBytecode.def"
40#undef DEFINE_SIGNATURE
41 }
42 return "@" + llvm::utostr(sel);
43}
44
46 switch (sig) {
47#define DEFINE_SIGNATURE(ID, NAME) \
48 case ID: \
49 return "@" #NAME;
50#include "FormatterBytecode.def"
51#undef DEFINE_SIGNATURE
52 }
53 return llvm::utostr(sig);
54}
55
56std::string toString(const FormatterBytecode::DataStack &data) {
57 std::string s;
58 llvm::raw_string_ostream os(s);
59 os << "[ ";
60 for (auto &d : data) {
61 if (auto s = std::get_if<std::string>(&d))
62 os << '"' << *s << '"';
63 else if (auto u = std::get_if<uint64_t>(&d))
64 os << *u << 'u';
65 else if (auto i = std::get_if<int64_t>(&d))
66 os << *i;
67 else if (auto valobj = std::get_if<ValueObjectSP>(&d)) {
68 if (!valobj->get())
69 os << "null";
70 else
71 os << "object(" << valobj->get()->GetValueAsCString() << ')';
72 } else if (auto type = std::get_if<CompilerType>(&d)) {
73 os << '(' << type->GetTypeName(true) << ')';
74 } else if (auto sel = std::get_if<FormatterBytecode::Selectors>(&d)) {
75 os << toString(*sel);
76 }
77 os << ' ';
78 }
79 os << ']';
80 return s;
81}
82
83namespace FormatterBytecode {
84
85/// Implement the @format function.
86static llvm::Error FormatImpl(DataStack &data) {
87 auto fmt = data.Pop<std::string>();
88 auto replacements =
89 llvm::formatv_object_base::parseFormatString(fmt, 0, false);
90 std::string s;
91 llvm::raw_string_ostream os(s);
92 unsigned num_args = 0;
93 for (const auto &r : replacements)
94 if (r.Type == llvm::ReplacementType::Format)
95 num_args = std::max(num_args, r.Index + 1);
96
97 if (data.size() < num_args)
98 return llvm::createStringError("not enough arguments");
99
100 for (const auto &r : replacements) {
101 if (r.Type == llvm::ReplacementType::Literal) {
102 os << r.Spec;
103 continue;
104 }
105 using namespace llvm::support::detail;
106 auto arg = data[data.size() - num_args + r.Index];
107 auto format = [&](format_adapter &&adapter) {
108 llvm::FmtAlign Align(adapter, r.Where, r.Width, r.Pad);
109 Align.format(os, r.Options);
110 };
111
112 if (auto s = std::get_if<std::string>(&arg))
113 format(build_format_adapter(s->c_str()));
114 else if (auto u = std::get_if<uint64_t>(&arg))
115 format(build_format_adapter(u));
116 else if (auto i = std::get_if<int64_t>(&arg))
117 format(build_format_adapter(i));
118 else if (auto valobj = std::get_if<ValueObjectSP>(&arg)) {
119 if (!valobj->get())
120 format(build_format_adapter("null object"));
121 else
122 format(build_format_adapter(valobj->get()->GetValueAsCString()));
123 } else if (auto type = std::get_if<CompilerType>(&arg))
124 format(build_format_adapter(type->GetDisplayTypeName()));
125 else if (auto sel = std::get_if<FormatterBytecode::Selectors>(&arg))
126 format(build_format_adapter(toString(*sel)));
127 }
128 data.Push(s);
129 return llvm::Error::success();
130}
131
132static llvm::Error TypeCheck(llvm::ArrayRef<DataStackElement> data,
133 DataType type) {
134 if (data.size() < 1)
135 return llvm::createStringError("not enough elements on data stack");
136
137 auto &elem = data.back();
138 switch (type) {
139 case Any:
140 break;
141 case String:
142 if (!std::holds_alternative<std::string>(elem))
143 return llvm::createStringError("expected String");
144 break;
145 case UInt:
146 if (!std::holds_alternative<uint64_t>(elem))
147 return llvm::createStringError("expected UInt");
148 break;
149 case Int:
150 if (!std::holds_alternative<int64_t>(elem))
151 return llvm::createStringError("expected Int");
152 break;
153 case Object:
154 if (!std::holds_alternative<ValueObjectSP>(elem))
155 return llvm::createStringError("expected Object");
156 break;
157 case Type:
158 if (!std::holds_alternative<CompilerType>(elem))
159 return llvm::createStringError("expected Type");
160 break;
161 case Selector:
162 if (!std::holds_alternative<Selectors>(elem))
163 return llvm::createStringError("expected Selector");
164 break;
165 }
166 return llvm::Error::success();
167}
168
169static llvm::Error TypeCheck(llvm::ArrayRef<DataStackElement> data,
170 DataType type1, DataType type2) {
171 if (auto error = TypeCheck(data, type2))
172 return error;
173 return TypeCheck(data.drop_back(), type1);
174}
175
176static llvm::Error TypeCheck(llvm::ArrayRef<DataStackElement> data,
177 DataType type1, DataType type2, DataType type3) {
178 if (auto error = TypeCheck(data, type3))
179 return error;
180 return TypeCheck(data.drop_back(1), type2, type1);
181}
182
183llvm::Error Interpret(std::vector<ControlStackElement> &control,
184 DataStack &data, Selectors sel) {
185 if (control.empty())
186 return llvm::Error::success();
187 // Since the only data types are single endian and ULEBs, the
188 // endianness should not matter.
189 llvm::DataExtractor cur_block(control.back(), true, 64);
190 llvm::DataExtractor::Cursor pc(0);
191
192 while (!control.empty()) {
193 /// Activate the top most block from the control stack.
194 auto activate_block = [&]() {
195 // Save the return address.
196 if (control.size() > 1)
197 control[control.size() - 2] = cur_block.getData().drop_front(pc.tell());
198 cur_block = llvm::DataExtractor(control.back(), true, 64);
199 if (pc)
200 pc = llvm::DataExtractor::Cursor(0);
201 };
202
203 /// Fetch the next byte in the instruction stream.
204 auto next_byte = [&]() -> uint8_t {
205 // At the end of the current block?
206 while (pc.tell() >= cur_block.size() && !control.empty()) {
207 if (control.size() == 1) {
208 control.pop_back();
209 return 0;
210 }
211 control.pop_back();
212 activate_block();
213 }
214
215 // Fetch the next instruction.
216 return cur_block.getU8(pc);
217 };
218
219 // Fetch the next opcode.
220 OpCodes opcode = (OpCodes)next_byte();
221 if (control.empty() || !pc)
222 return pc.takeError();
223
225 "[eval {0}] opcode={1}, control={2}, data={3}", toString(sel),
226 toString(opcode), control.size(), toString(data));
227
228 // Various shorthands to improve the readability of error handling.
229#define TYPE_CHECK(...) \
230 if (auto error = TypeCheck(data, __VA_ARGS__)) \
231 return error;
232
233 auto error = [&](llvm::Twine msg) {
234 return llvm::createStringError(msg + "(opcode=" + toString(opcode) + ")");
235 };
236
237 switch (opcode) {
238 // Data stack manipulation.
239 case op_dup:
241 data.Push(data.back());
242 continue;
243 case op_drop:
245 data.pop_back();
246 continue;
247 case op_pick: {
249 uint64_t idx = data.Pop<uint64_t>();
250 if (idx >= data.size())
251 return error("index out of bounds");
252 data.Push(data[idx]);
253 continue;
254 }
255 case op_over:
257 data.Push(data[data.size() - 2]);
258 continue;
259 case op_swap: {
261 auto x = data.PopAny();
262 auto y = data.PopAny();
263 data.Push(x);
264 data.Push(y);
265 continue;
266 }
267 case op_rot: {
269 auto z = data.PopAny();
270 auto y = data.PopAny();
271 auto x = data.PopAny();
272 data.Push(z);
273 data.Push(x);
274 data.Push(y);
275 continue;
276 }
277
278 // Control stack manipulation.
279 case op_begin: {
280 uint64_t length = cur_block.getULEB128(pc);
281 if (!pc)
282 return pc.takeError();
283 llvm::StringRef block = cur_block.getBytes(pc, length);
284 if (!pc)
285 return pc.takeError();
286 control.push_back(block);
287 continue;
288 }
289 case op_if:
291 if (data.Pop<uint64_t>() != 0) {
292 if (!cur_block.size())
293 return error("empty control stack");
294 activate_block();
295 } else
296 control.pop_back();
297 continue;
298 case op_ifelse:
300 if (cur_block.size() < 2)
301 return error("empty control stack");
302 if (data.Pop<uint64_t>() == 0)
303 control[control.size() - 2] = control.back();
304 control.pop_back();
305 activate_block();
306 continue;
307 case op_return:
308 control.clear();
309 return pc.takeError();
310
311 // Literals.
312 case op_lit_uint:
313 data.Push(cur_block.getULEB128(pc));
314 continue;
315 case op_lit_int:
316 data.Push(cur_block.getSLEB128(pc));
317 continue;
318 case op_lit_selector:
319 data.Push(Selectors(cur_block.getU8(pc)));
320 continue;
321 case op_lit_string: {
322 uint64_t length = cur_block.getULEB128(pc);
323 llvm::StringRef bytes = cur_block.getBytes(pc, length);
324 data.Push(bytes.str());
325 continue;
326 }
327 case op_as_uint: {
329 uint64_t casted;
330 int64_t val = data.Pop<int64_t>();
331 memcpy(&casted, &val, sizeof(val));
332 data.Push(casted);
333 continue;
334 }
335 case op_as_int: {
337 int64_t casted;
338 uint64_t val = data.Pop<uint64_t>();
339 memcpy(&casted, &val, sizeof(val));
340 data.Push(casted);
341 continue;
342 }
343 case op_is_null: {
345 data.Push(data.Pop<ValueObjectSP>() ? (uint64_t)0 : (uint64_t)1);
346 continue;
347 }
348
349 // Arithmetic, logic, etc.
350#define BINOP_IMPL(OP, CHECK_ZERO) \
351 { \
352 TYPE_CHECK(Any, Any); \
353 auto y = data.PopAny(); \
354 if (std::holds_alternative<uint64_t>(y)) { \
355 if (CHECK_ZERO && !std::get<uint64_t>(y)) \
356 return error(#OP " by zero"); \
357 TYPE_CHECK(UInt); \
358 data.Push((uint64_t)(data.Pop<uint64_t>() OP std::get<uint64_t>(y))); \
359 } else if (std::holds_alternative<int64_t>(y)) { \
360 if (CHECK_ZERO && !std::get<int64_t>(y)) \
361 return error(#OP " by zero"); \
362 TYPE_CHECK(Int); \
363 data.Push((int64_t)(data.Pop<int64_t>() OP std::get<int64_t>(y))); \
364 } else \
365 return error("unsupported data types"); \
366 }
367#define BINOP(OP) BINOP_IMPL(OP, false)
368#define BINOP_CHECKZERO(OP) BINOP_IMPL(OP, true)
369 case op_plus:
370 BINOP(+);
371 continue;
372 case op_minus:
373 BINOP(-);
374 continue;
375 case op_mul:
376 BINOP(*);
377 continue;
378 case op_div:
380 continue;
381 case op_mod:
383 continue;
384 case op_shl:
385#define SHIFTOP(OP, LEFT) \
386 { \
387 TYPE_CHECK(Any, UInt); \
388 uint64_t y = data.Pop<uint64_t>(); \
389 if (y > 64) \
390 return error("shift out of bounds"); \
391 if (std::holds_alternative<uint64_t>(data.back())) { \
392 uint64_t x = data.Pop<uint64_t>(); \
393 data.Push(x OP y); \
394 } else if (std::holds_alternative<int64_t>(data.back())) { \
395 int64_t x = data.Pop<int64_t>(); \
396 if (x < 0 && LEFT) \
397 return error("left shift of negative value"); \
398 if (y > 64) \
399 return error("shift out of bounds"); \
400 data.Push(x OP y); \
401 } else \
402 return error("unsupported data types"); \
403 }
404 SHIFTOP(<<, true);
405 continue;
406 case op_shr:
407 SHIFTOP(>>, false);
408 continue;
409 case op_and:
410 BINOP(&);
411 continue;
412 case op_or:
413 BINOP(|);
414 continue;
415 case op_xor:
416 BINOP(^);
417 continue;
418 case op_not:
420 data.Push(~data.Pop<uint64_t>());
421 continue;
422 case op_eq:
423 BINOP(==);
424 continue;
425 case op_neq:
426 BINOP(!=);
427 continue;
428 case op_lt:
429 BINOP(<);
430 continue;
431 case op_gt:
432 BINOP(>);
433 continue;
434 case op_le:
435 BINOP(<=);
436 continue;
437 case op_ge:
438 BINOP(>=);
439 continue;
440 case op_call: {
442 Selectors sel = data.Pop<Selectors>();
443
444 // Shorthand to improve readability.
445#define POP_VALOBJ(VALOBJ) \
446 auto VALOBJ = data.Pop<ValueObjectSP>(); \
447 if (!VALOBJ) \
448 return error("null object");
449
450 auto sel_error = [&](const char *msg) {
451 return llvm::createStringError("{0} (opcode={1}, selector={2})", msg,
452 toString(opcode).c_str(),
453 toString(sel).c_str());
454 };
455
456 switch (sel) {
457 case sel_summary: {
459 POP_VALOBJ(valobj);
460 const char *summary = valobj->GetSummaryAsCString();
461 data.Push(summary ? std::string(valobj->GetSummaryAsCString())
462 : std::string());
463 break;
464 }
465 case sel_get_num_children: {
467 POP_VALOBJ(valobj);
468 auto result = valobj->GetNumChildren();
469 if (!result)
470 return result.takeError();
471 data.Push((uint64_t)*result);
472 break;
473 }
474 case sel_get_child_at_index: {
476 auto index = data.Pop<uint64_t>();
477 POP_VALOBJ(valobj);
478 data.Push(valobj->GetChildAtIndex(index));
479 break;
480 }
481 case sel_get_child_with_name: {
483 auto name = data.Pop<std::string>();
484 POP_VALOBJ(valobj);
485 data.Push(valobj->GetChildMemberWithName(name));
486 break;
487 }
488 case sel_get_child_index: {
490 auto name = data.Pop<std::string>();
491 POP_VALOBJ(valobj);
492 data.Push((uint64_t)valobj->GetIndexOfChildWithName(name));
493 break;
494 }
495 case sel_get_type: {
497 POP_VALOBJ(valobj);
498 // FIXME: do we need to control dynamic type resolution?
499 data.Push(valobj->GetTypeImpl().GetCompilerType(false));
500 break;
501 }
502 case sel_get_template_argument_type: {
504 auto index = data.Pop<uint64_t>();
505 auto type = data.Pop<CompilerType>();
506 // FIXME: There is more code in SBType::GetTemplateArgumentType().
507 data.Push(type.GetTypeTemplateArgument(index, true));
508 break;
509 }
510 case sel_get_value: {
512 POP_VALOBJ(valobj);
513 data.Push(std::string(valobj->GetValueAsCString()));
514 break;
515 }
516 case sel_get_value_as_unsigned: {
518 POP_VALOBJ(valobj);
519 bool success;
520 uint64_t val = valobj->GetValueAsUnsigned(0, &success);
521 data.Push(val);
522 if (!success)
523 return sel_error("failed to get value");
524 break;
525 }
526 case sel_get_value_as_signed: {
528 POP_VALOBJ(valobj);
529 bool success;
530 int64_t val = valobj->GetValueAsSigned(0, &success);
531 data.Push(val);
532 if (!success)
533 return sel_error("failed to get value");
534 break;
535 }
536 case sel_get_value_as_address: {
538 POP_VALOBJ(valobj);
539 bool success;
540 uint64_t addr = valobj->GetValueAsUnsigned(0, &success);
541 if (!success)
542 return sel_error("failed to get value");
543 if (auto process_sp = valobj->GetProcessSP())
544 addr = process_sp->FixDataAddress(addr);
545 data.Push(addr);
546 break;
547 }
548 case sel_cast: {
550 auto type = data.Pop<CompilerType>();
551 POP_VALOBJ(valobj);
552 data.Push(valobj->Cast(type));
553 break;
554 }
555 case sel_strlen: {
557 data.Push((uint64_t)data.Pop<std::string>().size());
558 break;
559 }
560 case sel_fmt: {
562 if (auto error = FormatImpl(data))
563 return error;
564 break;
565 }
566 default:
567 return sel_error("selector not implemented");
568 }
569 continue;
570 }
571 }
572 return error("opcode not implemented");
573 }
574 return pc.takeError();
575}
576} // namespace FormatterBytecode
577
578} // namespace lldb_private
static llvm::raw_ostream & error(Stream &strm)
#define BINOP(OP)
#define SHIFTOP(OP, LEFT)
#define TYPE_CHECK(...)
#define POP_VALOBJ(VALOBJ)
#define BINOP_CHECKZERO(OP)
#define LLDB_LOGV(log,...)
Definition: Log.h:383
Generic representation of a type in a programming language.
Definition: CompilerType.h:36
llvm::Error Interpret(std::vector< ControlStackElement > &control, DataStack &data, Selectors sel)
static llvm::Error TypeCheck(llvm::ArrayRef< DataStackElement > data, DataType type)
static llvm::Error FormatImpl(DataStack &data)
Implement the @format function.
A class that represents a running process on the host machine.
Log * GetLog(Cat mask)
Retrieve the Log object for the channel associated with the given log enum.
Definition: Log.h:332
static uint32_t Align(uint32_t val, uint32_t alignment)
Definition: ARMUtils.h:21
const char * toString(AppleArm64ExceptionClass EC)
Definition: SBAddress.h:15
std::shared_ptr< lldb_private::ValueObject > ValueObjectSP
Definition: lldb-forward.h:484