1
0
mirror of https://github.com/RPCS3/rpcs3.git synced 2025-01-31 20:41:45 +01:00

LLVM DSL refactoring

Properly forward value categories in expression structs.
Simplify SFINAE tests (is_llvm_expr, llvm_common_t) in global operators.
Add llvm_const_int and remove llvm_add_const, llvm_sub_const, etc.
Add llvm_ord and llvm_uno for FP comparison via >=< operators.
Replace cpu_translator::fcmp with fcmp_ord and fcmp_uno.
This commit is contained in:
Nekotekina 2019-04-17 03:00:53 +03:00
parent 467ef2afca
commit dc9118ef50
3 changed files with 317 additions and 257 deletions

View File

@ -325,466 +325,460 @@ struct llvm_value_t<T[N]> : llvm_value_t<T>
} }
}; };
template <typename T, typename A1, typename A2> template <typename T>
struct llvm_add_t using llvm_expr_t = std::decay_t<T>;
template <typename T, typename = void>
struct is_llvm_expr
{
};
template <typename T>
struct is_llvm_expr<T, std::void_t<decltype(std::declval<T>().eval(std::declval<llvm::IRBuilder<>*>()))>>
{
using type = typename std::decay_t<T>::type;
};
template <typename T, typename Of, typename = void>
struct is_llvm_expr_of
{
static constexpr bool ok = false;
};
template <typename T, typename Of>
struct is_llvm_expr_of<T, Of, std::void_t<typename is_llvm_expr<T>::type, typename is_llvm_expr<Of>::type>>
{
static constexpr bool ok = std::is_same_v<typename is_llvm_expr<T>::type, typename is_llvm_expr<Of>::type>;
};
template <typename T, typename... Types>
using llvm_common_t = std::enable_if_t<(is_llvm_expr_of<T, Types>::ok && ...), typename is_llvm_expr<T>::type>;
template <typename T, bool ForceSigned = false>
struct llvm_const_int
{ {
using type = T; using type = T;
A1 a1; u64 val;
A2 a2;
static_assert(llvm_value_t<T>::is_sint || llvm_value_t<T>::is_uint || llvm_value_t<T>::is_float, "llvm_add_t<>: invalid type"); llvm::Value* eval(llvm::IRBuilder<>* ir) const
{
static_assert(llvm_value_t<T>::is_int, "llvm_const_int<>: invalid type");
return llvm::ConstantInt::get(llvm_value_t<T>::get_type(ir->getContext()), val, ForceSigned || llvm_value_t<T>::is_sint);
}
};
template <typename A1, typename A2, typename T = llvm_common_t<A1, A2>>
struct llvm_add
{
using type = T;
llvm_expr_t<A1> a1;
llvm_expr_t<A2> a2;
static_assert(llvm_value_t<T>::is_sint || llvm_value_t<T>::is_uint || llvm_value_t<T>::is_float, "llvm_add<>: invalid type");
llvm::Value* eval(llvm::IRBuilder<>* ir) const llvm::Value* eval(llvm::IRBuilder<>* ir) const
{ {
const auto v1 = a1.eval(ir); const auto v1 = a1.eval(ir);
const auto v2 = a2.eval(ir); const auto v2 = a2.eval(ir);
if (llvm_value_t<T>::is_int) if constexpr (llvm_value_t<T>::is_int)
{ {
return ir->CreateAdd(v1, v2); return ir->CreateAdd(v1, v2);
} }
if (llvm_value_t<T>::is_float) if constexpr (llvm_value_t<T>::is_float)
{ {
return ir->CreateFAdd(v1, v2); return ir->CreateFAdd(v1, v2);
} }
} }
}; };
template <typename T1, typename T2, typename = decltype(std::declval<T1>().eval(0)), typename = decltype(std::declval<T2>().eval(0)), typename = std::enable_if_t<std::is_same<typename T1::type, typename T2::type>::value>> template <typename T1, typename T2>
inline llvm_add_t<typename T1::type, T1, T2> operator +(T1 a1, T2 a2) inline llvm_add<T1, T2> operator +(T1&& a1, T2&& a2)
{ {
return {a1, a2}; return {a1, a2};
} }
template <typename T, typename A1> template <typename T1>
struct llvm_add_const_t inline llvm_add<T1, llvm_const_int<typename is_llvm_expr<T1>::type>> operator +(T1&& a1, u64 c)
{
return {a1, {c}};
}
template <typename A1, typename A2, typename A3, typename T = llvm_common_t<A1, A2, A3>>
struct llvm_sum
{ {
using type = T; using type = T;
A1 a1; llvm_expr_t<A1> a1;
u64 c; llvm_expr_t<A2> a2;
llvm_expr_t<A3> a3;
static_assert(llvm_value_t<T>::is_sint || llvm_value_t<T>::is_uint, "llvm_add_const_t<>: invalid type"); static_assert(llvm_value_t<T>::is_sint || llvm_value_t<T>::is_uint, "llvm_sum<>: invalid_type");
llvm::Value* eval(llvm::IRBuilder<>* ir) const llvm::Value* eval(llvm::IRBuilder<>* ir) const
{ {
return ir->CreateAdd(a1.eval(ir), llvm::ConstantInt::get(llvm_value_t<T>::get_type(ir->getContext()), c, llvm_value_t<T>::is_sint)); const auto v1 = a1.eval(ir);
const auto v2 = a2.eval(ir);
const auto v3 = a3.eval(ir);
if constexpr (llvm_value_t<T>::is_int)
{
return ir->CreateAdd(ir->CreateAdd(v1, v2), v3);
}
} }
}; };
template <typename T1, typename = decltype(std::declval<T1>().eval(0)), typename = std::enable_if_t<llvm_value_t<typename T1::type>::is_int>> template <typename T1, typename T2, typename T3>
inline llvm_add_const_t<typename T1::type, T1> operator +(T1 a1, u64 c) llvm_sum(T1&& a1, T2&& a2, T3&& a3) -> llvm_sum<T1, T2, T3>;
{
return {a1, c};
}
template <typename T, typename A1, typename A2> template <typename A1, typename A2, typename T = llvm_common_t<A1, A2>>
struct llvm_sub_t struct llvm_sub
{ {
using type = T; using type = T;
A1 a1; llvm_expr_t<A1> a1;
A2 a2; llvm_expr_t<A2> a2;
static_assert(llvm_value_t<T>::is_sint || llvm_value_t<T>::is_uint || llvm_value_t<T>::is_float, "llvm_sub_t<>: invalid type"); static_assert(llvm_value_t<T>::is_sint || llvm_value_t<T>::is_uint || llvm_value_t<T>::is_float, "llvm_sub<>: invalid type");
llvm::Value* eval(llvm::IRBuilder<>* ir) const llvm::Value* eval(llvm::IRBuilder<>* ir) const
{ {
const auto v1 = a1.eval(ir); const auto v1 = a1.eval(ir);
const auto v2 = a2.eval(ir); const auto v2 = a2.eval(ir);
if (llvm_value_t<T>::is_int) if constexpr (llvm_value_t<T>::is_int)
{ {
return ir->CreateSub(v1, v2); return ir->CreateSub(v1, v2);
} }
if (llvm_value_t<T>::is_float) if constexpr (llvm_value_t<T>::is_float)
{ {
return ir->CreateFSub(v1, v2); return ir->CreateFSub(v1, v2);
} }
} }
}; };
template <typename T1, typename T2, typename = decltype(std::declval<T1>().eval(0)), typename = decltype(std::declval<T2>().eval(0)), typename = std::enable_if_t<std::is_same<typename T1::type, typename T2::type>::value>> template <typename T1, typename T2>
inline llvm_sub_t<typename T1::type, T1, T2> operator -(T1 a1, T2 a2) inline llvm_sub<T1, T2> operator -(T1&& a1, T2&& a2)
{ {
return {a1, a2}; return {a1, a2};
} }
template <typename T, typename A1> template <typename T1>
struct llvm_sub_const_t inline llvm_sub<T1, llvm_const_int<typename is_llvm_expr<T1>::type>> operator -(T1&& a1, u64 c)
{ {
using type = T; return {a1, {c}};
A1 a1;
u64 c;
static_assert(llvm_value_t<T>::is_sint || llvm_value_t<T>::is_uint, "llvm_sub_const_t<>: invalid type");
llvm::Value* eval(llvm::IRBuilder<>* ir) const
{
return ir->CreateSub(a1.eval(ir), llvm::ConstantInt::get(llvm_value_t<T>::get_type(ir->getContext()), c, llvm_value_t<T>::is_sint));
}
};
template <typename T1, typename = decltype(std::declval<T1>().eval(0)), typename = std::enable_if_t<llvm_value_t<typename T1::type>::is_int>>
inline llvm_sub_const_t<typename T1::type, T1> operator -(T1 a1, u64 c)
{
return {a1, c};
} }
template <typename T, typename A1> template <typename T1>
struct llvm_const_sub_t inline llvm_sub<llvm_const_int<typename is_llvm_expr<T1>::type>, T1> operator -(u64 c, T1&& a1)
{ {
using type = T; return {{c}, a1};
A1 a1;
u64 c;
static_assert(llvm_value_t<T>::is_sint || llvm_value_t<T>::is_uint, "llvm_const_sub_t<>: invalid type");
llvm::Value* eval(llvm::IRBuilder<>* ir) const
{
return ir->CreateSub(llvm::ConstantInt::get(llvm_value_t<T>::get_type(ir->getContext()), c, llvm_value_t<T>::is_sint), a1.eval(ir));
}
};
template <typename T1, typename = decltype(std::declval<T1>().eval(0)), typename = std::enable_if_t<llvm_value_t<typename T1::type>::is_int>>
inline llvm_const_sub_t<typename T1::type, T1> operator -(u64 c, T1 a1)
{
return {a1, c};
} }
template <typename T, typename A1, typename A2> template <typename A1, typename A2, typename T = llvm_common_t<A1, A2>>
struct llvm_mul_t struct llvm_mul
{ {
using type = T; using type = T;
A1 a1; llvm_expr_t<A1> a1;
A2 a2; llvm_expr_t<A2> a2;
static_assert(llvm_value_t<T>::is_sint || llvm_value_t<T>::is_uint || llvm_value_t<T>::is_float, "llvm_mul_t<>: invalid type"); static_assert(llvm_value_t<T>::is_sint || llvm_value_t<T>::is_uint || llvm_value_t<T>::is_float, "llvm_mul<>: invalid type");
llvm::Value* eval(llvm::IRBuilder<>* ir) const llvm::Value* eval(llvm::IRBuilder<>* ir) const
{ {
const auto v1 = a1.eval(ir); const auto v1 = a1.eval(ir);
const auto v2 = a2.eval(ir); const auto v2 = a2.eval(ir);
if (llvm_value_t<T>::is_int) if constexpr (llvm_value_t<T>::is_int)
{ {
return ir->CreateMul(v1, v2); return ir->CreateMul(v1, v2);
} }
if (llvm_value_t<T>::is_float) if constexpr (llvm_value_t<T>::is_float)
{ {
return ir->CreateFMul(v1, v2); return ir->CreateFMul(v1, v2);
} }
} }
}; };
template <typename T1, typename T2, typename = decltype(std::declval<T1>().eval(0)), typename = decltype(std::declval<T2>().eval(0)), typename = std::enable_if_t<std::is_same<typename T1::type, typename T2::type>::value>> template <typename T1, typename T2>
inline llvm_mul_t<typename T1::type, T1, T2> operator *(T1 a1, T2 a2) inline llvm_mul<T1, T2> operator *(T1&& a1, T2&& a2)
{ {
return {a1, a2}; return {a1, a2};
} }
template <typename T, typename A1, typename A2> template <typename A1, typename A2, typename T = llvm_common_t<A1, A2>>
struct llvm_div_t struct llvm_div
{ {
using type = T; using type = T;
A1 a1; llvm_expr_t<A1> a1;
A2 a2; llvm_expr_t<A2> a2;
static_assert(llvm_value_t<T>::is_sint || llvm_value_t<T>::is_uint || llvm_value_t<T>::is_float, "llvm_div_t<>: invalid type"); static_assert(llvm_value_t<T>::is_sint || llvm_value_t<T>::is_uint || llvm_value_t<T>::is_float, "llvm_div<>: invalid type");
llvm::Value* eval(llvm::IRBuilder<>* ir) const llvm::Value* eval(llvm::IRBuilder<>* ir) const
{ {
const auto v1 = a1.eval(ir); const auto v1 = a1.eval(ir);
const auto v2 = a2.eval(ir); const auto v2 = a2.eval(ir);
if (llvm_value_t<T>::is_sint) if constexpr (llvm_value_t<T>::is_sint)
{ {
return ir->CreateSDiv(v1, v2); return ir->CreateSDiv(v1, v2);
} }
if (llvm_value_t<T>::is_uint) if constexpr (llvm_value_t<T>::is_uint)
{ {
return ir->CreateUDiv(v1, v2); return ir->CreateUDiv(v1, v2);
} }
if (llvm_value_t<T>::is_float) if constexpr (llvm_value_t<T>::is_float)
{ {
return ir->CreateFDiv(v1, v2); return ir->CreateFDiv(v1, v2);
} }
} }
}; };
template <typename T1, typename T2, typename = decltype(std::declval<T1>().eval(0)), typename = decltype(std::declval<T2>().eval(0)), typename = std::enable_if_t<std::is_same<typename T1::type, typename T2::type>::value>> template <typename T1, typename T2>
inline llvm_div_t<typename T1::type, T1, T2> operator /(T1 a1, T2 a2) inline llvm_div<T1, T2> operator /(T1&& a1, T2&& a2)
{ {
return {a1, a2}; return {a1, a2};
} }
template <typename T, typename A1> template <typename A1, typename T = llvm_common_t<A1>>
struct llvm_neg_t struct llvm_neg
{ {
using type = T; using type = T;
A1 a1; llvm_expr_t<A1> a1;
static_assert(llvm_value_t<T>::is_sint || llvm_value_t<T>::is_uint || llvm_value_t<T>::is_float, "llvm_neg_t<>: invalid type"); static_assert(llvm_value_t<T>::is_sint || llvm_value_t<T>::is_uint || llvm_value_t<T>::is_float, "llvm_neg<>: invalid type");
llvm::Value* eval(llvm::IRBuilder<>* ir) const llvm::Value* eval(llvm::IRBuilder<>* ir) const
{ {
const auto v1 = a1.eval(ir); const auto v1 = a1.eval(ir);
if (llvm_value_t<T>::is_int) if constexpr (llvm_value_t<T>::is_int)
{ {
return ir->CreateNeg(v1); return ir->CreateNeg(v1);
} }
if (llvm_value_t<T>::is_float) if constexpr (llvm_value_t<T>::is_float)
{ {
return ir->CreateFNeg(v1); return ir->CreateFNeg(v1);
} }
} }
}; };
template <typename T1, typename = decltype(std::declval<T1>().eval(0)), typename = std::enable_if_t<(llvm_value_t<typename T1::type>::esize > 1)>> template <typename T1>
inline llvm_neg_t<typename T1::type, T1> operator -(T1 a1) inline llvm_neg<T1> operator -(T1 a1)
{ {
return {a1}; return {a1};
} }
// Constant int helper template <typename A1, typename A2, typename T = llvm_common_t<A1, A2>>
struct llvm_int_t struct llvm_shl
{
u64 value;
u64 eval(llvm::IRBuilder<>*) const
{
return value;
}
};
template <typename T, typename A1, typename A2>
struct llvm_shl_t
{ {
using type = T; using type = T;
A1 a1; llvm_expr_t<A1> a1;
A2 a2; llvm_expr_t<A2> a2;
static_assert(llvm_value_t<T>::is_sint || llvm_value_t<T>::is_uint, "llvm_shl_t<>: invalid type"); static_assert(llvm_value_t<T>::is_sint || llvm_value_t<T>::is_uint, "llvm_shl<>: invalid type");
llvm::Value* eval(llvm::IRBuilder<>* ir) const llvm::Value* eval(llvm::IRBuilder<>* ir) const
{ {
const auto v1 = a1.eval(ir); const auto v1 = a1.eval(ir);
const auto v2 = a2.eval(ir); const auto v2 = a2.eval(ir);
if (llvm_value_t<T>::is_sint) if constexpr (llvm_value_t<T>::is_sint)
{ {
return ir->CreateShl(v1, v2); return ir->CreateShl(v1, v2);
} }
if (llvm_value_t<T>::is_uint) if constexpr (llvm_value_t<T>::is_uint)
{ {
return ir->CreateShl(v1, v2); return ir->CreateShl(v1, v2);
} }
} }
}; };
template <typename T1, typename T2, typename = decltype(std::declval<T1>().eval(0)), typename = decltype(std::declval<T2>().eval(0)), typename = std::enable_if_t<std::is_same<typename T1::type, typename T2::type>::value>> template <typename T1, typename T2>
inline llvm_shl_t<typename T1::type, T1, T2> operator <<(T1 a1, T2 a2) inline llvm_shl<T1, T2> operator <<(T1&& a1, T2&& a2)
{ {
return {a1, a2}; return {a1, a2};
} }
template <typename T1, typename = decltype(std::declval<T1>().eval(0)), typename = std::enable_if_t<llvm_value_t<typename T1::type>::is_int>> template <typename T1>
inline llvm_shl_t<typename T1::type, T1, llvm_int_t> operator <<(T1 a1, u64 a2) inline llvm_shl<T1, llvm_const_int<typename is_llvm_expr<T1>::type>> operator <<(T1&& a1, u64 c)
{ {
return {a1, llvm_int_t{a2}}; return {a1, {c}};
} }
template <typename T, typename A1, typename A2> template <typename A1, typename A2, typename T = llvm_common_t<A1, A2>>
struct llvm_shr_t struct llvm_shr
{ {
using type = T; using type = T;
A1 a1; llvm_expr_t<A1> a1;
A2 a2; llvm_expr_t<A2> a2;
static_assert(llvm_value_t<T>::is_sint || llvm_value_t<T>::is_uint, "llvm_shr_t<>: invalid type"); static_assert(llvm_value_t<T>::is_sint || llvm_value_t<T>::is_uint, "llvm_shr<>: invalid type");
llvm::Value* eval(llvm::IRBuilder<>* ir) const llvm::Value* eval(llvm::IRBuilder<>* ir) const
{ {
const auto v1 = a1.eval(ir); const auto v1 = a1.eval(ir);
const auto v2 = a2.eval(ir); const auto v2 = a2.eval(ir);
if (llvm_value_t<T>::is_sint) if constexpr (llvm_value_t<T>::is_sint)
{ {
return ir->CreateAShr(v1, v2); return ir->CreateAShr(v1, v2);
} }
if (llvm_value_t<T>::is_uint) if constexpr (llvm_value_t<T>::is_uint)
{ {
return ir->CreateLShr(v1, v2); return ir->CreateLShr(v1, v2);
} }
} }
}; };
template <typename T1, typename T2, typename = decltype(std::declval<T1>().eval(0)), typename = decltype(std::declval<T2>().eval(0)), typename = std::enable_if_t<std::is_same<typename T1::type, typename T2::type>::value>> template <typename T1, typename T2>
inline llvm_shr_t<typename T1::type, T1, T2> operator >>(T1 a1, T2 a2) inline llvm_shr<T1, T2> operator >>(T1&& a1, T2&& a2)
{ {
return {a1, a2}; return {a1, a2};
} }
template <typename T1, typename = decltype(std::declval<T1>().eval(0)), typename = std::enable_if_t<llvm_value_t<typename T1::type>::is_int>> template <typename T1>
inline llvm_shr_t<typename T1::type, T1, llvm_int_t> operator >>(T1 a1, u64 a2) inline llvm_shr<T1, llvm_const_int<typename is_llvm_expr<T1>::type>> operator >>(T1&& a1, u64 c)
{ {
return {a1, llvm_int_t{a2}}; return {a1, {c}};
} }
template <typename T, typename A1, typename A2> template <typename A1, typename A2, typename T = llvm_common_t<A1, A2>>
struct llvm_and_t struct llvm_and
{ {
using type = T; using type = T;
A1 a1; llvm_expr_t<A1> a1;
A2 a2; llvm_expr_t<A2> a2;
static_assert(llvm_value_t<T>::is_int, "llvm_and_t<>: invalid type"); static_assert(llvm_value_t<T>::is_int, "llvm_and<>: invalid type");
llvm::Value* eval(llvm::IRBuilder<>* ir) const llvm::Value* eval(llvm::IRBuilder<>* ir) const
{ {
const auto v1 = a1.eval(ir); const auto v1 = a1.eval(ir);
const auto v2 = a2.eval(ir); const auto v2 = a2.eval(ir);
if (llvm_value_t<T>::is_int) if constexpr (llvm_value_t<T>::is_int)
{ {
return ir->CreateAnd(v1, v2); return ir->CreateAnd(v1, v2);
} }
} }
}; };
template <typename T1, typename T2, typename = decltype(std::declval<T1>().eval(0)), typename = decltype(std::declval<T2>().eval(0)), typename = std::enable_if_t<std::is_same<typename T1::type, typename T2::type>::value>> template <typename T1, typename T2>
inline llvm_and_t<typename T1::type, T1, T2> operator &(T1 a1, T2 a2) inline llvm_and<T1, T2> operator &(T1&& a1, T2&& a2)
{ {
return {a1, a2}; return {a1, a2};
} }
template <typename T1, typename = decltype(std::declval<T1>().eval(0)), typename = std::enable_if_t<llvm_value_t<typename T1::type>::is_int>> template <typename T1>
inline llvm_and_t<typename T1::type, T1, llvm_int_t> operator &(T1 a1, u64 a2) inline llvm_and<T1, llvm_const_int<typename is_llvm_expr<T1>::type>> operator &(T1&& a1, u64 c)
{ {
return {a1, llvm_int_t{a2}}; return {a1, {c}};
} }
template <typename T, typename A1, typename A2> template <typename A1, typename A2, typename T = llvm_common_t<A1, A2>>
struct llvm_or_t struct llvm_or
{ {
using type = T; using type = T;
A1 a1; llvm_expr_t<A1> a1;
A2 a2; llvm_expr_t<A2> a2;
static_assert(llvm_value_t<T>::is_int, "llvm_or_t<>: invalid type"); static_assert(llvm_value_t<T>::is_int, "llvm_or<>: invalid type");
llvm::Value* eval(llvm::IRBuilder<>* ir) const llvm::Value* eval(llvm::IRBuilder<>* ir) const
{ {
const auto v1 = a1.eval(ir); const auto v1 = a1.eval(ir);
const auto v2 = a2.eval(ir); const auto v2 = a2.eval(ir);
if (llvm_value_t<T>::is_int) if constexpr (llvm_value_t<T>::is_int)
{ {
return ir->CreateOr(v1, v2); return ir->CreateOr(v1, v2);
} }
} }
}; };
template <typename T1, typename T2, typename = decltype(std::declval<T1>().eval(0)), typename = decltype(std::declval<T2>().eval(0)), typename = std::enable_if_t<std::is_same<typename T1::type, typename T2::type>::value>> template <typename T1, typename T2>
inline llvm_or_t<typename T1::type, T1, T2> operator |(T1 a1, T2 a2) inline llvm_or<T1, T2> operator |(T1&& a1, T2&& a2)
{ {
return {a1, a2}; return {a1, a2};
} }
template <typename T1, typename = decltype(std::declval<T1>().eval(0)), typename = std::enable_if_t<llvm_value_t<typename T1::type>::is_int>> template <typename T1>
inline llvm_or_t<typename T1::type, T1, llvm_int_t> operator |(T1 a1, u64 a2) inline llvm_or<T1, llvm_const_int<typename is_llvm_expr<T1>::type>> operator |(T1&& a1, u64 c)
{ {
return {a1, llvm_int_t{a2}}; return {a1, {c}};
} }
template <typename T, typename A1, typename A2> template <typename A1, typename A2, typename T = llvm_common_t<A1, A2>>
struct llvm_xor_t struct llvm_xor
{ {
using type = T; using type = T;
A1 a1; llvm_expr_t<A1> a1;
A2 a2; llvm_expr_t<A2> a2;
static_assert(llvm_value_t<T>::is_int, "llvm_xor_t<>: invalid type"); static_assert(llvm_value_t<T>::is_int, "llvm_xor<>: invalid type");
llvm::Value* eval(llvm::IRBuilder<>* ir) const llvm::Value* eval(llvm::IRBuilder<>* ir) const
{ {
const auto v1 = a1.eval(ir); const auto v1 = a1.eval(ir);
const auto v2 = a2.eval(ir); const auto v2 = a2.eval(ir);
if (llvm_value_t<T>::is_int) if constexpr (llvm_value_t<T>::is_int)
{ {
return ir->CreateXor(v1, v2); return ir->CreateXor(v1, v2);
} }
} }
}; };
template <typename T1, typename T2, typename = decltype(std::declval<T1>().eval(0)), typename = decltype(std::declval<T2>().eval(0)), typename = std::enable_if_t<std::is_same<typename T1::type, typename T2::type>::value>> template <typename T1, typename T2>
inline llvm_xor_t<typename T1::type, T1, T2> operator ^(T1 a1, T2 a2) inline llvm_xor<T1, T2> operator ^(T1&& a1, T2&& a2)
{ {
return {a1, a2}; return {a1, a2};
} }
template <typename T1, typename = decltype(std::declval<T1>().eval(0)), typename = std::enable_if_t<llvm_value_t<typename T1::type>::is_int>> template <typename T1>
inline llvm_xor_t<typename T1::type, T1, llvm_int_t> operator ^(T1 a1, u64 a2) inline llvm_xor<T1, llvm_const_int<typename is_llvm_expr<T1>::type>> operator ^(T1&& a1, u64 c)
{ {
return {a1, llvm_int_t{a2}}; return {a1, {c}};
} }
template <typename T, typename A1> template <typename T1>
struct llvm_not_t inline llvm_xor<T1, llvm_const_int<typename is_llvm_expr<T1>::type, true>> operator ~(T1&& a1)
{ {
using type = T; return {a1, {UINT64_MAX}};
A1 a1;
static_assert(llvm_value_t<T>::is_int, "llvm_not_t<>: invalid type");
llvm::Value* eval(llvm::IRBuilder<>* ir) const
{
const auto v1 = a1.eval(ir);
if (llvm_value_t<T>::is_int)
{
return ir->CreateNot(v1);
}
}
};
template <typename T1, typename = decltype(std::declval<T1>().eval(0)), typename = std::enable_if_t<llvm_value_t<typename T1::type>::is_int>>
inline llvm_not_t<typename T1::type, T1> operator ~(T1 a1)
{
return {a1};
} }
template <typename T, typename A1, typename A2, llvm::CmpInst::Predicate UPred> template <typename A1, typename A2, llvm::CmpInst::Predicate UPred, typename T = llvm_common_t<A1, A2>>
struct llvm_icmp_t struct llvm_cmp
{ {
using type = std::conditional_t<llvm_value_t<T>::is_vector != 0, bool[llvm_value_t<T>::is_vector], bool>; using type = std::conditional_t<llvm_value_t<T>::is_vector != 0, bool[llvm_value_t<T>::is_vector], bool>;
A1 a1; static constexpr bool is_float = llvm_value_t<T>::is_float;
A2 a2;
static_assert(llvm_value_t<T>::is_int, "llvm_eq_t<>: invalid type"); llvm_expr_t<A1> a1;
llvm_expr_t<A2> a2;
static_assert(llvm_value_t<T>::is_int || is_float, "llvm_cmp<>: invalid type");
// Convert unsigned comparison predicate to signed if necessary // Convert unsigned comparison predicate to signed if necessary
static constexpr llvm::CmpInst::Predicate pred = llvm_value_t<T>::is_uint ? UPred : static constexpr llvm::CmpInst::Predicate pred = llvm_value_t<T>::is_uint ? UPred :
@ -793,100 +787,162 @@ struct llvm_icmp_t
UPred == llvm::ICmpInst::ICMP_ULT ? llvm::ICmpInst::ICMP_SLT : UPred == llvm::ICmpInst::ICMP_ULT ? llvm::ICmpInst::ICMP_SLT :
UPred == llvm::ICmpInst::ICMP_ULE ? llvm::ICmpInst::ICMP_SLE : UPred; UPred == llvm::ICmpInst::ICMP_ULE ? llvm::ICmpInst::ICMP_SLE : UPred;
static_assert(llvm_value_t<T>::is_sint || llvm_value_t<T>::is_uint || UPred == llvm::ICmpInst::ICMP_EQ || UPred == llvm::ICmpInst::ICMP_NE, "llvm_eq_t<>: invalid type(II)"); static_assert(llvm_value_t<T>::is_sint || llvm_value_t<T>::is_uint || is_float || UPred == llvm::ICmpInst::ICMP_EQ || UPred == llvm::ICmpInst::ICMP_NE, "llvm_cmp<>: invalid operation on sign-undefined type");
static inline llvm::Value* icmp(llvm::IRBuilder<>* ir, llvm::Value* lhs, llvm::Value* rhs)
{
return ir->CreateICmp(pred, lhs, rhs);
}
static inline llvm::Value* icmp(llvm::IRBuilder<>* ir, llvm::Value* lhs, u64 value)
{
return ir->CreateICmp(pred, lhs, llvm::ConstantInt::get(llvm_value_t<T>::get_type(ir->getContext()), value, llvm_value_t<T>::is_sint));
}
llvm::Value* eval(llvm::IRBuilder<>* ir) const llvm::Value* eval(llvm::IRBuilder<>* ir) const
{ {
static_assert(!is_float, "llvm_cmp<>: invalid operation (missing fcmp_ord or fcmp_uno)");
const auto v1 = a1.eval(ir); const auto v1 = a1.eval(ir);
const auto v2 = a2.eval(ir); const auto v2 = a2.eval(ir);
if (llvm_value_t<T>::is_int) if constexpr (llvm_value_t<T>::is_int)
{ {
return icmp(ir, v1, v2); return ir->CreateICmp(pred, v1, v2);
} }
} }
}; };
template <typename T1, typename T2, typename = decltype(std::declval<T1>().eval(0)), typename = decltype(std::declval<T2>().eval(0)), typename = std::enable_if_t<std::is_same<typename T1::type, typename T2::type>::value>> template <typename T>
inline llvm_icmp_t<typename T1::type, T1, T2, llvm::ICmpInst::ICMP_EQ> operator ==(T1 a1, T2 a2) struct is_llvm_cmp : std::bool_constant<false>
{
};
template <typename A1, typename A2, auto UPred, typename T>
struct is_llvm_cmp<llvm_cmp<A1, A2, UPred, T>> : std::bool_constant<true>
{
};
template <typename Cmp, typename T = llvm_common_t<Cmp>>
struct llvm_ord
{
using base = std::decay_t<Cmp>;
using type = typename base::type;
llvm_expr_t<Cmp> cmp;
// Convert comparison predicate to ordered
static constexpr llvm::CmpInst::Predicate pred =
base::pred == llvm::ICmpInst::ICMP_EQ ? llvm::ICmpInst::FCMP_OEQ :
base::pred == llvm::ICmpInst::ICMP_NE ? llvm::ICmpInst::FCMP_ONE :
base::pred == llvm::ICmpInst::ICMP_SGT ? llvm::ICmpInst::FCMP_OGT :
base::pred == llvm::ICmpInst::ICMP_SGE ? llvm::ICmpInst::FCMP_OGE :
base::pred == llvm::ICmpInst::ICMP_SLT ? llvm::ICmpInst::FCMP_OLT :
base::pred == llvm::ICmpInst::ICMP_SLE ? llvm::ICmpInst::FCMP_OLE : base::pred;
static_assert(base::is_float, "llvm_ord<>: invalid type");
llvm::Value* eval(llvm::IRBuilder<>* ir) const
{
const auto v1 = cmp.a1.eval(ir);
const auto v2 = cmp.a2.eval(ir);
return ir->CreateFCmp(pred, v1, v2);
}
};
template <typename T>
llvm_ord(T&&) -> llvm_ord<std::enable_if_t<is_llvm_cmp<std::decay_t<T>>::value, T&&>>;
template <typename Cmp, typename T = llvm_common_t<Cmp>>
struct llvm_uno
{
using base = std::decay_t<Cmp>;
using type = typename base::type;
llvm_expr_t<Cmp> cmp;
// Convert comparison predicate to unordered
static constexpr llvm::CmpInst::Predicate pred =
base::pred == llvm::ICmpInst::ICMP_EQ ? llvm::ICmpInst::FCMP_UEQ :
base::pred == llvm::ICmpInst::ICMP_NE ? llvm::ICmpInst::FCMP_UNE :
base::pred == llvm::ICmpInst::ICMP_SGT ? llvm::ICmpInst::FCMP_UGT :
base::pred == llvm::ICmpInst::ICMP_SGE ? llvm::ICmpInst::FCMP_UGE :
base::pred == llvm::ICmpInst::ICMP_SLT ? llvm::ICmpInst::FCMP_ULT :
base::pred == llvm::ICmpInst::ICMP_SLE ? llvm::ICmpInst::FCMP_ULE : base::pred;
static_assert(base::is_float, "llvm_uno<>: invalid type");
llvm::Value* eval(llvm::IRBuilder<>* ir) const
{
const auto v1 = cmp.a1.eval(ir);
const auto v2 = cmp.a2.eval(ir);
return ir->CreateFCmp(pred, v1, v2);
}
};
template <typename T>
llvm_uno(T&&) -> llvm_uno<std::enable_if_t<is_llvm_cmp<std::decay_t<T>>::value, T&&>>;
template <typename T1, typename T2>
inline llvm_cmp<T1, T2, llvm::ICmpInst::ICMP_EQ> operator ==(T1&& a1, T2&& a2)
{ {
return {a1, a2}; return {a1, a2};
} }
template <typename T1, typename = decltype(std::declval<T1>().eval(0)), typename = std::enable_if_t<llvm_value_t<typename T1::type>::is_int>> template <typename T1>
inline llvm_icmp_t<typename T1::type, T1, llvm_int_t, llvm::ICmpInst::ICMP_EQ> operator ==(T1 a1, u64 a2) inline llvm_cmp<T1, llvm_const_int<typename is_llvm_expr<T1>::type>, llvm::ICmpInst::ICMP_EQ> operator ==(T1&& a1, u64 c)
{ {
return {a1, llvm_int_t{a2}}; return {a1, {c}};
} }
template <typename T1, typename T2, typename = decltype(std::declval<T1>().eval(0)), typename = decltype(std::declval<T2>().eval(0)), typename = std::enable_if_t<std::is_same<typename T1::type, typename T2::type>::value>> template <typename T1, typename T2>
inline llvm_icmp_t<typename T1::type, T1, T2, llvm::ICmpInst::ICMP_NE> operator !=(T1 a1, T2 a2) inline llvm_cmp<T1, T2, llvm::ICmpInst::ICMP_NE> operator !=(T1&& a1, T2&& a2)
{ {
return {a1, a2}; return {a1, a2};
} }
template <typename T1, typename = decltype(std::declval<T1>().eval(0)), typename = std::enable_if_t<llvm_value_t<typename T1::type>::is_int>> template <typename T1>
inline llvm_icmp_t<typename T1::type, T1, llvm_int_t, llvm::ICmpInst::ICMP_NE> operator !=(T1 a1, u64 a2) inline llvm_cmp<T1, llvm_const_int<typename is_llvm_expr<T1>::type>, llvm::ICmpInst::ICMP_NE> operator !=(T1&& a1, u64 c)
{ {
return {a1, llvm_int_t{a2}}; return {a1, {c}};
} }
template <typename T1, typename T2, typename = decltype(std::declval<T1>().eval(0)), typename = decltype(std::declval<T2>().eval(0)), typename = std::enable_if_t<std::is_same<typename T1::type, typename T2::type>::value>> template <typename T1, typename T2>
inline llvm_icmp_t<typename T1::type, T1, T2, llvm::ICmpInst::ICMP_UGT> operator >(T1 a1, T2 a2) inline llvm_cmp<T1, T2, llvm::ICmpInst::ICMP_UGT> operator >(T1&& a1, T2&& a2)
{ {
return {a1, a2}; return {a1, a2};
} }
template <typename T1, typename = decltype(std::declval<T1>().eval(0)), typename = std::enable_if_t<llvm_value_t<typename T1::type>::is_int>> template <typename T1>
inline llvm_icmp_t<typename T1::type, T1, llvm_int_t, llvm::ICmpInst::ICMP_UGT> operator >(T1 a1, u64 a2) inline llvm_cmp<T1, llvm_const_int<typename is_llvm_expr<T1>::type>, llvm::ICmpInst::ICMP_UGT> operator >(T1&& a1, u64 c)
{ {
return {a1, llvm_int_t{a2}}; return {a1, {c}};
} }
template <typename T1, typename T2, typename = decltype(std::declval<T1>().eval(0)), typename = decltype(std::declval<T2>().eval(0)), typename = std::enable_if_t<std::is_same<typename T1::type, typename T2::type>::value>> template <typename T1, typename T2>
inline llvm_icmp_t<typename T1::type, T1, T2, llvm::ICmpInst::ICMP_UGE> operator >=(T1 a1, T2 a2) inline llvm_cmp<T1, T2, llvm::ICmpInst::ICMP_UGE> operator >=(T1&& a1, T2&& a2)
{ {
return {a1, a2}; return {a1, a2};
} }
template <typename T1, typename = decltype(std::declval<T1>().eval(0)), typename = std::enable_if_t<llvm_value_t<typename T1::type>::is_int>> template <typename T1>
inline llvm_icmp_t<typename T1::type, T1, llvm_int_t, llvm::ICmpInst::ICMP_UGE> operator >=(T1 a1, u64 a2) inline llvm_cmp<T1, llvm_const_int<typename is_llvm_expr<T1>::type>, llvm::ICmpInst::ICMP_UGE> operator >=(T1&& a1, u64 c)
{ {
return {a1, llvm_int_t{a2}}; return {a1, {c}};
} }
template <typename T1, typename T2, typename = decltype(std::declval<T1>().eval(0)), typename = decltype(std::declval<T2>().eval(0)), typename = std::enable_if_t<std::is_same<typename T1::type, typename T2::type>::value>> template <typename T1, typename T2>
inline llvm_icmp_t<typename T1::type, T1, T2, llvm::ICmpInst::ICMP_ULT> operator <(T1 a1, T2 a2) inline llvm_cmp<T1, T2, llvm::ICmpInst::ICMP_ULT> operator <(T1&& a1, T2&& a2)
{ {
return {a1, a2}; return {a1, a2};
} }
template <typename T1, typename = decltype(std::declval<T1>().eval(0)), typename = std::enable_if_t<llvm_value_t<typename T1::type>::is_int>> template <typename T1>
inline llvm_icmp_t<typename T1::type, T1, llvm_int_t, llvm::ICmpInst::ICMP_ULT> operator <(T1 a1, u64 a2) inline llvm_cmp<T1, llvm_const_int<typename is_llvm_expr<T1>::type>, llvm::ICmpInst::ICMP_ULT> operator <(T1&& a1, u64 c)
{ {
return {a1, llvm_int_t{a2}}; return {a1, {c}};
} }
template <typename T1, typename T2, typename = decltype(std::declval<T1>().eval(0)), typename = decltype(std::declval<T2>().eval(0)), typename = std::enable_if_t<std::is_same<typename T1::type, typename T2::type>::value>> template <typename T1, typename T2>
inline llvm_icmp_t<typename T1::type, T1, T2, llvm::ICmpInst::ICMP_ULE> operator <=(T1 a1, T2 a2) inline llvm_cmp<T1, T2, llvm::ICmpInst::ICMP_ULE> operator <=(T1&& a1, T2&& a2)
{ {
return {a1, a2}; return {a1, a2};
} }
template <typename T1, typename = decltype(std::declval<T1>().eval(0)), typename = std::enable_if_t<llvm_value_t<typename T1::type>::is_int>> template <typename T1>
inline llvm_icmp_t<typename T1::type, T1, llvm_int_t, llvm::ICmpInst::ICMP_ULE> operator <=(T1 a1, u64 a2) inline llvm_cmp<T1, llvm_const_int<typename is_llvm_expr<T1>::type>, llvm::ICmpInst::ICMP_ULE> operator <=(T1&& a1, u64 c)
{ {
return {a1, llvm_int_t{a2}}; return {a1, {c}};
} }
class cpu_translator class cpu_translator
@ -946,13 +1002,25 @@ public:
} }
template <typename T> template <typename T>
auto eval(T expr) auto eval(T&& expr)
{ {
value_t<typename T::type> result; value_t<typename std::decay_t<T>::type> result;
result.value = expr.eval(m_ir); result.value = expr.eval(m_ir);
return result; return result;
} }
template <typename T, typename = std::enable_if_t<is_llvm_cmp<std::decay_t<T>>::value>>
static auto fcmp_ord(T&& cmp_expr)
{
return llvm_ord{std::forward<T>(cmp_expr)};
}
template <typename T, typename = std::enable_if_t<is_llvm_cmp<std::decay_t<T>>::value>>
static auto fcmp_uno(T&& cmp_expr)
{
return llvm_uno{std::forward<T>(cmp_expr)};
}
template <typename T, typename T2> template <typename T, typename T2>
value_t<T> bitcast(T2 expr) value_t<T> bitcast(T2 expr)
{ {
@ -1263,14 +1331,6 @@ public:
return result; return result;
} }
template <llvm::CmpInst::Predicate FPred, typename T>
auto fcmp(T a, T b)
{
value_t<std::conditional_t<llvm_value_t<typename T::type>::is_vector != 0, bool[llvm_value_t<typename T::type>::is_vector], bool>> result;
result.value = m_ir->CreateFCmp(FPred, a.eval(m_ir), b.eval(m_ir));
return result;
}
// Opportunistic hardware FMA, can be used if results are identical for all possible input values // Opportunistic hardware FMA, can be used if results are identical for all possible input values
template <typename T> template <typename T>
auto fmuladd(T a, T b, T c) auto fmuladd(T a, T b, T c)

