Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SIMD for Rust #5841

Closed
wants to merge 7 commits into from
Closed

SIMD for Rust #5841

wants to merge 7 commits into from

Conversation

sanxiyn
Copy link
Member

@sanxiyn sanxiyn commented Apr 11, 2013

Fix #3499.

This is a work in progress.

T ^ N (temporary syntax) parses to ast::ty_multi(@Ty, uint) and converts to ty::ty_multi(t, uint) and lowers to LLVM SIMD vector type <n x t>. Metadata support and reflection support are missing. There are minimal tests.

Casting scalar to vector broadcasts, and element-wise vector arithmetics is implemented. There should be a way to load from vectors to SIMD vector (since terminology is confusing, let's call this "multi"), which needs design.

Some TODOs are OpenCL-style swizzling, type conversion, more SIMD operations (such as saturated arithmetics), etc.

@graydon
Copy link
Contributor

graydon commented Apr 15, 2013

I'm ... generally uncomfortable with adding a new type for this. Feels like it ought to be possible with the existing type system. Will LLVM do the right thing if we just treat all small(ish) fixed-length vectors of appropriate types as the <n x t> types? Alternatively, can we introduce an attribute on such vectors similar to the #[packed] attribute recently added to structs?

@graydon
Copy link
Contributor

graydon commented Apr 15, 2013

(otherwise great to see work on this!)

@sanxiyn
Copy link
Member Author

sanxiyn commented Apr 15, 2013

As I understand, LLVM will scalarize vector types when SIMD is unavailable (see LegalizeVectorTypes.cpp and LegalizeVectorOps.cpp) or when it is cheaper (see InstCombineVectorOps.cpp). Of course it is not perfect. I think 16 is a reasonable cutoff for smallness; it's the maximum SIMD vector length for OpenCL.

On the other hand, should operators be overloaded for all fixed-length vectors, or just small fixed-length vectors (of appropriate types)? I don't think it is desirable that + is defined for [int, ..16] but not [int, ..20] or [T, ..16]. Another thing to consider is having vector types in a function signature seems to be able to crash LLVM (apparently some can't be done with ABI). I think fixed-length vectors won't be in the signature much, but it would be problematic for tuples.

@sanxiyn
Copy link
Member Author

sanxiyn commented Apr 15, 2013

Using an attribute is definitely possible (indeed, analogous to packed) and it's how it is done in C. What would be the name of the attribute? To what would the attribute apply?

// In C
typedef float float4 __attribute__((ext_vector_type(4)));
// As in C?
#[simd(4)]
type f32x4 = f32;
// Or like this?
#[simd]
type f32x4 = [f32, ..4];

Using an attribute was useful in packed case because packed struct is mostly like struct, except in a few places (like type_of). f32x4 is like f32 in that you can add, multiply, etc., but it is also like [f32, ..4] in that you can index it. My opinion is that a new type is cleaner than using an attribute.

@sanxiyn
Copy link
Member Author

sanxiyn commented Apr 15, 2013

I also think it should be possible to vectorize struct, to form structures of arrays. I found Extending a C-like Language for Portable SIMD Programming (PPoPP 2012) interesting.

@graydon
Copy link
Contributor

graydon commented Apr 19, 2013

Fascinating paper! I think it's beyond the scope of what we can get working in the 1.0 timeframe, honestly, but it is a promising looking extension to the language for ... some day.

I think in the nearer term I'd prefer to approach this via a #[simd] or [simd(4)] attribute, yes. I don't know how best to factor platform dependency around that, and whether it's necessary to specify a vector size in the attribute as well as in the vector size itself. If LLVM is good at choosing target representations and operations, I would be happy to leave the task to it and just generate vectorized code for any fixed sized vectors marked with #[simd].

I don't think we have machinery to overload arithmetic on "all" fixed length vectors at the moment. I am not even sure if defining on any fixed length vectors will work well; I suspect the type system will try to borrow a slice and dispatch to an overload on &[]. But if you can get it working on any of them, I don't mind restricting it to a fixed set of small sizes (1, 2, 4, 8, 16 say); these can be stamped out for different sizes using macros or similar techniques. It's a pretty specialized corner of the language and there are not actual hardware devices with abnormal (20, 27, 35, etc.) vector sizes to worry about.

It doesn't look like LLVM surfaces the entire repertoire of vector operations one might want to do in NEON or SSE; will we need to be putting bits of inline asm in the overloaded operators?

@sanxiyn
Copy link
Member Author

sanxiyn commented Apr 19, 2013

Ok @graydon, I will reimplement this using #[simd(4)] type f32x4 = f32 syntax. Is it okay for you?

@graydon
Copy link
Contributor

graydon commented Apr 19, 2013

I discussed with sanxiyn on IRC. The following transcript shows the discussion:

https://botbot.me/irc.mozilla.org/rust/msg/2799234/

General consensus points:

  • Using vector types (as I initially thought) is probably not wise since they support slice and element-address-taking in ways that SIMD never will. Either use an attribute-annotated scalar type or an attribute-annotated tuple type. Tuple types have the advantage of coming with a construct / destruct form.
  • tuple idioms like let (x, y, _, _) = v; (x, x, y, y) might be recognizable at the LLVM level (or pattern-match / destructure / tuple-formation code in trans) as things like SIMD shuffles; investigate whether LLVM and/or trans can do so, as it is a nice tidy idiom.
  • iteration can be done by equipping a vector with an .each_simd method. This takes two functions, one that handles the header/trailer parts of the block (taking T) and one that handles the SIMD-body. Other possibilities involving a maybe-SIMD trait implemented by both T and #[simd] (T,T,T,T) are possible.

@sanxiyn
Copy link
Member Author

sanxiyn commented Apr 25, 2013

When I tried to implement an attribute approach, I encountered the problem that attributes are attached to AST nodes, not types. Nominal types (enum, struct, trait) have def_id (as they should) so you can check whether struct has a packed attribute, but structural types (like primitive types, vectors, tuples) don't (as they shouldn't: the interner avoids allocating same types) so you can't check whether they have a simd attribute. Maybe structural types should not be uniqed when attributes are attached?

@graydon
Copy link
Contributor

graydon commented Apr 27, 2013

Can you use a "tuple struct" like struct vec4<T> (T,T,T,T) ?

@sanxiyn sanxiyn mentioned this pull request May 3, 2013
bors added a commit that referenced this pull request May 8, 2013
At the moment this only includes type checking and there is no code generation support yet. I wanted to get the design reviewed first.

From discussion with @graydon at #5841, re-implemented as `#[simd]` attribute on structs.

Progressing towards #3499.
@sanxiyn sanxiyn closed this May 8, 2013
bors added a commit that referenced this pull request Jul 12, 2013
Fix #3499.

This is the other half of #5841 that was left out when I revised it to #6214.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants