Last active
June 17, 2022 20:19
-
-
Save JohnnyonFlame/e6e5fbfa317a9c46d166115b30bc5130 to your computer and use it in GitHub Desktop.
Compile time automatic generation of softfp to hardfp thunks using templated specialization
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Compile time code-generation for conditional softfp->hardfp thunks using template specialization. | |
// As of June 2022 clang still doesn't generate correct code for this mixed ABI situation, use gcc. | |
// Special Thanks for the folks at FEX-Emu's discord for inspiration/help with this. | |
// Free software, Licensed under BSD-0. | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <stdarg.h> | |
#include <stdint.h> | |
#include <cmath> | |
#include <type_traits> | |
#include <string.h> | |
// Tags a function's ABI as softfp - | |
#define ABI_ATTR __attribute__((pcs("aapcs"))) | |
template <typename... Args> | |
struct has_float_arg: std::integral_constant<bool, false> {}; | |
template <typename T, typename... Args> | |
struct has_float_arg<T, Args...>: std::integral_constant<bool, std::is_floating_point<T>::value || has_float_arg<Args...>::value> { }; | |
template<typename D, typename R, typename... Args> | |
struct LmaoImpl; | |
template<typename D, typename R, typename... Args> | |
struct LmaoImpl<D, R(*)(Args...)> | |
{ | |
__attribute__((noinline)) ABI_ATTR static R bridge(Args... args) | |
{ | |
return D::template bridge_impl<Args...>(args...); | |
} | |
static constexpr bool has_float_args = has_float_arg<R, Args...>::value; | |
}; | |
template<typename D, typename R, typename... Args> | |
struct LmaoImpl<D, R(*)(Args...) noexcept> | |
{ | |
__attribute__((noinline)) ABI_ATTR static R bridge(Args... args) | |
{ | |
return D::template bridge_impl<Args...>(args...); | |
} | |
static constexpr bool has_float_args = has_float_arg<R, Args...>::value; | |
}; | |
template<auto *F> | |
struct Lmao : LmaoImpl<Lmao<F>, decltype(F)> | |
{ | |
static constexpr auto orig = F; | |
template<typename... Args> | |
static auto bridge_impl(Args... args) | |
{ | |
return F(args...); | |
} | |
}; | |
template<typename testType> | |
struct is_function_pointer | |
{ | |
static const bool value = | |
std::is_pointer<testType>::value ? | |
std::is_function<typename std::remove_pointer<testType>::type>::value : | |
false; | |
}; | |
// Functions with conditional fowarded arguments | |
template <auto *F, class T = Lmao<F>, typename std::enable_if< | |
is_function_pointer<decltype(F)>::value, | |
bool>::type = true> | |
constexpr intptr_t select_either() | |
{ | |
if constexpr (T::has_float_args) | |
return (intptr_t)T::bridge; | |
else | |
return (intptr_t)T::orig; | |
} | |
// Non-function or prototype-less symbols | |
template <auto **F, typename std::enable_if< | |
!is_function_pointer<decltype(F)>::value, | |
bool>::type = true> | |
constexpr intptr_t select_either() | |
{ | |
return (intptr_t)F; | |
} | |
#define PTR_ENTRY(f) {#f,select_either<&f>(), (intptr_t)&f} | |
typedef struct ThunkList { | |
const char *a; | |
intptr_t f; | |
intptr_t orig; | |
} ThunkList; | |
extern void *__aeabi_idivmod; | |
ThunkList example[] = { | |
PTR_ENTRY(atan2), | |
PTR_ENTRY(cos), | |
PTR_ENTRY(cosf), | |
PTR_ENTRY(atoi), | |
PTR_ENTRY(atof), | |
PTR_ENTRY(__aeabi_idivmod), | |
{NULL, 0} | |
}; | |
intptr_t emit_all(const char *f) | |
{ | |
for (int i = 0; example[i].f != 0; i++) | |
if (strcmp(example[i].a, f) == 0) | |
return example[i].f; | |
return 0; | |
} | |
int main() | |
{ | |
for (int i = 0; example[i].f != 0; i++) | |
printf("%s was %sthunked.\n", example[i].a, example[i].f == example[i].orig ? "not " : ""); | |
return 0; | |
} |
Author
JohnnyonFlame
commented
Jun 15, 2022
•
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment