mirror of
https://github.com/holub/mame
synced 2025-04-22 16:31:49 +03:00
util/delegate.cpp: Fixed multiple issues.
* Fixed this pointer displacement being reapplied after delegates are copied - caused issues with classes with multiple inheritance. * Made null member function pointer test conformant with Itanium C++ ABI specification. * Corrected size of this pointer displacement - fixes issues on big Endian targets. * Fixed function addresses for virtual member functions on targets that use function descriptors (e.g. PPC64). * Applied shift to this pointer displacement for targets that use the ARM variant of the Itanium ABI. * Fixed this pointer displacement not being applied for virtual member functions on targets using ARM variant of the Itanium ABI. * Fixed this pointer displacement being incorrectly applied to vptr on targets using ARM variant of the Itanium ABI. * Made less code conditionally compiled to make it easier to catch errors.
This commit is contained in:
parent
a06f194642
commit
e7afb00aea
@ -1,5 +1,5 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Aaron Giles
|
||||
// copyright-holders:Aaron Giles,Vas Crabb
|
||||
/***************************************************************************
|
||||
|
||||
delegate.cpp
|
||||
@ -23,6 +23,18 @@
|
||||
#define LOG(...) do { if (false) printf(__VA_ARGS__); } while (false)
|
||||
#endif
|
||||
|
||||
// some architectures use function descriptor pointers
|
||||
// usually this is a global pointer value along with the branch target
|
||||
// other platforms using this convention include:
|
||||
// * AIX, Classic MacOS and WinNT on 32-bit POWER/PowerPC
|
||||
// * pretty much anything on Itanium
|
||||
#if (defined(__ppc64__) || (defined(__PPC64__))) && !defined(__APPLE__)
|
||||
#define MAME_DELEGATE_VT_DESCRIPTOR 1
|
||||
#endif
|
||||
#ifndef MAME_DELEGATE_VT_DESCRIPTOR
|
||||
#define MAME_DELEGATE_VT_DESCRIPTOR 0
|
||||
#endif
|
||||
|
||||
|
||||
namespace util::detail {
|
||||
|
||||
@ -30,11 +42,7 @@ namespace util::detail {
|
||||
// GLOBAL VARIABLES
|
||||
//**************************************************************************
|
||||
|
||||
#if (USE_DELEGATE_TYPE == DELEGATE_TYPE_COMPATIBLE)
|
||||
|
||||
const delegate_mfp::raw_mfp_data delegate_mfp::s_null_mfp = { { 0 } };
|
||||
|
||||
#endif
|
||||
const delegate_mfp_compatible::raw_mfp_data delegate_mfp_compatible::s_null_mfp = { { 0 } };
|
||||
|
||||
|
||||
|
||||
@ -42,46 +50,56 @@ const delegate_mfp::raw_mfp_data delegate_mfp::s_null_mfp = { { 0 } };
|
||||
// INTERNAL DELEGATE HELPERS
|
||||
//**************************************************************************
|
||||
|
||||
#if (USE_DELEGATE_TYPE == DELEGATE_TYPE_INTERNAL)
|
||||
|
||||
//-------------------------------------------------
|
||||
// delegate_convert_raw - given an object and an raw function, adjust the object base
|
||||
// and return the actual final code pointer
|
||||
//-------------------------------------------------//
|
||||
|
||||
delegate_generic_function delegate_mfp::convert_to_generic(delegate_generic_class *&object) const
|
||||
delegate_generic_function delegate_mfp_itanium::convert_to_generic(delegate_generic_class *&object) const
|
||||
{
|
||||
#if defined(__arm__) || defined(__ARMEL__) || defined(__aarch64__) || defined(__MIPSEL__) || defined(__mips_isa_rev) || defined(__mips64) || defined(__EMSCRIPTEN__)
|
||||
if ((m_this_delta & 1) == 0)
|
||||
#if MAME_DELEGATE_ITANIUM_ARM
|
||||
// apply the "this" delta to the object first - remember to shift right one bit position
|
||||
object = reinterpret_cast<delegate_generic_class *>(reinterpret_cast<std::uint8_t *>(object) + (m_this_delta >> 1));
|
||||
if (!(m_this_delta & 1))
|
||||
{
|
||||
LOG("Calculated Addr = %p\n", reinterpret_cast<void const *>(uintptr_t(m_function)));
|
||||
object = reinterpret_cast<delegate_generic_class *>(reinterpret_cast<std::uint8_t *>(object) + m_this_delta);
|
||||
// if the low bit of the 'this' delta is clear, the pointer is a conventional function pointer
|
||||
LOG("Calculated Addr = %p\n", reinterpret_cast<void const *>(m_function));
|
||||
return reinterpret_cast<delegate_generic_function>(m_function);
|
||||
}
|
||||
object = reinterpret_cast<delegate_generic_class *>(reinterpret_cast<std::uint8_t *>(object));
|
||||
|
||||
// otherwise, it is the byte index into the vtable where the actual function lives
|
||||
std::uint8_t *vtable_base = *reinterpret_cast<std::uint8_t **>(object);
|
||||
LOG("Calculated Addr = %p (VTAB)\n", reinterpret_cast<void const *>(uintptr_t(*reinterpret_cast<delegate_generic_function *>(vtable_base + m_function + m_this_delta - 1))));
|
||||
return *reinterpret_cast<delegate_generic_function *>(vtable_base + m_function + m_this_delta - 1);
|
||||
#else
|
||||
else
|
||||
{
|
||||
// otherwise, it is the byte index into the vtable where the actual function lives
|
||||
std::uint8_t const *const vtable_ptr = *reinterpret_cast<std::uint8_t const *const *>(object) + m_function;
|
||||
#if MAME_DELEGATE_VT_DESCRIPTOR
|
||||
delegate_generic_function const result = reinterpret_cast<delegate_generic_function>(uintptr_t(vtable_ptr));
|
||||
#else // MAME_DELEGATE_VT_DESCRIPTOR
|
||||
delegate_generic_function const result = *reinterpret_cast<delegate_generic_function const *>(vtable_ptr);
|
||||
#endif // MAME_DELEGATE_VT_DESCRIPTOR
|
||||
LOG("Calculated Addr = %p (VTAB)\n", reinterpret_cast<void const *>(uintptr_t(result)));
|
||||
return result;
|
||||
}
|
||||
#else // MAME_DELEGATE_ITANIUM_ARM
|
||||
// apply the "this" delta to the object first
|
||||
object = reinterpret_cast<delegate_generic_class *>(reinterpret_cast<std::uint8_t *>(object) + m_this_delta);
|
||||
|
||||
// if the low bit of the vtable index is clear, then it is just a raw function pointer
|
||||
if (!(m_function & 1))
|
||||
{
|
||||
LOG("Calculated Addr = %p\n", reinterpret_cast<void const *>(uintptr_t(m_function)));
|
||||
// if the low bit of the pointer is clear, then it is a conventional function pointer
|
||||
LOG("Calculated Addr = %p\n", reinterpret_cast<void const *>(m_function));
|
||||
return reinterpret_cast<delegate_generic_function>(m_function);
|
||||
}
|
||||
|
||||
// otherwise, it is the byte index into the vtable where the actual function lives
|
||||
std::uint8_t *vtable_base = *reinterpret_cast<std::uint8_t **>(object);
|
||||
LOG("Calculated Addr = %p (VTAB)\n", reinterpret_cast<void *>(uintptr_t(*reinterpret_cast<delegate_generic_function *>(vtable_base + m_function - 1))));
|
||||
return *reinterpret_cast<delegate_generic_function *>(vtable_base + m_function - 1);
|
||||
#endif
|
||||
else
|
||||
{
|
||||
// otherwise, it is the byte index into the vtable where the actual function lives
|
||||
std::uint8_t const *const vtable_ptr = *reinterpret_cast<std::uint8_t const *const *>(object) + m_function - 1;
|
||||
#if MAME_DELEGATE_VT_DESCRIPTOR
|
||||
delegate_generic_function const result = reinterpret_cast<delegate_generic_function>(uintptr_t(vtable_ptr));
|
||||
#else // MAME_DELEGATE_VT_DESCRIPTOR
|
||||
delegate_generic_function const result = *reinterpret_cast<delegate_generic_function const *>(vtable_ptr);
|
||||
#endif // MAME_DELEGATE_VT_DESCRIPTOR
|
||||
LOG("Calculated Addr = %p (VTAB)\n", reinterpret_cast<void const *>(uintptr_t(result)));
|
||||
return result;
|
||||
}
|
||||
#endif // MAME_DELEGATE_ITANIUM_ARM
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace util::detail
|
||||
|
@ -1,5 +1,5 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Aaron Giles,Couriersud,Miodrag Milanovic
|
||||
// copyright-holders:Aaron Giles,Couriersud,Miodrag Milanovic,Vas Crabb
|
||||
/***************************************************************************
|
||||
|
||||
delegate.h
|
||||
@ -52,24 +52,45 @@
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
The "internal" version of delegates makes use of the internal
|
||||
The "Itanium" version of delegates makes use of the internal
|
||||
structure of member function pointers in order to convert them at
|
||||
binding time into simple static function pointers. This only works
|
||||
on platforms where object->func(p1, p2) is equivalent in calling
|
||||
convention to func(object, p1, p2).
|
||||
|
||||
Most of the information on how this works comes from Don Clugston
|
||||
in this article:
|
||||
|
||||
http://www.codeproject.com/KB/cpp/FastDelegate.aspx
|
||||
|
||||
Pros:
|
||||
* as fast as a standard function call in static and member cases
|
||||
* no stub functions or double-hops needed
|
||||
|
||||
Cons:
|
||||
* requires internal knowledge of the member function pointer
|
||||
* only works for GCC (for now; MSVC info is also readily available)
|
||||
* only works for two popular variants of the Itanium C++ ABI
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
The "MSVC" version of delegates makes use of the internal structure
|
||||
of member function pointers in order to convert them at binding time
|
||||
into simple static function pointers. This only works on platforms
|
||||
where object->func(p1, p2) is equivalent in calling convention to
|
||||
func(object, p1, p2).
|
||||
|
||||
Pros:
|
||||
* as fast as a standard function call in static and member cases
|
||||
* no stub functions or double-hops needed
|
||||
|
||||
Cons:
|
||||
* requires internal knowledge of the member function pointer
|
||||
* only works works with MSVC ABI, and not on 32-bit x86
|
||||
* does not work for classes with virtual inheritance
|
||||
* structure return does not work with member function pointers
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
Further reading:
|
||||
|
||||
http://www.codeproject.com/KB/cpp/FastDelegate.aspx
|
||||
|
||||
http://itanium-cxx-abi.github.io/cxx-abi/abi.html#member-pointers
|
||||
|
||||
***************************************************************************/
|
||||
#ifndef MAME_UTIL_DELEGATE_H
|
||||
@ -93,37 +114,47 @@
|
||||
//**************************************************************************
|
||||
|
||||
// types of delegates supported
|
||||
#define DELEGATE_TYPE_COMPATIBLE 0
|
||||
#define DELEGATE_TYPE_INTERNAL 1
|
||||
#define DELEGATE_TYPE_MSVC 2
|
||||
#define MAME_DELEGATE_TYPE_COMPATIBLE 0
|
||||
#define MAME_DELEGATE_TYPE_ITANIUM 1
|
||||
#define MAME_DELEGATE_TYPE_MSVC 2
|
||||
|
||||
// select which one we will be using
|
||||
#if defined(FORCE_COMPATIBLE)
|
||||
#define USE_DELEGATE_TYPE DELEGATE_TYPE_COMPATIBLE
|
||||
#if defined(MAME_DELEGATE_FORCE_COMPATIBLE)
|
||||
#define USE_DELEGATE_TYPE MAME_DELEGATE_TYPE_COMPATIBLE
|
||||
#elif defined(__GNUC__)
|
||||
/* 32bit MINGW asks for different convention */
|
||||
// 32bit MINGW asks for different convention
|
||||
#if defined(__MINGW32__) && !defined(__x86_64) && defined(__i386__)
|
||||
#define USE_DELEGATE_TYPE DELEGATE_TYPE_INTERNAL
|
||||
#define MEMBER_ABI __thiscall
|
||||
#define HAS_DIFFERENT_ABI 1
|
||||
#define USE_DELEGATE_TYPE MAME_DELEGATE_TYPE_ITANIUM
|
||||
#define MAME_DELEGATE_MEMBER_ABI __thiscall
|
||||
#define MAME_DELEGATE_DIFFERENT_MEMBER_ABI 1
|
||||
#elif defined(__clang__) && defined(__i386__) && defined(_WIN32)
|
||||
#define USE_DELEGATE_TYPE DELEGATE_TYPE_COMPATIBLE
|
||||
#define USE_DELEGATE_TYPE MAME_DELEGATE_TYPE_COMPATIBLE
|
||||
#else
|
||||
#define USE_DELEGATE_TYPE DELEGATE_TYPE_INTERNAL
|
||||
#define MEMBER_ABI
|
||||
#define HAS_DIFFERENT_ABI 0
|
||||
#define USE_DELEGATE_TYPE MAME_DELEGATE_TYPE_ITANIUM
|
||||
#define MAME_DELEGATE_MEMBER_ABI
|
||||
#define MAME_DELEGATE_DIFFERENT_MEMBER_ABI 0
|
||||
#endif
|
||||
#elif defined(_MSC_VER) && defined(_M_X64)
|
||||
#define MEMBER_ABI
|
||||
#define HAS_DIFFERENT_ABI 0
|
||||
#define USE_DELEGATE_TYPE DELEGATE_TYPE_MSVC
|
||||
#define MAME_DELEGATE_MEMBER_ABI
|
||||
#define MAME_DELEGATE_DIFFERENT_MEMBER_ABI 0
|
||||
#define USE_DELEGATE_TYPE MAME_DELEGATE_TYPE_MSVC
|
||||
#else
|
||||
#define USE_DELEGATE_TYPE DELEGATE_TYPE_COMPATIBLE
|
||||
#define USE_DELEGATE_TYPE MAME_DELEGATE_TYPE_COMPATIBLE
|
||||
#endif
|
||||
|
||||
#if (USE_DELEGATE_TYPE == DELEGATE_TYPE_COMPATIBLE)
|
||||
#define MEMBER_ABI
|
||||
#define HAS_DIFFERENT_ABI 0
|
||||
#if defined(__arm__) || defined(__ARMEL__) || defined(__aarch64__)
|
||||
#define MAME_DELEGATE_ITANIUM_ARM 1
|
||||
#elif defined(__MIPSEL__) || defined(__mips_isa_rev) || defined(__mips64)
|
||||
#define MAME_DELEGATE_ITANIUM_ARM 1
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
#define MAME_DELEGATE_ITANIUM_ARM 1
|
||||
#else
|
||||
#define MAME_DELEGATE_ITANIUM_ARM 0
|
||||
#endif
|
||||
|
||||
#if USE_DELEGATE_TYPE == MAME_DELEGATE_TYPE_COMPATIBLE
|
||||
#define MAME_DELEGATE_MEMBER_ABI
|
||||
#define MAME_DELEGATE_DIFFERENT_MEMBER_ABI 0
|
||||
#endif
|
||||
|
||||
|
||||
@ -164,50 +195,49 @@ struct delegate_traits
|
||||
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
// DELEGATE MEMBER FUNCTION POINTER WRAPPERS
|
||||
//**************************************************************************
|
||||
|
||||
#if (USE_DELEGATE_TYPE == DELEGATE_TYPE_COMPATIBLE)
|
||||
|
||||
// ======================> delegate_mfp
|
||||
|
||||
// delegate_mfp is a class that wraps a generic member function pointer
|
||||
// in a static buffer, and can effectively recast itself back for later use;
|
||||
// it hides some of the gross details involved in copying arbitrary member
|
||||
// function pointers around
|
||||
class delegate_mfp
|
||||
/// \brief Maximally compatible member function pointer wrapper
|
||||
///
|
||||
/// Instantiates a static member function template on construction as
|
||||
/// an adaptor thunk to call the supplied member function with the
|
||||
/// supplied object. Adds one layer of indirection to calls.
|
||||
///
|
||||
/// This implementation requires the representation of a null member
|
||||
/// function pointer to be all zeroes.
|
||||
class delegate_mfp_compatible
|
||||
{
|
||||
public:
|
||||
// default constructor
|
||||
delegate_mfp()
|
||||
delegate_mfp_compatible()
|
||||
: m_rawdata(s_null_mfp)
|
||||
, m_realobject(nullptr)
|
||||
, m_stubfunction(nullptr)
|
||||
{ }
|
||||
|
||||
// copy constructor
|
||||
delegate_mfp(const delegate_mfp &src) = default;
|
||||
delegate_mfp_compatible(const delegate_mfp_compatible &src) = default;
|
||||
|
||||
// construct from any member function pointer
|
||||
template <typename MemberFunctionType, class MemberFunctionClass, typename ReturnType, typename StaticFunctionType>
|
||||
delegate_mfp(MemberFunctionType mfp, MemberFunctionClass *, ReturnType *, StaticFunctionType)
|
||||
delegate_mfp_compatible(MemberFunctionType mfp, MemberFunctionClass *, ReturnType *, StaticFunctionType)
|
||||
: m_rawdata(s_null_mfp)
|
||||
, m_realobject(nullptr)
|
||||
, m_stubfunction(make_generic<StaticFunctionType>(&delegate_mfp::method_stub<MemberFunctionClass, ReturnType>))
|
||||
, m_stubfunction(make_generic<StaticFunctionType>(&delegate_mfp_compatible::method_stub<MemberFunctionClass, ReturnType>))
|
||||
{
|
||||
static_assert(sizeof(mfp) <= sizeof(m_rawdata), "Unsupported member function pointer size");
|
||||
*reinterpret_cast<MemberFunctionType *>(&m_rawdata) = mfp;
|
||||
}
|
||||
|
||||
// comparison helpers
|
||||
bool operator==(const delegate_mfp &rhs) const { return (m_rawdata == rhs.m_rawdata); }
|
||||
bool isnull() const { return (m_rawdata == s_null_mfp); }
|
||||
bool operator==(const delegate_mfp_compatible &rhs) const { return m_rawdata == rhs.m_rawdata; }
|
||||
bool isnull() const { return m_rawdata == s_null_mfp; }
|
||||
|
||||
// getters
|
||||
delegate_generic_class *real_object(delegate_generic_class *original) const { return m_realobject; }
|
||||
delegate_generic_class *real_object(delegate_generic_class *original) const
|
||||
{
|
||||
return m_realobject;
|
||||
}
|
||||
|
||||
// binding helper
|
||||
// binding helpers
|
||||
template <typename FunctionType>
|
||||
void update_after_bind(FunctionType &funcptr, delegate_generic_class *&object)
|
||||
{
|
||||
@ -216,18 +246,24 @@ public:
|
||||
funcptr = reinterpret_cast<FunctionType>(m_stubfunction);
|
||||
}
|
||||
|
||||
template <typename FunctionType>
|
||||
void update_after_copy(FunctionType &funcptr, delegate_generic_class *&object)
|
||||
{
|
||||
assert(reinterpret_cast<FunctionType>(m_stubfunction) == funcptr);
|
||||
object = reinterpret_cast<delegate_generic_class *>(this);
|
||||
}
|
||||
|
||||
private:
|
||||
// helper stubs for calling encased member function pointers
|
||||
template <class FunctionClass, typename ReturnType, typename... Params>
|
||||
static ReturnType method_stub(delegate_generic_class *object, Params ... args)
|
||||
{
|
||||
delegate_mfp *_this = reinterpret_cast<delegate_mfp *>(object);
|
||||
using mfptype = ReturnType(FunctionClass::*)(Params...);
|
||||
mfptype &mfp = *reinterpret_cast<mfptype *>(&_this->m_rawdata);
|
||||
return (reinterpret_cast<FunctionClass *>(_this->m_realobject)->*mfp)(std::forward<Params>(args)...);
|
||||
delegate_mfp_compatible &_this = *reinterpret_cast<delegate_mfp_compatible *>(object);
|
||||
mfptype &mfp = *reinterpret_cast<mfptype *>(&_this.m_rawdata);
|
||||
return (reinterpret_cast<FunctionClass *>(_this.m_realobject)->*mfp)(std::forward<Params>(args)...);
|
||||
}
|
||||
|
||||
|
||||
// helper to convert a function of a given type to a generic function, forcing template
|
||||
// instantiation to match the source type
|
||||
template <typename SourceType>
|
||||
@ -236,7 +272,7 @@ private:
|
||||
return reinterpret_cast<delegate_generic_function>(funcptr);
|
||||
}
|
||||
|
||||
|
||||
// FIXME: not properly aligned for storing pointers
|
||||
struct raw_mfp_data
|
||||
{
|
||||
#if defined(__INTEL_COMPILER) && defined(_M_X64) // needed for "Intel(R) C++ Intel(R) 64 Compiler XE for applications running on Intel(R) 64, Version 14.0.2.176 Build 20140130" at least
|
||||
@ -244,7 +280,7 @@ private:
|
||||
#else // all other cases - for MSVC maximum size is one pointer, plus 3 ints; all other implementations seem to be smaller
|
||||
int data[((sizeof(void *) + 3 * sizeof(int)) + (sizeof(int) - 1)) / sizeof(int)];
|
||||
#endif
|
||||
bool operator==(const raw_mfp_data &rhs) const { return (memcmp(data, rhs.data, sizeof(data)) == 0); }
|
||||
bool operator==(const raw_mfp_data &rhs) const { return !std::memcmp(data, rhs.data, sizeof(data)); }
|
||||
};
|
||||
|
||||
// internal state
|
||||
@ -255,106 +291,195 @@ private:
|
||||
static const raw_mfp_data s_null_mfp; // nullptr mfp
|
||||
};
|
||||
|
||||
#elif (USE_DELEGATE_TYPE == DELEGATE_TYPE_INTERNAL)
|
||||
|
||||
// ======================> delegate_mfp
|
||||
|
||||
// struct describing the contents of a member function pointer
|
||||
class delegate_mfp
|
||||
/// \brief Itanium C++ ABI member function pointer wrapper
|
||||
///
|
||||
/// Supports the two most popular pointer to member function
|
||||
/// implementations described in the Itanium C++ ABI. Both of these
|
||||
/// consist of a pointer followed by a pointer followed by a ptrdiff_t.
|
||||
///
|
||||
/// The first variant is used when member the least significant bit of a
|
||||
/// member function pointer need never be set and vtable entry offsets
|
||||
/// are guaranteed to be even numbers of bytes. If the pointer is even,
|
||||
/// it is a conventional function pointer to the member function. If
|
||||
/// the pointer is odd, it is a byte offset into the vtable plus one.
|
||||
/// The ptrdiff_t is a byte offset to add to the this pointer. A null
|
||||
/// member function pointer is represented by setting the pointer to
|
||||
/// a null pointer.
|
||||
///
|
||||
/// The second variant is used when the least significant bit of a
|
||||
/// pointer to a member function may need to be set or it may not be
|
||||
/// possible to distinguish between a vtable offset and a null pointer.
|
||||
/// (This is the case for ARM where the least significant bit of a
|
||||
/// pointer to a function is set if the function starts in Thumb mode.)
|
||||
/// If the least significant bit of the ptrdiff_t is clear, the pointer
|
||||
/// is a conventional function pointer to the member function. If the
|
||||
/// least significant bit of the ptrdiff_t is set, the pointer is a byte
|
||||
/// offset into the vtable. The ptrdiff_t must be shifted right one bit
|
||||
/// position to make a byte offset to add to the this pointer. A null
|
||||
/// member function pointer is represented by setting the pointer to a
|
||||
/// null pointer and clearing the least significant bit of the
|
||||
/// ptrdiff_t.
|
||||
class delegate_mfp_itanium
|
||||
{
|
||||
public:
|
||||
// default constructor
|
||||
delegate_mfp() = default;
|
||||
delegate_mfp_itanium() = default;
|
||||
|
||||
// copy constructor
|
||||
delegate_mfp(const delegate_mfp &src) = default;
|
||||
delegate_mfp_itanium(const delegate_mfp_itanium &src) = default;
|
||||
|
||||
// construct from any member function pointer
|
||||
template <typename MemberFunctionType, class MemberFunctionClass, typename ReturnType, typename StaticFunctionType>
|
||||
delegate_mfp(MemberFunctionType mfp, MemberFunctionClass *, ReturnType *, StaticFunctionType)
|
||||
delegate_mfp_itanium(MemberFunctionType mfp, MemberFunctionClass *, ReturnType *, StaticFunctionType)
|
||||
{
|
||||
static_assert(sizeof(mfp) == sizeof(*this), "Unsupported member function pointer size");
|
||||
*reinterpret_cast<MemberFunctionType *>(this) = mfp;
|
||||
}
|
||||
|
||||
// comparison helpers
|
||||
bool operator==(const delegate_mfp &rhs) const { return (m_function == rhs.m_function) && (m_this_delta == rhs.m_this_delta); }
|
||||
bool isnull() const { return !m_function && !m_this_delta; }
|
||||
bool operator==(const delegate_mfp_itanium &rhs) const
|
||||
{
|
||||
return (m_function == rhs.m_function) && (m_this_delta == rhs.m_this_delta);
|
||||
}
|
||||
bool isnull() const
|
||||
{
|
||||
#if MAME_DELEGATE_ITANIUM_ARM
|
||||
return !reinterpret_cast<void (*)()>(m_function) && !(m_this_delta & 1);
|
||||
#else
|
||||
return !reinterpret_cast<void (*)()>(m_function);
|
||||
#endif
|
||||
}
|
||||
|
||||
// getters
|
||||
static delegate_generic_class *real_object(delegate_generic_class *original) { return original; }
|
||||
static delegate_generic_class *real_object(delegate_generic_class *original)
|
||||
{
|
||||
return original;
|
||||
}
|
||||
|
||||
// binding helper
|
||||
// binding helpers
|
||||
template <typename FunctionType>
|
||||
void update_after_bind(FunctionType &funcptr, delegate_generic_class *&object)
|
||||
{
|
||||
funcptr = reinterpret_cast<FunctionType>(convert_to_generic(object));
|
||||
}
|
||||
|
||||
template <typename FunctionType>
|
||||
void update_after_copy(FunctionType &funcptr, delegate_generic_class *&object)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
// extract the generic function and adjust the object pointer
|
||||
delegate_generic_function convert_to_generic(delegate_generic_class *&object) const;
|
||||
|
||||
// actual state
|
||||
uintptr_t m_function = 0; // first item can be one of two things:
|
||||
// if even, it's a pointer to the function
|
||||
// if odd, it's the byte offset into the vtable
|
||||
int m_this_delta = 0; // delta to apply to the 'this' pointer
|
||||
uintptr_t m_function = reinterpret_cast<uintptr_t>(static_cast<void (*)()>(nullptr)); // function pointer or vtable offset
|
||||
ptrdiff_t m_this_delta = 0; // delta to apply to the 'this' pointer
|
||||
};
|
||||
|
||||
#elif (USE_DELEGATE_TYPE == DELEGATE_TYPE_MSVC)
|
||||
|
||||
// ======================> delegate_mfp
|
||||
|
||||
// struct describing the contents of a member function pointer
|
||||
class delegate_mfp
|
||||
/// \brief MSVC member function pointer wrapper
|
||||
///
|
||||
/// MSVC uses space optimisation. A member function pointer is a
|
||||
/// conventional function pointer followed by zero to three int values,
|
||||
/// depending on whether the class has single, multiple, virtual or
|
||||
/// unknown inheritance of base classes. The function pointer is always
|
||||
/// a conventional function pointer (a thunk is used to call virtual
|
||||
/// member functions through the vtable).
|
||||
///
|
||||
/// If present, the first int value is a byte offset to add to the this
|
||||
/// pointer before calling the function.
|
||||
///
|
||||
/// For the virtual inheritance case, the offset to the vtable pointer
|
||||
/// from the location the this pointer points to must be known by the
|
||||
/// compiler when the member function pointer is called. The second int
|
||||
/// value is a byte offset into the vtable to an int value containing an
|
||||
/// additional byte offset to add to the this pointer.
|
||||
///
|
||||
/// For the unknown inheritance case, the second int value is a byte
|
||||
/// offset add to the this pointer to obtain a pointer to the vtable
|
||||
/// pointer, or undefined if not required. If the third int value is
|
||||
/// not zero, it is a byte offset into the vtable to an int value
|
||||
/// containing an additional byte offset to add to the this pointer.
|
||||
///
|
||||
/// It is not possible to support the virtual inheritance case without
|
||||
/// some way of obtaining the offset to the vtable pointer.
|
||||
class delegate_mfp_msvc
|
||||
{
|
||||
struct single_base_equiv { delegate_generic_function p; };
|
||||
struct multi_base_equiv { delegate_generic_function p; int o; };
|
||||
struct single_base_equiv { delegate_generic_function fptr; };
|
||||
struct multi_base_equiv { delegate_generic_function fptr; int thisdisp; };
|
||||
struct unknown_base_equiv { delegate_generic_function fptr; int thisdisp, vptrdisp, vtdisp; };
|
||||
|
||||
public:
|
||||
// default constructor
|
||||
delegate_mfp() = default;
|
||||
delegate_mfp_msvc() = default;
|
||||
|
||||
// copy constructor
|
||||
delegate_mfp(const delegate_mfp &src) = default;
|
||||
delegate_mfp_msvc(const delegate_mfp_msvc &src) = default;
|
||||
|
||||
// construct from any member function pointer
|
||||
template <typename MemberFunctionType, class MemberFunctionClass, typename ReturnType, typename StaticFunctionType>
|
||||
delegate_mfp(MemberFunctionType mfp, MemberFunctionClass *, ReturnType *, StaticFunctionType)
|
||||
delegate_mfp_msvc(MemberFunctionType mfp, MemberFunctionClass *, ReturnType *, StaticFunctionType)
|
||||
{
|
||||
static_assert((sizeof(mfp) == sizeof(single_base_equiv)) || (sizeof(mfp) == sizeof(multi_base_equiv)), "Unsupported member function pointer size");
|
||||
m_size = sizeof(mfp);
|
||||
// FIXME: this doesn't actually catch the unsupported virtual inheritance case on 64-bit targets
|
||||
// alignment of the pointer means sizeof gives the same value for multiple inheritance and virtual inheritance cases
|
||||
static_assert(
|
||||
(sizeof(mfp) == sizeof(single_base_equiv)) || (sizeof(mfp) == sizeof(multi_base_equiv)) || (sizeof(mfp) == sizeof(unknown_base_equiv)),
|
||||
"Unsupported member function pointer size");
|
||||
static_assert(sizeof(mfp) <= sizeof(*this), "Member function pointer is too large to support");
|
||||
*reinterpret_cast<MemberFunctionType *>(this) = mfp;
|
||||
m_size = sizeof(mfp);
|
||||
}
|
||||
|
||||
// comparison helpers
|
||||
bool operator==(const delegate_mfp &rhs) const { return m_function == rhs.m_function; }
|
||||
bool isnull() const { return !m_function; }
|
||||
bool operator==(const delegate_mfp_msvc &rhs) const { return m_function == rhs.m_function; }
|
||||
bool isnull() const { return !reinterpret_cast<void (*)()>(m_function); }
|
||||
|
||||
// getters
|
||||
static delegate_generic_class *real_object(delegate_generic_class *original) { return original; }
|
||||
|
||||
// binding helper
|
||||
// binding helpers
|
||||
template <typename FunctionType>
|
||||
void update_after_bind(FunctionType &funcptr, delegate_generic_class *&object)
|
||||
{
|
||||
funcptr = reinterpret_cast<FunctionType>(m_function);
|
||||
if (m_size > sizeof(single_base_equiv))
|
||||
object = reinterpret_cast<delegate_generic_class *>(reinterpret_cast<std::uint8_t *>(object) + m_this_delta);
|
||||
std::uint8_t *byteptr = reinterpret_cast<std::uint8_t *>(object);
|
||||
if ((sizeof(unknown_base_equiv) == m_size) && m_vt_index)
|
||||
{
|
||||
std::uint8_t const *const vptr = *reinterpret_cast<std::uint8_t const *const *>(byteptr + m_vptr_offs);
|
||||
byteptr += *reinterpret_cast<int const *>(vptr + m_vt_index);
|
||||
}
|
||||
if (sizeof(single_base_equiv) < m_size)
|
||||
byteptr += m_this_delta;
|
||||
object = reinterpret_cast<delegate_generic_class *>(byteptr);
|
||||
}
|
||||
|
||||
template <typename FunctionType>
|
||||
void update_after_copy(FunctionType &funcptr, delegate_generic_class *&object)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
// actual state
|
||||
uintptr_t m_function = 0; // pointer to function or non-virtual thunk for vtable index
|
||||
uintptr_t m_function = 0; // pointer to function or non-virtual thunk for virtual function call
|
||||
int m_this_delta = 0; // delta to apply to the 'this' pointer for multiple inheritance
|
||||
|
||||
int m_dummy1 = 0;
|
||||
int m_dummy2 = 0;
|
||||
int m_vptr_offs = 0; // offset to apply to this pointer to obtain pointer to vptr
|
||||
int m_vt_index = 0; // offset into vtable to additional delta to apply to the 'this' pointer
|
||||
|
||||
unsigned m_size = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#if USE_DELEGATE_TYPE == MAME_DELEGATE_TYPE_COMPATIBLE
|
||||
using delegate_mfp = delegate_mfp_compatible;
|
||||
#elif USE_DELEGATE_TYPE == MAME_DELEGATE_TYPE_ITANIUM
|
||||
using delegate_mfp = delegate_mfp_itanium;
|
||||
#elif USE_DELEGATE_TYPE == MAME_DELEGATE_TYPE_MSVC
|
||||
using delegate_mfp = delegate_mfp_msvc;
|
||||
#endif
|
||||
|
||||
} // namespace util::detail
|
||||
@ -400,7 +525,7 @@ public:
|
||||
// define our traits
|
||||
template <class FunctionClass> using traits = util::detail::delegate_traits<FunctionClass, ReturnType, Params...>;
|
||||
using generic_static_func = typename traits<util::detail::delegate_generic_class>::static_func_type;
|
||||
typedef MEMBER_ABI generic_static_func generic_member_func;
|
||||
typedef MAME_DELEGATE_MEMBER_ABI generic_static_func generic_member_func;
|
||||
|
||||
// generic constructor
|
||||
delegate_base() = default;
|
||||
@ -408,11 +533,13 @@ public:
|
||||
// copy constructor
|
||||
delegate_base(const delegate_base &src)
|
||||
: m_function(src.m_function)
|
||||
, m_object(src.m_object)
|
||||
, m_latebinder(src.m_latebinder)
|
||||
, m_raw_function(src.m_raw_function)
|
||||
, m_raw_mfp(src.m_raw_mfp)
|
||||
{
|
||||
bind(src.object());
|
||||
if (src.object() && is_mfp())
|
||||
m_raw_mfp.update_after_copy(m_function, m_object);
|
||||
}
|
||||
|
||||
// copy constructor with late bind
|
||||
@ -431,7 +558,7 @@ public:
|
||||
: m_latebinder(&late_bind_helper<FunctionClass>)
|
||||
, m_raw_mfp(funcptr, object, static_cast<ReturnType *>(nullptr), static_cast<generic_static_func>(nullptr))
|
||||
{
|
||||
bind(reinterpret_cast<util::detail::delegate_generic_class *>(object));
|
||||
bind(object);
|
||||
}
|
||||
|
||||
template <class FunctionClass>
|
||||
@ -439,7 +566,7 @@ public:
|
||||
: m_latebinder(&late_bind_helper<FunctionClass>)
|
||||
, m_raw_mfp(funcptr, object, static_cast<ReturnType *>(nullptr), static_cast<generic_static_func>(nullptr))
|
||||
{
|
||||
bind(reinterpret_cast<util::detail::delegate_generic_class *>(object));
|
||||
bind(object);
|
||||
}
|
||||
|
||||
// construct from static reference function with object reference
|
||||
@ -449,7 +576,7 @@ public:
|
||||
, m_latebinder(&late_bind_helper<FunctionClass>)
|
||||
, m_raw_function(reinterpret_cast<generic_static_func>(funcptr))
|
||||
{
|
||||
bind(reinterpret_cast<util::detail::delegate_generic_class *>(object));
|
||||
bind(object);
|
||||
}
|
||||
|
||||
// copy operator
|
||||
@ -458,12 +585,13 @@ public:
|
||||
if (this != &src)
|
||||
{
|
||||
m_function = src.m_function;
|
||||
m_object = nullptr;
|
||||
m_object = src.m_object;
|
||||
m_latebinder = src.m_latebinder;
|
||||
m_raw_function = src.m_raw_function;
|
||||
m_raw_mfp = src.m_raw_mfp;
|
||||
|
||||
bind(src.object());
|
||||
if (src.object() && is_mfp())
|
||||
m_raw_mfp.update_after_copy(m_function, m_object);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
@ -478,7 +606,7 @@ public:
|
||||
// call the function
|
||||
ReturnType operator()(Params... args) const
|
||||
{
|
||||
if (is_mfp() && (HAS_DIFFERENT_ABI))
|
||||
if ((MAME_DELEGATE_DIFFERENT_MEMBER_ABI) && is_mfp())
|
||||
return (*reinterpret_cast<generic_member_func>(m_function))(m_object, std::forward<Params>(args)...);
|
||||
else
|
||||
return (*m_function)(m_object, std::forward<Params>(args)...);
|
||||
@ -517,9 +645,10 @@ protected:
|
||||
}
|
||||
|
||||
// bind the actual object
|
||||
void bind(util::detail::delegate_generic_class *object)
|
||||
template <typename FunctionClass>
|
||||
void bind(FunctionClass *object)
|
||||
{
|
||||
m_object = object;
|
||||
m_object = reinterpret_cast<util::detail::delegate_generic_class *>(object);
|
||||
|
||||
// if we're wrapping a member function pointer, handle special stuff
|
||||
if (m_object && is_mfp())
|
||||
@ -615,7 +744,7 @@ protected:
|
||||
template <class FunctionClass> using const_member_func_type = typename traits<FunctionClass>::const_member_func_type;
|
||||
template <class FunctionClass> using static_ref_func_type = typename traits<FunctionClass>::static_ref_func_type;
|
||||
|
||||
template <typename T> using suitable_functoid = std::bool_constant<std::is_convertible_v<std::invoke_result_t<T, Params...>, ReturnType> || std::is_same_v<std::void_t<std::invoke_result_t<T, Params...> >, ReturnType> >;
|
||||
template <typename T> using suitable_functoid = std::is_invocable_r<ReturnType, T, Params...>;
|
||||
|
||||
public:
|
||||
delegate() : basetype() { }
|
||||
|
Loading…
Reference in New Issue
Block a user