Skip to content

Instantly share code, notes, and snippets.

@exjam
Created October 6, 2017 16:25
Show Gist options
  • Save exjam/d9621881fb7fcb474c6b0ce5fc9da0f1 to your computer and use it in GitHub Desktop.
Save exjam/d9621881fb7fcb474c6b0ce5fc9da0f1 to your computer and use it in GitHub Desktop.
#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