mirror of
https://github.com/RPCS3/rpcs3.git
synced 2024-11-22 18:53:28 +01: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/IRBuilder.h"
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/Analysis/ConstantFolding.h"
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
@ -951,6 +952,226 @@ inline llvm_cmp<T1, llvm_const_int<typename is_llvm_expr<T1>::type>, llvm::ICmpI
|
||||
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
|
||||
{
|
||||
protected:
|
||||
@ -1027,36 +1248,52 @@ public:
|
||||
return llvm_uno{std::forward<T>(cmp_expr)};
|
||||
}
|
||||
|
||||
template <typename T, typename T2>
|
||||
value_t<T> bitcast(T2 expr)
|
||||
template <typename U, typename T, typename = std::enable_if_t<llvm_noncast<U, T>::is_ok>>
|
||||
static auto noncast(T&& expr)
|
||||
{
|
||||
value_t<T> result;
|
||||
result.value = m_ir->CreateBitCast(expr.eval(m_ir), result.get_type(m_context));
|
||||
return result;
|
||||
return llvm_noncast<U, T>{std::forward<T>(expr)};
|
||||
}
|
||||
|
||||
template <typename T, typename T2>
|
||||
value_t<T> trunc(T2 expr)
|
||||
template <typename U, typename T, typename = std::enable_if_t<llvm_bitcast<U, T>::is_ok>>
|
||||
static auto bitcast(T&& expr)
|
||||
{
|
||||
value_t<T> result;
|
||||
result.value = m_ir->CreateTrunc(expr.eval(m_ir), result.get_type(m_context));
|
||||
return result;
|
||||
return llvm_bitcast<U, T>{std::forward<T>(expr)};
|
||||
}
|
||||
|
||||
template <typename T, typename T2>
|
||||
value_t<T> sext(T2 expr)
|
||||
template <typename U, typename T, typename = std::enable_if_t<llvm_trunc<U, T>::is_ok>>
|
||||
static auto trunc(T&& expr)
|
||||
{
|
||||
value_t<T> result;
|
||||
result.value = m_ir->CreateSExt(expr.eval(m_ir), result.get_type(m_context));
|
||||
return result;
|
||||
return llvm_trunc<U, T>{std::forward<T>(expr)};
|
||||
}
|
||||
|
||||
template <typename T, typename T2>
|
||||
value_t<T> zext(T2 expr)
|
||||
template <typename U, typename T, typename = std::enable_if_t<llvm_sext<U, T>::is_ok>>
|
||||
static auto sext(T&& expr)
|
||||
{
|
||||
value_t<T> result;
|
||||
result.value = m_ir->CreateZExt(expr.eval(m_ir), result.get_type(m_context));
|
||||
return result;
|
||||
return llvm_sext<U, T>{std::forward<T>(expr)};
|
||||
}
|
||||
|
||||
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)
|
||||
@ -1194,17 +1431,6 @@ public:
|
||||
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>
|
||||
auto insert(T v, u64 i, E e)
|
||||
{
|
||||
@ -1246,24 +1472,6 @@ public:
|
||||
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
|
||||
template <typename T, typename T1, typename... Args>
|
||||
auto zshuffle(T1 a, Args... args)
|
||||
|
@ -271,7 +271,7 @@ extern void ppu_register_range(u32 addr, u32 size)
|
||||
// Register executable range at
|
||||
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));
|
||||
|
||||
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
|
||||
module->setTargetTriple(Triple::normalize(sys::getProcessTriple()));
|
||||
module->setDataLayout(jit.get_engine().getTargetMachine()->createDataLayout());
|
||||
|
||||
// Initialize translator
|
||||
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 b = get_vr<u16[8]>(op.vb);
|
||||
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 mh = bitcast<s32[4]>((a >> 8) * bitcast<s16[8]>(b >> 8));
|
||||
const auto ml = bitcast<s32[4]>((a << 8 >> 8) * noncast<s16[8]>(b << 8 >> 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));
|
||||
}
|
||||
|
||||
@ -1191,7 +1191,7 @@ void PPUTranslator::VPERM(ppu_opcode_t op)
|
||||
const auto b = get_vr<u8[16]>(op.vb);
|
||||
const auto c = get_vr<u8[16]>(op.vc);
|
||||
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)
|
||||
|
@ -3087,6 +3087,7 @@ public:
|
||||
// Create LLVM module
|
||||
std::unique_ptr<Module> module = std::make_unique<Module>(hash + ".obj", m_context);
|
||||
module->setTargetTriple(Triple::normalize(sys::getProcessTriple()));
|
||||
module->setDataLayout(m_jit.get_engine().getTargetMachine()->createDataLayout());
|
||||
m_module = module.get();
|
||||
|
||||
// Initialize IR Builder
|
||||
@ -3587,6 +3588,7 @@ public:
|
||||
// Create LLVM module
|
||||
std::unique_ptr<Module> module = std::make_unique<Module>("spu_interpreter.obj", m_context);
|
||||
module->setTargetTriple(Triple::normalize(sys::getProcessTriple()));
|
||||
module->setDataLayout(m_jit.get_engine().getTargetMachine()->createDataLayout());
|
||||
m_module = module.get();
|
||||
|
||||
// Initialize IR Builder
|
||||
@ -4425,12 +4427,12 @@ public:
|
||||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
case MFC_Cmd:
|
||||
@ -4447,14 +4449,14 @@ public:
|
||||
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 lsa = get_reg_fixed<u32>(s_reg_mfc_lsa);
|
||||
const auto tag = get_reg_fixed<u8>(s_reg_mfc_tag);
|
||||
|
||||
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 fail = llvm::BasicBlock::Create(m_context, "", m_function);
|
||||
const auto next = llvm::BasicBlock::Create(m_context, "", m_function);
|
||||
@ -4515,8 +4517,8 @@ public:
|
||||
csize = -1;
|
||||
}
|
||||
|
||||
llvm::Value* src = m_ir->CreateGEP(m_lsptr, zext<u64>(lsa).value);
|
||||
llvm::Value* dst = m_ir->CreateGEP(m_memptr, zext<u64>(eal).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).eval(m_ir));
|
||||
|
||||
if (cmd & MFC_GET_CMD)
|
||||
{
|
||||
@ -4599,7 +4601,7 @@ public:
|
||||
else
|
||||
{
|
||||
// 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);
|
||||
@ -4840,7 +4842,7 @@ public:
|
||||
if constexpr (!by.is_vector)
|
||||
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>
|
||||
@ -4866,7 +4868,7 @@ public:
|
||||
if constexpr (!by.is_vector)
|
||||
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)
|
||||
@ -4996,7 +4998,7 @@ public:
|
||||
}
|
||||
|
||||
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)
|
||||
@ -5014,7 +5016,7 @@ public:
|
||||
}
|
||||
|
||||
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)
|
||||
@ -5032,7 +5034,7 @@ public:
|
||||
}
|
||||
|
||||
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)
|
||||
@ -5047,7 +5049,7 @@ public:
|
||||
}
|
||||
|
||||
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)
|
||||
@ -5062,7 +5064,7 @@ public:
|
||||
}
|
||||
|
||||
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)
|
||||
@ -5077,7 +5079,7 @@ public:
|
||||
}
|
||||
|
||||
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)
|
||||
@ -5276,7 +5278,7 @@ public:
|
||||
|
||||
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)
|
||||
@ -5286,7 +5288,7 @@ public:
|
||||
|
||||
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)
|
||||
@ -5296,7 +5298,7 @@ public:
|
||||
|
||||
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)
|
||||
@ -5337,7 +5339,7 @@ public:
|
||||
|
||||
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)
|
||||
@ -5347,7 +5349,7 @@ public:
|
||||
|
||||
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)
|
||||
@ -5357,12 +5359,12 @@ public:
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
@ -5384,16 +5386,16 @@ public:
|
||||
{
|
||||
const auto a = get_vr(op.ra);
|
||||
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);
|
||||
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)
|
||||
{
|
||||
const auto a = get_vr(op.ra);
|
||||
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)));
|
||||
}
|
||||
|
||||
@ -5429,7 +5431,7 @@ public:
|
||||
|
||||
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)
|
||||
@ -5439,24 +5441,13 @@ public:
|
||||
|
||||
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)
|
||||
{
|
||||
if (m_interp_magn)
|
||||
{
|
||||
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);
|
||||
const auto m = bitcast<bool[16]>(get_imm<u16>(op.i16));
|
||||
set_vr(op.rt, sext<s8[16]>(m));
|
||||
}
|
||||
|
||||
void IL(spu_opcode_t op)
|
||||
@ -5546,32 +5537,32 @@ public:
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
@ -5586,17 +5577,17 @@ public:
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
@ -5819,11 +5810,11 @@ public:
|
||||
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 cr = c ^ 0xf;
|
||||
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 = eval(c ^ 0xf);
|
||||
const auto a = pshufb(get_vr<u8[16]>(op.ra), 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)
|
||||
@ -5924,7 +5915,7 @@ public:
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@ -5941,11 +5932,11 @@ public:
|
||||
// Use sign bits to invert abs values before comparison.
|
||||
const auto ca = eval(ia ^ (bitcast<s32[4]>(a) >> 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
|
||||
{
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@ -5969,11 +5960,11 @@ public:
|
||||
const auto ia = bitcast<s32[4]>(abs_a);
|
||||
const auto ib = bitcast<s32[4]>(abs_b);
|
||||
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
|
||||
{
|
||||
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)
|
||||
{
|
||||
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
|
||||
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)
|
||||
{
|
||||
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
|
||||
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.
|
||||
@ -6084,7 +6075,7 @@ public:
|
||||
// Compare absolute values with max positive float in normal range.
|
||||
const auto aa = bitcast<s32[4]>(fabs(a));
|
||||
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)
|
||||
@ -6365,7 +6356,7 @@ public:
|
||||
|
||||
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);
|
||||
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]>()));
|
||||
@ -6373,7 +6364,7 @@ public:
|
||||
|
||||
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;
|
||||
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});
|
||||
@ -6400,8 +6391,8 @@ public:
|
||||
void STQR(spu_opcode_t op) //
|
||||
{
|
||||
value_t<u64> addr;
|
||||
addr.value = m_interp_magn ? m_interp_pc : m_ir->getInt32(m_pos);
|
||||
addr = eval(((get_imm<u64>(op.i16, false) << 2) + zext<u64>(addr)) & 0x3fff0);
|
||||
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) + addr) & 0x3fff0);
|
||||
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});
|
||||
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) //
|
||||
{
|
||||
value_t<u64> addr;
|
||||
addr.value = m_interp_magn ? m_interp_pc : m_ir->getInt32(m_pos);
|
||||
addr = eval(((get_imm<u64>(op.i16, false) << 2) + zext<u64>(addr)) & 0x3fff0);
|
||||
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) + addr) & 0x3fff0);
|
||||
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->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)
|
||||
{
|
||||
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);
|
||||
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]>()));
|
||||
@ -6428,7 +6419,7 @@ public:
|
||||
|
||||
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;
|
||||
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});
|
||||
|
Loading…
Reference in New Issue
Block a user