Next: , Previous: Return Address, Up: C Extensions


5.43 Using vector instructions through built-in functions

On some targets, the instruction set contains SIMD vector instructions that operate on multiple values contained in one large register at the same time. For example, on the i386 the MMX, 3Dnow! and SSE extensions can be used this way.

The first step in using these extensions is to provide the necessary data types. This should be done using an appropriate typedef:

     typedef int v4si __attribute__ ((mode(V4SI)));

The base type int is effectively ignored by the compiler, the actual properties of the new type v4si are defined by the __attribute__. It defines the machine mode to be used; for vector types these have the form VnB; n should be the number of elements in the vector, and B should be the base mode of the individual elements. The following can be used as base modes:

QI
An integer that is as wide as the smallest addressable unit, usually 8 bits.
HI
An integer, twice as wide as a QI mode integer, usually 16 bits.
SI
An integer, four times as wide as a QI mode integer, usually 32 bits.
DI
An integer, eight times as wide as a QI mode integer, usually 64 bits.
SF
A floating point value, as wide as a SI mode integer, usually 32 bits.
DF
A floating point value, as wide as a DI mode integer, usually 64 bits.

Specifying a combination that is not valid for the current architecture will cause GCC to synthesize the instructions using a narrower mode. For example, if you specify a variable of type V4SI and your architecture does not allow for this specific SIMD type, GCC will produce code that uses 4 SIs.

The types defined in this manner can be used with a subset of normal C operations. Currently, GCC will allow using the following operators on these types: +, -, *, /, unary minus, ^, |, &, ~.

The operations behave like C++ valarrays. Addition is defined as the addition of the corresponding elements of the operands. For example, in the code below, each of the 4 elements in a will be added to the corresponding 4 elements in b and the resulting vector will be stored in c.

     typedef int v4si __attribute__ ((mode(V4SI)));
     
     v4si a, b, c;
     
     c = a + b;

Subtraction, multiplication, division, and the logical operations operate in a similar manner. Likewise, the result of using the unary minus or complement operators on a vector type is a vector whose elements are the negative or complemented values of the corresponding elements in the operand.

You can declare variables and use them in function calls and returns, as well as in assignments and some casts. You can specify a vector type as a return type for a function. Vector types can also be used as function arguments. It is possible to cast from one vector type to another, provided they are of the same size (in fact, you can also cast vectors to and from other datatypes of the same size).

You cannot operate between vectors of different lengths or different signedness without a cast.

A port that supports hardware vector operations, usually provides a set of built-in functions that can be used to operate on vectors. For example, a function to add two vectors and multiply the result by a third could look like this:

     v4si f (v4si a, v4si b, v4si c)
     {
       v4si tmp = __builtin_addv4si (a, b);
       return __builtin_mulv4si (tmp, c);
     }