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:
parent
467ef2afca
commit
dc9118ef50
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user