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
308 // Literals.
309 case op_lit_uint:
310 data.Push(cur_block.getULEB128(pc));
311 continue;
312 case op_lit_int:
313 data.Push(cur_block.getSLEB128(pc));
314 continue;
315 case op_lit_selector:
316 data.Push(Selectors(cur_block.getU8(pc)));
317 continue;
318 case op_lit_string: {
319 uint64_t length = cur_block.getULEB128(pc);
320 llvm::StringRef bytes = cur_block.getBytes(pc, length);
321 data.Push(bytes.str());
322 continue;
323 }
324 case op_as_uint: {
326 uint64_t casted;
327 int64_t val = data.Pop<int64_t>();
328 memcpy(&casted, &val, sizeof(val));
329 data.Push(casted);
330 continue;
331 }
332 case op_as_int: {
334 int64_t casted;
335 uint64_t val = data.Pop<uint64_t>();
336 memcpy(&casted, &val, sizeof(val));
337 data.Push(casted);
338 continue;
339 }
340 case op_is_null: {
342 data.Push(data.Pop<ValueObjectSP>() ? (uint64_t)0 : (uint64_t)1);
343 continue;
344 }
345
346 // Arithmetic, logic, etc.
347#define BINOP_IMPL(OP, CHECK_ZERO) \
348 { \
349 TYPE_CHECK(Any, Any); \
350 auto y = data.PopAny(); \
351 if (std::holds_alternative<uint64_t>(y)) { \
352 if (CHECK_ZERO && !std::get<uint64_t>(y)) \
353 return error(#OP " by zero"); \
354 TYPE_CHECK(UInt); \
355 data.Push((uint64_t)(data.Pop<uint64_t>() OP std::get<uint64_t>(y))); \
356 } else if (std::holds_alternative<int64_t>(y)) { \
357 if (CHECK_ZERO && !std::get<int64_t>(y)) \
358 return error(#OP " by zero"); \
359 TYPE_CHECK(Int); \
360 data.Push((int64_t)(data.Pop<int64_t>() OP std::get<int64_t>(y))); \
361 } else \
362 return error("unsupported data types"); \
363 }
364#define BINOP(OP) BINOP_IMPL(OP, false)
365#define BINOP_CHECKZERO(OP) BINOP_IMPL(OP, true)
366 case op_plus:
367 BINOP(+);
368 continue;
369 case op_minus:
370 BINOP(-);
371 continue;
372 case op_mul:
373 BINOP(*);
374 continue;
375 case op_div:
377 continue;
378 case op_mod:
380 continue;
381 case op_shl:
382#define SHIFTOP(OP, LEFT) \
383 { \
384 TYPE_CHECK(Any, UInt); \
385 uint64_t y = data.Pop<uint64_t>(); \
386 if (y > 64) \
387 return error("shift out of bounds"); \
388 if (std::holds_alternative<uint64_t>(data.back())) { \
389 uint64_t x = data.Pop<uint64_t>(); \
390 data.Push(x OP y); \
391 } else if (std::holds_alternative<int64_t>(data.back())) { \
392 int64_t x = data.Pop<int64_t>(); \
393 if (x < 0 && LEFT) \
394 return error("left shift of negative value"); \
395 if (y > 64) \
396 return error("shift out of bounds"); \
397 data.Push(x OP y); \
398 } else \
399 return error("unsupported data types"); \
400 }
401 SHIFTOP(<<, true);
402 continue;
403 case op_shr:
404 SHIFTOP(>>, false);
405 continue;
406 case op_and:
407 BINOP(&);
408 continue;
409 case op_or:
410 BINOP(|);
411 continue;
412 case op_xor:
413 BINOP(^);
414 continue;
415 case op_not:
417 data.Push(~data.Pop<uint64_t>());
418 continue;
419 case op_eq:
420 BINOP(==);
421 continue;
422 case op_neq:
423 BINOP(!=);
424 continue;
425 case op_lt:
426 BINOP(<);
427 continue;
428 case op_gt:
429 BINOP(>);
430 continue;
431 case op_le:
432 BINOP(<=);
433 continue;
434 case op_ge:
435 BINOP(>=);
436 continue;
437 case op_call: {
439 Selectors sel = data.Pop<Selectors>();
440
441 // Shorthand to improve readability.
442#define POP_VALOBJ(VALOBJ) \
443 auto VALOBJ = data.Pop<ValueObjectSP>(); \
444 if (!VALOBJ) \
445 return error("null object");
446
447 auto sel_error = [&](const char *msg) {
448 return llvm::createStringError("{0} (opcode={1}, selector={2})", msg,
449 toString(opcode).c_str(),
450 toString(sel).c_str());
451 };
452
453 switch (sel) {
454 case sel_summary: {
456 POP_VALOBJ(valobj);
457 const char *summary = valobj->GetSummaryAsCString();
458 data.Push(summary ? std::string(valobj->GetSummaryAsCString())
459 : std::string());
460 break;
461 }
462 case sel_get_num_children: {
464 POP_VALOBJ(valobj);
465 auto result = valobj->GetNumChildren();
466 if (!result)
467 return result.takeError();
468 data.Push((uint64_t)*result);
469 break;
470 }
471 case sel_get_child_at_index: {
473 auto index = data.Pop<uint64_t>();
474 POP_VALOBJ(valobj);
475 data.Push(valobj->GetChildAtIndex(index));
476 break;
477 }
478 case sel_get_child_with_name: {
480 auto name = data.Pop<std::string>();
481 POP_VALOBJ(valobj);
482 data.Push(valobj->GetChildMemberWithName(name));
483 break;
484 }
485 case sel_get_child_index: {
487 auto name = data.Pop<std::string>();
488 POP_VALOBJ(valobj);
489 data.Push((uint64_t)valobj->GetIndexOfChildWithName(name));
490 break;
491 }
492 case sel_get_type: {
494 POP_VALOBJ(valobj);
495 // FIXME: do we need to control dynamic type resolution?
496 data.Push(valobj->GetTypeImpl().GetCompilerType(false));
497 break;
498 }
499 case sel_get_template_argument_type: {
501 auto index = data.Pop<uint64_t>();
502 auto type = data.Pop<CompilerType>();
503 // FIXME: There is more code in SBType::GetTemplateArgumentType().
504 data.Push(type.GetTypeTemplateArgument(index, true));
505 break;
506 }
507 case sel_get_value: {
509 POP_VALOBJ(valobj);
510 data.Push(std::string(valobj->GetValueAsCString()));
511 break;
512 }
513 case sel_get_value_as_unsigned: {
515 POP_VALOBJ(valobj);
516 bool success;
517 uint64_t val = valobj->GetValueAsUnsigned(0, &success);
518 data.Push(val);
519 if (!success)
520 return sel_error("failed to get value");
521 break;
522 }
523 case sel_get_value_as_signed: {
525 POP_VALOBJ(valobj);
526 bool success;
527 int64_t val = valobj->GetValueAsSigned(0, &success);
528 data.Push(val);
529 if (!success)
530 return sel_error("failed to get value");
531 break;
532 }
533 case sel_get_value_as_address: {
535 POP_VALOBJ(valobj);
536 bool success;
537 uint64_t addr = valobj->GetValueAsUnsigned(0, &success);
538 if (!success)
539 return sel_error("failed to get value");
540 if (auto process_sp = valobj->GetProcessSP())
541 addr = process_sp->FixDataAddress(addr);
542 data.Push(addr);
543 break;
544 }
545 case sel_cast: {
547 auto type = data.Pop<CompilerType>();
548 POP_VALOBJ(valobj);
549 data.Push(valobj->Cast(type));
550 break;
551 }
552 case sel_strlen: {
554 data.Push((uint64_t)data.Pop<std::string>().size());
555 break;
556 }
557 case sel_fmt: {
559 if (auto error = FormatImpl(data))
560 return error;
561 break;
562 }
563 default:
564 return sel_error("selector not implemented");
565 }
566 continue;
567 }
568 }
569 return error("opcode not implemented");
570 }
571 return pc.takeError();
572}
573} // namespace FormatterBytecode
574
575} // 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