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 http://reviews.llvm.org/D10599 Files: include/clang/AST/ASTMutationListener.h include/clang/Basic/Attr.td include/clang/Basic/AttrDocs.td include/clang/Basic/DiagnosticParseKinds.td include/clang/Basic/DiagnosticSemaKinds.td include/clang/Basic/OpenMPKinds.def include/clang/Parse/Parser.h include/clang/Sema/Sema.h include/clang/Serialization/ASTWriter.h lib/AST/DeclPrinter.cpp lib/Basic/OpenMPKinds.cpp lib/Frontend/MultiplexConsumer.cpp lib/Parse/ParseDeclCXX.cpp lib/Parse/ParseOpenMP.cpp lib/Parse/Parser.cpp lib/Sema/SemaOpenMP.cpp lib/Serialization/ASTCommon.h lib/Serialization/ASTReaderDecl.cpp lib/Serialization/ASTWriter.cpp test/OpenMP/declare_simd_ast_print.c test/OpenMP/declare_simd_ast_print.cpp test/OpenMP/declare_simd_messages.cpp
Index: lib/Serialization/ASTWriter.cpp =================================================================== --- lib/Serialization/ASTWriter.cpp +++ lib/Serialization/ASTWriter.cpp @@ -4626,6 +4626,14 @@ Record); break; + case UPD_DECL_MARKED_OPENMP_DECLARE_SIMD: { + auto *Attr = D->getAttr<OMPDeclareSimdDeclAttr>(); + AddSourceRange(Attr->getRange(), Record); + Record.push_back(Attr->getNumberOfDirectives()); + Record.push_back(Attr->getComplete() ? 1 : 0); + break; + } + case UPD_DECL_EXPORTED: Record.push_back(getSubmoduleID(Update.getModule())); break; @@ -5776,6 +5784,14 @@ DeclUpdates[D].push_back(DeclUpdate(UPD_DECL_MARKED_OPENMP_THREADPRIVATE)); } +void ASTWriter::DeclarationMarkedOpenMPDeclareSimd(const Decl *D) { + assert(!WritingAST && "Already writing the AST!"); + if (!D->isFromASTFile()) + return; + + DeclUpdates[D].push_back(DeclUpdate(UPD_DECL_MARKED_OPENMP_DECLARE_SIMD)); +} + void ASTWriter::RedefinedHiddenDefinition(const NamedDecl *D, Module *M) { assert(!WritingAST && "Already writing the AST!"); assert(D->isHidden() && "expected a hidden declaration"); Index: lib/Serialization/ASTCommon.h =================================================================== --- lib/Serialization/ASTCommon.h +++ lib/Serialization/ASTCommon.h @@ -36,6 +36,7 @@ UPD_MANGLING_NUMBER, UPD_STATIC_LOCAL_NUMBER, UPD_DECL_MARKED_OPENMP_THREADPRIVATE, + UPD_DECL_MARKED_OPENMP_DECLARE_SIMD, UPD_DECL_EXPORTED, UPD_ADDED_ATTR_TO_RECORD }; Index: lib/Serialization/ASTReaderDecl.cpp =================================================================== --- lib/Serialization/ASTReaderDecl.cpp +++ lib/Serialization/ASTReaderDecl.cpp @@ -3916,6 +3916,16 @@ Reader.Context, ReadSourceRange(Record, Idx))); break; + case UPD_DECL_MARKED_OPENMP_DECLARE_SIMD: { + SourceRange SR = ReadSourceRange(Record, Idx); + unsigned NumberOfDirectives = Record[Idx++]; + bool Complete = Record[Idx++] != 0; + auto *Attr = new (Reader.Context) OMPDeclareSimdDeclAttr( + SR, Reader.Context, NumberOfDirectives, Complete, /*SI=*/0); + D->addAttr(Attr); + break; + } + case UPD_DECL_EXPORTED: { unsigned SubmoduleID = readSubmoduleID(Record, Idx); auto *Exported = cast<NamedDecl>(D); Index: lib/Parse/Parser.cpp =================================================================== --- lib/Parse/Parser.cpp +++ lib/Parse/Parser.cpp @@ -653,7 +653,7 @@ HandlePragmaOpenCLExtension(); return DeclGroupPtrTy(); case tok::annot_pragma_openmp: - return ParseOpenMPDeclarativeDirective(); + return ParseOpenMPDeclarativeDirective(/*IsInTagDecl=*/false); case tok::annot_pragma_ms_pointers_to_members: HandlePragmaMSPointersToMembers(); return DeclGroupPtrTy(); 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]; @@ -76,13 +77,17 @@ /// threadprivate-directive: /// annot_pragma_openmp 'threadprivate' simple-variable-list /// -Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirective() { +Parser::DeclGroupPtrTy +Parser::ParseOpenMPDeclarativeDirective(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 +105,85 @@ 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) { + LateParseOpenMPDeclarativeDirective(/*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 = ParseOpenMPDeclarativeDirective(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(); + } + + // 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. + // Consume the previously pushed token. + ConsumeAnyToken(/*ConsumeCodeCompletionTok=*/true); + + while (Tok.isNot(tok::annot_pragma_openmp_end)) { + OpenMPClauseKind CKind = Tok.isAnnotation() + ? OMPC_unknown + : getOpenMPClauseKind(PP.getSpelling(Tok)); + Actions.StartOpenMPClause(CKind); + OMPClause *Clause = + ParseOpenMPClause(DKind, CKind, !FirstClauses[CKind].getInt()); + FirstClauses[CKind].setInt(true); + if (Clause) { + FirstClauses[CKind].setPointer(Clause); + Clauses.push_back(Clause); + } + + // Skip ',' if any. + if (Tok.is(tok::comma)) + ConsumeToken(); + Actions.EndOpenMPClause(); + } + // Consume final annot_pragma_openmp_end. + ConsumeToken(); + + return Actions.ActOnOpenMPDeclareSimdDirective( + Clauses, Ptr.get().getSingleDecl(), Loc, Level == 0); + } case OMPD_unknown: Diag(Tok, diag::err_omp_unknown_directive); break; @@ -135,6 +219,70 @@ return DeclGroupPtrTy(); } +/// \brief Late parsing of declarative OpenMP directives. +/// +/// declare-simd: +/// annot_pragma_openmp 'declare simd' {<clause> [,]} +/// annot_pragma_openmp_end +/// <template function> +/// +void Parser::LateParseOpenMPDeclarativeDirective(OpenMPDirectiveKind DKind, + SourceLocation Loc) { + if (DKind == OMPD_declare_simd) { + LateParsedOpenMPDeclaration *Decl = new LateParsedOpenMPDeclaration(this); + getCurrentClass().LateParsedDeclarations.push_back(Decl); + + Token LocalTok; + LocalTok.startToken(); + LocalTok.setKind(tok::annot_pragma_openmp); + LocalTok.setLocation(Loc); + LocalTok.setAnnotationValue( + reinterpret_cast<void *>(static_cast<uintptr_t>(OMPD_declare_simd))); + Decl->Tokens.push_back(LocalTok); + + do { + while (Tok.isNot(tok::annot_pragma_openmp_end) && Tok.isNot(tok::eof)) { + Decl->Tokens.push_back(Tok); + ConsumeAnyToken(); + } + Decl->Tokens.push_back(Tok); + if (Tok.isNot(tok::eof)) + ConsumeAnyToken(); + } while (Tok.is(tok::annot_pragma_openmp)); + + LexTemplateFunctionForLateParsing(Decl->Tokens); + } +} + +/// \brief Actual parsing of late OpenMP declaration. +void Parser::LateParsedOpenMPDeclaration::ParseLexedMethodDeclarations() { + // Save the current token position. + SourceLocation OrigLoc = Self->Tok.getLocation(); + + assert(!Tokens.empty() && "Empty body!"); + // Append the current token at the end of the new token stream so that it + // doesn't get lost. + Tokens.push_back(Self->Tok); + Self->PP.EnterTokenStream(Tokens.data(), Tokens.size(), true, false); + + // Consume the previously pushed token. + Self->ConsumeAnyToken(/*ConsumeCodeCompletionTok=*/true); + + Self->ParseOpenMPDeclarativeDirective(/*IsInTagDecl=*/false); + + if (Self->Tok.getLocation() != OrigLoc) { + // Due to parsing error, we either went over the cached tokens or + // there are still cached tokens left. If it's the latter case skip the + // leftover tokens. + // Since this is an uncommon situation that should be avoided, use the + // expensive isBeforeInTranslationUnit call. + if (Self->PP.getSourceManager().isBeforeInTranslationUnit( + Self->Tok.getLocation(), OrigLoc)) + while (Self->Tok.getLocation() != OrigLoc && Self->Tok.isNot(tok::eof)) + Self->ConsumeAnyToken(); + } +} + /// \brief Parsing of declarative or executable OpenMP directives. /// /// threadprivate-directive: @@ -299,6 +447,11 @@ OMPDirectiveScope.Exit(); break; } + case OMPD_declare_simd: + Diag(Tok, diag::err_omp_unexpected_directive) + << getOpenMPDirectiveName(DKind); + SkipUntil(tok::annot_pragma_openmp_end); + break; case OMPD_unknown: Diag(Tok, diag::err_omp_unknown_directive); SkipUntil(tok::annot_pragma_openmp_end); Index: lib/Parse/ParseDeclCXX.cpp =================================================================== --- lib/Parse/ParseDeclCXX.cpp +++ lib/Parse/ParseDeclCXX.cpp @@ -2975,7 +2975,7 @@ } if (Tok.is(tok::annot_pragma_openmp)) { - ParseOpenMPDeclarativeDirective(); + ParseOpenMPDeclarativeDirective(/*IsInTagDecl=*/true); continue; } Index: lib/AST/DeclPrinter.cpp =================================================================== --- lib/AST/DeclPrinter.cpp +++ lib/AST/DeclPrinter.cpp @@ -36,6 +36,7 @@ void ProcessDeclGroup(SmallVectorImpl<Decl*>& Decls); void Print(AccessSpecifier AS); + void print(OMPDeclareSimdDeclAttr *A); /// Print an Objective-C method type in parentheses. /// @@ -407,7 +408,22 @@ } } +void DeclPrinter::print(OMPDeclareSimdDeclAttr *A) { + for (unsigned i = 0; i < A->getNumberOfDirectives(); ++i) { + A->printPrettyPragma(Out, Policy); + Indent(); + } +} + void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) { + if (auto *Attr = D->getAttr<OMPDeclareSimdDeclAttr>()) { + if (D->getTemplatedKind() != + FunctionDecl::TK_FunctionTemplateSpecialization && + D->getTemplatedKind() != FunctionDecl::TK_FunctionTemplate && + D->getTemplatedKind() != + FunctionDecl::TK_DependentFunctionTemplateSpecialization) + print(Attr); + } CXXConstructorDecl *CDecl = dyn_cast<CXXConstructorDecl>(D); CXXConversionDecl *ConversionDecl = dyn_cast<CXXConversionDecl>(D); if (!Policy.SuppressSpecifiers) { @@ -914,11 +930,15 @@ if (PrintInstantiation) { TemplateParameterList *Params = D->getTemplateParameters(); for (auto *I : D->specializations()) { + if (auto *Attr = I->getAttr<OMPDeclareSimdDeclAttr>()) + print(Attr); PrintTemplateParameters(Params, I->getTemplateSpecializationArgs()); Visit(I); } } + if (auto *Attr = D->getTemplatedDecl()->getAttr<OMPDeclareSimdDeclAttr>()) + print(Attr); return VisitRedeclarableTemplateDecl(D); } Index: lib/Sema/SemaOpenMP.cpp =================================================================== --- lib/Sema/SemaOpenMP.cpp +++ lib/Sema/SemaOpenMP.cpp @@ -1327,6 +1327,7 @@ case OMPD_cancellation_point: case OMPD_cancel: case OMPD_flush: + case OMPD_declare_simd: llvm_unreachable("OpenMP Directive is not allowed"); case OMPD_unknown: llvm_unreachable("Unknown OpenMP directive"); @@ -2117,6 +2118,7 @@ Res = ActOnOpenMPCancelDirective(StartLoc, EndLoc, CancelRegion); break; case OMPD_threadprivate: + case OMPD_declare_simd: llvm_unreachable("OpenMP Directive is not allowed"); case OMPD_unknown: llvm_unreachable("Unknown OpenMP directive"); @@ -2134,6 +2136,34 @@ return Res; } +Sema::DeclGroupPtrTy +Sema::ActOnOpenMPDeclareSimdDirective(ArrayRef<OMPClause *> Clauses, + Decl *ADecl, SourceLocation StartLoc, + bool LastDirective) { + if (auto *FTD = dyn_cast<FunctionTemplateDecl>(ADecl)) + ADecl = FTD->getTemplatedDecl(); + + if (!isa<FunctionDecl>(ADecl)) { + Diag(ADecl->getLocation(), diag::err_omp_function_expected) + << ADecl->getDeclContext()->isFileContext(); + return DeclGroupPtrTy(); + } + if (auto *Attr = ADecl->getAttr<OMPDeclareSimdDeclAttr>()) { + if (!Attr->getComplete()) { + Attr->setNumberOfDirectives(Attr->getNumberOfDirectives() + 1); + if (LastDirective) + Attr->setComplete(); + } + } else { + auto *NewAttr = new (Context) OMPDeclareSimdDeclAttr( + SourceRange(StartLoc, StartLoc), Context, 1, LastDirective, /*SI=*/0); + ADecl->addAttr(NewAttr); + if (auto *ML = Context.getASTMutationListener()) + ML->DeclarationMarkedOpenMPDeclareSimd(ADecl); + } + return ConvertDeclToDeclGroup(ADecl); +} + StmtResult Sema::ActOnOpenMPParallelDirective(ArrayRef<OMPClause *> Clauses, Stmt *AStmt, SourceLocation StartLoc, Index: lib/Basic/OpenMPKinds.cpp =================================================================== --- lib/Basic/OpenMPKinds.cpp +++ lib/Basic/OpenMPKinds.cpp @@ -338,6 +338,8 @@ break; } break; + case OMPD_declare_simd: + break; case OMPD_unknown: case OMPD_threadprivate: case OMPD_section: Index: lib/Frontend/MultiplexConsumer.cpp =================================================================== --- lib/Frontend/MultiplexConsumer.cpp +++ lib/Frontend/MultiplexConsumer.cpp @@ -127,6 +127,7 @@ const ObjCCategoryDecl *ClassExt) override; void DeclarationMarkedUsed(const Decl *D) override; void DeclarationMarkedOpenMPThreadPrivate(const Decl *D) override; + void DeclarationMarkedOpenMPDeclareSimd(const Decl *D) override; void RedefinedHiddenDefinition(const NamedDecl *D, Module *M) override; void AddedAttributeToRecord(const Attr *Attr, const RecordDecl *Record) override; @@ -223,6 +224,11 @@ for (size_t i = 0, e = Listeners.size(); i != e; ++i) Listeners[i]->DeclarationMarkedOpenMPThreadPrivate(D); } +void MultiplexASTMutationListener::DeclarationMarkedOpenMPDeclareSimd( + const Decl *D) { + for (auto *Listener : Listeners) + Listener->DeclarationMarkedOpenMPDeclareSimd(D); +} void MultiplexASTMutationListener::RedefinedHiddenDefinition(const NamedDecl *D, Module *M) { for (auto *L : Listeners) Index: include/clang/Serialization/ASTWriter.h =================================================================== --- include/clang/Serialization/ASTWriter.h +++ include/clang/Serialization/ASTWriter.h @@ -865,6 +865,7 @@ const ObjCCategoryDecl *ClassExt) override; void DeclarationMarkedUsed(const Decl *D) override; void DeclarationMarkedOpenMPThreadPrivate(const Decl *D) override; + void DeclarationMarkedOpenMPDeclareSimd(const Decl *D) override; void RedefinedHiddenDefinition(const NamedDecl *D, Module *M) override; void AddedAttributeToRecord(const Attr *Attr, const RecordDecl *Record) override; Index: include/clang/Parse/Parser.h =================================================================== --- include/clang/Parse/Parser.h +++ include/clang/Parse/Parser.h @@ -1021,6 +1021,19 @@ CachedTokens *ExceptionSpecTokens; }; + /// \brief An OpenMP declaration inside a class. + struct LateParsedOpenMPDeclaration : public LateParsedDeclaration { + explicit LateParsedOpenMPDeclaration(Parser *P) : Self(P) {} + + void ParseLexedMethodDeclarations() override; + + Parser *Self; + + /// \brief The set of tokens that make up an exception-specification that + /// has not yet been parsed. + CachedTokens Tokens; + }; + /// LateParsedMemberInitializer - An initializer for a non-static class data /// member whose parsing must to be delayed until the class is completely /// defined (C++11 [class.mem]p2). @@ -2416,7 +2429,13 @@ //===--------------------------------------------------------------------===// // OpenMP: Directives and clauses. /// \brief Parses declarative OpenMP directives. - DeclGroupPtrTy ParseOpenMPDeclarativeDirective(); + /// \param Level Current level of declarative directive, in case it is allowed + /// to apply multiple declarative directives to the same declaration. + DeclGroupPtrTy ParseOpenMPDeclarativeDirective(bool IsInTagDecl, + unsigned Level = 0); + /// \brief Late parse directive. + void LateParseOpenMPDeclarativeDirective(OpenMPDirectiveKind DKind, + SourceLocation Loc); /// \brief Parses simple list of variables. /// /// \param Kind Kind of the directive. Index: include/clang/AST/ASTMutationListener.h =================================================================== --- include/clang/AST/ASTMutationListener.h +++ include/clang/AST/ASTMutationListener.h @@ -115,6 +115,12 @@ /// \param D the declaration marked OpenMP threadprivate. virtual void DeclarationMarkedOpenMPThreadPrivate(const Decl *D) {} + /// \brief A declaration is marked as OpenMP declare simd which was not + /// previously marked as declare simd. + /// + /// \param D The declaration marked OpenMP declare simd. + virtual void DeclarationMarkedOpenMPDeclareSimd(const Decl *D) {} + /// \brief A definition has been made visible by being redefined locally. /// /// \param D The definition that was previously not visible. Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -144,6 +144,7 @@ class ObjCPropertyDecl; class ObjCProtocolDecl; class OMPThreadPrivateDecl; + class OMPDeclareSimdDecl; class OMPClause; class OverloadCandidateSet; class OverloadExpr; @@ -7869,6 +7870,13 @@ SourceLocation EndLoc, OpenMPDirectiveKind CancelRegion); + /// \brief Called on well-formed '\#pragma omp declare simd' after parsing of + /// the associated method/function. + DeclGroupPtrTy ActOnOpenMPDeclareSimdDirective(ArrayRef<OMPClause *> Clauses, + Decl *ADecl, + SourceLocation StartLoc, + bool LastDirective); + OMPClause *ActOnOpenMPSingleExprClause(OpenMPClauseKind Kind, Expr *Expr, SourceLocation StartLoc, Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -7637,6 +7637,8 @@ "the 'copyprivate' clause must not be used with the 'nowait' clause">; def note_omp_nowait_clause_here : Note< "'nowait' clause is here">; +def err_omp_function_expected : Error< + "'#pragma omp declare simd' can be applied to functions only">; def err_omp_wrong_cancel_region : Error< "one of 'for', 'parallel', 'sections' or 'taskgroup' is expected">; def err_omp_parent_cancel_region_nowait : Error< Index: include/clang/Basic/DiagnosticParseKinds.td =================================================================== --- include/clang/Basic/DiagnosticParseKinds.td +++ include/clang/Basic/DiagnosticParseKinds.td @@ -989,6 +989,8 @@ "'#pragma omp %0' cannot be an immediate substatement">; def err_omp_expected_identifier_for_critical : Error< "expected identifier specifying the name of the 'omp critical' directive">; +def err_omp_single_decl_in_declare_simd : Error< + "single declaration is expected with 'declare simd' directive">; // Pragma loop support. def err_pragma_loop_missing_argument : Error< Index: include/clang/Basic/AttrDocs.td =================================================================== --- include/clang/Basic/AttrDocs.td +++ include/clang/Basic/AttrDocs.td @@ -1583,3 +1583,33 @@ The ``returns_nonnull`` attribute implies that returning a null pointer is undefined behavior, which the optimizer may take advantage of. The ``_Nonnull`` type qualifier indicates that a pointer cannot be null in a more general manner (because it is part of the type system) and does not imply undefined behavior, making it more widely applicable }]; } + +def OMPDeclareSimdDocs : Documentation { + let Category = DocCatStmt; + let Heading = "#pragma omp declare simd"; + let Content = [{ +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. The declare simd directive is a declarative directive. There may be multiple declare simd directives for a function. The use of a declare simd construct on a function enables the creation of SIMD +versions of the associated function that can be used to process multiple arguments from a single invocation from a SIMD loop concurrently. +The syntax of the declare simd construct is as follows: + + .. code-block:: c + + #pragma omp declare simd [clause[[,] clause] ...] new-line + [#pragma omp declare simd [clause[[,] clause] ...] new-line] + [...] + function definition or declaration + +where clause is one of the following: + + .. code-block:: c + + simdlen(length) + linear(argument-list[:constant-linear-step]) + aligned(argument-list[:alignment]) + uniform(argument-list) + inbranch + notinbranch + + }]; +} + Index: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -2054,3 +2054,21 @@ let SemaHandler = 0; let Documentation = [Undocumented]; } + +def OMPDeclareSimdDecl : InheritableAttr { + // This attribute has no spellings as it is only ever created implicitly. + let Spellings = []; + let Subjects = SubjectList<[Function]>; + let SemaHandler = 0; + let HasCustomParsing = 1; + let Documentation = [OMPDeclareSimdDocs]; + let Args = [UnsignedArgument<"NumberOfDirectives">, BoolArgument<"Complete">]; + let AdditionalMembers = [{ + void printPrettyPragma(raw_ostream &OS, const PrintingPolicy &Policy) const { + OS << "#pragma omp declare simd\n"; + } + void setNumberOfDirectives(unsigned N) { numberOfDirectives = N; } + void setComplete() { complete = true; } + }]; +} + Index: include/clang/Basic/OpenMPKinds.def =================================================================== --- include/clang/Basic/OpenMPKinds.def +++ include/clang/Basic/OpenMPKinds.def @@ -99,6 +99,7 @@ OPENMP_DIRECTIVE_EXT(parallel_sections, "parallel sections") OPENMP_DIRECTIVE_EXT(for_simd, "for simd") OPENMP_DIRECTIVE_EXT(cancellation_point, "cancellation point") +OPENMP_DIRECTIVE_EXT(declare_simd, "declare simd") // OpenMP clauses. OPENMP_CLAUSE(if, OMPIfClause) Index: test/OpenMP/declare_simd_ast_print.c =================================================================== --- test/OpenMP/declare_simd_ast_print.c +++ test/OpenMP/declare_simd_ast_print.c @@ -0,0 +1,17 @@ +// RUN: %clang_cc1 -verify -fopenmp -ast-print %s | FileCheck %s +// RUN: %clang_cc1 -fopenmp -emit-pch -o %t %s +// RUN: %clang_cc1 -fopenmp -include-pch %t -fsyntax-only -verify %s -ast-print | FileCheck %s +// expected-no-diagnostics + +#ifndef HEADER +#define HEADER + +#pragma omp declare simd +#pragma omp declare simd +void add_1(float *d, float *s1, float *s2); + +// CHECK: #pragma omp declare simd +// CHECK-NEXT: #pragma omp declare simd +// CHECK-NEXT: void add_1(float *d, float *s1, float *s2) + +#endif Index: test/OpenMP/declare_simd_ast_print.cpp =================================================================== --- test/OpenMP/declare_simd_ast_print.cpp +++ test/OpenMP/declare_simd_ast_print.cpp @@ -0,0 +1,103 @@ +// RUN: %clang_cc1 -verify -fopenmp -x c++ -std=c++11 -ast-print %s | FileCheck %s +// RUN: %clang_cc1 -fopenmp -x c++ -std=c++11 -emit-pch -o %t %s +// RUN: %clang_cc1 -fopenmp -x c++ -std=c++11 -include-pch %t -fsyntax-only -verify %s -ast-print | FileCheck %s +// expected-no-diagnostics + +#ifndef HEADER +#define HEADER + +#pragma omp declare simd +void add_1(float *d); + +// CHECK: #pragma omp declare simd +// CHECK-NEXT: void add_1(float *d); +// + +#pragma omp declare simd +template <class C> void h(C *hp, C *hp2, C *hq, C *lin) { +} + +// CHECK: #pragma omp declare simd +// CHECK-NEXT: template <class C = int> void h(int *hp, int *hp2, int *hq, int *lin) { +// CHECK-NEXT: h((float *)hp, (float *)hp2, (float *)hq, (float *)lin); +// CHECK-NEXT: } + +// CHECK: #pragma omp declare simd +// CHECK-NEXT: template <class C = float> void h(float *hp, float *hp2, float *hq, float *lin) { +// CHECK-NEXT: } + +// CHECK: #pragma omp declare simd +// CHECK: template <class C> void h(C *hp, C *hp2, C *hq, C *lin) { +// CHECK-NEXT: } +// + +// Instatiate with <C=int> explicitly. +// Pragmas need to be same, otherwise standard says that's undefined behavior. +#pragma omp declare simd +template <> +void h(int *hp, int *hp2, int *hq, int *lin) +{ + // Instatiate with <C=float> implicitly. + // This is special case where the directive is stored by Sema and is + // generated together with the (pending) function instatiation. + h((float*) hp, (float*) hp2, (float*) hq, (float*) lin); +} + +class VV { + // CHECK: #pragma omp declare simd + // CHECK-NEXT: int add(int a, int b) { + // CHECK-NEXT: return a + b; + // CHECK-NEXT: } + #pragma omp declare simd + int add(int a, int b) { return a + b; } + + // CHECK: #pragma omp declare simd + // CHECK-NEXT: #pragma omp declare simd + // CHECK-NEXT: float addpf(float *a, float *b) { + // CHECK-NEXT: return *a + *b; + // CHECK-NEXT: } + #pragma omp declare simd + #pragma omp declare simd + float addpf(float *a, float *b) { return *a + *b; } +}; + +// CHECK: template <int X = 16> class TVV { +// CHECK: #pragma omp declare simd +// CHECK-NEXT: int tadd(int a, int b); +// CHECK: #pragma omp declare simd +// CHECK-NEXT: float taddpf(float *a, float *b) { +// CHECK-NEXT: return *a + *b; +// CHECK-NEXT: } +// CHECK: } +template <int X> +class TVV { +public: +// CHECK: template <int X> class TVV { + #pragma omp declare simd + int tadd(int a, int b) { return a + b; } + +// CHECK: #pragma omp declare simd +// CHECK-NEXT: int tadd(int a, int b) { +// CHECK-NEXT: return a + b; +// CHECK-NEXT: } + + + #pragma omp declare simd + float taddpf(float *a, float *b) { return *a + *b; } + +// CHECK: #pragma omp declare simd +// CHECK-NEXT: float taddpf(float *a, float *b) { +// CHECK-NEXT: return *a + *b; +// CHECK-NEXT: } +}; +// CHECK: }; + +// CHECK: TVV<16> t16; +TVV<16> t16; + +void f() { + float a = 1.0f, b = 2.0f; + float r = t16.taddpf(&a, &b); +} + +#endif Index: test/OpenMP/declare_simd_messages.cpp =================================================================== --- test/OpenMP/declare_simd_messages.cpp +++ test/OpenMP/declare_simd_messages.cpp @@ -0,0 +1,48 @@ +// RUN: %clang_cc1 -triple x86_64-apple-macos10.7.0 -verify -fopenmp -x c++ -std=c++11 %s + +// expected-error@+1 {{expected an OpenMP directive}} +#pragma omp declare + +// expected-error@+2 {{'#pragma omp declare simd' can be applied to functions only}} +#pragma omp declare simd +int a; +// expected-error@+2 {{'#pragma omp declare simd' can be applied to functions only}} +#pragma omp declare simd +#pragma omp threadprivate(a) +int var; +#pragma omp threadprivate(var) + +// expected-error@+2 {{expected an OpenMP directive}} +#pragma omp declare simd +#pragma omp declare + +#pragma omp declare simd +#pragma omp declare simd +int main(); + +#pragma omp declare simd +template <class C> +void h(C *hp, C *hp2, C *hq, C *lin) { +} + +#pragma omp declare simd +template <> +void h(int *hp, int *hp2, int *hq, int *lin) { + h((float *)hp, (float *)hp2, (float *)hq, (float *)lin); +} + +template <class T> +struct St { +#pragma omp declare simd + void h(T *hp) { +// expected-error@+1 {{unexpected OpenMP directive '#pragma omp declare simd'}} +#pragma omp declare simd + *hp = *t; + } + +private: + T t; +}; +#pragma omp declare simd +// expected-error@+1 {{expected external declaration}} +#pragma omp declare simd
_______________________________________________ cfe-commits mailing list cfe-commits@cs.uiuc.edu http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits