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>
22#include <fmt/compile.h>
23#include <fmt/format.h>
24#include <fmt/ranges.h>
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>
61template <
template <ProcessorKind>
typename Derived,
typename BaseType,
ProcessorKind Arch>
64template <
template <ProcessorKind>
typename Derived,
typename BaseType,
ProcessorKind Arch>
65 requires(Arch == SYSTEM_PROCESSOR)
70 template <ProcessorKind OtherArch>
78 return *
static_cast<Derived<Arch>*
>(
this);
83 constexpr const BaseType&
get_value()
const {
return value_; }
85 template <std::
integral IntType>
86 constexpr IntType
as()
const {
90 constexpr auto ALL_BITS =
static_cast<IntType
>(-1);
93 constexpr auto MASK =
static_cast<u64
>(ALL_BITS);
95 return static_cast<IntType
>(MASK & value_);
104template <
template <ProcessorKind>
typename Derived,
typename BaseType,
ProcessorKind Arch>
105 requires(Arch != SYSTEM_PROCESSOR)
110 template <ProcessorKind OtherArch>
112 : alternative{arch_alternative} {}
123 Derived<SYSTEM_PROCESSOR>* alternative{};
126 if (alternative ==
nullptr) {
127 constexpr auto MSG =
"Attempting to access non-native register without a native alternative";
129 throw std::runtime_error(MSG);
132 LOG_WARN(
"Attempting to access non-native register. Defaulting to a native alternative");
136 check_log_alternative();
137 return alternative->get_value();
140 template <std::
integral IntType>
141 constexpr IntType
as()
const {
142 check_log_alternative();
143 return alternative->template as<IntType>();
152template <ProcessorKind Arch = SYSTEM_PROCESSOR>
159 constexpr operator u64()
const {
164static_assert(
sizeof(IntRegister<>) ==
sizeof(u64));
166template <ProcessorKind Arch = SYSTEM_PROCESSOR>
173 template <
typename FPType>
174 requires(Arch == SYSTEM_PROCESSOR)
177 auto int_val = std::bit_cast<IntType>(val);
183 constexpr operator double()
const {
184 return std::bit_cast<double>(
190 template <std::
floating_po
int FPType>
191 constexpr FPType
as()
const {
195 return std::bit_cast<FPType>(int_val);
199static_assert(
sizeof(FloatingPointRegister<>) ==
sizeof(u128));
201template <ProcessorKind Arch = SYSTEM_PROCESSOR>
223#if defined(ASMGRADER_AARCH64)
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);
232 constexpr u64 nzcv()
const {
233 constexpr u64 NZCV_BIT_MASK = 0xF;
238#elif defined(ASMGRADER_X86_64)
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;
249static_assert(
sizeof(FlagsRegister<>) ==
sizeof(
u64));
256#define DEF_getter(name, expr) \
257 constexpr auto name() const { return expr; }
260#define NUM_GEN_REGISTERS 31
262#define NUM_FP_REGISTERS 32
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
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
297#ifdef ASMGRADER_AARCH64
298 static constexpr RegistersState from(
const user_regs_struct&
regs,
const user_fpsimd_struct& fpsimd_regs);
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;
310#undef DEF_gen_register
317#ifdef ASMGRADER_X86_64
318 static constexpr RegistersState from(
const user_regs_struct&
regs,
const user_fpregs_struct& fp_regs);
322#if defined(ASMGRADER_AARCH64)
323constexpr RegistersState RegistersState::from(
const user_regs_struct& regs,
const user_fpsimd_struct& fpsimd_regs) {
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));
332 result.pstate.get_value() =
regs.pstate;
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));
338 result.fpsr = fpsimd_regs.fpsr;
339 result.fpcr = fpsimd_regs.fpcr;
344inline Expected<void, std::vector<std::string_view>> valid_pcs(
const RegistersState& before_call,
345 const RegistersState& after_call)
noexcept {
349 std::vector<std::string_view> err_changed_names;
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);
357#define CHECK_reg_nr(z, nr, pre) check_reg(before_call.pre##nr(), after_call.pre##nr(), #pre #nr);
360 BOOST_PP_REPEAT_FROM_TO(19, 30, CHECK_reg_nr, x)
363 BOOST_PP_REPEAT_FROM_TO(8, 32, CHECK_reg_nr, d)
369 check_reg(before_call.sp, after_call.sp,
"sp");
371 if (err_changed_names.empty()) {
375 return err_changed_names;
378#elif defined(ASMGRADER_X86_64)
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{};
388 result.rip =
regs.rip;
389 result.rsp =
regs.rsp;
391 result.eflags.get_value() =
regs.eflags;
394 std::ignore = fp_regs;
400inline Expected<void, std::vector<std::string_view>> valid_pcs(
const RegistersState& before_call,
401 const RegistersState& after_call)
noexcept {
405 std::vector<std::string_view> err_changed_names;
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);
413#define CHECK_reg_nr(z, nr, pre) check_reg(before_call.pre##nr, after_call.pre##nr, #pre #nr);
416 BOOST_PP_REPEAT_FROM_TO(12, 15, CHECK_reg_nr, r)
424 check_reg(before_call.rsp, after_call.rsp,
"rsp");
427 check_reg(before_call.rbx, after_call.rbx,
"rbx");
428 check_reg(before_call.rbp, after_call.rbp,
"rbp");
430 if (err_changed_names.empty()) {
434 return err_changed_names;
443template <::asmgrader::ProcessorKind Arch>
446 auto format(const ::asmgrader::IntRegister<Arch>& from, format_context& ctx)
const {
447 if (is_debug_format) {
448 using IntType =
decltype(from.get_value());
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,
456 return format_to(ctx.out(),
"{}", from.get_value());
460template <::asmgrader::ProcessorKind Arch>
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});
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());
473template <::asmgrader::ProcessorKind Arch>
476 std::size_t bin_labels_offset{};
478 constexpr auto parse(fmt::format_parse_context& ctx) {
480 const auto* end = ctx.end();
482 const auto* closing_brace_iter = ranges::find(it, end,
'}');
484 if (closing_brace_iter == end) {
488 if (closing_brace_iter - it == 0) {
492 if (*it <
'0' || *it >
'9') {
493 throw fmt::format_error(
"invalid format - offset spec is not an integer");
496 int offset_spec = detail::parse_nonnegative_int(it, closing_brace_iter, -1);
498 if (offset_spec < 0) {
499 throw fmt::format_error(
"invalid format - offset spec is not a valid integer");
502 bin_labels_offset =
static_cast<std::size_t
>(offset_spec);
508 std::vector<char> set_flags;
510 if (flags.carry_set()) {
511 set_flags.push_back(
'C');
513 if (flags.zero_set()) {
514 set_flags.push_back(
'Z');
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');
523 if (flags.overflow_set()) {
524 set_flags.push_back(
'O');
527 return fmt::format(
"{}", fmt::join(set_flags,
"|"));
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);
534 if (is_debug_format) {
537 std::string flags_bin = fmt::format(
"0b{:032b}", from.get_value());
540 constexpr auto LABEL_INIT_OFFSET = 2;
542 std::string labels_offset(bin_labels_offset + LABEL_INIT_OFFSET,
' ');
543 std::string labels =
"NZCV";
545 ctx.advance_to(format_to(ctx.out(),
"{} ({})\n{}{}", flags_bin, short_flags_str, labels_offset, labels));
547 ctx.advance_to(format_to(ctx.out(),
"{}", short_flags_str));
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);
556 if (is_debug_format) {
560 std::string flags_bin = fmt::format(
"0b{:032b}", from.get_value());
563 constexpr auto LABEL_INIT_OFFSET =
566 std::string labels_offset(bin_labels_offset + LABEL_INIT_OFFSET,
' ');
567 std::string labels =
"O SZ C";
569 ctx.advance_to(format_to(ctx.out(),
"{} ({})\n{}{}", flags_bin, short_flags_str, labels_offset, labels));
571 ctx.advance_to(format_to(ctx.out(),
"{}", short_flags_str));
579#if defined(ASMGRADER_AARCH64)
584 auto format(const ::asmgrader::RegistersState& from, format_context& ctx)
const {
585 constexpr std::size_t TAB_WIDTH = 4;
586 std::string field_sep;
588 constexpr std::size_t MAX_NAME_LEN = 8;
590 if (is_debug_format) {
591 ctx.advance_to(fmt::format_to(ctx.out(),
"RegistersState{{\n{}", std::string(TAB_WIDTH,
' ')));
593 field_sep =
",\n" + std::string(TAB_WIDTH,
' ');
600 static constexpr auto LEN = [](gsl::czstring str) {
return std::string_view{str}.size(); };
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));
607 ctx.advance_to(format_to(ctx.out(),
"{}", name));
610 ctx.advance_to(format_to(ctx.out(),
" = {}{}", what, (print_sep ? field_sep :
"")));
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));
617 print_named_field(name, fmt::format(
"{}", reg));
621#define PRINT_gen_reg(z, num, base) print_reg("x" #num, (base).x##num());
626 print_reg(
"x29 [fp]", from.fp());
627 print_reg(
"x30 [lr]", from.lr());
629#define PRINT_fp_reg(z, num, base) print_reg((is_debug_format ? "q" #num : "d" #num), (base).q##num());
633 print_reg(
"fpsr", from.fpsr);
634 print_reg(
"fpcr", from.fpcr);
636 print_reg(
"sp", from.sp);
637 print_reg(
"pc", from.pc);
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);
644 print_named_field(
"pstate", from.pstate,
false);
647 if (is_debug_format) {
648 ctx.advance_to(fmt::format_to(ctx.out(),
"\n}}"));
657#undef NUM_GEN_REGISTERS
658#undef NUM_FP_REGISTERS
660#elif defined(ASMGRADER_X86_64)
665 auto format(const ::asmgrader::RegistersState& from, format_context& ctx)
const {
666 constexpr std::size_t TAB_WIDTH = 4;
667 std::string field_sep;
669 constexpr std::size_t MAX_NAME_LEN = 6;
671 if (is_debug_format) {
672 ctx.advance_to(fmt::format_to(ctx.out(),
"RegistersState{{\n{}", std::string(TAB_WIDTH,
' ')));
674 field_sep =
",\n" + std::string(TAB_WIDTH,
' ');
681 static constexpr auto LEN = [](gsl::czstring str) {
return std::string_view{str}.size(); };
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));
688 ctx.advance_to(format_to(ctx.out(),
"{}", name));
691 ctx.advance_to(format_to(ctx.out(),
" = {}{}", what, (print_sep ? field_sep :
"")));
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));
698 print_named_field(name, fmt::format(
"{}", reg));
702#define PRINT_gen_reg(r, base, elem) print_reg(BOOST_PP_STRINGIZE(elem), (base).elem);
707 print_reg(
"rip", from.rip);
708 print_reg(
"rsp", from.rip);
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);
715 print_named_field(
"eflags", from.eflags,
false);
718 if (is_debug_format) {
719 ctx.advance_to(fmt::format_to(ctx.out(),
"\n}}"));
#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 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
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 RegisterBaseImpl()=default
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