riscv-non-isa/rvv-intrinsic-doc

Encode all the effects of vsetvl in the return type, for use in subsequent type deductions

Closed this issue · 1 comments

If you encode information about a call to vsetvl into its return type, then it's possible to use that in function specialisations, and avoid needing to re-state it in the function name where the proper type cannot otherwise be deduced, so overloads are cleaner:

inline VLType_u8m1 rvv_setvl_u8m1(size_t avl) { return VLType_u8m1(__riscv_vsetvl_e8m1(avl)); }
inline VLType_u8m8 rvv_setvl_u8m8(size_t avl) { return VLType_u8m8(__riscv_vsetvl_e8m8(avl)); }
inline VLType_u32m1 rvv_setvl_u32m1(size_t avl) { return VLType_u32m1(__riscv_vsetvl_e32m1(avl)); }
inline VLType_u32m8 rvv_setvl_u32m8(size_t avl) { return VLType_u32m8(__riscv_vsetvl_e32m8(avl)); }
/*...*/

inline vuint8m1_t rvv_le(uint8_t const* p, VLType_u8m1 vl) { return __riscv_vle8_v_u8m1(p, vl); }
inline vuint8m8_t rvv_le(uint8_t const* p, VLType_u8m8 vl) { return __riscv_vle8_v_u8m8(p, vl); }
inline vuint32m1_t rvv_le(uint32_t const* p, VLType_u32m1 vl) { return __riscv_vle32_v_u32m1(p, vl); }
inline vuint32m8_t rvv_le(uint32_t const* p, VLType_u32m8 vl) { return __riscv_vle32_v_u32m8(p, vl); }
/*...*/

auto example0(uint8_t* ptr, std::size_t count) {
    auto vl = rvv_setvl_u8m1(count);
    return rvv_le(ptr, vl); 
}

And if you tabulate all of these under a template, you can deduce things from scalar arguments:

template <typename T, LMUL m> inline VLType<T, m> rvv_setvl_(std::size_t avl);
template<> inline VLType_u8m1 rvv_setvl_<uint8_t, LMUL_m1>(size_t avl) { return rvv_setvl_u8m1(avl); }
template<> inline VLType_u8m8 rvv_setvl_<uint8_t, LMUL_m8>(size_t avl) { return rvv_setvl_u8m8(avl); }
template<> inline VLType_u32m1 rvv_setvl_<uint32_t, LMUL_m1>(size_t avl) { return rvv_setvl_u32m1(avl); }
template<> inline VLType_u32m8 rvv_setvl_<uint32_t, LMUL_m8>(size_t avl) { return rvv_setvl_u32m8(avl); }
/*...*/
template <typename T, LMUL m> inline auto rvv_setvl(size_t avl) { return rvv_setvl_<std::remove_reference_t<T>, m>(avl); }

auto example1(uint32_t* ptr, std::size_t count) {
    constexpr LMUL lmul = LMUL_m8;
    auto vl = rvv_setvl<decltype(*ptr), lmul>(count);
    return rvv_le(ptr, vl); 
}

A godbolt link: https://godbolt.org/z/dfaxbzbE8

This sort of thing helps reduce the pain of template code. Plain-old C is harder to help, but if you've typedef-ed a vector type then you can repeat that once for the VL type and once more for setvl(), and should then be able to avoid a lot of the alias creation that is currently necessary for all the intrinsic functions with types in their names.

This actually belongs in discussions, as that was my intent when I posted it. I'm going to go repost it there. #311