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
202template <typename Base>
204{
205 constexpr bool n() const { return get_base().negative_set(); }
206
207 constexpr bool z() const { return get_base().zero_set(); }
208
209 constexpr bool c() const { return get_base().carry_set(); }
210
211 constexpr bool v() const { return get_base().overflow_set(); }
212
213 constexpr u64 nzcv() const {
214 constexpr u64 NZCV_BIT_MASK = 0xF;
215
216 return (get_base().get_value() >> NZCV_BASE_OFF) & NZCV_BIT_MASK;
217 }
218
219protected:
220 // Specification of pstate (for nzcv) obtained from:
221 // https://developer.arm.com/documentation/ddi0601/2025-06/AArch64-Registers/NZCV--Condition-Flags
222 static constexpr u64 NZCV_BASE_OFF = 28;
223 static constexpr u64 NEGATIVE_FLAG_BIT = 1U << (NZCV_BASE_OFF + 3);
224 static constexpr u64 ZERO_FLAG_BIT = 1U << (NZCV_BASE_OFF + 2);
225 static constexpr u64 CARRY_FLAG_BIT = 1U << (NZCV_BASE_OFF + 1);
226 static constexpr u64 OVERFLOW_FLAG_BIT = 1U << (NZCV_BASE_OFF + 0);
227
228private:
229 Base& get_base() { return *static_cast<Base*>(this); }
230
231 const Base& get_base() const { return *static_cast<const Base*>(this); }
232
233 Aarch64FlagsBase() = default;
234 friend Base;
235};
236
238template <typename Base>
240{
241protected:
242 // TODO: Implement specific flag getters
243
244 // Specification of eflags obtained from:
245 // https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html
246 // Volume 1 - 3.4.3.1 Status Flags
247 static constexpr u64 CARRY_FLAG_BIT = 1U << 0;
248 static constexpr u64 ZERO_FLAG_BIT = 1U << 6;
249 static constexpr u64 NEGATIVE_FLAG_BIT = 1U << 7;
250 static constexpr u64 OVERFLOW_FLAG_BIT = 1U << 11;
251
252private:
253 X64FlagsBase() = default;
254 friend Base;
255};
256
257#if defined(ASMGRADER_AARCH64)
258template <typename CrtpBase>
259using FlagsArchBase = Aarch64FlagsBase<CrtpBase>;
260template <typename CrtpBase>
261using FlagsArchBaseAlternative = X64FlagsBase<CrtpBase>;
262#elif defined(ASMGRADER_X86_64)
263template <typename CrtpBase>
264using FlagsArchBase = X64FlagsBase<CrtpBase>;
265template <typename CrtpBase>
266using FlagsArchBaseAlternative = Aarch64FlagsBase<CrtpBase>;
267#endif
268
269template <ProcessorKind Arch = SYSTEM_PROCESSOR>
270struct FlagsRegister : detail::RegisterBaseImpl<FlagsRegister, u64, Arch>,
271 FlagsArchBase<FlagsRegister<Arch>>,
272 FlagsArchBaseAlternative<FlagsRegister<Arch>>
273{
274 using detail::RegisterBaseImpl<FlagsRegister, u64, Arch>::RegisterBaseImpl;
275 using detail::RegisterBaseImpl<FlagsRegister, u64, Arch>::operator=;
276
277 constexpr bool negative_set() const {
279 FlagsArchBase<FlagsRegister>::NEGATIVE_FLAG_BIT) != 0U;
280 }
281
282 constexpr bool zero_set() const {
284 FlagsArchBase<FlagsRegister>::ZERO_FLAG_BIT) != 0U;
285 }
286
287 constexpr bool carry_set() const {
289 FlagsArchBase<FlagsRegister>::CARRY_FLAG_BIT) != 0U;
290 }
291
292 constexpr bool overflow_set() const {
294 FlagsArchBase<FlagsRegister>::OVERFLOW_FLAG_BIT) != 0U;
295 }
296};
297
298static_assert(sizeof(FlagsRegister<>) == sizeof(u64));
299
301{
302 //
303 // ##################### Aarch64
304 //
305#define DEF_getter(name, expr) \
306 constexpr auto name() const { return expr; }
307
308// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
309#define NUM_GEN_REGISTERS 31
310// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
311#define NUM_FP_REGISTERS 32
312
313 // General registers (x0-x30)
314 std::array<IntRegister<ProcessorKind::Aarch64>, NUM_GEN_REGISTERS> regs;
315
316#define DEF_gen_register(z, num, _) DEF_getter(x##num, regs[num]) DEF_getter(w##num, static_cast<u32>(regs[num]))
318#undef DEF_gen_register
319
320 DEF_getter(fp, x29()); // frame pointer
321 DEF_getter(lr, x30()); // link register
324
326
327 // Floating-point registers (Q0-Q31)
328 std::array<FloatingPointRegister<ProcessorKind::Aarch64>, NUM_FP_REGISTERS> vregs;
329
330#define DEF_fp_register(z, num, _) \
331 DEF_getter(q##num, vregs[num]) DEF_getter(d##num, vregs[num].as<u64>()) DEF_getter(s##num, vregs[num].as<u32>()) \
332 DEF_getter(h##num, vregs[num].as<u16>())
334#undef DEF_fp_register
335
338 IntRegister<ProcessorKind::Aarch64> fpsr; // Floating-point status register
339
342 IntRegister<ProcessorKind::Aarch64> fpcr; // Floating-point control register
343
344#undef DEF_getter
345
346#ifdef ASMGRADER_AARCH64
347 static constexpr RegistersState from(const user_regs_struct& regs, const user_fpsimd_struct& fpsimd_regs);
348#endif
349
350 //
351 // ##################### x86_64
352 //
353
354 // TODO: Different access modes for general registers (eax, ax, al, ah, etc.)
355#define REGS_tuple_set \
356 BOOST_PP_TUPLE_TO_SEQ(15, (r15, r14, r13, r12, rbp, rbx, r11, r10, r9, r8, rax, rcx, rdx, rsi, rdi))
357#define DEF_gen_register(r, _, elem) IntRegister<ProcessorKind::x86_64> elem;
358 BOOST_PP_SEQ_FOR_EACH(DEF_gen_register, _, REGS_tuple_set)
359#undef DEF_gen_register
360
363
365
366#ifdef ASMGRADER_X86_64
367 static constexpr RegistersState from(const user_regs_struct& regs, const user_fpregs_struct& fp_regs);
368#endif
369};
370
371#if defined(ASMGRADER_AARCH64)
372constexpr RegistersState RegistersState::from(const user_regs_struct& regs, const user_fpsimd_struct& fpsimd_regs) {
373 RegistersState result{};
374
375 auto writable_regs_view =
376 result.regs | ranges::views::transform([](auto& reg) -> decltype(auto) { return reg.get_value(); });
377 ranges::copy(regs.regs, ranges::begin(writable_regs_view));
378
379 result.sp = regs.sp;
380 result.pc = regs.pc;
381 result.pstate.get_value() = regs.pstate;
382
383 auto writable_vregs_view =
384 result.vregs | ranges::views::transform([](auto& reg) -> decltype(auto) { return reg.get_value(); });
385 ranges::copy(fpsimd_regs.vregs, ranges::begin(writable_vregs_view));
386
387 result.fpsr = fpsimd_regs.fpsr;
388 result.fpcr = fpsimd_regs.fpcr;
389
390 return result;
391}
392
393inline Expected<void, std::vector<std::string_view>> valid_pcs(const RegistersState& before_call,
394 const RegistersState& after_call) noexcept {
395 // PCS Docs (not official spec):
396 // https://developer.arm.com/documentation/102374/0102/Procedure-Call-Standard
397
398 std::vector<std::string_view> err_changed_names;
399
400 auto check_reg = [&](const auto& before, const auto& after, std::string_view name) {
401 if (before != after) {
402 err_changed_names.push_back(name);
403 }
404 };
405
406#define CHECK_reg_nr(z, nr, pre) check_reg(before_call.pre##nr(), after_call.pre##nr(), #pre #nr);
407
408 // General purpose regs X[19, 30) must be preserved
409 BOOST_PP_REPEAT_FROM_TO(19, 30, CHECK_reg_nr, x)
410
411 // Floating-point regs D[8, 32) must be preserved
412 BOOST_PP_REPEAT_FROM_TO(8, 32, CHECK_reg_nr, d)
413
414#undef CHECK_reg_nr
415
416 // Stack pointer must be preserved
417 // (caller-passed args are READ FROM, NOT POPPED from the stack by the callee)
418 check_reg(before_call.sp, after_call.sp, "sp");
419
420 if (err_changed_names.empty()) {
421 return {};
422 }
423
424 return err_changed_names;
425}
426
427#elif defined(ASMGRADER_X86_64)
428
429// A benefit of using the same names as user_regs_struct
430
431constexpr RegistersState RegistersState::from(const user_regs_struct& regs, const user_fpregs_struct& fp_regs) {
432#define COPY_gen_reg(r, base, elem) base.elem.get_value() = regs.elem;
433 RegistersState result{};
434
435 BOOST_PP_SEQ_FOR_EACH(COPY_gen_reg, result, REGS_tuple_set);
436
437 result.rip = regs.rip;
438 result.rsp = regs.rsp;
439
440 result.eflags.get_value() = regs.eflags;
441
442 // TODO: Support floating point on x86_64
443 std::ignore = fp_regs;
444
445 return result;
446#undef COPY_gen_reg
447}
448
449inline Expected<void, std::vector<std::string_view>> valid_pcs(const RegistersState& before_call,
450 const RegistersState& after_call) noexcept {
451 // Official (old, circa 2012) calling convention source:
452 // https://web.archive.org/web/20120913114312/http://www.x86-64.org/documentation/abi.pdf:
453
454 std::vector<std::string_view> err_changed_names;
455
456 auto check_reg = [&](const auto& before, const auto& after, std::string_view name) {
457 if (before != after) {
458 err_changed_names.push_back(name);
459 }
460 };
461
462#define CHECK_reg_nr(z, nr, pre) check_reg(before_call.pre##nr, after_call.pre##nr, #pre #nr);
463
464 // General purpose regs X[12, 36) must be preserved
465 BOOST_PP_REPEAT_FROM_TO(12, 15, CHECK_reg_nr, r)
466
467 // TODO: Check floating point and vectorized extension registers for x86_64 calling convention check
468
469#undef CHECK_reg_nr
470
471 // Stack pointer must be preserved
472 // (caller-passed args are READ FROM, NOT POPPED from the stack by the callee)
473 check_reg(before_call.rsp, after_call.rsp, "rsp");
474
475 // rbx and rbp must be preserved
476 check_reg(before_call.rbx, after_call.rbx, "rbx");
477 check_reg(before_call.rbp, after_call.rbp, "rbp");
478
479 if (err_changed_names.empty()) {
480 return {};
481 }
482
483 return err_changed_names;
484}
485
486#endif
487
488} // namespace asmgrader
489
490// ################ Formatter Specializations
491
492template <::asmgrader::ProcessorKind Arch>
494{
495 auto format(const ::asmgrader::IntRegister<Arch>& from, format_context& ctx) const {
496 if (is_debug_format) {
497 using IntType = decltype(from.get_value());
498 constexpr auto ALIGN_10 = ::asmgrader::digits10_max_count<IntType>;
499 constexpr auto ALIGN_16 = sizeof(IntType) * 2;
500 constexpr auto ALIGN_2 = sizeof(IntType) * 8;
501 return fmt::format_to(ctx.out(), "{0:>{1}} | 0x{0:0{2}X} | 0b{0:0{3}B}", from.get_value(), ALIGN_10, ALIGN_16,
502 ALIGN_2);
503 }
504
505 return fmt::format_to(ctx.out(), "{}", from.get_value());
506 }
507};
508
509template <::asmgrader::ProcessorKind Arch>
511{
512 auto format(const ::asmgrader::FloatingPointRegister<Arch>& from, format_context& ctx) const {
513 if (!is_debug_format) {
514 return fmt::format_to(ctx.out(), "{}", double{from});
515 }
516
517 const auto max_sz = formatted_size("{}", std::numeric_limits<double>::min());
518 return fmt::format_to(ctx.out(), "{:>{}} (f64) | 0x{:032X}", double{from}, max_sz, from.get_value());
519 }
520};
521
522template <::asmgrader::ProcessorKind Arch>
524{
525 std::size_t bin_labels_offset{};
526
527 constexpr auto parse(fmt::format_parse_context& ctx) {
528 const auto* it = DebugFormatter::parse(ctx);
529 const auto* end = ctx.end();
530
531 const auto* closing_brace_iter = ranges::find(it, end, '}');
532
533 if (closing_brace_iter == end) {
534 return it;
535 }
536
537 if (closing_brace_iter - it == 0) {
538 return ctx.end();
539 }
540
541 if (*it < '0' || *it > '9') {
542 throw fmt::format_error("invalid format - offset spec is not an integer");
543 }
544
545 int offset_spec = detail::parse_nonnegative_int(it, closing_brace_iter, -1);
546
547 if (offset_spec < 0) {
548 throw fmt::format_error("invalid format - offset spec is not a valid integer");
549 }
550
551 bin_labels_offset = static_cast<std::size_t>(offset_spec);
552
553 return it;
554 }
555
556 static std::string get_short_flags_str(const ::asmgrader::FlagsRegister<Arch>& flags) {
557 std::vector<char> set_flags;
558
559 if (flags.carry_set()) {
560 set_flags.push_back('C');
561 }
562 if (flags.zero_set()) {
563 set_flags.push_back('Z');
564 }
565 if (flags.negative_set()) {
566#if defined(ASMGRADER_AARCH64)
567 set_flags.push_back('V');
568#elif defined(ASMGRADER_X86_64)
569 set_flags.push_back('S');
570#endif
571 }
572 if (flags.overflow_set()) {
573 set_flags.push_back('O');
574 }
575
576 return fmt::format("{}", fmt::join(set_flags, "|"));
577 }
578
579#if defined(ASMGRADER_AARCH64)
580 auto format(const ::asmgrader::FlagsRegister<Arch>& from, format_context& ctx) const {
581 std::string short_flags_str = get_short_flags_str(from);
582
583 if (is_debug_format) {
584 // See:
585 // https://developer.arm.com/documentation/ddi0601/2025-06/AArch64-Registers/NZCV--Condition-Flags
586 std::string flags_bin = fmt::format("0b{:032b}", from.get_value());
587
588 // len('0b')
589 constexpr auto LABEL_INIT_OFFSET = 2;
590
591 std::string labels_offset(bin_labels_offset + LABEL_INIT_OFFSET, ' ');
592 std::string labels = "NZCV";
593
594 ctx.advance_to(fmt::format_to(ctx.out(), "{} ({})\n{}{}", flags_bin, short_flags_str, labels_offset, labels));
595 } else {
596 ctx.advance_to(fmt::format_to(ctx.out(), "{}", short_flags_str));
597 }
598
599 return ctx.out();
600 }
601#elif defined(ASMGRADER_X86_64)
602 auto format(const ::asmgrader::FlagsRegister<Arch>& from, format_context& ctx) const {
603 std::string short_flags_str = get_short_flags_str(from);
604
605 if (is_debug_format) {
606 // See:
607 // https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html
608 // Volume 1 - 3.4.3.1 Status Flags
609 std::string flags_bin = fmt::format("0b{:032b}", from.get_value());
610
611 // i.e., OVERFLOW_FLAG_BIT
612 constexpr auto FIRST_FLAG_OFF = 11;
613
614 // 32 bits - starting bit# of labels + len('0b')
615 constexpr auto LABEL_INIT_OFFSET = 32 - FIRST_FLAG_OFF + 2;
616
617 std::string labels_offset(bin_labels_offset + LABEL_INIT_OFFSET, ' ');
618 std::string labels = "O SZ C";
619
620 ctx.advance_to(fmt::format_to(ctx.out(), "{} ({})\n{}{}", flags_bin, short_flags_str, labels_offset, labels));
621 } else {
622 ctx.advance_to(fmt::format_to(ctx.out(), "{}", short_flags_str));
623 }
624
625 return ctx.out();
626 }
627#endif
628};
629
630#if defined(ASMGRADER_AARCH64)
631
632template <>
634{
635 auto format(const ::asmgrader::RegistersState& from, format_context& ctx) const {
636 constexpr std::size_t TAB_WIDTH = 4; // for debug format field sep
637 std::string field_sep;
638
639 constexpr std::size_t MAX_NAME_LEN = 8; // "x29 [fp]" | "x30 [lr]"
640
641 if (is_debug_format) {
642 ctx.advance_to(fmt::format_to(ctx.out(), "RegistersState{{\n{}", std::string(TAB_WIDTH, ' ')));
643
644 field_sep = ",\n" + std::string(TAB_WIDTH, ' ');
645 } else {
646 ctx.out()++ = '{';
647
648 field_sep = ", ";
649 }
650
651 static constexpr auto LEN = [](gsl::czstring str) { return std::string_view{str}.size(); };
652
653 auto print_named_field = [this, &ctx, field_sep, MAX_NAME_LEN](std::string_view name, const auto& what,
654 bool print_sep = true) {
655 if (is_debug_format) {
656 ctx.advance_to(fmt::format_to(ctx.out(), "{:<{}}", name, MAX_NAME_LEN));
657 } else {
658 ctx.advance_to(fmt::format_to(ctx.out(), "{}", name));
659 }
660
661 ctx.advance_to(fmt::format_to(ctx.out(), " = {}{}", what, (print_sep ? field_sep : "")));
662 };
663
664 auto print_reg = [this, print_named_field](std::string_view name, const auto& reg) {
665 if (is_debug_format) {
666 print_named_field(name, fmt::format("{:?}", reg));
667 } else {
668 print_named_field(name, fmt::format("{}", reg));
669 }
670 };
671
672#define PRINT_gen_reg(z, num, base) print_reg("x" #num, (base).x##num());
673 // omit x29 and x30 to do custom formatting
674 BOOST_PP_REPEAT(BOOST_PP_SUB(NUM_GEN_REGISTERS, 2), PRINT_gen_reg, from);
675#undef PRINT_gen_reg
676
677 print_reg("x29 [fp]", from.fp());
678 print_reg("x30 [lr]", from.lr());
679
680#define PRINT_fp_reg(z, num, base) print_reg((is_debug_format ? "q" #num : "d" #num), (base).q##num());
681 BOOST_PP_REPEAT(NUM_FP_REGISTERS, PRINT_fp_reg, from);
682#undef PRINT_fp_reg
683
684 print_reg("fpsr", from.fpsr);
685 print_reg("fpcr", from.fpcr);
686
687 print_reg("sp", from.sp);
688 print_reg("pc", from.pc);
689
690 if (is_debug_format) {
691 constexpr std::size_t PRE_WIDTH = LEN("pstate = ") + TAB_WIDTH;
692 const std::string pstate_fmt_spec = fmt::format("{{:?{}}}", PRE_WIDTH);
693 print_named_field("pstate", vformat(pstate_fmt_spec, make_format_args(from.pstate)), false);
694 } else {
695 print_named_field("pstate", from.pstate, false);
696 }
697
698 if (is_debug_format) {
699 ctx.advance_to(fmt::format_to(ctx.out(), "\n}}"));
700 } else {
701 ctx.out()++ = '}';
702 }
703
704 return ctx.out();
705 }
706};
707
708#undef NUM_GEN_REGISTERS
709#undef NUM_FP_REGISTERS
710
711#elif defined(ASMGRADER_X86_64)
712
713template <>
715{
716 auto format(const ::asmgrader::RegistersState& from, format_context& ctx) const {
717 constexpr std::size_t TAB_WIDTH = 4; // for debug format field sep
718 std::string field_sep;
719
720 constexpr std::size_t MAX_NAME_LEN = 6; // "eflags"
721
722 if (is_debug_format) {
723 ctx.advance_to(fmt::format_to(ctx.out(), "RegistersState{{\n{}", std::string(TAB_WIDTH, ' ')));
724
725 field_sep = ",\n" + std::string(TAB_WIDTH, ' ');
726 } else {
727 ctx.out()++ = '{';
728
729 field_sep = ", ";
730 }
731
732 static constexpr auto LEN = [](gsl::czstring str) { return std::string_view{str}.size(); };
733
734 auto print_named_field = [this, &ctx, field_sep, MAX_NAME_LEN](std::string_view name, const auto& what,
735 bool print_sep = true) {
736 if (is_debug_format) {
737 ctx.advance_to(fmt::format_to(ctx.out(), "{:<{}}", name, MAX_NAME_LEN));
738 } else {
739 ctx.advance_to(fmt::format_to(ctx.out(), "{}", name));
740 }
741
742 ctx.advance_to(fmt::format_to(ctx.out(), " = {}{}", what, (print_sep ? field_sep : "")));
743 };
744
745 auto print_reg = [this, print_named_field](std::string_view name, const auto& reg) {
746 if (is_debug_format) {
747 print_named_field(name, fmt::format("{:?}", reg));
748 } else {
749 print_named_field(name, fmt::format("{}", reg));
750 }
751 };
752
753#define PRINT_gen_reg(r, base, elem) print_reg(BOOST_PP_STRINGIZE(elem), (base).elem);
754
755 BOOST_PP_SEQ_FOR_EACH(PRINT_gen_reg, from, REGS_tuple_set);
756#undef PRINT_gen_reg
757
758 print_reg("rip", from.rip);
759 print_reg("rsp", from.rip);
760
761 if (is_debug_format) {
762 constexpr std::size_t PRE_WIDTH = LEN("eflags = ") + TAB_WIDTH;
763 const std::string eflags_fmt_spec = fmt::format("{{:?{}}}", PRE_WIDTH);
764 print_named_field("eflags", vformat(eflags_fmt_spec, make_format_args(from.eflags)), false);
765 } else {
766 print_named_field("eflags", from.eflags, false);
767 }
768
769 if (is_debug_format) {
770 ctx.advance_to(fmt::format_to(ctx.out(), "\n}}"));
771 } else {
772 ctx.out()++ = '}';
773 }
774
775 return ctx.out();
776 }
777};
778
779#undef REGS_tuple_set
780
781#endif
#define LOG_ERROR(...)
Definition logging.hpp:44
#define LOG_WARN(...)
Definition logging.hpp:43
constexpr const auto & str
str customization point object The return value of this function is guaranteed to be parsable by high...
Definition stringize.hpp:190
Definition asm_buffer.hpp:20
detail::sized_uint_impl< std::bit_width(NumBits) - 1 > sized_uint_t
Definition integer.hpp:34
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:49
ProcessorKind
Definition os.hpp:12
#define REGS_tuple_set
Definition registers_state.hpp:355
#define DEF_fp_register(z, num, _)
Definition registers_state.hpp:330
#define NUM_FP_REGISTERS
Definition registers_state.hpp:311
#define DEF_gen_register(z, num, _)
Definition registers_state.hpp:316
#define NUM_GEN_REGISTERS
Definition registers_state.hpp:309
Derived classes should use Aarch64FlagsBase with the CRTP pattern.
Definition registers_state.hpp:204
static constexpr u64 NZCV_BASE_OFF
Definition registers_state.hpp:222
constexpr bool n() const
Definition registers_state.hpp:205
constexpr bool v() const
Definition registers_state.hpp:211
constexpr u64 nzcv() const
Definition registers_state.hpp:213
static constexpr u64 CARRY_FLAG_BIT
Definition registers_state.hpp:225
constexpr bool z() const
Definition registers_state.hpp:207
constexpr bool c() const
Definition registers_state.hpp:209
static constexpr u64 ZERO_FLAG_BIT
Definition registers_state.hpp:224
static constexpr u64 NEGATIVE_FLAG_BIT
Definition registers_state.hpp:223
static constexpr u64 OVERFLOW_FLAG_BIT
Definition registers_state.hpp:226
Definition debug.hpp:9
constexpr auto parse(fmt::format_parse_context &ctx)
Definition debug.hpp:12
Definition registers_state.hpp:273
constexpr bool overflow_set() const
Definition registers_state.hpp:292
constexpr bool carry_set() const
Definition registers_state.hpp:287
constexpr bool negative_set() const
Definition registers_state.hpp:277
constexpr bool zero_set() const
Definition registers_state.hpp:282
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:301
std::array< IntRegister< ProcessorKind::Aarch64 >, NUM_GEN_REGISTERS > regs
Definition registers_state.hpp:314
IntRegister< ProcessorKind::Aarch64 > pc
Definition registers_state.hpp:323
BOOST_PP_REPEAT(NUM_GEN_REGISTERS, DEF_gen_register, ~)
IntRegister< ProcessorKind::x86_64 > rip
Definition registers_state.hpp:361
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:342
FlagsRegister< ProcessorKind::Aarch64 > pstate
Definition registers_state.hpp:325
FlagsRegister< ProcessorKind::x86_64 > eflags
Definition registers_state.hpp:364
std::array< FloatingPointRegister< ProcessorKind::Aarch64 >, NUM_FP_REGISTERS > vregs
Definition registers_state.hpp:328
IntRegister< ProcessorKind::x86_64 > rsp
Definition registers_state.hpp:362
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:338
IntRegister< ProcessorKind::Aarch64 > sp
Definition registers_state.hpp:322
Derived classes should use Aarch64FlagsBase with the CRTP pattern.
Definition registers_state.hpp:240
static constexpr u64 OVERFLOW_FLAG_BIT
Definition registers_state.hpp:250
static constexpr u64 NEGATIVE_FLAG_BIT
Definition registers_state.hpp:249
static constexpr u64 ZERO_FLAG_BIT
Definition registers_state.hpp:248
static constexpr u64 CARRY_FLAG_BIT
Definition registers_state.hpp:247
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:556
constexpr auto parse(fmt::format_parse_context &ctx)
Definition registers_state.hpp:527
auto format(const ::asmgrader::FloatingPointRegister< Arch > &from, format_context &ctx) const
Definition registers_state.hpp:512
auto format(const ::asmgrader::IntRegister< Arch > &from, format_context &ctx) const
Definition registers_state.hpp:495
std::size_t end
End of the block, non-inclusive i.e. of the final ` delimiter Will be the same as end_delim for now.
Definition syntax_highlighter.cpp:152