mirror of
https://github.com/RPCS3/rpcs3.git
synced 2026-07-09 17:25:18 -06:00
SPU LLVM: SVE multiply optimizations
- Reduces most SPU multiply instructions to a single operation - Add SVE length detection, only use SVE instructions for 128b sve for now
This commit is contained in:
parent
2f2ac69d60
commit
6349ea2ee9
@ -3790,8 +3790,81 @@ template <typename T1, typename T2, typename T3>
|
||||
|
||||
return from_sve_vector(result, fixed_type);
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
|
||||
template <typename T, typename T1, typename T2>
|
||||
value_t<T> sve_mull(llvm::Intrinsic::ID id, T1 a, T2 b)
|
||||
{
|
||||
value_t<T> result;
|
||||
|
||||
const auto fixed_type = llvm::cast<llvm::FixedVectorType>(get_type<T>());
|
||||
const auto scalable_type = llvm::ScalableVectorType::get(fixed_type->getElementType(), fixed_type->getNumElements());
|
||||
const auto data0 = to_sve_vector(a.eval(m_ir));
|
||||
const auto data1 = to_sve_vector(b.eval(m_ir));
|
||||
const std::array<llvm::Type*, 1> types{scalable_type};
|
||||
|
||||
result.value = from_sve_vector(m_ir->CreateIntrinsic(id, types, {data0, data1}), fixed_type);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, typename T0, typename T1, typename T2>
|
||||
value_t<T> sve_mlal(llvm::Intrinsic::ID id, T0 acc, T1 a, T2 b)
|
||||
{
|
||||
value_t<T> result;
|
||||
|
||||
const auto fixed_type = llvm::cast<llvm::FixedVectorType>(get_type<T>());
|
||||
const auto scalable_type = llvm::ScalableVectorType::get(fixed_type->getElementType(), fixed_type->getNumElements());
|
||||
const auto data0 = to_sve_vector(acc.eval(m_ir));
|
||||
const auto data1 = to_sve_vector(a.eval(m_ir));
|
||||
const auto data2 = to_sve_vector(b.eval(m_ir));
|
||||
const std::array<llvm::Type*, 1> types{scalable_type};
|
||||
|
||||
result.value = from_sve_vector(m_ir->CreateIntrinsic(id, types, {data0, data1, data2}), fixed_type);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
value_t<s32[4]> sve_smullb(T1 a, T2 b)
|
||||
{
|
||||
return sve_mull<s32[4]>(llvm::Intrinsic::aarch64_sve_smullb, a, b);
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
value_t<s32[4]> sve_smullt(T1 a, T2 b)
|
||||
{
|
||||
return sve_mull<s32[4]>(llvm::Intrinsic::aarch64_sve_smullt, a, b);
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
value_t<u32[4]> sve_umullb(T1 a, T2 b)
|
||||
{
|
||||
return sve_mull<u32[4]>(llvm::Intrinsic::aarch64_sve_umullb, a, b);
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
value_t<u32[4]> sve_umullt(T1 a, T2 b)
|
||||
{
|
||||
return sve_mull<u32[4]>(llvm::Intrinsic::aarch64_sve_umullt, a, b);
|
||||
}
|
||||
|
||||
template <typename T0, typename T1, typename T2>
|
||||
value_t<s32[4]> sve_smlalb(T0 acc, T1 a, T2 b)
|
||||
{
|
||||
return sve_mlal<s32[4]>(llvm::Intrinsic::aarch64_sve_smlalb, acc, a, b);
|
||||
}
|
||||
|
||||
template <typename T0, typename T1, typename T2>
|
||||
value_t<s32[4]> sve_smlalt(T0 acc, T1 a, T2 b)
|
||||
{
|
||||
return sve_mlal<s32[4]>(llvm::Intrinsic::aarch64_sve_smlalt, acc, a, b);
|
||||
}
|
||||
|
||||
template <typename T0, typename T1, typename T2>
|
||||
value_t<u32[4]> sve_umlalt(T0 acc, T1 a, T2 b)
|
||||
{
|
||||
return sve_mlal<u32[4]>(llvm::Intrinsic::aarch64_sve_umlalt, acc, a, b);
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
auto addp(T1 a, T2 b)
|
||||
{
|
||||
using T_vector = typename is_llvm_expr<T1>::type;
|
||||
|
||||
@ -6265,6 +6265,15 @@ public:
|
||||
|
||||
void MPYHHU(spu_opcode_t op)
|
||||
{
|
||||
#ifdef ARCH_ARM64
|
||||
const auto [a, b] = get_vrs<u32[4]>(op.ra, op.rb);
|
||||
|
||||
if (m_use_sve2_128)
|
||||
{
|
||||
set_vr(op.rt, sve_umullt(bitcast<u16[8]>(a), bitcast<u16[8]>(b)));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
set_vr(op.rt, (get_vr(op.ra) >> 16) * (get_vr(op.rb) >> 16));
|
||||
}
|
||||
|
||||
@ -6295,11 +6304,29 @@ public:
|
||||
|
||||
void MPYHHA(spu_opcode_t op)
|
||||
{
|
||||
#ifdef ARCH_ARM64
|
||||
const auto [a, b] = get_vrs<s32[4]>(op.ra, op.rb);
|
||||
|
||||
if (m_use_sve2_128)
|
||||
{
|
||||
set_vr(op.rt, sve_smlalt(get_vr<s32[4]>(op.rt), bitcast<s16[8]>(a), bitcast<s16[8]>(b)));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
set_vr(op.rt, (get_vr<s32[4]>(op.ra) >> 16) * (get_vr<s32[4]>(op.rb) >> 16) + get_vr<s32[4]>(op.rt));
|
||||
}
|
||||
|
||||
void MPYHHAU(spu_opcode_t op)
|
||||
{
|
||||
#ifdef ARCH_ARM64
|
||||
const auto [a, b] = get_vrs<u32[4]>(op.ra, op.rb);
|
||||
|
||||
if (m_use_sve2_128)
|
||||
{
|
||||
set_vr(op.rt, sve_umlalt(get_vr<u32[4]>(op.rt), bitcast<u16[8]>(a), bitcast<u16[8]>(b)));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
set_vr(op.rt, (get_vr(op.ra) >> 16) * (get_vr(op.rb) >> 16) + get_vr(op.rt));
|
||||
}
|
||||
|
||||
@ -6307,7 +6334,15 @@ public:
|
||||
{
|
||||
#ifdef ARCH_ARM64
|
||||
const auto [a, b] = get_vrs<s32[4]>(op.ra, op.rb);
|
||||
set_vr(op.rt, smull(zshuffle(bitcast<s16[8]>(a), 0, 2, 4, 6), zshuffle(bitcast<s16[8]>(b), 0, 2, 4, 6)));
|
||||
|
||||
if (m_use_sve2_128)
|
||||
{
|
||||
set_vr(op.rt, sve_smullb(bitcast<s16[8]>(a), bitcast<s16[8]>(b)));
|
||||
}
|
||||
else
|
||||
{
|
||||
set_vr(op.rt, smull(zshuffle(bitcast<s16[8]>(a), 0, 2, 4, 6), zshuffle(bitcast<s16[8]>(b), 0, 2, 4, 6)));
|
||||
}
|
||||
#else
|
||||
set_vr(op.rt, (get_vr<s32[4]>(op.ra) << 16 >> 16) * (get_vr<s32[4]>(op.rb) << 16 >> 16));
|
||||
#endif
|
||||
@ -6320,6 +6355,15 @@ public:
|
||||
|
||||
void MPYHH(spu_opcode_t op)
|
||||
{
|
||||
#ifdef ARCH_ARM64
|
||||
const auto [a, b] = get_vrs<s32[4]>(op.ra, op.rb);
|
||||
|
||||
if (m_use_sve2_128)
|
||||
{
|
||||
set_vr(op.rt, sve_smullt(bitcast<s16[8]>(a), bitcast<s16[8]>(b)));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
set_vr(op.rt, (get_vr<s32[4]>(op.ra) >> 16) * (get_vr<s32[4]>(op.rb) >> 16));
|
||||
}
|
||||
|
||||
@ -6327,7 +6371,15 @@ public:
|
||||
{
|
||||
#ifdef ARCH_ARM64
|
||||
const auto [a, b] = get_vrs<s32[4]>(op.ra, op.rb);
|
||||
set_vr(op.rt, smull(zshuffle(bitcast<s16[8]>(a), 0, 2, 4, 6), zshuffle(bitcast<s16[8]>(b), 0, 2, 4, 6)) >> 16);
|
||||
|
||||
if (m_use_sve2_128)
|
||||
{
|
||||
set_vr(op.rt, sve_smullb(bitcast<s16[8]>(a), bitcast<s16[8]>(b)) >> 16);
|
||||
}
|
||||
else
|
||||
{
|
||||
set_vr(op.rt, smull(zshuffle(bitcast<s16[8]>(a), 0, 2, 4, 6), zshuffle(bitcast<s16[8]>(b), 0, 2, 4, 6)) >> 16);
|
||||
}
|
||||
#else
|
||||
set_vr(op.rt, (get_vr<s32[4]>(op.ra) << 16 >> 16) * (get_vr<s32[4]>(op.rb) << 16 >> 16) >> 16);
|
||||
#endif
|
||||
@ -6342,7 +6394,15 @@ public:
|
||||
{
|
||||
#ifdef ARCH_ARM64
|
||||
const auto [a, b] = get_vrs<u32[4]>(op.ra, op.rb);
|
||||
set_vr(op.rt, umull(zshuffle(bitcast<u16[8]>(a), 0, 2, 4, 6), zshuffle(bitcast<u16[8]>(b), 0, 2, 4, 6)));
|
||||
|
||||
if (m_use_sve2_128)
|
||||
{
|
||||
set_vr(op.rt, sve_umullb(bitcast<u16[8]>(a), bitcast<u16[8]>(b)));
|
||||
}
|
||||
else
|
||||
{
|
||||
set_vr(op.rt, umull(zshuffle(bitcast<u16[8]>(a), 0, 2, 4, 6), zshuffle(bitcast<u16[8]>(b), 0, 2, 4, 6)));
|
||||
}
|
||||
#else
|
||||
set_vr(op.rt, mpyu(get_vr(op.ra), get_vr(op.rb)));
|
||||
#endif
|
||||
@ -6495,7 +6555,14 @@ public:
|
||||
void MPYI(spu_opcode_t op)
|
||||
{
|
||||
#ifdef ARCH_ARM64
|
||||
set_vr(op.rt, smull(zshuffle(bitcast<s16[8]>(get_vr<s32[4]>(op.ra)), 0, 2, 4, 6), get_imm<s16[4]>(op.si10)));
|
||||
if (m_use_sve2_128)
|
||||
{
|
||||
set_vr(op.rt, sve_smullb(bitcast<s16[8]>(get_vr<s32[4]>(op.ra)), get_imm<s16[8]>(op.si10)));
|
||||
}
|
||||
else
|
||||
{
|
||||
set_vr(op.rt, smull(zshuffle(bitcast<s16[8]>(get_vr<s32[4]>(op.ra)), 0, 2, 4, 6), get_imm<s16[4]>(op.si10)));
|
||||
}
|
||||
#else
|
||||
set_vr(op.rt, (get_vr<s32[4]>(op.ra) << 16 >> 16) * get_imm<s32[4]>(op.si10));
|
||||
#endif
|
||||
@ -6504,7 +6571,14 @@ public:
|
||||
void MPYUI(spu_opcode_t op)
|
||||
{
|
||||
#ifdef ARCH_ARM64
|
||||
set_vr(op.rt, umull(zshuffle(bitcast<u16[8]>(get_vr<u32[4]>(op.ra)), 0, 2, 4, 6), get_imm<u16[4]>(op.si10)));
|
||||
if (m_use_sve2_128)
|
||||
{
|
||||
set_vr(op.rt, sve_umullb(bitcast<u16[8]>(get_vr<u32[4]>(op.ra)), get_imm<u16[8]>(op.si10)));
|
||||
}
|
||||
else
|
||||
{
|
||||
set_vr(op.rt, umull(zshuffle(bitcast<u16[8]>(get_vr<u32[4]>(op.ra)), 0, 2, 4, 6), get_imm<u16[4]>(op.si10)));
|
||||
}
|
||||
#else
|
||||
set_vr(op.rt, (get_vr(op.ra) << 16 >> 16) * (get_imm(op.si10) & 0xffff));
|
||||
#endif
|
||||
@ -7091,7 +7165,15 @@ public:
|
||||
{
|
||||
#ifdef ARCH_ARM64
|
||||
const auto [a, b] = get_vrs<s32[4]>(op.ra, op.rb);
|
||||
set_vr(op.rt4, smull(zshuffle(bitcast<s16[8]>(a), 0, 2, 4, 6), zshuffle(bitcast<s16[8]>(b), 0, 2, 4, 6)) + get_vr<s32[4]>(op.rc));
|
||||
|
||||
if (m_use_sve2_128)
|
||||
{
|
||||
set_vr(op.rt4, sve_smlalb(get_vr<s32[4]>(op.rc), bitcast<s16[8]>(a), bitcast<s16[8]>(b)));
|
||||
}
|
||||
else
|
||||
{
|
||||
set_vr(op.rt4, smull(zshuffle(bitcast<s16[8]>(a), 0, 2, 4, 6), zshuffle(bitcast<s16[8]>(b), 0, 2, 4, 6)) + get_vr<s32[4]>(op.rc));
|
||||
}
|
||||
#else
|
||||
set_vr(op.rt4, (get_vr<s32[4]>(op.ra) << 16 >> 16) * (get_vr<s32[4]>(op.rb) << 16 >> 16) + get_vr<s32[4]>(op.rc));
|
||||
#endif
|
||||
|
||||
@ -531,13 +531,18 @@ std::string utils::get_system_info()
|
||||
}
|
||||
#ifdef ARCH_ARM64
|
||||
|
||||
if (has_neon())
|
||||
if (!has_neon())
|
||||
{
|
||||
result += " | Neon";
|
||||
fmt::throw_exception("Neon support not present");
|
||||
}
|
||||
|
||||
if (has_sve())
|
||||
{
|
||||
fmt::append(result, " | SVE%s-%d", has_sve2() ? "2" : "", sve_length());
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::throw_exception("Neon support not present");
|
||||
result += " | Neon";
|
||||
}
|
||||
#else
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user