vector/elementals attribute syntax on GCC

vector/elementals attribute syntax on GCC

Hi folks.  Me again; trying to incorporate the elemental feature into GCC trunk.

The attribute syntax, as specified in the language spec is a bit problematic for the GCC infrastructure, and I'm wondering how committed are you to the current syntax.  For example, this is a sample vector use as allowed by the syntax:

__attribute__ ((vector(vectorlength(4), nomask, processor (core_i7_sse4_2), uniform (x,y))))
int foobar(int x, int y)

First, the GCC way for something like uniform(x,y) is to tag the appropriate variables with a corresponding attribute, not to tag the entire function and then specify the arguments out of scope.  Also, instead of a "vector" attribute with sub-attributes therein, the attributes should be separate entities.  So, the above should be rewritten as:

__attribute__((vector, vectorlength(4), nomask, processor(core_i7_sse4_2)))
void foobar (int x __attribute__((uniform)), int y __attribute__((uniform)))

This is in keeping with the rest of the attributes we support.  I'm not saying it's impossible to implement this as in the spec, but it would be rather odd as compared to the rest of the attributes we support.

Can this be changed in the spec?  

Also, it would be nice to have less generic names for these attributes, perhaps something like:

vector -> cilk_elemental
nomask -> cilk_elemental_nomask
processor -> cilk_elemental_processor
uniform -> cilk_elemental_uniform


8 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.

Hi Aldy

Regarding the syntax, multiple vector attributes can be specified for a single functoin.  Each vector attribute groups a bunch of sub-attributes together, so the sub-attributes cannot be specified separately from the vector attribute.  For example:

float f(float a, float x);

directs the compiler to create four vector (+ 1 scalar) variants of function f.  Each variant can have a different clause for x (uniform or linear) so it is not possible to use the syntax you suggested for attaching the attribute to x -- it has to be attached to a specific vector clause.  As for whether we can use commas instead of parenthesis to separate the word "vector" from its sub-attributes, the only problem I see is that Cilk Plus has used the existing syntax for several years and if the existing syntax is not accepted by gcc, then gcc cannot be used to compile existing Cilk Plus code.  That we chose a non-gcc-like syntax for our attributes is unfortunate. Now that the specification is public, it should be easier to enhance the spec in the future in such a way that it avoids conflicting with existing practice. We could consider, for example, a future replacement syntax for the attribute syntax that uses keywords instead, so that the same syntax could be used for both Unix and Windows. For example:

float f(float a, float x)

Since such a syntax has not be implemented in any compiler yet, we can play around with it, choose commas or parenthesis as desired, etc. However, Cilk Plus, as implemented in the Intel compiler and specified in the 1.1 version of the specification uses the __attribute__ syntax that you complain about, for better or for worse.  I believe that Balaji already has some parsing code for this purpose, so I hope it won't be too difficult for you to handle the non-standard attribute syntax.

Regarding the names: any name change would, again, be incompatible with existing implementation and specification. By the way, we have moved away from the term "elemental function" because an elemental function is a standard Fortran concept that means something different than our use of the term in Cilk Plus.  The current term is "Simd-enabled" function, which is both more accurate and, I think, clearer in its intent.

I think another important question is (something that I've asked some time ago and got clarified in OpenMP 4.0 for #pragma omp declare simd), do vectorlength argument and linear clause step have to be integer constants, or can they be integral constant expressions, and if the latter, in which scope they are supposed to be parsed?


int x, y; __attribute__((vector(vectorlength(2*sizeof(x)),linear(y:sizeof(y))))) double foo (double x, double y);

double bar (double u, double v); double bar (double x, double y) __attribute__((vector(linear(y:sizeof (x)))));

Is the vectorlength 2*sizeof(int) or 2*sizeof(double)?  Is the linear step sizeof(int) or sizeof(double)?  Both in the first and second elemental.

It is a while since I enumerated them, but C has at least three (perhaps four) classes of integer constant, and C++ one or two more, and with some semantic differences in the ones it inherits from C.  This area of C is a ghastly mess, for which I must accept some responsibility (though I was only a minor player), and so it's not a simple question and needs precision.

But why should it need to be a constant at all?  There is no technical difficulty in steps being variable (provided that they are evaluated as part of the directive and not changed while a block of SIMD code is executing), as Fortran vectorisation has shown for the past half century.

Found where it says it is integer constant expression (and for linear step can be instead a parameter name).

Thus, my question above applies, what scope is it parsed in, and for the linear step if it really is allowed to be a uniform parameter name, how to find out if it is a parameter name or integer constant expression.

Say in C++: constexpr int x = 2; __attribute__((vector(linear(y: x), uniform (x)))) double (int x, int y);

Would : x in there be parsed in the scope before the arguments are parsed, and thus parsed as y : 2?

"An elemental function shall not have an exception specification."

Does this mean that throw() is not allowed either (or noexcept in C++11)?

 >> Thus, my question above applies, what scope is it parsed in, and for the linear step if it really is allowed to be a uniform parameter name, >> how to find out if it is a parameter name or integer constant expression.Say in C++: constexpr int x = 2; __attribute__((vector(linear(y: x), uniform (x)))) double (int x, int y); 

One way to resolve this ambiguity in the grammar is to defer the parsing of the linear step until you have seen all the parameter names. Or parser could guess what kind of linear step to be parsed by one token lookahead. Then parse it as an expression or just store it as an identifier (its semantic meaning will be deferred until all parameter names are available.) The latter is implemented in Clang here:

I'm not asking how to implement it, GCC of course has a way to defer parsing of something etc., I'm asking what the Cilk+ language semantics is supposed to be in this case.  If every compiler does something different here, then it will be hardly usable by users.  So the spec should make this clear.  Say OpenMP 4.0 in 2.8.2 says: "The expressions appearing in the clauses of this directive are evaluated in the scope of the arguments of the function declaration or definition."  Though, in the Cilk+ case it can be even more complicated than in OpenMP, as in OpenMP the clauses always lexically precede the arguments, so tokens can be saved and parsed in the right scope.  But __attribute__ can be present before or after the arguments, and merging of duplicate decls can change the names of the arguments.

The Cilk Plus team has discussed this extensively and have decided to clarify the wording as follows:

If a simd-function-linear-step consists of a single identifier matching the name of a parameter, or in the declaration of a non-static member function is the keyword this, the simd-function-linear-step is interpreted as referring to the parameter; otherwise, the simd-linear-step shall satisfy the requirements of a constant-expression. A constant-expression in a simd-function-linear-step shall be an integer constant expression. A parameter referenced as a simd-function-linear-step shall be the subject of a uniform clause.

But I see now that this still doesn't answer Jacub's question.  In his example:

int x, y;
__attribute__((vector(vectorlength(2*sizeof(x)),linear(y:sizeof(y))))) double foo (double x, double y);

2*sizeof(x) is not a single identifier matching an arugment name, so the ambiguity is still there.  I'll go back to the team and see if we can clarify further. (Of course, this is a perverse situation and I hope nobody would actually write such code!)


Leave a Comment

Please sign in to add a comment. Not a member? Join today