SPU LLVM: Use SVE2 XAR for constant rotates

This commit is contained in:
Malcolm 2026-06-02 23:45:05 -04:00
parent 2d1be09180
commit 60918be948

View File

@ -1319,11 +1319,96 @@ struct llvm_rol
llvm_expr_t<A1> a1;
llvm_expr_t<A2> a2;
bool use_sve_xar = false;
static_assert(llvm_value_t<T>::is_sint || llvm_value_t<T>::is_uint, "llvm_rol<>: invalid type");
static constexpr bool is_ok = llvm_value_t<T>::is_sint || llvm_value_t<T>::is_uint;
#ifdef ARCH_ARM64
static bool get_constant_splat(llvm::Value* value, u64& result)
{
if (const auto constant = llvm::dyn_cast<llvm::ConstantInt>(value))
{
result = constant->getZExtValue();
return true;
}
if (llvm::isa<llvm::ConstantAggregateZero>(value))
{
result = 0;
return true;
}
const auto vector_type = llvm::dyn_cast<llvm::FixedVectorType>(value->getType());
if (!vector_type)
{
return false;
}
const auto element_count = vector_type->getNumElements();
const auto get_element = [&](u32 index) -> llvm::Constant*
{
if (const auto data = llvm::dyn_cast<llvm::ConstantDataVector>(value))
{
return data->getElementAsConstant(index);
}
if (const auto vector = llvm::dyn_cast<llvm::ConstantVector>(value))
{
return vector->getAggregateElement(index);
}
return nullptr;
};
const auto first_element = llvm::dyn_cast_or_null<llvm::ConstantInt>(get_element(0));
if (!first_element)
{
return false;
}
result = first_element->getZExtValue();
for (u32 i = 1; i < element_count; i++)
{
const auto element = llvm::dyn_cast_or_null<llvm::ConstantInt>(get_element(i));
if (!element || element->getZExtValue() != result)
{
return false;
}
}
return true;
}
static llvm::Value* to_sve_vector(llvm::IRBuilder<>* ir, llvm::Value* value)
{
if (llvm::isa<llvm::ScalableVectorType>(value->getType()))
{
return value;
}
const auto fixed_type = llvm::cast<llvm::FixedVectorType>(value->getType());
const auto scalable_type = llvm::ScalableVectorType::get(fixed_type->getElementType(), fixed_type->getNumElements());
return ir->CreateInsertVector(scalable_type, llvm::UndefValue::get(scalable_type), value, ir->getInt64(0));
}
static llvm::Value* from_sve_vector(llvm::IRBuilder<>* ir, llvm::Value* value, llvm::FixedVectorType* fixed_type)
{
if (value->getType() == fixed_type)
{
return value;
}
return ir->CreateExtractVector(fixed_type, value, ir->getInt64(0));
}
#endif
llvm::Value* eval(llvm::IRBuilder<>* ir) const
{
const auto v1 = a1.eval(ir);
@ -1334,6 +1419,28 @@ struct llvm_rol
return llvm_fshl<A1, A1, A2>::fold(ir, v1, v1, v2);
}
#ifdef ARCH_ARM64
u64 rotate = 0;
if (use_sve_xar && llvm::isa<llvm::FixedVectorType>(v1->getType()) && get_constant_splat(v2, rotate))
{
constexpr u64 element_size = llvm_value_t<T>::esize;
const u32 rotate_right = static_cast<u32>((element_size - (rotate % element_size)) % element_size);
if (rotate_right == 0)
{
return v1;
}
const auto fixed_type = llvm::cast<llvm::FixedVectorType>(v1->getType());
const auto data = to_sve_vector(ir, v1);
const auto zero = llvm::Constant::getNullValue(data->getType());
const auto result = ir->CreateIntrinsic(llvm::Intrinsic::aarch64_sve_xar, {data->getType()}, {data, zero, ir->getInt32(rotate_right)});
return from_sve_vector(ir, result, fixed_type);
}
#endif
return ir->CreateCall(llvm_fshl<A1, A1, A2>::get_fshl(ir), {v1, v1, v2});
}
@ -3407,9 +3514,13 @@ public:
template <typename T, typename U>
requires llvm_rol<T, U>::is_ok
static auto rol(T&& a, U&& b)
auto rol(T&& a, U&& b)
{
#ifdef ARCH_ARM64
return llvm_rol<T, U>{std::forward<T>(a), std::forward<U>(b), m_use_sve2_128};
#else
return llvm_rol<T, U>{std::forward<T>(a), std::forward<U>(b)};
#endif
}
template <typename T, typename U>