From dc9118ef503d1fe426f9b0b32aea7941060541a2 Mon Sep 17 00:00:00 2001 From: Nekotekina Date: Wed, 17 Apr 2019 03:00:53 +0300 Subject: [PATCH] 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. --- rpcs3/Emu/CPU/CPUTranslator.h | 546 +++++++++++++++++-------------- rpcs3/Emu/Cell/PPUTranslator.h | 4 +- rpcs3/Emu/Cell/SPURecompiler.cpp | 24 +- 3 files changed, 317 insertions(+), 257 deletions(-) diff --git a/rpcs3/Emu/CPU/CPUTranslator.h b/rpcs3/Emu/CPU/CPUTranslator.h index d574a096d6..44d3dc253f 100644 --- a/rpcs3/Emu/CPU/CPUTranslator.h +++ b/rpcs3/Emu/CPU/CPUTranslator.h @@ -325,466 +325,460 @@ struct llvm_value_t : llvm_value_t } }; -template -struct llvm_add_t +template +using llvm_expr_t = std::decay_t; + +template +struct is_llvm_expr +{ +}; + +template +struct is_llvm_expr().eval(std::declval*>()))>> +{ + using type = typename std::decay_t::type; +}; + +template +struct is_llvm_expr_of +{ + static constexpr bool ok = false; +}; + +template +struct is_llvm_expr_of::type, typename is_llvm_expr::type>> +{ + static constexpr bool ok = std::is_same_v::type, typename is_llvm_expr::type>; +}; + +template +using llvm_common_t = std::enable_if_t<(is_llvm_expr_of::ok && ...), typename is_llvm_expr::type>; + +template +struct llvm_const_int { using type = T; - A1 a1; - A2 a2; + u64 val; - static_assert(llvm_value_t::is_sint || llvm_value_t::is_uint || llvm_value_t::is_float, "llvm_add_t<>: invalid type"); + llvm::Value* eval(llvm::IRBuilder<>* ir) const + { + static_assert(llvm_value_t::is_int, "llvm_const_int<>: invalid type"); + + return llvm::ConstantInt::get(llvm_value_t::get_type(ir->getContext()), val, ForceSigned || llvm_value_t::is_sint); + } +}; + +template > +struct llvm_add +{ + using type = T; + + llvm_expr_t a1; + llvm_expr_t a2; + + static_assert(llvm_value_t::is_sint || llvm_value_t::is_uint || llvm_value_t::is_float, "llvm_add<>: invalid type"); llvm::Value* eval(llvm::IRBuilder<>* ir) const { const auto v1 = a1.eval(ir); const auto v2 = a2.eval(ir); - if (llvm_value_t::is_int) + if constexpr (llvm_value_t::is_int) { return ir->CreateAdd(v1, v2); } - if (llvm_value_t::is_float) + if constexpr (llvm_value_t::is_float) { return ir->CreateFAdd(v1, v2); } } }; -template ().eval(0)), typename = decltype(std::declval().eval(0)), typename = std::enable_if_t::value>> -inline llvm_add_t operator +(T1 a1, T2 a2) +template +inline llvm_add operator +(T1&& a1, T2&& a2) { return {a1, a2}; } -template -struct llvm_add_const_t +template +inline llvm_add::type>> operator +(T1&& a1, u64 c) +{ + return {a1, {c}}; +} + +template > +struct llvm_sum { using type = T; - A1 a1; - u64 c; + llvm_expr_t a1; + llvm_expr_t a2; + llvm_expr_t a3; - static_assert(llvm_value_t::is_sint || llvm_value_t::is_uint, "llvm_add_const_t<>: invalid type"); + static_assert(llvm_value_t::is_sint || llvm_value_t::is_uint, "llvm_sum<>: invalid_type"); llvm::Value* eval(llvm::IRBuilder<>* ir) const { - return ir->CreateAdd(a1.eval(ir), llvm::ConstantInt::get(llvm_value_t::get_type(ir->getContext()), c, llvm_value_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::is_int) + { + return ir->CreateAdd(ir->CreateAdd(v1, v2), v3); + } } }; -template ().eval(0)), typename = std::enable_if_t::is_int>> -inline llvm_add_const_t operator +(T1 a1, u64 c) -{ - return {a1, c}; -} +template +llvm_sum(T1&& a1, T2&& a2, T3&& a3) -> llvm_sum; -template -struct llvm_sub_t +template > +struct llvm_sub { using type = T; - A1 a1; - A2 a2; + llvm_expr_t a1; + llvm_expr_t a2; - static_assert(llvm_value_t::is_sint || llvm_value_t::is_uint || llvm_value_t::is_float, "llvm_sub_t<>: invalid type"); + static_assert(llvm_value_t::is_sint || llvm_value_t::is_uint || llvm_value_t::is_float, "llvm_sub<>: invalid type"); llvm::Value* eval(llvm::IRBuilder<>* ir) const { const auto v1 = a1.eval(ir); const auto v2 = a2.eval(ir); - if (llvm_value_t::is_int) + if constexpr (llvm_value_t::is_int) { return ir->CreateSub(v1, v2); } - if (llvm_value_t::is_float) + if constexpr (llvm_value_t::is_float) { return ir->CreateFSub(v1, v2); } } }; -template ().eval(0)), typename = decltype(std::declval().eval(0)), typename = std::enable_if_t::value>> -inline llvm_sub_t operator -(T1 a1, T2 a2) +template +inline llvm_sub operator -(T1&& a1, T2&& a2) { return {a1, a2}; } -template -struct llvm_sub_const_t +template +inline llvm_sub::type>> operator -(T1&& a1, u64 c) { - using type = T; - - A1 a1; - u64 c; - - static_assert(llvm_value_t::is_sint || llvm_value_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::get_type(ir->getContext()), c, llvm_value_t::is_sint)); - } -}; - -template ().eval(0)), typename = std::enable_if_t::is_int>> -inline llvm_sub_const_t operator -(T1 a1, u64 c) -{ - return {a1, c}; + return {a1, {c}}; } -template -struct llvm_const_sub_t +template +inline llvm_sub::type>, T1> operator -(u64 c, T1&& a1) { - using type = T; - - A1 a1; - u64 c; - - static_assert(llvm_value_t::is_sint || llvm_value_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::get_type(ir->getContext()), c, llvm_value_t::is_sint), a1.eval(ir)); - } -}; - -template ().eval(0)), typename = std::enable_if_t::is_int>> -inline llvm_const_sub_t operator -(u64 c, T1 a1) -{ - return {a1, c}; + return {{c}, a1}; } -template -struct llvm_mul_t +template > +struct llvm_mul { using type = T; - A1 a1; - A2 a2; + llvm_expr_t a1; + llvm_expr_t a2; - static_assert(llvm_value_t::is_sint || llvm_value_t::is_uint || llvm_value_t::is_float, "llvm_mul_t<>: invalid type"); + static_assert(llvm_value_t::is_sint || llvm_value_t::is_uint || llvm_value_t::is_float, "llvm_mul<>: invalid type"); llvm::Value* eval(llvm::IRBuilder<>* ir) const { const auto v1 = a1.eval(ir); const auto v2 = a2.eval(ir); - if (llvm_value_t::is_int) + if constexpr (llvm_value_t::is_int) { return ir->CreateMul(v1, v2); } - if (llvm_value_t::is_float) + if constexpr (llvm_value_t::is_float) { return ir->CreateFMul(v1, v2); } } }; -template ().eval(0)), typename = decltype(std::declval().eval(0)), typename = std::enable_if_t::value>> -inline llvm_mul_t operator *(T1 a1, T2 a2) +template +inline llvm_mul operator *(T1&& a1, T2&& a2) { return {a1, a2}; } -template -struct llvm_div_t +template > +struct llvm_div { using type = T; - A1 a1; - A2 a2; + llvm_expr_t a1; + llvm_expr_t a2; - static_assert(llvm_value_t::is_sint || llvm_value_t::is_uint || llvm_value_t::is_float, "llvm_div_t<>: invalid type"); + static_assert(llvm_value_t::is_sint || llvm_value_t::is_uint || llvm_value_t::is_float, "llvm_div<>: invalid type"); llvm::Value* eval(llvm::IRBuilder<>* ir) const { const auto v1 = a1.eval(ir); const auto v2 = a2.eval(ir); - if (llvm_value_t::is_sint) + if constexpr (llvm_value_t::is_sint) { return ir->CreateSDiv(v1, v2); } - if (llvm_value_t::is_uint) + if constexpr (llvm_value_t::is_uint) { return ir->CreateUDiv(v1, v2); } - if (llvm_value_t::is_float) + if constexpr (llvm_value_t::is_float) { return ir->CreateFDiv(v1, v2); } } }; -template ().eval(0)), typename = decltype(std::declval().eval(0)), typename = std::enable_if_t::value>> -inline llvm_div_t operator /(T1 a1, T2 a2) +template +inline llvm_div operator /(T1&& a1, T2&& a2) { return {a1, a2}; } -template -struct llvm_neg_t +template > +struct llvm_neg { using type = T; - A1 a1; + llvm_expr_t a1; - static_assert(llvm_value_t::is_sint || llvm_value_t::is_uint || llvm_value_t::is_float, "llvm_neg_t<>: invalid type"); + static_assert(llvm_value_t::is_sint || llvm_value_t::is_uint || llvm_value_t::is_float, "llvm_neg<>: invalid type"); llvm::Value* eval(llvm::IRBuilder<>* ir) const { const auto v1 = a1.eval(ir); - if (llvm_value_t::is_int) + if constexpr (llvm_value_t::is_int) { return ir->CreateNeg(v1); } - if (llvm_value_t::is_float) + if constexpr (llvm_value_t::is_float) { return ir->CreateFNeg(v1); } } }; -template ().eval(0)), typename = std::enable_if_t<(llvm_value_t::esize > 1)>> -inline llvm_neg_t operator -(T1 a1) +template +inline llvm_neg operator -(T1 a1) { return {a1}; } -// Constant int helper -struct llvm_int_t -{ - u64 value; - - u64 eval(llvm::IRBuilder<>*) const - { - return value; - } -}; - -template -struct llvm_shl_t +template > +struct llvm_shl { using type = T; - A1 a1; - A2 a2; + llvm_expr_t a1; + llvm_expr_t a2; - static_assert(llvm_value_t::is_sint || llvm_value_t::is_uint, "llvm_shl_t<>: invalid type"); + static_assert(llvm_value_t::is_sint || llvm_value_t::is_uint, "llvm_shl<>: invalid type"); llvm::Value* eval(llvm::IRBuilder<>* ir) const { const auto v1 = a1.eval(ir); const auto v2 = a2.eval(ir); - if (llvm_value_t::is_sint) + if constexpr (llvm_value_t::is_sint) { return ir->CreateShl(v1, v2); } - if (llvm_value_t::is_uint) + if constexpr (llvm_value_t::is_uint) { return ir->CreateShl(v1, v2); } } }; -template ().eval(0)), typename = decltype(std::declval().eval(0)), typename = std::enable_if_t::value>> -inline llvm_shl_t operator <<(T1 a1, T2 a2) +template +inline llvm_shl operator <<(T1&& a1, T2&& a2) { return {a1, a2}; } -template ().eval(0)), typename = std::enable_if_t::is_int>> -inline llvm_shl_t operator <<(T1 a1, u64 a2) +template +inline llvm_shl::type>> operator <<(T1&& a1, u64 c) { - return {a1, llvm_int_t{a2}}; + return {a1, {c}}; } -template -struct llvm_shr_t +template > +struct llvm_shr { using type = T; - A1 a1; - A2 a2; + llvm_expr_t a1; + llvm_expr_t a2; - static_assert(llvm_value_t::is_sint || llvm_value_t::is_uint, "llvm_shr_t<>: invalid type"); + static_assert(llvm_value_t::is_sint || llvm_value_t::is_uint, "llvm_shr<>: invalid type"); llvm::Value* eval(llvm::IRBuilder<>* ir) const { const auto v1 = a1.eval(ir); const auto v2 = a2.eval(ir); - if (llvm_value_t::is_sint) + if constexpr (llvm_value_t::is_sint) { return ir->CreateAShr(v1, v2); } - if (llvm_value_t::is_uint) + if constexpr (llvm_value_t::is_uint) { return ir->CreateLShr(v1, v2); } } }; -template ().eval(0)), typename = decltype(std::declval().eval(0)), typename = std::enable_if_t::value>> -inline llvm_shr_t operator >>(T1 a1, T2 a2) +template +inline llvm_shr operator >>(T1&& a1, T2&& a2) { return {a1, a2}; } -template ().eval(0)), typename = std::enable_if_t::is_int>> -inline llvm_shr_t operator >>(T1 a1, u64 a2) +template +inline llvm_shr::type>> operator >>(T1&& a1, u64 c) { - return {a1, llvm_int_t{a2}}; + return {a1, {c}}; } -template -struct llvm_and_t +template > +struct llvm_and { using type = T; - A1 a1; - A2 a2; + llvm_expr_t a1; + llvm_expr_t a2; - static_assert(llvm_value_t::is_int, "llvm_and_t<>: invalid type"); + static_assert(llvm_value_t::is_int, "llvm_and<>: invalid type"); llvm::Value* eval(llvm::IRBuilder<>* ir) const { const auto v1 = a1.eval(ir); const auto v2 = a2.eval(ir); - if (llvm_value_t::is_int) + if constexpr (llvm_value_t::is_int) { return ir->CreateAnd(v1, v2); } } }; -template ().eval(0)), typename = decltype(std::declval().eval(0)), typename = std::enable_if_t::value>> -inline llvm_and_t operator &(T1 a1, T2 a2) +template +inline llvm_and operator &(T1&& a1, T2&& a2) { return {a1, a2}; } -template ().eval(0)), typename = std::enable_if_t::is_int>> -inline llvm_and_t operator &(T1 a1, u64 a2) +template +inline llvm_and::type>> operator &(T1&& a1, u64 c) { - return {a1, llvm_int_t{a2}}; + return {a1, {c}}; } -template -struct llvm_or_t +template > +struct llvm_or { using type = T; - A1 a1; - A2 a2; + llvm_expr_t a1; + llvm_expr_t a2; - static_assert(llvm_value_t::is_int, "llvm_or_t<>: invalid type"); + static_assert(llvm_value_t::is_int, "llvm_or<>: invalid type"); llvm::Value* eval(llvm::IRBuilder<>* ir) const { const auto v1 = a1.eval(ir); const auto v2 = a2.eval(ir); - if (llvm_value_t::is_int) + if constexpr (llvm_value_t::is_int) { return ir->CreateOr(v1, v2); } } }; -template ().eval(0)), typename = decltype(std::declval().eval(0)), typename = std::enable_if_t::value>> -inline llvm_or_t operator |(T1 a1, T2 a2) +template +inline llvm_or operator |(T1&& a1, T2&& a2) { return {a1, a2}; } -template ().eval(0)), typename = std::enable_if_t::is_int>> -inline llvm_or_t operator |(T1 a1, u64 a2) +template +inline llvm_or::type>> operator |(T1&& a1, u64 c) { - return {a1, llvm_int_t{a2}}; + return {a1, {c}}; } -template -struct llvm_xor_t +template > +struct llvm_xor { using type = T; - A1 a1; - A2 a2; + llvm_expr_t a1; + llvm_expr_t a2; - static_assert(llvm_value_t::is_int, "llvm_xor_t<>: invalid type"); + static_assert(llvm_value_t::is_int, "llvm_xor<>: invalid type"); llvm::Value* eval(llvm::IRBuilder<>* ir) const { const auto v1 = a1.eval(ir); const auto v2 = a2.eval(ir); - if (llvm_value_t::is_int) + if constexpr (llvm_value_t::is_int) { return ir->CreateXor(v1, v2); } } }; -template ().eval(0)), typename = decltype(std::declval().eval(0)), typename = std::enable_if_t::value>> -inline llvm_xor_t operator ^(T1 a1, T2 a2) +template +inline llvm_xor operator ^(T1&& a1, T2&& a2) { return {a1, a2}; } -template ().eval(0)), typename = std::enable_if_t::is_int>> -inline llvm_xor_t operator ^(T1 a1, u64 a2) +template +inline llvm_xor::type>> operator ^(T1&& a1, u64 c) { - return {a1, llvm_int_t{a2}}; + return {a1, {c}}; } -template -struct llvm_not_t +template +inline llvm_xor::type, true>> operator ~(T1&& a1) { - using type = T; - - A1 a1; - - static_assert(llvm_value_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::is_int) - { - return ir->CreateNot(v1); - } - } -}; - -template ().eval(0)), typename = std::enable_if_t::is_int>> -inline llvm_not_t operator ~(T1 a1) -{ - return {a1}; + return {a1, {UINT64_MAX}}; } -template -struct llvm_icmp_t +template > +struct llvm_cmp { using type = std::conditional_t::is_vector != 0, bool[llvm_value_t::is_vector], bool>; - A1 a1; - A2 a2; + static constexpr bool is_float = llvm_value_t::is_float; - static_assert(llvm_value_t::is_int, "llvm_eq_t<>: invalid type"); + llvm_expr_t a1; + llvm_expr_t a2; + + static_assert(llvm_value_t::is_int || is_float, "llvm_cmp<>: invalid type"); // Convert unsigned comparison predicate to signed if necessary static constexpr llvm::CmpInst::Predicate pred = llvm_value_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_ULE ? llvm::ICmpInst::ICMP_SLE : UPred; - static_assert(llvm_value_t::is_sint || llvm_value_t::is_uint || UPred == llvm::ICmpInst::ICMP_EQ || UPred == llvm::ICmpInst::ICMP_NE, "llvm_eq_t<>: invalid type(II)"); - - 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::get_type(ir->getContext()), value, llvm_value_t::is_sint)); - } + static_assert(llvm_value_t::is_sint || llvm_value_t::is_uint || is_float || UPred == llvm::ICmpInst::ICMP_EQ || UPred == llvm::ICmpInst::ICMP_NE, "llvm_cmp<>: invalid operation on sign-undefined type"); 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 v2 = a2.eval(ir); - if (llvm_value_t::is_int) + if constexpr (llvm_value_t::is_int) { - return icmp(ir, v1, v2); + return ir->CreateICmp(pred, v1, v2); } } }; -template ().eval(0)), typename = decltype(std::declval().eval(0)), typename = std::enable_if_t::value>> -inline llvm_icmp_t operator ==(T1 a1, T2 a2) +template +struct is_llvm_cmp : std::bool_constant +{ +}; + +template +struct is_llvm_cmp> : std::bool_constant +{ +}; + +template > +struct llvm_ord +{ + using base = std::decay_t; + using type = typename base::type; + + llvm_expr_t 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 +llvm_ord(T&&) -> llvm_ord>::value, T&&>>; + +template > +struct llvm_uno +{ + using base = std::decay_t; + using type = typename base::type; + + llvm_expr_t 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 +llvm_uno(T&&) -> llvm_uno>::value, T&&>>; + +template +inline llvm_cmp operator ==(T1&& a1, T2&& a2) { return {a1, a2}; } -template ().eval(0)), typename = std::enable_if_t::is_int>> -inline llvm_icmp_t operator ==(T1 a1, u64 a2) +template +inline llvm_cmp::type>, llvm::ICmpInst::ICMP_EQ> operator ==(T1&& a1, u64 c) { - return {a1, llvm_int_t{a2}}; + return {a1, {c}}; } -template ().eval(0)), typename = decltype(std::declval().eval(0)), typename = std::enable_if_t::value>> -inline llvm_icmp_t operator !=(T1 a1, T2 a2) +template +inline llvm_cmp operator !=(T1&& a1, T2&& a2) { return {a1, a2}; } -template ().eval(0)), typename = std::enable_if_t::is_int>> -inline llvm_icmp_t operator !=(T1 a1, u64 a2) +template +inline llvm_cmp::type>, llvm::ICmpInst::ICMP_NE> operator !=(T1&& a1, u64 c) { - return {a1, llvm_int_t{a2}}; + return {a1, {c}}; } -template ().eval(0)), typename = decltype(std::declval().eval(0)), typename = std::enable_if_t::value>> -inline llvm_icmp_t operator >(T1 a1, T2 a2) +template +inline llvm_cmp operator >(T1&& a1, T2&& a2) { return {a1, a2}; } -template ().eval(0)), typename = std::enable_if_t::is_int>> -inline llvm_icmp_t operator >(T1 a1, u64 a2) +template +inline llvm_cmp::type>, llvm::ICmpInst::ICMP_UGT> operator >(T1&& a1, u64 c) { - return {a1, llvm_int_t{a2}}; + return {a1, {c}}; } -template ().eval(0)), typename = decltype(std::declval().eval(0)), typename = std::enable_if_t::value>> -inline llvm_icmp_t operator >=(T1 a1, T2 a2) +template +inline llvm_cmp operator >=(T1&& a1, T2&& a2) { return {a1, a2}; } -template ().eval(0)), typename = std::enable_if_t::is_int>> -inline llvm_icmp_t operator >=(T1 a1, u64 a2) +template +inline llvm_cmp::type>, llvm::ICmpInst::ICMP_UGE> operator >=(T1&& a1, u64 c) { - return {a1, llvm_int_t{a2}}; + return {a1, {c}}; } -template ().eval(0)), typename = decltype(std::declval().eval(0)), typename = std::enable_if_t::value>> -inline llvm_icmp_t operator <(T1 a1, T2 a2) +template +inline llvm_cmp operator <(T1&& a1, T2&& a2) { return {a1, a2}; } -template ().eval(0)), typename = std::enable_if_t::is_int>> -inline llvm_icmp_t operator <(T1 a1, u64 a2) +template +inline llvm_cmp::type>, llvm::ICmpInst::ICMP_ULT> operator <(T1&& a1, u64 c) { - return {a1, llvm_int_t{a2}}; + return {a1, {c}}; } -template ().eval(0)), typename = decltype(std::declval().eval(0)), typename = std::enable_if_t::value>> -inline llvm_icmp_t operator <=(T1 a1, T2 a2) +template +inline llvm_cmp operator <=(T1&& a1, T2&& a2) { return {a1, a2}; } -template ().eval(0)), typename = std::enable_if_t::is_int>> -inline llvm_icmp_t operator <=(T1 a1, u64 a2) +template +inline llvm_cmp::type>, llvm::ICmpInst::ICMP_ULE> operator <=(T1&& a1, u64 c) { - return {a1, llvm_int_t{a2}}; + return {a1, {c}}; } class cpu_translator @@ -946,13 +1002,25 @@ public: } template - auto eval(T expr) + auto eval(T&& expr) { - value_t result; + value_t::type> result; result.value = expr.eval(m_ir); return result; } + template >::value>> + static auto fcmp_ord(T&& cmp_expr) + { + return llvm_ord{std::forward(cmp_expr)}; + } + + template >::value>> + static auto fcmp_uno(T&& cmp_expr) + { + return llvm_uno{std::forward(cmp_expr)}; + } + template value_t bitcast(T2 expr) { @@ -1263,14 +1331,6 @@ public: return result; } - template - auto fcmp(T a, T b) - { - value_t::is_vector != 0, bool[llvm_value_t::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 template auto fmuladd(T a, T b, T c) diff --git a/rpcs3/Emu/Cell/PPUTranslator.h b/rpcs3/Emu/Cell/PPUTranslator.h index 88f3edbe21..beb6017bd8 100644 --- a/rpcs3/Emu/Cell/PPUTranslator.h +++ b/rpcs3/Emu/Cell/PPUTranslator.h @@ -94,9 +94,9 @@ public: } template - void set_vr(u32 vr, value_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 diff --git a/rpcs3/Emu/Cell/SPURecompiler.cpp b/rpcs3/Emu/Cell/SPURecompiler.cpp index 1f62f3e78a..19ab156e2f 100644 --- a/rpcs3/Emu/Cell/SPURecompiler.cpp +++ b/rpcs3/Emu/Cell/SPURecompiler.cpp @@ -5372,7 +5372,7 @@ public: 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) @@ -5924,7 +5924,7 @@ public: { if (g_cfg.core.spu_accurate_xfloat) { - set_vr(op.rt, sext(fcmp(get_vr(op.ra), get_vr(op.rb)))); + set_vr(op.rt, sext(fcmp_ord(get_vr(op.ra) > get_vr(op.rb)))); return; } @@ -5945,7 +5945,7 @@ public: } else { - set_vr(op.rt, sext(fcmp(a, b))); + set_vr(op.rt, sext(fcmp_ord(a > b))); } } @@ -5953,7 +5953,7 @@ public: { if (g_cfg.core.spu_accurate_xfloat) { - set_vr(op.rt, sext(fcmp(fabs(get_vr(op.ra)), fabs(get_vr(op.rb))))); + set_vr(op.rt, sext(fcmp_ord(fabs(get_vr(op.ra)) > fabs(get_vr(op.rb))))); return; } @@ -5973,7 +5973,7 @@ public: } else { - set_vr(op.rt, sext(fcmp(abs_a, abs_b))); + set_vr(op.rt, sext(fcmp_ord(abs_a > abs_b))); } } @@ -6065,17 +6065,17 @@ public: void FCEQ(spu_opcode_t op) { if (g_cfg.core.spu_accurate_xfloat) - set_vr(op.rt, sext(fcmp(get_vr(op.ra), get_vr(op.rb)))); + set_vr(op.rt, sext(fcmp_ord(get_vr(op.ra) == get_vr(op.rb)))); else - set_vr(op.rt, sext(fcmp(get_vr(op.ra), get_vr(op.rb)))); + set_vr(op.rt, sext(fcmp_ord(get_vr(op.ra) == get_vr(op.rb)))); } void FCMEQ(spu_opcode_t op) { if (g_cfg.core.spu_accurate_xfloat) - set_vr(op.rt, sext(fcmp(fabs(get_vr(op.ra)), fabs(get_vr(op.rb))))); + set_vr(op.rt, sext(fcmp_ord(fabs(get_vr(op.ra)) == fabs(get_vr(op.rb))))); else - set_vr(op.rt, sext(fcmp(fabs(get_vr(op.ra)), fabs(get_vr(op.rb))))); + set_vr(op.rt, sext(fcmp_ord(fabs(get_vr(op.ra)) == fabs(get_vr(op.rb))))); } // 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()); - set_vr(op.rt, r ^ sext(fcmp(a, fsplat(std::exp2(31.f))))); + set_vr(op.rt, r ^ sext(fcmp_ord(a >= fsplat(std::exp2(31.f))))); } else { @@ -6199,7 +6199,7 @@ public: value_t r; r.value = m_ir->CreateFPToSI(a.value, get_type()); - set_vr(op.rt, r ^ sext(fcmp(a, fsplat(std::exp2(31.f))))); + set_vr(op.rt, r ^ sext(fcmp_ord(a >= fsplat(std::exp2(31.f))))); } } @@ -6259,7 +6259,7 @@ public: const auto _max = fsplat(std::exp2(32.f)); r.value = m_ir->CreateFPToUI(a.value, get_type()); - r.value = m_ir->CreateSelect(m_ir->CreateFCmpUGE(a.value, _max.value), splat(-1).eval(m_ir), (r & sext(fcmp(a, fsplat(0.)))).eval(m_ir)); + r.value = m_ir->CreateSelect(m_ir->CreateFCmpUGE(a.value, _max.value), splat(-1).eval(m_ir), (r & sext(fcmp_ord(a >= fsplat(0.)))).eval(m_ir)); set_vr(op.rt, r); } else