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 Support #15

Closed
wants to merge 3 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
187 changes: 187 additions & 0 deletions active/0000-simd-support.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
- Start Date: (fill me in with today's date, YYY-MM-DD)
- RFC PR #: (leave this empty)
- Rust Issue #: (leave this empty)

# Summary

Improve SIMD support in the Rust compiler and libraries.

# Motivation

[SIMD] is a useful way of exploiting instruction-level parallelism in modern
hardware. While some code can be optimized to use these instructions without
programmer instruction, they cannot be relied upon for real-world code.

Rust already has minimal support for SIMD vectors via an `#[simd]` attribute
on struct types that mean certain requirements. Extending this support into
something that allows access to more of the underlying features would greatly
help programmers to exploit modern hardware safely.

# Detailed design

The support for SIMD vectors is primarily designed to match [Open CL] vectors
where appropriate. Many of the operations have relatively simple syntactical
forms and carrying that over to Rust seems like a good idea.

## Syntax

SIMD vectors are unlikely to be common, therefore implementing an entire new
syntax is probably not worth it. Syntax extensions are the logical choice
here, as we only need a few elements to get this to work.

Given the similarity to the existing fixed-length vectors, I propose the
following syntax:

```rust
simd![T,..n] // SIMD type syntax
simd![Expr, Expr, Expr] // SIMD expression syntax
simd![Expr,..Expr] // SIMD repeat expression
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume the second expr here has to be compile-time evaluable to a uint?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, same as with fixed-length vectors.

```

The second expression in the repeat syntax must evaluate to a constant integral
greater than or equal to 1.

As such, these would be used like so:

```rust
fn make_vec3() -> simd![f32,..3] {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Syntax extensions cannot currently expand into types, FYI. I don't believe that it would be a hard change to support it, but it's something that should probably at least be discussed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One point on this that I wasn't sure how to fit into the RFC itself is that simd types could be special-cased in the parser, so they aren't actually macros there. That's what I've done in my current branch.

simd![0, ..3]
}
```

## Type System Integration

SIMD vectors would require the addition of a new base type similar to
fixed-length vectors. This type would, however, limit it's subtype to machine
types, i.e. integers, floats and bools.

Casting between different SIMD types of the same length should be supported,
e.g. `simd![f32, ..4] => simd![u32, ..4]`.

## Back-end Implementation

LLVM supports arbitrary vector types of integer or floating point types of any
length greater than zero. Rust SIMD vectors would map directly to these.

## API and Operations Support

Operations on SIMD vector types should follow that of the [OpenCL vectors].

### Binary Operations

For standard binary operations, the operation is performed component-wise,
for example:

```rust
let v : simd![f32,..4];
let u : simd![f32,..4];
let x : simd![f32,..4];

...

let x = v + u
```

will be quivalent to:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: "equivalent"


```rust
x.x = v.x + u.x;
x.y = v.y + u.y;
x.z = v.z + u.z;
x.w = v.w + u.w;
```

The operations would be limited based on the supported operations of the
vector's subtype.

Support for vector *op* scalar operations will not be supported, unlike Open
CL, as it is effectively is a coercion that we do not do for any other types.

### Comparisons

Programmers using SIMD vectors in comparisons are likely to want to get a
vector of boolean values as a result. However, the traits for these operations
require returning a single boolean value. With this in mind, I propose only
implementing the equality operator that returns whether every pair in the two
vectors are equal or not.

Component-wise comparison, returning a vector of boolean values, shall be done
by a set of appropriate intrinsics.

### Component/Element Access

Accessing individual elements of a vector can be done using standard indexing
syntax:

```rust
let a = v[0];
```

Also supported would be Open CL style field accessor syntax. With the x, y, z
and w fields returning the first, second, third and fourth elements of the
vector. The second field accessor syntax would be an 's' followed by a single
hexadecimal digit for accessing elements up to the sixteenth.

### Shuffle Access
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How bad would it be to be conservative with a macro simdex ("simd index" ;P ) like

simdex![v, 0, 0, 1, 1, 2, 2, 3, 3];

simdex![v, xyz];
// (or maybe)
simdex![v, x, y, z];

which doesn't require magical "field access" overloading.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The shuffle intrinsic probably makes this not really worthwhile.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Aatch A macro will prevent it from having variables in the shuffle, which is the correct semantics for llvm to generate a good shuffle / not crash

simdex![v, 0, 0, 1, 1, 2, 2, 3, 3];
is a good place to start that doesn't require inducing magic syntax.

better syntax can be added later


Shuffles are permutations of a vector, returning another vector. Open CL
extends the field access syntax to support this like so:

```rust
let v : simd![f32,...4] = simd![1.0, 2.0, 3.0, 4.0];

let x = v.x; // 1.0f32
let v3 : simd![f32,..3] = v.xyz; // simd![1.0, 2.0, 3.0]
let swiz : simd![f32,..4] = v.wzyx; // simd![4.0, 3.0, 2.0, 1.0]
let dup : simd![f32,..4] = v.xxyy; // simd![1.0, 1.0, 2.0, 2.0]

// simd![1.0, 1.0, 2.0, 2.0, 3.0, 3.0, 4.0, 4.0]
let num : simd![f32,..8] = v.s00112233;
```

The same field accessor syntax may be used to set arbitrary components of a
vector all at once:

```rust
v.s246 = simd![1.0, 2.0, 3.0];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand what this syntax means.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a very common short-cut found in shader languages for swizzling operations. In short:

v.xxyz <=> (v.x, v.x, v.y, v.z)
v.s00112233 <=> (v[0], v[0], v[1], v[1], v[2], v[2], v[3], v[3])

For a single variable like this, the notation is a little contrived, but imagine that 'v' above is replaced by a fairly complicated expression.

```

However, repeating a component on the right-hand side will not be allowed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need the s prefix?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'd guess to make it easier to parse?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

allows it to be parsed as an indent without changing any rules. Also, it matches Open CL.

`shuffle` and `shuffle2` intrinsics should be available to implement the
above behaviour for variable permutations and vectors of a size greater than
16.

### Load/Store Functions

There should be a set of unsafe load and store functions for reading and
writing vectors to a raw pointer. These are required due to alignment concerns
on some platforms.

### Miscellaneous Functions

There are numerous useful functions and methods that could be implemented for
SIMD vectors. I consider it to be beyond the scope of this RFC to explore that
part of SIMD support.

# Alternatives

* Use fixed-length vectors as they are now and do vector operations where
appropriate. I do not think that this is a good idea as SIMD vectors are
more strict than normal vectors, fixed-length or otherwise. They would also
not interact well under DST and the current coercion rules.
* Don't have SIMD support at all. If Rust does not wish to support SIMD
vectors in the language going forward, the current support for them should
be removed. If we do not support SIMD vectors properly, then we should not
support them at all.

# Unresolved questions

1. Syntax - should it stay as proposed or is there a better alternative?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The one you proposed in IRC is also reasonable: let v: <f32, .. 4> = <0.0, 1.0, 2.0, 3.0> + <0.0, .. 4>;.

(Although I'm mildly worried about some subtle grammar interaction appearing.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for the simd![] syntax

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really like the <f32, ..4> syntax, but on the other hand, we may want to save special syntax for things that appear more often in normal code. The simd! one could just be a normal syntax extension, and then people could add more crazy types that they need. (MIPS accumulators comes to mind)

Unless we decide that cornering the dense linear algebra/DSP/GPGPU market is important for Rust, then really slick SIMD syntax might be worth it.

2. Upper limit on vector size. To my knowledge, LLVM does not have an upper
limit on the size of the vector. Should we enforce one anyway?
3. LLVM supports vectors of pointers to the supported types, should we also
support this, given that they would need to be unsafe?

[SIMD]: http://en.wikipedia.org/wiki/SIMD
[OpenCL]: http://www.khronos.org/registry/cl/specs/opencl-1.2.pdf#page=234