DEV Community

Luca Barbato
Luca Barbato

Posted on

Supporting Altivec in stdsimd - quick notes

I already blogged about it before now I'm trying to complete it, few instructions per day.

How we support Altivec intrinsics

The Altivec intrinsics are sort of polymorphic: vec_add(a, b) -> c works for plenty of combinations of a and b (and c).

We want to support that in rust as well and that means we have to add plenty of traits and boilerplate (lots of it) to produce it.

Luckily we can leverage llvm low level intrinsics so the large part of the problem is wiring the low-level to an high level abstraction, here an example.

pub trait VectorAdd<Other> {
    type Return;
    vec_add(self, b: Other) -> Self::Return;
}

impl VectorAdd<vector_signed_char> for vector_bool_char {
    type Result = vector_signed_char;
    #[inline]
    #[target_feature(enable = "altivec")]
    unsafe fn vec_add(self, other: vector_signed_char) -> Self::Result {
        vec_add_bc_sc(self, other)
    }
}
...
Enter fullscreen mode Exit fullscreen mode

And then eventually you implement the generic function vec_add:

#[inline]
#[target_feature(enable = "altivec")]
pub unsafe fn vec_add<T, U>(a: T, b: U) -> <T as sealed::VectorAdd<U>>::Result
where
    T: sealed::VectorAdd<U>,
{
    a.vec_add(b)
}
Enter fullscreen mode Exit fullscreen mode

It is really boring endeavor that consists in:

  • write a simple C testcase for the instruction
  • feed it to clang to produce a .ll file and a .s file
  • write the stub for the llvm-internal intrinsic
  • write an assert_instr-wrapped test-function
  • write a wrapper trait
  • write the implementations for all the type combinations have to support
  • write the public generic function
  • write a test or many to make sure you did not botch all the previous steps.

Current limitations

Both rust and C do not have exactly great way to described a function argument as literal-only.

Altivec has plenty of intrinsics that really need to be explicit and strict about that since one of the argument of them is a 5-bit literal that is embedded right in the coded instruction.

In rust we have rustc_args_required_const to mark the argument as a compile-time constant.

Sadly the information is not forwarded so until this isn't addressed all those instructions (some relatively fringe such as vec_ctf some useful such as the family of vec_splat_{type} immediates) do not have a sane way to be implemented.

That's all for tonight, I'll edit this again soon and publish it on my other blog once I have more to write about this.

Coming soon: cargo-c development notes and some more information about crav1e and rav1e getting closer to its first ready-for-distribution snapshot.

Top comments (2)

Collapse
 
jeikabu profile image
jeikabu

I did a lot of work with AltiVec for the PowerPC-based PS3 and Xbox360 so it has a special place in my heart.

Besides the loss of constants, how painful is casting between the different vec types?
Truth be told I've yet to look at stdsimd, should probably get around to it one of these days. Working with all of AltiVec, SSE, and NEON was always interesting.

Collapse
 
luzero profile image
Luca Barbato

Tomorrow I'll try to get the constants part done since there is a way to do that (arguably it is an hack but it should work for the intended purposes)