Index: cfe/trunk/include/clang/Basic/Attr.td =================================================================== --- cfe/trunk/include/clang/Basic/Attr.td +++ cfe/trunk/include/clang/Basic/Attr.td @@ -2244,6 +2244,17 @@ let Documentation = [Undocumented]; } +def OMPDeclareSimdDecl : Attr { + let Spellings = [Pragma<"omp", "declare simd">]; + let Subjects = SubjectList<[Function]>; + let SemaHandler = 0; + let HasCustomParsing = 1; + let Documentation = [OMPDeclareSimdDocs]; + let AdditionalMembers = [{ + void printPrettyPragma(raw_ostream &OS, const PrintingPolicy &Policy) const {} + }]; +} + def InternalLinkage : InheritableAttr { let Spellings = [GNU<"internal_linkage">, CXX11<"clang", "internal_linkage">]; let Subjects = SubjectList<[Var, Function, CXXRecord]>; Index: cfe/trunk/include/clang/Basic/AttrDocs.td =================================================================== --- cfe/trunk/include/clang/Basic/AttrDocs.td +++ cfe/trunk/include/clang/Basic/AttrDocs.td @@ -1888,6 +1888,41 @@ }]; } +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 in 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 + + }]; +} + def NotTailCalledDocs : Documentation { let Category = DocCatFunction; let Content = [{ Index: cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td =================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td +++ cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td @@ -953,6 +953,8 @@ "expected identifier specifying the name of the 'omp critical' directive">; def err_omp_expected_reduction_identifier : Error< "expected identifier or one of the following operators: '+', '-', '*', '&', '|', '^', '&&', or '||'">; +def err_omp_decl_in_declare_simd : Error< + "function declaration is expected after 'declare simd' directive">; def err_omp_unknown_map_type : Error< "incorrect map type, expected one of 'to', 'from', 'tofrom', 'alloc', 'release', or 'delete'">; def err_omp_unknown_map_type_modifier : Error< Index: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td @@ -8039,6 +8039,10 @@ "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_single_decl_in_declare_simd : Error< + "single declaration is expected after 'declare simd' directive">; +def err_omp_function_expected : Error< + "'#pragma omp declare simd' can only be applied to functions">; 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: cfe/trunk/include/clang/Basic/OpenMPKinds.def =================================================================== --- cfe/trunk/include/clang/Basic/OpenMPKinds.def +++ cfe/trunk/include/clang/Basic/OpenMPKinds.def @@ -156,6 +156,7 @@ OPENMP_DIRECTIVE_EXT(for_simd, "for simd") OPENMP_DIRECTIVE_EXT(cancellation_point, "cancellation point") OPENMP_DIRECTIVE_EXT(declare_reduction, "declare reduction") +OPENMP_DIRECTIVE_EXT(declare_simd, "declare simd") OPENMP_DIRECTIVE(taskloop) OPENMP_DIRECTIVE_EXT(taskloop_simd, "taskloop simd") OPENMP_DIRECTIVE(distribute) Index: cfe/trunk/include/clang/Parse/Parser.h =================================================================== --- cfe/trunk/include/clang/Parse/Parser.h +++ cfe/trunk/include/clang/Parse/Parser.h @@ -2428,7 +2428,7 @@ ParsingDeclRAIIObject *DiagsFromTParams = nullptr); DeclGroupPtrTy ParseCXXClassMemberDeclarationWithPragmas( AccessSpecifier &AS, ParsedAttributesWithRange &AccessAttrs, - DeclSpec::TST TagType, Decl *TagDecl); + DeclSpec::TST TagType, Decl *Tag); void ParseConstructorInitializer(Decl *ConstructorDecl); MemInitResult ParseMemInitializer(Decl *ConstructorDecl); void HandleMemberFunctionDeclDelays(Declarator& DeclaratorInfo, @@ -2457,7 +2457,10 @@ //===--------------------------------------------------------------------===// // OpenMP: Directives and clauses. /// \brief Parses declarative OpenMP directives. - DeclGroupPtrTy ParseOpenMPDeclarativeDirective(AccessSpecifier AS); + DeclGroupPtrTy ParseOpenMPDeclarativeDirectiveWithExtDecl( + AccessSpecifier &AS, ParsedAttributesWithRange &Attrs, + DeclSpec::TST TagType = DeclSpec::TST_unspecified, + Decl *TagDecl = nullptr); /// \brief Parse 'omp declare reduction' construct. DeclGroupPtrTy ParseOpenMPDeclareReductionDirective(AccessSpecifier AS); /// \brief Parses simple list of variables. Index: cfe/trunk/include/clang/Sema/Sema.h =================================================================== --- cfe/trunk/include/clang/Sema/Sema.h +++ cfe/trunk/include/clang/Sema/Sema.h @@ -146,6 +146,7 @@ class ObjCProtocolDecl; class OMPThreadPrivateDecl; class OMPDeclareReductionDecl; + class OMPDeclareSimdDecl; class OMPClause; struct OverloadCandidate; class OverloadCandidateSet; @@ -8087,6 +8088,11 @@ SourceLocation EndLoc, llvm::DenseMap &VarsWithImplicitDSA); + /// \brief Called on well-formed '\#pragma omp declare simd' after parsing of + /// the associated method/function. + DeclGroupPtrTy ActOnOpenMPDeclareSimdDirective(DeclGroupPtrTy DG, + SourceLocation StartLoc); + OMPClause *ActOnOpenMPSingleExprClause(OpenMPClauseKind Kind, Expr *Expr, SourceLocation StartLoc, Index: cfe/trunk/lib/Basic/OpenMPKinds.cpp =================================================================== --- cfe/trunk/lib/Basic/OpenMPKinds.cpp +++ cfe/trunk/lib/Basic/OpenMPKinds.cpp @@ -485,6 +485,8 @@ break; } break; + case OMPD_declare_simd: + break; case OMPD_cancel: switch (CKind) { #define OPENMP_CANCEL_CLAUSE(Name) \ Index: cfe/trunk/lib/Parse/ParseDecl.cpp =================================================================== --- cfe/trunk/lib/Parse/ParseDecl.cpp +++ cfe/trunk/lib/Parse/ParseDecl.cpp @@ -3654,8 +3654,10 @@ } if (Tok.is(tok::annot_pragma_openmp)) { - // There may be declared reduction operator inside structure/union. - (void)ParseOpenMPDeclarativeDirective(AS_public); + // Result can be ignored, because it must be always empty. + AccessSpecifier AS = AS_none; + ParsedAttributesWithRange Attrs(AttrFactory); + (void)ParseOpenMPDeclarativeDirectiveWithExtDecl(AS, Attrs); continue; } Index: cfe/trunk/lib/Parse/ParseDeclCXX.cpp =================================================================== --- cfe/trunk/lib/Parse/ParseDeclCXX.cpp +++ cfe/trunk/lib/Parse/ParseDeclCXX.cpp @@ -2919,7 +2919,8 @@ } if (Tok.is(tok::annot_pragma_openmp)) - return ParseOpenMPDeclarativeDirective(AS); + return ParseOpenMPDeclarativeDirectiveWithExtDecl(AS, AccessAttrs, TagType, + TagDecl); // Parse all the comma separated declarators. return ParseCXXClassMemberDeclaration(AS, AccessAttrs.getList()); Index: cfe/trunk/lib/Parse/ParseOpenMP.cpp =================================================================== --- cfe/trunk/lib/Parse/ParseOpenMP.cpp +++ cfe/trunk/lib/Parse/ParseOpenMP.cpp @@ -65,6 +65,7 @@ static const unsigned F[][3] = { { OMPD_cancellation, OMPD_point, OMPD_cancellation_point }, { OMPD_declare, OMPD_reduction, OMPD_declare_reduction }, + { OMPD_declare, OMPD_simd, OMPD_declare_simd }, { OMPD_target, OMPD_data, OMPD_target_data }, { OMPD_target, OMPD_enter, OMPD_target_enter }, { OMPD_target, OMPD_exit, OMPD_target_exit }, @@ -332,8 +333,14 @@ /// annot_pragma_openmp 'declare' 'reduction' [...] /// annot_pragma_openmp_end /// -Parser::DeclGroupPtrTy -Parser::ParseOpenMPDeclarativeDirective(AccessSpecifier AS) { +/// declare-simd-directive: +/// annot_pragma_openmp 'declare simd' { [,]} +/// annot_pragma_openmp_end +/// +/// +Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirectiveWithExtDecl( + AccessSpecifier &AS, ParsedAttributesWithRange &Attrs, + DeclSpec::TST TagType, Decl *Tag) { assert(Tok.is(tok::annot_pragma_openmp) && "Not an OpenMP directive!"); ParenBraceBracketBalancer BalancerRAIIObj(*this); @@ -373,6 +380,47 @@ return Res; } break; + case OMPD_declare_simd: { + // The syntax is: + // { #pragma omp declare simd } + // + // + + ConsumeToken(); + // The last seen token is annot_pragma_openmp_end - need to check for + // extra tokens. + if (Tok.isNot(tok::annot_pragma_openmp_end)) { + Diag(Tok, diag::warn_omp_extra_tokens_at_eol) + << getOpenMPDirectiveName(OMPD_declare_simd); + while (Tok.isNot(tok::annot_pragma_openmp_end)) + ConsumeAnyToken(); + } + // Skip the last annot_pragma_openmp_end. + ConsumeToken(); + + DeclGroupPtrTy Ptr; + if (Tok.is(tok::annot_pragma_openmp)) { + Ptr = ParseOpenMPDeclarativeDirectiveWithExtDecl(AS, Attrs, TagType, Tag); + } else if (Tok.isNot(tok::r_brace) && !isEofOrEom()) { + // Here we expect to see some function declaration. + if (AS == AS_none) { + assert(TagType == DeclSpec::TST_unspecified); + MaybeParseCXX11Attributes(Attrs); + MaybeParseMicrosoftAttributes(Attrs); + ParsingDeclSpec PDS(*this); + Ptr = ParseExternalDeclaration(Attrs, &PDS); + } else { + Ptr = + ParseCXXClassMemberDeclarationWithPragmas(AS, Attrs, TagType, Tag); + } + } + if (!Ptr) { + Diag(Loc, diag::err_omp_decl_in_declare_simd); + return DeclGroupPtrTy(); + } + + return Actions.ActOnOpenMPDeclareSimdDirective(Ptr, Loc); + } case OMPD_unknown: Diag(Tok, diag::err_omp_unknown_directive); break; @@ -627,6 +675,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: cfe/trunk/lib/Parse/Parser.cpp =================================================================== --- cfe/trunk/lib/Parse/Parser.cpp +++ cfe/trunk/lib/Parse/Parser.cpp @@ -659,8 +659,10 @@ case tok::annot_pragma_opencl_extension: HandlePragmaOpenCLExtension(); return nullptr; - case tok::annot_pragma_openmp: - return ParseOpenMPDeclarativeDirective(/*AS=*/AS_none); + case tok::annot_pragma_openmp: { + AccessSpecifier AS = AS_none; + return ParseOpenMPDeclarativeDirectiveWithExtDecl(AS, attrs); + } case tok::annot_pragma_ms_pointers_to_members: HandlePragmaMSPointersToMembers(); return nullptr; Index: cfe/trunk/lib/Sema/SemaOpenMP.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaOpenMP.cpp +++ cfe/trunk/lib/Sema/SemaOpenMP.cpp @@ -1701,6 +1701,7 @@ case OMPD_target_enter_data: case OMPD_target_exit_data: case OMPD_declare_reduction: + case OMPD_declare_simd: llvm_unreachable("OpenMP Directive is not allowed"); case OMPD_unknown: llvm_unreachable("Unknown OpenMP directive"); @@ -3155,6 +3156,7 @@ break; case OMPD_threadprivate: case OMPD_declare_reduction: + case OMPD_declare_simd: llvm_unreachable("OpenMP Directive is not allowed"); case OMPD_unknown: llvm_unreachable("Unknown OpenMP directive"); @@ -3175,6 +3177,32 @@ return Res; } +Sema::DeclGroupPtrTy +Sema::ActOnOpenMPDeclareSimdDirective(DeclGroupPtrTy DG, + SourceLocation StartLoc) { + if (!DG || DG.get().isNull()) + return DeclGroupPtrTy(); + + if (!DG.get().isSingleDecl()) { + Diag(StartLoc, diag::err_omp_single_decl_in_declare_simd); + return DG; + } + auto *ADecl = DG.get().getSingleDecl(); + if (auto *FTD = dyn_cast(ADecl)) + ADecl = FTD->getTemplatedDecl(); + + if (!isa(ADecl)) { + Diag(ADecl->getLocation(), diag::err_omp_function_expected) + << ADecl->getDeclContext()->isFileContext(); + return DeclGroupPtrTy(); + } + + auto *NewAttr = OMPDeclareSimdDeclAttr::CreateImplicit( + Context, SourceRange(StartLoc, StartLoc)); + ADecl->addAttr(NewAttr); + return ConvertDeclToDeclGroup(ADecl); +} + StmtResult Sema::ActOnOpenMPParallelDirective(ArrayRef Clauses, Stmt *AStmt, SourceLocation StartLoc, Index: cfe/trunk/test/OpenMP/declare_simd_ast_print.c =================================================================== --- cfe/trunk/test/OpenMP/declare_simd_ast_print.c +++ cfe/trunk/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) __attribute__((cold)); + +// CHECK: #pragma omp declare simd +// CHECK-NEXT: #pragma omp declare simd +// CHECK-NEXT: void add_1(float *d, float *s1, float *s2) __attribute__((cold)) + +#endif Index: cfe/trunk/test/OpenMP/declare_simd_ast_print.cpp =================================================================== --- cfe/trunk/test/OpenMP/declare_simd_ast_print.cpp +++ cfe/trunk/test/OpenMP/declare_simd_ast_print.cpp @@ -0,0 +1,131 @@ +// 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) __attribute__((cold)); + +// CHECK: #pragma omp declare simd +// CHECK-NEXT: void add_1(float *d) __attribute__((cold)); +// + +#pragma omp declare simd +template void h(C *hp, C *hp2, C *hq, C *lin) { +} + +// CHECK: #pragma omp declare simd +// CHECK-NEXT: template 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 void h(float *hp, float *hp2, float *hq, float *lin) { +// CHECK-NEXT: } + +// CHECK: #pragma omp declare simd +// CHECK: template void h(C *hp, C *hp2, C *hq, C *lin) { +// CHECK-NEXT: } +// + +// Explicit specialization with . +// 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) +{ + // Implicit specialization with . + // 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) __attribute__((cold)) { + // CHECK-NEXT: return a + b; + // CHECK-NEXT: } + #pragma omp declare simd + int add(int a, int b) __attribute__((cold)) { return a + b; } + + // CHECK: #pragma omp declare simd + // CHECK-NEXT: float taddpf(float *a, float *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: #pragma omp declare simd +// CHECK-NEXT: int tadd(int b) { +// CHECK-NEXT: return this->x[b] + b; +// CHECK-NEXT: } + #pragma omp declare simd + #pragma omp declare simd + int tadd(int b) { return x[b] + b; } + +private: + int x[10]; +}; + +// CHECK: template 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: #pragma omp declare simd +// CHECK-NEXT: #pragma omp declare simd +// CHECK-NEXT: int tadd(int b) { +// CHECK-NEXT: return this->x[b] + b; +// CHECK-NEXT: } +// CHECK: } +template +class TVV { +public: +// CHECK: template 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: } + + #pragma omp declare simd + #pragma omp declare simd + int tadd(int b) { return x[b] + b; } + +// CHECK: #pragma omp declare simd +// CHECK-NEXT: #pragma omp declare simd +// CHECK-NEXT: int tadd(int b) { +// CHECK-NEXT: return this->x[b] + b; +// CHECK-NEXT: } + +private: + int x[X]; +}; +// CHECK: }; + +// CHECK: TVV<16> t16; +TVV<16> t16; + +void f() { + float a = 1.0f, b = 2.0f; + float r = t16.taddpf(&a, &b); + int res = t16.tadd(b); +} + +#endif Index: cfe/trunk/test/OpenMP/declare_simd_messages.cpp =================================================================== --- cfe/trunk/test/OpenMP/declare_simd_messages.cpp +++ cfe/trunk/test/OpenMP/declare_simd_messages.cpp @@ -0,0 +1,72 @@ +// RUN: %clang_cc1 -triple=x86_64-pc-win32 -verify -fopenmp -x c++ -std=c++11 -fms-extensions %s + +// expected-error@+1 {{expected an OpenMP directive}} +#pragma omp declare + +// expected-error@+2 {{'#pragma omp declare simd' can only be applied to functions}} +#pragma omp declare simd +int a; +// expected-error@+2 {{'#pragma omp declare simd' can only be applied to functions}} +#pragma omp declare simd +#pragma omp threadprivate(a) +int var; +#pragma omp threadprivate(var) + +// expected-error@+2 {{expected an OpenMP directive}} expected-error@+1 {{function declaration is expected after 'declare simd' directive}} +#pragma omp declare simd +#pragma omp declare + +// expected-error@+3 {{function declaration is expected after 'declare simd' directive}} +// expected-error@+1 {{function declaration is expected after 'declare simd' directive}} +#pragma omp declare simd +#pragma omp declare simd +#pragma options align=packed +int main(); + +// expected-error@+3 {{function declaration is expected after 'declare simd' directive}} +// expected-error@+1 {{function declaration is expected after 'declare simd' directive}} +#pragma omp declare simd +#pragma omp declare simd +#pragma init_seg(compiler) +int main(); + +// expected-error@+1 {{single declaration is expected after 'declare simd' directive}} +#pragma omp declare simd +int b, c; + +#pragma omp declare simd +template +void h(C *hp, C *hp2, C *hq, C *lin) { + b = 0; +} + +#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 +struct St { +// expected-error@+2 {{function declaration is expected after 'declare simd' directive}} +#pragma init_seg(compiler) +#pragma omp declare simd +#pragma init_seg(compiler) + void h(T *hp) { +// expected-error@+1 {{unexpected OpenMP directive '#pragma omp declare simd'}} +#pragma omp declare simd + *hp = *t; + } + +private: + T t; +}; + +namespace N { + // expected-error@+1 {{function declaration is expected after 'declare simd' directive}} + #pragma omp declare simd +} +// expected-error@+1 {{function declaration is expected after 'declare simd' directive}} +#pragma omp declare simd +// expected-error@+1 {{function declaration is expected after 'declare simd' directive}} +#pragma omp declare simd