Index: include/clang/AST/ASTMutationListener.h =================================================================== --- include/clang/AST/ASTMutationListener.h +++ include/clang/AST/ASTMutationListener.h @@ -107,6 +107,12 @@ /// \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. Index: include/clang/AST/Attr.h =================================================================== --- include/clang/AST/Attr.h +++ include/clang/AST/Attr.h @@ -112,6 +112,9 @@ // Pretty print this attribute. void printPretty(raw_ostream &OS, const PrintingPolicy &Policy) const; + // Pretty print this attribute ending text if any. + void printPrettyEnd(raw_ostream &OS, const PrintingPolicy &Policy) const; + /// \brief By default, attributes cannot be duplicated when being merged; /// however, an attribute can override this. Returns true if the attribute /// can be duplicated when merging. Index: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -202,8 +202,10 @@ int Version = version; } class Keyword : Spelling; -class Pragma : Spelling { +class Pragma + : Spelling { string Namespace = namespace; + string Ending = ending; } // The GCC spelling implies GNU and CXX11<"gnu", name> and also @@ -2244,6 +2246,16 @@ let Documentation = [Undocumented]; } +def OMPDeclareTargetDecl : Attr { + // This attribute has no spellings as it is only ever created implicitly. + let Spellings = [Pragma<"omp", "declare target", "end declare target">]; + let SemaHandler = 0; + let Documentation = [Undocumented]; + 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: include/clang/Basic/DiagnosticGroups.td =================================================================== --- include/clang/Basic/DiagnosticGroups.td +++ include/clang/Basic/DiagnosticGroups.td @@ -819,6 +819,7 @@ 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">; Index: include/clang/Basic/DiagnosticParseKinds.td =================================================================== --- include/clang/Basic/DiagnosticParseKinds.td +++ include/clang/Basic/DiagnosticParseKinds.td @@ -959,6 +959,8 @@ "incorrect map type modifier, expected 'always'">; def err_omp_map_type_missing : Error< "missing map type">; +def err_expected_end_declare_target : Error< + "expected '#pragma omp end declare target'">; // Pragma loop support. def err_pragma_loop_missing_argument : Error< Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -7813,6 +7813,8 @@ "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< @@ -7888,6 +7890,8 @@ 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< @@ -7914,6 +7918,11 @@ 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 into 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" Index: include/clang/Basic/OpenMPKinds.def =================================================================== --- include/clang/Basic/OpenMPKinds.def +++ include/clang/Basic/OpenMPKinds.def @@ -159,6 +159,8 @@ 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) Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -7826,6 +7826,8 @@ // 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(); @@ -7911,6 +7913,17 @@ 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() { + return IsInOpenMPDeclareTargetContext; + } + /// \brief Initialization of captured region for OpenMP region. void ActOnOpenMPRegionStart(OpenMPDirectiveKind DKind, Scope *CurScope); /// \brief End of OpenMP region. Index: include/clang/Serialization/ASTWriter.h =================================================================== --- include/clang/Serialization/ASTWriter.h +++ include/clang/Serialization/ASTWriter.h @@ -859,6 +859,7 @@ 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; Index: lib/AST/ASTContext.cpp =================================================================== --- lib/AST/ASTContext.cpp +++ lib/AST/ASTContext.cpp @@ -8505,6 +8505,9 @@ 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)) Index: lib/AST/DeclPrinter.cpp =================================================================== --- lib/AST/DeclPrinter.cpp +++ lib/AST/DeclPrinter.cpp @@ -99,6 +99,7 @@ const TemplateArgumentList *Args = nullptr); void prettyPrintAttributes(Decl *D); void prettyPrintPragmas(Decl *D); + void prettyPrintEndPragmas(Decl *D); void printDeclType(QualType T, StringRef DeclName, bool Pack = false); }; } @@ -236,6 +237,27 @@ } } +void DeclPrinter::prettyPrintEndPragmas(Decl *D) { + if (Policy.PolishForDeclaration) + return; + + if (D->hasAttrs()) { + AttrVec &Attrs = D->getAttrs(); + for (auto *A : Attrs) { + switch (A->getKind()) { +#define ATTR(X) +#define PRAGMA_SPELLING_ATTR(X) case attr::X: +#include "clang/Basic/AttrList.inc" + A->printPrettyEnd(Out, Policy); + Indent(); + break; + default: + break; + } + } + } +} + void DeclPrinter::printDeclType(QualType T, StringRef DeclName, bool Pack) { // Normally, a PackExpansionType is written as T[3]... (for instance, as a // template argument), but if it is the type of a declaration, the ellipsis @@ -358,6 +380,8 @@ if (Terminator) Out << Terminator; Out << "\n"; + + prettyPrintEndPragmas(*D); } if (!Decls.empty()) Index: lib/Basic/OpenMPKinds.cpp =================================================================== --- lib/Basic/OpenMPKinds.cpp +++ lib/Basic/OpenMPKinds.cpp @@ -545,6 +545,8 @@ break; } break; + case OMPD_declare_target: + case OMPD_end_declare_target: case OMPD_unknown: case OMPD_threadprivate: case OMPD_section: Index: lib/Frontend/MultiplexConsumer.cpp =================================================================== --- lib/Frontend/MultiplexConsumer.cpp +++ lib/Frontend/MultiplexConsumer.cpp @@ -125,6 +125,7 @@ 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 @@ for (size_t i = 0, e = Listeners.size(); i != e; ++i) Listeners[i]->DeclarationMarkedOpenMPThreadPrivate(D); } +void MultiplexASTMutationListener::DeclarationMarkedOpenMPDeclareTarget( + const Decl *D) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->DeclarationMarkedOpenMPDeclareTarget(D); +} void MultiplexASTMutationListener::RedefinedHiddenDefinition(const NamedDecl *D, Module *M) { for (auto *L : Listeners) Index: lib/Parse/ParseOpenMP.cpp =================================================================== --- lib/Parse/ParseOpenMP.cpp +++ lib/Parse/ParseOpenMP.cpp @@ -31,6 +31,8 @@ OMPD_cancellation = OMPD_unknown + 1, OMPD_data, OMPD_declare, + OMPD_end, + OMPD_end_declare, OMPD_enter, OMPD_exit, OMPD_point, @@ -51,6 +53,7 @@ .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) @@ -65,6 +68,9 @@ static const unsigned F[][3] = { { OMPD_cancellation, OMPD_point, OMPD_cancellation_point }, { OMPD_declare, OMPD_reduction, OMPD_declare_reduction }, + { 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 }, @@ -373,6 +379,53 @@ return Res; } break; + 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; @@ -408,6 +461,7 @@ 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; @@ -627,6 +681,12 @@ OMPDirectiveScope.Exit(); break; } + case OMPD_declare_target: + case OMPD_end_declare_target: + 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/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -5052,6 +5052,9 @@ CurContext->addHiddenDecl(New); } + if (isInOpenMPDeclareTargetContext()) + checkDeclIsAllowedInOpenMPTarget(nullptr, New); + return New; } Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -13801,6 +13801,9 @@ 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; Index: lib/Sema/SemaOpenMP.cpp =================================================================== --- lib/Sema/SemaOpenMP.cpp +++ lib/Sema/SemaOpenMP.cpp @@ -641,6 +641,11 @@ 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. @@ -1701,6 +1706,8 @@ case OMPD_target_enter_data: case OMPD_target_exit_data: case OMPD_declare_reduction: + case OMPD_declare_target: + case OMPD_end_declare_target: llvm_unreachable("OpenMP Directive is not allowed"); case OMPD_unknown: llvm_unreachable("Unknown OpenMP directive"); @@ -3153,6 +3160,8 @@ Res = ActOnOpenMPDistributeDirective(ClausesWithImplicit, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA); break; + case OMPD_declare_target: + case OMPD_end_declare_target: case OMPD_threadprivate: case OMPD_declare_reduction: llvm_unreachable("OpenMP Directive is not allowed"); @@ -10263,3 +10272,141 @@ 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 (auto *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 (auto *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 (auto *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 (auto *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 (auto *ML = Context.getASTMutationListener()) + ML->DeclarationMarkedOpenMPDeclareTarget(D); + } + return; + } + checkDeclInTargetContext(E->getExprLoc(), E->getSourceRange(), *this, D); +} Index: lib/Serialization/ASTCommon.h =================================================================== --- lib/Serialization/ASTCommon.h +++ lib/Serialization/ASTCommon.h @@ -37,6 +37,7 @@ 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 }; Index: lib/Serialization/ASTReaderDecl.cpp =================================================================== --- lib/Serialization/ASTReaderDecl.cpp +++ lib/Serialization/ASTReaderDecl.cpp @@ -3879,6 +3879,11 @@ 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); Index: lib/Serialization/ASTWriter.cpp =================================================================== --- lib/Serialization/ASTWriter.cpp +++ lib/Serialization/ASTWriter.cpp @@ -4756,6 +4756,11 @@ Record); break; + case UPD_DECL_MARKED_OPENMP_DECLARETARGET: + AddSourceRange(D->getAttr()->getRange(), + Record); + break; + case UPD_DECL_EXPORTED: Record.push_back(getSubmoduleID(Update.getModule())); break; @@ -5885,6 +5890,14 @@ 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"); Index: lib/Serialization/ASTWriterDecl.cpp =================================================================== --- lib/Serialization/ASTWriterDecl.cpp +++ lib/Serialization/ASTWriterDecl.cpp @@ -2129,8 +2129,10 @@ // 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 Index: test/OpenMP/declare_target_ast_print.cpp =================================================================== --- /dev/null +++ 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 Index: test/OpenMP/declare_target_messages.cpp =================================================================== --- /dev/null +++ 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'}} Index: utils/TableGen/ClangAttrEmitter.cpp =================================================================== --- utils/TableGen/ClangAttrEmitter.cpp +++ utils/TableGen/ClangAttrEmitter.cpp @@ -30,13 +30,14 @@ namespace { class FlattenedSpelling { - std::string V, N, NS; + std::string V, N, NS, E; bool K; public: FlattenedSpelling(const std::string &Variety, const std::string &Name, - const std::string &Namespace, bool KnownToGCC) : - V(Variety), N(Name), NS(Namespace), K(KnownToGCC) {} + const std::string &Namespace, const std::string &Ending, + bool KnownToGCC) : + V(Variety), N(Name), NS(Namespace), E(Ending), K(KnownToGCC) {} explicit FlattenedSpelling(const Record &Spelling) : V(Spelling.getValueAsString("Variety")), N(Spelling.getValueAsString("Name")) { @@ -45,6 +46,8 @@ "flattened!"); if (V == "CXX11" || V == "Pragma") NS = Spelling.getValueAsString("Namespace"); + if (V == "Pragma") + E = Spelling.getValueAsString("Ending"); bool Unset; K = Spelling.getValueAsBitOrUnset("KnownToGCC", Unset); } @@ -52,6 +55,7 @@ const std::string &variety() const { return V; } const std::string &name() const { return N; } const std::string &nameSpace() const { return NS; } + const std::string &ending() const { return E; } bool knownToGCC() const { return K; } }; } // end anonymous namespace @@ -64,8 +68,8 @@ for (const auto &Spelling : Spellings) { if (Spelling->getValueAsString("Variety") == "GCC") { // Gin up two new spelling objects to add into the list. - Ret.emplace_back("GNU", Spelling->getValueAsString("Name"), "", true); - Ret.emplace_back("CXX11", Spelling->getValueAsString("Name"), "gnu", + Ret.emplace_back("GNU", Spelling->getValueAsString("Name"), "", "", true); + Ret.emplace_back("CXX11", Spelling->getValueAsString("Name"), "gnu", "", true); } else Ret.push_back(FlattenedSpelling(*Spelling)); @@ -1259,6 +1263,63 @@ OS << "}\n\n"; } +// Determines if an attribute has a Pragma spelling. +static bool AttrHasPragmaSpelling(const Record *R) { + std::vector Spellings = GetFlattenedSpellings(*R); + return std::find_if(Spellings.begin(), Spellings.end(), + [](const FlattenedSpelling &S) { + return S.variety() == "Pragma"; + }) != Spellings.end(); +} + +static void +writePrettyPrintEndFunction(Record &R, + const std::vector> &Args, + raw_ostream &OS) { + std::vector Spellings = GetFlattenedSpellings(R); + + OS << "void " << R.getName() << "Attr::printPrettyEnd(" + << "raw_ostream &OS, const PrintingPolicy &Policy) const {\n"; + + if (!AttrHasPragmaSpelling(&R)) { + OS << "}\n\n"; + return; + } + + OS << + " switch (SpellingListIndex) {\n" + " default:\n" + " llvm_unreachable(\"Unknown attribute spelling!\");\n" + " break;\n"; + + for (unsigned I = 0; I < Spellings.size(); ++ I) { + if (Spellings[I].variety() != "Pragma") + continue; + + OS << " case " << I << " : {\n"; + std::string Ending = Spellings[I].ending(); + if (!Ending.empty()) { + llvm::SmallString<64> Spelling; + std::string Namespace = Spellings[I].nameSpace(); + if (!Namespace.empty()) { + Spelling += Namespace; + Spelling += " "; + } + Spelling += Ending; + + OS << " OS << \"#pragma " << Spelling << "\\n\";\n"; + + } + OS << " break;\n"; + OS << " }\n"; + } + + // End of the switch statement. + OS << "}\n"; + // End of the print function. + OS << "}\n\n"; +} + /// \brief Return the index of a spelling in a spelling list. static unsigned getSpellingListIndex(const std::vector &SpellingList, @@ -1642,6 +1703,8 @@ OS << " " << R.getName() << "Attr *clone(ASTContext &C) const;\n"; OS << " void printPretty(raw_ostream &OS,\n" << " const PrintingPolicy &Policy) const;\n"; + OS << " void printPrettyEnd(raw_ostream &OS,\n" + << " const PrintingPolicy &Policy) const;\n"; OS << " const char *getSpelling() const;\n"; if (!ElideSpelling) { @@ -1714,6 +1777,7 @@ OS << " return A;\n}\n\n"; writePrettyPrintFunction(R, Args, OS); + writePrettyPrintEndFunction(R, Args, OS); writeGetSpellingFunction(R, OS); } @@ -1744,6 +1808,10 @@ OS << "void Attr::printPretty(raw_ostream &OS, " "const PrintingPolicy &Policy) const {\n"; EmitFunc("printPretty(OS, Policy)"); + + OS << "void Attr::printPrettyEnd(raw_ostream &OS, " + "const PrintingPolicy &Policy) const {\n"; + EmitFunc("printPrettyEnd(OS, Policy)"); } } // end namespace clang @@ -1755,15 +1823,6 @@ } } -// Determines if an attribute has a Pragma spelling. -static bool AttrHasPragmaSpelling(const Record *R) { - std::vector Spellings = GetFlattenedSpellings(*R); - return std::find_if(Spellings.begin(), Spellings.end(), - [](const FlattenedSpelling &S) { - return S.variety() == "Pragma"; - }) != Spellings.end(); -} - namespace { struct AttrClassDescriptor { const char * const MacroName;