This is an archive of the discontinued LLVM Phabricator instance.

[OPENMP 4.0] Initial support for '#pragma omp declare simd' directive.
ClosedPublic

Authored by ABataev on Jun 22 2015, 4:35 AM.

Details

Summary

Initial parsing/sema/serialization/deserialization support for '#pragma omp declare simd' directive.
The declare simd construct can be applied to a function to enable the creation of one or more versions that can process multiple arguments using SIMD instructions from a single invocation from a SIMD loop.
If the function has any declarations, then the declare simd construct for any declaration that has one must be equivalent to the one specified for the definition. Otherwise, the result is unspecified.
This pragma can be applied many times to the same declaration.
Internally this pragma is represented as an attribute. But we need special processing for this pragma because it must be used before function declaration, this directive is applied to.
A delayed parsing is used for parsing a pragma itself, because this directive has several clauses that can reference arguments of the associated function.

Diff Detail

Event Timeline

ABataev updated this revision to Diff 28103.Jun 22 2015, 4:35 AM
ABataev retitled this revision from to [OPENMP] Initial support for '#pragma omp declare simd' directive..
ABataev updated this object.
ABataev edited the test plan for this revision. (Show Details)
ABataev added a subscriber: Unknown Object (MLST).
hfinkel added inline comments.Jun 22 2015, 6:25 PM
test/OpenMP/declare_simd_ast_print.c
11

I understand that the point is that you can have multiple 'declare simd' directives for the same function. However, if you have actual duplicates, should we issue a warning?

Hal, I don't think that this is necessary (at least now). It does not
change anything, the function still must be marked as simd. There will
be additional analysis for clauses, because most of clauses arguments
must be referenced only once.

Best regards,

Alexey Bataev

Software Engineer
Intel Compiler Team

23.06.2015 4:25, hfinkel@anl.gov пишет:

Comment at: test/OpenMP/declare_simd_ast_print.c:10
@@ +9,3 @@
+#pragma omp declare simd
+#pragma omp declare simd

+void add_1(float *d, float *s1, float *s2);

I understand that the point is that you can have multiple 'declare simd' directives for the same function. However, if you have actual duplicates, should we issue a warning?

http://reviews.llvm.org/D10599

EMAIL PREFERENCES

http://reviews.llvm.org/settings/panel/emailpreferences/
ABataev added a subscriber: ABataev.Jul 7 2015, 4:41 AM

I tend to agree with hal that this should be a warning. It's low
priority, definitely, but silently allowing the programmer to get away
with incorrect code doesn't seem beneficial to anyone. It would also
point out the two other places in your tests that have this exact same
code pattern with no additional testing benefit. ;-)

This code is correct, OpenMP standard allows to mark the same function many times with the same pragma. Here is an excerpt:

