Vector Initialization

Vector initialization used to be such a mess. You would think that creating a SIMD vector from its scalars would be easy to standardize, no?

Consider Motorola Altivec PIM style initialization. The SIMD vector in Altivec is a new built-in type initialized with the following syntax:

__vector signed int v = (__vector signed int) (1, 2, 3, 4); // element sequence is 1, 2, 3, 4
__vector signed int w = (__vector signed int) (1); // each element is 1

This style is supported by most standard OS X compilers: Apple gcc, Metrowerks Codewarrior and IBM XLC. However, the syntax actually means something else in the C89, C99 and C++98 standards and as such often confuses the poor parsers that have to decipher it. For example, within C++ template functions it is sometimes seen as an incorrect cast, or compile-time constants are not recognized as such. And finally, you cannot use variables to initialize.

Now look at the mainline gcc syntax for the same:

__vector signed int v = (__vector unsigned int) {1, 2, 3, 4}; // element sequence is 1, 2, 3, 4
__vector signed int w = (__vector unsigned int) {1}; // first element is 1, rest is 0

This style is equivalent to the C99 array initialization syntax, but is an error with C89 and C++98. Unfortunately, this syntax is not supported by the standard OS X compilers. More dangerously, the single element initializer does something different from the PIM-style version.

The situation gets worse on the Intel side. With the Visual C++ .NET 2003 compiler syntax, the SIMD vector is not a new built-in type but a union, so the following works:

__m128i v = {1, 2, 3, 4}; // element sequence is 1, 2, 3, 4
__m128i w = {1}; // first element is 1, rest is undefined

Unlike with the Altivec compilers, you can't initialize a temporary this way since Visual C++ doesn't support C99. And of course, it’s yet another incompatible syntax and difference with single element initializer.

Enter macstl's Grand Unified Theory of vector initialization.

Static Initialization

You can initialize a vector statically with constants:

vec <int, 4> v = vec <int, 4>::set <1, 2, 3, 4> (); // element sequence is 1, 2, 3, 4
vec <int, 4> w = vec <int, 4>::fill <1> (); // each element is 1

Amazingly this syntax works identically on all OS X compilers and also on Visual C++ .NET, a different architecture. It’s now clear which initializer sets individual elements, and which initializer fills all the elements.

On Altivec, the library will also transparently substitute 1 and 2 opcode VPERM sequences for some constants, avoiding the slower load from L1 cache.

One drawback: floating point vectors have to be initialized with unsigned integers, since C++ doesn't allow floating point numbers for non-type template parameters.

Dynamic Initialization

You can also initialize a vector dynamically with either constants or variables:

vec <int, 4> v = vec <int, 4>::set (1, 2, 3, 4); // element sequence is 1, 2, 3, 4
vec <int, 4> w = vec <int, 4>::fill (1); // each element is 1

Dynamic initialization is more flexible but can be slower than static initialization. It also yields a more intuitive syntax for floating point vectors.

Mon, 29 Sep 2003. © Pixelglow Software.
» a simd “hello world”