1
0
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:
Nekotekina 2015-07-09 03:33:15 +03:00
parent 096fc86f17
commit daa93a7226
11 changed files with 247 additions and 203 deletions

View File

@ -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("");
}

View File

@ -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("");
}

View File

@ -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);

View File

@ -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)

View File

@ -1,4 +1,5 @@
#pragma once
#include "Emu/Cell/Common.h"
#include "Emu/CPU/CPUThread.h"
#include "Emu/Memory/vm.h"

View File

@ -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);

View File

@ -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)))

View File

@ -8,7 +8,7 @@ namespace vm { using namespace ps3; }
extern Module cellAvconfExt;
s32 cellVideoOutConvertCursorColor(PPUThread& CPU)
s32 cellVideoOutConvertCursorColor()
{
UNIMPLEMENTED_FUNC(cellAvconfExt);
return CELL_OK;

View File

@ -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);
}

View File

@ -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);

View File

@ -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
{