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:
Nekotekina 2019-04-18 17:18:46 +03:00
parent 3925cb59ac
commit 524aac75ed
4 changed files with 324 additions and 124 deletions

View File

@ -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)

View File

@ -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());

View File

@ -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)

View File

@ -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});