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
13#include "lldb/lldb-forward.h"
14#include "llvm/ADT/StringExtras.h"
15#include "llvm/Support/DataExtractor.h"
16#include "llvm/Support/Error.h"
17#include "llvm/Support/ErrorExtras.h"
18#include "llvm/Support/Format.h"
19#include "llvm/Support/FormatProviders.h"
20#include "llvm/Support/FormatVariadicDetails.h"
21
22using namespace lldb;
23namespace lldb_private {
24
26 switch (op) {
27#define DEFINE_OPCODE(OP, MNEMONIC, NAME) \
28 case OP: { \
29 const char *s = MNEMONIC; \
30 return s ? s : #NAME; \
31 }
32#include "lldb/DataFormatters/FormatterBytecode.def"
33#undef DEFINE_OPCODE
34 }
35 return llvm::utostr(op);
36}
37
39 switch (sel) {
40#define DEFINE_SELECTOR(ID, NAME) \
41 case ID: \
42 return "@" #NAME;
43#include "lldb/DataFormatters/FormatterBytecode.def"
44#undef DEFINE_SELECTOR
45 }
46 return "@" + llvm::utostr(sel);
47}
48
50 switch (sig) {
51#define DEFINE_SIGNATURE(ID, NAME) \
52 case ID: \
53 return "@" #NAME;
54#include "lldb/DataFormatters/FormatterBytecode.def"
55#undef DEFINE_SIGNATURE
56 }
57 return llvm::utostr(sig);
58}
59
60std::string toString(const FormatterBytecode::DataStack &data) {
61 std::string s;
62 llvm::raw_string_ostream os(s);
63 os << "[ ";
64 for (auto &d : data) {
65 if (auto s = std::get_if<std::string>(&d))
66 os << '"' << *s << '"';
67 else if (auto u = std::get_if<uint64_t>(&d))
68 os << *u << 'u';
69 else if (auto i = std::get_if<int64_t>(&d))
70 os << *i;
71 else if (auto ap = std::get_if<llvm::APSInt>(&d))
72 os << *ap;
73 else if (auto valobj = std::get_if<ValueObjectSP>(&d)) {
74 if (!valobj->get())
75 os << "null";
76 else
77 os << "object(" << valobj->get()->GetValueAsCString() << ')';
78 } else if (auto type = std::get_if<CompilerType>(&d)) {
79 os << '(' << type->GetTypeName(true) << ')';
80 } else if (auto sel = std::get_if<FormatterBytecode::Selectors>(&d)) {
81 os << toString(*sel);
82 }
83 os << ' ';
84 }
85 os << ']';
86 return s;
87}
88
89namespace FormatterBytecode {
90
91/// Implement the @format function.
92static llvm::Error FormatImpl(DataStack &data) {
93 auto fmt = data.Pop<std::string>();
94 auto replacements =
95 llvm::formatv_object_base::parseFormatString(fmt, 0, false);
96 std::string s;
97 llvm::raw_string_ostream os(s);
98 unsigned num_args = 0;
99 for (const auto &r : replacements)
100 if (r.Type == llvm::ReplacementType::Format)
101 num_args = std::max(num_args, r.Index + 1);
102
103 if (data.size() < num_args)
104 return llvm::createStringError("not enough arguments");
105
106 for (const auto &r : replacements) {
107 if (r.Type == llvm::ReplacementType::Literal) {
108 os << r.Spec;
109 continue;
110 }
111 using namespace llvm::support::detail;
112 auto arg = data[data.size() - num_args + r.Index];
113 auto format = [&](FormatFunctorRef &&adapter) {
114 llvm::FmtAlign Align(adapter, r.Where, r.Width, r.Pad);
115 Align.format(os, r.Options);
116 };
117
118 if (auto s = std::get_if<std::string>(&arg))
119 format(FormatFunctor(s->c_str()));
120 else if (auto u = std::get_if<uint64_t>(&arg))
121 format(FormatFunctor(u));
122 else if (auto i = std::get_if<int64_t>(&arg))
123 format(FormatFunctor(i));
124 else if (auto ap = std::get_if<llvm::APSInt>(&arg))
125 format(FormatFunctor(*ap));
126 else if (auto valobj = std::get_if<ValueObjectSP>(&arg)) {
127 if (!valobj->get())
128 format(FormatFunctor("null object"));
129 else
130 format(FormatFunctor(valobj->get()->GetValueAsCString()));
131 } else if (auto type = std::get_if<CompilerType>(&arg))
132 format(FormatFunctor(type->GetDisplayTypeName()));
133 else if (auto sel = std::get_if<FormatterBytecode::Selectors>(&arg))
134 format(FormatFunctor(toString(*sel)));
135 }
136 data.Push(s);
137 return llvm::Error::success();
138}
139
140static llvm::Error TypeCheck(llvm::ArrayRef<DataStackElement> data,
141 DataType type) {
142 if (data.size() < 1)
143 return llvm::createStringError("not enough elements on data stack");
144
145 auto &elem = data.back();
146 switch (type) {
147 case Any:
148 break;
149 case String:
150 if (!std::holds_alternative<std::string>(elem))
151 return llvm::createStringError("expected String");
152 break;
153 case UInt:
154 if (!std::holds_alternative<uint64_t>(elem))
155 return llvm::createStringError("expected UInt");
156 break;
157 case Int:
158 if (!std::holds_alternative<int64_t>(elem))
159 return llvm::createStringError("expected Int");
160 break;
161 case Object:
162 if (!std::holds_alternative<ValueObjectSP>(elem))
163 return llvm::createStringError("expected Object");
164 break;
165 case Type:
166 if (!std::holds_alternative<CompilerType>(elem))
167 return llvm::createStringError("expected Type");
168 break;
169 case Selector:
170 if (!std::holds_alternative<Selectors>(elem))
171 return llvm::createStringError("expected Selector");
172 break;
173 case Integer:
174 if (!std::holds_alternative<llvm::APSInt>(elem))
175 return llvm::createStringError("expected Integer");
176 break;
177 }
178 return llvm::Error::success();
179}
180
181static llvm::Error TypeCheck(llvm::ArrayRef<DataStackElement> data,
182 DataType type1, DataType type2) {
183 if (auto error = TypeCheck(data, type2))
184 return error;
185 return TypeCheck(data.drop_back(), type1);
186}
187
188static llvm::Error TypeCheck(llvm::ArrayRef<DataStackElement> data,
189 DataType type1, DataType type2, DataType type3) {
190 if (auto error = TypeCheck(data, type3))
191 return error;
192 return TypeCheck(data.drop_back(1), type2, type1);
193}
194
195/// Wrap the result of a binary operator applied to two APSInts back into a
196/// DataStackElement. Comparison operators yield bool and need bit_width/
197/// is_unsigned to construct the boolean's APSInt representation; arithmetic
198/// operators already yield a correctly-tagged APSInt and ignore them.
199template <typename T>
200static DataStackElement WrapAPSIntResult(T result, unsigned bit_width,
201 bool is_unsigned) {
202 if constexpr (std::is_same_v<T, bool>)
203 return llvm::APSInt(llvm::APInt(bit_width, result), is_unsigned);
204 else
205 return DataStackElement(std::move(result));
206}
207
208llvm::Error Interpret(ControlStack &control, DataStack &data, Signatures sig) {
209 if (control.empty())
210 return llvm::Error::success();
211 // Since the only data types are single endian and ULEBs, the
212 // endianness should not matter.
213 llvm::DataExtractor cur_block(control.back(), true);
214 llvm::DataExtractor::Cursor pc(0);
215
216 while (!control.empty()) {
217 /// Activate the top most block from the control stack.
218 auto activate_block = [&]() {
219 // Save the return address.
220 if (control.size() > 1)
221 control[control.size() - 2] = cur_block.getData().drop_front(pc.tell());
222 cur_block = llvm::DataExtractor(control.back(), true);
223 if (pc)
224 pc = llvm::DataExtractor::Cursor(0);
225 };
226
227 /// Fetch the next byte in the instruction stream.
228 auto next_byte = [&]() -> uint8_t {
229 // At the end of the current block?
230 while (pc.tell() >= cur_block.size() && !control.empty()) {
231 if (control.size() == 1) {
232 control.pop_back();
233 return 0;
234 }
235 control.pop_back();
236 activate_block();
237 }
238
239 // Fetch the next instruction.
240 return cur_block.getU8(pc);
241 };
242
243 // Fetch the next opcode.
244 OpCodes opcode = (OpCodes)next_byte();
245 if (control.empty() || !pc)
246 return pc.takeError();
247
249 "[eval {0}] opcode={1}, control={2}, data={3}",
250 toString(sig), toString(opcode), control.size(),
251 toString(data));
252
253 // Various shorthands to improve the readability of error handling.
254#define TYPE_CHECK(...) \
255 if (auto error = TypeCheck(data, __VA_ARGS__)) \
256 return error;
257
258 auto error = [&](llvm::Twine msg) {
259 return llvm::createStringError(msg + "(opcode=" + toString(opcode) + ")");
260 };
261
262 switch (opcode) {
263 // Data stack manipulation.
264 case op_dup:
266 data.Push(data.back());
267 continue;
268 case op_drop:
270 data.pop_back();
271 continue;
272 case op_pick: {
274 uint64_t idx = data.Pop<uint64_t>();
275 if (idx >= data.size())
276 return error("index out of bounds");
277 data.Push(data[idx]);
278 continue;
279 }
280 case op_over:
282 data.Push(data[data.size() - 2]);
283 continue;
284 case op_swap: {
286 auto x = data.PopAny();
287 auto y = data.PopAny();
288 data.Push(x);
289 data.Push(y);
290 continue;
291 }
292 case op_rot: {
294 auto z = data.PopAny();
295 auto y = data.PopAny();
296 auto x = data.PopAny();
297 data.Push(z);
298 data.Push(x);
299 data.Push(y);
300 continue;
301 }
302
303 // Control stack manipulation.
304 case op_begin: {
305 uint64_t length = cur_block.getULEB128(pc);
306 if (!pc)
307 return pc.takeError();
308 llvm::StringRef block = cur_block.getBytes(pc, length);
309 if (!pc)
310 return pc.takeError();
311 control.push_back(block);
312 continue;
313 }
314 case op_if: {
315 auto cond = data.PopAny();
316 bool truthy;
317 if (auto *ap = std::get_if<llvm::APSInt>(&cond))
318 truthy = !ap->isZero();
319 else if (auto *u = std::get_if<uint64_t>(&cond))
320 // Deprecated.
321 truthy = *u != 0;
322 else
323 return error("expected Integer or UInt");
324 if (truthy) {
325 if (!cur_block.size())
326 return error("empty control stack");
327 activate_block();
328 } else
329 control.pop_back();
330 continue;
331 }
332 case op_ifelse: {
333 if (cur_block.size() < 2)
334 return error("empty control stack");
335 auto cond = data.PopAny();
336 bool truthy;
337 if (auto *ap = std::get_if<llvm::APSInt>(&cond))
338 truthy = !ap->isZero();
339 else if (auto *u = std::get_if<uint64_t>(&cond))
340 // Deprecated.
341 truthy = *u != 0;
342 else
343 return error("expected Integer or UInt");
344 if (!truthy)
345 control[control.size() - 2] = control.back();
346 control.pop_back();
347 activate_block();
348 continue;
349 }
350 case op_return:
351 control.clear();
352 return pc.takeError();
353
354 // Literals.
355 case op_lit_uint:
356 data.Push(cur_block.getULEB128(pc));
357 continue;
358 case op_lit_int:
359 data.Push(cur_block.getSLEB128(pc));
360 continue;
361 case op_lit_integer:
362 data.Push(cur_block.getSLEB128APSInt(pc));
363 continue;
364 case op_lit_selector:
365 data.Push(Selectors(cur_block.getU8(pc)));
366 continue;
367 case op_lit_string: {
368 uint64_t length = cur_block.getULEB128(pc);
369 llvm::StringRef bytes = cur_block.getBytes(pc, length);
370 data.Push(bytes.str());
371 continue;
372 }
373 case op_as_uint: {
375 uint64_t casted;
376 int64_t val = data.Pop<int64_t>();
377 memcpy(&casted, &val, sizeof(val));
378 data.Push(casted);
379 continue;
380 }
381 case op_as_int: {
383 int64_t casted;
384 uint64_t val = data.Pop<uint64_t>();
385 memcpy(&casted, &val, sizeof(val));
386 data.Push(casted);
387 continue;
388 }
389 case op_is_null: {
391 data.Push(data.Pop<ValueObjectSP>() ? (uint64_t)0 : (uint64_t)1);
392 continue;
393 }
394
395// Arithmetic operations.
396#define BINOP_IMPL(OP, CHECK_ZERO) \
397 { \
398 TYPE_CHECK(Any, Any); \
399 auto y = data.PopAny(); \
400 if (std::holds_alternative<uint64_t>(y)) { \
401 if (CHECK_ZERO && !std::get<uint64_t>(y)) \
402 return error(#OP " by zero"); \
403 TYPE_CHECK(UInt); \
404 data.Push((uint64_t)(data.Pop<uint64_t>() OP std::get<uint64_t>(y))); \
405 } else if (std::holds_alternative<int64_t>(y)) { \
406 if (CHECK_ZERO && !std::get<int64_t>(y)) \
407 return error(#OP " by zero"); \
408 TYPE_CHECK(Int); \
409 data.Push((int64_t)(data.Pop<int64_t>() OP std::get<int64_t>(y))); \
410 } else if (std::holds_alternative<llvm::APSInt>(y)) { \
411 TYPE_CHECK(Integer); \
412 llvm::APSInt rhs = std::get<llvm::APSInt>(y); \
413 llvm::APSInt lhs = data.Pop<llvm::APSInt>(); \
414 if (lhs.isUnsigned() || rhs.isUnsigned()) \
415 return error("unsupported unsigned value"); \
416 unsigned width = std::max(lhs.getBitWidth(), rhs.getBitWidth()); \
417 lhs = lhs.extend(width); \
418 rhs = rhs.extend(width); \
419 if (CHECK_ZERO && rhs.isZero()) \
420 return error(#OP " by zero"); \
421 data.Push(WrapAPSIntResult(lhs OP rhs, width, lhs.isUnsigned())); \
422 } else \
423 return error("unsupported data types"); \
424 }
425#define BINOP(OP) BINOP_IMPL(OP, false)
426#define BINOP_CHECKZERO(OP) BINOP_IMPL(OP, true)
427
428// Comparison operations.
429#define CMPOP(OP) \
430 { \
431 TYPE_CHECK(Any, Any); \
432 auto y = data.PopAny(); \
433 if (std::holds_alternative<uint64_t>(y)) { \
434 TYPE_CHECK(UInt); \
435 data.Push((uint64_t)(data.Pop<uint64_t>() OP std::get<uint64_t>(y))); \
436 } else if (std::holds_alternative<int64_t>(y)) { \
437 TYPE_CHECK(Int); \
438 data.Push((int64_t)(data.Pop<int64_t>() OP std::get<int64_t>(y))); \
439 } else if (std::holds_alternative<llvm::APSInt>(y)) { \
440 TYPE_CHECK(Integer); \
441 llvm::APSInt rhs = std::get<llvm::APSInt>(y); \
442 llvm::APSInt lhs = data.Pop<llvm::APSInt>(); \
443 if (lhs.isUnsigned() || rhs.isUnsigned()) \
444 return error("unsupported unsigned value"); \
445 unsigned width = std::max(lhs.getBitWidth(), rhs.getBitWidth()); \
446 lhs = lhs.extend(width); \
447 rhs = rhs.extend(width); \
448 data.Push(WrapAPSIntResult(lhs OP rhs, width, lhs.isUnsigned())); \
449 } else \
450 return error("unsupported data types"); \
451 }
452
453// Bitwise operations use an Integer's underlying bit pattern, not its
454// mathematical value (ie signed-ness is ignored). This means >> is always a
455// logical (zero-filling) shift, never an arithmetic shift. Mismatched bit
456// widths are implicitly zero-extended (not sign-extended).
457#define BITOP(OP) \
458 { \
459 TYPE_CHECK(Any, Any); \
460 auto y = data.PopAny(); \
461 if (std::holds_alternative<uint64_t>(y)) { \
462 TYPE_CHECK(UInt); \
463 data.Push((uint64_t)(data.Pop<uint64_t>() OP std::get<uint64_t>(y))); \
464 } else if (std::holds_alternative<int64_t>(y)) { \
465 TYPE_CHECK(Int); \
466 data.Push((int64_t)(data.Pop<int64_t>() OP std::get<int64_t>(y))); \
467 } else if (std::holds_alternative<llvm::APSInt>(y)) { \
468 TYPE_CHECK(Integer); \
469 llvm::APSInt rhs = std::get<llvm::APSInt>(y); \
470 llvm::APSInt lhs = data.Pop<llvm::APSInt>(); \
471 unsigned width = std::max(lhs.getBitWidth(), rhs.getBitWidth()); \
472 llvm::APInt lhs_bits = \
473 static_cast<const llvm::APInt &>(lhs).zext(width); \
474 llvm::APInt rhs_bits = \
475 static_cast<const llvm::APInt &>(rhs).zext(width); \
476 llvm::APInt bits = lhs_bits OP rhs_bits; \
477 data.Push(llvm::APSInt(std::move(bits), /*isUnsigned=*/false)); \
478 } else \
479 return error("unsupported data types"); \
480 }
481
482 case op_plus:
483 BINOP(+);
484 continue;
485 case op_minus:
486 BINOP(-);
487 continue;
488 case op_mul:
489 BINOP(*);
490 continue;
491 case op_div:
493 continue;
494 case op_mod:
496 continue;
497 case op_shl:
498#define SHIFTOP(OP, LEFT) \
499 { \
500 TYPE_CHECK(Any, UInt); \
501 uint64_t y = data.Pop<uint64_t>(); \
502 if (y > 64) \
503 return error("shift out of bounds"); \
504 if (std::holds_alternative<uint64_t>(data.back())) { \
505 uint64_t x = data.Pop<uint64_t>(); \
506 data.Push(x OP y); \
507 } else if (std::holds_alternative<int64_t>(data.back())) { \
508 int64_t x = data.Pop<int64_t>(); \
509 if (x < 0 && LEFT) \
510 return error("left shift of negative value"); \
511 if (y > 64) \
512 return error("shift out of bounds"); \
513 data.Push(x OP y); \
514 } else if (std::holds_alternative<llvm::APSInt>(data.back())) { \
515 llvm::APSInt x = data.Pop<llvm::APSInt>(); \
516 if (y > x.getBitWidth()) \
517 return error("shift out of bounds"); \
518 const llvm::APInt &bits = x; \
519 llvm::APInt shifted = \
520 LEFT ? bits.shl((unsigned)y) : bits.lshr((unsigned)y); \
521 data.Push(llvm::APSInt(std::move(shifted), /*isUnsigned=*/false)); \
522 } else \
523 return error("unsupported data types"); \
524 }
525 SHIFTOP(<<, true);
526 continue;
527 case op_shr:
528 SHIFTOP(>>, false);
529 continue;
530 case op_and:
531 BITOP(&);
532 continue;
533 case op_or:
534 BITOP(|);
535 continue;
536 case op_xor:
537 BITOP(^);
538 continue;
539 case op_not: {
541 auto x = data.PopAny();
542 if (std::holds_alternative<uint64_t>(x))
543 data.Push(~std::get<uint64_t>(x));
544 else if (auto *ap = std::get_if<llvm::APSInt>(&x)) {
545 llvm::APInt bits = ~static_cast<const llvm::APInt &>(*ap);
546 data.Push(llvm::APSInt(std::move(bits), /*isUnsigned=*/false));
547 } else
548 return error("unsupported data types");
549 continue;
550 }
551 case op_eq:
552 CMPOP(==);
553 continue;
554 case op_neq:
555 CMPOP(!=);
556 continue;
557 case op_lt:
558 CMPOP(<);
559 continue;
560 case op_gt:
561 CMPOP(>);
562 continue;
563 case op_le:
564 CMPOP(<=);
565 continue;
566 case op_ge:
567 CMPOP(>=);
568 continue;
569 case op_call: {
571 Selectors sel = data.Pop<Selectors>();
572
573 // Shorthand to improve readability.
574#define POP_VALOBJ(VALOBJ) \
575 auto VALOBJ = data.Pop<ValueObjectSP>(); \
576 if (!VALOBJ) \
577 return error("null object");
578
579 auto sel_error = [&](const char *msg) {
580 return llvm::createStringErrorV("{0} (opcode={1}, selector={2})", msg,
581 toString(opcode).c_str(),
582 toString(sel).c_str());
583 };
584
585 switch (sel) {
586 case sel_summary: {
588 POP_VALOBJ(valobj);
589 const char *summary = valobj->GetSummaryAsCString();
590 data.Push(summary ? std::string(valobj->GetSummaryAsCString())
591 : std::string());
592 break;
593 }
594 case sel_get_num_children: {
596 POP_VALOBJ(valobj);
597 auto result = valobj->GetNumChildren();
598 if (!result)
599 return result.takeError();
600 data.Push((uint64_t)*result);
601 break;
602 }
603 case sel_get_child_at_index: {
605 auto index = data.Pop<uint64_t>();
606 POP_VALOBJ(valobj);
607 data.Push(valobj->GetChildAtIndex(index));
608 break;
609 }
610 case sel_get_child_with_name: {
612 auto name = data.Pop<std::string>();
613 POP_VALOBJ(valobj);
614 data.Push(valobj->GetChildMemberWithName(name));
615 break;
616 }
617 case sel_get_child_index: {
619 auto name = data.Pop<std::string>();
620 POP_VALOBJ(valobj);
621 if (auto index_or_err = valobj->GetIndexOfChildWithName(name))
622 data.Push((uint64_t)*index_or_err);
623 else
624 return index_or_err.takeError();
625 break;
626 }
627 case sel_get_parent: {
629 POP_VALOBJ(valobj);
630 auto *parent = valobj->GetParent();
631 data.Push(parent ? parent->GetSP() : ValueObjectSP());
632 break;
633 }
634 case sel_get_type: {
636 POP_VALOBJ(valobj);
637 // FIXME: do we need to control dynamic type resolution?
638 data.Push(valobj->GetTypeImpl().GetCompilerType(false));
639 break;
640 }
641 case sel_get_template_argument_type: {
643 auto index = data.Pop<uint64_t>();
644 auto type = data.Pop<CompilerType>();
645 // FIXME: There is more code in SBType::GetTemplateArgumentType().
646 data.Push(type.GetTypeTemplateArgument(index, true));
647 break;
648 }
649 case sel_get_synthetic_value: {
651 POP_VALOBJ(valobj);
652 data.Push(valobj->GetSyntheticValue());
653 break;
654 }
655 case sel_get_non_synthetic_value: {
657 POP_VALOBJ(valobj);
658 data.Push(valobj->GetNonSyntheticValue());
659 break;
660 }
661 case sel_get_value: {
663 POP_VALOBJ(valobj);
664 data.Push(std::string(valobj->GetValueAsCString()));
665 break;
666 }
667 case sel_get_value_as_unsigned: {
669 POP_VALOBJ(valobj);
670 bool success;
671 uint64_t val = valobj->GetValueAsUnsigned(0, &success);
672 data.Push(val);
673 if (!success)
674 return sel_error("failed to get value");
675 break;
676 }
677 case sel_get_value_as_signed: {
679 POP_VALOBJ(valobj);
680 bool success;
681 int64_t val = valobj->GetValueAsSigned(0, &success);
682 data.Push(val);
683 if (!success)
684 return sel_error("failed to get value");
685 break;
686 }
687 case sel_get_value_as_address: {
689 POP_VALOBJ(valobj);
690 bool success;
691 uint64_t addr = valobj->GetValueAsUnsigned(0, &success);
692 if (!success)
693 return sel_error("failed to get value");
694 if (auto process_sp = valobj->GetProcessSP())
695 addr = process_sp->FixDataAddress(addr);
696 data.Push(addr);
697 break;
698 }
699 case sel_cast: {
701 auto type = data.Pop<CompilerType>();
702 POP_VALOBJ(valobj);
703 data.Push(valobj->Cast(type));
704 break;
705 }
706 case sel_clone: {
708 auto new_name = data.Pop<std::string>();
709 POP_VALOBJ(valobj);
710 data.Push(valobj->Clone(new_name));
711 break;
712 }
713 case sel_strlen: {
715 data.Push((uint64_t)data.Pop<std::string>().size());
716 break;
717 }
718 case sel_fmt: {
720 if (auto error = FormatImpl(data))
721 return error;
722 break;
723 }
724 default:
725 return sel_error("selector not implemented");
726 }
727 continue;
728 }
729 }
730 return error("opcode not implemented");
731 }
732 return pc.takeError();
733}
734} // namespace FormatterBytecode
735
736} // namespace lldb_private
static llvm::raw_ostream & error(Stream &strm)
#define BINOP(OP)
#define CMPOP(OP)
#define SHIFTOP(OP, LEFT)
#define BITOP(OP)
#define TYPE_CHECK(...)
#define POP_VALOBJ(VALOBJ)
#define BINOP_CHECKZERO(OP)
#define LLDB_LOG_VERBOSE(log,...)
Definition Log.h:382
Generic representation of a type in a programming language.
std::vector< ControlStackElement > ControlStack
std::variant< std::string, uint64_t, int64_t, lldb::ValueObjectSP, CompilerType, Selectors, llvm::APSInt > DataStackElement
static llvm::Error TypeCheck(llvm::ArrayRef< DataStackElement > data, DataType type)
static llvm::Error FormatImpl(DataStack &data)
Implement the @format function.
static DataStackElement WrapAPSIntResult(T result, unsigned bit_width, bool is_unsigned)
Wrap the result of a binary operator applied to two APSInts back into a DataStackElement.
llvm::Error Interpret(ControlStack &control, DataStack &data, Signatures sig)
@ Int
Deprecated: use Integer.
@ UInt
Deprecated: use Integer.
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:338
static uint32_t Align(uint32_t val, uint32_t alignment)
Definition ARMUtils.h:21
static uint32_t bits(const uint32_t val, const uint32_t msbit, const uint32_t lsbit)
Definition ARMUtils.h:265
std::string toString(FormatterBytecode::OpCodes op)
std::shared_ptr< lldb_private::ValueObject > ValueObjectSP