Whole-tree reformat

This follows discussions from #3413.
Followup commits add clang-format file, script and blame ignore lists.
This commit is contained in:
Paulo Matos committed 2024-04-12 16:26:02 +02:00
1 parent 028c220041
commit 2b4ec88dae
447 files changed
+78590 -73070

No files matched your search

@@ -7,8 +7,8 @@ struct generate_guest_symtable {};
struct indirect_guest_calls {};
struct callback_annotation_base {
// Prevent annotating multiple callback strategies
bool prevent_multiple;
// Prevent annotating multiple callback strategies
bool prevent_multiple;
};
struct callback_stub : callback_annotation_base {};
@@ -33,7 +33,9 @@ struct custom_repack {};
// copied (or zero-extended) without regard for the referred data.
struct emit_layout_wrappers {};
struct type_annotation_base { bool prevent_multiple; };
struct type_annotation_base {
bool prevent_multiple;
};
// Pointers to types annotated with this will be passed through without change
struct opaque_type : type_annotation_base {};
+67 -61
View File
@@ -15,31 +15,32 @@
#endif
template<typename signature>
THUNK_ABI
const int (*fexthunks_invoke_callback)(void*);
THUNK_ABI const int (*fexthunks_invoke_callback)(void*);
#ifndef _M_ARM_64
#define MAKE_THUNK(lib, name, hash) \
extern "C" __attribute__((visibility("hidden"))) THUNK_ABI int fexthunks_##lib##_##name(void *args); \
asm(".text\nfexthunks_" #lib "_" #name ":\n.byte 0xF, 0x3F\n.byte " hash );
extern "C" __attribute__((visibility("hidden"))) THUNK_ABI int fexthunks_##lib##_##name(void* args); \
asm(".text\nfexthunks_" #lib "_" #name ":\n.byte 0xF, 0x3F\n.byte " hash);
#define MAKE_CALLBACK_THUNK(name, signature, hash) \
extern "C" __attribute__((visibility("hidden"))) THUNK_ABI int fexthunks_##name(void *args); \
asm(".text\nfexthunks_" #name ":\n.byte 0xF, 0x3F\n.byte " hash ); \
template<> THUNK_ABI inline constexpr int (*fexthunks_invoke_callback<signature>)(void*) = fexthunks_##name;
extern "C" __attribute__((visibility("hidden"))) THUNK_ABI int fexthunks_##name(void* args); \
asm(".text\nfexthunks_" #name ":\n.byte 0xF, 0x3F\n.byte " hash); \
template<> \
THUNK_ABI inline constexpr int (*fexthunks_invoke_callback<signature>)(void*) = fexthunks_##name;
#else
// We're compiling for IDE integration, so provide a dummy-implementation that just calls an undefined function.
// The name of that function serves as an error message if this library somehow gets loaded at runtime.
extern "C" void BROKEN_INSTALL___TRIED_LOADING_AARCH64_BUILD_OF_GUEST_THUNK();
#define MAKE_THUNK(lib, name, hash) \
extern "C" int fexthunks_##lib##_##name(void *args) { \
extern "C" int fexthunks_##lib##_##name(void* args) { \
BROKEN_INSTALL___TRIED_LOADING_AARCH64_BUILD_OF_GUEST_THUNK(); \
return 0; \
}
#define MAKE_CALLBACK_THUNK(name, signature, hash) \
extern "C" int fexthunks_##name(void *args); \
template<> inline constexpr int (*fexthunks_invoke_callback<signature>)(void*) = fexthunks_##name;
extern "C" int fexthunks_##name(void* args); \
template<> \
inline constexpr int (*fexthunks_invoke_callback<signature>)(void*) = fexthunks_##name;
#endif
// Generated fexfn_pack_ symbols should be hidden by default, but clang does
@@ -52,41 +53,50 @@ extern "C" void BROKEN_INSTALL___TRIED_LOADING_AARCH64_BUILD_OF_GUEST_THUNK();
#endif
struct LoadlibArgs {
const char *Name;
uintptr_t CallbackThunks;
const char* Name;
uintptr_t CallbackThunks;
};
MAKE_THUNK(fex, loadlib, "0x27, 0x7e, 0xb7, 0x69, 0x5b, 0xe9, 0xab, 0x12, 0x6e, 0xf7, 0x85, 0x9d, 0x4b, 0xc9, 0xa2, 0x44, 0x46, 0xcf, 0xbd, 0xb5, 0x87, 0x43, 0xef, 0x28, 0xa2, 0x65, 0xba, 0xfc, 0x89, 0x0f, 0x77, 0x80")
MAKE_THUNK(fex, is_lib_loaded, "0xee, 0x57, 0xba, 0x0c, 0x5f, 0x6e, 0xef, 0x2a, 0x8c, 0xb5, 0x19, 0x81, 0xc9, 0x23, 0xe6, 0x51, 0xae, 0x65, 0x02, 0x8f, 0x2b, 0x5d, 0x59, 0x90, 0x6a, 0x7e, 0xe2, 0xe7, 0x1c, 0x33, 0x8a, 0xff")
MAKE_THUNK(fex, is_host_heap_allocation, "0xf5, 0x77, 0x68, 0x43, 0xbb, 0x6b, 0x28, 0x18, 0x40, 0xb0, 0xdb, 0x8a, 0x66, 0xfb, 0x0e, 0x2d, 0x98, 0xc2, 0xad, 0xe2, 0x5a, 0x18, 0x5a, 0x37, 0x2e, 0x13, 0xc9, 0xe7, 0xb9, 0x8c, 0xa9, 0x3e")
MAKE_THUNK(fex, link_address_to_function, "0xe6, 0xa8, 0xec, 0x1c, 0x7b, 0x74, 0x35, 0x27, 0xe9, 0x4f, 0x5b, 0x6e, 0x2d, 0xc9, 0xa0, 0x27, 0xd6, 0x1f, 0x2b, 0x87, 0x8f, 0x2d, 0x35, 0x50, 0xea, 0x16, 0xb8, 0xc4, 0x5e, 0x42, 0xfd, 0x77")
MAKE_THUNK(fex, allocate_host_trampoline_for_guest_function, "0x9b, 0xb2, 0xf4, 0xb4, 0x83, 0x7d, 0x28, 0x93, 0x40, 0xcb, 0xf4, 0x7a, 0x0b, 0x47, 0x85, 0x87, 0xf9, 0xbc, 0xb5, 0x27, 0xca, 0xa6, 0x93, 0xa5, 0xc0, 0x73, 0x27, 0x24, 0xae, 0xc8, 0xb8, 0x5a")
MAKE_THUNK(fex, loadlib,
"0x27, 0x7e, 0xb7, 0x69, 0x5b, 0xe9, 0xab, 0x12, 0x6e, 0xf7, 0x85, 0x9d, 0x4b, 0xc9, 0xa2, 0x44, 0x46, 0xcf, 0xbd, 0xb5, 0x87, "
"0x43, 0xef, 0x28, 0xa2, 0x65, 0xba, 0xfc, 0x89, 0x0f, 0x77, 0x80")
MAKE_THUNK(fex, is_lib_loaded,
"0xee, 0x57, 0xba, 0x0c, 0x5f, 0x6e, 0xef, 0x2a, 0x8c, 0xb5, 0x19, 0x81, 0xc9, 0x23, 0xe6, 0x51, 0xae, 0x65, 0x02, 0x8f, 0x2b, "
"0x5d, 0x59, 0x90, 0x6a, 0x7e, 0xe2, 0xe7, 0x1c, 0x33, 0x8a, 0xff")
MAKE_THUNK(fex, is_host_heap_allocation,
"0xf5, 0x77, 0x68, 0x43, 0xbb, 0x6b, 0x28, 0x18, 0x40, 0xb0, 0xdb, 0x8a, 0x66, 0xfb, 0x0e, 0x2d, 0x98, 0xc2, 0xad, 0xe2, 0x5a, "
"0x18, 0x5a, 0x37, 0x2e, 0x13, 0xc9, 0xe7, 0xb9, 0x8c, 0xa9, 0x3e")
MAKE_THUNK(fex, link_address_to_function,
"0xe6, 0xa8, 0xec, 0x1c, 0x7b, 0x74, 0x35, 0x27, 0xe9, 0x4f, 0x5b, 0x6e, 0x2d, 0xc9, 0xa0, 0x27, 0xd6, 0x1f, 0x2b, 0x87, 0x8f, "
"0x2d, 0x35, 0x50, 0xea, 0x16, 0xb8, 0xc4, 0x5e, 0x42, 0xfd, 0x77")
MAKE_THUNK(fex, allocate_host_trampoline_for_guest_function,
"0x9b, 0xb2, 0xf4, 0xb4, 0x83, 0x7d, 0x28, 0x93, 0x40, 0xcb, 0xf4, 0x7a, 0x0b, 0x47, 0x85, 0x87, 0xf9, 0xbc, 0xb5, 0x27, 0xca, "
"0xa6, 0x93, 0xa5, 0xc0, 0x73, 0x27, 0x24, 0xae, 0xc8, 0xb8, 0x5a")
#define LOAD_LIB_BASE(name, init_fn) \
__attribute__((constructor)) static void loadlib() \
{ \
LoadlibArgs args = { #name }; \
__attribute__((constructor)) static void loadlib() { \
LoadlibArgs args = {#name}; \
fexthunks_fex_loadlib(&args); \
if ((init_fn)) ((void(*)())init_fn)(); \
if ((init_fn)) ((void (*)())init_fn)(); \
}
#define LOAD_LIB(name) LOAD_LIB_BASE(name, nullptr)
#define LOAD_LIB_INIT(name, init_fn) LOAD_LIB_BASE(name, init_fn)
inline void LinkAddressToFunction(uintptr_t addr, uintptr_t target) {
struct args_t {
uint64_t original_callee;
uint64_t target_addr; // Function to call when branching to replaced_addr
};
args_t args = { addr, target };
fexthunks_fex_link_address_to_function(&args);
struct args_t {
uint64_t original_callee;
uint64_t target_addr; // Function to call when branching to replaced_addr
};
args_t args = {addr, target};
fexthunks_fex_link_address_to_function(&args);
}
inline bool IsLibLoaded(const char *libname) {
inline bool IsLibLoaded(const char* libname) {
struct {
const char *Name;
const char* Name;
bool rv;
} argsrv = { libname };
} argsrv = {libname};
fexthunks_fex_is_lib_loaded(&argsrv);
@@ -108,7 +118,7 @@ inline Result CallHostFunction(Args... args) {
// will clobber our r11 register we save the data that is inside of it.
// First we need to declare the r11 register variable
register uintptr_t host_addr asm ("r11");
register uintptr_t host_addr asm("r11");
// We then create an empty *volatile* asm block saying that it is assigning the register variable.
// Yes, it is already set coming in to this function due to custom ABI.
@@ -119,23 +129,22 @@ inline Result CallHostFunction(Args... args) {
// don't have intersecting ranges.
//
// Note that this issue is more likely to occur when clang is used to compile thunks, since its optimizer is more aggressive at using R11.
// This magic incantation also works in that instance so this is about the best we can do without adding a new attribute to clang for modifying the
// ABI.
asm volatile("" : "=r" (host_addr));
// This magic incantation also works in that instance so this is about the best we can do without adding a new attribute to clang for
// modifying the ABI.
asm volatile("" : "=r"(host_addr));
#else
// Use mm0 to pass in host_addr (chosen to avoid conflicts with vectorcall).
// Note this register overlaps the x87 st(0) register (used to return float values),
// so applications that expect this register to be preserved could run into problems.
uintptr_t host_addr; \
asm volatile("movd %%mm0, %0" : "=r" (host_addr));
uintptr_t host_addr;
asm volatile("movd %%mm0, %0" : "=r"(host_addr));
#endif
#else
uintptr_t host_addr = 0;
#endif
PackedArguments<Result, Args..., uint64_t> packed_args = {
args...,
host_addr
args..., host_addr
// Return value not explicitly initialized since an initializer would fail to compile for the void case
};
@@ -148,21 +157,20 @@ inline Result CallHostFunction(Args... args) {
// Convenience wrapper that returns the function pointer to a CallHostFunction
// instantiation matching the function signature of `host_func`
template<typename Result, typename...Args>
static auto GetCallerForHostFunction(Result (*host_func)(Args...))
-> Result(*)(Args...) {
template<typename Result, typename... Args>
static auto GetCallerForHostFunction(Result (*host_func)(Args...)) -> Result (*)(Args...) {
return &CallHostFunction<fexthunks_invoke_callback<Result(Args...)>, Result, Args...>;
}
// Ensures the given host function can safely be called from guest code.
template<typename Result, typename...Args>
template<typename Result, typename... Args>
inline void MakeHostFunctionGuestCallable(THUNK_ABI Result (*host_func)(Args...)) {
auto caller = (uintptr_t)GetCallerForHostFunction(host_func);
LinkAddressToFunction((uintptr_t)host_func, (uintptr_t)caller);
}
template<typename Target>
inline Target* AllocateHostTrampolineForGuestFunction(void THUNK_ABI (*GuestUnpacker)(uintptr_t, void*), Target *GuestTarget) {
inline Target* AllocateHostTrampolineForGuestFunction(void THUNK_ABI (*GuestUnpacker)(uintptr_t, void*), Target* GuestTarget) {
if (!GuestTarget) {
return 0;
}
@@ -171,7 +179,7 @@ inline Target* AllocateHostTrampolineForGuestFunction(void THUNK_ABI (*GuestUnpa
uint64_t GuestUnpacker;
uint64_t GuestTarget;
uint64_t rv;
} argsrv = { (uintptr_t)GuestUnpacker, (uintptr_t)GuestTarget };
} argsrv = {(uintptr_t)GuestUnpacker, (uintptr_t)GuestTarget};
fexthunks_fex_allocate_host_trampoline_for_guest_function((void*)&argsrv);
@@ -183,30 +191,28 @@ struct CallbackUnpack;
template<typename Result, typename... Args>
struct CallbackUnpack<Result(Args...)> {
static void THUNK_ABI Unpack(uintptr_t cb, void* argsv) {
using fn_t = Result(Args...);
auto callback = reinterpret_cast<fn_t*>(cb);
auto args = reinterpret_cast<PackedArguments<Result, Args...>*>(argsv);
Invoke(callback, *args);
}
static void THUNK_ABI Unpack(uintptr_t cb, void* argsv) {
using fn_t = Result(Args...);
auto callback = reinterpret_cast<fn_t*>(cb);
auto args = reinterpret_cast<PackedArguments<Result, Args...>*>(argsv);
Invoke(callback, *args);
}
};
template<typename Result, typename... Args>
struct CallbackUnpack<Result(*)(Args...)> : CallbackUnpack<Result(Args...)> {
};
struct CallbackUnpack<Result (*)(Args...)> : CallbackUnpack<Result(Args...)> {};
template<typename Target>
inline Target *AllocateHostTrampolineForGuestFunction(Target *GuestTarget) {
return AllocateHostTrampolineForGuestFunction(CallbackUnpack<Target*>::Unpack,
GuestTarget);
inline Target* AllocateHostTrampolineForGuestFunction(Target* GuestTarget) {
return AllocateHostTrampolineForGuestFunction(CallbackUnpack<Target*>::Unpack, GuestTarget);
}
inline bool IsHostHeapAllocation(void* ptr) {
struct {
void* ptr;
bool rv;
} args = { ptr, {} };
struct {
void* ptr;
bool rv;
} args = {ptr, {}};
fexthunks_fex_is_host_heap_allocation(&args);
return args.rv;
fexthunks_fex_is_host_heap_allocation(&args);
return args.rv;
}
+139 -133
View File
@@ -21,56 +21,51 @@ $end_info$
// doesn't know about them when linking thunk libraries. This issue is avoided
// by declaring the functions as weak symbols.
namespace FEXCore {
struct HostToGuestTrampolinePtr;
struct HostToGuestTrampolinePtr;
__attribute__((weak))
HostToGuestTrampolinePtr*
MakeHostTrampolineForGuestFunction(void* HostPacker, uintptr_t GuestTarget, uintptr_t GuestUnpacker);
__attribute__((weak)) HostToGuestTrampolinePtr* MakeHostTrampolineForGuestFunction(void* HostPacker, uintptr_t GuestTarget, uintptr_t GuestUnpacker);
__attribute__((weak))
HostToGuestTrampolinePtr*
FinalizeHostTrampolineForGuestFunction(HostToGuestTrampolinePtr*, void* HostPacker);
__attribute__((weak)) HostToGuestTrampolinePtr* FinalizeHostTrampolineForGuestFunction(HostToGuestTrampolinePtr*, void* HostPacker);
__attribute__((weak))
void* GetGuestStack();
__attribute__((weak)) void* GetGuestStack();
__attribute__((weak))
void MoveGuestStack(uintptr_t NewAddress);
}
__attribute__((weak)) void MoveGuestStack(uintptr_t NewAddress);
} // namespace FEXCore
template<typename Fn>
struct function_traits;
template<typename Result, typename Arg>
struct function_traits<Result(*)(Arg)> {
using result_t = Result;
using arg_t = Arg;
struct function_traits<Result (*)(Arg)> {
using result_t = Result;
using arg_t = Arg;
};
template<auto Fn>
static typename function_traits<decltype(Fn)>::result_t
fexfn_type_erased_unpack(void* argsv) {
using args_t = typename function_traits<decltype(Fn)>::arg_t;
return Fn(reinterpret_cast<args_t>(argsv));
static typename function_traits<decltype(Fn)>::result_t fexfn_type_erased_unpack(void* argsv) {
using args_t = typename function_traits<decltype(Fn)>::arg_t;
return Fn(reinterpret_cast<args_t>(argsv));
}
struct ExportEntry { uint8_t* sha256; void(*fn)(void *); };
struct ExportEntry {
uint8_t* sha256;
void (*fn)(void*);
};
typedef void fex_call_callback_t(uintptr_t callback, void *arg0, void* arg1);
typedef void fex_call_callback_t(uintptr_t callback, void* arg0, void* arg1);
#define EXPORTS(name) \
extern "C" { \
ExportEntry* fexthunks_exports_##name() { \
if (!fexldr_init_##name()) { \
return nullptr; \
} \
return exports; \
ExportEntry* fexthunks_exports_##name() { \
if (!fexldr_init_##name()) { \
return nullptr; \
} \
return exports; \
} \
}
#define LOAD_LIB_INIT(init_fn) \
__attribute__((constructor)) static void loadlib() \
{ \
init_fn (); \
__attribute__((constructor)) static void loadlib() { \
init_fn(); \
}
struct GuestcallInfo {
@@ -84,22 +79,21 @@ struct GuestcallInfo {
// host register. This macro must be placed at the very beginning of
// the function it is used in.
#if defined(_M_X86_64)
#define LOAD_INTERNAL_GUESTPTR_VIA_CUSTOM_ABI(target_variable) \
asm volatile("mov %%r11, %0" : "=r" (target_variable))
#define LOAD_INTERNAL_GUESTPTR_VIA_CUSTOM_ABI(target_variable) asm volatile("mov %%r11, %0" : "=r"(target_variable))
#elif defined(_M_ARM_64)
#define LOAD_INTERNAL_GUESTPTR_VIA_CUSTOM_ABI(target_variable) \
asm volatile("mov %0, x11" : "=r" (target_variable))
#define LOAD_INTERNAL_GUESTPTR_VIA_CUSTOM_ABI(target_variable) asm volatile("mov %0, x11" : "=r"(target_variable))
#endif
struct ParameterAnnotations {
bool is_passthrough = false;
bool assume_compatible = false;
bool is_passthrough = false;
bool assume_compatible = false;
};
// Generator emits specializations for this for each type that has compatible layout
template<typename T>
inline constexpr bool has_compatible_data_layout =
std::is_integral_v<T> || std::is_enum_v<T> || std::is_floating_point_v<T>
std::is_integral_v<T> || std::is_enum_v<T> ||
std::is_floating_point_v<T>
#ifndef IS_32BIT_THUNK
// If none of the previous predicates matched, the thunk generator did *not* emit a specialization for T.
// This should not happen on 64-bit with the currently thunked libraries, since their types
@@ -109,7 +103,7 @@ inline constexpr bool has_compatible_data_layout =
// Throwing a fake exception here will trigger a build failure.
|| (throw "Instantiated on a type that was expected to be compatible", true)
#endif
;
;
#ifndef IS_32BIT_THUNK
// Pointers have the same size, hence data layout compatibility only depends on the pointee type
@@ -119,10 +113,14 @@ template<typename T>
inline constexpr bool has_compatible_data_layout<T* const> = has_compatible_data_layout<std::remove_cv_t<T>*>;
// void* and void** are assumed to be compatible to simplify handling of libraries that use them ubiquitously
template<> inline constexpr bool has_compatible_data_layout<void*> = true;
template<> inline constexpr bool has_compatible_data_layout<const void*> = true;
template<> inline constexpr bool has_compatible_data_layout<void**> = true;
template<> inline constexpr bool has_compatible_data_layout<const void**> = true;
template<>
inline constexpr bool has_compatible_data_layout<void*> = true;
template<>
inline constexpr bool has_compatible_data_layout<const void*> = true;
template<>
inline constexpr bool has_compatible_data_layout<void**> = true;
template<>
inline constexpr bool has_compatible_data_layout<const void**> = true;
#endif
// Placeholder type to indicate the given data is in guest-layout
@@ -154,26 +152,27 @@ struct guest_layout<T*> {
type data;
// Allow implicit conversion for function pointers, since they disallow use of host_layout
guest_layout& operator=(const T* from) requires (std::is_function_v<T>) {
guest_layout& operator=(const T* from) requires (std::is_function_v<T>)
{
// TODO: Assert upper 32 bits are zero
data = reinterpret_cast<uintptr_t>(from);
return *this;
}
guest_layout<T>* get_pointer() {
return reinterpret_cast<guest_layout<T>*>(uintptr_t { data });
return reinterpret_cast<guest_layout<T>*>(uintptr_t {data});
}
const guest_layout<T>* get_pointer() const {
return reinterpret_cast<const guest_layout<T>*>(uintptr_t { data });
return reinterpret_cast<const guest_layout<T>*>(uintptr_t {data});
}
T* force_get_host_pointer() {
return reinterpret_cast<T*>(uintptr_t { data });
return reinterpret_cast<T*>(uintptr_t {data});
}
const T* force_get_host_pointer() const {
return reinterpret_cast<const T*>(uintptr_t { data });
return reinterpret_cast<const T*>(uintptr_t {data});
}
};
@@ -187,18 +186,19 @@ struct guest_layout<T* const> {
type data;
// Allow implicit conversion for function pointers, since they disallow use of host_layout
guest_layout& operator=(const T* from) requires (std::is_function_v<T>) {
guest_layout& operator=(const T* from) requires (std::is_function_v<T>)
{
// TODO: Assert upper 32 bits are zero
data = reinterpret_cast<uintptr_t>(from);
return *this;
}
guest_layout<T>* get_pointer() {
return reinterpret_cast<guest_layout<T>*>(uintptr_t { data });
return reinterpret_cast<guest_layout<T>*>(uintptr_t {data});
}
const guest_layout<T>* get_pointer() const {
return reinterpret_cast<const guest_layout<T>*>(uintptr_t { data });
return reinterpret_cast<const guest_layout<T>*>(uintptr_t {data});
}
};
@@ -215,23 +215,25 @@ struct host_layout {
T data;
explicit host_layout(const guest_layout<T>& from) requires (!std::is_enum_v<T>) : data { from.data } {
explicit host_layout(const guest_layout<T>& from) requires (!std::is_enum_v<T>)
: data {from.data} {
// NOTE: This is not strictly neccessary since differently sized types may
// be used across architectures. It's important that the host type
// can represent all guest values without loss, however.
static_assert(sizeof(data) == sizeof(from));
}
explicit host_layout(const guest_layout<T>& from) requires (std::is_enum_v<T>) : data { static_cast<T>(from.data) } {
}
explicit host_layout(const guest_layout<T>& from) requires (std::is_enum_v<T>)
: data {static_cast<T>(from.data)} {}
// Allow conversion of integral types of smaller or equal size and same sign
// to each other. Zero-extension is applied if needed.
// Notably, this is useful for handling "long"/"long long" on 64-bit, as well
// as uint8_t/char.
template<typename U>
explicit host_layout(const guest_layout<U>& from) requires (std::is_integral_v<U> && sizeof(U) <= sizeof(T) && std::is_convertible_v<T, U> && std::is_signed_v<T> == std::is_signed_v<U>) : data { static_cast<T>(from.data) } {
}
explicit host_layout(const guest_layout<U>& from)
requires (std::is_integral_v<U> && sizeof(U) <= sizeof(T) && std::is_convertible_v<T, U> && std::is_signed_v<T> == std::is_signed_v<U>)
: data {static_cast<T>(from.data)} {}
};
// Explicitly turn a host type into its corresponding host_layout
@@ -243,10 +245,7 @@ const host_layout<T>& to_host_layout(const T& t) {
template<typename T>
constexpr bool is_long_or_longlong =
std::is_same_v<T, long> ||
std::is_same_v<T, unsigned long> ||
std::is_same_v<T, long long> ||
std::is_same_v<T, unsigned long long>;
std::is_same_v<T, long> || std::is_same_v<T, unsigned long> || std::is_same_v<T, long long> || std::is_same_v<T, unsigned long long>;
template<typename T>
struct host_layout<T*> {
@@ -255,8 +254,8 @@ struct host_layout<T*> {
static_assert(!std::is_function_v<T>, "Function types must be handled separately");
// Assume underlying data is compatible and just convert the guest-sized pointer to 64-bit
explicit host_layout(const guest_layout<T*>& from) : data { (T*)(uintptr_t)from.data } {
}
explicit host_layout(const guest_layout<T*>& from)
: data {(T*)(uintptr_t)from.data} {}
host_layout() = default;
@@ -264,25 +263,30 @@ struct host_layout<T*> {
// This is useful for handling "long"/"long long" on 64-bit, which are distinct types
// but have equal data layout.
template<typename U>
explicit host_layout(const guest_layout<U*>& from) requires (is_long_or_longlong<std::remove_cv_t<T>> && std::is_integral_v<U> && std::is_convertible_v<T, U> && std::is_signed_v<T> == std::is_signed_v<U>
explicit host_layout(const guest_layout<U*>& from)
requires (is_long_or_longlong<std::remove_cv_t<T>> && std::is_integral_v<U> && std::is_convertible_v<T, U> &&
std::is_signed_v<T> == std::is_signed_v<U>
#if __clang_major__ >= 16
// Old clang versions don't support using sizeof on incomplete types when evaluating requires()
&& sizeof(T) == sizeof(U)
// Old clang versions don't support using sizeof on incomplete types when evaluating requires()
&& sizeof(T) == sizeof(U)
#endif
) : data { (T*)(uintptr_t)from.data } {
)
: data {(T*)(uintptr_t)from.data} {
}
// Allow conversion of pointers to 8-bit integer types to "char*".
// This is useful since "char"/"signed char"/"unsigned char"/"int8_t"/"uint8_t"
// may all be distinct types but have equal data layout
template<typename U>
explicit host_layout(const guest_layout<U*>& from) requires (std::is_same_v<std::remove_cv_t<T>, char> && std::is_integral_v<U> && std::is_convertible_v<T, U> && sizeof(U) == 1) : data { (T*)(uintptr_t)from.data } {
}
explicit host_layout(const guest_layout<U*>& from)
requires (std::is_same_v<std::remove_cv_t<T>, char> && std::is_integral_v<U> && std::is_convertible_v<T, U> && sizeof(U) == 1)
: data {(T*)(uintptr_t)from.data} {}
// Allow conversion of pointers to 32-bit integer types to "wchar_t*".
template<typename U>
explicit host_layout(const guest_layout<U*>& from) requires (std::is_same_v<std::remove_cv_t<T>, wchar_t> && std::is_integral_v<U> && std::is_convertible_v<T, U> && sizeof(U) == sizeof(wchar_t)) : data { (T*)(uintptr_t)from.data } {
}
explicit host_layout(const guest_layout<U*>& from) requires (
std::is_same_v<std::remove_cv_t<T>, wchar_t> && std::is_integral_v<U> && std::is_convertible_v<T, U> && sizeof(U) == sizeof(wchar_t))
: data {(T*)(uintptr_t)from.data} {}
};
template<typename T>
@@ -292,8 +296,8 @@ struct host_layout<T* const> {
static_assert(!std::is_function_v<T>, "Function types must be handled separately");
// Assume underlying data is compatible and just convert the guest-sized pointer to 64-bit
explicit host_layout(const guest_layout<T* const>& from) : data { (T*)(uintptr_t)from.data } {
}
explicit host_layout(const guest_layout<T* const>& from)
: data {(T*)(uintptr_t)from.data} {}
};
// Wrapper around host_layout that repacks from a guest_layout on construction
@@ -311,9 +315,10 @@ struct repack_wrapper {
std::optional<host_layout<PointeeT>> data;
guest_layout<GuestT>& orig_arg;
repack_wrapper(guest_layout<GuestT>& orig_arg_) : orig_arg(orig_arg_) {
repack_wrapper(guest_layout<GuestT>& orig_arg_)
: orig_arg(orig_arg_) {
if (orig_arg.get_pointer()) {
data = { *orig_arg_.get_pointer() };
data = {*orig_arg_.get_pointer()};
if constexpr (!std::is_enum_v<T>) {
constexpr bool is_compatible = has_compatible_data_layout<T> && std::is_same_v<T, GuestT>;
@@ -326,7 +331,11 @@ struct repack_wrapper {
~repack_wrapper() {
// TODO: Properly detect opaque types
if constexpr (std::is_class_v<std::remove_pointer_t<T>> && requires(guest_layout<T> t, decltype(data) h) { t.get_pointer(); (bool)h; *data; }) {
if constexpr (std::is_class_v<std::remove_pointer_t<T>> && requires(guest_layout<T> t, decltype(data) h) {
t.get_pointer();
(bool)h;
*data;
}) {
if (data) {
// NOTE: It's assumed that the native host library didn't modify any
// const-pointees, so we skip automatic exit repacking for them.
@@ -353,7 +362,7 @@ struct repack_wrapper {
template<typename T, typename GuestT>
static repack_wrapper<T, GuestT> make_repack_wrapper(guest_layout<GuestT>& orig_arg) {
return { orig_arg };
return {orig_arg};
}
template<typename T>
@@ -371,17 +380,19 @@ struct host_to_guest_convertible {
const host_layout<T>& from;
// Conversion from host to guest layout for non-pointers
operator guest_layout<T>() const requires(!std::is_pointer_v<T>) {
operator guest_layout<T>() const requires (!std::is_pointer_v<T>)
{
if constexpr (std::is_enum_v<T>) {
// enums are represented by fixed-size integers in guest_layout, so explicitly cast them
return guest_layout<T> { static_cast<std::underlying_type_t<T>>(from.data) };
return guest_layout<T> {static_cast<std::underlying_type_t<T>>(from.data)};
} else {
guest_layout<T> ret { .data = from.data };
guest_layout<T> ret {.data = from.data};
return ret;
}
}
operator guest_layout<T>() const requires(std::is_pointer_v<T>) {
operator guest_layout<T>() const requires (std::is_pointer_v<T>)
{
// TODO: Assert upper 32 bits are zero
guest_layout<T> ret;
ret.data = reinterpret_cast<uintptr_t>(from.data);
@@ -390,28 +401,33 @@ struct host_to_guest_convertible {
#if IS_32BIT_THUNK
// Allow size_t -> uint32_t conversions, since they are so common on 32-bit
operator guest_layout<uint32_t>() const requires(std::is_same_v<T, size_t>) {
return { static_cast<uint32_t>(from.data) };
operator guest_layout<uint32_t>() const requires (std::is_same_v<T, size_t>)
{
return {static_cast<uint32_t>(from.data)};
}
#endif
// Make guest_layout of "long long" and "long" interoperable, since they are
// the same type as far as data layout is concerned.
operator guest_layout<const unsigned long long*>() const requires(std::is_same_v<T, const unsigned long*>) {
operator guest_layout<const unsigned long long*>() const requires (std::is_same_v<T, const unsigned long*>)
{
return (guest_layout<const unsigned long long*>)reinterpret_cast<const host_to_guest_convertible<const unsigned long long*>&>(*this);
}
// Make guest_layout of "char" and "uint8_t" interoperable
operator guest_layout<const uint8_t*>() const requires(std::is_same_v<T, const char*>) {
operator guest_layout<const uint8_t*>() const requires (std::is_same_v<T, const char*>)
{
return (guest_layout<const uint8_t*>)reinterpret_cast<const host_to_guest_convertible<const uint8_t*>&>(*this);
}
operator guest_layout<uint8_t*>() const requires(std::is_same_v<T, char*>) {
operator guest_layout<uint8_t*>() const requires (std::is_same_v<T, char*>)
{
return (guest_layout<uint8_t*>)reinterpret_cast<const host_to_guest_convertible<uint8_t*>&>(*this);
}
// Make guest_layout of "wchar_t" and "uint32_t" interoperable
operator guest_layout<uint32_t*>() const requires(std::is_same_v<T, wchar_t*>) {
operator guest_layout<uint32_t*>() const requires (std::is_same_v<T, wchar_t*>)
{
return (guest_layout<uint32_t*>)reinterpret_cast<const host_to_guest_convertible<uint32_t*>&>(*this);
}
@@ -420,14 +436,16 @@ struct host_to_guest_convertible {
// Allow conversion of integral types of same size and sign to each other.
// This is useful for handling "long"/"long long" on 64-bit, as well as uint8_t/char.
template<typename U>
operator guest_layout<U>() const requires (std::is_integral_v<U> && sizeof(U) == sizeof(T) && std::is_convertible_v<T, U> && std::is_signed_v<T> == std::is_signed_v<U>) {
return guest_layout<U> { .data { static_cast<T>(from.data) } };
operator guest_layout<U>() const
requires (std::is_integral_v<U> && sizeof(U) == sizeof(T) && std::is_convertible_v<T, U> && std::is_signed_v<T> == std::is_signed_v<U>)
{
return guest_layout<U> {.data {static_cast<T>(from.data)}};
}
};
template<typename T>
inline host_to_guest_convertible<T> to_guest(const host_layout<T>& from) {
return { from };
return {from};
}
template<typename>
@@ -453,7 +471,7 @@ auto Projection(guest_layout<T>& data) {
if constexpr (Annotation.is_passthrough) {
return data;
} else if constexpr ((IsCompatible<T, Annotation>() && std::is_same_v<T, HostT>) || !std::is_pointer_v<T>) {
return host_layout<HostT> { data }.data;
return host_layout<HostT> {data}.data;
} else {
// This argument requires temporary storage for repacked data
// *and* it needs to call custom repack functions (if any)
@@ -481,11 +499,9 @@ public:
template<typename T, typename... Args>
T* New(Args&&... args) {
Next -= sizeof(T);
Next &= ~uintptr_t { alignof(T) - 1 };
Next &= ~uintptr_t {alignof(T) - 1};
FEXCore::MoveGuestStack(Next);
return new (reinterpret_cast<void*>(Next)) T {
std::forward<Args>(args)...
};
return new (reinterpret_cast<void*>(Next)) T {std::forward<Args>(args)...};
}
};
#endif
@@ -493,18 +509,14 @@ public:
template<typename Result, typename... Args>
struct CallbackUnpack<Result(Args...)> {
static Result CallGuestPtr(Args... args) {
GuestcallInfo *guestcall;
GuestcallInfo* guestcall;
LOAD_INTERNAL_GUESTPTR_VIA_CUSTOM_ABI(guestcall);
#ifndef IS_32BIT_THUNK
PackedArguments<Result, guest_layout<Args>...> packed_args = {
to_guest(to_host_layout(args))...
};
PackedArguments<Result, guest_layout<Args>...> packed_args = {to_guest(to_host_layout(args))...};
#else
GuestStackBumpAllocator GuestStack;
auto& packed_args = *GuestStack.New<PackedArguments<Result, guest_layout<Args>...>>(
to_guest(to_host_layout(args))...
);
auto& packed_args = *GuestStack.New<PackedArguments<Result, guest_layout<Args>...>>(to_guest(to_host_layout(args))...);
#endif
guestcall->CallCallback(guestcall->GuestUnpacker, guestcall->GuestTarget, &packed_args);
@@ -530,51 +542,51 @@ struct GuestWrapperForHostFunction<Result(Args...), GuestArgs...> {
constexpr auto CBIndex = sizeof...(GuestArgs);
uintptr_t cb;
static_assert(CBIndex <= 18 || CBIndex == 23);
if constexpr(CBIndex == 0) {
if constexpr (CBIndex == 0) {
cb = args->a0;
} else if constexpr(CBIndex == 1) {
} else if constexpr (CBIndex == 1) {
cb = args->a1;
} else if constexpr(CBIndex == 2) {
} else if constexpr (CBIndex == 2) {
cb = args->a2;
} else if constexpr(CBIndex == 3) {
} else if constexpr (CBIndex == 3) {
cb = args->a3;
} else if constexpr(CBIndex == 4) {
} else if constexpr (CBIndex == 4) {
cb = args->a4;
} else if constexpr(CBIndex == 5) {
} else if constexpr (CBIndex == 5) {
cb = args->a5;
} else if constexpr(CBIndex == 6) {
} else if constexpr (CBIndex == 6) {
cb = args->a6;
} else if constexpr(CBIndex == 7) {
} else if constexpr (CBIndex == 7) {
cb = args->a7;
} else if constexpr(CBIndex == 8) {
} else if constexpr (CBIndex == 8) {
cb = args->a8;
} else if constexpr(CBIndex == 9) {
} else if constexpr (CBIndex == 9) {
cb = args->a9;
} else if constexpr(CBIndex == 10) {
} else if constexpr (CBIndex == 10) {
cb = args->a10;
} else if constexpr(CBIndex == 11) {
} else if constexpr (CBIndex == 11) {
cb = args->a11;
} else if constexpr(CBIndex == 12) {
} else if constexpr (CBIndex == 12) {
cb = args->a12;
} else if constexpr(CBIndex == 13) {
} else if constexpr (CBIndex == 13) {
cb = args->a13;
} else if constexpr(CBIndex == 14) {
} else if constexpr (CBIndex == 14) {
cb = args->a14;
} else if constexpr(CBIndex == 15) {
} else if constexpr (CBIndex == 15) {
cb = args->a15;
} else if constexpr(CBIndex == 16) {
} else if constexpr (CBIndex == 16) {
cb = args->a16;
} else if constexpr(CBIndex == 17) {
} else if constexpr (CBIndex == 17) {
cb = args->a17;
} else if constexpr(CBIndex == 18) {
} else if constexpr (CBIndex == 18) {
cb = args->a18;
} else if constexpr(CBIndex == 23) {
} else if constexpr (CBIndex == 23) {
cb = args->a23;
}
// This is almost the same type as "Result func(Args..., uintptr_t)", but
// individual parameters annotated as passthrough are replaced by guest_layout<GuestArgs>
auto callback = reinterpret_cast<Result(*)(std::conditional_t<Annotations.is_passthrough, guest_layout<GuestArgs>, Args>..., uintptr_t)>(cb);
auto callback = reinterpret_cast<Result (*)(std::conditional_t<Annotations.is_passthrough, guest_layout<GuestArgs>, Args>..., uintptr_t)>(cb);
auto f = [&callback](guest_layout<GuestArgs>... args, uintptr_t target) -> Result {
// Fold over each of Annotations, Args, and args. This will match up the elements in triplets.
@@ -585,25 +597,20 @@ struct GuestWrapperForHostFunction<Result(Args...), GuestArgs...> {
};
template<typename FuncType>
void MakeHostTrampolineForGuestFunctionAt(uintptr_t GuestTarget, uintptr_t GuestUnpacker, FuncType **Func) {
*Func = (FuncType*)FEXCore::MakeHostTrampolineForGuestFunction(
(void*)&CallbackUnpack<FuncType>::CallGuestPtr,
GuestTarget,
GuestUnpacker);
void MakeHostTrampolineForGuestFunctionAt(uintptr_t GuestTarget, uintptr_t GuestUnpacker, FuncType** Func) {
*Func = (FuncType*)FEXCore::MakeHostTrampolineForGuestFunction((void*)&CallbackUnpack<FuncType>::CallGuestPtr, GuestTarget, GuestUnpacker);
}
template<typename F>
void FinalizeHostTrampolineForGuestFunction(F* PreallocatedTrampolineForGuestFunction) {
FEXCore::FinalizeHostTrampolineForGuestFunction(
(FEXCore::HostToGuestTrampolinePtr*)PreallocatedTrampolineForGuestFunction,
(void*)&CallbackUnpack<F>::CallGuestPtr);
FEXCore::FinalizeHostTrampolineForGuestFunction((FEXCore::HostToGuestTrampolinePtr*)PreallocatedTrampolineForGuestFunction,
(void*)&CallbackUnpack<F>::CallGuestPtr);
}
template<typename F>
void FinalizeHostTrampolineForGuestFunction(guest_layout<F*> PreallocatedTrampolineForGuestFunction) {
FEXCore::FinalizeHostTrampolineForGuestFunction(
(FEXCore::HostToGuestTrampolinePtr*)PreallocatedTrampolineForGuestFunction.data,
(void*)&CallbackUnpack<F>::CallGuestPtr);
FEXCore::FinalizeHostTrampolineForGuestFunction((FEXCore::HostToGuestTrampolinePtr*)PreallocatedTrampolineForGuestFunction.data,
(void*)&CallbackUnpack<F>::CallGuestPtr);
}
// In the case of the thunk host_loader being the default, FEX need to use dlsym with RTLD_DEFAULT.
@@ -612,7 +619,6 @@ void FinalizeHostTrampolineForGuestFunction(guest_layout<F*> PreallocatedTrampol
// Common usecase is LD_PRELOAD with a library that defines some symbols.
// And then programs and libraries will pick up the preloaded symbols.
// ex: MangoHud overrides GLX and EGL symbols.
inline
void *dlsym_default(void* handle, const char* symbol) {
inline void* dlsym_default(void* handle, const char* symbol) {
return dlsym(RTLD_DEFAULT, symbol);
}
+545 -117
View File
@@ -8,100 +8,513 @@ template<typename Result, typename... Args>
struct __attribute__((packed)) PackedArguments;
template<typename R>
struct __attribute__((packed)) PackedArguments<R> { R rv; };
struct __attribute__((packed)) PackedArguments<R> {
R rv;
};
template<typename R, typename A0>
struct __attribute__((packed)) PackedArguments<R, A0> { A0 a0; R rv; };
struct __attribute__((packed)) PackedArguments<R, A0> {
A0 a0;
R rv;
};
template<typename R, typename A0, typename A1>
struct __attribute__((packed)) PackedArguments<R, A0, A1> { A0 a0; A1 a1; R rv; };
struct __attribute__((packed)) PackedArguments<R, A0, A1> {
A0 a0;
A1 a1;
R rv;
};
template<typename R, typename A0, typename A1, typename A2>
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2> { A0 a0; A1 a1; A2 a2; R rv; };
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2> {
A0 a0;
A1 a1;
A2 a2;
R rv;
};
template<typename R, typename A0, typename A1, typename A2, typename A3>
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2, A3> { A0 a0; A1 a1; A2 a2; A3 a3; R rv; };
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2, A3> {
A0 a0;
A1 a1;
A2 a2;
A3 a3;
R rv;
};
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4>
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2, A3, A4> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; R rv; };
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2, A3, A4> {
A0 a0;
A1 a1;
A2 a2;
A3 a3;
A4 a4;
R rv;
};
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5>
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2, A3, A4, A5> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; R rv; };
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2, A3, A4, A5> {
A0 a0;
A1 a1;
A2 a2;
A3 a3;
A4 a4;
A5 a5;
R rv;
};
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6>
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2, A3, A4, A5, A6> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; R rv; };
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2, A3, A4, A5, A6> {
A0 a0;
A1 a1;
A2 a2;
A3 a3;
A4 a4;
A5 a5;
A6 a6;
R rv;
};
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7>
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; R rv; };
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7> {
A0 a0;
A1 a1;
A2 a2;
A3 a3;
A4 a4;
A5 a5;
A6 a6;
A7 a7;
R rv;
};
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8>
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; R rv; };
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8> {
A0 a0;
A1 a1;
A2 a2;
A3 a3;
A4 a4;
A5 a5;
A6 a6;
A7 a7;
A8 a8;
R rv;
};
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9>
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; R rv; };
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9> {
A0 a0;
A1 a1;
A2 a2;
A3 a3;
A4 a4;
A5 a5;
A6 a6;
A7 a7;
A8 a8;
A9 a9;
R rv;
};
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10>
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; R rv; };
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11>
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; R rv; };
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12>
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; R rv; };
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13>
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; A13 a13; R rv; };
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13, typename A14>
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; A13 a13; A14 a14; R rv; };
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9,
typename A10, typename A11, typename A12, typename A13, typename A14, typename A15, typename A16, typename A17, typename A18, typename A19,
typename A20, typename A21, typename A22>
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9,
A10, A11, A12, A13, A14, A15, A16, A17, A18, A19,
A20, A21, A22> {
A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9;
A10 a10; A11 a11; A12 a12; A13 a13; A14 a14; A15 a15; A16 a16; A17 a17; A18 a18; A19 a19;
A20 a20; A21 a21; A22 a22; R rv;
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10> {
A0 a0;
A1 a1;
A2 a2;
A3 a3;
A4 a4;
A5 a5;
A6 a6;
A7 a7;
A8 a8;
A9 a9;
A10 a10;
R rv;
};
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8,
typename A9, typename A10, typename A11>
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11> {
A0 a0;
A1 a1;
A2 a2;
A3 a3;
A4 a4;
A5 a5;
A6 a6;
A7 a7;
A8 a8;
A9 a9;
A10 a10;
A11 a11;
R rv;
};
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8,
typename A9, typename A10, typename A11, typename A12>
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12> {
A0 a0;
A1 a1;
A2 a2;
A3 a3;
A4 a4;
A5 a5;
A6 a6;
A7 a7;
A8 a8;
A9 a9;
A10 a10;
A11 a11;
A12 a12;
R rv;
};
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8,
typename A9, typename A10, typename A11, typename A12, typename A13>
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13> {
A0 a0;
A1 a1;
A2 a2;
A3 a3;
A4 a4;
A5 a5;
A6 a6;
A7 a7;
A8 a8;
A9 a9;
A10 a10;
A11 a11;
A12 a12;
A13 a13;
R rv;
};
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8,
typename A9, typename A10, typename A11, typename A12, typename A13, typename A14>
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14> {
A0 a0;
A1 a1;
A2 a2;
A3 a3;
A4 a4;
A5 a5;
A6 a6;
A7 a7;
A8 a8;
A9 a9;
A10 a10;
A11 a11;
A12 a12;
A13 a13;
A14 a14;
R rv;
};
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9,
typename A10, typename A11, typename A12, typename A13, typename A14, typename A15, typename A16, typename A17, typename A18, typename A19,
typename A20, typename A21, typename A22, typename A23>
struct __attribute__((packed)) PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9,
A10, A11, A12, A13, A14, A15, A16, A17, A18, A19,
A20, A21, A22, A23> {
A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9;
A10 a10; A11 a11; A12 a12; A13 a13; A14 a14; A15 a15; A16 a16; A17 a17; A18 a18; A19 a19;
A20 a20; A21 a21; A22 a22; A23 a23; R rv;
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8,
typename A9, typename A10, typename A11, typename A12, typename A13, typename A14, typename A15, typename A16, typename A17,
typename A18, typename A19, typename A20, typename A21, typename A22>
struct __attribute__((packed))
PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22> {
A0 a0;
A1 a1;
A2 a2;
A3 a3;
A4 a4;
A5 a5;
A6 a6;
A7 a7;
A8 a8;
A9 a9;
A10 a10;
A11 a11;
A12 a12;
A13 a13;
A14 a14;
A15 a15;
A16 a16;
A17 a17;
A18 a18;
A19 a19;
A20 a20;
A21 a21;
A22 a22;
R rv;
};
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8,
typename A9, typename A10, typename A11, typename A12, typename A13, typename A14, typename A15, typename A16, typename A17,
typename A18, typename A19, typename A20, typename A21, typename A22, typename A23>
struct __attribute__((packed))
PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23> {
A0 a0;
A1 a1;
A2 a2;
A3 a3;
A4 a4;
A5 a5;
A6 a6;
A7 a7;
A8 a8;
A9 a9;
A10 a10;
A11 a11;
A12 a12;
A13 a13;
A14 a14;
A15 a15;
A16 a16;
A17 a17;
A18 a18;
A19 a19;
A20 a20;
A21 a21;
A22 a22;
A23 a23;
R rv;
};
template<>
struct __attribute__((packed)) PackedArguments<void> { };
struct __attribute__((packed)) PackedArguments<void> {};
template<typename A0>
struct __attribute__((packed)) PackedArguments<void, A0> { A0 a0; };
struct __attribute__((packed)) PackedArguments<void, A0> {
A0 a0;
};
template<typename A0, typename A1>
struct __attribute__((packed)) PackedArguments<void, A0, A1> { A0 a0; A1 a1; };
struct __attribute__((packed)) PackedArguments<void, A0, A1> {
A0 a0;
A1 a1;
};
template<typename A0, typename A1, typename A2>
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2> { A0 a0; A1 a1; A2 a2; };
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2> {
A0 a0;
A1 a1;
A2 a2;
};
template<typename A0, typename A1, typename A2, typename A3>
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3> { A0 a0; A1 a1; A2 a2; A3 a3; };
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3> {
A0 a0;
A1 a1;
A2 a2;
A3 a3;
};
template<typename A0, typename A1, typename A2, typename A3, typename A4>
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; };
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4> {
A0 a0;
A1 a1;
A2 a2;
A3 a3;
A4 a4;
};
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5>
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; };
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5> {
A0 a0;
A1 a1;
A2 a2;
A3 a3;
A4 a4;
A5 a5;
};
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6>
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5, A6> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; };
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5, A6> {
A0 a0;
A1 a1;
A2 a2;
A3 a3;
A4 a4;
A5 a5;
A6 a6;
};
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7>
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; };
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7> {
A0 a0;
A1 a1;
A2 a2;
A3 a3;
A4 a4;
A5 a5;
A6 a6;
A7 a7;
};
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8>
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; };
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8> {
A0 a0;
A1 a1;
A2 a2;
A3 a3;
A4 a4;
A5 a5;
A6 a6;
A7 a7;
A8 a8;
};
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9>
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; };
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9> {
A0 a0;
A1 a1;
A2 a2;
A3 a3;
A4 a4;
A5 a5;
A6 a6;
A7 a7;
A8 a8;
A9 a9;
};
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10>
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; };
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10> {
A0 a0;
A1 a1;
A2 a2;
A3 a3;
A4 a4;
A5 a5;
A6 a6;
A7 a7;
A8 a8;
A9 a9;
A10 a10;
};
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11>
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; };
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12>
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; };
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13>
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; A13 a13; };
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13, typename A14>
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; A13 a13; A14 a14; };
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13, typename A14, typename A15>
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; A13 a13; A14 a14; A15 a15; };
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13, typename A14, typename A15, typename A16>
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; A13 a13; A14 a14; A15 a15; A16 a16; };
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13, typename A14, typename A15, typename A16, typename A17>
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; A13 a13; A14 a14; A15 a15; A16 a16; A17 a17; };
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13, typename A14, typename A15, typename A16, typename A17, typename A18>
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; A13 a13; A14 a14; A15 a15; A16 a16; A17 a17; A18 a18; };
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11> {
A0 a0;
A1 a1;
A2 a2;
A3 a3;
A4 a4;
A5 a5;
A6 a6;
A7 a7;
A8 a8;
A9 a9;
A10 a10;
A11 a11;
};
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9,
typename A10, typename A11, typename A12>
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12> {
A0 a0;
A1 a1;
A2 a2;
A3 a3;
A4 a4;
A5 a5;
A6 a6;
A7 a7;
A8 a8;
A9 a9;
A10 a10;
A11 a11;
A12 a12;
};
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9,
typename A10, typename A11, typename A12, typename A13>
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13> {
A0 a0;
A1 a1;
A2 a2;
A3 a3;
A4 a4;
A5 a5;
A6 a6;
A7 a7;
A8 a8;
A9 a9;
A10 a10;
A11 a11;
A12 a12;
A13 a13;
};
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9,
typename A10, typename A11, typename A12, typename A13, typename A14>
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14> {
A0 a0;
A1 a1;
A2 a2;
A3 a3;
A4 a4;
A5 a5;
A6 a6;
A7 a7;
A8 a8;
A9 a9;
A10 a10;
A11 a11;
A12 a12;
A13 a13;
A14 a14;
};
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9,
typename A10, typename A11, typename A12, typename A13, typename A14, typename A15>
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15> {
A0 a0;
A1 a1;
A2 a2;
A3 a3;
A4 a4;
A5 a5;
A6 a6;
A7 a7;
A8 a8;
A9 a9;
A10 a10;
A11 a11;
A12 a12;
A13 a13;
A14 a14;
A15 a15;
};
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9,
typename A10, typename A11, typename A12, typename A13, typename A14, typename A15, typename A16>
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16> {
A0 a0;
A1 a1;
A2 a2;
A3 a3;
A4 a4;
A5 a5;
A6 a6;
A7 a7;
A8 a8;
A9 a9;
A10 a10;
A11 a11;
A12 a12;
A13 a13;
A14 a14;
A15 a15;
A16 a16;
};
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9,
typename A10, typename A11, typename A12, typename A13, typename A14, typename A15, typename A16, typename A17>
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17> {
A0 a0;
A1 a1;
A2 a2;
A3 a3;
A4 a4;
A5 a5;
A6 a6;
A7 a7;
A8 a8;
A9 a9;
A10 a10;
A11 a11;
A12 a12;
A13 a13;
A14 a14;
A15 a15;
A16 a16;
A17 a17;
};
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9,
typename A10, typename A11, typename A12, typename A13, typename A14, typename A15, typename A16, typename A17, typename A18>
struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18> {
A0 a0;
A1 a1;
A2 a2;
A3 a3;
A4 a4;
A5 a5;
A6 a6;
A7 a7;
A8 a8;
A9 a9;
A10 a10;
A11 a11;
A12 a12;
A13 a13;
A14 a14;
A15 a15;
A16 a16;
A17 a17;
A18 a18;
};
// Helper struct that allows assigning the result of a function to a variable, even if that result is a void type.
//
@@ -110,60 +523,75 @@ struct __attribute__((packed)) PackedArguments<void, A0, A1, A2, A3, A4, A5, A6,
struct Regularize {};
template<typename T>
T&& operator,(T&& t, Regularize) {
return std::forward<T>(t);
return std::forward<T>(t);
}
template<typename Result, typename... Args, typename Func>
void Invoke(Func&& func, PackedArguments<Result, Args...>& args) requires(std::is_invocable_r_v<Result, Func, Args...>) {
constexpr auto NumArgs = sizeof...(Args);
static_assert(NumArgs <= 19 || NumArgs == 24);
void Invoke(Func&& func, PackedArguments<Result, Args...>& args) requires (std::is_invocable_r_v<Result, Func, Args...>)
{
constexpr auto NumArgs = sizeof...(Args);
static_assert(NumArgs <= 19 || NumArgs == 24);
std::conditional_t<std::is_void_v<Result>, Regularize, Result> rv;
if constexpr (NumArgs == 0) {
rv = (func(), Regularize{});
} else if constexpr (NumArgs == 1) {
rv = (func(args.a0), Regularize {});
} else if constexpr (NumArgs == 2) {
rv = (func(args.a0, args.a1), Regularize{});
} else if constexpr (NumArgs == 3) {
rv = (func(args.a0, args.a1, args.a2), Regularize{});
} else if constexpr (NumArgs == 4) {
rv = (func(args.a0, args.a1, args.a2, args.a3), Regularize{});
} else if constexpr (NumArgs == 5) {
rv = (func(args.a0, args.a1, args.a2, args.a3, args.a4), Regularize{});
} else if constexpr (NumArgs == 6) {
rv = (func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5), Regularize{});
} else if constexpr (NumArgs == 7) {
rv = (func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6), Regularize{});
} else if constexpr (NumArgs == 8) {
rv = (func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7), Regularize{});
} else if constexpr (NumArgs == 9) {
rv = (func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8), Regularize{});
} else if constexpr (NumArgs == 10) {
rv = (func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9), Regularize{});
} else if constexpr (NumArgs == 11) {
rv = (func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10), Regularize{});
} else if constexpr (NumArgs == 12) {
rv = (func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11), Regularize{});
} else if constexpr (NumArgs == 13) {
rv = (func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11, args.a12), Regularize{});
} else if constexpr (NumArgs == 14) {
rv = (func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11, args.a12, args.a13), Regularize{});
} else if constexpr (NumArgs == 15) {
rv = (func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11, args.a12, args.a13, args.a14), Regularize{});
} else if constexpr (NumArgs == 16) {
rv = (func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11, args.a12, args.a13, args.a14, args.a15), Regularize{});
} else if constexpr (NumArgs == 17) {
rv = (func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11, args.a12, args.a13, args.a14, args.a15, args.a16), Regularize{});
} else if constexpr (NumArgs == 18) {
rv = (func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11, args.a12, args.a13, args.a14, args.a15, args.a16, args.a17), Regularize{});
} else if constexpr (NumArgs == 19) {
rv = (func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11, args.a12, args.a13, args.a14, args.a15, args.a16, args.a17, args.a18), Regularize{});
} else if constexpr (NumArgs == 24) {
rv = (func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11, args.a12, args.a13, args.a14, args.a15, args.a16, args.a17, args.a18, args.a19, args.a20, args.a21, args.a22, args.a23), Regularize{});
}
std::conditional_t<std::is_void_v<Result>, Regularize, Result> rv;
if constexpr (NumArgs == 0) {
rv = (func(), Regularize {});
} else if constexpr (NumArgs == 1) {
rv = (func(args.a0), Regularize {});
} else if constexpr (NumArgs == 2) {
rv = (func(args.a0, args.a1), Regularize {});
} else if constexpr (NumArgs == 3) {
rv = (func(args.a0, args.a1, args.a2), Regularize {});
} else if constexpr (NumArgs == 4) {
rv = (func(args.a0, args.a1, args.a2, args.a3), Regularize {});
} else if constexpr (NumArgs == 5) {
rv = (func(args.a0, args.a1, args.a2, args.a3, args.a4), Regularize {});
} else if constexpr (NumArgs == 6) {
rv = (func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5), Regularize {});
} else if constexpr (NumArgs == 7) {
rv = (func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6), Regularize {});
} else if constexpr (NumArgs == 8) {
rv = (func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7), Regularize {});
} else if constexpr (NumArgs == 9) {
rv = (func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8), Regularize {});
} else if constexpr (NumArgs == 10) {
rv = (func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9), Regularize {});
} else if constexpr (NumArgs == 11) {
rv = (func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10), Regularize {});
} else if constexpr (NumArgs == 12) {
rv = (func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11), Regularize {});
} else if constexpr (NumArgs == 13) {
rv = (func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11, args.a12),
Regularize {});
} else if constexpr (NumArgs == 14) {
rv = (func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11, args.a12, args.a13),
Regularize {});
} else if constexpr (NumArgs == 15) {
rv = (func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11, args.a12,
args.a13, args.a14),
Regularize {});
} else if constexpr (NumArgs == 16) {
rv = (func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11, args.a12,
args.a13, args.a14, args.a15),
Regularize {});
} else if constexpr (NumArgs == 17) {
rv = (func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11, args.a12,
args.a13, args.a14, args.a15, args.a16),
Regularize {});
} else if constexpr (NumArgs == 18) {
rv = (func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11, args.a12,
args.a13, args.a14, args.a15, args.a16, args.a17),
Regularize {});
} else if constexpr (NumArgs == 19) {
rv = (func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11, args.a12,
args.a13, args.a14, args.a15, args.a16, args.a17, args.a18),
Regularize {});
} else if constexpr (NumArgs == 24) {
rv = (func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11, args.a12,
args.a13, args.a14, args.a15, args.a16, args.a17, args.a18, args.a19, args.a20, args.a21, args.a22, args.a23),
Regularize {});
}
if constexpr (!std::is_void_v<Result>) {
args.rv = rv;
}
if constexpr (!std::is_void_v<Result>) {
args.rv = rv;
}
}