View File

@ -94,9 +94,9 @@ public:
} }
template <typename T> template <typename T>
void set_vr(u32 vr, value_t<T> v) void set_vr(u32 vr, T&& expr)
{ {
return SetVr(vr, v.value); return SetVr(vr, expr.eval(m_ir));
} }
// Get current instruction address // Get current instruction address

View File

@ -5372,7 +5372,7 @@ public:
void ADDX(spu_opcode_t op) void ADDX(spu_opcode_t op)
{ {
set_vr(op.rt, get_vr(op.ra) + get_vr(op.rb) + (get_vr(op.rt) & 1)); set_vr(op.rt, llvm_sum{get_vr(op.ra), get_vr(op.rb), get_vr(op.rt) & 1});
} }
void SFX(spu_opcode_t op) void SFX(spu_opcode_t op)
@ -5924,7 +5924,7 @@ public:
{ {
if (g_cfg.core.spu_accurate_xfloat) if (g_cfg.core.spu_accurate_xfloat)
{ {
set_vr(op.rt, sext<u32[4]>(fcmp<llvm::FCmpInst::FCMP_OGT>(get_vr<f64[4]>(op.ra), get_vr<f64[4]>(op.rb)))); set_vr(op.rt, sext<u32[4]>(fcmp_ord(get_vr<f64[4]>(op.ra) > get_vr<f64[4]>(op.rb))));
return; return;
} }
@ -5945,7 +5945,7 @@ public:
} }
else else
{ {
set_vr(op.rt, sext<u32[4]>(fcmp<llvm::FCmpInst::FCMP_OGT>(a, b))); set_vr(op.rt, sext<u32[4]>(fcmp_ord(a > b)));
} }
} }
@ -5953,7 +5953,7 @@ public:
{ {
if (g_cfg.core.spu_accurate_xfloat) if (g_cfg.core.spu_accurate_xfloat)
{ {
set_vr(op.rt, sext<u32[4]>(fcmp<llvm::FCmpInst::FCMP_OGT>(fabs(get_vr<f64[4]>(op.ra)), fabs(get_vr<f64[4]>(op.rb))))); set_vr(op.rt, sext<u32[4]>(fcmp_ord(fabs(get_vr<f64[4]>(op.ra)) > fabs(get_vr<f64[4]>(op.rb)))));
return; return;
} }
@ -5973,7 +5973,7 @@ public:
} }
else else
{ {
set_vr(op.rt, sext<u32[4]>(fcmp<llvm::FCmpInst::FCMP_OGT>(abs_a, abs_b))); set_vr(op.rt, sext<u32[4]>(fcmp_ord(abs_a > abs_b)));
} }
} }
@ -6065,17 +6065,17 @@ public:
void FCEQ(spu_opcode_t op) void FCEQ(spu_opcode_t op)
{ {
if (g_cfg.core.spu_accurate_xfloat) if (g_cfg.core.spu_accurate_xfloat)
set_vr(op.rt, sext<u32[4]>(fcmp<llvm::FCmpInst::FCMP_OEQ>(get_vr<f64[4]>(op.ra), get_vr<f64[4]>(op.rb)))); set_vr(op.rt, sext<u32[4]>(fcmp_ord(get_vr<f64[4]>(op.ra) == get_vr<f64[4]>(op.rb))));
else else
set_vr(op.rt, sext<u32[4]>(fcmp<llvm::FCmpInst::FCMP_OEQ>(get_vr<f32[4]>(op.ra), get_vr<f32[4]>(op.rb)))); set_vr(op.rt, sext<u32[4]>(fcmp_ord(get_vr<f32[4]>(op.ra) == get_vr<f32[4]>(op.rb))));
} }
void FCMEQ(spu_opcode_t op) void FCMEQ(spu_opcode_t op)
{ {
if (g_cfg.core.spu_accurate_xfloat) if (g_cfg.core.spu_accurate_xfloat)
set_vr(op.rt, sext<u32[4]>(fcmp<llvm::FCmpInst::FCMP_OEQ>(fabs(get_vr<f64[4]>(op.ra)), fabs(get_vr<f64[4]>(op.rb))))); set_vr(op.rt, sext<u32[4]>(fcmp_ord(fabs(get_vr<f64[4]>(op.ra)) == fabs(get_vr<f64[4]>(op.rb)))));
else else
set_vr(op.rt, sext<u32[4]>(fcmp<llvm::FCmpInst::FCMP_OEQ>(fabs(get_vr<f32[4]>(op.ra)), fabs(get_vr<f32[4]>(op.rb))))); set_vr(op.rt, sext<u32[4]>(fcmp_ord(fabs(get_vr<f32[4]>(op.ra)) == fabs(get_vr<f32[4]>(op.rb)))));
} }
// Multiply and return zero if any of the arguments is in the xfloat range. // Multiply and return zero if any of the arguments is in the xfloat range.
@ -6184,7 +6184,7 @@ public:
} }
r.value = m_ir->CreateFPToSI(a.value, get_type<s32[4]>()); r.value = m_ir->CreateFPToSI(a.value, get_type<s32[4]>());
set_vr(op.rt, r ^ sext<s32[4]>(fcmp<llvm::FCmpInst::FCMP_OGE>(a, fsplat<f64[4]>(std::exp2(31.f))))); set_vr(op.rt, r ^ sext<s32[4]>(fcmp_ord(a >= fsplat<f64[4]>(std::exp2(31.f)))));
} }
else else
{ {
@ -6199,7 +6199,7 @@ public:
value_t<s32[4]> r; value_t<s32[4]> r;
r.value = m_ir->CreateFPToSI(a.value, get_type<s32[4]>()); r.value = m_ir->CreateFPToSI(a.value, get_type<s32[4]>());
set_vr(op.rt, r ^ sext<s32[4]>(fcmp<llvm::FCmpInst::FCMP_OGE>(a, fsplat<f32[4]>(std::exp2(31.f))))); set_vr(op.rt, r ^ sext<s32[4]>(fcmp_ord(a >= fsplat<f32[4]>(std::exp2(31.f)))));
} }
} }
@ -6259,7 +6259,7 @@ public:
const auto _max = fsplat<f64[4]>(std::exp2(32.f)); const auto _max = fsplat<f64[4]>(std::exp2(32.f));
r.value = m_ir->CreateFPToUI(a.value, get_type<s32[4]>()); r.value = m_ir->CreateFPToUI(a.value, get_type<s32[4]>());
r.value = m_ir->CreateSelect(m_ir->CreateFCmpUGE(a.value, _max.value), splat<s32[4]>(-1).eval(m_ir), (r & sext<s32[4]>(fcmp<llvm::FCmpInst::FCMP_OGE>(a, fsplat<f64[4]>(0.)))).eval(m_ir)); r.value = m_ir->CreateSelect(m_ir->CreateFCmpUGE(a.value, _max.value), splat<s32[4]>(-1).eval(m_ir), (r & sext<s32[4]>(fcmp_ord(a >= fsplat<f64[4]>(0.)))).eval(m_ir));
set_vr(op.rt, r); set_vr(op.rt, r);
} }
else else