summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib
diff options
context:
space:
mode:
author Vas Crabb <vas@vastheman.com>2021-09-21 01:02:11 +1000
committer Vas Crabb <vas@vastheman.com>2021-09-21 01:02:11 +1000
commitda1af2b0d6816a5da1544176b882407bd0dad2ee (patch)
tree9a7db16882a0fe937a1b2cbdcb6e78711f33dfa3 /src/lib
parent362d859dc15384f98b97d37cb5e21c7798df3cab (diff)
-delegates: Fixed structure return with MSVC C++ ABI.
* Automatically use delegate_mfp_compatible to generate an adaptor for member functions that return non-scalar, non-reference types (partially addresses #8597). * Enabled the MSVC delegate implemenation for MSVC on AArch64. * Switched back to neater delegate types for layout item bounds and colour. -docs: Updated the example layout links to point to 0.235 - this means there's now an example of embedded SVG.
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/util/delegate.h127
1 files changed, 101 insertions, 26 deletions
diff --git a/src/lib/util/delegate.h b/src/lib/util/delegate.h
index a92d943bbd4..59e83f82617 100644
--- a/src/lib/util/delegate.h
+++ b/src/lib/util/delegate.h
@@ -116,6 +116,7 @@
#include <any>
#include <cassert>
+#include <cstddef>
#include <cstdint>
#include <cstring>
#include <functional>
@@ -123,7 +124,7 @@
#include <type_traits>
#include <typeinfo>
#include <utility>
-#include <cstddef>
+
//**************************************************************************
// MACROS
@@ -139,7 +140,7 @@
#define MAME_DELEGATE_USE_TYPE MAME_DELEGATE_TYPE_COMPATIBLE
#elif defined(__GNUC__)
// 32bit MINGW asks for different convention
- #if defined(__MINGW32__) && !defined(__x86_64) && defined(__i386__)
+ #if defined(__MINGW32__) && !defined(__x86_64__) && defined(__i386__)
#define MAME_DELEGATE_USE_TYPE MAME_DELEGATE_TYPE_ITANIUM
#define MAME_DELEGATE_MEMBER_ABI __thiscall
#define MAME_DELEGATE_DIFFERENT_MEMBER_ABI 1
@@ -150,7 +151,7 @@
#define MAME_DELEGATE_MEMBER_ABI
#define MAME_DELEGATE_DIFFERENT_MEMBER_ABI 0
#endif
-#elif defined(_MSC_VER) && defined(_M_X64)
+#elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64))
#define MAME_DELEGATE_MEMBER_ABI
#define MAME_DELEGATE_DIFFERENT_MEMBER_ABI 0
#define MAME_DELEGATE_USE_TYPE MAME_DELEGATE_TYPE_MSVC
@@ -287,30 +288,15 @@ public:
// binding helpers
template <typename FunctionType>
- void update_after_bind(FunctionType &funcptr, delegate_generic_class *&object)
- {
- m_realobject = object;
- object = reinterpret_cast<delegate_generic_class *>(this);
- funcptr = reinterpret_cast<FunctionType>(m_stubfunction);
- }
+ void update_after_bind(FunctionType &funcptr, delegate_generic_class *&object);
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);
- }
+ void update_after_copy(FunctionType &funcptr, delegate_generic_class *&object);
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)
- {
- using mfptype = ReturnType(FunctionClass::*)(Params...);
- 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)...);
- }
+ static ReturnType method_stub(delegate_generic_class *object, Params ... args);
// helper to convert a function of a given type to a generic function, forcing template
// instantiation to match the source type
@@ -340,6 +326,33 @@ private:
};
+template <typename FunctionType>
+void delegate_mfp_compatible::update_after_bind(FunctionType &funcptr, delegate_generic_class *&object)
+{
+ m_realobject = object;
+ object = reinterpret_cast<delegate_generic_class *>(this);
+ funcptr = reinterpret_cast<FunctionType>(m_stubfunction);
+}
+
+
+template <typename FunctionType>
+void delegate_mfp_compatible::update_after_copy(FunctionType &funcptr, delegate_generic_class *&object)
+{
+ assert(reinterpret_cast<FunctionType>(m_stubfunction) == funcptr);
+ object = reinterpret_cast<delegate_generic_class *>(this);
+}
+
+
+template <class FunctionClass, typename ReturnType, typename... Params>
+ReturnType delegate_mfp_compatible::method_stub(delegate_generic_class *object, Params ... args)
+{
+ using mfptype = ReturnType(FunctionClass::*)(Params...);
+ 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)...);
+}
+
+
/// \brief Itanium C++ ABI member function pointer wrapper
///
@@ -512,19 +525,81 @@ private:
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;
+ unsigned m_size = 0; // overall size of the pointer to member function representation
};
#if MAME_DELEGATE_USE_TYPE == MAME_DELEGATE_TYPE_COMPATIBLE
-using delegate_mfp = delegate_mfp_compatible;
+
+template <typename ReturnType>
+struct delegate_mfp { using type = delegate_mfp_compatible; };
+
#elif MAME_DELEGATE_USE_TYPE == MAME_DELEGATE_TYPE_ITANIUM
-using delegate_mfp = delegate_mfp_itanium;
+
+template <typename ReturnType>
+struct delegate_mfp { using type = delegate_mfp_itanium; };
+
#elif MAME_DELEGATE_USE_TYPE == MAME_DELEGATE_TYPE_MSVC
-using delegate_mfp = delegate_mfp_msvc;
+
+/// \brief Determine whether a type is returned conventionally
+///
+/// Under the MSVC C++ ABI with the Microsoft calling convention for
+/// x86-64 or AArch64, the calling convention for member functions is
+/// not quite the same as a free function with the "this" pointer as the
+/// first parameter.
+///
+/// Conventionally, structure and union values can be returned in
+/// registers if they are small enough and are aggregates (trivially
+/// constructible, destructible, copyable and assignable). On x86-64,
+/// if the value cannot be returned in registers, the pointer to the
+/// area for the return value is conventionally passed in RCX and
+/// explicit parameters are shifted by one position. On AArch64, if the
+/// value cannot be returned in registers, the pointer to the area for
+/// the return value is passed in X8 (explicit parameters do not need to
+/// be shifted).
+///
+/// For member functions, structure and union types are never returned
+/// in registers, and the pointer to the area for the return value is
+/// passed differently for structures and unions. When a structure or
+/// union is to be returned, a pointer to the area for the return value
+/// is effectively passed as a second implicit parameter. On x86-64,
+/// the "this" pointer is passed in RCX and the pointer to the area for
+/// the return value is passed in RDX; on AArch64, the "this" pointer is
+/// passed in X0 and the pointer to the area for the return value is
+/// passed in X1. Explicit parameters are shifted an additional
+/// position to allow for the second implicit parameter.
+///
+/// Note that pointer types are returned conventionally from member
+/// functions even when they're too large to return in registers (e.g. a
+/// pointer to a function member of a class with unknown inheritance).
+///
+/// Because of this, we may need to use the #delegate_mfp_compatible
+/// class to generate adaptor thunks depending on the return type. This
+/// trait doesn't need to reliably be true for types that are returned
+/// conventionally from member functions; it only needs to reliably be
+/// false for types that aren't. Incorrectly yielding true will result
+/// in incorrect behaviour while incorrectly yielding false will just
+/// cause increased overhead (both compile-time and run-time).
+template <typename ReturnType>
+using delegate_mfp_conventional_return = std::bool_constant<
+ std::is_void_v<ReturnType> ||
+ std::is_scalar_v<ReturnType> ||
+ std::is_reference_v<ReturnType> >;
+
+template <typename ReturnType, typename Enable = void>
+struct delegate_mfp;
+
+template <typename ReturnType>
+struct delegate_mfp<ReturnType, std::enable_if_t<delegate_mfp_conventional_return<ReturnType>::value> > { using type = delegate_mfp_msvc; };
+
+template <typename ReturnType>
+struct delegate_mfp<ReturnType, std::enable_if_t<!delegate_mfp_conventional_return<ReturnType>::value> > { using type = delegate_mfp_compatible; };
+
#endif
+template <typename ReturnType> using delegate_mfp_t = typename delegate_mfp<ReturnType>::type;
+
/// \brief Helper class for generating late bind functions
@@ -705,7 +780,7 @@ protected:
delegate_generic_class * m_object = nullptr; // resolved object to the post-cast object
delegate_late_bind_helper<LateBindBase> m_latebinder; // late binding helper
generic_static_func m_raw_function = nullptr; // raw static function pointer
- delegate_mfp m_raw_mfp; // raw member function pointer
+ delegate_mfp_t<ReturnType> m_raw_mfp; // raw member function pointer
};
} // namespace util::detail