FEX/FEXCore/Source/Utils/MemberFunctionToPointer.h
Ryan Houdek f9902142f7 Utils: Add ability to get VTable entries to PMF helper
This will be useful to remove an indirection.
2023-12-21 01:55:02 -08:00

80 lines
3.0 KiB
C++

// SPDX-License-Identifier: MIT
#pragma once
#include <FEXCore/Utils/LogManager.h>
#include <cstdint>
namespace FEXCore::Utils {
/**
* @brief Casts a class's member function pointer to a raw pointer that we can JIT
*
* Has additional validation to ensure we aren't casting a class member that is invalid
*/
template <typename PointerToMemberType>
class MemberFunctionToPointerCast final {
public:
MemberFunctionToPointerCast(PointerToMemberType Function) {
memcpy(&PMF, &Function, sizeof(PMF));
}
uintptr_t GetConvertedPointer() const {
#ifdef _M_X86_64
// Itanium C++ ABI (https://itanium-cxx-abi.github.io/cxx-abi/abi.html#member-function-pointers)
// Low bit of ptr specifies if this Member function pointer is virtual or not
// Throw an assert if we were trying to cast a virtual member
LOGMAN_THROW_AA_FMT((PMF.ptr & 1) == 0, "C++ Pointer-To-Member representation didn't have low bit set to 0. Are you trying to cast a virtual member?");
#elif defined(_M_ARM_64)
// C++ ABI for the Arm 64-bit Architecture (IHI 0059E)
// 4.2.1 Representation of pointer to member function
// Differs from Itanium specification
LOGMAN_THROW_AA_FMT(PMF.adj == 0, "C++ Pointer-To-Member representation didn't have adj == 0. Are you trying to cast a virtual member?");
#else
#error Don't know how to cast Member to function here. Likely just Itanium
#endif
return PMF.ptr;
}
// Gets the vtable entry position of a virtual member function.
size_t GetVTableOffset() const {
#ifdef _M_X86_64
// Itanium C++ ABI (https://itanium-cxx-abi.github.io/cxx-abi/abi.html#member-function-pointers)
// Low bit of ptr specifies if this Member function pointer is virtual or not
// Throw an assert if we are not loading a virtual member.
LOGMAN_THROW_AA_FMT((PMF.ptr & 1) == 1, "C++ Pointer-To-Member representation didn't have low bit set to 1. This cast only works for virtual members.");
return PMF.ptr & ~1ULL;
#elif defined(_M_ARM_64)
// C++ ABI for the Arm 64-bit Architecture (IHI 0059E)
// 4.2.1 Representation of pointer to member function
// Differs from Itanium specification
LOGMAN_THROW_AA_FMT((PMF.adj & 1) == 1, "C++ Pointer-To-Member representation didn't have adj == 1. This cast only works for virtual members.");
return PMF.ptr;
#else
#error Don't know how to cast Member to function here. Likely just Itanium
#endif
}
// Gets the pointer to the vtable entry for the object passed it.
template<typename Class>
uintptr_t GetVTableEntry(Class *VirtualClass) const {
// VTable is always stored at the beginning of a class object.
uintptr_t *VTable = *reinterpret_cast<uintptr_t**>(VirtualClass);
size_t Offset = GetVTableOffset() / sizeof(void*);
return VTable[Offset];
}
private:
struct PointerToMember {
uintptr_t ptr;
uintptr_t adj;
};
PointerToMember PMF;
// Ensure the representation of PointerToMember matches
static_assert(sizeof(PMF) == sizeof(PointerToMemberType));
};
}