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));
202template <
typename Base>
205 constexpr bool n()
const {
return get_base().negative_set(); }
207 constexpr bool z()
const {
return get_base().zero_set(); }
209 constexpr bool c()
const {
return get_base().carry_set(); }
211 constexpr bool v()
const {
return get_base().overflow_set(); }
214 constexpr u64 NZCV_BIT_MASK = 0xF;
216 return (get_base().get_value() >>
NZCV_BASE_OFF) & NZCV_BIT_MASK;
229 Base& get_base() {
return *
static_cast<Base*
>(
this); }
231 const Base& get_base()
const {
return *
static_cast<const Base*
>(
this); }
233 Aarch64FlagsBase() =
default;
238template <
typename Base>
257#if defined(ASMGRADER_AARCH64)
258template <
typename CrtpBase>
260template <
typename CrtpBase>
262#elif defined(ASMGRADER_X86_64)
263template <
typename CrtpBase>
265template <
typename CrtpBase>
269template <ProcessorKind Arch = SYSTEM_PROCESSOR>
271 FlagsArchBase<FlagsRegister<Arch>>,
272 FlagsArchBaseAlternative<FlagsRegister<Arch>>
279 FlagsArchBase<FlagsRegister>::NEGATIVE_FLAG_BIT) != 0U;
284 FlagsArchBase<FlagsRegister>::ZERO_FLAG_BIT) != 0U;
289 FlagsArchBase<FlagsRegister>::CARRY_FLAG_BIT) != 0U;
294 FlagsArchBase<FlagsRegister>::OVERFLOW_FLAG_BIT) != 0U;
298static_assert(
sizeof(FlagsRegister<>) ==
sizeof(u64));
305#define DEF_getter(name, expr) \
306 constexpr auto name() const { return expr; }
309#define NUM_GEN_REGISTERS 31
311#define NUM_FP_REGISTERS 32
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
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
346#ifdef ASMGRADER_AARCH64
347 static constexpr RegistersState from(
const user_regs_struct&
regs,
const user_fpsimd_struct& fpsimd_regs);
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;
359#undef DEF_gen_register
366#ifdef ASMGRADER_X86_64
367 static constexpr RegistersState from(
const user_regs_struct&
regs,
const user_fpregs_struct& fp_regs);
371#if defined(ASMGRADER_AARCH64)
372constexpr RegistersState RegistersState::from(
const user_regs_struct& regs,
const user_fpsimd_struct& fpsimd_regs) {
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));
381 result.pstate.get_value() =
regs.pstate;
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));
387 result.fpsr = fpsimd_regs.fpsr;
388 result.fpcr = fpsimd_regs.fpcr;
393inline Expected<void, std::vector<std::string_view>> valid_pcs(
const RegistersState& before_call,
394 const RegistersState& after_call)
noexcept {
398 std::vector<std::string_view> err_changed_names;
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);
406#define CHECK_reg_nr(z, nr, pre) check_reg(before_call.pre##nr(), after_call.pre##nr(), #pre #nr);
409 BOOST_PP_REPEAT_FROM_TO(19, 30, CHECK_reg_nr, x)
412 BOOST_PP_REPEAT_FROM_TO(8, 32, CHECK_reg_nr, d)
418 check_reg(before_call.sp, after_call.sp,
"sp");
420 if (err_changed_names.empty()) {
424 return err_changed_names;
427#elif defined(ASMGRADER_X86_64)
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{};
437 result.rip =
regs.rip;
438 result.rsp =
regs.rsp;
440 result.eflags.get_value() =
regs.eflags;
443 std::ignore = fp_regs;
449inline Expected<void, std::vector<std::string_view>> valid_pcs(
const RegistersState& before_call,
450 const RegistersState& after_call)
noexcept {
454 std::vector<std::string_view> err_changed_names;
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);
462#define CHECK_reg_nr(z, nr, pre) check_reg(before_call.pre##nr, after_call.pre##nr, #pre #nr);
465 BOOST_PP_REPEAT_FROM_TO(12, 15, CHECK_reg_nr, r)
473 check_reg(before_call.rsp, after_call.rsp,
"rsp");
476 check_reg(before_call.rbx, after_call.rbx,
"rbx");
477 check_reg(before_call.rbp, after_call.rbp,
"rbp");
479 if (err_changed_names.empty()) {
483 return err_changed_names;
492template <::asmgrader::ProcessorKind Arch>
495 auto format(const ::asmgrader::IntRegister<Arch>& from, format_context& ctx)
const {
496 if (is_debug_format) {
497 using IntType =
decltype(from.get_value());
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,
505 return fmt::format_to(ctx.out(),
"{}", from.get_value());
509template <::asmgrader::ProcessorKind Arch>
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});
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());
522template <::asmgrader::ProcessorKind Arch>
525 std::size_t bin_labels_offset{};
527 constexpr auto parse(fmt::format_parse_context& ctx) {
529 const auto*
end = ctx.end();
531 const auto* closing_brace_iter = ranges::find(it,
end,
'}');
533 if (closing_brace_iter ==
end) {
537 if (closing_brace_iter - it == 0) {
541 if (*it <
'0' || *it >
'9') {
542 throw fmt::format_error(
"invalid format - offset spec is not an integer");
545 int offset_spec = detail::parse_nonnegative_int(it, closing_brace_iter, -1);
547 if (offset_spec < 0) {
548 throw fmt::format_error(
"invalid format - offset spec is not a valid integer");
551 bin_labels_offset =
static_cast<std::size_t
>(offset_spec);
557 std::vector<char> set_flags;
559 if (flags.carry_set()) {
560 set_flags.push_back(
'C');
562 if (flags.zero_set()) {
563 set_flags.push_back(
'Z');
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');
572 if (flags.overflow_set()) {
573 set_flags.push_back(
'O');
576 return fmt::format(
"{}", fmt::join(set_flags,
"|"));
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);
583 if (is_debug_format) {
586 std::string flags_bin = fmt::format(
"0b{:032b}", from.get_value());
589 constexpr auto LABEL_INIT_OFFSET = 2;
591 std::string labels_offset(bin_labels_offset + LABEL_INIT_OFFSET,
' ');
592 std::string labels =
"NZCV";
594 ctx.advance_to(fmt::format_to(ctx.out(),
"{} ({})\n{}{}", flags_bin, short_flags_str, labels_offset, labels));
596 ctx.advance_to(fmt::format_to(ctx.out(),
"{}", short_flags_str));
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);
605 if (is_debug_format) {
609 std::string flags_bin = fmt::format(
"0b{:032b}", from.get_value());
612 constexpr auto FIRST_FLAG_OFF = 11;
615 constexpr auto LABEL_INIT_OFFSET = 32 - FIRST_FLAG_OFF + 2;
617 std::string labels_offset(bin_labels_offset + LABEL_INIT_OFFSET,
' ');
618 std::string labels =
"O SZ C";
620 ctx.advance_to(fmt::format_to(ctx.out(),
"{} ({})\n{}{}", flags_bin, short_flags_str, labels_offset, labels));
622 ctx.advance_to(fmt::format_to(ctx.out(),
"{}", short_flags_str));
630#if defined(ASMGRADER_AARCH64)
635 auto format(const ::asmgrader::RegistersState& from, format_context& ctx)
const {
636 constexpr std::size_t TAB_WIDTH = 4;
637 std::string field_sep;
639 constexpr std::size_t MAX_NAME_LEN = 8;
641 if (is_debug_format) {
642 ctx.advance_to(fmt::format_to(ctx.out(),
"RegistersState{{\n{}", std::string(TAB_WIDTH,
' ')));
644 field_sep =
",\n" + std::string(TAB_WIDTH,
' ');
651 static constexpr auto LEN = [](gsl::czstring
str) {
return std::string_view{
str}.size(); };
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));
658 ctx.advance_to(fmt::format_to(ctx.out(),
"{}", name));
661 ctx.advance_to(fmt::format_to(ctx.out(),
" = {}{}", what, (print_sep ? field_sep :
"")));
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));
668 print_named_field(name, fmt::format(
"{}", reg));
672#define PRINT_gen_reg(z, num, base) print_reg("x" #num, (base).x##num());
677 print_reg(
"x29 [fp]", from.fp());
678 print_reg(
"x30 [lr]", from.lr());
680#define PRINT_fp_reg(z, num, base) print_reg((is_debug_format ? "q" #num : "d" #num), (base).q##num());
684 print_reg(
"fpsr", from.fpsr);
685 print_reg(
"fpcr", from.fpcr);
687 print_reg(
"sp", from.sp);
688 print_reg(
"pc", from.pc);
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);
695 print_named_field(
"pstate", from.pstate,
false);
698 if (is_debug_format) {
699 ctx.advance_to(fmt::format_to(ctx.out(),
"\n}}"));
708#undef NUM_GEN_REGISTERS
709#undef NUM_FP_REGISTERS
711#elif defined(ASMGRADER_X86_64)
716 auto format(const ::asmgrader::RegistersState& from, format_context& ctx)
const {
717 constexpr std::size_t TAB_WIDTH = 4;
718 std::string field_sep;
720 constexpr std::size_t MAX_NAME_LEN = 6;
722 if (is_debug_format) {
723 ctx.advance_to(fmt::format_to(ctx.out(),
"RegistersState{{\n{}", std::string(TAB_WIDTH,
' ')));
725 field_sep =
",\n" + std::string(TAB_WIDTH,
' ');
732 static constexpr auto LEN = [](gsl::czstring
str) {
return std::string_view{
str}.size(); };
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));
739 ctx.advance_to(fmt::format_to(ctx.out(),
"{}", name));
742 ctx.advance_to(fmt::format_to(ctx.out(),
" = {}{}", what, (print_sep ? field_sep :
"")));
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));
749 print_named_field(name, fmt::format(
"{}", reg));
753#define PRINT_gen_reg(r, base, elem) print_reg(BOOST_PP_STRINGIZE(elem), (base).elem);
758 print_reg(
"rip", from.rip);
759 print_reg(
"rsp", from.rip);
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);
766 print_named_field(
"eflags", from.eflags,
false);
769 if (is_debug_format) {
770 ctx.advance_to(fmt::format_to(ctx.out(),
"\n}}"));
#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 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
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 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
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