summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib
diff options
context:
space:
mode:
author Vas Crabb <vas@vastheman.com>2021-09-22 05:05:19 +1000
committer Vas Crabb <vas@vastheman.com>2021-09-22 05:05:19 +1000
commit86c005377d62adf7996f3d6def3046e3fde1da27 (patch)
tree8f4ac56e77d3d2cc250fb8be4e3627404aafefcb /src/lib
parenta0289bfd4c041a780440e1d1e0055b531069dd50 (diff)
cpu/drcbex64.cpp: Proof-of-concept for optimisation of calling out.
Resolve address space virtual member function addresses on constrcution and call them directly. Provides a small but measurable improvement to performance in drivers that use the recompiler and access the memory system a lot. Also made MSVC delegates capable of walking past all the thunks MSVC puts in the way of actually calling a member function. I'm not accounting for the "this" pointer being passed in RDX when the return value is an oversize scalar. This is harmless because it won't see anything that looks like a virtual call thunk using RCX when RCX points to uninitialised space for the return value. It just means virtual member function calls won't be bypassed if the return value is an oversize scalar, but that doesn't happen frequently anyway.
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/util/delegate.cpp42
-rw-r--r--src/lib/util/delegate.h7
2 files changed, 43 insertions, 6 deletions
diff --git a/src/lib/util/delegate.cpp b/src/lib/util/delegate.cpp
index 33d5f8d54e8..346ba1283a0 100644
--- a/src/lib/util/delegate.cpp
+++ b/src/lib/util/delegate.cpp
@@ -113,10 +113,11 @@ delegate_generic_function delegate_mfp_itanium::convert_to_generic(delegate_gene
//-------------------------------------------------
// delegate_mfp_msvc::adjust_this_pointer - given
// an object pointer and member function pointer,
-// apply the displacement
+// apply the displacement, and walk past
+// recognisable thunks
//-------------------------------------------------
-void delegate_mfp_msvc::adjust_this_pointer(delegate_generic_class *&object) const
+delegate_generic_function 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)
@@ -124,16 +125,53 @@ void delegate_mfp_msvc::adjust_this_pointer(delegate_generic_class *&object) con
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);
+
+ // test for pointer to member function cast across virtual inheritance relationship
if ((sizeof(unknown_base_equiv) == m_size) && m_vt_index)
{
+ // add offset from "this" pointer to location of vptr, and add offset to virtual base from vtable
byteptr += m_vptr_offs;
std::uint8_t const *const vptr = *reinterpret_cast<std::uint8_t const *const *>(byteptr);
byteptr += *reinterpret_cast<int const *>(vptr + m_vt_index);
}
+
+ // add "this" pointer displacement if present in the pointer to member function
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);
+
+ // walk past recognisable thunks
+ std::uint8_t const *func = reinterpret_cast<std::uint8_t const *>(m_function);
+#if defined(__x86_64__) || defined(_M_X64)
+ while (true)
+ {
+ if (0xe9 == func[0])
+ {
+ // absolute jump with 32-bit displacement
+ LOG("Found relative jump at %p ", func);
+ func += 5 + *reinterpret_cast<std::int32_t const *>(func + 1);
+ LOG("redirecting to %p\n", func);
+ }
+ else if ((0x48 == func[0]) && (0x8b == func[1]) && (0x01 == func[2]) && (0xff == func[3]) && ((0x60 == func[4]) || (0xa0 == func[4])))
+ {
+ // virtual function call thunk - mov rax,QWORD PTR [rcx] ; jmp QWORD PTR [rax+...]
+ LOG("Found virtual member function thunk at %p ", func);
+ std::uint8_t const *const vptr = *reinterpret_cast<std::uint8_t const *const *>(object);
+ if (0x60 == func[4])
+ func = *reinterpret_cast<std::uint8_t const *const *>(vptr + *reinterpret_cast<std::int8_t const *>(func + 5));
+ else
+ func = *reinterpret_cast<std::uint8_t const *const *>(vptr + *reinterpret_cast<std::int32_t const *>(func + 5));
+ LOG("redirecting to %p\n", func);
+ }
+ else
+ {
+ // not something we can easily bypass
+ break;
+ }
+ }
+#endif
+ return reinterpret_cast<delegate_generic_function>(std::uintptr_t(func));
}
} // namespace util::detail
diff --git a/src/lib/util/delegate.h b/src/lib/util/delegate.h
index 59e83f82617..f79f8a110ad 100644
--- a/src/lib/util/delegate.h
+++ b/src/lib/util/delegate.h
@@ -506,8 +506,7 @@ public:
template <typename FunctionType>
void update_after_bind(FunctionType &funcptr, delegate_generic_class *&object)
{
- funcptr = reinterpret_cast<FunctionType>(m_function);
- return adjust_this_pointer(object);
+ funcptr = reinterpret_cast<FunctionType>(adjust_this_pointer(object));
}
template <typename FunctionType>
@@ -516,8 +515,8 @@ public:
}
private:
- // adjust the object pointer
- void adjust_this_pointer(delegate_generic_class *&object) const;
+ // adjust the object pointer and bypass thunks
+ delegate_generic_function adjust_this_pointer(delegate_generic_class *&object) const;
// actual state
uintptr_t m_function = 0; // pointer to function or non-virtual thunk for virtual function call