AsmGrader 0.0.0
Loading...
Searching...
No Matches
registers_state.hpp
Go to the documentation of this file.
1
3
4#pragma once
5
10#include <asmgrader/logging.hpp>
12
13#include <boost/preprocessor/arithmetic/sub.hpp>
14#include <boost/preprocessor/repetition/for.hpp>
15#include <boost/preprocessor/repetition/repeat.hpp>
16#include <boost/preprocessor/repetition/repeat_from_to.hpp>
17#include <boost/preprocessor/seq/for_each.hpp>
18#include <boost/preprocessor/stringize.hpp>
19#include <boost/preprocessor/tuple/to_seq.hpp>
20#include <fmt/base.h>
21#include <fmt/color.h>
22#include <fmt/compile.h>
23#include <fmt/format.h>
24#include <fmt/ranges.h>
25#include <gsl/util>
26#include <gsl/zstring>
27#include <range/v3/action/insert.hpp>
28#include <range/v3/algorithm/copy.hpp>
29#include <range/v3/algorithm/equal.hpp>
30#include <range/v3/algorithm/find.hpp>
31#include <range/v3/range/concepts.hpp>
32#include <range/v3/utility/memory.hpp>
33#include <range/v3/view/map.hpp>
34#include <range/v3/view/set_algorithm.hpp>
35#include <range/v3/view/subrange.hpp>
36#include <range/v3/view/transform.hpp>
37
38#include <array>
39#include <bit>
40#include <concepts>
41#include <cstddef>
42#include <cstdint>
43#include <cstring>
44#include <iterator>
45#include <limits>
46#include <stdexcept>
47#include <string>
48#include <string_view>
49#include <tuple>
50#include <type_traits>
51#include <utility>
52#include <vector>
53
54#include <sys/user.h> // user_regs_struct, ([x86_64] user_fpregs_struct | [aarch64] user_fpsimd_struct)
55
56namespace asmgrader {
57namespace detail {
58
61template <template <ProcessorKind> typename Derived, typename BaseType, ProcessorKind Arch>
63
64template <template <ProcessorKind> typename Derived, typename BaseType, ProcessorKind Arch>
65 requires(Arch == SYSTEM_PROCESSOR)
67{
68 constexpr RegisterBaseImpl() = default;
69
70 template <ProcessorKind OtherArch>
71 constexpr explicit RegisterBaseImpl(const Derived<OtherArch>* /*arch_alternative*/) {}
72
73 constexpr explicit RegisterBaseImpl(BaseType value)
74 : value_{value} {}
75
76 constexpr auto& operator=(BaseType rhs) {
77 value_ = rhs;
78 return *static_cast<Derived<Arch>*>(this);
79 }
80
81 constexpr BaseType& get_value() { return value_; }
82
83 constexpr const BaseType& get_value() const { return value_; }
84
85 template <std::integral IntType>
86 constexpr IntType as() const {
87 // FIXME: Something something endianness
88
89 // Set all bits (either 2's complement, or undeflow of unsigned integer)
90 constexpr auto ALL_BITS = static_cast<IntType>(-1);
91
92 // NOLINTNEXTLINE(bugprone-signed-char-misuse,cert-str34-c)
93 constexpr auto MASK = static_cast<u64>(ALL_BITS);
94
95 return static_cast<IntType>(MASK & value_);
96 }
97
98 constexpr auto operator<=>(const RegisterBaseImpl<Derived, BaseType, Arch>& rhs) const = default;
99
100private:
101 BaseType value_{};
102};
103
104template <template <ProcessorKind> typename Derived, typename BaseType, ProcessorKind Arch>
105 requires(Arch != SYSTEM_PROCESSOR)
107{
108 constexpr RegisterBaseImpl() = default;
109
110 template <ProcessorKind OtherArch>
111 constexpr explicit RegisterBaseImpl(Derived<OtherArch>* arch_alternative)
112 : alternative{arch_alternative} {}
113
114 constexpr explicit RegisterBaseImpl(BaseType /*value*/) {}
115
116 constexpr auto& operator=(BaseType rhs) {
117 get_value() = rhs;
118 return *this;
119 }
120
123 Derived<SYSTEM_PROCESSOR>* alternative{};
124
126 if (alternative == nullptr) {
127 constexpr auto MSG = "Attempting to access non-native register without a native alternative";
128 LOG_ERROR(MSG);
129 throw std::runtime_error(MSG);
130 }
131
132 LOG_WARN("Attempting to access non-native register. Defaulting to a native alternative");
133 }
134
135 constexpr decltype(auto) get_value() const {
136 check_log_alternative();
137 return alternative->get_value();
138 }
139
140 template <std::integral IntType>
141 constexpr IntType as() const {
142 check_log_alternative();
143 return alternative->template as<IntType>();
144 }
145
146 constexpr auto operator<=>(const RegisterBaseImpl<Derived, BaseType, Arch>& rhs) const = default;
147};
148
149} // namespace detail
150
152template <ProcessorKind Arch = SYSTEM_PROCESSOR>
153struct IntRegister : detail::RegisterBaseImpl<IntRegister, u64, Arch>
154{
155 using detail::RegisterBaseImpl<IntRegister, u64, Arch>::RegisterBaseImpl;
156 using detail::RegisterBaseImpl<IntRegister, u64, Arch>::operator=;
157
158 // NOLINTNEXTLINE(google-explicit-constructor)
159 /*implicit*/ constexpr operator u64() const {
161 }
162};
163
164static_assert(sizeof(IntRegister<>) == sizeof(u64));
165
166template <ProcessorKind Arch = SYSTEM_PROCESSOR>
167struct FloatingPointRegister : detail::RegisterBaseImpl<FloatingPointRegister, u128, Arch>
168{
169 using detail::RegisterBaseImpl<FloatingPointRegister, u128, Arch>::RegisterBaseImpl;
170 using detail::RegisterBaseImpl<FloatingPointRegister, u128, Arch>::operator=;
171
172 // FIXME: floating point trait that supports f128
173 template </*std::floating_point*/ typename FPType>
174 requires(Arch == SYSTEM_PROCESSOR)
175 static constexpr FloatingPointRegister from(FPType val) {
176 using IntType = sized_uint_t<sizeof(FPType)>;
177 auto int_val = std::bit_cast<IntType>(val);
178
179 return FloatingPointRegister{int_val};
180 }
181
182 // NOLINTNEXTLINE(google-explicit-constructor)
183 /*implicit*/ constexpr operator double() const {
184 return std::bit_cast<double>(
186 }
187
189
190 template <std::floating_point FPType>
191 constexpr FPType as() const {
192 using IntType = sized_uint_t<sizeof(FPType)>;
194
195 return std::bit_cast<FPType>(int_val);
196 }
197};
198
199static_assert(sizeof(FloatingPointRegister<>) == sizeof(u128));
200
201template <ProcessorKind Arch = SYSTEM_PROCESSOR>
202struct FlagsRegister : detail::RegisterBaseImpl<FlagsRegister, u64, Arch>
203{
204 using detail::RegisterBaseImpl<FlagsRegister, u64, Arch>::RegisterBaseImpl;
205 using detail::RegisterBaseImpl<FlagsRegister, u64, Arch>::operator=;
206
207 constexpr bool negative_set() const {
208 return (detail::RegisterBaseImpl<FlagsRegister, u64, Arch>::get_value() & NEGATIVE_FLAG_BIT) != 0U;
209 }
210
211 constexpr bool zero_set() const {
213 }
214
215 constexpr bool carry_set() const {
217 }
218
219 constexpr bool overflow_set() const {
220 return (detail::RegisterBaseImpl<FlagsRegister, u64, Arch>::get_value() & OVERFLOW_FLAG_BIT) != 0U;
221 }
222
223#if defined(ASMGRADER_AARCH64)
224 // Specification of pstate (for nzcv) obtained from:
225 // https://developer.arm.com/documentation/ddi0601/2025-06/AArch64-Registers/NZCV--Condition-Flags
226 static constexpr u64 NZCV_BASE_OFF = 28;
227 static constexpr u64 NEGATIVE_FLAG_BIT = 1U << (NZCV_BASE_OFF + 3);
228 static constexpr u64 ZERO_FLAG_BIT = 1U << (NZCV_BASE_OFF + 2);
229 static constexpr u64 CARRY_FLAG_BIT = 1U << (NZCV_BASE_OFF + 1);
230 static constexpr u64 OVERFLOW_FLAG_BIT = 1U << (NZCV_BASE_OFF + 0);
231
232 constexpr u64 nzcv() const {
233 constexpr u64 NZCV_BIT_MASK = 0xF;
234
235 return (detail::RegisterBaseImpl<FlagsRegister, u64, Arch>::get_value() >> NZCV_BASE_OFF) & NZCV_BIT_MASK;
236 }
237
238#elif defined(ASMGRADER_X86_64)
239 // Specification of eflags obtained from:
240 // https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html
241 // Volume 1 - 3.4.3.1 Status Flags
242 static constexpr u64 CARRY_FLAG_BIT = 1U << 0;
243 static constexpr u64 ZERO_FLAG_BIT = 1U << 6;
244 static constexpr u64 NEGATIVE_FLAG_BIT = 1U << 7;
245 static constexpr u64 OVERFLOW_FLAG_BIT = 1U << 11;
246#endif
247};
248
249static_assert(sizeof(FlagsRegister<>) == sizeof(u64));
250
252{
253 //
254 // ##################### Aarch64
255 //
256#define DEF_getter(name, expr) \
257 constexpr auto name() const { return expr; }
258
259// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
260#define NUM_GEN_REGISTERS 31
261// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
262#define NUM_FP_REGISTERS 32
263
264 // General registers (x0-x30)
265 std::array<IntRegister<ProcessorKind::Aarch64>, NUM_GEN_REGISTERS> regs;
266
267#define DEF_gen_register(z, num, _) DEF_getter(x##num, regs[num]) DEF_getter(w##num, static_cast<u32>(regs[num]))
269#undef DEF_gen_register
270
271 DEF_getter(fp, x29()); // frame pointer
272 DEF_getter(lr, x30()); // link register
275
277
278 // Floating-point registers (Q0-Q31)
279 std::array<FloatingPointRegister<ProcessorKind::Aarch64>, NUM_FP_REGISTERS> vregs;
280
281#define DEF_fp_register(z, num, _) \
282 DEF_getter(q##num, vregs[num]) DEF_getter(d##num, vregs[num].as<u64>()) DEF_getter(s##num, vregs[num].as<u32>()) \
283 DEF_getter(h##num, vregs[num].as<u16>())
285#undef DEF_fp_register
286
289 IntRegister<ProcessorKind::Aarch64> fpsr; // Floating-point status register
290
293 IntRegister<ProcessorKind::Aarch64> fpcr; // Floating-point control register
294
295#undef DEF_getter
296
297#ifdef ASMGRADER_AARCH64
298 static constexpr RegistersState from(const user_regs_struct& regs, const user_fpsimd_struct& fpsimd_regs);
299#endif
300
301 //
302 // ##################### x86_64
303 //
304
305 // TODO: Different access modes for general registers (eax, ax, al, ah, etc.)
306#define REGS_tuple_set \
307 BOOST_PP_TUPLE_TO_SEQ(15, (r15, r14, r13, r12, rbp, rbx, r11, r10, r9, r8, rax, rcx, rdx, rsi, rdi))
308#define DEF_gen_register(r, _, elem) IntRegister<ProcessorKind::x86_64> elem;
309 BOOST_PP_SEQ_FOR_EACH(DEF_gen_register, _, REGS_tuple_set)
310#undef DEF_gen_register
311
314
316
317#ifdef ASMGRADER_X86_64
318 static constexpr RegistersState from(const user_regs_struct& regs, const user_fpregs_struct& fp_regs);
319#endif
320};
321
322#if defined(ASMGRADER_AARCH64)
323constexpr RegistersState RegistersState::from(const user_regs_struct& regs, const user_fpsimd_struct& fpsimd_regs) {
324 RegistersState result{};
325
326 auto writable_regs_view =
327 result.regs | ranges::views::transform([](auto& reg) -> decltype(auto) { return reg.get_value(); });
328 ranges::copy(regs.regs, ranges::begin(writable_regs_view));
329
330 result.sp = regs.sp;
331 result.pc = regs.pc;
332 result.pstate.get_value() = regs.pstate;
333
334 auto writable_vregs_view =
335 result.vregs | ranges::views::transform([](auto& reg) -> decltype(auto) { return reg.get_value(); });
336 ranges::copy(fpsimd_regs.vregs, ranges::begin(writable_vregs_view));
337
338 result.fpsr = fpsimd_regs.fpsr;
339 result.fpcr = fpsimd_regs.fpcr;
340
341 return result;
342}
343
344inline Expected<void, std::vector<std::string_view>> valid_pcs(const RegistersState& before_call,
345 const RegistersState& after_call) noexcept {
346 // PCS Docs (not official spec):
347 // https://developer.arm.com/documentation/102374/0102/Procedure-Call-Standard
348
349 std::vector<std::string_view> err_changed_names;
350
351 auto check_reg = [&](const auto& before, const auto& after, std::string_view name) {
352 if (before != after) {
353 err_changed_names.push_back(name);
354 }
355 };
356
357#define CHECK_reg_nr(z, nr, pre) check_reg(before_call.pre##nr(), after_call.pre##nr(), #pre #nr);
358
359 // General purpose regs X[19, 30) must be preserved
360 BOOST_PP_REPEAT_FROM_TO(19, 30, CHECK_reg_nr, x)
361
362 // Floating-point regs D[8, 32) must be preserved
363 BOOST_PP_REPEAT_FROM_TO(8, 32, CHECK_reg_nr, d)
364
365#undef CHECK_reg_nr
366
367 // Stack pointer must be preserved
368 // (caller-passed args are READ FROM, NOT POPPED from the stack by the callee)
369 check_reg(before_call.sp, after_call.sp, "sp");
370
371 if (err_changed_names.empty()) {
372 return {};
373 }
374
375 return err_changed_names;
376}
377
378#elif defined(ASMGRADER_X86_64)
379
380// A benefit of using the same names as user_regs_struct
381
382constexpr RegistersState RegistersState::from(const user_regs_struct& regs, const user_fpregs_struct& fp_regs) {
383#define COPY_gen_reg(r, base, elem) base.elem.get_value() = regs.elem;
384 RegistersState result{};
385
386 BOOST_PP_SEQ_FOR_EACH(COPY_gen_reg, result, REGS_tuple_set);
387
388 result.rip = regs.rip;
389 result.rsp = regs.rsp;
390
391 result.eflags.get_value() = regs.eflags;
392
393 // TODO: Support floating point on x86_64
394 std::ignore = fp_regs;
395
396 return result;
397#undef COPY_gen_reg
398}
399
400inline Expected<void, std::vector<std::string_view>> valid_pcs(const RegistersState& before_call,
401 const RegistersState& after_call) noexcept {
402 // Official (old, circa 2012) calling convention source:
403 // https://web.archive.org/web/20120913114312/http://www.x86-64.org/documentation/abi.pdf:
404
405 std::vector<std::string_view> err_changed_names;
406
407 auto check_reg = [&](const auto& before, const auto& after, std::string_view name) {
408 if (before != after) {
409 err_changed_names.push_back(name);
410 }
411 };
412
413#define CHECK_reg_nr(z, nr, pre) check_reg(before_call.pre##nr, after_call.pre##nr, #pre #nr);
414
415 // General purpose regs X[12, 36) must be preserved
416 BOOST_PP_REPEAT_FROM_TO(12, 15, CHECK_reg_nr, r)
417
418 // TODO: Check floating point and vectorized extension registers for x86_64 calling convention check
419
420#undef CHECK_reg_nr
421
422 // Stack pointer must be preserved
423 // (caller-passed args are READ FROM, NOT POPPED from the stack by the callee)
424 check_reg(before_call.rsp, after_call.rsp, "rsp");
425
426 // rbx and rbp must be preserved
427 check_reg(before_call.rbx, after_call.rbx, "rbx");
428 check_reg(before_call.rbp, after_call.rbp, "rbp");
429
430 if (err_changed_names.empty()) {
431 return {};
432 }
433
434 return err_changed_names;
435}
436
437#endif
438
439} // namespace asmgrader
440
441// ################ Formatter Specializations
442
443template <::asmgrader::ProcessorKind Arch>
445{
446 auto format(const ::asmgrader::IntRegister<Arch>& from, format_context& ctx) const {
447 if (is_debug_format) {
448 using IntType = decltype(from.get_value());
449 constexpr auto ALIGN_10 = ::asmgrader::digits10_max_count<IntType>;
450 constexpr auto ALIGN_16 = sizeof(IntType) * 2;
451 constexpr auto ALIGN_2 = sizeof(IntType) * 8;
452 return format_to(ctx.out(), "{0:>{1}} | 0x{0:0{2}X} | 0b{0:0{3}B}", from.get_value(), ALIGN_10, ALIGN_16,
453 ALIGN_2);
454 }
455
456 return format_to(ctx.out(), "{}", from.get_value());
457 }
458};
459
460template <::asmgrader::ProcessorKind Arch>
462{
463 auto format(const ::asmgrader::FloatingPointRegister<Arch>& from, format_context& ctx) const {
464 if (!is_debug_format) {
465 return format_to(ctx.out(), "{}", double{from});
466 }
467
468 const auto max_sz = formatted_size("{}", std::numeric_limits<double>::min());
469 return format_to(ctx.out(), "{:>{}} (f64) | 0x{:032X}", double{from}, max_sz, from.get_value());
470 }
471};
472
473template <::asmgrader::ProcessorKind Arch>
475{
476 std::size_t bin_labels_offset{};
477
478 constexpr auto parse(fmt::format_parse_context& ctx) {
479 const auto* it = DebugFormatter::parse(ctx);
480 const auto* end = ctx.end();
481
482 const auto* closing_brace_iter = ranges::find(it, end, '}');
483
484 if (closing_brace_iter == end) {
485 return it;
486 }
487
488 if (closing_brace_iter - it == 0) {
489 return ctx.end();
490 }
491
492 if (*it < '0' || *it > '9') {
493 throw fmt::format_error("invalid format - offset spec is not an integer");
494 }
495
496 int offset_spec = detail::parse_nonnegative_int(it, closing_brace_iter, -1);
497
498 if (offset_spec < 0) {
499 throw fmt::format_error("invalid format - offset spec is not a valid integer");
500 }
501
502 bin_labels_offset = static_cast<std::size_t>(offset_spec);
503
504 return it;
505 }
506
507 static std::string get_short_flags_str(const ::asmgrader::FlagsRegister<Arch>& flags) {
508 std::vector<char> set_flags;
509
510 if (flags.carry_set()) {
511 set_flags.push_back('C');
512 }
513 if (flags.zero_set()) {
514 set_flags.push_back('Z');
515 }
516 if (flags.negative_set()) {
517#if defined(ASMGRADER_AARCH64)
518 set_flags.push_back('V');
519#elif defined(ASMGRADER_X86_64)
520 set_flags.push_back('S');
521#endif
522 }
523 if (flags.overflow_set()) {
524 set_flags.push_back('O');
525 }
526
527 return fmt::format("{}", fmt::join(set_flags, "|"));
528 }
529
530#if defined(ASMGRADER_AARCH64)
531 auto format(const ::asmgrader::FlagsRegister<Arch>& from, format_context& ctx) const {
532 std::string short_flags_str = get_short_flags_str(from);
533
534 if (is_debug_format) {
535 // See:
536 // https://developer.arm.com/documentation/ddi0601/2025-06/AArch64-Registers/NZCV--Condition-Flags
537 std::string flags_bin = fmt::format("0b{:032b}", from.get_value());
538
539 // len('0b')
540 constexpr auto LABEL_INIT_OFFSET = 2;
541
542 std::string labels_offset(bin_labels_offset + LABEL_INIT_OFFSET, ' ');
543 std::string labels = "NZCV";
544
545 ctx.advance_to(format_to(ctx.out(), "{} ({})\n{}{}", flags_bin, short_flags_str, labels_offset, labels));
546 } else {
547 ctx.advance_to(format_to(ctx.out(), "{}", short_flags_str));
548 }
549
550 return ctx.out();
551 }
552#elif defined(ASMGRADER_X86_64)
553 auto format(const ::asmgrader::FlagsRegister<Arch>& from, format_context& ctx) const {
554 std::string short_flags_str = get_short_flags_str(from);
555
556 if (is_debug_format) {
557 // See:
558 // https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html
559 // Volume 1 - 3.4.3.1 Status Flags
560 std::string flags_bin = fmt::format("0b{:032b}", from.get_value());
561
562 // 32 bits - starting bit# of labels + len('0b')
563 constexpr auto LABEL_INIT_OFFSET =
565
566 std::string labels_offset(bin_labels_offset + LABEL_INIT_OFFSET, ' ');
567 std::string labels = "O SZ C";
568
569 ctx.advance_to(format_to(ctx.out(), "{} ({})\n{}{}", flags_bin, short_flags_str, labels_offset, labels));
570 } else {
571 ctx.advance_to(format_to(ctx.out(), "{}", short_flags_str));
572 }
573
574 return ctx.out();
575 }
576#endif
577};
578
579#if defined(ASMGRADER_AARCH64)
580
581template <>
583{
584 auto format(const ::asmgrader::RegistersState& from, format_context& ctx) const {
585 constexpr std::size_t TAB_WIDTH = 4; // for debug format field sep
586 std::string field_sep;
587
588 constexpr std::size_t MAX_NAME_LEN = 8; // "x29 [fp]" | "x30 [lr]"
589
590 if (is_debug_format) {
591 ctx.advance_to(fmt::format_to(ctx.out(), "RegistersState{{\n{}", std::string(TAB_WIDTH, ' ')));
592
593 field_sep = ",\n" + std::string(TAB_WIDTH, ' ');
594 } else {
595 ctx.out()++ = '{';
596
597 field_sep = ", ";
598 }
599
600 static constexpr auto LEN = [](gsl::czstring str) { return std::string_view{str}.size(); };
601
602 auto print_named_field = [this, &ctx, field_sep, MAX_NAME_LEN](std::string_view name, const auto& what,
603 bool print_sep = true) {
604 if (is_debug_format) {
605 ctx.advance_to(format_to(ctx.out(), "{:<{}}", name, MAX_NAME_LEN));
606 } else {
607 ctx.advance_to(format_to(ctx.out(), "{}", name));
608 }
609
610 ctx.advance_to(format_to(ctx.out(), " = {}{}", what, (print_sep ? field_sep : "")));
611 };
612
613 auto print_reg = [this, print_named_field](std::string_view name, const auto& reg) {
614 if (is_debug_format) {
615 print_named_field(name, fmt::format("{:?}", reg));
616 } else {
617 print_named_field(name, fmt::format("{}", reg));
618 }
619 };
620
621#define PRINT_gen_reg(z, num, base) print_reg("x" #num, (base).x##num());
622 // omit x29 and x30 to do custom formatting
623 BOOST_PP_REPEAT(BOOST_PP_SUB(NUM_GEN_REGISTERS, 2), PRINT_gen_reg, from);
624#undef PRINT_gen_reg
625
626 print_reg("x29 [fp]", from.fp());
627 print_reg("x30 [lr]", from.lr());
628
629#define PRINT_fp_reg(z, num, base) print_reg((is_debug_format ? "q" #num : "d" #num), (base).q##num());
630 BOOST_PP_REPEAT(NUM_FP_REGISTERS, PRINT_fp_reg, from);
631#undef PRINT_fp_reg
632
633 print_reg("fpsr", from.fpsr);
634 print_reg("fpcr", from.fpcr);
635
636 print_reg("sp", from.sp);
637 print_reg("pc", from.pc);
638
639 if (is_debug_format) {
640 constexpr std::size_t PRE_WIDTH = LEN("pstate = ") + TAB_WIDTH;
641 const std::string pstate_fmt_spec = fmt::format("{{:?{}}}", PRE_WIDTH);
642 print_named_field("pstate", vformat(pstate_fmt_spec, make_format_args(from.pstate)), false);
643 } else {
644 print_named_field("pstate", from.pstate, false);
645 }
646
647 if (is_debug_format) {
648 ctx.advance_to(fmt::format_to(ctx.out(), "\n}}"));
649 } else {
650 ctx.out()++ = '}';
651 }
652
653 return ctx.out();
654 }
655};
656
657#undef NUM_GEN_REGISTERS
658#undef NUM_FP_REGISTERS
659
660#elif defined(ASMGRADER_X86_64)
661
662template <>
664{
665 auto format(const ::asmgrader::RegistersState& from, format_context& ctx) const {
666 constexpr std::size_t TAB_WIDTH = 4; // for debug format field sep
667 std::string field_sep;
668
669 constexpr std::size_t MAX_NAME_LEN = 6; // "eflags"
670
671 if (is_debug_format) {
672 ctx.advance_to(fmt::format_to(ctx.out(), "RegistersState{{\n{}", std::string(TAB_WIDTH, ' ')));
673
674 field_sep = ",\n" + std::string(TAB_WIDTH, ' ');
675 } else {
676 ctx.out()++ = '{';
677
678 field_sep = ", ";
679 }
680
681 static constexpr auto LEN = [](gsl::czstring str) { return std::string_view{str}.size(); };
682
683 auto print_named_field = [this, &ctx, field_sep, MAX_NAME_LEN](std::string_view name, const auto& what,
684 bool print_sep = true) {
685 if (is_debug_format) {
686 ctx.advance_to(format_to(ctx.out(), "{:<{}}", name, MAX_NAME_LEN));
687 } else {
688 ctx.advance_to(format_to(ctx.out(), "{}", name));
689 }
690
691 ctx.advance_to(format_to(ctx.out(), " = {}{}", what, (print_sep ? field_sep : "")));
692 };
693
694 auto print_reg = [this, print_named_field](std::string_view name, const auto& reg) {
695 if (is_debug_format) {
696 print_named_field(name, fmt::format("{:?}", reg));
697 } else {
698 print_named_field(name, fmt::format("{}", reg));
699 }
700 };
701
702#define PRINT_gen_reg(r, base, elem) print_reg(BOOST_PP_STRINGIZE(elem), (base).elem);
703
704 BOOST_PP_SEQ_FOR_EACH(PRINT_gen_reg, from, REGS_tuple_set);
705#undef PRINT_gen_reg
706
707 print_reg("rip", from.rip);
708 print_reg("rsp", from.rip);
709
710 if (is_debug_format) {
711 constexpr std::size_t PRE_WIDTH = LEN("eflags = ") + TAB_WIDTH;
712 const std::string eflags_fmt_spec = fmt::format("{{:?{}}}", PRE_WIDTH);
713 print_named_field("eflags", vformat(eflags_fmt_spec, make_format_args(from.eflags)), false);
714 } else {
715 print_named_field("eflags", from.eflags, false);
716 }
717
718 if (is_debug_format) {
719 ctx.advance_to(fmt::format_to(ctx.out(), "\n}}"));
720 } else {
721 ctx.out()++ = '}';
722 }
723
724 return ctx.out();
725 }
726};
727
728#undef REGS_tuple_set
729
730#endif
#define LOG_ERROR(...)
Definition logging.hpp:47
#define LOG_WARN(...)
Definition logging.hpp:46
std::uint64_t u64
Definition aliases.hpp:21
Definition asm_buffer.hpp:19
detail::sized_uint_impl< std::bit_width(NumBits) - 1 > sized_uint_t
Definition integer.hpp:36
constexpr std::size_t digits10_max_count
This was a little confusing (to me at least) digits10 => maximum number of base-10 digits that can be...
Definition integer.hpp:51
ProcessorKind
Definition os.hpp:9
#define REGS_tuple_set
Definition registers_state.hpp:306
#define DEF_fp_register(z, num, _)
Definition registers_state.hpp:281
#define NUM_FP_REGISTERS
Definition registers_state.hpp:262
#define DEF_gen_register(z, num, _)
Definition registers_state.hpp:267
#define NUM_GEN_REGISTERS
Definition registers_state.hpp:260
Definition debug.hpp:9
constexpr auto parse(fmt::format_parse_context &ctx)
Definition debug.hpp:12
Definition registers_state.hpp:203
constexpr bool overflow_set() const
Definition registers_state.hpp:219
constexpr bool carry_set() const
Definition registers_state.hpp:215
constexpr bool negative_set() const
Definition registers_state.hpp:207
constexpr bool zero_set() const
Definition registers_state.hpp:211
Definition registers_state.hpp:168
constexpr FPType as() const
Definition registers_state.hpp:191
static constexpr FloatingPointRegister from(FPType val)
Definition registers_state.hpp:175
General Purpose Register.
Definition registers_state.hpp:154
Definition registers_state.hpp:252
std::array< IntRegister< ProcessorKind::Aarch64 >, NUM_GEN_REGISTERS > regs
Definition registers_state.hpp:265
IntRegister< ProcessorKind::Aarch64 > pc
Definition registers_state.hpp:274
BOOST_PP_REPEAT(NUM_GEN_REGISTERS, DEF_gen_register, ~)
IntRegister< ProcessorKind::x86_64 > rip
Definition registers_state.hpp:312
IntRegister< ProcessorKind::Aarch64 > fpcr
Actually a u32, but I can't imagine a scenario where that would matter, so I don't think it's worth a...
Definition registers_state.hpp:293
FlagsRegister< ProcessorKind::Aarch64 > pstate
Definition registers_state.hpp:276
FlagsRegister< ProcessorKind::x86_64 > eflags
Definition registers_state.hpp:315
std::array< FloatingPointRegister< ProcessorKind::Aarch64 >, NUM_FP_REGISTERS > vregs
Definition registers_state.hpp:279
IntRegister< ProcessorKind::x86_64 > rsp
Definition registers_state.hpp:313
BOOST_PP_REPEAT(NUM_FP_REGISTERS, DEF_fp_register, ~)
IntRegister< ProcessorKind::Aarch64 > fpsr
Actually a u32, but I can't imagine a scenario where that would matter, so I don't think it's worth a...
Definition registers_state.hpp:289
IntRegister< ProcessorKind::Aarch64 > sp
Definition registers_state.hpp:273
constexpr RegisterBaseImpl(Derived< OtherArch > *arch_alternative)
Definition registers_state.hpp:111
constexpr IntType as() const
Definition registers_state.hpp:86
constexpr auto operator<=>(const RegisterBaseImpl< Derived, BaseType, Arch > &rhs) const =default
void check_log_alternative() const
Definition registers_state.hpp:125
constexpr RegisterBaseImpl(BaseType value)
Definition registers_state.hpp:73
constexpr RegisterBaseImpl(const Derived< OtherArch > *)
Definition registers_state.hpp:71
constexpr BaseType & get_value()
Definition registers_state.hpp:81
constexpr decltype(auto) get_value() const
Definition registers_state.hpp:135
constexpr const BaseType & get_value() const
Definition registers_state.hpp:83
constexpr RegisterBaseImpl(BaseType)
Definition registers_state.hpp:114
constexpr auto & operator=(BaseType rhs)
Definition registers_state.hpp:76
CRTP (or is it CRTTP in this case?) is used to pass an arch alternative. See gh#21 for details.
Definition registers_state.hpp:62
static std::string get_short_flags_str(const ::asmgrader::FlagsRegister< Arch > &flags)
Definition registers_state.hpp:507
constexpr auto parse(fmt::format_parse_context &ctx)
Definition registers_state.hpp:478
auto format(const ::asmgrader::FloatingPointRegister< Arch > &from, format_context &ctx) const
Definition registers_state.hpp:463
auto format(const ::asmgrader::IntRegister< Arch > &from, format_context &ctx) const
Definition registers_state.hpp:446