diff options
| author | 2021-09-20 03:03:26 +1000 | |
|---|---|---|
| committer | 2021-09-20 03:03:26 +1000 | |
| commit | 401b22bbf50dbddc45fa99fb92249fd0c86c1bed (patch) | |
| tree | 76cce80eccf1f99272551fb234d7783d36a6962e /src/lib/util/delegate.cpp | |
| parent | 566a741f4fda9be53504eb9bc965bd9f1649ae2d (diff) | |
util/delegate.h: Try to catch issues earlier, and some cleanup.
Optimised generation of late bind helper functions. The late bind
helper function doesn't depend on the delegate signature - only on the
late bind base class and function target class. Having it inside the
delegate base class means it needs to be instantiated for every
combination of late bind base class, function target class and delegate
signature. In a typical driver file, there is only one late bind base
class (delegate_late_bind), and there will be delegates with multiple
signatures bound to function members of the same class (e.g. read and
write handlers, possibly of different widths, bound to members of the
driver state class). By moving the late bind helper out of the delegate
base class, the number of required instantiations can be reduced. By
moving the body out of the enclosing class declaration, the compiler can
be encouraged to coalese instantiations across translation units. While
this won't give a further reduction in compile time, it should at least
reduce the output binary size by reducing duplication for devices that
frequently have handlers installed in memory maps.
Added an additional template parameter to delegates allowing the late
bind base class to be changed if desired.
Moved the MSVC implementation "this" pointer optimisation out-of-line
and added logging. Also cleaned up the Itanium "this" pointer
adjustment and code pointer resolution implementation to reduce
duplication and conditional compilation.
Made binding_type_exception give a more meaningful what() message than
"std::exception".
Added extensive validity tests for delegate functionality. Pointers to
member functions are tested, including multiple inheritance, virtual and
non-virtual member functions, and checking for generational loss across
copying/assigning delegates. This should properly exercise "this"
pointer adjustment for the Itanium and MSVC implementations, and vtable
lookup for the Itanium implementation. So-called late binding
functionality is tested, including exceptions on failure. Functoids are
tested, although given the encapsulation it's not possible to check that
an apator isn't generated when it shouldn't be.
Diffstat (limited to 'src/lib/util/delegate.cpp')
| -rw-r--r-- | src/lib/util/delegate.cpp | 119 |
1 files changed, 76 insertions, 43 deletions
diff --git a/src/lib/util/delegate.cpp b/src/lib/util/delegate.cpp index 4b4812d3c56..ffe3b060784 100644 --- a/src/lib/util/delegate.cpp +++ b/src/lib/util/delegate.cpp @@ -11,31 +11,54 @@ #include "delegate.h" #include <cstdio> +#include <sstream> //************************************************************************** // MACROS //************************************************************************** -#if defined(LOG_DELEGATES) -#define LOG(...) printf(__VA_ARGS__) +#if defined(MAME_DELEGATE_LOG_ADJ) + #define LOG(...) printf(__VA_ARGS__) #else -#define LOG(...) do { if (false) printf(__VA_ARGS__); } while (false) + #define LOG(...) do { if (false) printf(__VA_ARGS__); } while (false) #endif -// some architectures use function descriptor pointers +// on some architectures, function pointers point to descriptors // 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 + #define MAME_DELEGATE_VT_DESCRIPTOR 1 #endif #ifndef MAME_DELEGATE_VT_DESCRIPTOR -#define MAME_DELEGATE_VT_DESCRIPTOR 0 + #define MAME_DELEGATE_VT_DESCRIPTOR 0 #endif + +//************************************************************************** +// LATE BINDING EXCEPTION +//************************************************************************** + +binding_type_exception::binding_type_exception(std::type_info const &target_type, std::type_info const &actual_type) + : m_target_type(&target_type) + , m_actual_type(&actual_type) +{ + std::ostringstream os; + os << "Error performing late bind of function expecting type " << target_type.name() << " to instance of type " << actual_type.name(); + m_what = os.str(); +} + + +char const *binding_type_exception::what() const noexcept +{ + return m_what.c_str(); +} + + + namespace util::detail { //************************************************************************** @@ -51,55 +74,65 @@ const delegate_mfp_compatible::raw_mfp_data delegate_mfp_compatible::s_null_mfp //************************************************************************** //------------------------------------------------- -// delegate_convert_raw - given an object and an raw function, adjust the object base -// and return the actual final code pointer -//-------------------------------------------------// +// delegate_mfp_itanium::convert_to_generic - +// given an object pointer and member function +// pointer, apply the displacement and get the +// actual function pointer +//------------------------------------------------- delegate_generic_function delegate_mfp_itanium::convert_to_generic(delegate_generic_class *&object) const { -#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)) + // apply the "this" delta to the object first - the value is shifted to the left one bit position for the ARM-like variant + LOG("Input this=%p ptr=%p adj=%ld ", reinterpret_cast<void const *>(object), reinterpret_cast<void const *>(m_function), long(m_this_delta)); + object = reinterpret_cast<delegate_generic_class *>( + reinterpret_cast<std::uint8_t *>(object) + (m_this_delta >> (MAME_DELEGATE_ITANIUM_ARM ? 1 : 0))); + LOG("Calculated this=%p ", reinterpret_cast<void const *>(object)); + + // test the virtual member function flag - it's the low bit of either the ptr or adj field, depending on the variant + if (MAME_DELEGATE_ITANIUM_ARM ? !(m_this_delta & 1) : !(m_function & 1)) { - // 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)); + // conventional function pointer + LOG("ptr=%p\n", reinterpret_cast<void const *>(m_function)); return reinterpret_cast<delegate_generic_function>(m_function); } 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))); + // byte index into the vtable to the function + std::uint8_t const *const vtable_ptr = *reinterpret_cast<std::uint8_t const *const *>(object) + m_function - (MAME_DELEGATE_ITANIUM_ARM ? 0 : 1); + delegate_generic_function result; + if (MAME_DELEGATE_VT_DESCRIPTOR) + result = reinterpret_cast<delegate_generic_function>(uintptr_t(vtable_ptr)); + else + result = *reinterpret_cast<delegate_generic_function const *>(vtable_ptr); + LOG("ptr=%p (vtable)\n", reinterpret_cast<void const *>(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 (!(m_function & 1)) - { - // 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); - } - else +} + + +//------------------------------------------------- +// delegate_mfp_msvc::adjust_this_pointer - given +// an object pointer and member function pointer, +// apply the displacement +//------------------------------------------------- + +void delegate_mfp_msvc::adjust_this_pointer(delegate_generic_class *&object) const +{ + LOG("Input this=%p ", reinterpret_cast<void const *>(object)); + if (sizeof(single_base_equiv) < m_size) + LOG("thisdelta=%d ", m_this_delta); + if (sizeof(unknown_base_equiv) == m_size) + LOG("vptrdelta=%d vindex=%d ", m_vptr_offs, m_vt_index); + std::uint8_t *byteptr = reinterpret_cast<std::uint8_t *>(object); + if ((sizeof(unknown_base_equiv) == m_size) && m_vt_index) { - // 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; + 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); } -#endif // MAME_DELEGATE_ITANIUM_ARM + if (sizeof(single_base_equiv) < m_size) + byteptr += m_this_delta; + LOG("Calculated this=%p\n", reinterpret_cast<void const *>(byteptr)); + object = reinterpret_cast<delegate_generic_class *>(byteptr); } } // namespace util::detail |
