mirror of
https://github.com/RPCS3/rpcs3.git
synced 2024-11-25 12:12:50 +01:00
ARMv7 func binder improved (faster compilation)
This commit is contained in:
parent
096fc86f17
commit
daa93a7226
@ -14,12 +14,12 @@ s32 sceDbgSetBreakOnErrorState(SceDbgBreakOnErrorState state)
|
||||
throw EXCEPTION("");
|
||||
}
|
||||
|
||||
s32 sceDbgAssertionHandler(vm::cptr<char> pFile, s32 line, bool stop, vm::cptr<char> pComponent, vm::cptr<char> pMessage) // va_args...
|
||||
s32 sceDbgAssertionHandler(vm::cptr<char> pFile, s32 line, bool stop, vm::cptr<char> pComponent, vm::cptr<char> pMessage, armv7_va_args_t va_args)
|
||||
{
|
||||
throw EXCEPTION("");
|
||||
}
|
||||
|
||||
s32 sceDbgLoggingHandler(vm::cptr<char> pFile, s32 line, s32 severity, vm::cptr<char> pComponent, vm::cptr<char> pMessage) // va_args...
|
||||
s32 sceDbgLoggingHandler(vm::cptr<char> pFile, s32 line, s32 severity, vm::cptr<char> pComponent, vm::cptr<char> pMessage, armv7_va_args_t va_args)
|
||||
{
|
||||
throw EXCEPTION("");
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ s32 sceFiosPathncmp(vm::cptr<char> pA, vm::cptr<char> pB, u32 n)
|
||||
throw EXCEPTION("");
|
||||
}
|
||||
|
||||
s32 sceFiosPrintf(vm::cptr<char> pFormat) // va_args...
|
||||
s32 sceFiosPrintf(vm::cptr<char> pFormat, armv7_va_args_t va_args)
|
||||
{
|
||||
throw EXCEPTION("");
|
||||
}
|
||||
|
@ -201,23 +201,23 @@ namespace sce_libc_func
|
||||
}
|
||||
}
|
||||
|
||||
void printf(ARMv7Context& context, vm::cptr<char> fmt) // va_args...
|
||||
void printf(ARMv7Context& context, vm::cptr<char> fmt, armv7_va_args_t va_args)
|
||||
{
|
||||
sceLibc.Warning("printf(fmt=*0x%x)", fmt);
|
||||
sceLibc.Log("*** *fmt = '%s'", fmt.get_ptr());
|
||||
|
||||
const std::string& result = armv7_fmt(context, fmt, 1, 0, 0);
|
||||
const std::string& result = armv7_fmt(context, fmt, va_args.g_count, va_args.f_count, va_args.v_count);
|
||||
sceLibc.Log("*** -> '%s'", result);
|
||||
|
||||
LOG_NOTICE(TTY, result);
|
||||
}
|
||||
|
||||
void sprintf(ARMv7Context& context, vm::ptr<char> str, vm::cptr<char> fmt) // va_args...
|
||||
void sprintf(ARMv7Context& context, vm::ptr<char> str, vm::cptr<char> fmt, armv7_va_args_t va_args)
|
||||
{
|
||||
sceLibc.Warning("sprintf(str=*0x%x, fmt=*0x%x)", str, fmt);
|
||||
sceLibc.Log("*** *fmt = '%s'", fmt.get_ptr());
|
||||
|
||||
const std::string& result = armv7_fmt(context, fmt, 2, 0, 0);
|
||||
const std::string& result = armv7_fmt(context, fmt, va_args.g_count, va_args.f_count, va_args.v_count);
|
||||
sceLibc.Log("*** -> '%s'", result);
|
||||
|
||||
::memcpy(str.get_ptr(), result.c_str(), result.size() + 1);
|
||||
|
@ -35,27 +35,37 @@ public:
|
||||
|
||||
};
|
||||
|
||||
typedef void(*psv_func_caller)(ARMv7Context&);
|
||||
using armv7_func_caller = void(*)(ARMv7Context&);
|
||||
|
||||
struct armv7_va_args_t
|
||||
{
|
||||
u32 g_count;
|
||||
u32 f_count;
|
||||
u32 v_count;
|
||||
};
|
||||
|
||||
// Utilities for binding ARMv7Context to C++ function arguments received by HLE functions or sent to callbacks
|
||||
namespace psv_func_detail
|
||||
{
|
||||
enum arg_class
|
||||
enum arg_class : u32
|
||||
{
|
||||
ARG_GENERAL,
|
||||
ARG_FLOAT,
|
||||
ARG_VECTOR,
|
||||
ARG_STACK,
|
||||
ARG_CONTEXT,
|
||||
ARG_VARIADIC,
|
||||
ARG_UNKNOWN,
|
||||
};
|
||||
|
||||
static const auto FIXED_STACK_FRAME_SIZE = 0x80; // described in CB_FUNC.h
|
||||
|
||||
template<typename T, arg_class type, int g_count, int f_count, int v_count>
|
||||
struct bind_arg;
|
||||
|
||||
template<typename T, int g_count, int f_count, int v_count>
|
||||
struct bind_arg<T, ARG_GENERAL, g_count, f_count, v_count>
|
||||
template<typename T, arg_class type, u32 g_count, u32 f_count, u32 v_count>
|
||||
struct bind_arg
|
||||
{
|
||||
static_assert(type == ARG_GENERAL, "Unknown function argument type");
|
||||
static_assert(!std::is_pointer<T>::value, "Invalid function argument type (pointer)");
|
||||
static_assert(!std::is_reference<T>::value, "Invalid function argument type (reference)");
|
||||
static_assert(sizeof(T) <= 4, "Invalid function argument type for ARG_GENERAL");
|
||||
|
||||
force_inline static T get_arg(ARMv7Context& context)
|
||||
@ -69,40 +79,40 @@ namespace psv_func_detail
|
||||
}
|
||||
};
|
||||
|
||||
template<int g_count, int f_count, int v_count>
|
||||
template<u32 g_count, u32 f_count, u32 v_count>
|
||||
struct bind_arg<u64, ARG_GENERAL, g_count, f_count, v_count>
|
||||
{
|
||||
// first u64 argument is passed in r0-r1, second one is passed in r2-r3 (if g_count = 3)
|
||||
static_assert(g_count == 1 || g_count == 3, "Wrong u64 argument position");
|
||||
static_assert(g_count == 2 || g_count == 4, "Wrong u64 argument position");
|
||||
|
||||
force_inline static u64 get_arg(ARMv7Context& context)
|
||||
{
|
||||
return context.GPR_D[g_count >> 1];
|
||||
return context.GPR_D[(g_count - 1) >> 1];
|
||||
}
|
||||
|
||||
force_inline static void put_arg(ARMv7Context& context, u64 arg)
|
||||
{
|
||||
context.GPR_D[g_count >> 1] = arg;
|
||||
context.GPR_D[(g_count - 1) >> 1] = arg;
|
||||
}
|
||||
};
|
||||
|
||||
template<int g_count, int f_count, int v_count>
|
||||
template<u32 g_count, u32 f_count, u32 v_count>
|
||||
struct bind_arg<s64, ARG_GENERAL, g_count, f_count, v_count>
|
||||
{
|
||||
static_assert(g_count == 1 || g_count == 3, "Wrong s64 argument position");
|
||||
static_assert(g_count == 2 || g_count == 4, "Wrong s64 argument position");
|
||||
|
||||
force_inline static s64 get_arg(ARMv7Context& context)
|
||||
{
|
||||
return context.GPR_D[g_count >> 1];
|
||||
return context.GPR_D[(g_count - 1) >> 1];
|
||||
}
|
||||
|
||||
force_inline static void put_arg(ARMv7Context& context, s64 arg)
|
||||
{
|
||||
context.GPR_D[g_count >> 1] = arg;
|
||||
context.GPR_D[(g_count - 1) >> 1] = arg;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, int g_count, int f_count, int v_count>
|
||||
template<typename T, u32 g_count, u32 f_count, u32 v_count>
|
||||
struct bind_arg<T, ARG_FLOAT, g_count, f_count, v_count>
|
||||
{
|
||||
static_assert(f_count <= 0, "TODO: Unsupported argument type (float)");
|
||||
@ -117,11 +127,11 @@ namespace psv_func_detail
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, int g_count, int f_count, int v_count>
|
||||
template<typename T, u32 g_count, u32 f_count, u32 v_count>
|
||||
struct bind_arg<T, ARG_VECTOR, g_count, f_count, v_count>
|
||||
{
|
||||
static_assert(v_count <= 0, "TODO: Unsupported argument type (vector)");
|
||||
static_assert(std::is_same<T, u128>::value, "Invalid function argument type for ARG_VECTOR");
|
||||
static_assert(std::is_same<std::remove_cv_t<T>, u128>::value, "Invalid function argument type for ARG_VECTOR");
|
||||
|
||||
force_inline static T get_arg(ARMv7Context& context)
|
||||
{
|
||||
@ -132,7 +142,7 @@ namespace psv_func_detail
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, int g_count, int f_count, int v_count>
|
||||
template<typename T, u32 g_count, u32 f_count, u32 v_count>
|
||||
struct bind_arg<T, ARG_STACK, g_count, f_count, v_count>
|
||||
{
|
||||
static_assert(f_count <= 0, "TODO: Unsupported stack argument type (float)");
|
||||
@ -155,44 +165,70 @@ namespace psv_func_detail
|
||||
}
|
||||
};
|
||||
|
||||
template<int g_count, int f_count, int v_count>
|
||||
template<u32 g_count, u32 f_count, u32 v_count>
|
||||
struct bind_arg<u64, ARG_STACK, g_count, f_count, v_count>
|
||||
{
|
||||
force_inline static u64 get_arg(ARMv7Context& context)
|
||||
{
|
||||
// TODO: check
|
||||
return vm::read64(context.SP + sizeof(u32) * (g_count - 5));
|
||||
return vm::read64(context.SP + sizeof(u32) * (g_count - 6));
|
||||
}
|
||||
|
||||
force_inline static void put_arg(ARMv7Context& context, u64 arg)
|
||||
{
|
||||
// TODO: check
|
||||
const int stack_pos = (g_count - 5) * 4 - FIXED_STACK_FRAME_SIZE;
|
||||
const int stack_pos = (g_count - 6) * 4 - FIXED_STACK_FRAME_SIZE;
|
||||
static_assert(stack_pos < -4, "TODO: Increase fixed stack frame size (arg count limit broken)");
|
||||
|
||||
vm::write64(context.SP + stack_pos, arg);
|
||||
}
|
||||
};
|
||||
|
||||
template<int g_count, int f_count, int v_count>
|
||||
template<u32 g_count, u32 f_count, u32 v_count>
|
||||
struct bind_arg<s64, ARG_STACK, g_count, f_count, v_count>
|
||||
{
|
||||
force_inline static s64 get_arg(ARMv7Context& context)
|
||||
{
|
||||
// TODO: check
|
||||
return vm::read64(context.SP + sizeof(u32) * (g_count - 5));
|
||||
return vm::read64(context.SP + sizeof(u32) * (g_count - 6));
|
||||
}
|
||||
|
||||
force_inline static void put_arg(ARMv7Context& context, s64 arg)
|
||||
{
|
||||
// TODO: check
|
||||
const int stack_pos = (g_count - 5) * 4 - FIXED_STACK_FRAME_SIZE;
|
||||
const int stack_pos = (g_count - 6) * 4 - FIXED_STACK_FRAME_SIZE;
|
||||
static_assert(stack_pos < -4, "TODO: Increase fixed stack frame size (arg count limit broken)");
|
||||
|
||||
vm::write64(context.SP + stack_pos, arg);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, u32 g_count, u32 f_count, u32 v_count>
|
||||
struct bind_arg<T, ARG_CONTEXT, g_count, f_count, v_count>
|
||||
{
|
||||
static_assert(std::is_same<T, ARMv7Context&>::value, "Invalid function argument type for ARG_CONTEXT");
|
||||
|
||||
force_inline static ARMv7Context& get_arg(ARMv7Context& context)
|
||||
{
|
||||
return context;
|
||||
}
|
||||
|
||||
force_inline static void put_arg(ARMv7Context& context, ARMv7Context& arg)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, u32 g_count, u32 f_count, u32 v_count>
|
||||
struct bind_arg<T, ARG_VARIADIC, g_count, f_count, v_count>
|
||||
{
|
||||
static_assert(std::is_same<std::remove_cv_t<T>, armv7_va_args_t>::value, "Invalid function argument type for ARG_VARIADIC");
|
||||
|
||||
force_inline static armv7_va_args_t get_arg(ARMv7Context& context)
|
||||
{
|
||||
return{ g_count, f_count, v_count };
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, arg_class type>
|
||||
struct bind_result
|
||||
{
|
||||
@ -253,7 +289,7 @@ namespace psv_func_detail
|
||||
//template<typename T>
|
||||
//struct bind_result<T, ARG_VECTOR>
|
||||
//{
|
||||
// static_assert(std::is_same<T, u128>::value, "Invalid function result type for ARG_VECTOR");
|
||||
// static_assert(std::is_same<std::remove_cv_t<T>, u128>::value, "Invalid function result type for ARG_VECTOR");
|
||||
|
||||
// static force_inline void put_result(ARMv7Context& context, const T& result)
|
||||
// {
|
||||
@ -266,94 +302,105 @@ namespace psv_func_detail
|
||||
static_assert(!std::is_pointer<RT>::value, "Invalid function result type (pointer)");
|
||||
static_assert(!std::is_reference<RT>::value, "Invalid function result type (reference)");
|
||||
static const bool is_float = std::is_floating_point<RT>::value;
|
||||
static const bool is_vector = std::is_same<RT, u128>::value;
|
||||
static const bool is_vector = std::is_same<std::remove_cv_t<RT>, u128>::value;
|
||||
static const arg_class value = is_float ? ARG_FLOAT : (is_vector ? ARG_VECTOR : ARG_GENERAL);
|
||||
};
|
||||
|
||||
template<typename T, int g_count, int f_count, int v_count>
|
||||
template<typename T, u32 g_count, u32 f_count, u32 v_count>
|
||||
struct arg_type
|
||||
{
|
||||
static_assert(!std::is_pointer<T>::value, "Invalid function argument type (pointer)");
|
||||
static_assert(!std::is_reference<T>::value, "Invalid function argument type (reference)");
|
||||
// TODO: check calculations
|
||||
static const bool is_float = std::is_floating_point<T>::value;
|
||||
static const bool is_vector = std::is_same<T, u128>::value;
|
||||
static const int g_align = __alignof(T) > 4 ? __alignof(T) >> 2 : 1;
|
||||
static const int g_pos = (is_float || is_vector) ? g_count : ((g_count + (g_align - 1)) & ~(g_align - 1)) + 1;
|
||||
static const int g_next = g_pos + g_align - 1;
|
||||
static const int f_value = !is_float ? f_count : f_count + 1;
|
||||
static const int v_value = !is_vector ? v_count : v_count + 1;
|
||||
static const arg_class value = is_float
|
||||
? ((f_value > 9000) ? ARG_STACK : ARG_FLOAT)
|
||||
: (is_vector ? ((v_value > 9000) ? ARG_STACK : ARG_VECTOR) : ((g_pos > 4) ? ARG_STACK : ARG_GENERAL));
|
||||
static const bool is_vector = std::is_same<std::remove_cv_t<T>, u128>::value;
|
||||
static const bool is_context = std::is_same<T, ARMv7Context&>::value;
|
||||
static const bool is_variadic = std::is_same<std::remove_cv_t<T>, armv7_va_args_t>::value;
|
||||
static const bool is_general = !is_float && !is_vector && !is_context && !is_variadic;
|
||||
|
||||
static const u32 g_align = alignof32(T) > 4 ? alignof32(T) >> 2 : 1;
|
||||
static const u32 g_value = is_general ? ((g_count + (g_align - 1)) & ~(g_align - 1)) + (g_align) : g_count;
|
||||
static const u32 f_value = f_count + is_float;
|
||||
static const u32 v_value = v_count + is_vector;
|
||||
|
||||
static const arg_class value =
|
||||
is_general ? (g_value > 4 ? ARG_STACK : ARG_GENERAL) :
|
||||
is_float ? (f_value > 9000 ? ARG_STACK : ARG_FLOAT) :
|
||||
is_vector ? (v_value > 9000 ? ARG_STACK : ARG_VECTOR) :
|
||||
is_context ? ARG_CONTEXT :
|
||||
is_variadic ? ARG_VARIADIC :
|
||||
ARG_UNKNOWN;
|
||||
};
|
||||
|
||||
template <typename RT, typename F, typename Tuple, bool Done, int Total, int... N>
|
||||
struct call_impl
|
||||
// wrapper for variadic argument info list, each value contains packed argument type and counts of GENERAL, FLOAT and VECTOR arguments
|
||||
template<u32... Values> struct arg_info_pack_t;
|
||||
|
||||
template<u32 First, u32... Values> struct arg_info_pack_t<First, Values...>
|
||||
{
|
||||
static force_inline RT call(F f, Tuple && t)
|
||||
static const u32 last_value = arg_info_pack_t<Values...>::last_value;
|
||||
};
|
||||
|
||||
template<u32 First> struct arg_info_pack_t<First>
|
||||
{
|
||||
static const u32 last_value = First;
|
||||
};
|
||||
|
||||
template<> struct arg_info_pack_t<>
|
||||
{
|
||||
static const u32 last_value = 0;
|
||||
};
|
||||
|
||||
// argument type + g/f/v_count unpacker
|
||||
template<typename T, u32 type_pack> struct bind_arg_packed
|
||||
{
|
||||
force_inline static T get_arg(ARMv7Context& context)
|
||||
{
|
||||
return call_impl<RT, F, Tuple, Total == 1 + sizeof...(N), Total, N..., sizeof...(N)>::call(f, std::forward<Tuple>(t));
|
||||
return bind_arg<T, static_cast<arg_class>(type_pack & 0xff), (type_pack >> 8) & 0xff, (type_pack >> 16) & 0xff, (type_pack >> 24)>::get_arg(context);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename RT, typename F, typename Tuple, int Total, int... N>
|
||||
struct call_impl<RT, F, Tuple, true, Total, N...>
|
||||
template<u32... Info, typename RT, typename... Args>
|
||||
force_inline RT call(ARMv7Context& context, RT(*func)(Args...), arg_info_pack_t<Info...> info)
|
||||
{
|
||||
static force_inline RT call(F f, Tuple && t)
|
||||
{
|
||||
return f(std::get<N>(std::forward<Tuple>(t))...);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename RT, typename F, typename Tuple>
|
||||
force_inline RT call(F f, Tuple && t)
|
||||
{
|
||||
using ttype = std::decay_t<Tuple>;
|
||||
return psv_func_detail::call_impl<RT, F, Tuple, 0 == std::tuple_size<ttype>::value, std::tuple_size<ttype>::value>::call(f, std::forward<Tuple>(t));
|
||||
// do the actual function call when all arguments are prepared (simultaneous unpacking of Args... and Info...)
|
||||
return func(bind_arg_packed<Args, Info>::get_arg(context)...);
|
||||
}
|
||||
|
||||
template<int g_count, int f_count, int v_count>
|
||||
force_inline std::tuple<> get_func_args(ARMv7Context& context)
|
||||
template<typename T, typename... Types, u32... Info, typename RT, typename... Args>
|
||||
force_inline RT call(ARMv7Context& context, RT(*func)(Args...), arg_info_pack_t<Info...> info)
|
||||
{
|
||||
// terminator
|
||||
return std::tuple<>();
|
||||
}
|
||||
// unpack previous type counts (0/0/0 for the first time)
|
||||
const u32 g_count = (info.last_value >> 8) & 0xff;
|
||||
const u32 f_count = (info.last_value >> 16) & 0xff;
|
||||
const u32 v_count = (info.last_value >> 24);
|
||||
|
||||
template<int g_count, int f_count, int v_count, typename T, typename... A>
|
||||
force_inline std::tuple<T, A...> get_func_args(ARMv7Context& context)
|
||||
{
|
||||
typedef arg_type<T, g_count, f_count, v_count> type;
|
||||
using type = arg_type<T, g_count, f_count, v_count>;
|
||||
const arg_class t = type::value;
|
||||
const int g0 = type::g_pos;
|
||||
const int g1 = type::g_next;
|
||||
const int f = type::f_value;
|
||||
const int v = type::v_value;
|
||||
const u32 g = type::g_value;
|
||||
const u32 f = type::f_value;
|
||||
const u32 v = type::v_value;
|
||||
|
||||
return std::tuple_cat(std::tuple<T>(bind_arg<T, t, g0, f, v>::get_arg(context)), get_func_args<g1, f, v, A...>(context));
|
||||
return call<Types...>(context, func, arg_info_pack_t<Info..., t | (g << 8) | (f << 16) | (v << 24)>{});
|
||||
}
|
||||
|
||||
template<int g_count, int f_count, int v_count>
|
||||
template<u32 g_count, u32 f_count, u32 v_count>
|
||||
force_inline static bool put_func_args(ARMv7Context& context)
|
||||
{
|
||||
// terminator
|
||||
return false;
|
||||
}
|
||||
|
||||
template<int g_count, int f_count, int v_count, typename T1, typename... T>
|
||||
template<u32 g_count, u32 f_count, u32 v_count, typename T1, typename... T>
|
||||
force_inline static bool put_func_args(ARMv7Context& context, T1 arg, T... args)
|
||||
{
|
||||
typedef arg_type<T1, g_count, f_count, v_count> type;
|
||||
using type = arg_type<T1, g_count, f_count, v_count>;
|
||||
const arg_class t = type::value;
|
||||
const int g0 = type::g_pos;
|
||||
const int g1 = type::g_next;
|
||||
const int f = type::f_value;
|
||||
const int v = type::v_value;
|
||||
const u32 g = type::g_value;
|
||||
const u32 f = type::f_value;
|
||||
const u32 v = type::v_value;
|
||||
|
||||
bind_arg<T1, t, g0, f, v>::put_arg(context, arg);
|
||||
bind_arg<T1, t, g, f, v>::put_arg(context, arg);
|
||||
|
||||
// return true if stack was used
|
||||
return put_func_args<g1, f, v>(context, args...) || (t == ARG_STACK);
|
||||
return put_func_args<g, f, v>(context, args...) || (t == ARG_STACK);
|
||||
}
|
||||
|
||||
template<typename RT, typename... T>
|
||||
@ -362,44 +409,22 @@ namespace psv_func_detail
|
||||
template<typename... T>
|
||||
struct func_binder<void, T...>
|
||||
{
|
||||
typedef void(*func_t)(T...);
|
||||
using func_t = void(*)(T...);
|
||||
|
||||
static void do_call(ARMv7Context& context, func_t _func)
|
||||
static void do_call(ARMv7Context& context, func_t func)
|
||||
{
|
||||
call<void>(_func, get_func_args<0, 0, 0, T...>(context));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... T>
|
||||
struct func_binder<void, ARMv7Context&, T...>
|
||||
{
|
||||
typedef void(*func_t)(ARMv7Context&, T...);
|
||||
|
||||
static void do_call(ARMv7Context& context, func_t _func)
|
||||
{
|
||||
call<void>(_func, std::tuple_cat(std::tuple<ARMv7Context&>(context), get_func_args<0, 0, 0, T...>(context)));
|
||||
call<T...>(context, func, arg_info_pack_t<>{});
|
||||
}
|
||||
};
|
||||
|
||||
template<typename RT, typename... T>
|
||||
struct func_binder
|
||||
{
|
||||
typedef RT(*func_t)(T...);
|
||||
using func_t = RT(*)(T...);
|
||||
|
||||
static void do_call(ARMv7Context& context, func_t _func)
|
||||
static void do_call(ARMv7Context& context, func_t func)
|
||||
{
|
||||
bind_result<RT, result_type<RT>::value>::put_result(context, call<RT>(_func, get_func_args<0, 0, 0, T...>(context)));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename RT, typename... T>
|
||||
struct func_binder<RT, ARMv7Context&, T...>
|
||||
{
|
||||
typedef RT(*func_t)(ARMv7Context&, T...);
|
||||
|
||||
static void do_call(ARMv7Context& context, func_t _func)
|
||||
{
|
||||
bind_result<RT, result_type<RT>::value>::put_result(context, call<RT>(_func, std::tuple_cat(std::tuple<ARMv7Context&>(context), get_func_args<0, 0, 0, T...>(context))));
|
||||
bind_result<RT, result_type<RT>::value>::put_result(context, call<T...>(context, func, arg_info_pack_t<>{}));
|
||||
}
|
||||
};
|
||||
|
||||
@ -439,14 +464,14 @@ struct psv_func
|
||||
u32 nid; // Unique function ID (should be generated individually for each elf loaded)
|
||||
u32 flags;
|
||||
const char* name; // Function name for information
|
||||
psv_func_caller func; // Function caller
|
||||
armv7_func_caller func; // Function caller
|
||||
psv_log_base* module; // Module for information
|
||||
|
||||
psv_func()
|
||||
{
|
||||
}
|
||||
|
||||
psv_func(u32 nid, u32 flags, psv_log_base* module, const char* name, psv_func_caller func)
|
||||
psv_func(u32 nid, u32 flags, psv_log_base* module, const char* name, armv7_func_caller func)
|
||||
: nid(nid)
|
||||
, flags(flags)
|
||||
, name(name)
|
||||
|
@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "Emu/Cell/Common.h"
|
||||
#include "Emu/CPU/CPUThread.h"
|
||||
#include "Emu/Memory/vm.h"
|
||||
|
@ -10,6 +10,8 @@ namespace cb_detail
|
||||
ARG_FLOAT,
|
||||
ARG_VECTOR,
|
||||
ARG_STACK,
|
||||
ARG_CONTEXT, // for compatibility with SC_FUNC and CALL_FUNC
|
||||
ARG_UNKNOWN,
|
||||
};
|
||||
|
||||
// Current implementation can handle only fixed amount of stack arguments.
|
||||
@ -18,11 +20,11 @@ namespace cb_detail
|
||||
static const auto FIXED_STACK_FRAME_SIZE = 0x90;
|
||||
|
||||
template<typename T, _func_arg_type type, int g_count, int f_count, int v_count>
|
||||
struct _func_arg;
|
||||
|
||||
template<typename T, int g_count, int f_count, int v_count>
|
||||
struct _func_arg<T, ARG_GENERAL, g_count, f_count, v_count>
|
||||
struct _func_arg
|
||||
{
|
||||
static_assert(type == ARG_GENERAL, "Unknown callback argument type");
|
||||
static_assert(!std::is_pointer<T>::value, "Invalid callback argument type (pointer)");
|
||||
static_assert(!std::is_reference<T>::value, "Invalid callback argument type (reference)");
|
||||
static_assert(sizeof(T) <= 8, "Invalid callback argument type for ARG_GENERAL");
|
||||
|
||||
force_inline static void set_value(PPUThread& CPU, const T& arg)
|
||||
@ -45,7 +47,7 @@ namespace cb_detail
|
||||
template<typename T, int g_count, int f_count, int v_count>
|
||||
struct _func_arg<T, ARG_VECTOR, g_count, f_count, v_count>
|
||||
{
|
||||
static_assert(std::is_same<T, u128>::value, "Invalid callback argument type for ARG_VECTOR");
|
||||
static_assert(std::is_same<std::remove_cv_t<T>, u128>::value, "Invalid callback argument type for ARG_VECTOR");
|
||||
|
||||
force_inline static void set_value(PPUThread& CPU, const T& arg)
|
||||
{
|
||||
@ -68,6 +70,16 @@ namespace cb_detail
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, int g_count, int f_count, int v_count>
|
||||
struct _func_arg<T, ARG_CONTEXT, g_count, f_count, v_count>
|
||||
{
|
||||
static_assert(std::is_same<T, PPUThread&>::value, "Invalid callback argument type for ARG_CONTEXT");
|
||||
|
||||
force_inline static void set_value(PPUThread& CPU, const T& arg)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template<int g_count, int f_count, int v_count>
|
||||
force_inline static bool _bind_func_args(PPUThread& CPU)
|
||||
{
|
||||
@ -78,18 +90,24 @@ namespace cb_detail
|
||||
template<int g_count, int f_count, int v_count, typename T1, typename... T>
|
||||
force_inline static bool _bind_func_args(PPUThread& CPU, T1 arg1, T... args)
|
||||
{
|
||||
static_assert(!std::is_pointer<T1>::value, "Invalid callback argument type (pointer)");
|
||||
static_assert(!std::is_reference<T1>::value, "Invalid callback argument type (reference)");
|
||||
const bool is_float = std::is_floating_point<T1>::value;
|
||||
const bool is_vector = std::is_same<T1, u128>::value;
|
||||
const _func_arg_type t = is_float
|
||||
? ((f_count >= 13) ? ARG_STACK : ARG_FLOAT)
|
||||
: (is_vector ? ((v_count >= 12) ? ARG_STACK : ARG_VECTOR) : ((g_count >= 8) ? ARG_STACK : ARG_GENERAL));
|
||||
const int g = g_count + (is_float || is_vector ? 0 : 1);
|
||||
const int f = f_count + (is_float ? 1 : 0);
|
||||
const int v = v_count + (is_vector ? 1 : 0);
|
||||
const bool is_vector = std::is_same<std::remove_cv_t<T1>, u128>::value;
|
||||
const bool is_context = std::is_same<T1, PPUThread&>::value;
|
||||
const bool is_general = !is_float && !is_vector && !is_context;
|
||||
|
||||
const _func_arg_type t =
|
||||
is_general ? (g_count >= 8 ? ARG_STACK : ARG_GENERAL) :
|
||||
is_float ? (f_count >= 13 ? ARG_STACK : ARG_FLOAT) :
|
||||
is_vector ? (v_count >= 12 ? ARG_STACK : ARG_VECTOR) :
|
||||
is_context ? ARG_CONTEXT :
|
||||
ARG_UNKNOWN;
|
||||
|
||||
const int g = g_count + is_general;
|
||||
const int f = f_count + is_float;
|
||||
const int v = v_count + is_vector;
|
||||
|
||||
_func_arg<T1, t, g, f, v>::set_value(CPU, arg1);
|
||||
|
||||
// return true if stack was used
|
||||
return _bind_func_args<g, f, v>(CPU, args...) || (t == ARG_STACK);
|
||||
}
|
||||
@ -97,7 +115,7 @@ namespace cb_detail
|
||||
template<typename T, _func_arg_type type>
|
||||
struct _func_res
|
||||
{
|
||||
static_assert(type == ARG_GENERAL, "Wrong use of _func_res template");
|
||||
static_assert(type == ARG_GENERAL, "Unknown callback result type");
|
||||
static_assert(sizeof(T) <= 8, "Invalid callback result type for ARG_GENERAL");
|
||||
|
||||
force_inline static T get_value(const PPUThread& CPU)
|
||||
@ -120,7 +138,7 @@ namespace cb_detail
|
||||
template<typename T>
|
||||
struct _func_res<T, ARG_VECTOR>
|
||||
{
|
||||
static_assert(std::is_same<T, u128>::value, "Invalid callback result type for ARG_VECTOR");
|
||||
static_assert(std::is_same<std::remove_cv_t<T>, u128>::value, "Invalid callback result type for ARG_VECTOR");
|
||||
|
||||
force_inline static T get_value(const PPUThread& CPU)
|
||||
{
|
||||
@ -138,7 +156,7 @@ namespace cb_detail
|
||||
static_assert(!std::is_pointer<RT>::value, "Invalid callback result type (pointer)");
|
||||
static_assert(!std::is_reference<RT>::value, "Invalid callback result type (reference)");
|
||||
const bool is_float = std::is_floating_point<RT>::value;
|
||||
const bool is_vector = std::is_same<RT, u128>::value;
|
||||
const bool is_vector = std::is_same<std::remove_cv_t<RT>, u128>::value;
|
||||
const _func_arg_type t = is_float ? ARG_FLOAT : (is_vector ? ARG_VECTOR : ARG_GENERAL);
|
||||
|
||||
return _func_res<RT, t>::get_value(CPU);
|
||||
|
@ -125,20 +125,8 @@ void hook_ppu_funcs(vm::ptr<u32> base, u32 size);
|
||||
|
||||
bool patch_ppu_import(u32 addr, u32 index);
|
||||
|
||||
// don't use directly
|
||||
template<typename RT, typename... T, typename... Args> inline auto _call_ppu(RT(*func)(PPUThread&, T...), PPUThread& CPU, Args&&... args) -> decltype(func(CPU, args...))
|
||||
{
|
||||
return func(CPU, args...);
|
||||
}
|
||||
|
||||
// don't use directly
|
||||
template<typename RT, typename... T, typename... Args> inline auto _call_ppu(RT(*func)(T...), PPUThread& CPU, Args&&... args) -> decltype(func(args...))
|
||||
{
|
||||
return func(args...);
|
||||
}
|
||||
|
||||
// call specified function directly if LLE is not available, call LLE equivalent in callback style otherwise
|
||||
template<typename T, typename... Args> inline auto hle_call_func(T func, u32 index, PPUThread& CPU, Args... args) -> decltype(_call_ppu(func, CPU, args...))
|
||||
template<typename T, typename... Args> inline auto hle_call_func(PPUThread& CPU, T func, u32 index, Args&&... args) -> decltype(func(std::forward<Args>(args)...))
|
||||
{
|
||||
const auto mfunc = get_ppu_func_by_index(index);
|
||||
|
||||
@ -147,15 +135,15 @@ template<typename T, typename... Args> inline auto hle_call_func(T func, u32 ind
|
||||
const u32 pc = vm::read32(mfunc->lle_func.addr());
|
||||
const u32 rtoc = vm::read32(mfunc->lle_func.addr() + 4);
|
||||
|
||||
return cb_call<decltype(_call_ppu(func, CPU, args...)), Args...>(CPU, pc, rtoc, args...);
|
||||
return cb_call<decltype(func(std::forward<Args>(args)...)), Args...>(CPU, pc, rtoc, std::forward<Args>(args)...);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _call_ppu(func, CPU, args...);
|
||||
return func(std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
#define CALL_FUNC(func, ...) hle_call_func(func, g_ppu_func_index__##func, __VA_ARGS__)
|
||||
#define CALL_FUNC(cpu, func, ...) hle_call_func(cpu, func, g_ppu_func_index__##func, __VA_ARGS__)
|
||||
|
||||
#define REG_FUNC(module, name) add_ppu_func(ModuleFunc(get_function_id(#name), 0, &module, #name, bind_func(name)))
|
||||
#define REG_FUNC_FH(module, name) add_ppu_func(ModuleFunc(get_function_id(#name), MFF_FORCED_HLE, &module, #name, bind_func(name)))
|
||||
|
@ -8,7 +8,7 @@ namespace vm { using namespace ps3; }
|
||||
|
||||
extern Module cellAvconfExt;
|
||||
|
||||
s32 cellVideoOutConvertCursorColor(PPUThread& CPU)
|
||||
s32 cellVideoOutConvertCursorColor()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellAvconfExt);
|
||||
return CELL_OK;
|
||||
|
@ -427,7 +427,7 @@ void spursHandlerWaitReady(PPUThread& CPU, vm::ptr<CellSpurs> spurs)
|
||||
{
|
||||
extern u32 g_ppu_func_index__sys_lwmutex_unlock; // test
|
||||
|
||||
if (s32 rc = CALL_FUNC(sys_lwmutex_unlock, CPU, spurs.of(&CellSpurs::mutex)))
|
||||
if (s32 rc = CALL_FUNC(CPU, sys_lwmutex_unlock, CPU, spurs.of(&CellSpurs::mutex)))
|
||||
{
|
||||
throw EXCEPTION("sys_lwmutex_unlock() failed (0x%x)", rc);
|
||||
}
|
||||
|
@ -1141,11 +1141,11 @@ s32 _sys_free(u32 addr)
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 _sys_snprintf(PPUThread& CPU, vm::ptr<char> dst, u32 count, vm::cptr<char> fmt) // va_args...
|
||||
s32 _sys_snprintf(PPUThread& CPU, vm::ptr<char> dst, u32 count, vm::cptr<char> fmt, ppu_va_args_t va_args)
|
||||
{
|
||||
sysPrxForUser.Warning("_sys_snprintf(dst=*0x%x, count=%d, fmt=*0x%x, ...)", dst, count, fmt);
|
||||
|
||||
std::string result = ps3_fmt(CPU, fmt, 3, 0, 0);
|
||||
std::string result = ps3_fmt(CPU, fmt, va_args.g_count, va_args.f_count, va_args.v_count);
|
||||
|
||||
sysPrxForUser.Warning("*** '%s' -> '%s'", fmt.get_ptr(), result);
|
||||
|
||||
@ -1163,7 +1163,7 @@ s32 _sys_snprintf(PPUThread& CPU, vm::ptr<char> dst, u32 count, vm::cptr<char> f
|
||||
}
|
||||
}
|
||||
|
||||
s32 _sys_printf(vm::cptr<char> fmt) // va_args...
|
||||
s32 _sys_printf(vm::cptr<char> fmt, ppu_va_args_t va_args)
|
||||
{
|
||||
sysPrxForUser.Todo("_sys_printf(fmt=*0x%x, ...)", fmt);
|
||||
|
||||
|
@ -4,22 +4,33 @@
|
||||
|
||||
using ppu_func_caller = void(*)(PPUThread&);
|
||||
|
||||
struct ppu_va_args_t
|
||||
{
|
||||
u32 g_count;
|
||||
u32 f_count;
|
||||
u32 v_count;
|
||||
};
|
||||
|
||||
namespace ppu_func_detail
|
||||
{
|
||||
// argument type classification
|
||||
enum arg_class : u32
|
||||
{
|
||||
ARG_GENERAL, // argument is stored in GPR registers (from r3 to r10)
|
||||
ARG_FLOAT, // argument is stored in FPR registers (from f1 to f12)
|
||||
ARG_FLOAT, // argument is stored in FPR registers (from f1 to f13)
|
||||
ARG_VECTOR, // argument is stored in VPR registers (from v2 to v13)
|
||||
ARG_STACK,
|
||||
ARG_STACK, // argument is stored on the stack
|
||||
ARG_CONTEXT, // PPUThread& passed, doesn't affect g/f/v_count
|
||||
ARG_VARIADIC, // information about arg counts already passed, doesn't affect g/f/v_count
|
||||
ARG_UNKNOWN,
|
||||
};
|
||||
|
||||
template<typename T, arg_class type, u32 g_count, u32 f_count, u32 v_count> struct bind_arg;
|
||||
|
||||
template<typename T, u32 g_count, u32 f_count, u32 v_count>
|
||||
struct bind_arg<T, ARG_GENERAL, g_count, f_count, v_count>
|
||||
template<typename T, arg_class type, u32 g_count, u32 f_count, u32 v_count>
|
||||
struct bind_arg
|
||||
{
|
||||
static_assert(type == ARG_GENERAL, "Unknown function argument type");
|
||||
static_assert(!std::is_pointer<T>::value, "Invalid function argument type (pointer)");
|
||||
static_assert(!std::is_reference<T>::value, "Invalid function argument type (reference)");
|
||||
static_assert(sizeof(T) <= 8, "Invalid function argument type for ARG_GENERAL");
|
||||
|
||||
static force_inline T get_arg(PPUThread& CPU)
|
||||
@ -42,7 +53,7 @@ namespace ppu_func_detail
|
||||
template<typename T, u32 g_count, u32 f_count, u32 v_count>
|
||||
struct bind_arg<T, ARG_VECTOR, g_count, f_count, v_count>
|
||||
{
|
||||
static_assert(std::is_same<T, u128>::value, "Invalid function argument type for ARG_VECTOR");
|
||||
static_assert(std::is_same<std::remove_cv_t<T>, u128>::value, "Invalid function argument type for ARG_VECTOR");
|
||||
|
||||
static force_inline T get_arg(PPUThread& CPU)
|
||||
{
|
||||
@ -65,10 +76,32 @@ namespace ppu_func_detail
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, u32 g_count, u32 f_count, u32 v_count>
|
||||
struct bind_arg<T, ARG_CONTEXT, g_count, f_count, v_count>
|
||||
{
|
||||
static_assert(std::is_same<T, PPUThread&>::value, "Invalid function argument type for ARG_CONTEXT");
|
||||
|
||||
static force_inline PPUThread& get_arg(PPUThread& CPU)
|
||||
{
|
||||
return CPU;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, u32 g_count, u32 f_count, u32 v_count>
|
||||
struct bind_arg<T, ARG_VARIADIC, g_count, f_count, v_count>
|
||||
{
|
||||
static_assert(std::is_same<T, ppu_va_args_t>::value, "Invalid function argument type for ARG_VARIADIC");
|
||||
|
||||
static force_inline ppu_va_args_t get_arg(PPUThread& CPU)
|
||||
{
|
||||
return{ g_count, f_count, v_count };
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, arg_class type>
|
||||
struct bind_result
|
||||
{
|
||||
static_assert(type == ARG_GENERAL, "Wrong use of bind_result template");
|
||||
static_assert(type == ARG_GENERAL, "Unknown function result type");
|
||||
static_assert(sizeof(T) <= 8, "Invalid function result type for ARG_GENERAL");
|
||||
|
||||
static force_inline void put_result(PPUThread& CPU, const T& result)
|
||||
@ -91,7 +124,7 @@ namespace ppu_func_detail
|
||||
template<typename T>
|
||||
struct bind_result<T, ARG_VECTOR>
|
||||
{
|
||||
static_assert(std::is_same<T, u128>::value, "Invalid function result type for ARG_VECTOR");
|
||||
static_assert(std::is_same<std::remove_cv_t<T>, u128>::value, "Invalid function result type for ARG_VECTOR");
|
||||
|
||||
static force_inline void put_result(PPUThread& CPU, const T& result)
|
||||
{
|
||||
@ -117,7 +150,7 @@ namespace ppu_func_detail
|
||||
static const u32 last_value = 0;
|
||||
};
|
||||
|
||||
// argument unpacker
|
||||
// argument type + g/f/v_count unpacker
|
||||
template<typename T, u32 type_pack> struct bind_arg_packed
|
||||
{
|
||||
static force_inline T get_arg(PPUThread& CPU)
|
||||
@ -129,21 +162,13 @@ namespace ppu_func_detail
|
||||
template<u32... Info, typename RT, typename... Args>
|
||||
force_inline RT call(PPUThread& CPU, RT(*func)(Args...), arg_info_pack_t<Info...>)
|
||||
{
|
||||
// do the actual function call when all arguments are prepared (simultaneous unpacking of Args... and Info...)
|
||||
return func(bind_arg_packed<Args, Info>::get_arg(CPU)...);
|
||||
}
|
||||
|
||||
template<u32... Info, typename RT, typename... Args>
|
||||
force_inline RT call(PPUThread& CPU, RT(*func)(PPUThread&, Args...), arg_info_pack_t<Info...>)
|
||||
{
|
||||
return func(CPU, bind_arg_packed<Args, Info>::get_arg(CPU)...);
|
||||
}
|
||||
|
||||
template<typename T, typename... Types, u32... Info, typename RT, typename... Args>
|
||||
force_inline RT call(PPUThread& CPU, RT(*func)(Args...), arg_info_pack_t<Info...> info)
|
||||
{
|
||||
static_assert(!std::is_pointer<T>::value, "Invalid function argument type (pointer)");
|
||||
static_assert(!std::is_reference<T>::value, "Invalid function argument type (reference)");
|
||||
|
||||
// unpack previous type counts (0/0/0 for the first time)
|
||||
const u32 g_count = (info.last_value >> 8) & 0xff;
|
||||
const u32 f_count = (info.last_value >> 16) & 0xff;
|
||||
@ -151,13 +176,22 @@ namespace ppu_func_detail
|
||||
|
||||
// TODO: check calculations
|
||||
const bool is_float = std::is_floating_point<T>::value;
|
||||
const bool is_vector = std::is_same<T, u128>::value;
|
||||
const arg_class t = is_float
|
||||
? ((f_count >= 13) ? ARG_STACK : ARG_FLOAT)
|
||||
: (is_vector ? ((v_count >= 12) ? ARG_STACK : ARG_VECTOR) : ((g_count >= 8) ? ARG_STACK : ARG_GENERAL));
|
||||
const u32 g = g_count + (is_float || is_vector ? 0 : 1);
|
||||
const u32 f = f_count + (is_float ? 1 : 0);
|
||||
const u32 v = v_count + (is_vector ? 1 : 0);
|
||||
const bool is_vector = std::is_same<std::remove_cv_t<T>, u128>::value;
|
||||
const bool is_context = std::is_same<T, PPUThread&>::value;
|
||||
const bool is_variadic = std::is_same<std::remove_cv_t<T>, ppu_va_args_t>::value;
|
||||
const bool is_general = !is_float && !is_vector && !is_context && !is_variadic;
|
||||
|
||||
const arg_class t =
|
||||
is_general ? (g_count >= 8 ? ARG_STACK : ARG_GENERAL) :
|
||||
is_float ? (f_count >= 13 ? ARG_STACK : ARG_FLOAT) :
|
||||
is_vector ? (v_count >= 12 ? ARG_STACK : ARG_VECTOR) :
|
||||
is_context ? ARG_CONTEXT :
|
||||
is_variadic ? ARG_VARIADIC :
|
||||
ARG_UNKNOWN;
|
||||
|
||||
const u32 g = g_count + is_general;
|
||||
const u32 f = f_count + is_float;
|
||||
const u32 v = v_count + is_vector;
|
||||
|
||||
return call<Types...>(CPU, func, arg_info_pack_t<Info..., t | (g << 8) | (f << 16) | (v << 24)>{});
|
||||
}
|
||||
@ -167,23 +201,12 @@ namespace ppu_func_detail
|
||||
static_assert(!std::is_pointer<RT>::value, "Invalid function result type (pointer)");
|
||||
static_assert(!std::is_reference<RT>::value, "Invalid function result type (reference)");
|
||||
static const bool is_float = std::is_floating_point<RT>::value;
|
||||
static const bool is_vector = std::is_same<RT, u128>::value;
|
||||
static const bool is_vector = std::is_same<std::remove_cv_t<RT>, u128>::value;
|
||||
static const arg_class value = is_float ? ARG_FLOAT : (is_vector ? ARG_VECTOR : ARG_GENERAL);
|
||||
};
|
||||
|
||||
template<typename RT, typename... T> struct func_binder;
|
||||
|
||||
template<typename... T>
|
||||
struct func_binder<void, PPUThread&, T...>
|
||||
{
|
||||
using func_t = void(*)(PPUThread&, T...);
|
||||
|
||||
static force_inline void do_call(PPUThread& CPU, func_t func)
|
||||
{
|
||||
call<T...>(CPU, func, arg_info_pack_t<>{});
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... T>
|
||||
struct func_binder<void, T...>
|
||||
{
|
||||
@ -217,17 +240,6 @@ namespace ppu_func_detail
|
||||
}
|
||||
};
|
||||
|
||||
template<typename RT, typename... T>
|
||||
struct func_binder<RT, PPUThread&, T...>
|
||||
{
|
||||
using func_t = RT(*)(PPUThread&, T...);
|
||||
|
||||
static force_inline void do_call(PPUThread& CPU, func_t func)
|
||||
{
|
||||
bind_result<RT, result_type<RT>::value>::put_result(CPU, call<T...>(CPU, func, arg_info_pack_t<>{}));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename RT, typename... T>
|
||||
struct func_binder
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user