ReXGlue SDK
Loading...
Searching...
No Matches
midasm.h
Go to the documentation of this file.
1
10
11#pragma once
12
13#include <array>
14#include <cstddef>
15#include <string_view>
16#include <tuple>
17#include <type_traits>
18
20#include <rex/ppc/context.h>
21#include <rex/types.h>
22
30#define REX_DETAIL_MIDASM_COUNT(...) \
31 REX_DETAIL_MIDASM_PICK(__VA_ARGS__ __VA_OPT__(, ) 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, \
32 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, \
33 1, 0)
34
45#define REX_DETAIL_MIDASM_PICK(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \
46 _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, \
47 _29, _30, _31, _32, count, ...) \
48 count
49
57#define REX_DETAIL_MIDASM_UNPAREN(...) __VA_ARGS__
58
66#define REX_DETAIL_MIDASM_EXPAND_PASTE(a, b) REX_DETAIL_MIDASM_PASTE(a, b)
67
75#define REX_DETAIL_MIDASM_PASTE(a, b) a##b
76
84#define REX_DETAIL_MIDASM_PARAM(body, index) \
85 rex::ppc::detail::MidAsmErasedT<rex::ppc::detail::MidAsmArg<decltype(&(body)), (index)>> \
86 rex_arg##index
87
95#define REX_DETAIL_MIDASM_ARG(body, index) \
96 rex::ppc::detail::RestoreMidAsmArg<rex::ppc::detail::MidAsmArg<decltype(&(body)), (index)>>( \
97 rex_arg##index)
98
108#define REX_DETAIL_MIDASM_LIST_0(body, each)
109
119#define REX_DETAIL_MIDASM_LIST_1(body, each) each(body, 0)
120
130#define REX_DETAIL_MIDASM_LIST_2(body, each) REX_DETAIL_MIDASM_LIST_1(body, each), each(body, 1)
131
141#define REX_DETAIL_MIDASM_LIST_3(body, each) REX_DETAIL_MIDASM_LIST_2(body, each), each(body, 2)
142
152#define REX_DETAIL_MIDASM_LIST_4(body, each) REX_DETAIL_MIDASM_LIST_3(body, each), each(body, 3)
153
163#define REX_DETAIL_MIDASM_LIST_5(body, each) REX_DETAIL_MIDASM_LIST_4(body, each), each(body, 4)
164
174#define REX_DETAIL_MIDASM_LIST_6(body, each) REX_DETAIL_MIDASM_LIST_5(body, each), each(body, 5)
175
185#define REX_DETAIL_MIDASM_LIST_7(body, each) REX_DETAIL_MIDASM_LIST_6(body, each), each(body, 6)
186
196#define REX_DETAIL_MIDASM_LIST_8(body, each) REX_DETAIL_MIDASM_LIST_7(body, each), each(body, 7)
197
207#define REX_DETAIL_MIDASM_LIST_9(body, each) REX_DETAIL_MIDASM_LIST_8(body, each), each(body, 8)
208
218#define REX_DETAIL_MIDASM_LIST_10(body, each) REX_DETAIL_MIDASM_LIST_9(body, each), each(body, 9)
219
229#define REX_DETAIL_MIDASM_LIST_11(body, each) REX_DETAIL_MIDASM_LIST_10(body, each), each(body, 10)
230
240#define REX_DETAIL_MIDASM_LIST_12(body, each) REX_DETAIL_MIDASM_LIST_11(body, each), each(body, 11)
241
251#define REX_DETAIL_MIDASM_LIST_13(body, each) REX_DETAIL_MIDASM_LIST_12(body, each), each(body, 12)
252
262#define REX_DETAIL_MIDASM_LIST_14(body, each) REX_DETAIL_MIDASM_LIST_13(body, each), each(body, 13)
263
273#define REX_DETAIL_MIDASM_LIST_15(body, each) REX_DETAIL_MIDASM_LIST_14(body, each), each(body, 14)
274
284#define REX_DETAIL_MIDASM_LIST_16(body, each) REX_DETAIL_MIDASM_LIST_15(body, each), each(body, 15)
285
295#define REX_DETAIL_MIDASM_LIST_17(body, each) REX_DETAIL_MIDASM_LIST_16(body, each), each(body, 16)
296
306#define REX_DETAIL_MIDASM_LIST_18(body, each) REX_DETAIL_MIDASM_LIST_17(body, each), each(body, 17)
307
317#define REX_DETAIL_MIDASM_LIST_19(body, each) REX_DETAIL_MIDASM_LIST_18(body, each), each(body, 18)
318
328#define REX_DETAIL_MIDASM_LIST_20(body, each) REX_DETAIL_MIDASM_LIST_19(body, each), each(body, 19)
329
339#define REX_DETAIL_MIDASM_LIST_21(body, each) REX_DETAIL_MIDASM_LIST_20(body, each), each(body, 20)
340
350#define REX_DETAIL_MIDASM_LIST_22(body, each) REX_DETAIL_MIDASM_LIST_21(body, each), each(body, 21)
351
361#define REX_DETAIL_MIDASM_LIST_23(body, each) REX_DETAIL_MIDASM_LIST_22(body, each), each(body, 22)
362
372#define REX_DETAIL_MIDASM_LIST_24(body, each) REX_DETAIL_MIDASM_LIST_23(body, each), each(body, 23)
373
383#define REX_DETAIL_MIDASM_LIST_25(body, each) REX_DETAIL_MIDASM_LIST_24(body, each), each(body, 24)
384
394#define REX_DETAIL_MIDASM_LIST_26(body, each) REX_DETAIL_MIDASM_LIST_25(body, each), each(body, 25)
395
405#define REX_DETAIL_MIDASM_LIST_27(body, each) REX_DETAIL_MIDASM_LIST_26(body, each), each(body, 26)
406
416#define REX_DETAIL_MIDASM_LIST_28(body, each) REX_DETAIL_MIDASM_LIST_27(body, each), each(body, 27)
417
427#define REX_DETAIL_MIDASM_LIST_29(body, each) REX_DETAIL_MIDASM_LIST_28(body, each), each(body, 28)
428
438#define REX_DETAIL_MIDASM_LIST_30(body, each) REX_DETAIL_MIDASM_LIST_29(body, each), each(body, 29)
439
449#define REX_DETAIL_MIDASM_LIST_31(body, each) REX_DETAIL_MIDASM_LIST_30(body, each), each(body, 30)
450
460#define REX_DETAIL_MIDASM_LIST_32(body, each) REX_DETAIL_MIDASM_LIST_31(body, each), each(body, 31)
461
473// NOLINTBEGIN(bugprone-macro-parentheses): ret declares a function
474#define REX_DETAIL_MIDASM_ENTRY(ret, name, params) \
475 extern "C" ret name(REX_DETAIL_MIDASM_EXPAND_PASTE( \
476 REX_DETAIL_MIDASM_LIST_, REX_DETAIL_MIDASM_COUNT(REX_DETAIL_MIDASM_UNPAREN params))( \
477 name##_rex_body, REX_DETAIL_MIDASM_PARAM)) { \
478 return name##_rex_body(REX_DETAIL_MIDASM_EXPAND_PASTE( \
479 REX_DETAIL_MIDASM_LIST_, REX_DETAIL_MIDASM_COUNT(REX_DETAIL_MIDASM_UNPAREN params))( \
480 name##_rex_body, REX_DETAIL_MIDASM_ARG)); \
481 }
482// NOLINTEND(bugprone-macro-parentheses)
483
484namespace rex::ppc::detail {
485
486enum class MidAsmType : u8 {
487 kHook,
488 kBranch,
489 kReturnIf,
490};
491
492enum class MidAsmParam : u8 {
493 kInvalid,
494 kU32,
495 kU64,
496 kF64,
497 kVector,
498 kVectorRef,
499 kPointer,
500 kMappedPointer,
501 kRegister,
502 kCR,
503 kXER,
504 kFPSCR,
505 kContext,
506};
507
508enum class MidAsmRegisterType : u8 {
509 kInvalid,
510 kGPR,
511 kFPR,
512 kVR,
513 kCR,
514 kCTR,
515 kXER,
516 kFPSCR,
517 kReserved,
518 kContext,
519};
520
521enum class MidAsmAtResult : u8 {
522 kOk,
523 kBadPlacement,
524 kUnexpectedTarget,
525 kMissingTarget,
526 kBadTarget,
527 kBadRegister,
528 kCountMismatch,
529 kTypeMismatch,
530};
531
532inline constexpr size_t kMaxMidAsmParams = 32;
533
534struct MidAsmBody {
535 MidAsmType type;
536 size_t count;
537 std::array<MidAsmParam, kMaxMidAsmParams> params;
538};
539
541template <typename T>
542constexpr MidAsmParam ClassifyMidAsmParam() {
543 if constexpr (!std::is_reference_v<T>) {
544 if constexpr (std::is_same_v<T, u32>) {
545 return MidAsmParam::kU32;
546 } else if constexpr (std::is_same_v<T, u64>) {
547 return MidAsmParam::kU64;
548 } else if constexpr (std::is_same_v<T, f64>) {
549 return MidAsmParam::kF64;
550 } else if constexpr (std::is_same_v<T, PPCVRegister>) {
551 return MidAsmParam::kVector;
552 } else if constexpr (std::is_pointer_v<T> && !std::is_function_v<std::remove_pointer_t<T>>) {
553 return MidAsmParam::kPointer;
554 } else if constexpr (memory::is_mapped_ptr_v<T>) {
555 return MidAsmParam::kMappedPointer;
556 } else {
557 return MidAsmParam::kInvalid;
558 }
559 } else if constexpr (std::is_lvalue_reference_v<T> &&
560 !std::is_const_v<std::remove_reference_t<T>>) {
561 using Bare = std::remove_reference_t<T>;
562 if constexpr (std::is_same_v<Bare, PPCRegister>) {
563 return MidAsmParam::kRegister;
564 } else if constexpr (std::is_same_v<Bare, PPCVRegister>) {
565 return MidAsmParam::kVectorRef;
566 } else if constexpr (std::is_same_v<Bare, PPCCRRegister>) {
567 return MidAsmParam::kCR;
568 } else if constexpr (std::is_same_v<Bare, PPCXERRegister>) {
569 return MidAsmParam::kXER;
570 } else if constexpr (std::is_same_v<Bare, PPCFPSCRRegister>) {
571 return MidAsmParam::kFPSCR;
572 } else if constexpr (std::is_same_v<Bare, PPCContext>) {
573 return MidAsmParam::kContext;
574 } else {
575 return MidAsmParam::kInvalid;
576 }
577 } else {
578 return MidAsmParam::kInvalid;
579 }
580}
581
583template <typename F>
584struct MidAsmParams;
585
586template <typename R, typename... A>
587struct MidAsmParams<R (*)(A...)> {
588 static constexpr size_t kCount = sizeof...(A);
589 using Types = std::tuple<A...>;
590 static constexpr std::array<MidAsmParam, sizeof...(A)> kTypes = {ClassifyMidAsmParam<A>()...};
591};
592
593template <typename F, size_t I>
594using MidAsmArg = std::tuple_element_t<I, typename MidAsmParams<F>::Types>;
595
600template <typename T>
601struct MidAsmErased {
602 using Type = T;
603};
604
606template <typename T>
607struct MidAsmErased<T*> {
608 using Type = void*;
609};
610
612template <typename T>
613struct MidAsmErased<memory::MappedPtr<T>> {
614 using Type = memory::MappedPtr<u8>;
615};
616
618template <typename T>
619using MidAsmErasedT = MidAsmErased<T>::Type;
620
622template <typename T>
623constexpr T RestoreMidAsmArg(MidAsmErasedT<T> value) {
624 if constexpr (std::is_pointer_v<T>) {
625 return static_cast<T>(value);
626 } else if constexpr (memory::is_mapped_ptr_v<T>) {
627 return T(reinterpret_cast<memory::MappedPtrInnerType<T>::type*>(value.GetHostPtr()),
628 value.GetGuestAddress());
629 } else {
630 return value;
631 }
632}
633
639template <typename F>
640constexpr MidAsmBody MakeMidAsmBody(MidAsmType type) {
641 using Traits = MidAsmParams<F>;
642 static_assert(Traits::kCount <= kMaxMidAsmParams, "A mid asm body takes at most 32 parameters");
643 MidAsmBody body{type, Traits::kCount, {}};
644 for (size_t index = 0; index < Traits::kCount; ++index) {
645 body.params[index] = Traits::kTypes[index];
646 }
647 return body;
648}
649
655constexpr bool IsMidAsmBodyValid(const MidAsmBody& body) {
656 for (size_t index = 0; index < body.count; ++index) {
657 if (body.params[index] == MidAsmParam::kInvalid) {
658 return false;
659 }
660 }
661 return true;
662}
663
671constexpr bool ParseMidAsmIndex(std::string_view text, u32 limit, u32& value) {
672 if (text.empty() || text.size() > 3 || (text.size() > 1 && text[0] == '0')) {
673 return false;
674 }
675 u32 result = 0;
676 for (const char digit : text) {
677 if (digit < '0' || digit > '9') {
678 return false;
679 }
680 result = result * 10 + static_cast<u32>(digit - '0');
681 }
682 if (result > limit) {
683 return false;
684 }
685 value = result;
686 return true;
687}
688
695constexpr bool ParseMidAsmAddress(std::string_view text, u32& value) {
696 while (!text.empty() &&
697 (text.back() == 'u' || text.back() == 'U' || text.back() == 'l' || text.back() == 'L')) {
698 text.remove_suffix(1);
699 }
700 if (text.empty()) {
701 return false;
702 }
703 u64 result = 0;
704 size_t index = 0;
705 u32 radix = 10;
706 if (text.size() > 2 && text[0] == '0' && (text[1] == 'x' || text[1] == 'X')) {
707 radix = 16;
708 index = 2;
709 } else if (text.size() > 2 && text[0] == '0' && (text[1] == 'b' || text[1] == 'B')) {
710 radix = 2;
711 index = 2;
712 } else if (text.size() > 1 && text[0] == '0') {
713 radix = 8;
714 index = 1;
715 }
716 bool digits = false;
717 for (; index < text.size(); ++index) {
718 const char digit = text[index];
719 u32 digit_value = 0;
720 if (digit == '\'' && digits && index + 1 < text.size()) {
721 continue;
722 }
723 if (digit >= '0' && digit <= '9') {
724 digit_value = static_cast<u32>(digit - '0');
725 } else if (radix == 16 && digit >= 'a' && digit <= 'f') {
726 digit_value = static_cast<u32>(digit - 'a') + 10;
727 } else if (radix == 16 && digit >= 'A' && digit <= 'F') {
728 digit_value = static_cast<u32>(digit - 'A') + 10;
729 } else {
730 return false;
731 }
732 if (digit_value >= radix) {
733 return false;
734 }
735 digits = true;
736 result = result * radix + digit_value;
737 if (result > 0xFFFFFFFFull) {
738 return false;
739 }
740 }
741 if (!digits) {
742 return false;
743 }
744 value = static_cast<u32>(result);
745 return true;
746}
747
753constexpr bool IsMidAsmAddress(u64 address) {
754 return address != 0 && address % 4 == 0 && address <= 0xFFFFFFFFull;
755}
756
757constexpr std::string_view TrimMidAsmToken(std::string_view text) {
758 while (!text.empty() && (text.front() == ' ' || text.front() == '\t' || text.front() == '\n' ||
759 text.front() == '\r')) {
760 text.remove_prefix(1);
761 }
762 while (!text.empty() && (text.back() == ' ' || text.back() == '\t' || text.back() == '\n' ||
763 text.back() == '\r')) {
764 text.remove_suffix(1);
765 }
766 return text;
767}
768
775constexpr MidAsmRegisterType ParseMidAsmRegister(std::string_view token) {
776 u32 index = 0;
777 if (token == "ctx") {
778 return MidAsmRegisterType::kContext;
779 }
780 if (token == "ctr") {
781 return MidAsmRegisterType::kCTR;
782 }
783 if (token == "xer") {
784 return MidAsmRegisterType::kXER;
785 }
786 if (token == "fpscr") {
787 return MidAsmRegisterType::kFPSCR;
788 }
789 if (token == "reserved") {
790 return MidAsmRegisterType::kReserved;
791 }
792 if (token.starts_with("cr")) {
793 return ParseMidAsmIndex(token.substr(2), 7, index) ? MidAsmRegisterType::kCR
794 : MidAsmRegisterType::kInvalid;
795 }
796 if (token.starts_with("r")) {
797 return ParseMidAsmIndex(token.substr(1), 31, index) && index != 1
798 ? MidAsmRegisterType::kGPR
799 : MidAsmRegisterType::kInvalid;
800 }
801 if (token.starts_with("f")) {
802 return ParseMidAsmIndex(token.substr(1), 31, index) ? MidAsmRegisterType::kFPR
803 : MidAsmRegisterType::kInvalid;
804 }
805 if (token.starts_with("v")) {
806 return ParseMidAsmIndex(token.substr(1), 127, index) ? MidAsmRegisterType::kVR
807 : MidAsmRegisterType::kInvalid;
808 }
809 return MidAsmRegisterType::kInvalid;
810}
811
818constexpr bool IsMidAsmRegisterFit(MidAsmRegisterType reg, MidAsmParam param) {
819 switch (reg) {
820 case MidAsmRegisterType::kGPR:
821 return param == MidAsmParam::kU32 || param == MidAsmParam::kU64 ||
822 param == MidAsmParam::kRegister || param == MidAsmParam::kPointer ||
823 param == MidAsmParam::kMappedPointer;
824 case MidAsmRegisterType::kFPR:
825 return param == MidAsmParam::kF64 || param == MidAsmParam::kRegister;
826 case MidAsmRegisterType::kVR:
827 return param == MidAsmParam::kVector || param == MidAsmParam::kVectorRef;
828 case MidAsmRegisterType::kCR:
829 return param == MidAsmParam::kCR;
830 case MidAsmRegisterType::kCTR:
831 case MidAsmRegisterType::kReserved:
832 return param == MidAsmParam::kRegister;
833 case MidAsmRegisterType::kXER:
834 return param == MidAsmParam::kXER;
835 case MidAsmRegisterType::kFPSCR:
836 return param == MidAsmParam::kFPSCR;
837 case MidAsmRegisterType::kContext:
838 return param == MidAsmParam::kContext;
839 case MidAsmRegisterType::kInvalid:
840 return false;
841 }
842 return false;
843}
844
852constexpr MidAsmAtResult CheckMidAsmAt(const MidAsmBody& body, std::string_view placement,
853 std::string_view arguments) {
854 if (TrimMidAsmToken(placement) != "before" && TrimMidAsmToken(placement) != "after") {
855 return MidAsmAtResult::kBadPlacement;
856 }
857 std::string_view rest = TrimMidAsmToken(arguments);
858 const bool has_arguments = !rest.empty();
859 size_t registers = 0;
860 bool first = true;
861 while (has_arguments) {
862 const size_t comma = rest.find(',');
863 const std::string_view token = TrimMidAsmToken(rest.substr(0, comma));
864 const bool is_number = !token.empty() && token[0] >= '0' && token[0] <= '9';
865 if (first && body.type == MidAsmType::kBranch) {
866 if (!is_number) {
867 return MidAsmAtResult::kMissingTarget;
868 }
869 u32 target = 0;
870 if (!ParseMidAsmAddress(token, target) || !IsMidAsmAddress(target)) {
871 return MidAsmAtResult::kBadTarget;
872 }
873 } else if (first && is_number) {
874 return MidAsmAtResult::kUnexpectedTarget;
875 } else {
876 const MidAsmRegisterType type = ParseMidAsmRegister(token);
877 if (type == MidAsmRegisterType::kInvalid) {
878 return MidAsmAtResult::kBadRegister;
879 }
880 if (registers < body.count && !IsMidAsmRegisterFit(type, body.params[registers])) {
881 return MidAsmAtResult::kTypeMismatch;
882 }
883 ++registers;
884 }
885 first = false;
886 if (comma == std::string_view::npos) {
887 break;
888 }
889 rest.remove_prefix(comma + 1);
890 }
891 if (body.type == MidAsmType::kBranch && first) {
892 return MidAsmAtResult::kMissingTarget;
893 }
894 if (registers != body.count) {
895 return MidAsmAtResult::kCountMismatch;
896 }
897 return MidAsmAtResult::kOk;
898}
899
900} // namespace rex::ppc::detail
901
917// NOLINTBEGIN(bugprone-macro-parentheses): ret and params declare a function
918#define REX_DETAIL_MIDASM_BODY(type, ret, name, params) \
919 static ret name##_rex_body params; \
920 inline constexpr rex::ppc::detail::MidAsmBody name##_rex_midasm = \
921 rex::ppc::detail::MakeMidAsmBody<decltype(&name##_rex_body)>( \
922 rex::ppc::detail::MidAsmType::type); \
923 static_assert(rex::ppc::detail::IsMidAsmBodyValid(name##_rex_midasm), \
924 "A mid asm body takes u32, u64, f64, PPCVRegister, a pointer, a MappedPtr, " \
925 "or a non const reference to a register type or PPCContext"); \
926 REX_DETAIL_MIDASM_ENTRY(ret, name, params) \
927 static ret name##_rex_body params
928// NOLINTEND(bugprone-macro-parentheses)
929
944#define REX_MIDASM_HOOK(name, params) REX_DETAIL_MIDASM_BODY(kHook, void, name, params)
945
960#define REX_MIDASM_BRANCH(name, params) REX_DETAIL_MIDASM_BODY(kBranch, bool, name, params)
961
976#define REX_MIDASM_RETURN_IF(name, params) REX_DETAIL_MIDASM_BODY(kReturnIf, bool, name, params)
977
990#define REX_DETAIL_MIDASM_AT_CHECK(name, placement, arguments, code, text) \
991 static_assert(rex::ppc::detail::CheckMidAsmAt(name##_rex_midasm, placement, arguments) != \
992 rex::ppc::detail::MidAsmAtResult::code, \
993 text)
994
1009#define REX_MIDASM_AT(name, address, placement, ...) \
1010 static_assert(rex::ppc::detail::IsMidAsmAddress(address), \
1011 "REX_MIDASM_AT address must be a nonzero multiple of 4"); \
1012 REX_DETAIL_MIDASM_AT_CHECK(name, #placement, #__VA_ARGS__, kBadPlacement, \
1013 "REX_MIDASM_AT placement must be before or after"); \
1014 REX_DETAIL_MIDASM_AT_CHECK(name, #placement, #__VA_ARGS__, kUnexpectedTarget, \
1015 "REX_MIDASM_AT takes a target only for a branch body"); \
1016 REX_DETAIL_MIDASM_AT_CHECK(name, #placement, #__VA_ARGS__, kMissingTarget, \
1017 "REX_MIDASM_AT needs the target of a branch body first"); \
1018 REX_DETAIL_MIDASM_AT_CHECK(name, #placement, #__VA_ARGS__, kBadTarget, \
1019 "REX_MIDASM_AT target must be a nonzero multiple of 4"); \
1020 REX_DETAIL_MIDASM_AT_CHECK(name, #placement, #__VA_ARGS__, kBadRegister, \
1021 "REX_MIDASM_AT register must be r0 to r31 except r1, f0 to f31, " \
1022 "v0 to v127, cr0 to cr7, ctr, xer, fpscr, reserved or ctx"); \
1023 REX_DETAIL_MIDASM_AT_CHECK(name, #placement, #__VA_ARGS__, kCountMismatch, \
1024 "REX_MIDASM_AT register count must equal the body parameters"); \
1025 REX_DETAIL_MIDASM_AT_CHECK(name, #placement, #__VA_ARGS__, kTypeMismatch, \
1026 "REX_MIDASM_AT register type does not fit its parameter")
@ kCount
Definition guest_allocator.h:42
Integer types, byte order wrappers and size literals.