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.
Details
- Reviewers
aaron.ballman rjmccall • fraggamuffin • ejstotzer hfinkel rsmith - Commits
- rG587e1de4ea2a: [OPENMP 4.0] Initial support for '#pragma omp declare simd' directive.
rC264853: [OPENMP 4.0] Initial support for '#pragma omp declare simd' directive.
rL264853: [OPENMP 4.0] Initial support for '#pragma omp declare simd' directive.
Diff Detail
Event Timeline
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/
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 пишет:
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"));
+ elseTokenMatched = 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");
+ elseTokenMatched = 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
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"));
+ elseTokenMatched = 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");
+ elseTokenMatched = 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 | ||
---|---|---|
149 | 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"));
+ elseTokenMatched = 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");
+ elseTokenMatched = 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
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 | ||
---|---|---|
143–144 | 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 | ||
34 | This is an explicit specialization, not an instantiation. |
include/clang/AST/ASTMutationListener.h | ||
---|---|---|
118–122 | 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 | ||
2059 | Comment is out of date. | |
2070–2071 | Why do you need setters? | |
include/clang/Parse/Parser.h | ||
1032 | exception-specification? | |
lib/AST/DeclPrinter.cpp | ||
412–427 | This level of special-casing is not OK, please find a more general way of dealing with this. | |
lib/Parse/ParseOpenMP.cpp | ||
87 | IsInTagDecl is the wrong name for this; enums are tag decls, but they shouldn't get this treatment. | |
244 | What does this have to do with "TemplateFunction"s? | |
268 | This doesn't make sense; that function is for MS-compatibility delayed template parsing, which seems entirely unrelated to this OpenMP pragma. |
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 | ||
---|---|---|
2780 | 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 | ||
47–66 | Bracing here is now inconsistent. | |
141 | Add braces around this multi-line else (and the corresponding if for consistency). | |
145 | Do you really need both these checks? | |
162–168 | Why are you doing this delayed parsing? You still seem to have no tests that require it. | |
lib/Sema/SemaOpenMP.cpp | ||
2140 | 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 | ||
---|---|---|
2780 | Ok, done | |
lib/Parse/ParseOpenMP.cpp | ||
47–66 | Fixed, thanks | |
141 | Done, thanks | |
145 | Removed the second one and turned it to assert() | |
162–168 | 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. | |
lib/Sema/SemaOpenMP.cpp | ||
2140 | It will be used later when we'll add parsing of clauses for this pragma |
lib/Parse/ParseOpenMP.cpp | ||
---|---|---|
162–168 | 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.) |
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.)
include/clang/Basic/Attr.td | ||
---|---|---|
2064–2068 | What's this for? | |
include/clang/Basic/DiagnosticParseKinds.td | ||
992–995 | with -> after, in both diagnstics. | |
include/clang/Basic/DiagnosticSemaKinds.td | ||
7641 | can be applied to functions only -> can only be applied to functions | |
lib/AST/DeclPrinter.cpp | ||
217 | 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 | ||
127 | 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. | |
138–139 | 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(); | |
139 | 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. | |
154 | 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 } | |
156–162 | 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. |
Richard, thanks for the review!
include/clang/Basic/Attr.td | ||
---|---|---|
2064–2068 | 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 | ||
992–995 | Done, thanks. | |
include/clang/Basic/DiagnosticSemaKinds.td | ||
7641 | Done | |
lib/AST/DeclPrinter.cpp | ||
217 | Ok, will do. | |
lib/Parse/ParseOpenMP.cpp | ||
127 | Done, thanks. | |
138–139 | 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. | |
139 | Ok, removed Ptr.get().isNull() | |
156–162 | Ok, done |
include/clang/Basic/Attr.td | ||
---|---|---|
2064–2068 | 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 | ||
127 | 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. | |
138–139 | I don't think your example is reasonable to accept. Right now (prior to your patch), we have two fundamentally different kinds of pragmas:
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. |
include/clang/Basic/AttrDocs.td | ||
---|---|---|
1591 | 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 | ||
88 | Can we not name the last parameter with an identifier that is also a type name? That always weirds me out. ;-) | |
146 | 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 | ||
2159 | Please use OMPDeclareSimdDeclAttr::CreateImplicit instead. |
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.
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.
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)?