Created
October 6, 2017 16:25
-
-
Save exjam/d9621881fb7fcb474c6b0ce5fc9da0f1 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include <cstdint> | |
#include <common/type_traits.h> | |
#include <libcpu/be2_struct.h> | |
#include <type_traits> | |
namespace cafe | |
{ | |
namespace detail | |
{ | |
// Equivalent to std:true_type if type T is a virt_ptr. | |
template<typename> | |
struct is_virt_ptr : std::false_type { }; | |
template<typename T> | |
struct is_virt_ptr<virt_ptr<T>> : std::true_type { }; | |
/** | |
* The register a type is stored in. | |
* | |
* We must distinguish between gpr for 32 and 64 bit values because a 64 bit | |
* value has register index alignment in the PPC calling convention. | |
*/ | |
enum class RegisterType | |
{ | |
Gpr32, | |
Gpr64, | |
Fpr, | |
}; | |
/** | |
* A type T is stored in an FPR register if it is: | |
* - A floating point type | |
*/ | |
template<typename T> | |
using is_fpr_type = is_true<std::is_floating_point<T>::value>; | |
/** | |
* A type T is stored in a single GPR register if it is: | |
* - sizeof(T) <= 4 | |
* - Not a floating point | |
* - If a uint32_t can be constructed from T or it is a virt_ptr | |
*/ | |
template<typename T> | |
using is_gpr32_type = is_true< | |
sizeof(T) <= 4 && | |
!std::is_floating_point<T>::value && | |
(std::is_constructible<typename safe_underlying_type<T>::type, uint32_t>::value || | |
is_virt_ptr<T>::value)>; | |
/** | |
* A type T is stored in two aligned GPR registers if it is: | |
* - sizeof(T) == 8 | |
* - Not a floating point | |
* - If a uint64_t can be constructed from T | |
*/ | |
template<typename T> | |
using is_gpr64_type = is_true< | |
sizeof(T) == 8 && | |
!std::is_floating_point<T>::value && | |
std::is_constructible<typename safe_underlying_type<T>::type, uint64_t>::value>; | |
// Gets the register type for a type T. | |
template<typename, typename T2 = void> | |
struct register_type; | |
template<typename T> | |
struct register_type<T, typename std::enable_if<is_fpr_type<T>::value>::type> | |
{ | |
static constexpr auto value = RegisterType::Fpr; | |
}; | |
template<typename T> | |
struct register_type<T, typename std::enable_if<is_gpr32_type<T>::value>::type> | |
{ | |
static constexpr auto value = RegisterType::Gpr32; | |
}; | |
template<typename T> | |
struct register_type<T, typename std::enable_if<is_gpr64_type<T>::value>::type> | |
{ | |
static constexpr auto value = RegisterType::Gpr64; | |
}; | |
// Prepends a type T to a tuple | |
template<typename T, typename Ts> | |
struct tuple_prepend; | |
template<typename T, typename... Ts> | |
struct tuple_prepend<T, std::tuple<Ts...>> | |
{ | |
using type = std::tuple<T, Ts...>; | |
}; | |
template<typename T> | |
struct tuple_prepend<T, void> | |
{ | |
using type = std::tuple<T>; | |
}; | |
// Calculate the index for a given RegisterType | |
template<RegisterType type, std::size_t GprIndex, std::size_t FprIndex> | |
struct register_index; | |
template<std::size_t GprIndex, std::size_t FprIndex> | |
struct register_index<RegisterType::Gpr32, GprIndex, FprIndex> | |
{ | |
static constexpr auto value = GprIndex; | |
static constexpr auto gpr_next = value + 1; | |
static constexpr auto fpr_next = FprIndex; | |
}; | |
template<std::size_t GprIndex, std::size_t FprIndex> | |
struct register_index<RegisterType::Gpr64, GprIndex, FprIndex> | |
{ | |
static constexpr auto value = ((GprIndex % 2) == 0) ? (GprIndex + 1) : GprIndex; | |
static constexpr auto gpr_next = value + 2; | |
static constexpr auto fpr_next = FprIndex; | |
}; | |
template<std::size_t GprIndex, std::size_t FprIndex> | |
struct register_index<RegisterType::Fpr, GprIndex, FprIndex> | |
{ | |
static constexpr auto value = FprIndex; | |
static constexpr auto gpr_next = GprIndex; | |
static constexpr auto fpr_next = value + 1; | |
}; | |
// An empty type to store | |
template<typename ValueType, RegisterType Type, std::size_t Index> | |
struct param_info | |
{ | |
using type = ValueType; | |
using reg_type = Type; | |
using reg_index = Index; | |
}; | |
// Calculates a std::tuple<param_info...> type for a list of types | |
template<std::size_t GprIndex, std::size_t FprIndex, typename... Ts> | |
struct get_param_infos_impl; | |
template<std::size_t GprIndex, std::size_t FprIndex, typename Head, typename... Tail> | |
struct get_param_infos_impl<GprIndex, FprIndex, Head, Tail...> | |
{ | |
using head_register_type = register_type<Head>; | |
using head_arg_index = register_index<head_register_type::value, GprIndex, FprIndex>; | |
using type = typename tuple_prepend< | |
param_info<Head, head_register_type::value, head_arg_index::value>, | |
typename get_param_infos_impl<head_arg_index::gpr_next, head_arg_index::fpr_next, Tail...>::type | |
>::type; | |
}; | |
template<std::size_t GprIndex, std::size_t FprIndex> | |
struct get_param_infos_impl<GprIndex, FprIndex> | |
{ | |
using type = void; | |
}; | |
// Stores information about a function | |
template<typename T> | |
struct function_traits; | |
template<typename ReturnType, typename... ArgTypes> | |
struct function_traits<ReturnType(*)(ArgTypes...)> | |
{ | |
using return_type = ReturnType; | |
using param_info = typename get_param_infos_impl<3, 1, ArgTypes...>::type; | |
}; | |
template<typename ObjectType, typename ReturnType, typename... ArgTypes> | |
struct function_traits<ReturnType(ObjectType::*)(ArgTypes...)> | |
{ | |
using object_type = ObjectType; | |
using return_type = ReturnType; | |
using param_info = typename get_param_infos_impl<4, 1, ArgTypes...>::type; | |
}; | |
template<typename ArgType, RegisterType regType, std::size_t regIndex> | |
inline ArgType readParam(cpu::Core *core, param_info<ArgType, regType, regIndex>) | |
{ | |
if constexpr (regType == RegisterType::Gpr32) { | |
if constexpr (is_virt_ptr<ArgType>::value) { | |
return virt_cast<ArgType::value_type>(static_cast<virt_addr>(core->gpr[regIndex])); | |
} else { | |
return static_cast<ArgType>(core->gpr[regIndex]); | |
} | |
} else if constexpr (regType == RegisterType::Gpr64) { | |
return static_cast<ArgType>((static_cast<uint64_t>(core->gpr[regIndex]) << 32) | static_cast<uint64_t>(core->gpr[regIndex + 1])); | |
} else if constexpr (regType == RegisterType::Fpr) { | |
return static_cast<ArgType>(core->fpr[regIndex].paired0); | |
} | |
} | |
template<typename ArgType, RegisterType regType, std::size_t regIndex> | |
inline ArgType writeParam(cpu::Core *core, param_info<ArgType, regType, regIndex>, ArgType value) | |
{ | |
if constexpr (regType == RegisterType::Gpr32) { | |
if constexpr (is_virt_ptr<ArgType>::value) { | |
core->gpr[regIndex] = virt_cast<virt_addr>(value); | |
} else { | |
core->gpr[regIndex] = static_cast<uint32_t>(value); | |
} | |
} else if constexpr (regType == RegisterType::Gpr64) { | |
core->gpr[regIndex] = static_cast<uint32_t>((value >> 32) & 0xFFFFFFFF); | |
core->gpr[regIndex + 1] = static_cast<uint32_t>(value & 0xFFFFFFFF); | |
} else if constexpr (regType == RegisterType::Fpr) { | |
core->fpr[regIndex].paired0 = static_cast<double>(value); | |
} | |
} | |
} // namespace detail | |
} // namespace cafe |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment