From: Dmitry Polukhin Date: Wed, 6 Apr 2016 11:38:59 +0000 (+0000) Subject: [OPENMP] Parsing and Sema support for 'omp declare target' directive X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=6b41e0a3104cbc911323f5349bbc39ca8988e1b4;p=clang [OPENMP] Parsing and Sema support for 'omp declare target' directive Add parsing, sema analysis for 'declare target' construct for OpenMP 4.0 (4.5 support will be added in separate patch). The declare target directive specifies that variables, functions (C, C++ and Fortran), and subroutines (Fortran) are mapped to a device. The declare target directive is a declarative directive. In Clang declare target is implemented as implicit attribute for the declaration. The syntax of the declare target directive is as follows: #pragma omp declare target declarations-definition-seq #pragma omp end declare target Based on patch from Michael Wong http://reviews.llvm.org/D15321 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@265530 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/ASTMutationListener.h b/include/clang/AST/ASTMutationListener.h index cf3b55d7b2..85896cf8e7 100644 --- a/include/clang/AST/ASTMutationListener.h +++ b/include/clang/AST/ASTMutationListener.h @@ -107,6 +107,12 @@ public: /// \param D the declaration marked OpenMP threadprivate. virtual void DeclarationMarkedOpenMPThreadPrivate(const Decl *D) {} + /// \brief A declaration is marked as OpenMP declaretarget which was not + /// previously marked as declaretarget. + /// + /// \param D the declaration marked OpenMP declaretarget. + virtual void DeclarationMarkedOpenMPDeclareTarget(const Decl *D) {} + /// \brief A definition has been made visible by being redefined locally. /// /// \param D The definition that was previously not visible. diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index 3dd83dcfe3..c7c54c75ef 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -2276,6 +2276,15 @@ def OMPDeclareSimdDecl : Attr { }]; } +def OMPDeclareTargetDecl : Attr { + let Spellings = [Pragma<"omp", "declare target">]; + let SemaHandler = 0; + let Documentation = [OMPDeclareTargetDocs]; + 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]>; diff --git a/include/clang/Basic/AttrDocs.td b/include/clang/Basic/AttrDocs.td index 426c019dd9..1c6396eb3c 100644 --- a/include/clang/Basic/AttrDocs.td +++ b/include/clang/Basic/AttrDocs.td @@ -1941,6 +1941,23 @@ where clause is one of the following: }]; } +def OMPDeclareTargetDocs : Documentation { + let Category = DocCatFunction; + let Heading = "#pragma omp declare target"; + let Content = [{ +The `declare target` directive specifies that variables and functions are mapped +to a device for OpenMP offload mechanism. + +The syntax of the declare target directive is as follows: + + .. code-block:: c + + #pragma omp declare target new-line + declarations-definition-seq + #pragma omp end declare target new-line + }]; +} + def NotTailCalledDocs : Documentation { let Category = DocCatFunction; let Content = [{ diff --git a/include/clang/Basic/DiagnosticGroups.td b/include/clang/Basic/DiagnosticGroups.td index e5d8794b55..ddfccfb415 100644 --- a/include/clang/Basic/DiagnosticGroups.td +++ b/include/clang/Basic/DiagnosticGroups.td @@ -819,6 +819,7 @@ def ASM : DiagGroup<"asm", [ def SourceUsesOpenMP : DiagGroup<"source-uses-openmp">; def OpenMPClauses : DiagGroup<"openmp-clauses">; def OpenMPLoopForm : DiagGroup<"openmp-loop-form">; +def OpenMPTarget : DiagGroup<"openmp-target">; // Backend warnings. def BackendInlineAsm : DiagGroup<"inline-asm">; diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td index 4c28f0e29f..cde0308816 100644 --- a/include/clang/Basic/DiagnosticParseKinds.td +++ b/include/clang/Basic/DiagnosticParseKinds.td @@ -963,6 +963,8 @@ def err_omp_map_type_missing : Error< "missing map type">; def err_omp_declare_simd_inbranch_notinbranch : Error< "unexpected '%0' clause, '%1' is specified already">; +def err_expected_end_declare_target : Error< + "expected '#pragma omp end declare target'">; // Pragma loop support. def err_pragma_loop_missing_argument : Error< diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 35adb6e8d4..6f4c863a0a 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -7828,6 +7828,8 @@ def err_omp_global_var_arg : Error< "arguments of '#pragma omp %0' must have %select{global storage|static storage duration}1">; def err_omp_ref_type_arg : Error< "arguments of '#pragma omp %0' cannot be of reference type %1">; +def err_omp_region_not_file_context : Error< + "directive must be at file or namespace scope">; def err_omp_var_scope : Error< "'#pragma omp %0' must appear in the scope of the %q1 variable declaration">; def err_omp_var_used : Error< @@ -7903,6 +7905,8 @@ def err_omp_negative_expression_in_clause : Error< def err_omp_not_integral : Error< "expression must have integral or unscoped enumeration " "type, not %0">; +def err_omp_threadprivate_in_target : Error< + "threadprivate variables cannot be used in target constructs">; def err_omp_incomplete_type : Error< "expression has incomplete class type %0">; def err_omp_explicit_conversion : Error< @@ -7929,6 +7933,11 @@ def warn_omp_linear_step_zero : Warning< def warn_omp_alignment_not_power_of_two : Warning< "aligned clause will be ignored because the requested alignment is not a power of 2">, InGroup; +def err_omp_enclosed_declare_target : Error< + "declare target region may not be enclosed within another declare target region">; +def warn_omp_not_in_target_context : Warning< + "declaration is not declared in any declare target region">, + InGroup; def err_omp_aligned_expected_array_or_ptr : Error< "argument of aligned clause should be array" "%select{ or pointer|, pointer, reference to array or reference to pointer}1" diff --git a/include/clang/Basic/OpenMPKinds.def b/include/clang/Basic/OpenMPKinds.def index b2d2af6f07..cff09568bb 100644 --- a/include/clang/Basic/OpenMPKinds.def +++ b/include/clang/Basic/OpenMPKinds.def @@ -160,6 +160,8 @@ OPENMP_DIRECTIVE_EXT(declare_simd, "declare simd") OPENMP_DIRECTIVE(taskloop) OPENMP_DIRECTIVE_EXT(taskloop_simd, "taskloop simd") OPENMP_DIRECTIVE(distribute) +OPENMP_DIRECTIVE_EXT(declare_target, "declare target") +OPENMP_DIRECTIVE_EXT(end_declare_target, "end declare target") // OpenMP clauses. OPENMP_CLAUSE(if, OMPIfClause) diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index e58013856b..92d61317d1 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -7828,6 +7828,8 @@ public: // private: void *VarDataSharingAttributesStack; + /// Set to true inside '#pragma omp declare target' region. + bool IsInOpenMPDeclareTargetContext = false; /// \brief Initialization of data-sharing attributes stack. void InitDataSharingAttributesStack(); void DestroyDataSharingAttributesStack(); @@ -7913,6 +7915,17 @@ public: DeclGroupPtrTy ActOnOpenMPDeclareReductionDirectiveEnd( Scope *S, DeclGroupPtrTy DeclReductions, bool IsValid); + /// Called on the start of target region i.e. '#pragma omp declare target'. + bool ActOnStartOpenMPDeclareTargetDirective(SourceLocation Loc); + /// Called at the end of target region i.e. '#pragme omp end declare target'. + void ActOnFinishOpenMPDeclareTargetDirective(); + /// Check declaration inside target region. + void checkDeclIsAllowedInOpenMPTarget(Expr *E, Decl *D); + /// Return true inside OpenMP target region. + bool isInOpenMPDeclareTargetContext() const { + return IsInOpenMPDeclareTargetContext; + } + /// \brief Initialization of captured region for OpenMP region. void ActOnOpenMPRegionStart(OpenMPDirectiveKind DKind, Scope *CurScope); /// \brief End of OpenMP region. diff --git a/include/clang/Serialization/ASTWriter.h b/include/clang/Serialization/ASTWriter.h index 2cf1658c14..35e0360834 100644 --- a/include/clang/Serialization/ASTWriter.h +++ b/include/clang/Serialization/ASTWriter.h @@ -861,6 +861,7 @@ public: const ObjCInterfaceDecl *IFD) override; void DeclarationMarkedUsed(const Decl *D) override; void DeclarationMarkedOpenMPThreadPrivate(const Decl *D) override; + void DeclarationMarkedOpenMPDeclareTarget(const Decl *D) override; void RedefinedHiddenDefinition(const NamedDecl *D, Module *M) override; void AddedAttributeToRecord(const Attr *Attr, const RecordDecl *Record) override; diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index aa9277cdc1..3a55e2310c 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -8528,6 +8528,9 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) { return false; } else if (isa(D)) return true; + else if (isa(D) || + D->hasAttr()) + return true; else if (isa(D)) return true; else if (isa(D)) diff --git a/lib/AST/DeclPrinter.cpp b/lib/AST/DeclPrinter.cpp index f7a8eb2729..9d76f65b62 100644 --- a/lib/AST/DeclPrinter.cpp +++ b/lib/AST/DeclPrinter.cpp @@ -358,6 +358,11 @@ void DeclPrinter::VisitDeclContext(DeclContext *DC, bool Indent) { if (Terminator) Out << Terminator; Out << "\n"; + + // Declare target attribute is special one, natural spelling for the pragma + // assumes "ending" construct so print it here. + if (D->hasAttr()) + Out << "#pragma omp end declare target\n"; } if (!Decls.empty()) diff --git a/lib/Basic/OpenMPKinds.cpp b/lib/Basic/OpenMPKinds.cpp index fa40d016c9..07a410f052 100644 --- a/lib/Basic/OpenMPKinds.cpp +++ b/lib/Basic/OpenMPKinds.cpp @@ -547,6 +547,8 @@ bool clang::isAllowedClauseForDirective(OpenMPDirectiveKind DKind, break; } break; + case OMPD_declare_target: + case OMPD_end_declare_target: case OMPD_unknown: case OMPD_threadprivate: case OMPD_section: diff --git a/lib/Frontend/MultiplexConsumer.cpp b/lib/Frontend/MultiplexConsumer.cpp index ccda8c360a..581ed13ce0 100644 --- a/lib/Frontend/MultiplexConsumer.cpp +++ b/lib/Frontend/MultiplexConsumer.cpp @@ -125,6 +125,7 @@ public: void FunctionDefinitionInstantiated(const FunctionDecl *D) override; void DeclarationMarkedUsed(const Decl *D) override; void DeclarationMarkedOpenMPThreadPrivate(const Decl *D) override; + void DeclarationMarkedOpenMPDeclareTarget(const Decl *D) override; void RedefinedHiddenDefinition(const NamedDecl *D, Module *M) override; void AddedAttributeToRecord(const Attr *Attr, const RecordDecl *Record) override; @@ -219,6 +220,11 @@ void MultiplexASTMutationListener::DeclarationMarkedOpenMPThreadPrivate( for (size_t i = 0, e = Listeners.size(); i != e; ++i) Listeners[i]->DeclarationMarkedOpenMPThreadPrivate(D); } +void MultiplexASTMutationListener::DeclarationMarkedOpenMPDeclareTarget( + const Decl *D) { + for (auto *L : Listeners) + L->DeclarationMarkedOpenMPDeclareTarget(D); +} void MultiplexASTMutationListener::RedefinedHiddenDefinition(const NamedDecl *D, Module *M) { for (auto *L : Listeners) diff --git a/lib/Parse/ParseOpenMP.cpp b/lib/Parse/ParseOpenMP.cpp index dcff159561..a280bb9e26 100644 --- a/lib/Parse/ParseOpenMP.cpp +++ b/lib/Parse/ParseOpenMP.cpp @@ -31,6 +31,8 @@ enum OpenMPDirectiveKindEx { OMPD_cancellation = OMPD_unknown + 1, OMPD_data, OMPD_declare, + OMPD_end, + OMPD_end_declare, OMPD_enter, OMPD_exit, OMPD_point, @@ -51,6 +53,7 @@ static unsigned getOpenMPDirectiveKindEx(StringRef S) { .Case("cancellation", OMPD_cancellation) .Case("data", OMPD_data) .Case("declare", OMPD_declare) + .Case("end", OMPD_end) .Case("enter", OMPD_enter) .Case("exit", OMPD_exit) .Case("point", OMPD_point) @@ -66,6 +69,9 @@ static OpenMPDirectiveKind ParseOpenMPDirectiveKind(Parser &P) { { OMPD_cancellation, OMPD_point, OMPD_cancellation_point }, { OMPD_declare, OMPD_reduction, OMPD_declare_reduction }, { OMPD_declare, OMPD_simd, OMPD_declare_simd }, + { OMPD_declare, OMPD_target, OMPD_declare_target }, + { OMPD_end, OMPD_declare, OMPD_end_declare }, + { OMPD_end_declare, OMPD_target, OMPD_end_declare_target }, { OMPD_target, OMPD_data, OMPD_target_data }, { OMPD_target, OMPD_enter, OMPD_target_enter }, { OMPD_target, OMPD_exit, OMPD_target_exit }, @@ -456,6 +462,53 @@ Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirectiveWithExtDecl( return Actions.ActOnOpenMPDeclareSimdDirective(Ptr, BS, SourceRange(Loc, EndLoc)); } + case OMPD_declare_target: { + SourceLocation DTLoc = ConsumeAnyToken(); + if (Tok.isNot(tok::annot_pragma_openmp_end)) { + Diag(Tok, diag::warn_omp_extra_tokens_at_eol) + << getOpenMPDirectiveName(OMPD_declare_target); + SkipUntil(tok::annot_pragma_openmp_end, StopBeforeMatch); + } + // Skip the last annot_pragma_openmp_end. + ConsumeAnyToken(); + + if (!Actions.ActOnStartOpenMPDeclareTargetDirective(DTLoc)) + return DeclGroupPtrTy(); + + DKind = ParseOpenMPDirectiveKind(*this); + while (DKind != OMPD_end_declare_target && DKind != OMPD_declare_target && + Tok.isNot(tok::eof) && Tok.isNot(tok::r_brace)) { + ParsedAttributesWithRange attrs(AttrFactory); + MaybeParseCXX11Attributes(attrs); + MaybeParseMicrosoftAttributes(attrs); + ParseExternalDeclaration(attrs); + if (Tok.isAnnotation() && Tok.is(tok::annot_pragma_openmp)) { + TentativeParsingAction TPA(*this); + ConsumeToken(); + DKind = ParseOpenMPDirectiveKind(*this); + if (DKind != OMPD_end_declare_target) + TPA.Revert(); + else + TPA.Commit(); + } + } + + if (DKind == OMPD_end_declare_target) { + ConsumeAnyToken(); + if (Tok.isNot(tok::annot_pragma_openmp_end)) { + Diag(Tok, diag::warn_omp_extra_tokens_at_eol) + << getOpenMPDirectiveName(OMPD_end_declare_target); + SkipUntil(tok::annot_pragma_openmp_end, StopBeforeMatch); + } + // Skip the last annot_pragma_openmp_end. + ConsumeAnyToken(); + } else { + Diag(Tok, diag::err_expected_end_declare_target); + Diag(DTLoc, diag::note_matching) << "'#pragma omp declare target'"; + } + Actions.ActOnFinishOpenMPDeclareTargetDirective(); + return DeclGroupPtrTy(); + } case OMPD_unknown: Diag(Tok, diag::err_omp_unknown_directive); break; @@ -491,6 +544,7 @@ Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirectiveWithExtDecl( case OMPD_taskloop: case OMPD_taskloop_simd: case OMPD_distribute: + case OMPD_end_declare_target: Diag(Tok, diag::err_omp_unexpected_directive) << getOpenMPDirectiveName(DKind); break; @@ -711,6 +765,8 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective( break; } case OMPD_declare_simd: + case OMPD_declare_target: + case OMPD_end_declare_target: Diag(Tok, diag::err_omp_unexpected_directive) << getOpenMPDirectiveName(DKind); SkipUntil(tok::annot_pragma_openmp_end); diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 0d91e976d4..774af59e49 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -5052,6 +5052,9 @@ NamedDecl *Sema::HandleDeclarator(Scope *S, Declarator &D, CurContext->addHiddenDecl(New); } + if (isInOpenMPDeclareTargetContext()) + checkDeclIsAllowedInOpenMPTarget(nullptr, New); + return New; } diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index de9ebe7719..cfd58983df 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -13814,6 +13814,9 @@ void Sema::MarkVariableReferenced(SourceLocation Loc, VarDecl *Var) { static void MarkExprReferenced(Sema &SemaRef, SourceLocation Loc, Decl *D, Expr *E, bool MightBeOdrUse) { + if (SemaRef.isInOpenMPDeclareTargetContext()) + SemaRef.checkDeclIsAllowedInOpenMPTarget(E, D); + if (VarDecl *Var = dyn_cast(D)) { DoMarkVarDeclReferenced(SemaRef, Loc, Var, E); return; diff --git a/lib/Sema/SemaOpenMP.cpp b/lib/Sema/SemaOpenMP.cpp index 8d53dc4e72..0ef796557a 100644 --- a/lib/Sema/SemaOpenMP.cpp +++ b/lib/Sema/SemaOpenMP.cpp @@ -644,6 +644,11 @@ DSAStackTy::DSAVarData DSAStackTy::getTopDSA(ValueDecl *D, bool FromParent) { return DVar; } + if (Stack.size() == 1) { + // Not in OpenMP execution region and top scope was already checked. + return DVar; + } + // OpenMP [2.9.1.1, Data-sharing Attribute Rules for Variables Referenced // in a Construct, C/C++, predetermined, p.4] // Static data members are shared. @@ -1706,6 +1711,8 @@ void Sema::ActOnOpenMPRegionStart(OpenMPDirectiveKind DKind, Scope *CurScope) { case OMPD_target_exit_data: case OMPD_declare_reduction: case OMPD_declare_simd: + case OMPD_declare_target: + case OMPD_end_declare_target: llvm_unreachable("OpenMP Directive is not allowed"); case OMPD_unknown: llvm_unreachable("Unknown OpenMP directive"); @@ -3158,6 +3165,8 @@ StmtResult Sema::ActOnOpenMPExecutableDirective( Res = ActOnOpenMPDistributeDirective(ClausesWithImplicit, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA); break; + case OMPD_declare_target: + case OMPD_end_declare_target: case OMPD_threadprivate: case OMPD_declare_reduction: case OMPD_declare_simd: @@ -10354,3 +10363,141 @@ OMPClause *Sema::ActOnOpenMPDefaultmapClause( return new (Context) OMPDefaultmapClause(StartLoc, LParenLoc, MLoc, KindLoc, EndLoc, Kind, M); } + +bool Sema::ActOnStartOpenMPDeclareTargetDirective(SourceLocation Loc) { + DeclContext *CurLexicalContext = getCurLexicalContext(); + if (!CurLexicalContext->isFileContext() && + !CurLexicalContext->isExternCContext() && + !CurLexicalContext->isExternCXXContext()) { + Diag(Loc, diag::err_omp_region_not_file_context); + return false; + } + if (IsInOpenMPDeclareTargetContext) { + Diag(Loc, diag::err_omp_enclosed_declare_target); + return false; + } + + IsInOpenMPDeclareTargetContext = true; + return true; +} + +void Sema::ActOnFinishOpenMPDeclareTargetDirective() { + assert(IsInOpenMPDeclareTargetContext && + "Unexpected ActOnFinishOpenMPDeclareTargetDirective"); + + IsInOpenMPDeclareTargetContext = false; +} + +static void checkDeclInTargetContext(SourceLocation SL, SourceRange SR, + Sema &SemaRef, Decl *D) { + if (!D) + return; + Decl *LD = nullptr; + if (isa(D)) { + LD = cast(D)->getDefinition(); + } else if (isa(D)) { + LD = cast(D)->getDefinition(); + + // If this is an implicit variable that is legal and we do not need to do + // anything. + if (cast(D)->isImplicit()) { + D->addAttr(OMPDeclareTargetDeclAttr::CreateImplicit(SemaRef.Context)); + if (ASTMutationListener *ML = SemaRef.Context.getASTMutationListener()) + ML->DeclarationMarkedOpenMPDeclareTarget(D); + return; + } + + } else if (isa(D)) { + const FunctionDecl *FD = nullptr; + if (cast(D)->hasBody(FD)) + LD = const_cast(FD); + + // If the definition is associated with the current declaration in the + // target region (it can be e.g. a lambda) that is legal and we do not need + // to do anything else. + if (LD == D) { + D->addAttr(OMPDeclareTargetDeclAttr::CreateImplicit(SemaRef.Context)); + if (ASTMutationListener *ML = SemaRef.Context.getASTMutationListener()) + ML->DeclarationMarkedOpenMPDeclareTarget(D); + return; + } + } + if (!LD) + LD = D; + if (LD && !LD->hasAttr() && + (isa(LD) || isa(LD))) { + // Outlined declaration is not declared target. + if (LD->isOutOfLine()) { + SemaRef.Diag(LD->getLocation(), diag::warn_omp_not_in_target_context); + SemaRef.Diag(SL, diag::note_used_here) << SR; + } else { + DeclContext *DC = LD->getDeclContext(); + while (DC) { + if (isa(DC) && + cast(DC)->hasAttr()) + break; + DC = DC->getParent(); + } + if (DC) + return; + + // Is not declared in target context. + SemaRef.Diag(LD->getLocation(), diag::warn_omp_not_in_target_context); + SemaRef.Diag(SL, diag::note_used_here) << SR; + } + // Mark decl as declared target to prevent further diagnostic. + D->addAttr(OMPDeclareTargetDeclAttr::CreateImplicit(SemaRef.Context)); + if (ASTMutationListener *ML = SemaRef.Context.getASTMutationListener()) + ML->DeclarationMarkedOpenMPDeclareTarget(D); + } +} + +static bool checkValueDeclInTarget(SourceLocation SL, SourceRange SR, + Sema &SemaRef, DSAStackTy *Stack, + ValueDecl *VD) { + if (VD->hasAttr()) + return true; + if (!CheckTypeMappable(SL, SR, SemaRef, Stack, VD->getType())) + return false; + return true; +} + +void Sema::checkDeclIsAllowedInOpenMPTarget(Expr *E, Decl *D) { + if (!D || D->isInvalidDecl()) + return; + SourceRange SR = E ? E->getSourceRange() : D->getSourceRange(); + SourceLocation SL = E ? E->getLocStart() : D->getLocation(); + // 2.10.6: threadprivate variable cannot appear in a declare target directive. + if (VarDecl *VD = dyn_cast(D)) { + if (DSAStack->isThreadPrivate(VD)) { + Diag(SL, diag::err_omp_threadprivate_in_target); + ReportOriginalDSA(*this, DSAStack, VD, DSAStack->getTopDSA(VD, false)); + return; + } + } + if (ValueDecl *VD = dyn_cast(D)) { + // Problem if any with var declared with incomplete type will be reported + // as normal, so no need to check it here. + if ((E || !VD->getType()->isIncompleteType()) && + !checkValueDeclInTarget(SL, SR, *this, DSAStack, VD)) { + // Mark decl as declared target to prevent further diagnostic. + if (isa(VD) || isa(VD)) { + VD->addAttr(OMPDeclareTargetDeclAttr::CreateImplicit(Context)); + if (ASTMutationListener *ML = Context.getASTMutationListener()) + ML->DeclarationMarkedOpenMPDeclareTarget(VD); + } + return; + } + } + if (!E) { + // Checking declaration inside declare target region. + if (!D->hasAttr() && + (isa(D) || isa(D))) { + D->addAttr(OMPDeclareTargetDeclAttr::CreateImplicit(Context)); + if (ASTMutationListener *ML = Context.getASTMutationListener()) + ML->DeclarationMarkedOpenMPDeclareTarget(D); + } + return; + } + checkDeclInTargetContext(E->getExprLoc(), E->getSourceRange(), *this, D); +} diff --git a/lib/Serialization/ASTCommon.h b/lib/Serialization/ASTCommon.h index 64f583c987..641165e417 100644 --- a/lib/Serialization/ASTCommon.h +++ b/lib/Serialization/ASTCommon.h @@ -37,6 +37,7 @@ enum DeclUpdateKind { UPD_MANGLING_NUMBER, UPD_STATIC_LOCAL_NUMBER, UPD_DECL_MARKED_OPENMP_THREADPRIVATE, + UPD_DECL_MARKED_OPENMP_DECLARETARGET, UPD_DECL_EXPORTED, UPD_ADDED_ATTR_TO_RECORD }; diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index b2fdbb8aa2..47ac407675 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -3879,6 +3879,11 @@ void ASTDeclReader::UpdateDecl(Decl *D, ModuleFile &ModuleFile, Reader.Context, ReadSourceRange(Record, Idx))); break; + case UPD_DECL_MARKED_OPENMP_DECLARETARGET: + D->addAttr(OMPDeclareTargetDeclAttr::CreateImplicit( + Reader.Context, ReadSourceRange(Record, Idx))); + break; + case UPD_DECL_EXPORTED: { unsigned SubmoduleID = readSubmoduleID(Record, Idx); auto *Exported = cast(D); diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index c77a709964..3a1901d5e6 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -4760,6 +4760,11 @@ void ASTWriter::WriteDeclUpdatesBlocks(RecordDataImpl &OffsetsRecord) { D->getAttr()->getRange()); break; + case UPD_DECL_MARKED_OPENMP_DECLARETARGET: + Record.AddSourceRange( + D->getAttr()->getRange()); + break; + case UPD_DECL_EXPORTED: Record.push_back(getSubmoduleID(Update.getModule())); break; @@ -5887,6 +5892,14 @@ void ASTWriter::DeclarationMarkedOpenMPThreadPrivate(const Decl *D) { DeclUpdates[D].push_back(DeclUpdate(UPD_DECL_MARKED_OPENMP_THREADPRIVATE)); } +void ASTWriter::DeclarationMarkedOpenMPDeclareTarget(const Decl *D) { + assert(!WritingAST && "Already writing the AST!"); + if (!D->isFromASTFile()) + return; + + DeclUpdates[D].push_back(DeclUpdate(UPD_DECL_MARKED_OPENMP_DECLARETARGET)); +} + void ASTWriter::RedefinedHiddenDefinition(const NamedDecl *D, Module *M) { assert(!WritingAST && "Already writing the AST!"); assert(D->isHidden() && "expected a hidden declaration"); diff --git a/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp index 79453be815..4c0fa1a201 100644 --- a/lib/Serialization/ASTWriterDecl.cpp +++ b/lib/Serialization/ASTWriterDecl.cpp @@ -2133,8 +2133,10 @@ static bool isRequiredDecl(const Decl *D, ASTContext &Context, // An ObjCMethodDecl is never considered as "required" because its // implementation container always is. - // File scoped assembly or obj-c implementation must be seen. - if (isa(D) || isa(D)) + // File scoped assembly or obj-c or OMP declare target implementation must be + // seen. + if (isa(D) || isa(D) || + D->hasAttr()) return true; // ImportDecl is used by codegen to determine the set of imported modules to diff --git a/test/OpenMP/declare_target_ast_print.cpp b/test/OpenMP/declare_target_ast_print.cpp new file mode 100644 index 0000000000..53e0d764e8 --- /dev/null +++ b/test/OpenMP/declare_target_ast_print.cpp @@ -0,0 +1,93 @@ +// RUN: %clang_cc1 -verify -fopenmp -ast-print %s | FileCheck %s +// RUN: %clang_cc1 -fopenmp -x c++ -std=c++11 -emit-pch -o %t %s +// RUN: %clang_cc1 -fopenmp -std=c++11 -include-pch %t -fsyntax-only -verify %s -ast-print | FileCheck %s +// expected-no-diagnostics + +#ifndef HEADER +#define HEADER + +#pragma omp declare target +// CHECK: #pragma omp declare target +void foo() {} +// CHECK-NEXT: void foo() +#pragma omp end declare target +// CHECK: #pragma omp end declare target + +extern "C" { +#pragma omp declare target +// CHECK: #pragma omp declare target +void foo_c() {} +// CHECK-NEXT: void foo_c() +#pragma omp end declare target +// CHECK: #pragma omp end declare target +} + +extern "C++" { +#pragma omp declare target +// CHECK: #pragma omp declare target +void foo_cpp() {} +// CHECK-NEXT: void foo_cpp() +#pragma omp end declare target +// CHECK: #pragma omp end declare target +} + +#pragma omp declare target +template +struct C { +// CHECK: template struct C + T t; +// CHECK-NEXT: int t; + static T ts; +// CHECK-NEXT: #pragma omp declare target +// CHECK-NEXT: static int ts; +// CHECK: #pragma omp end declare target + + C(T t) : t(t) { + } +// CHECK: #pragma omp declare target +// CHECK-NEXT: C(int t) : t(t) { +// CHECK-NEXT: } +// CHECK: #pragma omp end declare target + + T foo() { + return t; + } +// CHECK: #pragma omp declare target +// CHECK-NEXT: int foo() { +// CHECK-NEXT: return this->t; +// CHECK-NEXT: } +// CHECK: #pragma omp end declare target +}; + +// CHECK: template struct C { +// CHECK: #pragma omp declare target +// CHECK-NEXT: static T ts; +// CHECK-NEXT: #pragma omp end declare target + +template +T C::ts = 1; +// CHECK: #pragma omp declare target +// CHECK: T ts = 1; +// CHECK: #pragma omp end declare target + +// CHECK: #pragma omp declare target +// CHECK: int test1() +int test1() { + C c(1); + return c.foo() + c.ts; +} +#pragma omp end declare target +// CHECK: #pragma omp end declare target + +int main (int argc, char **argv) { + foo(); + foo_c(); + foo_cpp(); + test1(); + return (0); +} + +// CHECK: #pragma omp declare target +// CHECK-NEXT: int ts = 1; +// CHECK-NEXT: #pragma omp end declare target +#endif diff --git a/test/OpenMP/declare_target_messages.cpp b/test/OpenMP/declare_target_messages.cpp new file mode 100644 index 0000000000..50f0ed98fc --- /dev/null +++ b/test/OpenMP/declare_target_messages.cpp @@ -0,0 +1,89 @@ +// RUN: %clang_cc1 -triple x86_64-apple-macos10.7.0 -verify -fopenmp -fnoopenmp-use-tls -ferror-limit 100 -o - %s + +#pragma omp end declare target // expected-error {{unexpected OpenMP directive '#pragma omp end declare target'}} + +int a, b; // expected-warning {{declaration is not declared in any declare target region}} +__thread int t; // expected-note {{defined as threadprivate or thread local}} +#pragma omp declare target private(a) // expected-warning {{extra tokens at the end of '#pragma omp declare target' are ignored}} +void f(); +#pragma omp end declare target shared(a) // expected-warning {{extra tokens at the end of '#pragma omp end declare target' are ignored}} +void c(); // expected-warning {{declaration is not declared in any declare target region}} + +extern int b; + +struct NonT { + int a; +}; + +typedef int sint; + +#pragma omp declare target // expected-note {{to match this '#pragma omp declare target'}} +#pragma omp threadprivate(a) // expected-note {{defined as threadprivate or thread local}} +extern int b; +int g; + +struct T { // expected-note {{mappable type cannot be polymorphic}} + int a; + virtual int method(); +}; + +class VC { // expected-note {{mappable type cannot be polymorphic}} + T member; + NonT member1; + public: + virtual int method() { T a; return 0; } // expected-error {{type 'T' is not mappable to target}} +}; + +struct C { + NonT a; + sint b; + int method(); + int method1(); +}; + +int C::method1() { + return 0; +} + +void foo() { + a = 0; // expected-error {{threadprivate variables cannot be used in target constructs}} + b = 0; // expected-note {{used here}} + t = 1; // expected-error {{threadprivate variables cannot be used in target constructs}} + C object; + VC object1; // expected-error {{type 'VC' is not mappable to target}} + g = object.method(); + g += object.method1(); + g += object1.method(); + f(); + c(); // expected-note {{used here}} +} +#pragma omp declare target // expected-error {{expected '#pragma omp end declare target'}} +void foo1() {} +#pragma omp end declare target +#pragma omp end declare target // expected-error {{unexpected OpenMP directive '#pragma omp end declare target'}} + +int C::method() { + return 0; +} + +struct S { +#pragma omp declare target // expected-error {{directive must be at file or namespace scope}} + int v; +#pragma omp end declare target // expected-error {{unexpected OpenMP directive '#pragma omp end declare target'}} +}; + +int main (int argc, char **argv) { +#pragma omp declare target // expected-error {{unexpected OpenMP directive '#pragma omp declare target'}} + int v; +#pragma omp end declare target // expected-error {{unexpected OpenMP directive '#pragma omp end declare target'}} + foo(); + return (0); +} + +namespace { +#pragma omp declare target // expected-note {{to match this '#pragma omp declare target'}} + int x; +} // expected-error {{expected '#pragma omp end declare target'}} +#pragma omp end declare target // expected-error {{unexpected OpenMP directive '#pragma omp end declare target'}} + +#pragma omp declare target // expected-error {{expected '#pragma omp end declare target'}} expected-note {{to match this '#pragma omp declare target'}}