diff --git a/src/backend/A64/devirtualize.h b/src/backend/A64/devirtualize.h new file mode 100644 index 00000000..7697ff8f --- /dev/null +++ b/src/backend/A64/devirtualize.h @@ -0,0 +1,98 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2018 MerryMage + * This software may be used and distributed according to the terms of the GNU + * General Public License version 2 or any later version. + */ + +#pragma once + +#include +#include + +#include "backend/A64/callback.h" +#include "common/assert.h" +#include "common/cast_util.h" +#include "common/common_types.h" +#include "common/mp/function_info.h" + +namespace Dynarmic::BackendA64 { + +namespace impl { + +template +struct ThunkBuilder; + +template +struct ThunkBuilder { + static R Thunk(C* this_, Args... args) { + return (this_->*mfp)(std::forward(args)...); + } +}; + +} // namespace impl + +template +ArgCallback DevirtualizeGeneric(Common::mp::class_type_t* this_) { + return ArgCallback{&impl::ThunkBuilder::Thunk, reinterpret_cast(this_)}; +} + +template +ArgCallback DevirtualizeWindows(Common::mp::class_type_t* this_) { + static_assert(sizeof(mfp) == 8); + return ArgCallback{Common::BitCast(mfp), reinterpret_cast(this_)}; +} + +template +ArgCallback DevirtualizeItanium(Common::mp::class_type_t* this_) { + struct MemberFunctionPointer { + /// For a non-virtual function, this is a simple function pointer. + /// For a virtual function, it is (1 + virtual table offset in bytes). + u64 ptr; + /// The required adjustment to `this`, prior to the call. + u64 adj; + } mfp_struct = Common::BitCast(mfp); + + static_assert(sizeof(MemberFunctionPointer) == 16); + static_assert(sizeof(MemberFunctionPointer) == sizeof(mfp)); + + u64 fn_ptr = mfp_struct.ptr; + u64 this_ptr = reinterpret_cast(this_) + mfp_struct.adj; + if (mfp_struct.ptr & 1) { + u64 vtable = Common::BitCastPointee(this_ptr); + fn_ptr = Common::BitCastPointee(vtable + fn_ptr - 1); + } + return ArgCallback{fn_ptr, this_ptr}; +} + +template +ArgCallback DevirtualizeAarch64(Common::mp::class_type_t* this_) { + struct MemberFunctionPointer { + /// For a non-virtual function, this is a simple function pointer. + /// For a virtual function, it is virtual table offset in bytes. + u64 ptr; + /// Twice the required adjustment to `this`, plus 1 if the member function is virtual. + u64 adj; + } mfp_struct = Common::BitCast(mfp); + + static_assert(sizeof(MemberFunctionPointer) == 16); + static_assert(sizeof(MemberFunctionPointer) == sizeof(mfp)); + + u64 fn_ptr = mfp_struct.ptr; + u64 this_ptr = reinterpret_cast(this_) + mfp_struct.adj / 2; + if (mfp_struct.adj & 1) { + u64 vtable = Common::BitCastPointee(this_ptr); + fn_ptr = Common::BitCastPointee(vtable + fn_ptr); + } + return ArgCallback{fn_ptr, this_ptr}; +} + +template +ArgCallback Devirtualize(Common::mp::class_type_t* this_) { +#if defined(linux) || defined(__linux) || defined(__linux__) + return DevirtualizeAarch64(this_); +#else + return DevirtualizeGeneric(this_); +#endif +} + +} // namespace Dynarmic::BackendA64