SPU LLVM: Use I8MM for GBH and GBB
This commit is contained in:
@@ -208,6 +208,11 @@ void cpu_translator::initialize(llvm::LLVMContext& context, llvm::ExecutionEngin
|
|||||||
m_use_dotprod = true;
|
m_use_dotprod = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (utils::has_i8mm())
|
||||||
|
{
|
||||||
|
m_use_i8mm = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (utils::has_sve() && utils::sve_length() == 128)
|
if (utils::has_sve() && utils::sve_length() == 128)
|
||||||
{
|
{
|
||||||
m_use_sve_128 = true;
|
m_use_sve_128 = true;
|
||||||
|
|||||||
@@ -3121,6 +3121,9 @@ protected:
|
|||||||
// ARMv8 SDOT/UDOT
|
// ARMv8 SDOT/UDOT
|
||||||
bool m_use_dotprod = false;
|
bool m_use_dotprod = false;
|
||||||
|
|
||||||
|
// ARMv8.6 SMMLA/UMMLA
|
||||||
|
bool m_use_i8mm = false;
|
||||||
|
|
||||||
// Allow direct TBL2/TBX2 emission.
|
// Allow direct TBL2/TBX2 emission.
|
||||||
bool m_use_tbl2 = true;
|
bool m_use_tbl2 = true;
|
||||||
|
|
||||||
@@ -3728,6 +3731,32 @@ template <typename T1, typename T2, typename T3>
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T1, typename T2, typename T3>
|
||||||
|
value_t<u32[4]> ummla(T1 a, T2 b, T3 c)
|
||||||
|
{
|
||||||
|
value_t<u32[4]> result;
|
||||||
|
|
||||||
|
const auto data0 = a.eval(m_ir);
|
||||||
|
const auto data1 = b.eval(m_ir);
|
||||||
|
const auto data2 = c.eval(m_ir);
|
||||||
|
|
||||||
|
result.value = m_ir->CreateCall(get_intrinsic<u32[4], u8[16]>(llvm::Intrinsic::aarch64_neon_ummla), {data0, data1, data2});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T1, typename T2, typename T3>
|
||||||
|
value_t<u32[4]> smmla(T1 a, T2 b, T3 c)
|
||||||
|
{
|
||||||
|
value_t<u32[4]> result;
|
||||||
|
|
||||||
|
const auto data0 = a.eval(m_ir);
|
||||||
|
const auto data1 = b.eval(m_ir);
|
||||||
|
const auto data2 = c.eval(m_ir);
|
||||||
|
|
||||||
|
result.value = m_ir->CreateCall(get_intrinsic<u32[4], u8[16]>(llvm::Intrinsic::aarch64_neon_smmla), {data0, data1, data2});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T1, typename T2>
|
template <typename T1, typename T2>
|
||||||
value_t<s32[4]> smull(T1 a, T2 b)
|
value_t<s32[4]> smull(T1 a, T2 b)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5524,6 +5524,44 @@ public:
|
|||||||
const auto a = get_vr<s16[8]>(op.ra);
|
const auto a = get_vr<s16[8]>(op.ra);
|
||||||
|
|
||||||
#ifdef ARCH_ARM64
|
#ifdef ARCH_ARM64
|
||||||
|
if (m_use_i8mm)
|
||||||
|
{
|
||||||
|
if (match_vr<s16[8], s32[4], s64[2]>(op.ra, [&](auto c, auto MP)
|
||||||
|
{
|
||||||
|
using VT = typename decltype(MP)::type;
|
||||||
|
|
||||||
|
if (auto [ok, x] = match_expr(c, sext<VT>(match<bool[std::extent_v<VT>]>())); ok)
|
||||||
|
{
|
||||||
|
const auto zeroes = splat<u32[4]>(0);
|
||||||
|
const auto es = zshuffle(bitcast<u8[16]>(a), 16, 16, 16, 16, 16, 16, 16, 16, 0, 2, 4, 6, 8, 10, 12, 14);
|
||||||
|
|
||||||
|
set_vr(op.rt, smmla(zeroes, es, build<u8[16]>(
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
-0x01, -0x02, -0x04, -0x08,
|
||||||
|
-0x10, -0x20, -0x40, -0x80
|
||||||
|
)));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto zeroes = splat<u32[4]>(0);
|
||||||
|
const auto masked = a & 0x01;
|
||||||
|
const auto es = zshuffle(bitcast<u8[16]>(masked), 16, 16, 16, 16, 16, 16, 16, 16, 0, 2, 4, 6, 8, 10, 12, 14);
|
||||||
|
|
||||||
|
set_vr(op.rt, ummla(zeroes, es, build<u8[16]>(
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x01, 0x02, 0x04, 0x08,
|
||||||
|
0x10, 0x20, 0x40, 0x80
|
||||||
|
)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Use dot product instructions with special values to shift then sum results into the preferred slot
|
// Use dot product instructions with special values to shift then sum results into the preferred slot
|
||||||
if (m_use_dotprod)
|
if (m_use_dotprod)
|
||||||
{
|
{
|
||||||
@@ -5579,6 +5617,48 @@ public:
|
|||||||
const auto a = get_vr<u8[16]>(op.ra);
|
const auto a = get_vr<u8[16]>(op.ra);
|
||||||
|
|
||||||
#ifdef ARCH_ARM64
|
#ifdef ARCH_ARM64
|
||||||
|
if (m_use_i8mm)
|
||||||
|
{
|
||||||
|
if (match_vr<s8[16], s16[8], s32[4], s64[2]>(op.ra, [&](auto c, auto MP)
|
||||||
|
{
|
||||||
|
using VT = typename decltype(MP)::type;
|
||||||
|
|
||||||
|
if (auto [ok, x] = match_expr(c, sext<VT>(match<bool[std::extent_v<VT>]>())); ok)
|
||||||
|
{
|
||||||
|
const auto zeroes = splat<u32[4]>(0);
|
||||||
|
|
||||||
|
const auto extracted = smmla(zeroes, a, build<u8[16]>(
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
-0x01, -0x02, -0x04, -0x08,
|
||||||
|
-0x10, -0x20, -0x40, -0x80
|
||||||
|
));
|
||||||
|
|
||||||
|
const auto es = zshuffle(bitcast<u8[16]>(extracted), 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 4, 12, 16, 16);
|
||||||
|
set_vr(op.rt, bitcast<u32[4]>(es));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto zeroes = splat<u32[4]>(0);
|
||||||
|
const auto masked = a & 0x01;
|
||||||
|
|
||||||
|
const auto extracted = ummla(zeroes, masked, build<u8[16]>(
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x01, 0x02, 0x04, 0x08,
|
||||||
|
0x10, 0x20, 0x40, 0x80
|
||||||
|
));
|
||||||
|
|
||||||
|
const auto es = zshuffle(bitcast<u8[16]>(extracted), 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 4, 12, 16, 16);
|
||||||
|
set_vr(op.rt, bitcast<u32[4]>(es));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Use dot product instructions with special values to shift then sum results into the preferred slot
|
// Use dot product instructions with special values to shift then sum results into the preferred slot
|
||||||
if (m_use_dotprod)
|
if (m_use_dotprod)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -426,6 +426,26 @@ bool utils::has_dotprod()
|
|||||||
return g_value;
|
return g_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool utils::has_i8mm()
|
||||||
|
{
|
||||||
|
static const bool g_value = []() -> bool
|
||||||
|
{
|
||||||
|
#if defined(__linux__)
|
||||||
|
return (getauxval(AT_HWCAP2) & HWCAP2_I8MM) != 0;
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
int val = 0;
|
||||||
|
size_t len = sizeof(val);
|
||||||
|
sysctlbyname("hw.optional.arm.FEAT_I8MM", &val, &len, nullptr, 0);
|
||||||
|
return val != 0;
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
return IsProcessorFeaturePresent(PF_ARM_V82_I8MM_INSTRUCTIONS_AVAILABLE) != 0;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}();
|
||||||
|
return g_value;
|
||||||
|
}
|
||||||
|
|
||||||
bool utils::has_sve()
|
bool utils::has_sve()
|
||||||
{
|
{
|
||||||
static const bool g_value = []() -> bool
|
static const bool g_value = []() -> bool
|
||||||
|
|||||||
@@ -61,6 +61,8 @@ namespace utils
|
|||||||
|
|
||||||
bool has_dotprod();
|
bool has_dotprod();
|
||||||
|
|
||||||
|
bool has_i8mm();
|
||||||
|
|
||||||
bool has_sve();
|
bool has_sve();
|
||||||
|
|
||||||
bool has_sve2();
|
bool has_sve2();
|
||||||
|
|||||||
Reference in New Issue
Block a user