mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-02-23 06:40:49 +00:00
LLVM DSL: rewrite bitcast, zext, sext, trunc, select, min, max ops
Are made composable in expressions similar to arithmetic ops. Implement noncast in addition to bitcast (no-op case). Implement bitcast constant folding. Fixed some misuse of sext<>.
This commit is contained in:
parent
3925cb59ac
commit
524aac75ed
@ -9,6 +9,7 @@
|
|||||||
#include "llvm/IR/LLVMContext.h"
|
#include "llvm/IR/LLVMContext.h"
|
||||||
#include "llvm/IR/IRBuilder.h"
|
#include "llvm/IR/IRBuilder.h"
|
||||||
#include "llvm/IR/Module.h"
|
#include "llvm/IR/Module.h"
|
||||||
|
#include "llvm/Analysis/ConstantFolding.h"
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#pragma warning(pop)
|
#pragma warning(pop)
|
||||||
#endif
|
#endif
|
||||||
@ -951,6 +952,226 @@ inline llvm_cmp<T1, llvm_const_int<typename is_llvm_expr<T1>::type>, llvm::ICmpI
|
|||||||
return {a1, {c}};
|
return {a1, {c}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename U, typename A1, typename T = llvm_common_t<A1>>
|
||||||
|
struct llvm_noncast
|
||||||
|
{
|
||||||
|
using type = U;
|
||||||
|
|
||||||
|
llvm_expr_t<A1> a1;
|
||||||
|
|
||||||
|
static_assert(llvm_value_t<T>::is_int, "llvm_noncast<>: invalid type");
|
||||||
|
static_assert(llvm_value_t<U>::is_int, "llvm_noncast<>: invalid result type");
|
||||||
|
static_assert(llvm_value_t<T>::esize == llvm_value_t<U>::esize, "llvm_noncast<>: result is resized");
|
||||||
|
static_assert(llvm_value_t<T>::is_vector == llvm_value_t<U>::is_vector, "llvm_noncast<>: vector element mismatch");
|
||||||
|
|
||||||
|
static constexpr bool is_ok =
|
||||||
|
llvm_value_t<T>::is_int &&
|
||||||
|
llvm_value_t<U>::is_int &&
|
||||||
|
llvm_value_t<T>::esize == llvm_value_t<U>::esize &&
|
||||||
|
llvm_value_t<T>::is_vector == llvm_value_t<U>::is_vector;
|
||||||
|
|
||||||
|
llvm::Value* eval(llvm::IRBuilder<>* ir) const
|
||||||
|
{
|
||||||
|
// No operation required
|
||||||
|
return a1.eval(ir);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename U, typename A1, typename T = llvm_common_t<A1>>
|
||||||
|
struct llvm_bitcast
|
||||||
|
{
|
||||||
|
using type = U;
|
||||||
|
|
||||||
|
llvm_expr_t<A1> a1;
|
||||||
|
|
||||||
|
static constexpr uint bitsize0 = llvm_value_t<T>::is_vector ? llvm_value_t<T>::is_vector * llvm_value_t<T>::esize : llvm_value_t<T>::esize;
|
||||||
|
static constexpr uint bitsize1 = llvm_value_t<U>::is_vector ? llvm_value_t<U>::is_vector * llvm_value_t<U>::esize : llvm_value_t<U>::esize;
|
||||||
|
|
||||||
|
static_assert(bitsize0 == bitsize1, "llvm_bitcast<>: invalid type (size mismatch)");
|
||||||
|
static_assert(llvm_value_t<T>::is_int || llvm_value_t<T>::is_float, "llvm_bitcast<>: invalid type");
|
||||||
|
static_assert(llvm_value_t<U>::is_int || llvm_value_t<U>::is_float, "llvm_bitcast<>: invalid result type");
|
||||||
|
|
||||||
|
static constexpr bool is_ok =
|
||||||
|
bitsize0 && bitsize0 == bitsize1 &&
|
||||||
|
(llvm_value_t<T>::is_int || llvm_value_t<T>::is_float) &&
|
||||||
|
(llvm_value_t<U>::is_int || llvm_value_t<U>::is_float);
|
||||||
|
|
||||||
|
llvm::Value* eval(llvm::IRBuilder<>* ir) const
|
||||||
|
{
|
||||||
|
const auto v1 = a1.eval(ir);
|
||||||
|
const auto rt = llvm_value_t<U>::get_type(ir->getContext());
|
||||||
|
|
||||||
|
if constexpr (llvm_value_t<T>::is_int == llvm_value_t<U>::is_int && llvm_value_t<T>::is_vector == llvm_value_t<U>::is_vector)
|
||||||
|
{
|
||||||
|
// No-op case
|
||||||
|
return v1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const auto c1 = llvm::dyn_cast<llvm::Constant>(v1))
|
||||||
|
{
|
||||||
|
const auto module = ir->GetInsertBlock()->getParent()->getParent();
|
||||||
|
const auto result = llvm::ConstantFoldCastOperand(llvm::Instruction::BitCast, c1, rt, module->getDataLayout());
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ir->CreateBitCast(v1, rt);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename U, typename A1, typename T = llvm_common_t<A1>>
|
||||||
|
struct llvm_trunc
|
||||||
|
{
|
||||||
|
using type = U;
|
||||||
|
|
||||||
|
llvm_expr_t<A1> a1;
|
||||||
|
|
||||||
|
static_assert(llvm_value_t<T>::is_int, "llvm_trunc<>: invalid type");
|
||||||
|
static_assert(llvm_value_t<U>::is_int, "llvm_trunc<>: invalid result type");
|
||||||
|
static_assert(llvm_value_t<T>::esize > llvm_value_t<U>::esize, "llvm_trunc<>: result is not truncated");
|
||||||
|
static_assert(llvm_value_t<T>::is_vector == llvm_value_t<U>::is_vector, "llvm_trunc<>: vector element mismatch");
|
||||||
|
|
||||||
|
static constexpr bool is_ok =
|
||||||
|
llvm_value_t<T>::is_int &&
|
||||||
|
llvm_value_t<U>::is_int &&
|
||||||
|
llvm_value_t<T>::esize > llvm_value_t<U>::esize &&
|
||||||
|
llvm_value_t<T>::is_vector == llvm_value_t<U>::is_vector;
|
||||||
|
|
||||||
|
llvm::Value* eval(llvm::IRBuilder<>* ir) const
|
||||||
|
{
|
||||||
|
return ir->CreateTrunc(a1.eval(ir), llvm_value_t<U>::get_type(ir->getContext()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename U, typename A1, typename T = llvm_common_t<A1>>
|
||||||
|
struct llvm_sext
|
||||||
|
{
|
||||||
|
using type = U;
|
||||||
|
|
||||||
|
llvm_expr_t<A1> a1;
|
||||||
|
|
||||||
|
static_assert(llvm_value_t<T>::is_int, "llvm_sext<>: invalid type");
|
||||||
|
static_assert(llvm_value_t<U>::is_sint, "llvm_sext<>: invalid result type");
|
||||||
|
static_assert(llvm_value_t<T>::esize < llvm_value_t<U>::esize, "llvm_sext<>: result is not extended");
|
||||||
|
static_assert(llvm_value_t<T>::is_vector == llvm_value_t<U>::is_vector, "llvm_sext<>: vector element mismatch");
|
||||||
|
|
||||||
|
static constexpr bool is_ok =
|
||||||
|
llvm_value_t<T>::is_int &&
|
||||||
|
llvm_value_t<U>::is_sint &&
|
||||||
|
llvm_value_t<T>::esize < llvm_value_t<U>::esize &&
|
||||||
|
llvm_value_t<T>::is_vector == llvm_value_t<U>::is_vector;
|
||||||
|
|
||||||
|
llvm::Value* eval(llvm::IRBuilder<>* ir) const
|
||||||
|
{
|
||||||
|
return ir->CreateSExt(a1.eval(ir), llvm_value_t<U>::get_type(ir->getContext()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename U, typename A1, typename T = llvm_common_t<A1>>
|
||||||
|
struct llvm_zext
|
||||||
|
{
|
||||||
|
using type = U;
|
||||||
|
|
||||||
|
llvm_expr_t<A1> a1;
|
||||||
|
|
||||||
|
static_assert(llvm_value_t<T>::is_int, "llvm_zext<>: invalid type");
|
||||||
|
static_assert(llvm_value_t<U>::is_uint, "llvm_zext<>: invalid result type");
|
||||||
|
static_assert(llvm_value_t<T>::esize < llvm_value_t<U>::esize, "llvm_zext<>: result is not extended");
|
||||||
|
static_assert(llvm_value_t<T>::is_vector == llvm_value_t<U>::is_vector, "llvm_zext<>: vector element mismatch");
|
||||||
|
|
||||||
|
static constexpr bool is_ok =
|
||||||
|
llvm_value_t<T>::is_int &&
|
||||||
|
llvm_value_t<U>::is_uint &&
|
||||||
|
llvm_value_t<T>::esize < llvm_value_t<U>::esize &&
|
||||||
|
llvm_value_t<T>::is_vector == llvm_value_t<U>::is_vector;
|
||||||
|
|
||||||
|
llvm::Value* eval(llvm::IRBuilder<>* ir) const
|
||||||
|
{
|
||||||
|
return ir->CreateZExt(a1.eval(ir), llvm_value_t<U>::get_type(ir->getContext()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename A1, typename A2, typename A3, typename T = llvm_common_t<A2, A3>, typename U = llvm_common_t<A1>>
|
||||||
|
struct llvm_select
|
||||||
|
{
|
||||||
|
using type = T;
|
||||||
|
|
||||||
|
llvm_expr_t<A1> cond;
|
||||||
|
llvm_expr_t<A2> a2;
|
||||||
|
llvm_expr_t<A3> a3;
|
||||||
|
|
||||||
|
static_assert(llvm_value_t<U>::esize == 1 && llvm_value_t<U>::is_int, "llvm_select<>: invalid condition type (bool expected)");
|
||||||
|
static_assert(llvm_value_t<T>::is_vector == llvm_value_t<U>::is_vector, "llvm_select<>: vector element mismatch");
|
||||||
|
|
||||||
|
static constexpr bool is_ok =
|
||||||
|
llvm_value_t<U>::esize == 1 && llvm_value_t<U>::is_int &&
|
||||||
|
llvm_value_t<T>::is_vector == llvm_value_t<U>::is_vector;
|
||||||
|
|
||||||
|
llvm::Value* eval(llvm::IRBuilder<>* ir) const
|
||||||
|
{
|
||||||
|
return ir->CreateSelect(cond.eval(ir), a2.eval(ir), a3.eval(ir));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename A1, typename A2, typename T = llvm_common_t<A1, A2>>
|
||||||
|
struct llvm_min
|
||||||
|
{
|
||||||
|
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_min<>: invalid type");
|
||||||
|
|
||||||
|
static constexpr bool is_ok = llvm_value_t<T>::is_sint || llvm_value_t<T>::is_uint;
|
||||||
|
|
||||||
|
llvm::Value* eval(llvm::IRBuilder<>* ir) const
|
||||||
|
{
|
||||||
|
const auto v1 = a1.eval(ir);
|
||||||
|
const auto v2 = a2.eval(ir);
|
||||||
|
|
||||||
|
if constexpr (llvm_value_t<T>::is_sint)
|
||||||
|
{
|
||||||
|
return ir->CreateSelect(ir->CreateICmpSLT(v1, v2), v1, v2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (llvm_value_t<T>::is_uint)
|
||||||
|
{
|
||||||
|
return ir->CreateSelect(ir->CreateICmpULT(v1, v2), v1, v2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename A1, typename A2, typename T = llvm_common_t<A1, A2>>
|
||||||
|
struct llvm_max
|
||||||
|
{
|
||||||
|
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_max<>: invalid type");
|
||||||
|
|
||||||
|
llvm::Value* eval(llvm::IRBuilder<>* ir) const
|
||||||
|
{
|
||||||
|
const auto v1 = a1.eval(ir);
|
||||||
|
const auto v2 = a2.eval(ir);
|
||||||
|
|
||||||
|
if constexpr (llvm_value_t<T>::is_sint)
|
||||||
|
{
|
||||||
|
return ir->CreateSelect(ir->CreateICmpSLT(v1, v2), v2, v1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (llvm_value_t<T>::is_uint)
|
||||||
|
{
|
||||||
|
return ir->CreateSelect(ir->CreateICmpULT(v1, v2), v2, v1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class cpu_translator
|
class cpu_translator
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
@ -1027,36 +1248,52 @@ public:
|
|||||||
return llvm_uno{std::forward<T>(cmp_expr)};
|
return llvm_uno{std::forward<T>(cmp_expr)};
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename T2>
|
template <typename U, typename T, typename = std::enable_if_t<llvm_noncast<U, T>::is_ok>>
|
||||||
value_t<T> bitcast(T2 expr)
|
static auto noncast(T&& expr)
|
||||||
{
|
{
|
||||||
value_t<T> result;
|
return llvm_noncast<U, T>{std::forward<T>(expr)};
|
||||||
result.value = m_ir->CreateBitCast(expr.eval(m_ir), result.get_type(m_context));
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename T2>
|
template <typename U, typename T, typename = std::enable_if_t<llvm_bitcast<U, T>::is_ok>>
|
||||||
value_t<T> trunc(T2 expr)
|
static auto bitcast(T&& expr)
|
||||||
{
|
{
|
||||||
value_t<T> result;
|
return llvm_bitcast<U, T>{std::forward<T>(expr)};
|
||||||
result.value = m_ir->CreateTrunc(expr.eval(m_ir), result.get_type(m_context));
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename T2>
|
template <typename U, typename T, typename = std::enable_if_t<llvm_trunc<U, T>::is_ok>>
|
||||||
value_t<T> sext(T2 expr)
|
static auto trunc(T&& expr)
|
||||||
{
|
{
|
||||||
value_t<T> result;
|
return llvm_trunc<U, T>{std::forward<T>(expr)};
|
||||||
result.value = m_ir->CreateSExt(expr.eval(m_ir), result.get_type(m_context));
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename T2>
|
template <typename U, typename T, typename = std::enable_if_t<llvm_sext<U, T>::is_ok>>
|
||||||
value_t<T> zext(T2 expr)
|
static auto sext(T&& expr)
|
||||||
{
|
{
|
||||||
value_t<T> result;
|
return llvm_sext<U, T>{std::forward<T>(expr)};
|
||||||
result.value = m_ir->CreateZExt(expr.eval(m_ir), result.get_type(m_context));
|
}
|
||||||
return result;
|
|
||||||
|
template <typename U, typename T, typename = std::enable_if_t<llvm_zext<U, T>::is_ok>>
|
||||||
|
static auto zext(T&& expr)
|
||||||
|
{
|
||||||
|
return llvm_zext<U, T>{std::forward<T>(expr)};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename U, typename V, typename = std::enable_if_t<llvm_select<T, U, V>::is_ok>>
|
||||||
|
static auto select(T&& c, U&& a, V&& b)
|
||||||
|
{
|
||||||
|
return llvm_select<T, U, V>{std::forward<T>(c), std::forward<U>(a), std::forward<V>(b)};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename U, typename = std::enable_if_t<llvm_min<T, U>::is_ok>>
|
||||||
|
static auto min(T&& a, U&& b)
|
||||||
|
{
|
||||||
|
return llvm_min<T, U>{std::forward<T>(a), std::forward<U>(b)};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename U, typename = std::enable_if_t<llvm_min<T, U>::is_ok>>
|
||||||
|
static auto max(T&& a, U&& b)
|
||||||
|
{
|
||||||
|
return llvm_max<T, U>{std::forward<T>(a), std::forward<U>(b)};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get signed addition overflow into the sign bit (s = a + b)
|
// Get signed addition overflow into the sign bit (s = a + b)
|
||||||
@ -1194,17 +1431,6 @@ public:
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Select (c ? a : b)
|
|
||||||
template <typename T, typename T2>
|
|
||||||
auto select(T2 c, T a, T b)
|
|
||||||
{
|
|
||||||
static_assert(value_t<typename T2::type>::esize == 1, "select: expected bool type (first argument)");
|
|
||||||
static_assert(value_t<typename T2::type>::is_vector == value_t<typename T::type>::is_vector, "select: incompatible arguments (vectors)");
|
|
||||||
T result;
|
|
||||||
result.value = m_ir->CreateSelect(c.eval(m_ir), a.eval(m_ir), b.eval(m_ir));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename E>
|
template <typename T, typename E>
|
||||||
auto insert(T v, u64 i, E e)
|
auto insert(T v, u64 i, E e)
|
||||||
{
|
{
|
||||||
@ -1246,24 +1472,6 @@ public:
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Min
|
|
||||||
template <typename T>
|
|
||||||
auto min(T a, T b)
|
|
||||||
{
|
|
||||||
T result;
|
|
||||||
result.value = m_ir->CreateSelect((a > b).eval(m_ir), b.eval(m_ir), a.eval(m_ir));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Max
|
|
||||||
template <typename T>
|
|
||||||
auto max(T a, T b)
|
|
||||||
{
|
|
||||||
T result;
|
|
||||||
result.value = m_ir->CreateSelect((a > b).eval(m_ir), a.eval(m_ir), b.eval(m_ir));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shuffle single vector using all zeros second vector of the same size
|
// Shuffle single vector using all zeros second vector of the same size
|
||||||
template <typename T, typename T1, typename... Args>
|
template <typename T, typename T1, typename... Args>
|
||||||
auto zshuffle(T1 a, Args... args)
|
auto zshuffle(T1 a, Args... args)
|
||||||
|
@ -271,7 +271,7 @@ extern void ppu_register_range(u32 addr, u32 size)
|
|||||||
// Register executable range at
|
// Register executable range at
|
||||||
utils::memory_commit(&ppu_ref(addr), size * 2, utils::protection::rw);
|
utils::memory_commit(&ppu_ref(addr), size * 2, utils::protection::rw);
|
||||||
|
|
||||||
const u32 fallback = ::narrow<u32>(g_cfg.core.ppu_decoder == ppu_decoder_type::llvm ?
|
const u32 fallback = ::narrow<u32>(g_cfg.core.ppu_decoder == ppu_decoder_type::llvm ?
|
||||||
reinterpret_cast<uptr>(ppu_recompiler_fallback) : reinterpret_cast<uptr>(ppu_fallback));
|
reinterpret_cast<uptr>(ppu_recompiler_fallback) : reinterpret_cast<uptr>(ppu_fallback));
|
||||||
|
|
||||||
size &= ~3; // Loop assumes `size = n * 4`, enforce that by rounding down
|
size &= ~3; // Loop assumes `size = n * 4`, enforce that by rounding down
|
||||||
@ -1708,6 +1708,7 @@ static void ppu_initialize2(jit_compiler& jit, const ppu_module& module_part, co
|
|||||||
|
|
||||||
// Initialize target
|
// Initialize target
|
||||||
module->setTargetTriple(Triple::normalize(sys::getProcessTriple()));
|
module->setTargetTriple(Triple::normalize(sys::getProcessTriple()));
|
||||||
|
module->setDataLayout(jit.get_engine().getTargetMachine()->createDataLayout());
|
||||||
|
|
||||||
// Initialize translator
|
// Initialize translator
|
||||||
PPUTranslator translator(jit.get_context(), module.get(), module_part, jit.has_ssse3());
|
PPUTranslator translator(jit.get_context(), module.get(), module_part, jit.has_ssse3());
|
||||||
|
@ -1053,8 +1053,8 @@ void PPUTranslator::VMSUMMBM(ppu_opcode_t op)
|
|||||||
const auto a = get_vr<s16[8]>(op.va);
|
const auto a = get_vr<s16[8]>(op.va);
|
||||||
const auto b = get_vr<u16[8]>(op.vb);
|
const auto b = get_vr<u16[8]>(op.vb);
|
||||||
const auto c = get_vr<s32[4]>(op.vc);
|
const auto c = get_vr<s32[4]>(op.vc);
|
||||||
const auto ml = bitcast<s32[4]>((a << 8 >> 8) * bitcast<s16[8]>(b << 8 >> 8));
|
const auto ml = bitcast<s32[4]>((a << 8 >> 8) * noncast<s16[8]>(b << 8 >> 8));
|
||||||
const auto mh = bitcast<s32[4]>((a >> 8) * bitcast<s16[8]>(b >> 8));
|
const auto mh = bitcast<s32[4]>((a >> 8) * noncast<s16[8]>(b >> 8));
|
||||||
set_vr(op.vd, eval(((ml << 16 >> 16) + (ml >> 16)) + ((mh << 16 >> 16) + (mh >> 16)) + c));
|
set_vr(op.vd, eval(((ml << 16 >> 16) + (ml >> 16)) + ((mh << 16 >> 16) + (mh >> 16)) + c));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1191,7 +1191,7 @@ void PPUTranslator::VPERM(ppu_opcode_t op)
|
|||||||
const auto b = get_vr<u8[16]>(op.vb);
|
const auto b = get_vr<u8[16]>(op.vb);
|
||||||
const auto c = get_vr<u8[16]>(op.vc);
|
const auto c = get_vr<u8[16]>(op.vc);
|
||||||
const auto i = eval(~c & 0x1f);
|
const auto i = eval(~c & 0x1f);
|
||||||
set_vr(op.vd, select(bitcast<s8[16]>(c << 3) >= 0, pshufb(a, i), pshufb(b, i)));
|
set_vr(op.vd, select(noncast<s8[16]>(c << 3) >= 0, pshufb(a, i), pshufb(b, i)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void PPUTranslator::VPKPX(ppu_opcode_t op)
|
void PPUTranslator::VPKPX(ppu_opcode_t op)
|
||||||
|
@ -3087,6 +3087,7 @@ public:
|
|||||||
// Create LLVM module
|
// Create LLVM module
|
||||||
std::unique_ptr<Module> module = std::make_unique<Module>(hash + ".obj", m_context);
|
std::unique_ptr<Module> module = std::make_unique<Module>(hash + ".obj", m_context);
|
||||||
module->setTargetTriple(Triple::normalize(sys::getProcessTriple()));
|
module->setTargetTriple(Triple::normalize(sys::getProcessTriple()));
|
||||||
|
module->setDataLayout(m_jit.get_engine().getTargetMachine()->createDataLayout());
|
||||||
m_module = module.get();
|
m_module = module.get();
|
||||||
|
|
||||||
// Initialize IR Builder
|
// Initialize IR Builder
|
||||||
@ -3587,6 +3588,7 @@ public:
|
|||||||
// Create LLVM module
|
// Create LLVM module
|
||||||
std::unique_ptr<Module> module = std::make_unique<Module>("spu_interpreter.obj", m_context);
|
std::unique_ptr<Module> module = std::make_unique<Module>("spu_interpreter.obj", m_context);
|
||||||
module->setTargetTriple(Triple::normalize(sys::getProcessTriple()));
|
module->setTargetTriple(Triple::normalize(sys::getProcessTriple()));
|
||||||
|
module->setDataLayout(m_jit.get_engine().getTargetMachine()->createDataLayout());
|
||||||
m_module = module.get();
|
m_module = module.get();
|
||||||
|
|
||||||
// Initialize IR Builder
|
// Initialize IR Builder
|
||||||
@ -4425,12 +4427,12 @@ public:
|
|||||||
}
|
}
|
||||||
case MFC_Size:
|
case MFC_Size:
|
||||||
{
|
{
|
||||||
set_reg_fixed(s_reg_mfc_size, trunc<u16>(val & 0x7fff).value);
|
set_reg_fixed(s_reg_mfc_size, trunc<u16>(val & 0x7fff).eval(m_ir));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case MFC_TagID:
|
case MFC_TagID:
|
||||||
{
|
{
|
||||||
set_reg_fixed(s_reg_mfc_tag, trunc<u8>(val & 0x1f).value);
|
set_reg_fixed(s_reg_mfc_tag, trunc<u8>(val & 0x1f).eval(m_ir));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case MFC_Cmd:
|
case MFC_Cmd:
|
||||||
@ -4447,14 +4449,14 @@ public:
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto ci = llvm::dyn_cast<llvm::ConstantInt>(trunc<u8>(val).value))
|
if (auto ci = llvm::dyn_cast<llvm::ConstantInt>(trunc<u8>(val).eval(m_ir)))
|
||||||
{
|
{
|
||||||
const auto eal = get_reg_fixed<u32>(s_reg_mfc_eal);
|
const auto eal = get_reg_fixed<u32>(s_reg_mfc_eal);
|
||||||
const auto lsa = get_reg_fixed<u32>(s_reg_mfc_lsa);
|
const auto lsa = get_reg_fixed<u32>(s_reg_mfc_lsa);
|
||||||
const auto tag = get_reg_fixed<u8>(s_reg_mfc_tag);
|
const auto tag = get_reg_fixed<u8>(s_reg_mfc_tag);
|
||||||
|
|
||||||
const auto size = get_reg_fixed<u16>(s_reg_mfc_size);
|
const auto size = get_reg_fixed<u16>(s_reg_mfc_size);
|
||||||
const auto mask = m_ir->CreateShl(m_ir->getInt32(1), zext<u32>(tag).value);
|
const auto mask = m_ir->CreateShl(m_ir->getInt32(1), zext<u32>(tag).eval(m_ir));
|
||||||
const auto exec = llvm::BasicBlock::Create(m_context, "", m_function);
|
const auto exec = llvm::BasicBlock::Create(m_context, "", m_function);
|
||||||
const auto fail = llvm::BasicBlock::Create(m_context, "", m_function);
|
const auto fail = llvm::BasicBlock::Create(m_context, "", m_function);
|
||||||
const auto next = llvm::BasicBlock::Create(m_context, "", m_function);
|
const auto next = llvm::BasicBlock::Create(m_context, "", m_function);
|
||||||
@ -4515,8 +4517,8 @@ public:
|
|||||||
csize = -1;
|
csize = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
llvm::Value* src = m_ir->CreateGEP(m_lsptr, zext<u64>(lsa).value);
|
llvm::Value* src = m_ir->CreateGEP(m_lsptr, zext<u64>(lsa).eval(m_ir));
|
||||||
llvm::Value* dst = m_ir->CreateGEP(m_memptr, zext<u64>(eal).value);
|
llvm::Value* dst = m_ir->CreateGEP(m_memptr, zext<u64>(eal).eval(m_ir));
|
||||||
|
|
||||||
if (cmd & MFC_GET_CMD)
|
if (cmd & MFC_GET_CMD)
|
||||||
{
|
{
|
||||||
@ -4599,7 +4601,7 @@ public:
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// TODO
|
// TODO
|
||||||
m_ir->CreateCall(get_intrinsic<u8*, u8*, u32>(llvm::Intrinsic::memcpy), {dst, src, zext<u32>(size).value, m_ir->getTrue()});
|
m_ir->CreateCall(get_intrinsic<u8*, u8*, u32>(llvm::Intrinsic::memcpy), {dst, src, zext<u32>(size).eval(m_ir), m_ir->getTrue()});
|
||||||
}
|
}
|
||||||
|
|
||||||
m_ir->CreateBr(next);
|
m_ir->CreateBr(next);
|
||||||
@ -4840,7 +4842,7 @@ public:
|
|||||||
if constexpr (!by.is_vector)
|
if constexpr (!by.is_vector)
|
||||||
sh.value = m_ir->CreateVectorSplat(sh.is_vector, sh.value);
|
sh.value = m_ir->CreateVectorSplat(sh.is_vector, sh.value);
|
||||||
|
|
||||||
set_vr(op.rt, select(sh < by.esize, eval(get_vr<R>(op.ra) >> sh), splat<R>(0)));
|
set_vr(op.rt, select(sh < by.esize, get_vr<R>(op.ra) >> sh, splat<R>(0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename R, typename T>
|
template <typename R, typename T>
|
||||||
@ -4866,7 +4868,7 @@ public:
|
|||||||
if constexpr (!by.is_vector)
|
if constexpr (!by.is_vector)
|
||||||
sh.value = m_ir->CreateVectorSplat(sh.is_vector, sh.value);
|
sh.value = m_ir->CreateVectorSplat(sh.is_vector, sh.value);
|
||||||
|
|
||||||
set_vr(op.rt, select(sh < by.esize, eval(get_vr<R>(op.ra) << sh), splat<R>(0)));
|
set_vr(op.rt, select(sh < by.esize, get_vr<R>(op.ra) << sh, splat<R>(0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ROT(spu_opcode_t op)
|
void ROT(spu_opcode_t op)
|
||||||
@ -4996,7 +4998,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto m = zext<u32>(bitcast<i4>(trunc<bool[4]>(a)));
|
const auto m = zext<u32>(bitcast<i4>(trunc<bool[4]>(a)));
|
||||||
set_vr(op.rt, insert(splat<u32[4]>(0), 3, m));
|
set_vr(op.rt, insert(splat<u32[4]>(0), 3, eval(m)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBH(spu_opcode_t op)
|
void GBH(spu_opcode_t op)
|
||||||
@ -5014,7 +5016,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto m = zext<u32>(bitcast<u8>(trunc<bool[8]>(a)));
|
const auto m = zext<u32>(bitcast<u8>(trunc<bool[8]>(a)));
|
||||||
set_vr(op.rt, insert(splat<u32[4]>(0), 3, m));
|
set_vr(op.rt, insert(splat<u32[4]>(0), 3, eval(m)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBB(spu_opcode_t op)
|
void GBB(spu_opcode_t op)
|
||||||
@ -5032,7 +5034,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto m = zext<u32>(bitcast<u16>(trunc<bool[16]>(a)));
|
const auto m = zext<u32>(bitcast<u16>(trunc<bool[16]>(a)));
|
||||||
set_vr(op.rt, insert(splat<u32[4]>(0), 3, m));
|
set_vr(op.rt, insert(splat<u32[4]>(0), 3, eval(m)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void FSM(spu_opcode_t op)
|
void FSM(spu_opcode_t op)
|
||||||
@ -5047,7 +5049,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto m = bitcast<bool[4]>(trunc<i4>(v));
|
const auto m = bitcast<bool[4]>(trunc<i4>(v));
|
||||||
set_vr(op.rt, sext<u32[4]>(m));
|
set_vr(op.rt, sext<s32[4]>(m));
|
||||||
}
|
}
|
||||||
|
|
||||||
void FSMH(spu_opcode_t op)
|
void FSMH(spu_opcode_t op)
|
||||||
@ -5062,7 +5064,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto m = bitcast<bool[8]>(trunc<u8>(v));
|
const auto m = bitcast<bool[8]>(trunc<u8>(v));
|
||||||
set_vr(op.rt, sext<u16[8]>(m));
|
set_vr(op.rt, sext<s16[8]>(m));
|
||||||
}
|
}
|
||||||
|
|
||||||
void FSMB(spu_opcode_t op)
|
void FSMB(spu_opcode_t op)
|
||||||
@ -5077,7 +5079,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto m = bitcast<bool[16]>(trunc<u16>(v));
|
const auto m = bitcast<bool[16]>(trunc<u16>(v));
|
||||||
set_vr(op.rt, sext<u8[16]>(m));
|
set_vr(op.rt, sext<s8[16]>(m));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ROTQBYBI(spu_opcode_t op)
|
void ROTQBYBI(spu_opcode_t op)
|
||||||
@ -5276,7 +5278,7 @@ public:
|
|||||||
|
|
||||||
void CGT(spu_opcode_t op)
|
void CGT(spu_opcode_t op)
|
||||||
{
|
{
|
||||||
set_vr(op.rt, sext<u32[4]>(get_vr<s32[4]>(op.ra) > get_vr<s32[4]>(op.rb)));
|
set_vr(op.rt, sext<s32[4]>(get_vr<s32[4]>(op.ra) > get_vr<s32[4]>(op.rb)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void XOR(spu_opcode_t op)
|
void XOR(spu_opcode_t op)
|
||||||
@ -5286,7 +5288,7 @@ public:
|
|||||||
|
|
||||||
void CGTH(spu_opcode_t op)
|
void CGTH(spu_opcode_t op)
|
||||||
{
|
{
|
||||||
set_vr(op.rt, sext<u16[8]>(get_vr<s16[8]>(op.ra) > get_vr<s16[8]>(op.rb)));
|
set_vr(op.rt, sext<s16[8]>(get_vr<s16[8]>(op.ra) > get_vr<s16[8]>(op.rb)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EQV(spu_opcode_t op)
|
void EQV(spu_opcode_t op)
|
||||||
@ -5296,7 +5298,7 @@ public:
|
|||||||
|
|
||||||
void CGTB(spu_opcode_t op)
|
void CGTB(spu_opcode_t op)
|
||||||
{
|
{
|
||||||
set_vr(op.rt, sext<u8[16]>(get_vr<s8[16]>(op.ra) > get_vr<s8[16]>(op.rb)));
|
set_vr(op.rt, sext<s8[16]>(get_vr<s8[16]>(op.ra) > get_vr<s8[16]>(op.rb)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SUMB(spu_opcode_t op)
|
void SUMB(spu_opcode_t op)
|
||||||
@ -5337,7 +5339,7 @@ public:
|
|||||||
|
|
||||||
void CLGT(spu_opcode_t op)
|
void CLGT(spu_opcode_t op)
|
||||||
{
|
{
|
||||||
set_vr(op.rt, sext<u32[4]>(get_vr(op.ra) > get_vr(op.rb)));
|
set_vr(op.rt, sext<s32[4]>(get_vr(op.ra) > get_vr(op.rb)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ANDC(spu_opcode_t op)
|
void ANDC(spu_opcode_t op)
|
||||||
@ -5347,7 +5349,7 @@ public:
|
|||||||
|
|
||||||
void CLGTH(spu_opcode_t op)
|
void CLGTH(spu_opcode_t op)
|
||||||
{
|
{
|
||||||
set_vr(op.rt, sext<u16[8]>(get_vr<u16[8]>(op.ra) > get_vr<u16[8]>(op.rb)));
|
set_vr(op.rt, sext<s16[8]>(get_vr<u16[8]>(op.ra) > get_vr<u16[8]>(op.rb)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ORC(spu_opcode_t op)
|
void ORC(spu_opcode_t op)
|
||||||
@ -5357,12 +5359,12 @@ public:
|
|||||||
|
|
||||||
void CLGTB(spu_opcode_t op)
|
void CLGTB(spu_opcode_t op)
|
||||||
{
|
{
|
||||||
set_vr(op.rt, sext<u8[16]>(get_vr<u8[16]>(op.ra) > get_vr<u8[16]>(op.rb)));
|
set_vr(op.rt, sext<s8[16]>(get_vr<u8[16]>(op.ra) > get_vr<u8[16]>(op.rb)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CEQ(spu_opcode_t op)
|
void CEQ(spu_opcode_t op)
|
||||||
{
|
{
|
||||||
set_vr(op.rt, sext<u32[4]>(get_vr(op.ra) == get_vr(op.rb)));
|
set_vr(op.rt, sext<s32[4]>(get_vr(op.ra) == get_vr(op.rb)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MPYHHU(spu_opcode_t op)
|
void MPYHHU(spu_opcode_t op)
|
||||||
@ -5384,16 +5386,16 @@ public:
|
|||||||
{
|
{
|
||||||
const auto a = get_vr(op.ra);
|
const auto a = get_vr(op.ra);
|
||||||
const auto b = get_vr(op.rb);
|
const auto b = get_vr(op.rb);
|
||||||
const auto x = eval(~get_vr<u32[4]>(op.rt) & 1);
|
const auto x = ~get_vr(op.rt) & 1;
|
||||||
const auto s = eval(a + b);
|
const auto s = eval(a + b);
|
||||||
set_vr(op.rt, zext<u32[4]>((sext<u32[4]>(s < a) | (s & ~x)) == -1));
|
set_vr(op.rt, zext<u32[4]>((noncast<u32[4]>(sext<s32[4]>(s < a)) | (s & ~x)) == -1));
|
||||||
}
|
}
|
||||||
|
|
||||||
void BGX(spu_opcode_t op)
|
void BGX(spu_opcode_t op)
|
||||||
{
|
{
|
||||||
const auto a = get_vr(op.ra);
|
const auto a = get_vr(op.ra);
|
||||||
const auto b = get_vr(op.rb);
|
const auto b = get_vr(op.rb);
|
||||||
const auto c = eval(get_vr<s32[4]>(op.rt) << 31);
|
const auto c = get_vr<s32[4]>(op.rt) << 31;
|
||||||
set_vr(op.rt, zext<u32[4]>(a <= b & ~(a == b & c >= 0)));
|
set_vr(op.rt, zext<u32[4]>(a <= b & ~(a == b & c >= 0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5429,7 +5431,7 @@ public:
|
|||||||
|
|
||||||
void CEQH(spu_opcode_t op)
|
void CEQH(spu_opcode_t op)
|
||||||
{
|
{
|
||||||
set_vr(op.rt, sext<u16[8]>(get_vr<u16[8]>(op.ra) == get_vr<u16[8]>(op.rb)));
|
set_vr(op.rt, sext<s16[8]>(get_vr<u16[8]>(op.ra) == get_vr<u16[8]>(op.rb)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MPYU(spu_opcode_t op)
|
void MPYU(spu_opcode_t op)
|
||||||
@ -5439,24 +5441,13 @@ public:
|
|||||||
|
|
||||||
void CEQB(spu_opcode_t op)
|
void CEQB(spu_opcode_t op)
|
||||||
{
|
{
|
||||||
set_vr(op.rt, sext<u8[16]>(get_vr<u8[16]>(op.ra) == get_vr<u8[16]>(op.rb)));
|
set_vr(op.rt, sext<s8[16]>(get_vr<u8[16]>(op.ra) == get_vr<u8[16]>(op.rb)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void FSMBI(spu_opcode_t op)
|
void FSMBI(spu_opcode_t op)
|
||||||
{
|
{
|
||||||
if (m_interp_magn)
|
const auto m = bitcast<bool[16]>(get_imm<u16>(op.i16));
|
||||||
{
|
set_vr(op.rt, sext<s8[16]>(m));
|
||||||
const auto m = bitcast<bool[16]>(get_imm<u16>(op.i16));
|
|
||||||
set_vr(op.rt, sext<u8[16]>(m));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
v128 data;
|
|
||||||
for (u32 i = 0; i < 16; i++)
|
|
||||||
data._bytes[i] = op.i16 & (1u << i) ? -1 : 0;
|
|
||||||
value_t<u8[16]> r;
|
|
||||||
r.value = make_const_vector<v128>(data, get_type<u8[16]>());
|
|
||||||
set_vr(op.rt, r);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IL(spu_opcode_t op)
|
void IL(spu_opcode_t op)
|
||||||
@ -5546,32 +5537,32 @@ public:
|
|||||||
|
|
||||||
void CGTI(spu_opcode_t op)
|
void CGTI(spu_opcode_t op)
|
||||||
{
|
{
|
||||||
set_vr(op.rt, sext<u32[4]>(get_vr<s32[4]>(op.ra) > get_imm<s32[4]>(op.si10)));
|
set_vr(op.rt, sext<s32[4]>(get_vr<s32[4]>(op.ra) > get_imm<s32[4]>(op.si10)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGTHI(spu_opcode_t op)
|
void CGTHI(spu_opcode_t op)
|
||||||
{
|
{
|
||||||
set_vr(op.rt, sext<u16[8]>(get_vr<s16[8]>(op.ra) > get_imm<s16[8]>(op.si10)));
|
set_vr(op.rt, sext<s16[8]>(get_vr<s16[8]>(op.ra) > get_imm<s16[8]>(op.si10)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGTBI(spu_opcode_t op)
|
void CGTBI(spu_opcode_t op)
|
||||||
{
|
{
|
||||||
set_vr(op.rt, sext<u8[16]>(get_vr<s8[16]>(op.ra) > get_imm<s8[16]>(op.si10)));
|
set_vr(op.rt, sext<s8[16]>(get_vr<s8[16]>(op.ra) > get_imm<s8[16]>(op.si10)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CLGTI(spu_opcode_t op)
|
void CLGTI(spu_opcode_t op)
|
||||||
{
|
{
|
||||||
set_vr(op.rt, sext<u32[4]>(get_vr(op.ra) > get_imm(op.si10)));
|
set_vr(op.rt, sext<s32[4]>(get_vr(op.ra) > get_imm(op.si10)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CLGTHI(spu_opcode_t op)
|
void CLGTHI(spu_opcode_t op)
|
||||||
{
|
{
|
||||||
set_vr(op.rt, sext<u16[8]>(get_vr<u16[8]>(op.ra) > get_imm<u16[8]>(op.si10)));
|
set_vr(op.rt, sext<s16[8]>(get_vr<u16[8]>(op.ra) > get_imm<u16[8]>(op.si10)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CLGTBI(spu_opcode_t op)
|
void CLGTBI(spu_opcode_t op)
|
||||||
{
|
{
|
||||||
set_vr(op.rt, sext<u8[16]>(get_vr<u8[16]>(op.ra) > get_imm<u8[16]>(op.si10)));
|
set_vr(op.rt, sext<s8[16]>(get_vr<u8[16]>(op.ra) > get_imm<u8[16]>(op.si10)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MPYI(spu_opcode_t op)
|
void MPYI(spu_opcode_t op)
|
||||||
@ -5586,17 +5577,17 @@ public:
|
|||||||
|
|
||||||
void CEQI(spu_opcode_t op)
|
void CEQI(spu_opcode_t op)
|
||||||
{
|
{
|
||||||
set_vr(op.rt, sext<u32[4]>(get_vr(op.ra) == get_imm(op.si10)));
|
set_vr(op.rt, sext<s32[4]>(get_vr(op.ra) == get_imm(op.si10)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CEQHI(spu_opcode_t op)
|
void CEQHI(spu_opcode_t op)
|
||||||
{
|
{
|
||||||
set_vr(op.rt, sext<u16[8]>(get_vr<u16[8]>(op.ra) == get_imm<u16[8]>(op.si10)));
|
set_vr(op.rt, sext<s16[8]>(get_vr<u16[8]>(op.ra) == get_imm<u16[8]>(op.si10)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CEQBI(spu_opcode_t op)
|
void CEQBI(spu_opcode_t op)
|
||||||
{
|
{
|
||||||
set_vr(op.rt, sext<u8[16]>(get_vr<u8[16]>(op.ra) == get_imm<u8[16]>(op.si10)));
|
set_vr(op.rt, sext<s8[16]>(get_vr<u8[16]>(op.ra) == get_imm<u8[16]>(op.si10)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ILA(spu_opcode_t op)
|
void ILA(spu_opcode_t op)
|
||||||
@ -5819,11 +5810,11 @@ public:
|
|||||||
LOG_TODO(SPU, "[0x%x] Const SHUFB mask: %s", m_pos, mask);
|
LOG_TODO(SPU, "[0x%x] Const SHUFB mask: %s", m_pos, mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto x = avg(sext<u8[16]>((c & 0xc0) == 0xc0), sext<u8[16]>((c & 0xe0) == 0xc0));
|
const auto x = avg(noncast<u8[16]>(sext<s8[16]>((c & 0xc0) == 0xc0)), noncast<u8[16]>(sext<s8[16]>((c & 0xe0) == 0xc0)));
|
||||||
const auto cr = c ^ 0xf;
|
const auto cr = eval(c ^ 0xf);
|
||||||
const auto a = pshufb(get_vr<u8[16]>(op.ra), cr);
|
const auto a = pshufb(get_vr<u8[16]>(op.ra), cr);
|
||||||
const auto b = pshufb(get_vr<u8[16]>(op.rb), cr);
|
const auto b = pshufb(get_vr<u8[16]>(op.rb), cr);
|
||||||
set_vr(op.rt4, select(bitcast<s8[16]>(cr << 3) >= 0, a, b) | x);
|
set_vr(op.rt4, select(noncast<s8[16]>(cr << 3) >= 0, a, b) | x);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MPYA(spu_opcode_t op)
|
void MPYA(spu_opcode_t op)
|
||||||
@ -5924,7 +5915,7 @@ public:
|
|||||||
{
|
{
|
||||||
if (g_cfg.core.spu_accurate_xfloat)
|
if (g_cfg.core.spu_accurate_xfloat)
|
||||||
{
|
{
|
||||||
set_vr(op.rt, sext<u32[4]>(fcmp_ord(get_vr<f64[4]>(op.ra) > get_vr<f64[4]>(op.rb))));
|
set_vr(op.rt, sext<s32[4]>(fcmp_ord(get_vr<f64[4]>(op.ra) > get_vr<f64[4]>(op.rb))));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5941,11 +5932,11 @@ public:
|
|||||||
// Use sign bits to invert abs values before comparison.
|
// Use sign bits to invert abs values before comparison.
|
||||||
const auto ca = eval(ia ^ (bitcast<s32[4]>(a) >> 31));
|
const auto ca = eval(ia ^ (bitcast<s32[4]>(a) >> 31));
|
||||||
const auto cb = eval(ib ^ (bitcast<s32[4]>(b) >> 31));
|
const auto cb = eval(ib ^ (bitcast<s32[4]>(b) >> 31));
|
||||||
set_vr(op.rt, sext<u32[4]>((ca > cb) & nz));
|
set_vr(op.rt, sext<s32[4]>((ca > cb) & nz));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
set_vr(op.rt, sext<u32[4]>(fcmp_ord(a > b)));
|
set_vr(op.rt, sext<s32[4]>(fcmp_ord(a > b)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5953,7 +5944,7 @@ public:
|
|||||||
{
|
{
|
||||||
if (g_cfg.core.spu_accurate_xfloat)
|
if (g_cfg.core.spu_accurate_xfloat)
|
||||||
{
|
{
|
||||||
set_vr(op.rt, sext<u32[4]>(fcmp_ord(fabs(get_vr<f64[4]>(op.ra)) > fabs(get_vr<f64[4]>(op.rb)))));
|
set_vr(op.rt, sext<s32[4]>(fcmp_ord(fabs(get_vr<f64[4]>(op.ra)) > fabs(get_vr<f64[4]>(op.rb)))));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5969,11 +5960,11 @@ public:
|
|||||||
const auto ia = bitcast<s32[4]>(abs_a);
|
const auto ia = bitcast<s32[4]>(abs_a);
|
||||||
const auto ib = bitcast<s32[4]>(abs_b);
|
const auto ib = bitcast<s32[4]>(abs_b);
|
||||||
const auto nz = eval((ia > 0x7fffff) | (ib > 0x7fffff));
|
const auto nz = eval((ia > 0x7fffff) | (ib > 0x7fffff));
|
||||||
set_vr(op.rt, sext<u32[4]>((ia > ib) & nz));
|
set_vr(op.rt, sext<s32[4]>((ia > ib) & nz));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
set_vr(op.rt, sext<u32[4]>(fcmp_ord(abs_a > abs_b)));
|
set_vr(op.rt, sext<s32[4]>(fcmp_ord(abs_a > abs_b)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6065,17 +6056,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_ord(get_vr<f64[4]>(op.ra) == get_vr<f64[4]>(op.rb))));
|
set_vr(op.rt, sext<s32[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_ord(get_vr<f32[4]>(op.ra) == get_vr<f32[4]>(op.rb))));
|
set_vr(op.rt, sext<s32[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_ord(fabs(get_vr<f64[4]>(op.ra)) == fabs(get_vr<f64[4]>(op.rb)))));
|
set_vr(op.rt, sext<s32[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_ord(fabs(get_vr<f32[4]>(op.ra)) == fabs(get_vr<f32[4]>(op.rb)))));
|
set_vr(op.rt, sext<s32[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.
|
||||||
@ -6084,7 +6075,7 @@ public:
|
|||||||
// Compare absolute values with max positive float in normal range.
|
// Compare absolute values with max positive float in normal range.
|
||||||
const auto aa = bitcast<s32[4]>(fabs(a));
|
const auto aa = bitcast<s32[4]>(fabs(a));
|
||||||
const auto ab = bitcast<s32[4]>(fabs(b));
|
const auto ab = bitcast<s32[4]>(fabs(b));
|
||||||
return select(eval(max(aa, ab) > 0x7f7fffff), fsplat<f32[4]>(0.), eval(a * b));
|
return eval(select(max(aa, ab) > 0x7f7fffff, fsplat<f32[4]>(0.), a * b));
|
||||||
}
|
}
|
||||||
|
|
||||||
void FNMS(spu_opcode_t op)
|
void FNMS(spu_opcode_t op)
|
||||||
@ -6365,7 +6356,7 @@ public:
|
|||||||
|
|
||||||
void STQX(spu_opcode_t op)
|
void STQX(spu_opcode_t op)
|
||||||
{
|
{
|
||||||
value_t<u64> addr = zext<u64>((extract(get_vr(op.ra), 3) + extract(get_vr(op.rb), 3)) & 0x3fff0);
|
value_t<u64> addr = eval(zext<u64>((extract(get_vr(op.ra), 3) + extract(get_vr(op.rb), 3)) & 0x3fff0));
|
||||||
value_t<u8[16]> r = get_vr<u8[16]>(op.rt);
|
value_t<u8[16]> r = get_vr<u8[16]>(op.rt);
|
||||||
r.value = m_ir->CreateShuffleVector(r.value, r.value, {15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0});
|
r.value = m_ir->CreateShuffleVector(r.value, r.value, {15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0});
|
||||||
m_ir->CreateStore(r.value, m_ir->CreateBitCast(m_ir->CreateGEP(m_lsptr, addr.value), get_type<u8(*)[16]>()));
|
m_ir->CreateStore(r.value, m_ir->CreateBitCast(m_ir->CreateGEP(m_lsptr, addr.value), get_type<u8(*)[16]>()));
|
||||||
@ -6373,7 +6364,7 @@ public:
|
|||||||
|
|
||||||
void LQX(spu_opcode_t op)
|
void LQX(spu_opcode_t op)
|
||||||
{
|
{
|
||||||
value_t<u64> addr = zext<u64>((extract(get_vr(op.ra), 3) + extract(get_vr(op.rb), 3)) & 0x3fff0);
|
value_t<u64> addr = eval(zext<u64>((extract(get_vr(op.ra), 3) + extract(get_vr(op.rb), 3)) & 0x3fff0));
|
||||||
value_t<u8[16]> r;
|
value_t<u8[16]> r;
|
||||||
r.value = m_ir->CreateLoad(m_ir->CreateBitCast(m_ir->CreateGEP(m_lsptr, addr.value), get_type<u8(*)[16]>()));
|
r.value = m_ir->CreateLoad(m_ir->CreateBitCast(m_ir->CreateGEP(m_lsptr, addr.value), get_type<u8(*)[16]>()));
|
||||||
r.value = m_ir->CreateShuffleVector(r.value, r.value, {15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0});
|
r.value = m_ir->CreateShuffleVector(r.value, r.value, {15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0});
|
||||||
@ -6400,8 +6391,8 @@ public:
|
|||||||
void STQR(spu_opcode_t op) //
|
void STQR(spu_opcode_t op) //
|
||||||
{
|
{
|
||||||
value_t<u64> addr;
|
value_t<u64> addr;
|
||||||
addr.value = m_interp_magn ? m_interp_pc : m_ir->getInt32(m_pos);
|
addr.value = m_interp_magn ? m_ir->CreateZExt(m_interp_pc, get_type<u64>()) : m_ir->getInt64(m_pos);
|
||||||
addr = eval(((get_imm<u64>(op.i16, false) << 2) + zext<u64>(addr)) & 0x3fff0);
|
addr = eval(((get_imm<u64>(op.i16, false) << 2) + addr) & 0x3fff0);
|
||||||
value_t<u8[16]> r = get_vr<u8[16]>(op.rt);
|
value_t<u8[16]> r = get_vr<u8[16]>(op.rt);
|
||||||
r.value = m_ir->CreateShuffleVector(r.value, r.value, {15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0});
|
r.value = m_ir->CreateShuffleVector(r.value, r.value, {15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0});
|
||||||
m_ir->CreateStore(r.value, m_ir->CreateBitCast(m_ir->CreateGEP(m_lsptr, addr.value), get_type<u8(*)[16]>()));
|
m_ir->CreateStore(r.value, m_ir->CreateBitCast(m_ir->CreateGEP(m_lsptr, addr.value), get_type<u8(*)[16]>()));
|
||||||
@ -6410,8 +6401,8 @@ public:
|
|||||||
void LQR(spu_opcode_t op) //
|
void LQR(spu_opcode_t op) //
|
||||||
{
|
{
|
||||||
value_t<u64> addr;
|
value_t<u64> addr;
|
||||||
addr.value = m_interp_magn ? m_interp_pc : m_ir->getInt32(m_pos);
|
addr.value = m_interp_magn ? m_ir->CreateZExt(m_interp_pc, get_type<u64>()) : m_ir->getInt64(m_pos);
|
||||||
addr = eval(((get_imm<u64>(op.i16, false) << 2) + zext<u64>(addr)) & 0x3fff0);
|
addr = eval(((get_imm<u64>(op.i16, false) << 2) + addr) & 0x3fff0);
|
||||||
value_t<u8[16]> r;
|
value_t<u8[16]> r;
|
||||||
r.value = m_ir->CreateLoad(m_ir->CreateBitCast(m_ir->CreateGEP(m_lsptr, addr.value), get_type<u8(*)[16]>()));
|
r.value = m_ir->CreateLoad(m_ir->CreateBitCast(m_ir->CreateGEP(m_lsptr, addr.value), get_type<u8(*)[16]>()));
|
||||||
r.value = m_ir->CreateShuffleVector(r.value, r.value, {15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0});
|
r.value = m_ir->CreateShuffleVector(r.value, r.value, {15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0});
|
||||||
@ -6420,7 +6411,7 @@ public:
|
|||||||
|
|
||||||
void STQD(spu_opcode_t op)
|
void STQD(spu_opcode_t op)
|
||||||
{
|
{
|
||||||
value_t<u64> addr = zext<u64>((extract(get_vr(op.ra), 3) + (get_imm<u32>(op.si10) << 4)) & 0x3fff0);
|
value_t<u64> addr = eval(zext<u64>((extract(get_vr(op.ra), 3) + (get_imm<u32>(op.si10) << 4)) & 0x3fff0));
|
||||||
value_t<u8[16]> r = get_vr<u8[16]>(op.rt);
|
value_t<u8[16]> r = get_vr<u8[16]>(op.rt);
|
||||||
r.value = m_ir->CreateShuffleVector(r.value, r.value, {15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0});
|
r.value = m_ir->CreateShuffleVector(r.value, r.value, {15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0});
|
||||||
m_ir->CreateStore(r.value, m_ir->CreateBitCast(m_ir->CreateGEP(m_lsptr, addr.value), get_type<u8(*)[16]>()));
|
m_ir->CreateStore(r.value, m_ir->CreateBitCast(m_ir->CreateGEP(m_lsptr, addr.value), get_type<u8(*)[16]>()));
|
||||||
@ -6428,7 +6419,7 @@ public:
|
|||||||
|
|
||||||
void LQD(spu_opcode_t op)
|
void LQD(spu_opcode_t op)
|
||||||
{
|
{
|
||||||
value_t<u64> addr = zext<u64>((extract(get_vr(op.ra), 3) + (get_imm<u32>(op.si10) << 4)) & 0x3fff0);
|
value_t<u64> addr = eval(zext<u64>((extract(get_vr(op.ra), 3) + (get_imm<u32>(op.si10) << 4)) & 0x3fff0));
|
||||||
value_t<u8[16]> r;
|
value_t<u8[16]> r;
|
||||||
r.value = m_ir->CreateLoad(m_ir->CreateBitCast(m_ir->CreateGEP(m_lsptr, addr.value), get_type<u8(*)[16]>()));
|
r.value = m_ir->CreateLoad(m_ir->CreateBitCast(m_ir->CreateGEP(m_lsptr, addr.value), get_type<u8(*)[16]>()));
|
||||||
r.value = m_ir->CreateShuffleVector(r.value, r.value, {15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0});
|
r.value = m_ir->CreateShuffleVector(r.value, r.value, {15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user