Message ID | 5175B40F.7040709@redhat.com |
---|---|
State | New |
Headers | show |
On Mon, Apr 22, 2013 at 05:05:03PM -0500, Aldy Hernandez wrote: > On 04/19/13 08:29, Jakub Jelinek wrote: > >I've committed the following patch to gomp4 branch. > >#pragma omp simd loops now are handled with all its clauses from parsing up > >to and including omp expansion, so should actually run correctly, though > >haven't added any runtime testcases yet. > > I like it. Thanks for working on this. > > I've been working on rewriting the <#pragma simd> support on the > cilkplus branch to use a similar approach to what you do for openmp, > especially since both constructs seem to behave similarly, with the > exception of the "vectorlength" clause in Cilk Plus. Attached is a > patch against yours, doing so. > > The idea is that <#prama omp simd> and <#pragma simd> are pretty > much the same thing, so we can probably get away with outputting the > same OMP_SIMD tree code and letting omp do it's thing. I don't think you can use OMP_SIMD resp. GF_OMP_FOR_KIND_SIMD for #pragma simd, given that it has different semantics wrt. aliasing. Like: void foo (int *p, int *q) { int i; #pragma omp simd for (i = 0; i < 1024; i++) p[i] = q[i] + 1; } vs. void bar (int *p, int *q) { int i; #pragma simd for (i = 0; i < 1024; i++) p[i] = q[i] + 1; } It is a user error if one calls foo say with int arr[1028]; ... foo (arr + 4, arr); because through #pragma omp simd without safelen the programmer has asserted that all iterations can be executed in the same simd chunk (think about 4096 bytes long vectors in this case). So, #pragma omp simd expansion should be able to tell the vectorizer that it can ignore all inter-iteration dependencies during analysis. If I understood right, vectorlength isn't anything close to it, it is just a hint, if you vectorize, prefer this vector length, but the compiler is still responsible for doing the analysis, and punting if it can't prove there is no aliasing (or go for runtime checks). There is #pragma ivdep in ICC, but it's definition is vague - the compiler is still supposed to do analysis, but if some dependency isn't certain, it can assume it doesn't happen (which is the fuzzy thing about it, if the compiler can prove there is some dependency, then the code is valid and vectorization can't be done). So, IMHO you want CILK_SIMD tree (but it can use OMP and CILK clauses etc.), and GF_OMP_FOR_KIND_CILK_SIMD or so, and perhaps it can be expanded etc. exactly the same as #pragma omp simd, except for not telling the loop optimizers that it should imply safelen(+infinity). Or another option is let the C/C++ FEs, when seeing #pragma omp simd without safelen clause just add one with some very large value (unsigned TYPE_MAX_VALUE of a type with precision > precision of the loop iterator?). BTW, what restrictions has Cilk+ on the for stmt after the pragma? OpenMP has lots of restrictions, it doesn't allow arbitrary for stmt there. Like is int i, j, k; #pragma simd for (i = 0, j = 4, k = 5; i < 10 && j < 12; i++, j += 2, k += 3) ... valid Cilk+? It isn't valid with #pragma omp simd... Jakub
[Balaji, see below] On 04/23/13 08:54, Jakub Jelinek wrote: > On Mon, Apr 22, 2013 at 05:05:03PM -0500, Aldy Hernandez wrote: >> On 04/19/13 08:29, Jakub Jelinek wrote: > If I understood right, vectorlength isn't anything close to it, it is just a > hint, if you vectorize, prefer this vector length, but the compiler is still > responsible for doing the analysis, and punting if it can't prove there is > no aliasing (or go for runtime checks). Are you sure it's just a hint? (My legalese is very bad). The spec says "If the vectorlength clause is used, the VL is selected from among the values of its arguments." I guess it's not clear what happens if the compiler can't use the vectorlength argument. Balaji, would this be a hard error? Is this merely a hint as Jakub says? > So, IMHO you want CILK_SIMD tree (but it can use OMP and CILK clauses etc.), > and GF_OMP_FOR_KIND_CILK_SIMD or so, and perhaps it can be expanded etc. > exactly the same as #pragma omp simd, except for not telling the loop > optimizers that it should imply safelen(+infinity). Or another option > is let the C/C++ FEs, when seeing #pragma omp simd without safelen clause > just add one with some very large value (unsigned TYPE_MAX_VALUE of > a type with precision > precision of the loop iterator?). Actually, if it's all the same to you, I would prefer the latter. It seems the easier approach is to have Cilk use OMP_SIMD, and set a safelen of 0 or something at parse time. Would this be ok, or would you prefer a separate CILK_SIMD tree which gets expanded into OMP_FOR with a GF_OMP_FOR_KIND_CILK_SIMD? > > BTW, what restrictions has Cilk+ on the for stmt after the pragma? > OpenMP has lots of restrictions, it doesn't allow arbitrary for stmt there. > > Like is > int i, j, k; > #pragma simd > for (i = 0, j = 4, k = 5; i < 10 && j < 12; i++, j += 2, k += 3) > ... > valid Cilk+? It isn't valid with #pragma omp simd... You obviously didn't look at the plethora of testcases I included with my patch :). There are quite a few restrictions, thus the need in my patch to provide a separate parser for the for loop (not totally unlike what is done for gomp). See c_check_cilk_loop(), but basically... 1. Initialization is required and can only include one variable. Variable cannot be volatile, extern, global, register (have any storage class specifiers). 2. Condition is required and can only be of the form: DECL <comparison_op> EXPR EXPR <comparison_op> DECL Where comparison_op is one of !=, <, <=, >, >=. (== is not allowed). 3. Increment is a simple expression on the induction variable. It is required, and can only be one of: ++var, var++, --var, var--, var += incr, var -= incr. Where incr is a conditional-expression with integral (or enum) type. Can you take a peek at the patch? Are we in agreement wrt the general approach here-- lest I go too far down the wrong rabbit hole? Thanks.
> -----Original Message----- > From: Jakub Jelinek [mailto:jakub@redhat.com] > Sent: Tuesday, April 23, 2013 9:55 AM > To: Aldy Hernandez > Cc: Richard Henderson; gcc-patches@gcc.gnu.org; Iyer, Balaji V > Subject: Re: [gomp4] Some progress on #pragma omp simd > > On Mon, Apr 22, 2013 at 05:05:03PM -0500, Aldy Hernandez wrote: > > On 04/19/13 08:29, Jakub Jelinek wrote: > > >I've committed the following patch to gomp4 branch. > > >#pragma omp simd loops now are handled with all its clauses from > > >parsing up to and including omp expansion, so should actually run > > >correctly, though haven't added any runtime testcases yet. > > > > I like it. Thanks for working on this. > > > > I've been working on rewriting the <#pragma simd> support on the > > cilkplus branch to use a similar approach to what you do for openmp, > > especially since both constructs seem to behave similarly, with the > > exception of the "vectorlength" clause in Cilk Plus. Attached is a > > patch against yours, doing so. > > > > The idea is that <#prama omp simd> and <#pragma simd> are pretty much > > the same thing, so we can probably get away with outputting the same > > OMP_SIMD tree code and letting omp do it's thing. > > I don't think you can use OMP_SIMD resp. GF_OMP_FOR_KIND_SIMD for > #pragma simd, given that it has different semantics wrt. aliasing. > > Like: > void > foo (int *p, int *q) > { > int i; > #pragma omp simd > for (i = 0; i < 1024; i++) > p[i] = q[i] + 1; > } > vs. > void > bar (int *p, int *q) > { > int i; > #pragma simd > for (i = 0; i < 1024; i++) > p[i] = q[i] + 1; > } > > It is a user error if one calls foo say with int arr[1028]; ... foo (arr + 4, arr); > because through #pragma omp simd without safelen the programmer has > asserted that all iterations can be executed in the same simd chunk (think about > 4096 bytes long vectors in this case). So, #pragma omp simd expansion should > be able to tell the vectorizer that it can ignore all inter-iteration dependencies > during analysis. > > If I understood right, vectorlength isn't anything close to it, it is just a hint, if you > vectorize, prefer this vector length, but the compiler is still responsible for doing > the analysis, and punting if it can't prove there is no aliasing (or go for runtime > checks). Hi Jakub, My apologies if the documentation did not explain this correctly. It was written by compiler developers and not language developers. #pragma simd is the guarantee the user gives the compiler that the inter-iteration dependencies do not matter. So, if the user omits the vectorlength the clause then the compiler can, in effect, choose N, where N is the number of loop iterations. Thanks, Balaji V. Iyer. > > There is #pragma ivdep in ICC, but it's definition is vague - the compiler is still > supposed to do analysis, but if some dependency isn't certain, it can assume it > doesn't happen (which is the fuzzy thing about it, if the compiler can prove there > is some dependency, then the code is valid and vectorization can't be done). > > So, IMHO you want CILK_SIMD tree (but it can use OMP and CILK clauses etc.), > and GF_OMP_FOR_KIND_CILK_SIMD or so, and perhaps it can be expanded etc. > exactly the same as #pragma omp simd, except for not telling the loop > optimizers that it should imply safelen(+infinity). Or another option is let the > C/C++ FEs, when seeing #pragma omp simd without safelen clause just add one > with some very large value (unsigned TYPE_MAX_VALUE of a type with precision > > precision of the loop iterator?). > > BTW, what restrictions has Cilk+ on the for stmt after the pragma? > OpenMP has lots of restrictions, it doesn't allow arbitrary for stmt there. > > Like is > int i, j, k; > #pragma simd > for (i = 0, j = 4, k = 5; i < 10 && j < 12; i++, j += 2, k += 3) > ... > valid Cilk+? It isn't valid with #pragma omp simd... > > Jakub
On Tue, Apr 23, 2013 at 09:32:29PM +0000, Iyer, Balaji V wrote: > My apologies if the documentation did not explain this correctly. It > was written by compiler developers and not language developers. #pragma > simd is the guarantee the user gives the compiler that the inter-iteration > dependencies do not matter. So, if the user omits the vectorlength the > clause then the compiler can, in effect, choose N, where N is the number > of loop iterations. The documentation doesn't suggest that. Anyway, so #pragma simd should be equivalent to #pragma omp simd wrt. inter-iteration dependencies, and #pragma simd vectorlength(a, b, c) to #pragma omp simd safelen(max (a, b, c)) ? If so, then the FE could emit OMP_SIMD for #pragma simd, and if vectorlength is present, add OMP_CLAUSE_SAFELEN with the maximum of the values in all vectorlength clauses, and keep the vectorlength clauses around too as CILK_CLAUSE_VECTORLENGTH as hints to the vectorizer? Also, Aldy said that #pragma simd loops allow != condition, how do you compute number of iterations in that case if the increment isn't constant? As conditional depending on whether increment is positive or negative? != condition isn't allowed in OpenMP, so there it is always obvious which direction it should iterate, and the expansion code will assume if it sees NE_EXPR that it is just folded border test (comparison with maximum or minimum value). Jakub
On Wed, Apr 24, 2013 at 08:01:17AM +0200, Jakub Jelinek wrote: > On Tue, Apr 23, 2013 at 09:32:29PM +0000, Iyer, Balaji V wrote: > The documentation doesn't suggest that. Anyway, so > #pragma simd > should be equivalent to > #pragma omp simd > wrt. inter-iteration dependencies, and > #pragma simd vectorlength(a, b, c) > to > #pragma omp simd safelen(max (a, b, c)) > ? If so, then the FE could emit OMP_SIMD for #pragma simd, and if > vectorlength is present, add OMP_CLAUSE_SAFELEN with the maximum of the > values in all vectorlength clauses, and keep the vectorlength clauses around > too as CILK_CLAUSE_VECTORLENGTH as hints to the vectorizer? > > Also, Aldy said that #pragma simd loops allow != condition, how do you > compute number of iterations in that case if the increment isn't constant? > As conditional depending on whether increment is positive or negative? > != condition isn't allowed in OpenMP, so there it is always obvious which > direction it should iterate, and the expansion code will assume if it sees > NE_EXPR that it is just folded border test (comparison with maximum or > minimum value). BTW, the semantics of private/firstprivate/lastprivate desribed in http://software.intel.com/sites/products/documentation/studio/composer/en-us/2011Update/compiler_c/cref_cls/common/cppref_pragma_simd.htm doesn't seem to match the semantics of those in #pragma omp simd. private in OpenMP I understand is private to the whole loop (or SIMD lane?; at least, that was the semantics of #pragma omp for too and there is no wording to suggest otherwise for #pragma omp simd or #pragma omp for simd), while the above html suggests in Cilk+ it is private to each iteration. #pragma omp simd doesn't support firstprivate. The lastprivate semantics wrt. returning the last iteration's value is the same. Jakub
On Wed, Apr 24, 2013 at 08:25:36AM +0200, Jakub Jelinek wrote: > BTW, the semantics of private/firstprivate/lastprivate desribed in > http://software.intel.com/sites/products/documentation/studio/composer/en-us/2011Update/compiler_c/cref_cls/common/cppref_pragma_simd.htm > doesn't seem to match the semantics of those in #pragma omp simd. > private in OpenMP I understand is private to the whole loop (or SIMD lane?; SIMD lane apparently. Guess that is going to be quite difficult, because at the point of omp lowering or expansion we are nowhere close to knowing what vectorization factor we are going to choose, all we have is an upper bound on that based on the target ISA and safelen clause. If say private clause is used with C++ classes with non-trivial ctors/dtors that would make a difference. Plus how to represent this in the IL. struct A { A (); ~A (); A (const A &); int i; }; void foo () { A a, b; #pragma omp simd private (a) lastprivate (b) for (int i = 0; i < 10; i++) { a.i++; b.i++; } } Right now what gomp4 branch does is that it will just construct private vars around the whole loop, as in: void foo () { A a, b; { A a', b'; int i; for (i = 0; i < 10; i++) { a'.i++; b'.i++; if (i == 9) b = b'; } } } Jakub
[Balaji, see below]. Ok, this is confusing. While the document in the link you posted (the ICC manual?) says so, the document I'm following says otherwise. I'm following this (which, until a few days was a link accessible from the cilk plus web page, though I no longer see it): http://software.intel.com/sites/default/files/m/4/e/7/3/1/40297-Intel_Cilk_plus_lang_spec_2.htm The document above is for version 1.1 of the Cilk Plus language extension specification, which I was told was the latest. There it explicitly says that the clauses behave exactly like in OpenMP: "The syntax and semantics of the various simd-openmp-data-clauses are detailed in the OpenMP specification. (http://www.openmp.org/mp-documents/spec30.pdf, Section 2.9.3)." Balaji, can you verify which is correct? For that matter, which are the official specs from which we should be basing this work? Aldy On 04/24/13 01:40, Jakub Jelinek wrote: > On Wed, Apr 24, 2013 at 08:25:36AM +0200, Jakub Jelinek wrote: >> BTW, the semantics of private/firstprivate/lastprivate desribed in >> http://software.intel.com/sites/products/documentation/studio/composer/en-us/2011Update/compiler_c/cref_cls/common/cppref_pragma_simd.htm >> doesn't seem to match the semantics of those in #pragma omp simd. >> private in OpenMP I understand is private to the whole loop (or SIMD lane?; > > SIMD lane apparently. Guess that is going to be quite difficult, because > at the point of omp lowering or expansion we are nowhere close to knowing > what vectorization factor we are going to choose, all we have is an > upper bound on that based on the target ISA and safelen clause. > If say private clause is used with C++ classes with non-trivial ctors/dtors > that would make a difference. Plus how to represent this in the IL. > > struct A { A (); ~A (); A (const A &); int i; }; > > void > foo () > { > A a, b; > #pragma omp simd private (a) lastprivate (b) > for (int i = 0; i < 10; i++) > { > a.i++; > b.i++; > } > } > > Right now what gomp4 branch does is that it will just construct private > vars around the whole loop, as in: > void > foo () > { > A a, b; > { > A a', b'; > int i; > for (i = 0; i < 10; i++) > { > a'.i++; > b'.i++; > if (i == 9) > b = b'; > } > } > } > > Jakub >
On 04/24/13 18:22, Aldy Hernandez wrote: Hmmm, furthermore, even if the simd + private semantics in Cilk Plus have the same semantics of the OpenMP standard, is it the OpenMP 3.0 semantics like the openmp link suggests (private to task), or is it the OpenMP 4.0 rc 2 semantics which you suggest (private to SIMD lane)? Lemme ask icc. > [Balaji, see below]. > > Ok, this is confusing. While the document in the link you posted (the > ICC manual?) says so, the document I'm following says otherwise. > > I'm following this (which, until a few days was a link accessible from > the cilk plus web page, though I no longer see it): > > http://software.intel.com/sites/default/files/m/4/e/7/3/1/40297-Intel_Cilk_plus_lang_spec_2.htm > > > The document above is for version 1.1 of the Cilk Plus language > extension specification, which I was told was the latest. There it > explicitly says that the clauses behave exactly like in OpenMP: > > "The syntax and semantics of the various simd-openmp-data-clauses are > detailed in the OpenMP specification. > (http://www.openmp.org/mp-documents/spec30.pdf, Section 2.9.3)." > > Balaji, can you verify which is correct? For that matter, which are the > official specs from which we should be basing this work? > > Aldy > > > On 04/24/13 01:40, Jakub Jelinek wrote: >> On Wed, Apr 24, 2013 at 08:25:36AM +0200, Jakub Jelinek wrote: >>> BTW, the semantics of private/firstprivate/lastprivate desribed in >>> http://software.intel.com/sites/products/documentation/studio/composer/en-us/2011Update/compiler_c/cref_cls/common/cppref_pragma_simd.htm >>> >>> doesn't seem to match the semantics of those in #pragma omp simd. >>> private in OpenMP I understand is private to the whole loop (or SIMD >>> lane?; >> >> SIMD lane apparently. Guess that is going to be quite difficult, because >> at the point of omp lowering or expansion we are nowhere close to knowing >> what vectorization factor we are going to choose, all we have is an >> upper bound on that based on the target ISA and safelen clause. >> If say private clause is used with C++ classes with non-trivial >> ctors/dtors >> that would make a difference. Plus how to represent this in the IL. >> >> struct A { A (); ~A (); A (const A &); int i; }; >> >> void >> foo () >> { >> A a, b; >> #pragma omp simd private (a) lastprivate (b) >> for (int i = 0; i < 10; i++) >> { >> a.i++; >> b.i++; >> } >> } >> >> Right now what gomp4 branch does is that it will just construct private >> vars around the whole loop, as in: >> void >> foo () >> { >> A a, b; >> { >> A a', b'; >> int i; >> for (i = 0; i < 10; i++) >> { >> a'.i++; >> b'.i++; >> if (i == 9) >> b = b'; >> } >> } >> } >> >> Jakub >> >
On 04/24/13 01:01, Jakub Jelinek wrote: > The documentation doesn't suggest that. Anyway, so > #pragma simd > should be equivalent to > #pragma omp simd > wrt. inter-iteration dependencies, and > #pragma simd vectorlength(a, b, c) > to > #pragma omp simd safelen(max (a, b, c)) > ? If so, then the FE could emit OMP_SIMD for #pragma simd, and if > vectorlength is present, add OMP_CLAUSE_SAFELEN with the maximum of the > values in all vectorlength clauses, and keep the vectorlength clauses around > too as CILK_CLAUSE_VECTORLENGTH as hints to the vectorizer? I can make these changes to the Cilk Plus code. > > Also, Aldy said that #pragma simd loops allow != condition, how do you > compute number of iterations in that case if the increment isn't constant? > As conditional depending on whether increment is positive or negative? > != condition isn't allowed in OpenMP, so there it is always obvious which > direction it should iterate, and the expansion code will assume if it sees > NE_EXPR that it is just folded border test (comparison with maximum or > minimum value). Perhaps it is an oversight in the documentation, because icc disallows both == and !=. Disallowing != makes it a lot easier :).
Hello Aldy and Jakub, Please see my response below. Thanks, Balaji V. Iyer. > -----Original Message----- > From: Aldy Hernandez [mailto:aldyh@redhat.com] > Sent: Wednesday, April 24, 2013 7:22 PM > To: Jakub Jelinek > Cc: Iyer, Balaji V; Richard Henderson; gcc-patches@gcc.gnu.org > Subject: Re: [gomp4] Some progress on #pragma omp simd > > [Balaji, see below]. > > Ok, this is confusing. While the document in the link you posted (the ICC > manual?) says so, the document I'm following says otherwise. > > I'm following this (which, until a few days was a link accessible from the cilk plus > web page, though I no longer see it): Yes, I am aware of the missing link. We are currently looking into it. > > http://software.intel.com/sites/default/files/m/4/e/7/3/1/40297- > Intel_Cilk_plus_lang_spec_2.htm > > The document above is for version 1.1 of the Cilk Plus language extension > specification, which I was told was the latest. There it explicitly says that the > clauses behave exactly like in OpenMP: > > "The syntax and semantics of the various simd-openmp-data-clauses are > detailed in the OpenMP specification. > (http://www.openmp.org/mp-documents/spec30.pdf, Section 2.9.3)." > > Balaji, can you verify which is correct? For that matter, which are the official > specs from which we should be basing this work? Privatization clause makes a variable private for the simd lane. In general, I would follow the spec. If you have further questions, please feel free to ask. Thanks, Balaji V. Iyer. > > Aldy > > > On 04/24/13 01:40, Jakub Jelinek wrote: > > On Wed, Apr 24, 2013 at 08:25:36AM +0200, Jakub Jelinek wrote: > >> BTW, the semantics of private/firstprivate/lastprivate desribed in > >> http://software.intel.com/sites/products/documentation/studio/compose > >> r/en- > us/2011Update/compiler_c/cref_cls/common/cppref_pragma_simd.htm > >> doesn't seem to match the semantics of those in #pragma omp simd. > >> private in OpenMP I understand is private to the whole loop (or SIMD > >> lane?; > > > > SIMD lane apparently. Guess that is going to be quite difficult, > > because at the point of omp lowering or expansion we are nowhere close > > to knowing what vectorization factor we are going to choose, all we > > have is an upper bound on that based on the target ISA and safelen clause. > > If say private clause is used with C++ classes with non-trivial > > ctors/dtors that would make a difference. Plus how to represent this in the IL. > > > > struct A { A (); ~A (); A (const A &); int i; }; > > > > void > > foo () > > { > > A a, b; > > #pragma omp simd private (a) lastprivate (b) > > for (int i = 0; i < 10; i++) > > { > > a.i++; > > b.i++; > > } > > } > > > > Right now what gomp4 branch does is that it will just construct > > private vars around the whole loop, as in: > > void > > foo () > > { > > A a, b; > > { > > A a', b'; > > int i; > > for (i = 0; i < 10; i++) > > { > > a'.i++; > > b'.i++; > > if (i == 9) > > b = b'; > > } > > } > > } > > > > Jakub > >
Hi Balaji. >> "The syntax and semantics of the various simd-openmp-data-clauses >> are detailed in the OpenMP specification. >> (http://www.openmp.org/mp-documents/spec30.pdf, Section 2.9.3)." >> >> Balaji, can you verify which is correct? For that matter, which >> are the official specs from which we should be basing this work? > > Privatization clause makes a variable private for the simd lane. In > general, I would follow the spec. If you have further questions, > please feel free to ask. Ok, so the Cilk Plus 1.1 spec is incorrectly pointing to the OpenMP 3.0 spec, because the OpenMP 3.0 spec has the private clause being task/thread private. Since the OpenMP 4.0rc2 explicitly says that the private clause is for the SIMD lane (as you've stated), can we assume that when the Cilk Plus 1.1 spec mentions OpenMP, it is talking about the OpenMP 4.0 spec? One more question Balaji, the Cilk Plus spec says that for #pragma simd, the private, firstprivate, lastprivate, and reduction clauses are as OpenMP. However, for <#omp simd>, there is no firstprivate in the OpenMP 4.0rc2 spec. Is the firstprivate clause valid for Cilk Plus' <#pragma simd>? Thanks. Aldy
On Sat, Apr 27, 2013 at 12:30:28PM -0500, Aldy Hernandez wrote: > >>"The syntax and semantics of the various simd-openmp-data-clauses > >>are detailed in the OpenMP specification. > >>(http://www.openmp.org/mp-documents/spec30.pdf, Section 2.9.3)." > >> > >>Balaji, can you verify which is correct? For that matter, which > >>are the official specs from which we should be basing this work? > > > >Privatization clause makes a variable private for the simd lane. In > >general, I would follow the spec. If you have further questions, > >please feel free to ask. > > Ok, so the Cilk Plus 1.1 spec is incorrectly pointing to the OpenMP > 3.0 spec, because the OpenMP 3.0 spec has the private clause being > task/thread private. Since the OpenMP 4.0rc2 explicitly says that > the private clause is for the SIMD lane (as you've stated), can we > assume that when the Cilk Plus 1.1 spec mentions OpenMP, it is > talking about the OpenMP 4.0 spec? One way we could implement the SIMD private/lastprivate/reduction vars and for Cilk+ also firstprivate ones might be: - query the target what the maximum possible vectorization factor for the loop is (and min that with simdlen if any), let's call it MAXVF for say struct S { S (); ~S (); int x; }; ... int a, b; S s; #pragma omp simd private (a, s) reduction (+:b) for (int i = 0; i < N; i++) { foo (&a, &s); b += a; } we'd then emit something like: int a_[MAXVF], b_[MAXVF]; S s_[MAXVF]; for (tmp = 0; tmp < __builtin_omp.simd_vf (simd_uid); tmp++) { b_[tmp] = 0; S::S (&s_[tmp]); } # loop simd_uid with safelen(MAXVF) for (i = 0; i < N; i++) { tmp = __builtin_omp.simd_lane (simd_uid); foo (&a_[tmp], &s_[tmp]); b_[tmp] += a_[tmp]; } for (tmp = 0; tmp < __builtin_omp.simd_vf (simd_uid); tmp++) { S::~S (&s[tmp]); b += b_[tmp]; } where simd_uid would be some say integer constant, unique to the simd loop (at least unique within the same function, and perhaps inlining/LTO would need to remap). The loop simd_uid would be stored by ompexp pass into the loop structure. Then the vectorizer (ideally, we'd enable vectorization even when not explicitly disabled through -fno-tree-vectorize for -fopenmp or -fcilk+, though in that case only for the explicit simd loops) would treat arrays indexed by __builtin_omp.simd_lane (simd_uid) (dot in the name just to make it impossible to be used by users) (or marked with some special hidden attribute or something) specially, allow promoting them to just vector vars if not addressable, etc., and would record the chosen vectorization factor in the loop structure, and __builtin_omp.simd_vf would then expand to the vectorization factor and __builtin_omp.simd_lane to the number of the lane. If vectorization couldn't be performed on some loop, __builtin_omp.simd_vf would just be folded into 1 and __builtin_omp.simd_lane into 0 say by some ompsimd pass run soon after the vectorization. Thoughts on this? Or do you see better IL representation of this stuff from the omp expansion till vectorization? I mean, e.g. for floating point or user defined reductions it might be important in what order they are performed (unless -ffast-math for the former). Jakub
> -----Original Message----- > From: gcc-patches-owner@gcc.gnu.org [mailto:gcc-patches- > owner@gcc.gnu.org] On Behalf Of Aldy Hernandez > Sent: Saturday, April 27, 2013 1:30 PM > To: Iyer, Balaji V > Cc: Jakub Jelinek; Richard Henderson; gcc-patches@gcc.gnu.org > Subject: Re: [gomp4] Some progress on #pragma omp simd > > Hi Balaji. > > >> "The syntax and semantics of the various simd-openmp-data-clauses are > >> detailed in the OpenMP specification. > >> (http://www.openmp.org/mp-documents/spec30.pdf, Section 2.9.3)." > >> > >> Balaji, can you verify which is correct? For that matter, which are > >> the official specs from which we should be basing this work? > > > > Privatization clause makes a variable private for the simd lane. In > > general, I would follow the spec. If you have further questions, > > please feel free to ask. > > Ok, so the Cilk Plus 1.1 spec is incorrectly pointing to the OpenMP 3.0 spec, > because the OpenMP 3.0 spec has the private clause being task/thread private. > Since the OpenMP 4.0rc2 explicitly says that the private clause is for the SIMD > lane (as you've stated), can we assume that when the Cilk Plus 1.1 spec mentions > OpenMP, it is talking about the OpenMP 4.0 spec? I don't know of all the references to the OMP manual in the spec, so I will be a bit hesitant to make a blanket assumption like that. In this case, I think you can assume that it behaves in the same way as 4.0. If you have further questions, please feel free to ask. In general, #pragma simd, array notation and elemental functions deal with vectorization, not threading. But, Cilk part (Cilk keywords and reducers) deal with threading. All these parts can be mixed and matched (with restrictions) to take advantage of both threading and vectorization. > > One more question Balaji, the Cilk Plus spec says that for #pragma simd, the > private, firstprivate, lastprivate, and reduction clauses are as OpenMP. > However, for <#omp simd>, there is no firstprivate in the OpenMP 4.0rc2 spec. > Is the firstprivate clause valid for Cilk Plus' > <#pragma simd>? > > Thanks. > Aldy
On 04/28/13 09:45, Iyer, Balaji V wrote: >> One more question Balaji, the Cilk Plus spec says that for #pragma simd, the >> private, firstprivate, lastprivate, and reduction clauses are as OpenMP. >> However, for <#omp simd>, there is no firstprivate in the OpenMP 4.0rc2 spec. >> Is the firstprivate clause valid for Cilk Plus' >> <#pragma simd>? Well, it looks like the Cilk Plus folks are looking into possibly deprecating the firstprivate clause for the next revision of the spec. This would bring things in harmony with OpenMP 4.0.
On 04/24/13 01:01, Jakub Jelinek wrote: > On Tue, Apr 23, 2013 at 09:32:29PM +0000, Iyer, Balaji V wrote: >> My apologies if the documentation did not explain this correctly. It >> was written by compiler developers and not language developers. #pragma >> simd is the guarantee the user gives the compiler that the inter-iteration >> dependencies do not matter. So, if the user omits the vectorlength the >> clause then the compiler can, in effect, choose N, where N is the number >> of loop iterations. > > The documentation doesn't suggest that. Anyway, so > #pragma simd > should be equivalent to > #pragma omp simd > wrt. inter-iteration dependencies, and > #pragma simd vectorlength(a, b, c) > to > #pragma omp simd safelen(max (a, b, c)) > ? If so, then the FE could emit OMP_SIMD for #pragma simd, and if > vectorlength is present, add OMP_CLAUSE_SAFELEN with the maximum of the > values in all vectorlength clauses, and keep the vectorlength clauses around > too as CILK_CLAUSE_VECTORLENGTH as hints to the vectorizer? Well, it looks like things are bit simpler than expected. Multiple vectorlength clauses are being deprecated or eliminated in the upcoming spec. So it looks like vectorlength is the same thing as the safelen clause. If you agree then I can get rid of OMP_CLAUSE_CILK_VECTORLENGTH and just emit an OMP_CLAUSE_SAFELEN. Agreed? > > Also, Aldy said that #pragma simd loops allow != condition, how do you > compute number of iterations in that case if the increment isn't constant? > As conditional depending on whether increment is positive or negative? > != condition isn't allowed in OpenMP, so there it is always obvious which > direction it should iterate, and the expansion code will assume if it sees > NE_EXPR that it is just folded border test (comparison with maximum or > minimum value). I verified with the Cilk Plus folks, and the number of iterations is calculated with a conditional. So to evaluate something like this: // incr = -1 for (i=N; i != limit; i += incr) [body] We would generate something like this: if (incr > 0) count = (limit - N) / incr; else count = (N - limit) / -incr; for (i=N; count > 0; --count) [body]
> -----Original Message----- > From: gcc-patches-owner@gcc.gnu.org [mailto:gcc-patches- > owner@gcc.gnu.org] On Behalf Of Aldy Hernandez > Sent: Wednesday, May 01, 2013 11:52 AM > To: Jakub Jelinek > Cc: Iyer, Balaji V; Richard Henderson; gcc-patches@gcc.gnu.org > Subject: Re: [gomp4] Some progress on #pragma omp simd > > On 04/24/13 01:01, Jakub Jelinek wrote: > > On Tue, Apr 23, 2013 at 09:32:29PM +0000, Iyer, Balaji V wrote: > >> My apologies if the documentation did not explain this correctly. It > >> was written by compiler developers and not language developers. > >> #pragma simd is the guarantee the user gives the compiler that the > >> inter-iteration dependencies do not matter. So, if the user omits > >> the vectorlength the clause then the compiler can, in effect, choose > >> N, where N is the number of loop iterations. > > > > The documentation doesn't suggest that. Anyway, so #pragma simd > > should be equivalent to #pragma omp simd wrt. inter-iteration > > dependencies, and #pragma simd vectorlength(a, b, c) to #pragma omp > > simd safelen(max (a, b, c)) ? If so, then the FE could emit OMP_SIMD > > for #pragma simd, and if vectorlength is present, add > > OMP_CLAUSE_SAFELEN with the maximum of the values in all vectorlength > > clauses, and keep the vectorlength clauses around too as > > CILK_CLAUSE_VECTORLENGTH as hints to the vectorizer? > > Well, it looks like things are bit simpler than expected. > > Multiple vectorlength clauses are being deprecated or eliminated in the > upcoming spec. So it looks like vectorlength is the same thing as the safelen > clause. > > If you agree then I can get rid of OMP_CLAUSE_CILK_VECTORLENGTH and just > emit an OMP_CLAUSE_SAFELEN. > > Agreed? To my best knowledge, Yes. I believe safelen requires/allows only 1 value, so we should do what Jakub mentioned (vectorlength (a, b, c)) should be converted to safelen (max(a,b,c)) > > > > > Also, Aldy said that #pragma simd loops allow != condition, how do you > > compute number of iterations in that case if the increment isn't constant? > > As conditional depending on whether increment is positive or negative? > > != condition isn't allowed in OpenMP, so there it is always obvious > > which direction it should iterate, and the expansion code will assume > > if it sees NE_EXPR that it is just folded border test (comparison with > > maximum or minimum value). > > I verified with the Cilk Plus folks, and the number of iterations is calculated with > a conditional. So to evaluate something like this: > > // incr = -1 > for (i=N; i != limit; i += incr) > [body] > > We would generate something like this: > > if (incr > 0) count = (limit - N) / incr; else count = (N - limit) / -incr; for (i=N; count > > 0; --count) > [body] Sounds OK.
On 05/01/13 10:58, Iyer, Balaji V wrote: >> >> Well, it looks like things are bit simpler than expected. >> >> Multiple vectorlength clauses are being deprecated or eliminated >> in the upcoming spec. So it looks like vectorlength is the same >> thing as the safelen clause. >> >> If you agree then I can get rid of OMP_CLAUSE_CILK_VECTORLENGTH >> and just emit an OMP_CLAUSE_SAFELEN. >> >> Agreed? > > To my best knowledge, Yes. I believe safelen requires/allows only 1 > value, so we should do what Jakub mentioned (vectorlength (a, b, c)) > should be converted to safelen (max(a,b,c)) Well, we don't even need to do max(a,b,c) because according to the Cilk Plus forum, the upcoming spec does not allow multiple vectorlength clauses (or arguments), so only vectorlength(const_expr) is allowed. Did I misunderstand something here?
> -----Original Message----- > From: gcc-patches-owner@gcc.gnu.org [mailto:gcc-patches- > owner@gcc.gnu.org] On Behalf Of Aldy Hernandez > Sent: Wednesday, May 01, 2013 12:02 PM > To: Iyer, Balaji V > Cc: Jakub Jelinek; Richard Henderson; gcc-patches@gcc.gnu.org > Subject: Re: [gomp4] Some progress on #pragma omp simd > > On 05/01/13 10:58, Iyer, Balaji V wrote: > >> > >> Well, it looks like things are bit simpler than expected. > >> > >> Multiple vectorlength clauses are being deprecated or eliminated in > >> the upcoming spec. So it looks like vectorlength is the same thing > >> as the safelen clause. > >> > >> If you agree then I can get rid of OMP_CLAUSE_CILK_VECTORLENGTH and > >> just emit an OMP_CLAUSE_SAFELEN. > >> > >> Agreed? > > > > To my best knowledge, Yes. I believe safelen requires/allows only 1 > > value, so we should do what Jakub mentioned (vectorlength (a, b, c)) > > should be converted to safelen (max(a,b,c)) > > Well, we don't even need to do max(a,b,c) because according to the Cilk Plus > forum, the upcoming spec does not allow multiple vectorlength clauses (or > arguments), so only vectorlength(const_expr) is allowed. > > Did I misunderstand something here? I am not sure about the upcoming spec, but as per today's spec vectorlength in pragma simd can have multiple parameters and so I think it would be a good idea to use a max. Thanks, Balaji V. Iyer.
> I am not sure about the upcoming spec, but as per today's spec > vectorlength in pragma simd can have multiple parameters and so I > think it would be a good idea to use a max. As discussed here: http://software.intel.com/en-us/forums/topic/391056 "The use of multiple vectorlength arguments is being removed (or at least deprecated) in the next revision of the spec., since it carries no semantic meaning and is not even a very good hint to the compiler." If/when we merge into mainline, I am pretty sure the latest spec will be out, so there's little sense in spending time on implementing things that will be obsolete by the time we release. Disallowing multiple vectorlengths makes it trivial to just use OpenMP's safelen clause without any additional code. Aldy
On 05/01/2013 01:53 PM, Aldy Hernandez wrote: > >> I am not sure about the upcoming spec, but as per today's spec >> vectorlength in pragma simd can have multiple parameters and so I >> think it would be a good idea to use a max. > > As discussed here: > > http://software.intel.com/en-us/forums/topic/391056 > > "The use of multiple vectorlength arguments is being removed (or at > least deprecated) in the next revision of the spec., since it carries no > semantic meaning and is not even a very good hint to the compiler." > > If/when we merge into mainline, I am pretty sure the latest spec will be > out, so there's little sense in spending time on implementing things > that will be obsolete by the time we release. Disallowing multiple > vectorlengths makes it trivial to just use OpenMP's safelen clause > without any additional code. Agreed. If it's being deprecated, let's just not bother ever implementing it in GCC. The fact that doing so allows OpenMP & Cilk to share implementation bits is just icing on the cake. jeff
On 04/27/2013 11:17 AM, Jakub Jelinek wrote: > where simd_uid would be some say integer constant, unique to the simd loop > (at least unique within the same function, and perhaps inlining/LTO would > need to remap). If all we need is uniqueness, then perhaps an otherwise unused decl would do? We're already prepared to remap those during inlining/LTO... > treat arrays indexed by __builtin_omp.simd_lane (simd_uid) (dot in the name just > to make it impossible to be used by users) (or marked with some special > hidden attribute or something) I see /* This file specifies a list of internal "functions". These functions differ from built-in functions in that they have no linkage and cannot be called directly by the user. They represent operations that are only synthesised by GCC itself. and think that may be more applicable than adding dots to regular builtins. r~
On Wed, Jun 12, 2013 at 10:21:53AM -0700, Richard Henderson wrote: > On 04/27/2013 11:17 AM, Jakub Jelinek wrote: > > where simd_uid would be some say integer constant, unique to the simd loop > > (at least unique within the same function, and perhaps inlining/LTO would > > need to remap). > > If all we need is uniqueness, then perhaps an otherwise unused decl would do? > We're already prepared to remap those during inlining/LTO... So the built-ins would take address of this decl, something else? Then there is the _simduid_ clause (also can hold address of the decl), and after lowering it lives only in loop structure (so perhaps remove_unused_locals would need to mark the decls referenced from loop structure as used?). > > treat arrays indexed by __builtin_omp.simd_lane (simd_uid) (dot in the name just > > to make it impossible to be used by users) (or marked with some special > > hidden attribute or something) > > I see > > /* This file specifies a list of internal "functions". These functions > differ from built-in functions in that they have no linkage and cannot > be called directly by the user. They represent operations that are only > synthesised by GCC itself. > > and think that may be more applicable than adding dots to regular builtins. I can certainly try to use internal function instead of builtin with dot in name, will report later if it is possible and how much changes would it need. Jakub
On 06/12/2013 10:30 AM, Jakub Jelinek wrote: > So the built-ins would take address of this decl, something else? Perhaps address, perhaps just referenced uninitialized? > Then there is the _simduid_ clause (also can hold address of the decl), and > after lowering it lives only in loop structure (so perhaps > remove_unused_locals would need to mark the decls referenced from loop > structure as used?). But that simd_uid clause refers to the same decl as the builtins, so the builtins should keep the decl around, at least until they themselves are transformed. At which point the decl is no longer needed, no? Indeed, I am really hoping that the decl vanishes completely before rtl. r~
On Wed, Jun 12, 2013 at 10:38:00AM -0700, Richard Henderson wrote: > On 06/12/2013 10:30 AM, Jakub Jelinek wrote: > > So the built-ins would take address of this decl, something else? > > Perhaps address, perhaps just referenced uninitialized? True, assuming no pass would actually want to change that SSA_NAME of the magic decl just because it is undefined (coalesce with some other undefined SSA_NAME or something similar). I hope nothing does that, it would be problematic for the uninitialized warning pass too I bet. > But that simd_uid clause refers to the same decl as the builtins, so the > builtins should keep the decl around, at least until they themselves are > transformed. At which point the decl is no longer needed, no? > > Indeed, I am really hoping that the decl vanishes completely before rtl. Sure, it certainly should go away at the end of vectorization (and, when we know vectorization won't happen we just should assume safelen will be 1). Jakub
On 06/12/13 16:36, Jakub Jelinek wrote: > On Wed, Jun 12, 2013 at 10:38:00AM -0700, Richard Henderson wrote: >> On 06/12/2013 10:30 AM, Jakub Jelinek wrote: >>> So the built-ins would take address of this decl, something else? >> >> Perhaps address, perhaps just referenced uninitialized? > > True, assuming no pass would actually want to change that SSA_NAME of the > magic decl just because it is undefined (coalesce with some other undefined > SSA_NAME or something similar). I hope nothing does that, it would be > problematic for the uninitialized warning pass too I bet. Boo hiss! I've seen uninitialized variables cause all sorts of grief when cleaning up SSA.
diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 54ea04f..e0d6092 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1137,6 +1137,7 @@ C_COMMON_OBJS = c-family/c-common.o c-family/c-cppbuiltin.o c-family/c-dump.o \ c-family/c-format.o c-family/c-gimplify.o c-family/c-lex.o \ c-family/c-omp.o c-family/c-opts.o c-family/c-pch.o \ c-family/c-ppoutput.o c-family/c-pragma.o c-family/c-pretty-print.o \ + c-family/c-cilkplus.o \ c-family/c-semantics.o c-family/c-ada-spec.o tree-mudflap.o # Language-independent object files. @@ -1966,6 +1967,9 @@ c-family/c-lex.o : c-family/c-lex.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ c-family/c-omp.o : c-family/c-omp.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ $(TREE_H) $(C_COMMON_H) $(GIMPLE_H) langhooks.h +c-family/c-cilkplus.o : c-family/c-cilkplus.c $(CONFIG_H) $(SYSTEM_H) \ + coretypes.h $(TREE_H) $(C_COMMON_H) langhooks.h + CFLAGS-c-family/c-opts.o += @TARGET_SYSTEM_ROOT_DEFINE@ c-family/c-opts.o : c-family/c-opts.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ $(TREE_H) $(C_PRAGMA_H) $(FLAGS_H) toplev.h langhooks.h \ diff --git a/gcc/c-family/c-cilkplus.c b/gcc/c-family/c-cilkplus.c new file mode 100644 index 0000000..d5f069f --- /dev/null +++ b/gcc/c-family/c-cilkplus.c @@ -0,0 +1,278 @@ +/* This file contains routines to construct and validate Cilk Plus + constructs within the C and C++ front ends. + + Copyright (C) 2011-2013 Free Software Foundation, Inc. + Contributed by Balaji V. Iyer <balaji.v.iyer@intel.com>, + Aldy Hernandez <aldyh@redhat.com>. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tree.h" +#include "c-common.h" + +/* Helper function for c_check_cilk_loop. + + Validate the increment in a _Cilk_for construct or a <#pragma simd> + for loop. + + LOC is the location of the `for' keyword. DECL is the induction + variable. INCR is the original increment expression. + + Returns the canonicalized increment expression for an OMP_FOR_INCR. + If there is a validation error, returns error_mark_node. */ + +static tree +c_check_cilk_loop_incr (location_t loc, tree decl, tree incr) +{ + if (EXPR_HAS_LOCATION (incr)) + loc = EXPR_LOCATION (incr); + + if (!incr) + { + error_at (loc, "missing increment"); + return error_mark_node; + } + + switch (TREE_CODE (incr)) + { + case POSTINCREMENT_EXPR: + case PREINCREMENT_EXPR: + case POSTDECREMENT_EXPR: + case PREDECREMENT_EXPR: + if (TREE_OPERAND (incr, 0) != decl) + break; + + // Bah... canonicalize into whatever OMP_FOR_INCR needs. + if (POINTER_TYPE_P (TREE_TYPE (decl)) + && TREE_OPERAND (incr, 1)) + { + tree t = fold_convert_loc (loc, + sizetype, TREE_OPERAND (incr, 1)); + + if (TREE_CODE (incr) == POSTDECREMENT_EXPR + || TREE_CODE (incr) == PREDECREMENT_EXPR) + t = fold_build1_loc (loc, NEGATE_EXPR, sizetype, t); + t = fold_build_pointer_plus (decl, t); + incr = build2 (MODIFY_EXPR, void_type_node, decl, t); + } + return incr; + + case MODIFY_EXPR: + { + tree rhs; + + if (TREE_OPERAND (incr, 0) != decl) + break; + + rhs = TREE_OPERAND (incr, 1); + if (TREE_CODE (rhs) == PLUS_EXPR + && (TREE_OPERAND (rhs, 0) == decl + || TREE_OPERAND (rhs, 1) == decl) + && INTEGRAL_TYPE_P (TREE_TYPE (rhs))) + return incr; + else if (TREE_CODE (rhs) == MINUS_EXPR + && TREE_OPERAND (rhs, 0) == decl + && INTEGRAL_TYPE_P (TREE_TYPE (rhs))) + return incr; + // Otherwise fail because only PLUS_EXPR and MINUS_EXPR are + // allowed. + break; + } + + default: + break; + } + + error_at (loc, "invalid increment expression"); + return error_mark_node; +} + +/* Validate a _Cilk_for construct (or a #pragma simd for loop, which + has the same syntactic restrictions). Returns TRUE if there were + no errors, FALSE otherwise. LOC is the location of the for. DECL + is the controlling variable. COND is the condition. INCR is the + increment expression. BODY is the body of the LOOP. */ + +static bool +c_check_cilk_loop (location_t loc, tree decl, tree cond, tree incr, tree body) +{ + if (decl == error_mark_node + || cond == error_mark_node + || incr == error_mark_node + || body == error_mark_node) + return false; + + /* Validate the initialization. */ + gcc_assert (decl != NULL); + if (TREE_THIS_VOLATILE (decl)) + { + error_at (loc, "induction variable cannot be volatile"); + return false; + } + if (DECL_EXTERNAL (decl)) + { + error_at (loc, "induction variable cannot be extern"); + return false; + } + if (TREE_STATIC (decl)) + { + error_at (loc, "induction variable cannot be static"); + return false; + } + if (DECL_REGISTER (decl)) + { + error_at (loc, "induction variable cannot be declared register"); + return false; + } + if (!INTEGRAL_TYPE_P (TREE_TYPE (decl)) + && !POINTER_TYPE_P (TREE_TYPE (decl))) + { + error_at (loc, "initialization variable must be of integral " + "or pointer type"); + return false; + } + + /* Validate the condition. */ + if (!cond) + { + error_at (loc, "missing condition"); + return false; + } + bool cond_ok = false; + if (TREE_CODE (cond) == NE_EXPR + || TREE_CODE (cond) == LT_EXPR + || TREE_CODE (cond) == LE_EXPR + || TREE_CODE (cond) == GT_EXPR + || TREE_CODE (cond) == GE_EXPR) + { + /* Comparison must either be: + DECL <comparison_operator> EXPR + EXPR <comparison_operator> DECL + */ + if (decl == TREE_OPERAND (cond, 0)) + cond_ok = true; + else if (decl == TREE_OPERAND (cond, 1)) + { + /* Canonicalize the comparison so the DECL is on the LHS. */ + TREE_SET_CODE (cond, + swap_tree_comparison (TREE_CODE (cond))); + TREE_OPERAND (cond, 1) = TREE_OPERAND (cond, 0); + TREE_OPERAND (cond, 0) = decl; + cond_ok = true; + } + } + if (!cond_ok) + { + error_at (loc, "invalid controlling predicate"); + return false; + } + + /* Validate the increment. */ + incr = c_check_cilk_loop_incr (loc, decl, incr); + if (incr == error_mark_node) + return false; + + return true; + } + +/* Validate and emit code for the FOR loop following a #<pragma simd> + construct. + + LOC is the location of the location of the FOR. + DECL is the iteration variable. + INIT is the initialization expression. + COND is the controlling predicate. + INCR is the increment expression. + BODY is the body of the loop. + CLAUSES are the clauses associated with the pragma simd loop. + + Returns the generated statement. */ + +tree +c_finish_cilk_simd_loop (location_t loc, + tree decl, + tree init, tree cond, tree incr, + tree body, + tree clauses) +{ + location_t rhs_loc; + + if (!c_check_cilk_loop (loc, decl, cond, incr, body)) + return NULL; + + /* In the case of "for (int i = 0...)", init will be a decl. It should + have a DECL_INITIAL that we can turn into an assignment. */ + if (init == decl) + { + rhs_loc = DECL_SOURCE_LOCATION (decl); + + init = DECL_INITIAL (decl); + if (init == NULL) + { + error_at (rhs_loc, "%qE is not initialized", decl); + init = integer_zero_node; + return NULL; + } + + init = build_modify_expr (loc, decl, NULL_TREE, NOP_EXPR, rhs_loc, + init, NULL_TREE); + } + gcc_assert (TREE_CODE (init) == MODIFY_EXPR); + gcc_assert (TREE_OPERAND (init, 0) == decl); + + tree initv = make_tree_vec (1); + tree condv = make_tree_vec (1); + tree incrv = make_tree_vec (1); + TREE_VEC_ELT (initv, 0) = init; + TREE_VEC_ELT (condv, 0) = cond; + TREE_VEC_ELT (incrv, 0) = incr; + + // FIXME: What should we do about nested loops? Look at specs. + + /* The OpenMP <#pragma omp simd> construct is exactly the same as + the Cilk Plus one, with the exception of the vectorlength() + clause in Cilk Plus. Emitting an OMP_SIMD simlifies + everything. */ + tree t = make_node (OMP_SIMD); + TREE_TYPE (t) = void_type_node; + OMP_FOR_INIT (t) = initv; + OMP_FOR_COND (t) = condv; + OMP_FOR_INCR (t) = incrv; + OMP_FOR_BODY (t) = body; + OMP_FOR_PRE_BODY (t) = NULL; + OMP_FOR_CLAUSES (t) = clauses; + + SET_EXPR_LOCATION (t, loc); + return add_stmt (t); +} + +/* Validate and emit code for <#pragma simd> clauses. */ + +tree +c_finish_cilk_clauses (tree clauses) +{ + // FIXME: "...no variable shall be the subject of more than one + // linear clause". Verify and check for this. + + // FIXME: Also, do whatever we were doing before in + // same_var_in_multiple_lists_p, but rewrite to use OMP_CLAUSEs. + + return clauses; +} diff --git a/gcc/c-family/c-pragma.c b/gcc/c-family/c-pragma.c index b03ddc1..c05b8d0 100644 --- a/gcc/c-family/c-pragma.c +++ b/gcc/c-family/c-pragma.c @@ -1352,6 +1352,12 @@ init_pragma (void) omp_pragmas[i].id, true, true); } + if (flag_enable_cilk && !flag_preprocess_only) + { + cpp_register_deferred_pragma (parse_in, NULL, "simd", + PRAGMA_CILK_SIMD, true, false); + } + if (!flag_preprocess_only) cpp_register_deferred_pragma (parse_in, "GCC", "pch_preprocess", PRAGMA_GCC_PCH_PREPROCESS, false, false); diff --git a/gcc/c-family/c-pragma.h b/gcc/c-family/c-pragma.h index cd121d4..2ff5a00 100644 --- a/gcc/c-family/c-pragma.h +++ b/gcc/c-family/c-pragma.h @@ -60,6 +60,9 @@ typedef enum pragma_kind { PRAGMA_OMP_THREADPRIVATE, PRAGMA_OMP_TEAMS, + /* Top level clause to handle all Cilk Plus pragma simd clauses. */ + PRAGMA_CILK_SIMD, + PRAGMA_GCC_PCH_PREPROCESS, PRAGMA_FIRST_EXTERNAL @@ -109,6 +112,19 @@ typedef enum pragma_omp_clause { PRAGMA_OMP_CLAUSE_UNTIED } pragma_omp_clause; +/* All Cilk Plus #pragma omp clauses. */ +typedef enum pragma_cilk_clause { + PRAGMA_CILK_CLAUSE_NONE = 0, + PRAGMA_CILK_CLAUSE_NOASSERT, + PRAGMA_CILK_CLAUSE_ASSERT, + PRAGMA_CILK_CLAUSE_VECTORLENGTH, + PRAGMA_CILK_CLAUSE_LINEAR, + PRAGMA_CILK_CLAUSE_PRIVATE, + PRAGMA_CILK_CLAUSE_FIRSTPRIVATE, + PRAGMA_CILK_CLAUSE_LASTPRIVATE, + PRAGMA_CILK_CLAUSE_REDUCTION +} pragma_cilk_clause; + extern struct cpp_reader* parse_in; /* It's safe to always leave visibility pragma enabled as if diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index 10ae84d..e502b3f 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -839,6 +839,10 @@ Recognize built-in functions fbuiltin- C ObjC C++ ObjC++ Joined +fcilkplus +C ObjC C++ ObjC++ LTO Report Var(flag_enable_cilk) Init(0) +Enable Cilk + fcanonical-system-headers C ObjC C++ ObjC++ Where shorter, use canonicalized paths to systems headers. diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c index 5b06803..41d44a4 100644 --- a/gcc/c/c-parser.c +++ b/gcc/c/c-parser.c @@ -1217,6 +1217,11 @@ static void c_parser_objc_at_dynamic_declaration (c_parser *); static bool c_parser_objc_diagnose_bad_element_prefix (c_parser *, struct c_declspecs *); +/* Cilk Plus supporting routines. */ +static void c_parser_cilk_for_statement (c_parser *, enum rid, tree); +static void c_parser_cilk_simd_construct (c_parser *); +static bool c_parser_cilk_verify_simd (c_parser *, enum pragma_context); + /* Parse a translation unit (C90 6.7, C99 6.9). translation-unit: @@ -8622,6 +8627,13 @@ c_parser_pragma (c_parser *parser, enum pragma_context context) c_parser_skip_until_found (parser, CPP_PRAGMA_EOL, NULL); return false; + case PRAGMA_CILK_SIMD: + if (!c_parser_cilk_verify_simd (parser, context)) + return false; + c_parser_consume_pragma (parser); + c_parser_cilk_simd_construct (parser); + return false; + default: if (id < PRAGMA_FIRST_EXTERNAL) { @@ -10664,7 +10676,457 @@ c_parser_omp_threadprivate (c_parser *parser) c_parser_skip_to_pragma_eol (parser); } + +/* Cilk Plus <#pragma simd> parsing routines. */ + +/* Helper function for c_parser_pragma. Perform some sanity checking + for <#pragma simd> constructs. Returns FALSE if there was a + problem. */ + +static bool +c_parser_cilk_verify_simd (c_parser *parser, + enum pragma_context context) +{ + if (!flag_enable_cilk) + { + warning (0, "pragma simd ignored because -fcilkplus is not enabled"); + c_parser_skip_until_found (parser, CPP_PRAGMA_EOL, NULL); + return false; + } + if (!flag_tree_vectorize) + { + warning (0, "pragma simd is useless without -ftree-vectorize"); + c_parser_skip_until_found (parser, CPP_PRAGMA_EOL, NULL); + return false; + } + if (context == pragma_external) + { + c_parser_error (parser,"pragma simd must be inside a function"); + c_parser_skip_until_found (parser, CPP_PRAGMA_EOL, NULL); + return false; + } + return true; +} + +/* Cilk Plus: + assert */ + +static tree +c_parser_cilk_clause_assert (c_parser *parser, tree clauses) +{ + check_no_duplicate_clause (clauses, OMP_CLAUSE_CILK_ASSERT, "assert"); + + location_t loc = c_parser_peek_token (parser)->location; + tree c = build_omp_clause (loc, OMP_CLAUSE_CILK_ASSERT); + OMP_CLAUSE_CHAIN (c) = clauses; + return c; +} + +/* Cilk Plus: + noassert */ + +static tree +c_parser_cilk_clause_noassert (c_parser *parser ATTRIBUTE_UNUSED, + tree clauses) +{ + /* Only check that we don't already have an assert clause. */ + check_no_duplicate_clause (clauses, OMP_CLAUSE_CILK_ASSERT, "assert"); + + return clauses; +} + +/* Cilk Plus: + vectorlength (constant-expression-list ) + + constant-expression-list: + constant-expression + constant-expression-list , constant-expression */ + +static tree +c_parser_cilk_clause_vectorlength (c_parser *parser, tree clauses) +{ + check_no_duplicate_clause (clauses, OMP_CLAUSE_CILK_VECTORLENGTH, + "vectorlength"); + + if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) + return clauses; + + location_t loc = c_parser_peek_token (parser)->location; + while (true) + { + tree expr = c_parser_expr_no_commas (parser, NULL).value; + expr = c_fully_fold (expr, false, NULL); + + if (!TREE_TYPE (expr) + || !TREE_CONSTANT (expr) + || !INTEGRAL_TYPE_P (TREE_TYPE (expr))) + error_at (loc, "vectorlength must be an integer constant"); + else + { + tree u = build_omp_clause (loc, OMP_CLAUSE_CILK_VECTORLENGTH); + OMP_CLAUSE_CILK_VECTORLENGTH_EXPR (u) = expr; + OMP_CLAUSE_CHAIN (u) = clauses; + clauses = u; + } + + if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN)) + { + c_parser_consume_token (parser); + return clauses; + } + if (c_parser_next_token_is (parser, CPP_COMMA)) + c_parser_consume_token (parser); + } + + return clauses; +} + +/* Cilk Plus: + linear ( simd-linear-variable-list ) + + simd-linear-variable-list: + simd-linear-variable + simd-linear-variable-list , simd-linear-variable + + simd-linear-variable: + id-expression + id-expression : simd-linear-step + + simd-linear-step: + conditional-expression */ + +static tree +c_parser_cilk_clause_linear (c_parser *parser, tree clauses) +{ + if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) + return clauses; + + location_t loc = c_parser_peek_token (parser)->location; + if (c_parser_next_token_is_not (parser, CPP_NAME) + || c_parser_peek_token (parser)->id_kind != C_ID_ID) + c_parser_error (parser, "expected identifier"); + + while (c_parser_next_token_is (parser, CPP_NAME) + && c_parser_peek_token (parser)->id_kind == C_ID_ID) + { + tree var = lookup_name (c_parser_peek_token (parser)->value); + + if (var == NULL) + { + undeclared_variable (c_parser_peek_token (parser)->location, + c_parser_peek_token (parser)->value); + c_parser_consume_token (parser); + } + else if (var == error_mark_node) + c_parser_consume_token (parser); + else + { + tree step = integer_one_node; + + /* Parse the linear step if present. */ + if (c_parser_peek_2nd_token (parser)->type == CPP_COLON) + { + c_parser_consume_token (parser); + c_parser_consume_token (parser); + + tree expr = c_parser_expr_no_commas (parser, NULL).value; + expr = c_fully_fold (expr, false, NULL); + + if (!TREE_TYPE (expr) + || !TREE_CONSTANT (expr) + || !INTEGRAL_TYPE_P (TREE_TYPE (expr))) + c_parser_error (parser, + "step size must be an integer constant"); + else + step = expr; + } + else + c_parser_consume_token (parser); + + /* Use OMP_CLAUSE_LINEAR, which has the same semantics. */ + tree u = build_omp_clause (loc, OMP_CLAUSE_LINEAR); + OMP_CLAUSE_DECL (u) = var; + OMP_CLAUSE_LINEAR_STEP (u) = step; + OMP_CLAUSE_CHAIN (u) = clauses; + clauses = u; + } + + if (c_parser_next_token_is_not (parser, CPP_COMMA)) + break; + + c_parser_consume_token (parser); + } + + c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>"); + + return clauses; +} + +/* Returns the name of the next clause. If the clause is not + recognized SIMD_OMP_CLAUSE_NONE is returned and the next token is + not consumed. Otherwise, the appropriate pragma_simd_clause is + returned and the token is consumed. */ + +static pragma_cilk_clause +c_parser_cilk_clause_name (c_parser *parser) +{ + pragma_cilk_clause result; + c_token *token = c_parser_peek_token (parser); + + if (!token->value || token->type != CPP_NAME) + return PRAGMA_CILK_CLAUSE_NONE; + + const char *p = IDENTIFIER_POINTER (token->value); + + if (!strcmp (p, "noassert")) + result = PRAGMA_CILK_CLAUSE_NOASSERT; + else if (!strcmp (p, "assert")) + result = PRAGMA_CILK_CLAUSE_ASSERT; + else if (!strcmp (p, "vectorlength")) + result = PRAGMA_CILK_CLAUSE_VECTORLENGTH; + else if (!strcmp (p, "linear")) + result = PRAGMA_CILK_CLAUSE_LINEAR; + else if (!strcmp (p, "private")) + result = PRAGMA_CILK_CLAUSE_PRIVATE; + else if (!strcmp (p, "firstprivate")) + result = PRAGMA_CILK_CLAUSE_FIRSTPRIVATE; + else if (!strcmp (p, "lastprivate")) + result = PRAGMA_CILK_CLAUSE_LASTPRIVATE; + else if (!strcmp (p, "reduction")) + result = PRAGMA_CILK_CLAUSE_REDUCTION; + else + return PRAGMA_CILK_CLAUSE_NONE; + + c_parser_consume_token (parser); + return result; +} + +/* Parse all #<pragma simd> clauses. Return the list of clauses + found. */ + +static tree +c_parser_cilk_all_clauses (c_parser *parser) +{ + tree clauses = NULL; + + while (c_parser_next_token_is_not (parser, CPP_PRAGMA_EOL)) + { + pragma_cilk_clause c_kind; + + c_kind = c_parser_cilk_clause_name (parser); + + switch (c_kind) + { + case PRAGMA_CILK_CLAUSE_NOASSERT: + clauses = c_parser_cilk_clause_noassert (parser, clauses); + break; + case PRAGMA_CILK_CLAUSE_ASSERT: + clauses = c_parser_cilk_clause_assert (parser, clauses); + break; + case PRAGMA_CILK_CLAUSE_VECTORLENGTH: + clauses = c_parser_cilk_clause_vectorlength (parser, clauses); + break; + case PRAGMA_CILK_CLAUSE_LINEAR: + clauses = c_parser_cilk_clause_linear (parser, clauses); + break; + case PRAGMA_CILK_CLAUSE_PRIVATE: + /* Use the OpenMP counterpart. */ + clauses = c_parser_omp_clause_private (parser, clauses); + break; + case PRAGMA_CILK_CLAUSE_FIRSTPRIVATE: + /* Use the OpenMP counterpart. */ + clauses = c_parser_omp_clause_firstprivate (parser, clauses); + break; + case PRAGMA_CILK_CLAUSE_LASTPRIVATE: + /* Use the OpenMP counterpart. */ + clauses = c_parser_omp_clause_lastprivate (parser, clauses); + break; + case PRAGMA_CILK_CLAUSE_REDUCTION: + /* Use the OpenMP counterpart. */ + clauses = c_parser_omp_clause_reduction (parser, clauses); + break; + default: + c_parser_error (parser, "expected %<#pragma simd%> clause"); + goto saw_error; + } + } + + saw_error: + c_parser_skip_to_pragma_eol (parser); + return c_finish_cilk_clauses (clauses); +} + +/* Parse the restriction form of the for statement allowed by + Cilk Plus. This function parses both the _CILK_FOR construct as + well as the for loop following a <#pragma simd> construct, both of + which have the same syntactic restrictions. + + FOR_KEYWORD can be either RID_CILK_FOR or RID_FOR, for parsing + _cilk_for or the <#pragma simd> for loop construct respectively. + + (NOTE: For now, only RID_FOR is handled). + + For a <#pragma simd>, CLAUSES are the clauses that should have been + previously parsed. If there are none, or if we are parsing a + _Cilk_for instead, this will be NULL. */ + +static void +c_parser_cilk_for_statement (c_parser *parser, enum rid for_keyword, + tree clauses) +{ + tree init, decl, cond, stmt; + tree block, incr, save_break, save_cont, body; + location_t loc; + bool fail = false; + + gcc_assert (/*for_keyword == RID_CILK_FOR || */for_keyword == RID_FOR); + + if (!c_parser_next_token_is_keyword (parser, for_keyword)) + { + if (for_keyword == RID_FOR) + c_parser_error (parser, "for statement expected"); + else + c_parser_error (parser, "_Cilk_for statement expected"); + return; + } + + loc = c_parser_peek_token (parser)->location; + c_parser_consume_token (parser); + + block = c_begin_compound_stmt (true); + + if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) + { + add_stmt (c_end_compound_stmt (loc, block, true)); + return; + } + + /* Parse the initialization declaration. */ + if (c_parser_next_tokens_start_declaration (parser)) + { + c_parser_declaration_or_fndef (parser, true, false, false, + false, false, NULL); + decl = check_for_loop_decls (loc, flag_isoc99); + if (decl == NULL) + goto error_init; + if (DECL_INITIAL (decl) == error_mark_node) + decl = error_mark_node; + init = decl; + } + else if (c_parser_next_token_is (parser, CPP_NAME) + && c_parser_peek_2nd_token (parser)->type == CPP_EQ) + { + struct c_expr decl_exp; + struct c_expr init_exp; + location_t init_loc; + + decl_exp = c_parser_postfix_expression (parser); + decl = decl_exp.value; + + c_parser_require (parser, CPP_EQ, "expected %<=%>"); + + init_loc = c_parser_peek_token (parser)->location; + init_exp = c_parser_expr_no_commas (parser, NULL); + init_exp = default_function_array_read_conversion (init_loc, + init_exp); + init = build_modify_expr (init_loc, decl, decl_exp.original_type, + NOP_EXPR, init_loc, init_exp.value, + init_exp.original_type); + init = c_process_expr_stmt (init_loc, init); + + c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>"); + } + else + { + error_init: + c_parser_error (parser, + "expected iteration declaration or initialization"); + c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, + "expected %<)%>"); + return; + } + + /* Parse the loop condition. */ + cond = NULL_TREE; + if (c_parser_next_token_is_not (parser, CPP_SEMICOLON)) + { + location_t cond_loc = c_parser_peek_token (parser)->location; + struct c_expr cond_expr = c_parser_binary_expression (parser, NULL, + NULL); + + cond = cond_expr.value; + cond = c_objc_common_truthvalue_conversion (cond_loc, cond); + cond = c_fully_fold (cond, false, NULL); + } + c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>"); + + /* Parse the increment expression. */ + incr = NULL_TREE; + if (c_parser_next_token_is_not (parser, CPP_CLOSE_PAREN)) + { + location_t incr_loc = c_parser_peek_token (parser)->location; + incr = c_process_expr_stmt (incr_loc, + c_parser_expression (parser).value); + } + c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>"); + + if (decl == NULL || decl == error_mark_node || init == error_mark_node) + fail = true; + + save_break = c_break_label; + /* Magic number to inform c_finish_bc_stmt() that we are within a + Cilk for construct. */ + c_break_label = build_int_cst (size_type_node, 2); + + save_cont = c_cont_label; + c_cont_label = NULL_TREE; + body = c_parser_c99_block_statement (parser); + c_break_label = save_break; + c_cont_label = save_cont; + + // FIXME: Disallow the following constructs within a SIMD loop: + // + // RETURN + // GOTO + // _Cilk_spawn + // _Cilk_for + // OpenMP directive or construct + // Calls to setjmp() + + if (!fail) + { + /* + // FIXME: Uncomment when RID_CILK_FOR is implemented. + if (for_keyword == RID_CILK_FOR) + c_finish_cilk_loop (loc, decl, cond, incr, body, grain); + else + */ + c_finish_cilk_simd_loop (loc, decl, init, cond, incr, body, clauses); + } + + stmt = c_end_compound_stmt (loc, block, true); + add_stmt (stmt); + c_break_label = save_break; + c_cont_label = save_cont; +} + +/* Main entry point for parsing Cilk Plus <#pragma simd> for + loops. */ + +static void +c_parser_cilk_simd_construct (c_parser *parser) +{ + tree clauses = c_parser_cilk_all_clauses (parser); + + /* For <#pragma simd> we will be generating OMP_SIMD's and let the + OpenMP mechanism handle everything. */ + if (!flag_openmp) + flag_openmp = true; + + c_parser_cilk_for_statement (parser, RID_FOR, clauses); +} + /* Parse a transaction attribute (GCC Extension). transaction-attribute: diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h index c4210a5..0fd9cc6 100644 --- a/gcc/c/c-tree.h +++ b/gcc/c/c-tree.h @@ -642,6 +642,11 @@ extern tree c_build_va_arg (location_t, tree, tree); extern tree c_finish_transaction (location_t, tree, int); extern bool c_tree_equal (tree, tree); +/* In c-cilkplus.c */ +extern tree c_finish_cilk_simd_loop (location_t, tree, tree, tree, tree, + tree, tree); +extern tree c_finish_cilk_clauses (tree); + /* Set to 0 at beginning of a function definition, set to 1 if a return statement that specifies a return value is seen. */ diff --git a/gcc/gimplify.c b/gcc/gimplify.c index f6b6717..013fe43 100644 --- a/gcc/gimplify.c +++ b/gcc/gimplify.c @@ -6337,6 +6337,8 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p, case OMP_CLAUSE_MERGEABLE: case OMP_CLAUSE_PROC_BIND: case OMP_CLAUSE_SAFELEN: + case OMP_CLAUSE_CILK_ASSERT: + case OMP_CLAUSE_CILK_VECTORLENGTH: break; case OMP_CLAUSE_ALIGNED: @@ -6528,6 +6530,8 @@ gimplify_adjust_omp_clauses (tree *list_p) case OMP_CLAUSE_MERGEABLE: case OMP_CLAUSE_PROC_BIND: case OMP_CLAUSE_SAFELEN: + case OMP_CLAUSE_CILK_ASSERT: + case OMP_CLAUSE_CILK_VECTORLENGTH: break; default: diff --git a/gcc/omp-low.c b/gcc/omp-low.c index 58117d9..b38ed9b 100644 --- a/gcc/omp-low.c +++ b/gcc/omp-low.c @@ -1491,6 +1491,8 @@ scan_sharing_clauses (tree clauses, omp_context *ctx) case OMP_CLAUSE_PROC_BIND: case OMP_CLAUSE_SAFELEN: case OMP_CLAUSE_ALIGNED: + case OMP_CLAUSE_CILK_ASSERT: + case OMP_CLAUSE_CILK_VECTORLENGTH: break; default: @@ -1547,6 +1549,8 @@ scan_sharing_clauses (tree clauses, omp_context *ctx) case OMP_CLAUSE_PROC_BIND: case OMP_CLAUSE_SAFELEN: case OMP_CLAUSE_ALIGNED: + case OMP_CLAUSE_CILK_ASSERT: + case OMP_CLAUSE_CILK_VECTORLENGTH: break; default: diff --git a/gcc/testsuite/gcc.dg/cilk-plus/cilk-simd/compile/cilk-simd-compile.exp b/gcc/testsuite/gcc.dg/cilk-plus/cilk-simd/compile/cilk-simd-compile.exp new file mode 100644 index 0000000..154bb8c --- /dev/null +++ b/gcc/testsuite/gcc.dg/cilk-plus/cilk-simd/compile/cilk-simd-compile.exp @@ -0,0 +1,23 @@ +# Copyright (C) 2013 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GCC; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. + +load_lib gcc-dg.exp + +set OPTS "-fcilkplus -c -ftree-vectorize" + +dg-init +dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.c]] " $OPTS" " " +dg-finish diff --git a/gcc/testsuite/gcc.dg/cilk-plus/cilk-simd/compile/clauses1.c b/gcc/testsuite/gcc.dg/cilk-plus/cilk-simd/compile/clauses1.c new file mode 100644 index 0000000..ae01db3 --- /dev/null +++ b/gcc/testsuite/gcc.dg/cilk-plus/cilk-simd/compile/clauses1.c @@ -0,0 +1,84 @@ +/* { dg-do compile } */ +/* { dg-options "-O3 -std=c99 -fcilkplus" } */ + +volatile int *a, *b; + +void foo() +{ + int i, j, k; + +#pragma simd assert aoeu /* { dg-error "expected '#pragma simd' clause" } */ + for (int i=0; i < 1000; ++i) + a[i] = b[j]; + +#pragma simd noassert aoeu /* { dg-error "expected '#pragma simd' clause" } */ + for (int i=0; i < 1000; ++i) + a[i] = b[j]; + +#pragma simd assert noassert /* { dg-error "too many 'assert' clauses" } */ + for (int i=0; i < 1000; ++i) + a[i] = b[j]; + +#pragma simd vectorlength /* { dg-error "expected '\\('" } */ + for (int i=0; i < 1000; ++i) + a[i] = b[j]; + +#pragma simd vectorlength /* { dg-error "expected '\\('" } */ + for (int i=0; i < 1000; ++i) + a[i] = b[j]; + +#pragma simd vectorlength(sizeof (a) == sizeof (float) ? 4 : 8) + for (int i=0; i < 1000; ++i) + a[i] = b[j]; + +#pragma simd vectorlength(4,8) + for (int i=0; i < 1000; ++i) + a[i] = b[j]; + +#pragma simd vectorlength(i) /* { dg-error "vectorlength must be an integer" } */ + for (int i=0; i < 1000; ++i) + a[i] = b[j]; + +#pragma simd linear(35) /* { dg-error "expected identifier" } */ + for (int i=0; i < 1000; ++i) + a[i] = b[j]; + +#pragma simd linear(blah) /* { dg-error "'blah' undeclared" } */ + for (int i=0; i < 1000; ++i) + a[i] = b[j]; + +#pragma simd linear(blah2, 36) + /* { dg-error "'blah2' undeclared" "undeclared" { target *-*-* } 50 } */ + /* { dg-error "expected '\\)'" "expected" { target *-*-* } 50 } */ + for (int i=0; i < 1000; ++i) + a[i] = b[j]; + +#pragma simd linear(j, 36, k) /* { dg-error "expected '\\)'" } */ + for (int i=0; i < 1000; ++i) + a[i] = b[j]; + +#pragma simd linear(i, j) + for (int i=0; i < 1000; ++i) + a[i] = b[j]; + +#pragma simd linear(i) + for (int i=0; i < 1000; ++i) + a[i] = b[j]; + +#pragma simd linear(i : 4) + for (int i=0; i < 1000; ++i) + a[i] = b[j]; + +#pragma simd linear(i : 2, j : 4, k) + for (int i=0; i < 1000; ++i) + a[i] = b[j]; + +#pragma simd linear(j : sizeof (a) == sizeof (float) ? 4 : 8) + for (int i=0; i < 1000; ++i) + a[i] = b[j]; + + // And now everyone in unison! +#pragma simd assert linear(j : 4) vectorlength(4) + for (i=0; i < 1000; ++i) + a[i] = b[j]; +} diff --git a/gcc/testsuite/gcc.dg/cilk-plus/cilk-simd/compile/clauses2.c b/gcc/testsuite/gcc.dg/cilk-plus/cilk-simd/compile/clauses2.c new file mode 100644 index 0000000..6be6085 --- /dev/null +++ b/gcc/testsuite/gcc.dg/cilk-plus/cilk-simd/compile/clauses2.c @@ -0,0 +1,19 @@ +/* { dg-do compile } */ +/* { dg-options "-O3 -std=c99 -fcilkplus -fdump-tree-original" } */ + +volatile int *a, *b; + +void foo() +{ + int i, j, k; + +#pragma simd assert linear(j : 4, k) vectorlength(4) + for (i=0; i < 1000; ++i) + a[i] = b[j]; +} + +/* { dg-final { scan-tree-dump-times "linear\\(j:4\\)" 1 "original" } } */ +/* { dg-final { scan-tree-dump-times "linear\\(k:1\\)" 1 "original" } } */ +/* { dg-final { scan-tree-dump-times "cilk_vectorlength\\(4\\)" 1 "original" } } */ +/* { dg-final { scan-tree-dump-times "cilk_assert" 1 "original" } } */ +/* { dg-final { cleanup-tree-dump "original" } } */ diff --git a/gcc/testsuite/gcc.dg/cilk-plus/cilk-simd/compile/for1.c b/gcc/testsuite/gcc.dg/cilk-plus/cilk-simd/compile/for1.c new file mode 100644 index 0000000..38700e4 --- /dev/null +++ b/gcc/testsuite/gcc.dg/cilk-plus/cilk-simd/compile/for1.c @@ -0,0 +1,135 @@ +/* { dg-do compile } */ +/* { dg-options "-O3 -std=c99 -fcilkplus" } */ + +int *a, *b, *c; + +void foo() +{ + int i, j; + + // The initialization shall declare or initialize a *SINGLE* variable. +#pragma simd + for (i=0, j=5; i < 1000; i++) // { dg-error "expected ';' before ','" } + a[i] = b[j]; + + // Declaration and initialization is allowed. +#pragma simd + for (int i=0; i < 1000; i++) + a[i] = b[j]; + + // Empty initialization is not allowed. +#pragma simd + for (; i < 5; ++i) // { dg-error "expected iteration decl" } + a[i] = i; + + // Empty condition is not allowed. +#pragma simd + for (i=0; ; ++i) /* { dg-error "missing condition" } */ + a[i] = i; + + // Empty increment is not allowed. +#pragma simd + for (i=0; i < 1234; ) /* { dg-error "missing increment" } */ + a[i] = i*2; + +#pragma simd + i = 5; /* { dg-error "for statement expected" } */ + + // Initialization variables must be either integral or pointer types. + struct S { + int i; + }; +#pragma simd + for (struct S ss = { 0 }; ss.i <= 1000; ++ss.i) /* { dg-error "initialization variable must be of integral or pointer type" } */ + a[ss.i] = b[ss.i]; + + #pragma simd + for (float f=0.0; f < 15.0; ++f) /* { dg-error "must be of integral" } */ + a[(int)f] = (int) f; + + // Pointers are OK. + #pragma simd + for (int *i=c; i < &c[100]; ++i) + *a = '5'; + + // Condition of '==' is not allowed. +#pragma simd + for (i=j; i == 5; ++i) /* { dg-error "invalid controlling predicate" } */ + a[i] = b[i]; + + // The LHS or RHS of the condition must be the initialization variable. +#pragma simd + for (i=0; i+j < 1234; ++i) /* { dg-error "invalid controlling predicate" } */ + a[i] = b[i]; + + // Likewise. +#pragma simd + for (i=0; 1234 < i + j; ++i) /* { dg-error "invalid controlling predicate" } */ + a[i] = b[i]; + + // Likewise, this is ok. +#pragma simd + for (i=0; 1234 + j < i; ++i) + a[i] = b[i]; + + // According to the CilkPlus forum, casts are not allowed, even if + // they are no-ops. +#pragma simd + for (i=0; (char)i < 1234; ++i) /* { dg-error "invalid controlling predicate" } */ + a[i] = b[i]; + + // ?? This condition gets folded into "i != 0" by + // c_parser_cilk_for_statement(). Does this count as a "!=", or is + // this disallowed? Assume it is allowed. +#pragma simd + for (i=100; i; --i) + a[i] = b[i]; + + // Increment must be on the induction variable. +#pragma simd + for (i=0; i < 100; j++) /* { dg-error "invalid increment expression" } */ + a[i] = b[i]; + + // Likewise. +#pragma simd + for (i=0; i < 100; j = i + 1) /* { dg-error "invalid increment expression" } */ + a[i] = b[i]; + + // Likewise. +#pragma simd + for (i=0; i < 100; i = j + 1) /* { dg-error "invalid increment expression" } */ + a[i] = b[i]; + +#pragma simd + for (i=0; i < 100; i = i + 5) + a[i] = b[i]; + + // Only PLUS and MINUS increments are allowed. +#pragma simd + for (i=0; i < 100; i *= 5) /* { dg-error "invalid increment expression" } */ + a[i] = b[i]; + +#pragma simd + for (i=0; i < 100; i -= j) + a[i] = b[i]; + +#pragma simd + for (i=0; i < 100; i = i + j) + a[i] = b[i]; + +#pragma simd + for (i=0; i < 100; i = j + i) + a[i] = b[i]; + +#pragma simd + for (i=0; i < 100; ++i, ++j) /* { dg-error "invalid increment expression" } */ + a[i] = b[i]; + +#pragma simd + for (int *point=0; point < b; ++point) + *point = 555; + +#pragma simd + for (int *point=0; point > b; --point) + *point = 555; +} diff --git a/gcc/testsuite/gcc.dg/cilk-plus/cilk-simd/compile/for2.c b/gcc/testsuite/gcc.dg/cilk-plus/cilk-simd/compile/for2.c new file mode 100644 index 0000000..2d09ae8 --- /dev/null +++ b/gcc/testsuite/gcc.dg/cilk-plus/cilk-simd/compile/for2.c @@ -0,0 +1,66 @@ +/* { dg-do compile } */ +/* { dg-options "-O3 -std=c99 -fcilkplus" } */ + +// Test storage classes in the initialization of a <#pragma simd> for +// loop. + +int *a, *b; + +void foo() +{ +#pragma simd + for (static int foo=5; foo < 10; ++foo) + a[foo] = b[foo]; + /* { dg-error "declaration of static variable" "storage class1" { target *-*-* } 12 } */ + /* { dg-error "induction variable cannot be static" "storage class2" { target *-*-* } 12 } */ + + static int bar; +#pragma simd + for (bar=0; bar < 1000; ++bar) /* { dg-error "induction variable cannot be static" } */ + a[bar] = bar; + +#pragma simd + for (extern int var=0; var < 1000; ++var) + a[var] = var; + /* { dg-error "has both 'extern' and initializer" "extern" { target *-*-* } 23 } */ + /* { dg-error "declaration of static variable" "" { target *-*-* } 23 } */ + /* { dg-error "induction variable cannot be static" "" { target *-*-* } 23 } */ + + extern int extvar; +#pragma simd + for (extvar = 0; extvar < 1000; ++extvar) /* { dg-error "induction variable cannot be extern" } */ + b[extvar] = a[extvar]; + + // This seems like it should be ok. + // Must check with standards people. +#pragma simd + for (auto int autoi = 0; autoi < 1000; ++autoi) + b[autoi] = a[autoi] * 2; + // Similarly here. + auto int autoj; +#pragma simd + for (auto int autoj = 0; autoj < 1000; ++autoj) + b[autoj] = a[autoj] * 2; + + register int regi; +#pragma simd + for (regi = 0; regi < 1000; ++regi) /* { dg-error "induction variable cannot be declared register" } */ + b[regi] = a[regi] * 2; + +#pragma simd + for (register int regj = 0; regj < 1000; ++regj) /* { dg-error "induction variable cannot be declared register" } */ + b[regj] = a[regj] * 2; + + volatile int vi; +#pragma simd + for (vi=0; vi<1000; ++vi) /* { dg-error "induction variable cannot be volatile" } */ + a[vi] = b[vi]; + +#pragma simd + for (volatile int vj=0; vj<1000; ++vj) /* { dg-error "induction variable cannot be volatile" } */ + a[vj] = b[vj]; + +#pragma simd + for (const int ci=0; ci<1000; ++ci) /* { dg-error "increment of read-only var" } */ + a[ci] = b[ci]; +} diff --git a/gcc/testsuite/gcc.dg/cilk-plus/cilk-simd/compile/for3.c b/gcc/testsuite/gcc.dg/cilk-plus/cilk-simd/compile/for3.c new file mode 100644 index 0000000..8660627 --- /dev/null +++ b/gcc/testsuite/gcc.dg/cilk-plus/cilk-simd/compile/for3.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-O3 -fcilkplus" } */ + +#pragma simd /* { dg-error "must be inside a function" } */ + +void foo() +{ +} diff --git a/gcc/tree-pretty-print.c b/gcc/tree-pretty-print.c index f3de68c..8d1f06e9 100644 --- a/gcc/tree-pretty-print.c +++ b/gcc/tree-pretty-print.c @@ -586,6 +586,17 @@ dump_omp_clause (pretty_printer *buffer, tree clause, int spc, int flags) pp_string (buffer, "taskgroup"); break; + case OMP_CLAUSE_CILK_ASSERT: + pp_string (buffer, "cilk_assert"); + break; + + case OMP_CLAUSE_CILK_VECTORLENGTH: + pp_string (buffer, "cilk_vectorlength("); + dump_generic_node (buffer, OMP_CLAUSE_CILK_VECTORLENGTH_EXPR (clause), + spc, flags, false); + pp_character (buffer, ')'); + break; + default: /* Should never happen. */ dump_generic_node (buffer, clause, spc, flags, false); diff --git a/gcc/tree.c b/gcc/tree.c index 36fadff..2bc6131 100644 --- a/gcc/tree.c +++ b/gcc/tree.c @@ -264,6 +264,8 @@ unsigned const char omp_clause_num_ops[] = 0, /* OMP_CLAUSE_PARALLEL */ 0, /* OMP_CLAUSE_SECTIONS */ 0 /* OMP_CLAUSE_TASKGROUP */ + , 0, /* OMP_CLAUSE_CILK_ASSERT */ + 1, /* OMP_CLAUSE_CILK_VECTORLENGTH */ }; const char * const omp_clause_code_name[] = @@ -305,6 +307,8 @@ const char * const omp_clause_code_name[] = "parallel", "sections", "taskgroup" + , "cilk_assert", + "cilk_vectorlength", }; @@ -10809,6 +10813,7 @@ walk_tree_1 (tree *tp, walk_tree_fn func, void *data, case OMP_CLAUSE_DIST_SCHEDULE: case OMP_CLAUSE_SAFELEN: case OMP_CLAUSE_SIMDLEN: + case OMP_CLAUSE_CILK_VECTORLENGTH: WALK_SUBTREE (OMP_CLAUSE_OPERAND (*tp, 0)); /* FALLTHRU */ @@ -10853,6 +10858,9 @@ walk_tree_1 (tree *tp, walk_tree_fn func, void *data, WALK_SUBTREE_TAIL (OMP_CLAUSE_CHAIN (*tp)); } + case OMP_CLAUSE_CILK_ASSERT: + break; + default: gcc_unreachable (); } diff --git a/gcc/tree.h b/gcc/tree.h index b4bd6c4..75fbebf 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -446,7 +446,13 @@ enum omp_clause_code OMP_CLAUSE_SECTIONS, /* OpenMP clause: taskgroup. */ - OMP_CLAUSE_TASKGROUP + OMP_CLAUSE_TASKGROUP, + + /* Cilk Plus clause: assert. */ + OMP_CLAUSE_CILK_ASSERT, + + /* Cilk Plus clause: vectorlength (constant-expression-list). */ + OMP_CLAUSE_CILK_VECTORLENGTH }; /* The definition of tree nodes fills the next several pages. */ @@ -1857,6 +1863,13 @@ extern void protected_set_expr_location (tree, location_t); OMP_CLAUSE_OPERAND (OMP_CLAUSE_RANGE_CHECK (OMP_CLAUSE_CHECK (NODE), \ OMP_CLAUSE_PRIVATE, \ OMP_CLAUSE_MAP), 0) + +/* In an OMP_SIMD_CLAUSE_CILK_VECTORLENGTH, one vectorlength + expression. */ +#define OMP_CLAUSE_CILK_VECTORLENGTH_EXPR(NODE) \ + OMP_CLAUSE_OPERAND (OMP_CLAUSE_SUBCODE_CHECK \ + (NODE, OMP_CLAUSE_CILK_VECTORLENGTH), 0) + #define OMP_CLAUSE_HAS_LOCATION(NODE) \ (LOCATION_LOCUS ((OMP_CLAUSE_CHECK (NODE))->omp_clause.locus) \ != UNKNOWN_LOCATION)