#pragma omp declare simd [clause[[,] clause] ...] new-line
[#pragma omp declare simd [clause[[,] clause] ...] new-line]
[...]
function definition or declaration

There may be multiple declare simd directives for a function (C, C++, Fortran) or subroutine (Fortran).

I would also like to see a test for #pragma omp declare simd that is
at the end of the TU with no futher statements (to ensure it gets
diagnosed).

Ok, will be added.

Copy paste error?

Nope, I have to verify that the compiler allows to mark the same function several times with '#pragma omp declare simd' according to standard.

This attribute has a pragma spelling where the namespace is openmp and
the name is declare. It's the same pattern used by the loop hint and
init_seg pragmas.

Can't add spellings, it breaks -ast-print mode (pragmas are printed just like attributes, directly after function declaration).

Please document this attribute as it does have a pragma spelling.

Will add

This attribute appears to be missing its Subjects clause as well.
That's not information we currently care about for pragmas, but I
would like the declarative part of the attribute to match the semantic
expectations.

Will add too.

I've not seen any tests for ObjC, so does this really apply to methods?

I'll fix this message.

Capitalize the first word in the documentation part of the sentence.

Thanks, fixed.

Drop the virtual, add override.

Fixed.

Drop the "if"

Fixed

Can you use a range-based for loop please?

Done

This is not an implicit attribute because it's the direct result of
the user typing something in source code.

Fixed.

This really weirds me out. That means this function doesn't parse just
the pragma, it also parses any external declaration.

Yes, we have to parse function right after pragma to be able correctly handle clauses associated with this pragma. These clauses may have refernces to function arguments and because of that we have to parse the function first and only then the pragma itself.

The comments don't look correct here.

Missed this one, thanks.

Is this safe to assert in a parsing method, or should this be diagnosed?

There always must be at least one token if the compiler works correctly - tok::annot_pragma_openmp. No need to diagnose.

Has this situation already been diagnosed? If so, then why are we
generating the tokens in a way that require this work? And if not, are
we missing a diagnostic here?

Yes, this was diagnosed already. We need to remove cached tokens, so just skip them.

Attribute is not implicit.

Fixed.

Best regards,

Alexey Bataev

Software Engineer
Intel Compiler Team

06.07.2015 16:13, Aaron Ballman пишет:

ABataev updated this revision to Diff 29164.Jul 7 2015, 4:47 AM
ABataev retitled this revision from [OPENMP] Initial support for '#pragma omp declare simd' directive. to [OPENMP 4.0] Initial support for '#pragma omp declare simd' directive..

Update after review

ABataev updated this revision to Diff 29237.Jul 7 2015, 9:35 PM

Update after review

aaron.ballman edited edge metadata.Jul 8 2015, 1:55 PM
aaron.ballman added a subscriber: aaron.ballman.

LGTM with one question below. I would wait for review from Richard or
Hal before committing.

Index: lib/Parse/ParseOpenMP.cpp

  • lib/Parse/ParseOpenMP.cpp

+++ lib/Parse/ParseOpenMP.cpp
@@ -30,6 +30,7 @@

// E.g.: OMPD_for OMPD_simd ===> OMPD_for_simd
// TODO: add other combined directives in topological order.
const OpenMPDirectiveKind F[][3] = {

+ {OMPD_unknown /*declare*/, OMPD_simd, OMPD_declare_simd},

{OMPD_unknown /*cancellation*/, OMPD_unknown /*point*/,
 OMPD_cancellation_point},
{OMPD_for, OMPD_simd, OMPD_for_simd},

@@ -43,25 +44,25 @@

        : getOpenMPDirectiveKind(P.getPreprocessor().getSpelling(Tok));
bool TokenMatched = false;
for (unsigned i = 0; i < llvm::array_lengthof(F); ++i) {
  • if (!Tok.isAnnotation() && DKind == OMPD_unknown) {

+ if (!Tok.isAnnotation() && DKind == OMPD_unknown)

TokenMatched =
  • (i == 0) &&
  • !P.getPreprocessor().getSpelling(Tok).compare("cancellation");
  • } else {

+ ((i == 0) &&
+ !P.getPreprocessor().getSpelling(Tok).compare("declare")) ||
+ ((i == 1) &&
+ !P.getPreprocessor().getSpelling(Tok).compare("cancellation"));
+ else

TokenMatched = DKind == F[i][0] && DKind != OMPD_unknown;
  • } if (TokenMatched) { Tok = P.getPreprocessor().LookAhead(0); auto SDKind = Tok.isAnnotation() ? OMPD_unknown : getOpenMPDirectiveKind(P.getPreprocessor().getSpelling(Tok));
  • if (!Tok.isAnnotation() && DKind == OMPD_unknown) {

+ if (!Tok.isAnnotation() && SDKind == OMPD_unknown)

TokenMatched =
  • (i == 0) && !P.getPreprocessor().getSpelling(Tok).compare("point");
  • } else {

+ (i == 1) && !P.getPreprocessor().getSpelling(Tok).compare("point");
+ else

TokenMatched = SDKind == F[i][1] && SDKind != OMPD_unknown;
  • } if (TokenMatched) { P.ConsumeToken(); DKind = F[i][2];

@@ -75,14 +76,25 @@
/
/ threadprivate-directive:
/ annot_pragma_openmp 'threadprivate' simple-variable-list
+
/ annot_pragma_omp_end
/
-Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirective() {
+
/ declare-simd-directive:
+/ annot_pragma_openmp 'declare simd' {<clause> [,]}
+
/ annot_pragma_omp_end
+/ <function declaration/definition>
+
/
+Parser::DeclGroupPtrTy
+Parser::ParseOpenMPDeclarativeDirectiveWithExtDecl(bool IsInTagDecl,
+ unsigned Level) {

assert(Tok.is(tok::annot_pragma_openmp) && "Not an OpenMP directive!");
ParenBraceBracketBalancer BalancerRAIIObj(*this);

+ auto AnnotationVal = reinterpret_cast<uintptr_t>(Tok.getAnnotationValue());

SourceLocation Loc = ConsumeToken();
SmallVector<Expr *, 5> Identifiers;
  • auto DKind = ParseOpenMPDirectiveKind(*this);

+ OpenMPDirectiveKind DKind =
+ (AnnotationVal == 0) ? ParseOpenMPDirectiveKind(*this)
+ : static_cast<OpenMPDirectiveKind>(AnnotationVal);

switch (DKind) {
case OMPD_threadprivate:

@@ -100,6 +112,86 @@

  return Actions.ActOnOpenMPThreadprivateDirective(Loc, Identifiers);
}
break;

+ case OMPD_declare_simd: {
+ The syntax is:
+
{ #pragma omp declare simd }
+ <function-declaration-or-definition>
+

+ if (AnnotationVal == 0)
+ Skip 'simd' if it was restored from cached tokens.
+ ConsumeToken();
+ if (IsInTagDecl) {
+ LateParseOpenMPDeclarativeDirectiveWithTemplateFunction(
+ /*DKind=*/OMPD_declare_simd, Loc);
+ return DeclGroupPtrTy();
+ }
+
+ SmallVector<llvm::PointerIntPair<OMPClause *, 1, bool>, OMPC_unknown + 1>
+ FirstClauses(OMPC_unknown + 1);
+ SmallVector<OMPClause *, 4> Clauses;
+ SmallVector<Token, 8> CachedPragmas;
+
+ while (Tok.isNot(tok::annot_pragma_openmp_end) && Tok.isNot(tok::eof)) {
+ CachedPragmas.push_back(Tok);
+ ConsumeAnyToken();
+ }
+ CachedPragmas.push_back(Tok);
+ if (Tok.isNot(tok::eof))
+ ConsumeAnyToken();
+
+ DeclGroupPtrTy Ptr;
+ if (Tok.is(tok::annot_pragma_openmp)) {
+ Ptr = ParseOpenMPDeclarativeDirectiveWithExtDecl(IsInTagDecl, Level + 1);
+ } else {
+
Here we expect to see some function declaration.
+ ParsedAttributesWithRange Attrs(AttrFactory);
+ MaybeParseCXX11Attributes(Attrs);
+ MaybeParseMicrosoftAttributes(Attrs);
+ ParsingDeclSpec PDS(*this);
+ Ptr = ParseExternalDeclaration(Attrs, &PDS);
+ }
+ if (!Ptr || Ptr.get().isNull())
+ return DeclGroupPtrTy();
+ if (Ptr.get().isDeclGroup()) {
+ Diag(Tok, diag::err_omp_single_decl_in_declare_simd);
+ return DeclGroupPtrTy();

Would it make sense to return Ptr here instead so that further
diagnostics can be reported?

~Aaron

hfinkel edited edge metadata.Jul 8 2015, 5:50 PM

LGTM with one question below. I would wait for review from Richard or
Hal before committing.

I'm not really the right person to okay this patch. Richard, can you please look at this?

Index: lib/Parse/ParseOpenMP.cpp

  • lib/Parse/ParseOpenMP.cpp

+++ lib/Parse/ParseOpenMP.cpp
@@ -30,6 +30,7 @@

// E.g.: OMPD_for OMPD_simd ===> OMPD_for_simd
// TODO: add other combined directives in topological order.
const OpenMPDirectiveKind F[][3] = {

+ {OMPD_unknown /*declare*/, OMPD_simd, OMPD_declare_simd},

{OMPD_unknown /*cancellation*/, OMPD_unknown /*point*/,
 OMPD_cancellation_point},
{OMPD_for, OMPD_simd, OMPD_for_simd},

@@ -43,25 +44,25 @@

        : getOpenMPDirectiveKind(P.getPreprocessor().getSpelling(Tok));
bool TokenMatched = false;
for (unsigned i = 0; i < llvm::array_lengthof(F); ++i) {
  • if (!Tok.isAnnotation() && DKind == OMPD_unknown) {

+ if (!Tok.isAnnotation() && DKind == OMPD_unknown)

TokenMatched =
  • (i == 0) &&
  • !P.getPreprocessor().getSpelling(Tok).compare("cancellation");
  • } else {

+ ((i == 0) &&
+ !P.getPreprocessor().getSpelling(Tok).compare("declare")) ||
+ ((i == 1) &&
+ !P.getPreprocessor().getSpelling(Tok).compare("cancellation"));
+ else

TokenMatched = DKind == F[i][0] && DKind != OMPD_unknown;
  • } if (TokenMatched) { Tok = P.getPreprocessor().LookAhead(0); auto SDKind = Tok.isAnnotation() ? OMPD_unknown : getOpenMPDirectiveKind(P.getPreprocessor().getSpelling(Tok));
  • if (!Tok.isAnnotation() && DKind == OMPD_unknown) {

+ if (!Tok.isAnnotation() && SDKind == OMPD_unknown)

TokenMatched =
  • (i == 0) && !P.getPreprocessor().getSpelling(Tok).compare("point");
  • } else {

+ (i == 1) && !P.getPreprocessor().getSpelling(Tok).compare("point");
+ else

TokenMatched = SDKind == F[i][1] && SDKind != OMPD_unknown;
  • } if (TokenMatched) { P.ConsumeToken(); DKind = F[i][2];

@@ -75,14 +76,25 @@
/
/ threadprivate-directive:
/ annot_pragma_openmp 'threadprivate' simple-variable-list
+
/ annot_pragma_omp_end
/
-Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirective() {
+
/ declare-simd-directive:
+/ annot_pragma_openmp 'declare simd' {<clause> [,]}
+
/ annot_pragma_omp_end
+/ <function declaration/definition>
+
/
+Parser::DeclGroupPtrTy
+Parser::ParseOpenMPDeclarativeDirectiveWithExtDecl(bool IsInTagDecl,
+ unsigned Level) {

assert(Tok.is(tok::annot_pragma_openmp) && "Not an OpenMP directive!");
ParenBraceBracketBalancer BalancerRAIIObj(*this);

+ auto AnnotationVal = reinterpret_cast<uintptr_t>(Tok.getAnnotationValue());

SourceLocation Loc = ConsumeToken();
SmallVector<Expr *, 5> Identifiers;
  • auto DKind = ParseOpenMPDirectiveKind(*this);

+ OpenMPDirectiveKind DKind =
+ (AnnotationVal == 0) ? ParseOpenMPDirectiveKind(*this)
+ : static_cast<OpenMPDirectiveKind>(AnnotationVal);

switch (DKind) {
case OMPD_threadprivate:

@@ -100,6 +112,86 @@

  return Actions.ActOnOpenMPThreadprivateDirective(Loc, Identifiers);
}
break;

+ case OMPD_declare_simd: {
+ The syntax is:
+
{ #pragma omp declare simd }
+ <function-declaration-or-definition>
+

+ if (AnnotationVal == 0)
+ Skip 'simd' if it was restored from cached tokens.
+ ConsumeToken();
+ if (IsInTagDecl) {
+ LateParseOpenMPDeclarativeDirectiveWithTemplateFunction(
+ /*DKind=*/OMPD_declare_simd, Loc);
+ return DeclGroupPtrTy();
+ }
+
+ SmallVector<llvm::PointerIntPair<OMPClause *, 1, bool>, OMPC_unknown + 1>
+ FirstClauses(OMPC_unknown + 1);
+ SmallVector<OMPClause *, 4> Clauses;
+ SmallVector<Token, 8> CachedPragmas;
+
+ while (Tok.isNot(tok::annot_pragma_openmp_end) && Tok.isNot(tok::eof)) {
+ CachedPragmas.push_back(Tok);
+ ConsumeAnyToken();
+ }
+ CachedPragmas.push_back(Tok);
+ if (Tok.isNot(tok::eof))
+ ConsumeAnyToken();
+
+ DeclGroupPtrTy Ptr;
+ if (Tok.is(tok::annot_pragma_openmp)) {
+ Ptr = ParseOpenMPDeclarativeDirectiveWithExtDecl(IsInTagDecl, Level + 1);
+ } else {
+
Here we expect to see some function declaration.
+ ParsedAttributesWithRange Attrs(AttrFactory);
+ MaybeParseCXX11Attributes(Attrs);
+ MaybeParseMicrosoftAttributes(Attrs);
+ ParsingDeclSpec PDS(*this);
+ Ptr = ParseExternalDeclaration(Attrs, &PDS);
+ }
+ if (!Ptr || Ptr.get().isNull())
+ return DeclGroupPtrTy();
+ if (Ptr.get().isDeclGroup()) {
+ Diag(Tok, diag::err_omp_single_decl_in_declare_simd);
+ return DeclGroupPtrTy();

Would it make sense to return Ptr here instead so that further
diagnostics can be reported?

~Aaron

lib/Parse/ParseOpenMP.cpp
155

This is not your fault, and should not be fixed by this patch, but this series of three:

ParsedAttributesWithRange Attrs(AttrFactory);
MaybeParseCXX11Attributes(Attrs);
MaybeParseMicrosoftAttributes(Attrs);

occurs a lot. It would be nice to factor this into one function to reduce the amount of repeated logic.

Would it make sense to return Ptr here instead so that further
diagnostics can be reported?

I think you're right. Will be fixed.

Best regards,

Alexey Bataev

Software Engineer
Intel Compiler Team

08.07.2015 23:55, Aaron Ballman пишет:

LGTM with one question below. I would wait for review from Richard or
Hal before committing.

Index: lib/Parse/ParseOpenMP.cpp

  • lib/Parse/ParseOpenMP.cpp

+++ lib/Parse/ParseOpenMP.cpp
@@ -30,6 +30,7 @@

// E.g.: OMPD_for OMPD_simd ===> OMPD_for_simd
// TODO: add other combined directives in topological order.
const OpenMPDirectiveKind F[][3] = {

+ {OMPD_unknown /*declare*/, OMPD_simd, OMPD_declare_simd},

{OMPD_unknown /*cancellation*/, OMPD_unknown /*point*/,
 OMPD_cancellation_point},
{OMPD_for, OMPD_simd, OMPD_for_simd},

@@ -43,25 +44,25 @@

        : getOpenMPDirectiveKind(P.getPreprocessor().getSpelling(Tok));
bool TokenMatched = false;
for (unsigned i = 0; i < llvm::array_lengthof(F); ++i) {
  • if (!Tok.isAnnotation() && DKind == OMPD_unknown) {

+ if (!Tok.isAnnotation() && DKind == OMPD_unknown)

TokenMatched =
  • (i == 0) &&
  • !P.getPreprocessor().getSpelling(Tok).compare("cancellation");
  • } else {

+ ((i == 0) &&
+ !P.getPreprocessor().getSpelling(Tok).compare("declare")) ||
+ ((i == 1) &&
+ !P.getPreprocessor().getSpelling(Tok).compare("cancellation"));
+ else

TokenMatched = DKind == F[i][0] && DKind != OMPD_unknown;
  • } if (TokenMatched) { Tok = P.getPreprocessor().LookAhead(0); auto SDKind = Tok.isAnnotation() ? OMPD_unknown : getOpenMPDirectiveKind(P.getPreprocessor().getSpelling(Tok));
  • if (!Tok.isAnnotation() && DKind == OMPD_unknown) {

+ if (!Tok.isAnnotation() && SDKind == OMPD_unknown)

TokenMatched =
  • (i == 0) && !P.getPreprocessor().getSpelling(Tok).compare("point");
  • } else {

+ (i == 1) && !P.getPreprocessor().getSpelling(Tok).compare("point");
+ else

TokenMatched = SDKind == F[i][1] && SDKind != OMPD_unknown;
  • } if (TokenMatched) { P.ConsumeToken(); DKind = F[i][2];

@@ -75,14 +76,25 @@

///
///       threadprivate-directive:
///         annot_pragma_openmp 'threadprivate' simple-variable-list

+/// annot_pragma_omp_end

///

-Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirective() {
+/ declare-simd-directive:
+
/ annot_pragma_openmp 'declare simd' {<clause> [,]}
+/ annot_pragma_omp_end
+
/ <function declaration/definition>
+///
+Parser::DeclGroupPtrTy
+Parser::ParseOpenMPDeclarativeDirectiveWithExtDecl(bool IsInTagDecl,
+ unsigned Level) {

assert(Tok.is(tok::annot_pragma_openmp) && "Not an OpenMP directive!");
ParenBraceBracketBalancer BalancerRAIIObj(*this);

+ auto AnnotationVal = reinterpret_cast<uintptr_t>(Tok.getAnnotationValue());

SourceLocation Loc = ConsumeToken();
SmallVector<Expr *, 5> Identifiers;
  • auto DKind = ParseOpenMPDirectiveKind(*this);

+ OpenMPDirectiveKind DKind =
+ (AnnotationVal == 0) ? ParseOpenMPDirectiveKind(*this)
+ : static_cast<OpenMPDirectiveKind>(AnnotationVal);

switch (DKind) {
case OMPD_threadprivate:

@@ -100,6 +112,86 @@

  return Actions.ActOnOpenMPThreadprivateDirective(Loc, Identifiers);
}
break;

+ case OMPD_declare_simd: {
+ The syntax is:
+
{ #pragma omp declare simd }
+ <function-declaration-or-definition>
+

+ if (AnnotationVal == 0)
+ Skip 'simd' if it was restored from cached tokens.
+ ConsumeToken();
+ if (IsInTagDecl) {
+ LateParseOpenMPDeclarativeDirectiveWithTemplateFunction(
+ /*DKind=*/OMPD_declare_simd, Loc);
+ return DeclGroupPtrTy();
+ }
+
+ SmallVector<llvm::PointerIntPair<OMPClause *, 1, bool>, OMPC_unknown + 1>
+ FirstClauses(OMPC_unknown + 1);
+ SmallVector<OMPClause *, 4> Clauses;
+ SmallVector<Token, 8> CachedPragmas;
+
+ while (Tok.isNot(tok::annot_pragma_openmp_end) && Tok.isNot(tok::eof)) {
+ CachedPragmas.push_back(Tok);
+ ConsumeAnyToken();
+ }
+ CachedPragmas.push_back(Tok);
+ if (Tok.isNot(tok::eof))
+ ConsumeAnyToken();
+
+ DeclGroupPtrTy Ptr;
+ if (Tok.is(tok::annot_pragma_openmp)) {
+ Ptr = ParseOpenMPDeclarativeDirectiveWithExtDecl(IsInTagDecl, Level + 1);
+ } else {
+
Here we expect to see some function declaration.
+ ParsedAttributesWithRange Attrs(AttrFactory);
+ MaybeParseCXX11Attributes(Attrs);
+ MaybeParseMicrosoftAttributes(Attrs);
+ ParsingDeclSpec PDS(*this);
+ Ptr = ParseExternalDeclaration(Attrs, &PDS);
+ }
+ if (!Ptr || Ptr.get().isNull())
+ return DeclGroupPtrTy();
+ if (Ptr.get().isDeclGroup()) {
+ Diag(Tok, diag::err_omp_single_decl_in_declare_simd);
+ return DeclGroupPtrTy();

Would it make sense to return Ptr here instead so that further
diagnostics can be reported?

~Aaron

ABataev updated this revision to Diff 29297.Jul 8 2015, 9:27 PM
ABataev edited edge metadata.

Update after review

rsmith edited edge metadata.Jul 22 2015, 1:47 PM

You have a lot of logic here to handle delayed parsing, but none of your testcases need any delayed parsing.

Are you supporting delayed parsing just to deal with the argument-list in the various clauses? (Your current delayed parsing support doesn't work for that, because the pragma is not parsed within the scope of the function prototype.) If so, have you considered producing a list of identifiers from the parser, and looking them up when the attribute is applied to the declaration in Sema? That would seem to remove the need for all of the delayed parsing.

lib/Parse/ParseOpenMP.cpp
149–150

So, you can combine "#pragma omp declare simd" with other pragmas, but only if the simd pragmas come first?

test/OpenMP/declare_simd_ast_print.cpp
35

This is an explicit specialization, not an instantiation.

rsmith added inline comments.Jul 22 2015, 1:47 PM
include/clang/AST/ASTMutationListener.h
118–122 ↗(On Diff #29297)

Why do you need this? Isn't the attribute always applied to a "fresh" declaration (one that was recently created, rather than one that was imported from an external source)?

include/clang/Basic/Attr.td
2113

Comment is out of date.

2124–2125

Why do you need setters?

include/clang/Parse/Parser.h
1036

exception-specification?

lib/AST/DeclPrinter.cpp
412–427 ↗(On Diff #29297)

This level of special-casing is not OK, please find a more general way of dealing with this.

lib/Parse/ParseOpenMP.cpp
96

IsInTagDecl is the wrong name for this; enums are tag decls, but they shouldn't get this treatment.

206

What does this have to do with "TemplateFunction"s?

230

This doesn't make sense; that function is for MS-compatibility delayed template parsing, which seems entirely unrelated to this OpenMP pragma.

ABataev updated this revision to Diff 30669.Jul 26 2015, 9:00 PM
ABataev updated this object.
ABataev edited edge metadata.

Update after review

Please find a better way to handle this attribute in DeclPrinter. Perhaps you could generalize your existing support to cover all attributes with pragma spelling?

lib/Parse/ParseDeclCXX.cpp
2817

Please can you split this refactoring out into a separate commit? That'll make the functional change here (handling annot_pragma_openmp) much easier for future code archaeologists to see.

lib/Parse/ParseOpenMP.cpp
50–74

Bracing here is now inconsistent.

147

Add braces around this multi-line else (and the corresponding if for consistency).

151

Do you really need both these checks?

168–174

Why are you doing this delayed parsing? You still seem to have no tests that require it.

lib/Sema/SemaOpenMP.cpp
2351

You don't use Clauses for anything in here; is that intentional?

Richard, thanks for the review. I'll try to fix printing of pragmas.

lib/Parse/ParseDeclCXX.cpp
2817

Ok, done

lib/Parse/ParseOpenMP.cpp
50–74

Fixed, thanks

147

Done, thanks

151

Removed the second one and turned it to assert()

168–174

We need it for future parsing of clauses associated with the 'declare simd' construct. Some of the clauses may have references to function arguments. That's why I'm using delayed parsing: at first we need to parse a function along with arguments and only then we'll parse all the pragmas along with their clauses and references to args.
Of course currently it is not used, because this patch does not introduce parsing of clauses. It will be added later.

lib/Sema/SemaOpenMP.cpp
2351

It will be used later when we'll add parsing of clauses for this pragma

rsmith added inline comments.Jul 31 2015, 2:07 PM
lib/Parse/ParseOpenMP.cpp
168–174

This code won't work for that; you've left the scope of the function parameters, so lookup for them will fail here. Generally-speaking, we don't like speculative / untested code to be committed, and would prefer to hold back on those changes until we can actually test them in some way.

Here's what I suggest: remove the delayed parsing code from here for this commit (parse the pragma first, then parse the nested declaration, then act on the result), and bring that code back once you introduce parsing for a clause that needs it, if indeed that's the right approach for those clauses. (As I mentioned before, if all they do is to provide a list of identifiers naming parameters, you don't need delay parsing and can instead store them as identifiers and look them up in Sema after the fact. On the other hand, if this pragma allows an arbitrary expression referencing a parameter to appear before the declaration of that parameter, then we'll need something like delayed parsing.)

rsmith added a subscriber: rsmith.Jul 31 2015, 2:54 PM

See also this unanswered question on the OpenMP forums:
http://openmp.org/forum/viewtopic.php?f=12&t=1532

Ok,
I'll remove it for now and rework it later when it will be required.

Best regards,

Alexey Bataev

Software Engineer
Intel Compiler Team

01.08.2015 0:07, Richard Smith пишет:

rsmith added inline comments.

Comment at: lib/Parse/ParseOpenMP.cpp:171-177
@@ +170,9 @@
+
+ Append the current token at the end of the new token stream so that it
+
doesn't get lost.
+ CachedPragmas.push_back(Tok);
+ // Push back tokens for pragma.
+ PP.EnterTokenStream(CachedPragmas.data(), CachedPragmas.size(),
+ /*DisableMacroExpansion=*/true,
+ /*OwnsTokens=*/false);

+ // Parse pragma itself.

ABataev wrote:

rsmith wrote:

Why are you doing this delayed parsing? You still seem to have no tests that require it.

We need it for future parsing of clauses associated with the 'declare simd' construct. Some of the clauses may have references to function arguments. That's why I'm using delayed parsing: at first we need to parse a function along with arguments and only then we'll parse all the pragmas along with their clauses and references to args.
Of course currently it is not used, because this patch does not introduce parsing of clauses. It will be added later.

This code won't work for that; you've left the scope of the function parameters, so lookup for them will fail here. Generally-speaking, we don't like speculative / untested code to be committed, and would prefer to hold back on those changes until we can actually test them in some way.

Here's what I suggest: remove the delayed parsing code from here for this commit (parse the pragma first, then parse the nested declaration, then act on the result), and bring that code back once you introduce parsing for a clause that needs it, if indeed that's the right approach for those clauses. (As I mentioned before, if all they do is to provide a list of identifiers naming parameters, you don't need delay parsing and can instead store them as identifiers and look them up in Sema after the fact. On the other hand, if this pragma allows an arbitrary expression referencing a parameter to appear before the declaration of that parameter, then we'll need something like delayed parsing.)

http://reviews.llvm.org/D10599

ABataev updated this revision to Diff 31216.Aug 3 2015, 12:51 AM

Update after review

rsmith added inline comments.Aug 25 2015, 7:04 PM
include/clang/Basic/Attr.td
2118–2122

What's this for?

include/clang/Basic/DiagnosticParseKinds.td
994–997

with -> after, in both diagnstics.

include/clang/Basic/DiagnosticSemaKinds.td
7721

can be applied to functions only -> can only be applied to functions

lib/AST/DeclPrinter.cpp
214 ↗(On Diff #31216)

It looks like we would no longer print attributes with pragma spelling for non-function declarations after this change (but I don't think we have any such pragmas at the moment). I don't want to ask you to write a bunch more code here that you can't test, but can you add a FIXME somewhere in here to add calls to print pragmas in more places?

lib/Parse/ParseOpenMP.cpp
133

You should bail out if you don't actually find your desired token. If you carry on without checking, your ConsumeToken call might consume the tok::eof token or some similar bad thing.

144–145

I find this loop slightly surprising: the cases where Parse*Declaration return a null DeclGroupPtrTy are when they parsed some declaration-like entity (such as a declaration-like pragma) but didn't create some declaration to represent it.

Do you really want to allow such things between your pragma and its associated function? As an extreme case, you'd accept things like this:

#pragma omp declare simd
#pragma pack (...)
;
public:
__if_exists(foo) {
  int n;
}
int the_simd_function();
145

Do you really need to check both !Ptr and Ptr.get().isNull() here? Generally, the parser shouldn't really be calling get() on an OpaquePtr -- these pointers are supposed to be opaque to the parser.

160

The only way you could get here without being at EOF or EOM would be if you hit an unexpected right-brace. Shouldn't you diagnose that? For instance, it looks like you won't diagnose this case:

namespace N {
  #pragma omp declare simd
}
162–168

I would suggest that you instead call ActOnOpenMPDeclareSimdDirective unconditionally, pass in Ptr rather than its contained declaration, and check for a single declaration in Sema -- this seems much more of a semantic check than a syntactic one.

ABataev marked 7 inline comments as done.Aug 25 2015, 11:04 PM

Richard, thanks for the review!

include/clang/Basic/Attr.td
2118–2122

This is required for proper AST printing of pragma. Without this additional member (this one is required for pragma attributes by attribute generation system) all pragmas will be printed in one line.

include/clang/Basic/DiagnosticParseKinds.td
994–997

Done, thanks.

include/clang/Basic/DiagnosticSemaKinds.td
7721

Done

lib/AST/DeclPrinter.cpp
214 ↗(On Diff #31216)

Ok, will do.

lib/Parse/ParseOpenMP.cpp
133

Done, thanks.

144–145

We need to be able to combine 'declare simd' pragma with other pragmas, for example:

#pragma omp declare simd
#pragma GCC visibility push(default)
#pragma options align=packed
#pragma omp declare simd
#pragma GCC visibility pop
int tadd(int b) { return x[b] + b; }

We should be able to accept such code. But I will reduce the number of allowed constructs.

145

Ok, removed Ptr.get().isNull()

162–168

Ok, done

ABataev updated this revision to Diff 33181.Aug 25 2015, 11:05 PM
ABataev marked 7 inline comments as done.

Update after review

Richard, any comments about latest version?

rsmith added inline comments.Oct 7 2015, 6:25 PM
include/clang/Basic/Attr.td
2118–2122

Can we instead handle this in the generated code? Presumably we'd get this wrong in the same way for all attributes with pragma spelling?

lib/Parse/ParseOpenMP.cpp
133

Hmm, on reflection it would be better to use:

while (Tok.isNot(tok::annot_pragma_openmp_end))
  ConsumeAnyToken();

We know that there is an annot_pragma_openmp_end token coming, but SkipUntil might stop early if there's (say) a stray right-paren in the token stream.

144–145

I don't think your example is reasonable to accept. Right now (prior to your patch), we have two fundamentally different kinds of pragmas:

  1. Lexer-level pragmas. These can appear anywhere in the token stream, and take effect immediately. These are handled entirely by the lexer.
  1. Declaration-like pragmas. These can only appear where a declaration is permitted, and act as if they declare ... something. We transform these into tokens and handle them in the parser.

You're adding a third kind of pragma, an attribute-like pragma, that can only appear before declarations. As such, if your pragma appears before a pragma of kind 2 (such as #pragma GCC visibility), it should act as an attribute applying to *that* declaration-like entity.

So I think the only thing you should allow between your pragma and the declaration to which it applies is more attribute-like pragmas.

ABataev marked 3 inline comments as done.Oct 13 2015, 1:21 AM
ABataev added inline comments.
include/clang/Basic/Attr.td
2118–2122

Done.

lib/Parse/ParseOpenMP.cpp
133

Done, thanks.

144–145

Reworked.

ABataev updated this revision to Diff 37218.Oct 13 2015, 1:21 AM
ABataev marked 3 inline comments as done.

Update after review

aaron.ballman added inline comments.Nov 6 2015, 7:55 AM
include/clang/Basic/AttrDocs.td
1620

Line length is an issue here. ;-)

Also, "from a single invocation from a SIMD loop"; should that be "from a single invocation of a SIMD loop" instead?

Any time you use "declare simd" as a syntactic phrase, it should be properly formatted for RST as `declare simd`.

lib/Parse/ParseOpenMP.cpp
97

Can we not name the last parameter with an identifier that is also a type name? That always weirds me out. ;-)

152

Can you add a test case for a member declaration that has attributes to ensure the attributes are properly handled here? In fact, it would be good to add one for the top-level function as well.

lib/Sema/SemaOpenMP.cpp
2370

Please use OMPDeclareSimdDeclAttr::CreateImplicit instead.

ABataev marked 4 inline comments as done.Nov 16 2015, 1:49 AM
ABataev added inline comments.
include/clang/Basic/AttrDocs.td
1620

Fixed, thanks

lib/Parse/ParseOpenMP.cpp
97

Fixed, thanks.

152

Ok, will add

lib/Sema/SemaOpenMP.cpp
2370

Fixed, thanks

ABataev updated this revision to Diff 40255.Nov 16 2015, 1:50 AM
ABataev marked 4 inline comments as done.

Update after review

aaron.ballman accepted this revision.Nov 16 2015, 12:57 PM
aaron.ballman edited edge metadata.

I don't think we generally want files commit with explicit svn properties like your new test files have. However, beyond that, LGTM. You should still wait for approval from Richard before committing, however.

This revision is now accepted and ready to land.Nov 16 2015, 12:57 PM

Richard, any comments?

rjmccall edited edge metadata.Dec 16 2015, 9:46 AM

Richard should really be the one to sign off on this, since he's been managing the review so far.

I am curious why we decided to handle this separately instead of treating it as a different attribute spelling kind.

Richard should really be the one to sign off on this, since he's been managing the review so far.

I am curious why we decided to handle this separately instead of treating it as a different attribute spelling kind.

It is a separate pragma with many clauses (that will be implemented as soon as the pragma itself is committed), so we just can't treat it as a different spelling kind.

ABataev updated this revision to Diff 44833.Jan 13 2016, 10:00 PM
ABataev edited edge metadata.
ABataev removed a subscriber: rsmith.

Updated to latest version

This revision was automatically updated to reflect the committed changes.