== Quote from Andrei Alexandrescu (seewebsiteforem...@erdani.org)'s article > On 9/22/11 1:39 AM, Don wrote: > > On 22.09.2011 05:24, a wrote: > >> How would one do something like this without intrinsics (the code is > >> c++ using > >> gcc vector extensions): > > > > [snip] > > At present, you can't do it without ultimately resorting to inline asm. > > But, what we've done is to move SIMD into the machine model: the D > > machine model assumes that float[4] + float[4] is a more efficient > > operation than a loop. > > Currently, only arithmetic operations are implemented, and on DMD at > > least, they're still not proper intrinsics. So in the long term it'll be > > possible to do it directly, but not yet. > > > > At various times, several of us have implemented 'swizzle' using CTFE, > > giving you a syntax like: > > > > float[4] x, y; > > x[] = y[].swizzle!"cdcd"(); > > // x[0]=y[2], x[1]=y[3], x[2]=y[2], x[3]=y[3] > > > > which compiles to a single shufps instruction. > > > > That "cdcd" string is really a tiny DSL: the language consists of four > > characters, each of which is a, b, c, or d. > I think we should put swizzle in std.numeric once and for all. Is anyone > interested in taking up that task? > > A couple of years ago I made a DSL compiler for BLAS1 operations. It was > > capable of doing some pretty wild stuff, even then. (The DSL looked like > > normal D code). > > But the compiler has improved enormously since that time. It's now > > perfectly feasible to make a DSL for the SIMD operations you need. > > > > The really nice thing about this, compared to normal asm, is that you > > have access to the compiler's symbol table. This lets you add > > compile-time error messages, for example. > > > > A funny thing about this, which I found after working on the DMD > > back-end, is that is MUCH easier to write an optimizer/code generator in > > a DSL in D, than in a compiler back-end. > A good argument for (a) moving stuff from the compiler into the library, > (b) continuing Don's great work on making CTFE a solid proposition. > Andrei
This sounds really dangerous to me. I really like the idea where CTFE can be used to produce some pretty powerful almost-like-intrinsics code, but applying it in this context sounds like a really bad idea. Firstly, so I'm not misunderstanding, is this suggestion building on Don's previous post saying that float[4] is somehow intercepted and special-cased by the compiler, reinterpreting as a candidate for hardware vector operations? I think that's a wrong decision in its self, and a poor foundation for this approach. Let me try and convince you that the language should have an explicit hardware vector type, and not attempt to make use of any clever language tricks... If float[4] is considered a hardware vector by the compiler, - How to I define an ACTUAL float[4]? - How can I be confident that it actually WILL be a hardware vector? Hardware vectors are NOT float[4]'s, they are a reference to an 128bit hardware register upon which various vector operations may be supported, they are probably aligned, and they are only accessible in 128bit quantities. I think they should be explicitly defined as such. They may be float4, u/int4, u/short8, u/byte16, double2... All these types are interchangeable within the one register, do you intend to special case fixed length arrays of all those types to support the hardware functionality for those? Hardware vectors are NOT floats, they can not interact with the floating point unit, dereferencing of this style 'float x = myVector[0]' is NOT supported by the hardware and it should not be exposed to the programmer as a trivial possibility. This seemingly harmless line of code will undermine the entire reason for using hardware vector hardware in the first place. Allowing easy access of individual floats within a hardware vector breaks the languages stated premise that the path of least resistance also be the 'correct' optimal choice, whereby a seemingly simple line of code may ruin the entire function. float[4] is not even a particularly conveniently sized vector for most inexperienced programmers, the majority will want float[3]. This is NOT a trivial map to float[4], and programmers should be well aware that there is inherent complexity in using the hardware vector architecture, and forced to think it through. Most inexperienced programmers think of results of operations like dot product and magnitude as being scalar values, but they are not, they are a scalar value repeated across all 4 components of a vector 4, and this should be explicit too. .... I know I'm nobody around here, so I can't expect to be taken too seriously, but I'm very excited about the language, so here's what I would consider instead: Add a hardware vector type, lets call it 'v128' for the exercise. It is a primitive type, aligned by definition, and does not have any members. You may use this to refer to a hardware vector registers explicitly, as vector register function arguments, or as arguments to inline asm blocks. Add some functions to the standard library (ideally implemented as compiler intrinsics) which do very specific stuff to vectors, and ideally expandable by hardware vendors or platform holders. You might want to have some classes in the standard library which wrap said v128, and expose the concept as a float4, int4, byte16, etc. These classes would provide maths operators, comparisons, initialisation and immediate assignment, and casts between various vector types. Different vector units support completely different methods of permutation. I would be very careful about adding intrinsic support into the library for generalised permutation. And if so, at least leave the capability of implementing intrinsic architecture-specific permutation but the hardware vendors/platform holders. At the end of the day, it is imperative that the code generator and optimiser still retain the concept of hardware vectors, and can perform appropriate load/store elimination, apply hardware specific optimisation to operations like permutes/swizzles, component broadcasts, load immediates, etc. .... The reason I am so adamant about this, is in almost all architectures (SSE is the most tolerant by far), using the hardware vector unit is an all-or-nothing choice. If you interact between the vector and float registers, you will almost certainly result in slower code than if you just used the float unit outright. Also, since people usually use hardware vectors in areas of extreme performance optimisation, it's not tolerable for the compiler to be making mistakes. As a minimum the programmer needs to be able to explicitly address the vector registers, pass it to and from functions, and perform explicit (probably IHV supplied) functions on them. The code generator and optimiser needs all the information possible, and as explicit as possible so IHV's can implement the best possible support for their architecture. The API should reflect this, and not allow easy access to functionality that would violate hardware support. Ease of programming should be a SECONDARY goal, at which point something like the typed wrapper classes I described would come in, allowing maths operators, comparisons and all I mentioned above, ie, making them look like a real mathematical type, but still keeping their distance from primitive float/int types, to discourage interaction at all costs. I hope this doesn't sound too much like an overly long rant! :) And hopefully I've managed to sell my point... Don: I'd love to hear counter arguments to justify float[4] as a reasonable solution. Currently no matter how I slice it, I just can't see it. Criticism welcome? Cheers! - Manu