Skip to content

Instantly share code, notes, and snippets.

@JohnnyonFlame
Last active June 17, 2022 20:19
Show Gist options
  • Save JohnnyonFlame/e6e5fbfa317a9c46d166115b30bc5130 to your computer and use it in GitHub Desktop.
Save JohnnyonFlame/e6e5fbfa317a9c46d166115b30bc5130 to your computer and use it in GitHub Desktop.
Compile time automatic generation of softfp to hardfp thunks using templated specialization
// 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;
}
@JohnnyonFlame
Copy link
Author

JohnnyonFlame commented Jun 15, 2022

$ ./test
atan2 was thunked.
cos was thunked.
cosf was thunked.
atoi was not thunked.
atof was thunked.
LmaoImpl<Lmao<&atof>, double (*)(char const*) noexcept>::bridge(char const*):
        push    {r3, lr}
        movs    r1, #0
        bl      strtod
        vmov    r0, r1, d0
        pop     {r3, pc}
LmaoImpl<Lmao<&cosf>, float (*)(float) noexcept>::bridge(float):
        push    {r3, lr}
        vmov    s0, r0
        bl      cosf
        vmov    r0, s0
        pop     {r3, pc}
LmaoImpl<Lmao<&cos>, double (*)(double) noexcept>::bridge(double):
        push    {r3, lr}
        vmov    d0, r0, r1
        bl      cos
        vmov    r0, r1, d0
        pop     {r3, pc}
LmaoImpl<Lmao<&atan2>, double (*)(double, double) noexcept>::bridge(double, double):
        push    {r3, lr}
        vmov    d0, r0, r1
        vmov    d1, r2, r3
        bl      atan2
        vmov    r0, r1, d0
        pop     {r3, pc}
emit_all(char const*):
        push    {r4, r5, r6, lr}
        movw    r4, #:lower16:.LANCHOR0
        movt    r4, #:upper16:.LANCHOR0
        ldr     r5, [r4, #4]
        cbz     r5, .L10
        mov     r6, r0
        ldr     r0, [r4]
        mov     r1, r6
        bl      strcmp
        cbz     r0, .L10
        ldr     r5, [r4, #12]
        cbz     r5, .L10
        ldr     r0, [r4, #8]
        mov     r1, r6
        bl      strcmp
        cbz     r0, .L10
        ldr     r5, [r4, #20]
        cbz     r5, .L10
        ldr     r0, [r4, #16]
        mov     r1, r6
        bl      strcmp
        cbz     r0, .L10
        ldr     r5, [r4, #28]
        cbz     r5, .L10
        ldr     r0, [r4, #24]
        mov     r1, r6
        bl      strcmp
        cbz     r0, .L10
        ldr     r5, [r4, #36]
        cbz     r5, .L10
        ldr     r0, [r4, #32]
        mov     r1, r6
        bl      strcmp
        cbz     r0, .L10
        ldr     r5, [r4, #44]
.L10:
        mov     r0, r5
        pop     {r4, r5, r6, pc}
.LC0:
        .ascii  "atan2\000"
.LC1:
        .ascii  "cos\000"
.LC2:
        .ascii  "cosf\000"
.LC3:
        .ascii  "atoi\000"
.LC4:
        .ascii  "atof\000"
func_list:
        .word   .LC0
        .word   LmaoImpl<Lmao<&atan2>, double (*)(double, double) noexcept>::bridge(double, double)
        .word   .LC1
        .word   LmaoImpl<Lmao<&cos>, double (*)(double) noexcept>::bridge(double)
        .word   .LC2
        .word   LmaoImpl<Lmao<&cosf>, float (*)(float) noexcept>::bridge(float)
        .word   .LC3
        .word   atoi
        .word   .LC4
        .word   LmaoImpl<Lmao<&atof>, double (*)(char const*) noexcept>::bridge(char const*)
        .word   0
        .word   0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment