diff --git a/rpcs3/Emu/CPU/CPUTranslator.cpp b/rpcs3/Emu/CPU/CPUTranslator.cpp index 4f3ad59106..6ef7c521b6 100644 --- a/rpcs3/Emu/CPU/CPUTranslator.cpp +++ b/rpcs3/Emu/CPU/CPUTranslator.cpp @@ -1 +1,13 @@ +#ifdef LLVM_AVAILABLE + #include "CPUTranslator.h" + +cpu_translator::cpu_translator(llvm::LLVMContext& context, llvm::Module* module, bool is_be) + : m_context(context) + , m_module(module) + , m_is_be(is_be) +{ + +} + +#endif \ No newline at end of file diff --git a/rpcs3/Emu/CPU/CPUTranslator.h b/rpcs3/Emu/CPU/CPUTranslator.h index 6f70f09bee..a6290ccc6e 100644 --- a/rpcs3/Emu/CPU/CPUTranslator.h +++ b/rpcs3/Emu/CPU/CPUTranslator.h @@ -1 +1,750 @@ #pragma once + +#ifdef LLVM_AVAILABLE + +#include "restore_new.h" +#ifdef _MSC_VER +#pragma warning(push, 0) +#endif +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Module.h" +#ifdef _MSC_VER +#pragma warning(pop) +#endif +#include "define_new_memleakdetect.h" + +#include "../Utilities/types.h" +#include "../Utilities/StrFmt.h" +#include "../Utilities/BEType.h" +#include "../Utilities/BitField.h" + +#include +#include +#include +#include +#include +#include + +template +struct llvm_value_t +{ + static_assert(std::is_same::value, "llvm_value_t<> error: unknown type"); + + using type = void; + static constexpr uint esize = 0; + static constexpr bool is_int = false; + static constexpr bool is_sint = false; + static constexpr bool is_uint = false; + static constexpr bool is_float = false; + static constexpr uint is_vector = false; + static constexpr uint is_pointer = false; + + static llvm::Type* get_type(llvm::LLVMContext& context) + { + return llvm::Type::getVoidTy(context); + } + + llvm::Value* eval(llvm::IRBuilder<>* ir) const + { + return value; + } + + llvm::Value* value; + + // llvm_value_t() = default; + + // llvm_value_t(llvm::Value* value) + // : value(value) + // { + // } +}; + +template <> +struct llvm_value_t : llvm_value_t +{ + using type = bool; + using base = llvm_value_t; + using base::base; + + static constexpr uint esize = 1; + static constexpr uint is_int = true; + + static llvm::Type* get_type(llvm::LLVMContext& context) + { + return llvm::Type::getInt1Ty(context); + } +}; + +template <> +struct llvm_value_t : llvm_value_t +{ + using type = char; + using base = llvm_value_t; + using base::base; + + static constexpr uint esize = 8; + static constexpr bool is_int = true; + + static llvm::Type* get_type(llvm::LLVMContext& context) + { + return llvm::Type::getInt8Ty(context); + } +}; + +template <> +struct llvm_value_t : llvm_value_t +{ + using type = s8; + using base = llvm_value_t; + using base::base; + + static constexpr bool is_sint = true; +}; + +template <> +struct llvm_value_t : llvm_value_t +{ + using type = u8; + using base = llvm_value_t; + using base::base; + + static constexpr bool is_uint = true; +}; + +template <> +struct llvm_value_t : llvm_value_t +{ + using type = s16; + using base = llvm_value_t; + using base::base; + + static constexpr uint esize = 16; + + static llvm::Type* get_type(llvm::LLVMContext& context) + { + return llvm::Type::getInt16Ty(context); + } +}; + +template <> +struct llvm_value_t : llvm_value_t +{ + using type = u16; + using base = llvm_value_t; + using base::base; + + static constexpr bool is_sint = false; + static constexpr bool is_uint = true; +}; + +template <> +struct llvm_value_t : llvm_value_t +{ + using type = s32; + using base = llvm_value_t; + using base::base; + + static constexpr uint esize = 32; + + static llvm::Type* get_type(llvm::LLVMContext& context) + { + return llvm::Type::getInt32Ty(context); + } +}; + +template <> +struct llvm_value_t : llvm_value_t +{ + using type = u32; + using base = llvm_value_t; + using base::base; + + static constexpr bool is_sint = false; + static constexpr bool is_uint = true; +}; + +template <> +struct llvm_value_t : llvm_value_t +{ + using type = s64; + using base = llvm_value_t; + using base::base; + + static constexpr uint esize = 64; + + static llvm::Type* get_type(llvm::LLVMContext& context) + { + return llvm::Type::getInt64Ty(context); + } +}; + +template <> +struct llvm_value_t : llvm_value_t +{ + using type = u64; + using base = llvm_value_t; + using base::base; + + static constexpr bool is_sint = false; + static constexpr bool is_uint = true; +}; + +template <> +struct llvm_value_t : llvm_value_t +{ + using type = s128; + using base = llvm_value_t; + using base::base; + + static constexpr uint esize = 128; + + static llvm::Type* get_type(llvm::LLVMContext& context) + { + return llvm::Type::getIntNTy(context, 128); + } +}; + +template <> +struct llvm_value_t : llvm_value_t +{ + using type = u128; + using base = llvm_value_t; + using base::base; + + static constexpr bool is_sint = false; + static constexpr bool is_uint = true; +}; + +template <> +struct llvm_value_t : llvm_value_t +{ + using type = f32; + using base = llvm_value_t; + using base::base; + + static constexpr uint esize = 32; + static constexpr bool is_float = true; + + static llvm::Type* get_type(llvm::LLVMContext& context) + { + return llvm::Type::getFloatTy(context); + } +}; + +template <> +struct llvm_value_t : llvm_value_t +{ + using type = f64; + using base = llvm_value_t; + using base::base; + + static constexpr uint esize = 64; + static constexpr bool is_float = true; + + static llvm::Type* get_type(llvm::LLVMContext& context) + { + return llvm::Type::getDoubleTy(context); + } +}; + +template +struct llvm_value_t : llvm_value_t +{ + static_assert(!std::is_void::value, "llvm_value_t<> error: invalid pointer to void type"); + + using type = T*; + using base = llvm_value_t; + using base::base; + + static constexpr uint is_pointer = llvm_value_t::is_pointer + 1; + + static llvm::Type* get_type(llvm::LLVMContext& context) + { + return llvm_value_t::get_type(context)->getPointerTo(); + } +}; + +template +struct llvm_value_t : llvm_value_t +{ + static_assert(!llvm_value_t::is_vector, "llvm_value_t<> error: invalid multidimensional vector"); + static_assert(!llvm_value_t::is_pointer, "llvm_value_t<>: vector of pointers is not allowed"); + + using type = T[N]; + using base = llvm_value_t; + using base::base; + + static constexpr uint is_vector = N; + static constexpr uint is_pointer = 0; + + static llvm::Type* get_type(llvm::LLVMContext& context) + { + return llvm::VectorType::get(llvm_value_t::get_type(context), N); + } +}; + +template +struct llvm_add_t +{ + using type = T; + + A1 a1; + A2 a2; + + 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 + { + const auto v1 = a1.eval(ir); + const auto v2 = a2.eval(ir); + + if (llvm_value_t::is_int) + { + return ir->CreateAdd(v1, v2); + } + + if (llvm_value_t::is_float) + { + return ir->CreateFAdd(v1, v2); + } + } +}; + +template ().eval(0)), typename = std::enable_if_t::value>> +inline llvm_add_t operator +(T1 a1, T2 a2) +{ + return {a1, a2}; +} + +template +struct llvm_sub_t +{ + using type = T; + + A1 a1; + A2 a2; + + static_assert(llvm_value_t::is_sint || llvm_value_t::is_uint || llvm_value_t::is_float, "llvm_sub_t<>: 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) + { + return ir->CreateSub(v1, v2); + } + + if (llvm_value_t::is_float) + { + return ir->CreateFSub(v1, v2); + } + } +}; + +template ().eval(0)), typename = std::enable_if_t::value>> +inline llvm_sub_t operator -(T1 a1, T2 a2) +{ + return {a1, a2}; +} + +template +struct llvm_mul_t +{ + using type = T; + + A1 a1; + A2 a2; + + static_assert(llvm_value_t::is_sint || llvm_value_t::is_uint || llvm_value_t::is_float, "llvm_mul_t<>: 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) + { + return ir->CreateMul(v1, v2); + } + + if (llvm_value_t::is_float) + { + return ir->CreateFMul(v1, v2); + } + } +}; + +template ().eval(0)), typename = std::enable_if_t::value>> +inline llvm_mul_t operator *(T1 a1, T2 a2) +{ + return {a1, a2}; +} + +template +struct llvm_div_t +{ + using type = T; + + A1 a1; + A2 a2; + + static_assert(llvm_value_t::is_sint || llvm_value_t::is_uint || llvm_value_t::is_float, "llvm_div_t<>: 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) + { + return ir->CreateSDiv(v1, v2); + } + + if (llvm_value_t::is_uint) + { + return ir->CreateUDiv(v1, v2); + } + + if (llvm_value_t::is_float) + { + return ir->CreateFDiv(v1, v2); + } + } +}; + +template ().eval(0)), typename = std::enable_if_t::value>> +inline llvm_div_t operator /(T1 a1, T2 a2) +{ + return {a1, a2}; +} + +template +struct llvm_neg_t +{ + using type = T; + + A1 a1; + + static_assert(llvm_value_t::is_sint || llvm_value_t::is_uint || llvm_value_t::is_float, "llvm_neg_t<>: invalid type"); + + llvm::Value* eval(llvm::IRBuilder<>* ir) const + { + const auto v1 = a1.eval(ir); + + if (llvm_value_t::is_int) + { + return ir->CreateNeg(v1); + } + + if (llvm_value_t::is_float) + { + return ir->CreateFNeg(v1); + } + } +}; + +template ().eval(0)), typename = std::enable_if_t::esize>> +inline llvm_neg_t 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 +{ + using type = T; + + A1 a1; + A2 a2; + + static_assert(llvm_value_t::is_sint || llvm_value_t::is_uint, "llvm_shl_t<>: 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) + { + return ir->CreateShl(v1, v2); + } + + if (llvm_value_t::is_uint) + { + return ir->CreateShl(v1, v2); + } + } +}; + +template ().eval(0)), typename = std::enable_if_t::value>> +inline llvm_shl_t 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) +{ + return {a1, llvm_int_t{a2}}; +} + +template +struct llvm_shr_t +{ + using type = T; + + A1 a1; + A2 a2; + + static_assert(llvm_value_t::is_sint || llvm_value_t::is_uint, "llvm_shr_t<>: 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) + { + return ir->CreateAShr(v1, v2); + } + + if (llvm_value_t::is_uint) + { + return ir->CreateLShr(v1, v2); + } + } +}; + +template ().eval(0)), typename = std::enable_if_t::value>> +inline llvm_shr_t 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) +{ + return {a1, llvm_int_t{a2}}; +} + +template +struct llvm_and_t +{ + using type = T; + + A1 a1; + A2 a2; + + static_assert(llvm_value_t::is_int, "llvm_and_t<>: 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) + { + return ir->CreateAnd(v1, v2); + } + } +}; + +template ().eval(0)), typename = std::enable_if_t::value>> +inline llvm_and_t 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) +{ + return {a1, llvm_int_t{a2}}; +} + +template +struct llvm_or_t +{ + using type = T; + + A1 a1; + A2 a2; + + static_assert(llvm_value_t::is_int, "llvm_or_t<>: 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) + { + return ir->CreateOr(v1, v2); + } + } +}; + +template ().eval(0)), typename = std::enable_if_t::value>> +inline llvm_or_t 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) +{ + return {a1, llvm_int_t{a2}}; +} + +template +struct llvm_xor_t +{ + using type = T; + + A1 a1; + A2 a2; + + static_assert(llvm_value_t::is_int, "llvm_xor_t<>: 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) + { + return ir->CreateXor(v1, v2); + } + } +}; + +template ().eval(0)), typename = std::enable_if_t::value>> +inline llvm_xor_t 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) +{ + return {a1, llvm_int_t{a2}}; +} + +template +struct llvm_not_t +{ + 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}; +} + +class cpu_translator +{ +protected: + cpu_translator(llvm::LLVMContext& context, llvm::Module* module, bool is_be); + + // LLVM context + llvm::LLVMContext& m_context; + + // Module to which all generated code is output to + llvm::Module* const m_module; + + // Endianness, affects vector element numbering (TODO) + const bool m_is_be; + + // IR builder + llvm::IRBuilder<>* m_ir; + +public: + // Convert a C++ type to an LLVM type (TODO: remove) + template + llvm::Type* GetType() + { + return llvm_value_t::get_type(m_context); + } + + template + llvm::Type* get_type() + { + return llvm_value_t::get_type(m_context); + } + + template + using value_t = llvm_value_t; + + template + auto eval(T expr) + { + value_t result; + result.value = expr.eval(m_ir); + return result; + } + + // Get unsigned addition carry into the sign bit (s = a + b) + template + static inline auto ucarry(T a, T b, T s) + { + return ((a ^ b) & ~s) | (a & b); + } + + // Get signed addition overflow into the sign bit (s = a + b) + template + static inline auto scarry(T a, T b, T s) + { + return (b ^ s) & ~(a ^ b); + } + + // Get signed subtraction overflow into the sign bit (d = a - b) + template + static inline auto sborrow(T a, T b, T d) + { + return (a ^ b) & (a ^ d); + } + + // Bitwise select (c ? a : b) + template + static inline auto merge(T c, T a, T b) + { + return (a & c) | (b & ~c); + } + + // Average: (a + b + 1) >> 1 + template + static inline auto avg(T a, T b) + { + return (a >> 1) + (b >> 1) + ((a | b) & 1); + } +}; + +#endif \ No newline at end of file diff --git a/rpcs3/Emu/Cell/PPUTranslator.cpp b/rpcs3/Emu/Cell/PPUTranslator.cpp index e3f535c1f3..71c78fcd2d 100644 --- a/rpcs3/Emu/Cell/PPUTranslator.cpp +++ b/rpcs3/Emu/Cell/PPUTranslator.cpp @@ -12,9 +12,7 @@ using namespace llvm; const ppu_decoder s_ppu_decoder; PPUTranslator::PPUTranslator(LLVMContext& context, Module* module, const ppu_module& info) - : m_context(context) - , m_module(module) - , m_is_be(false) + : cpu_translator(context, module, false) , m_info(info) , m_pure_attr(AttributeSet::get(m_context, AttributeSet::FunctionIndex, {Attribute::NoUnwind, Attribute::ReadNone})) { diff --git a/rpcs3/Emu/Cell/PPUTranslator.h b/rpcs3/Emu/Cell/PPUTranslator.h index cefabf306e..d94f8fde9c 100644 --- a/rpcs3/Emu/Cell/PPUTranslator.h +++ b/rpcs3/Emu/Cell/PPUTranslator.h @@ -2,117 +2,12 @@ #ifdef LLVM_AVAILABLE -#include -#include -#include -#include -#include -#include - +#include "../rpcs3/Emu/CPU/CPUTranslator.h" #include "../rpcs3/Emu/Cell/PPUOpcodes.h" #include "../rpcs3/Emu/Cell/PPUAnalyser.h" -#include "restore_new.h" -#ifdef _MSC_VER -#pragma warning(push, 0) -#endif -#include "llvm/IR/LLVMContext.h" -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/Module.h" -#ifdef _MSC_VER -#pragma warning(pop) -#endif -#include "define_new_memleakdetect.h" - -#include "../Utilities/types.h" -#include "../Utilities/StrFmt.h" -#include "../Utilities/BEType.h" - -template -struct TypeGen +class PPUTranslator final : public cpu_translator { - static_assert(!sizeof(T), "GetType<>() error: unknown type"); -}; - -template -struct TypeGen::value>> -{ - static llvm::Type* get(llvm::LLVMContext& context) { return llvm::Type::getVoidTy(context); } -}; - -template -struct TypeGen::value || std::is_same::value || std::is_same::value>> -{ - static llvm::Type* get(llvm::LLVMContext& context) { return llvm::Type::getInt64Ty(context); } -}; - -template -struct TypeGen::value || std::is_same::value>> -{ - static llvm::Type* get(llvm::LLVMContext& context) { return llvm::Type::getInt32Ty(context); } -}; - -template -struct TypeGen::value || std::is_same::value>> -{ - static llvm::Type* get(llvm::LLVMContext& context) { return llvm::Type::getInt16Ty(context); } -}; - -template -struct TypeGen::value || std::is_same::value || std::is_same::value>> -{ - static llvm::Type* get(llvm::LLVMContext& context) { return llvm::Type::getInt8Ty(context); } -}; - -template<> -struct TypeGen -{ - static llvm::Type* get(llvm::LLVMContext& context) { return llvm::Type::getFloatTy(context); } -}; - -template<> -struct TypeGen -{ - static llvm::Type* get(llvm::LLVMContext& context) { return llvm::Type::getDoubleTy(context); } -}; - -template<> -struct TypeGen -{ - static llvm::Type* get(llvm::LLVMContext& context) { return llvm::Type::getInt1Ty(context); } -}; - -template<> -struct TypeGen -{ - static llvm::Type* get(llvm::LLVMContext& context) { return llvm::Type::getIntNTy(context, 128); } -}; - -// Pointer type -template -struct TypeGen -{ - static llvm::Type* get(llvm::LLVMContext& context) { return TypeGen::get(context)->getPointerTo(); } -}; - -// Vector type -template -struct TypeGen -{ - static llvm::Type* get(llvm::LLVMContext& context) { return llvm::VectorType::get(TypeGen::get(context), N); } -}; - -class PPUTranslator final //: public CPUTranslator -{ - // LLVM context - llvm::LLVMContext& m_context; - - // Module to which all generated code is output to - llvm::Module* const m_module; - - // Endianness, affects vector element numbering (TODO) - const bool m_is_be; - // PPU Module const ppu_module& m_info; @@ -122,9 +17,6 @@ class PPUTranslator final //: public CPUTranslator // Attributes for function calls which are "pure" and may be optimized away if their results are unused const llvm::AttributeSet m_pure_attr; - // IR builder - llvm::IRBuilder<>* m_ir; - // LLVM function llvm::Function* m_function; @@ -191,6 +83,20 @@ class PPUTranslator final //: public CPUTranslator #undef DEF_VALUE public: + template + value_t get_vr(u32 vr) + { + value_t result; + result.value = m_ir->CreateBitCast(GetVr(vr, VrType::vi32), value_t::get_type(m_context)); + return result; + } + + template + void set_vr(u32 vr, value_t v) + { + return SetVr(vr, v.value); + } + // Get current instruction address llvm::Value* GetAddr(u64 _add = 0); @@ -382,19 +288,6 @@ public: // Write to memory void WriteMemory(llvm::Value* addr, llvm::Value* value, bool is_be = true, u32 align = 1); - // Convert a C++ type to an LLVM type - template - llvm::Type* GetType() - { - return TypeGen::get(m_context); - } - - template - llvm::PointerType* GetPtrType() - { - return TypeGen::get(m_context)->getPointerTo(); - } - // Get an undefined value with specified type template llvm::Value* GetUndef()