diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -3606,6 +3606,7 @@ void printPrettyPragma(raw_ostream &OS, const PrintingPolicy &Policy) const; static llvm::Optional isDeclareTargetDeclaration(const ValueDecl *VD); + static llvm::Optional getActiveAttr(const ValueDecl *VD); static llvm::Optional getDeviceType(const ValueDecl *VD); static llvm::Optional getLocation(const ValueDecl *VD); }]; diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1314,7 +1314,15 @@ def note_omp_assumption_clause_continue_here : Note<"the ignored tokens spans until here">; def err_omp_declare_target_unexpected_clause: Error< - "unexpected '%0' clause, only %select{'to' or 'link'|'to', 'link' or 'device_type'}1 clauses expected">; + "unexpected '%0' clause, only %select{'device_type'|'to' or 'link'|'to', 'link' or 'device_type'}1 clauses expected">; +def err_omp_begin_declare_target_unexpected_implicit_to_clause: Error< + "unexpected '(', only 'to', 'link' or 'device_type' clauses expected for 'begin declare target' directive">; +def err_omp_declare_target_unexpected_clause_after_implicit_to: Error< + "unexpected clause after an implicit 'to' clause">; +def err_omp_declare_target_missing_to_or_link_clause: Error< + "expected at least one 'to' or 'link' clause">; +def err_omp_declare_target_multiple : Error< + "%0 appears multiple times in clauses on the same declare target directive">; def err_omp_expected_clause: Error< "expected at least one clause on '#pragma omp %0' directive">; def err_omp_mapper_illegal_identifier : Error< diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10222,8 +10222,6 @@ InGroup; def err_omp_invalid_target_decl : Error< "%0 used in declare target directive is not a variable or a function name">; -def err_omp_declare_target_multiple : Error< - "%0 appears multiple times in clauses on the same declare target directive">; def err_omp_declare_target_to_and_link : Error< "%0 must not appear in both clauses 'to' and 'link'">; def warn_omp_not_in_target_context : Warning< diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -3181,10 +3181,12 @@ /// Parse 'omp end assumes' directive. void ParseOpenMPEndAssumesDirective(SourceLocation Loc); - /// Parse clauses for '#pragma omp declare target'. - DeclGroupPtrTy ParseOMPDeclareTargetClauses(); + /// Parse clauses for '#pragma omp [begin] declare target'. + void ParseOMPDeclareTargetClauses(Sema::DeclareTargetContextInfo &DTCI); + /// Parse '#pragma omp end declare target'. - void ParseOMPEndDeclareTargetDirective(OpenMPDirectiveKind DKind, + void ParseOMPEndDeclareTargetDirective(OpenMPDirectiveKind BeginDKind, + OpenMPDirectiveKind EndDKind, SourceLocation Loc); /// Skip tokens until a `annot_pragma_openmp_end` was found. Emit a warning if diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -10207,8 +10207,31 @@ // private: void *VarDataSharingAttributesStack; + + struct DeclareTargetContextInfo { + struct MapInfo { + OMPDeclareTargetDeclAttr::MapTypeTy MT; + SourceLocation Loc; + }; + /// Explicitly listed variables and functions in a 'to' or 'link' clause. + llvm::DenseMap ExplicitlyMapped; + + /// The 'device_type' as parsed from the clause. + OMPDeclareTargetDeclAttr::DevTypeTy DT = OMPDeclareTargetDeclAttr::DT_Any; + + /// The directive kind, `begin declare target` or `declare target`. + OpenMPDirectiveKind Kind; + + /// The directive location. + SourceLocation Loc; + + DeclareTargetContextInfo(OpenMPDirectiveKind Kind, SourceLocation Loc) + : Kind(Kind), Loc(Loc) {} + }; + /// Number of nested '#pragma omp declare target' directives. - SmallVector DeclareTargetNesting; + SmallVector DeclareTargetNesting; + /// Initialization of data-sharing attributes stack. void InitDataSharingAttributesStack(); void DestroyDataSharingAttributesStack(); @@ -10474,19 +10497,28 @@ const ValueDecl *getOpenMPDeclareMapperVarName() const; /// 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(); + bool ActOnStartOpenMPDeclareTargetContext(DeclareTargetContextInfo &DTCI); + + /// Called at the end of target region i.e. '#pragma omp end declare target'. + const DeclareTargetContextInfo ActOnOpenMPEndDeclareTargetDirective(); + + /// Called once a target context is completed, that can be when a + /// '#pragma omp end declare target' was encountered or when a + /// '#pragma omp declare target' without declaration-definition-seq was + /// encountered. + void ActOnFinishedOpenMPDeclareTargetContext(DeclareTargetContextInfo &DTCI); + /// Searches for the provided declaration name for OpenMP declare target /// directive. - NamedDecl * - lookupOpenMPDeclareTargetName(Scope *CurScope, CXXScopeSpec &ScopeSpec, - const DeclarationNameInfo &Id, - NamedDeclSetType &SameDirectiveDecls); + NamedDecl *lookupOpenMPDeclareTargetName(Scope *CurScope, + CXXScopeSpec &ScopeSpec, + const DeclarationNameInfo &Id); + /// Called on correct id-expression from the '#pragma omp declare target'. void ActOnOpenMPDeclareTargetName(NamedDecl *ND, SourceLocation Loc, OMPDeclareTargetDeclAttr::MapTypeTy MT, OMPDeclareTargetDeclAttr::DevTypeTy DT); + /// Check declaration inside target region. void checkDeclIsAllowedInOpenMPTarget(Expr *E, Decl *D, diff --git a/clang/lib/AST/AttrImpl.cpp b/clang/lib/AST/AttrImpl.cpp --- a/clang/lib/AST/AttrImpl.cpp +++ b/clang/lib/AST/AttrImpl.cpp @@ -141,57 +141,44 @@ OS << ' ' << ConvertMapTypeTyToStr(getMapType()); } -llvm::Optional -OMPDeclareTargetDeclAttr::isDeclareTargetDeclaration(const ValueDecl *VD) { +llvm::Optional +OMPDeclareTargetDeclAttr::getActiveAttr(const ValueDecl *VD) { if (!VD->hasAttrs()) return llvm::None; unsigned Level = 0; - const OMPDeclareTargetDeclAttr *FoundAttr = nullptr; - for (const auto *Attr : VD->specific_attrs()) { - if (Level < Attr->getLevel()) { + OMPDeclareTargetDeclAttr *FoundAttr = nullptr; + for (auto *Attr : VD->specific_attrs()) { + if (Level <= Attr->getLevel()) { Level = Attr->getLevel(); FoundAttr = Attr; } } if (FoundAttr) - return FoundAttr->getMapType(); + return FoundAttr; + return llvm::None; +} +llvm::Optional +OMPDeclareTargetDeclAttr::isDeclareTargetDeclaration(const ValueDecl *VD) { + llvm::Optional ActiveAttr = getActiveAttr(VD); + if (ActiveAttr.hasValue()) + return ActiveAttr.getValue()->getMapType(); return llvm::None; } llvm::Optional OMPDeclareTargetDeclAttr::getDeviceType(const ValueDecl *VD) { - if (!VD->hasAttrs()) - return llvm::None; - unsigned Level = 0; - const OMPDeclareTargetDeclAttr *FoundAttr = nullptr; - for (const auto *Attr : VD->specific_attrs()) { - if (Level < Attr->getLevel()) { - Level = Attr->getLevel(); - FoundAttr = Attr; - } - } - if (FoundAttr) - return FoundAttr->getDevType(); - + llvm::Optional ActiveAttr = getActiveAttr(VD); + if (ActiveAttr.hasValue()) + return ActiveAttr.getValue()->getDevType(); return llvm::None; } llvm::Optional OMPDeclareTargetDeclAttr::getLocation(const ValueDecl *VD) { - if (!VD->hasAttrs()) - return llvm::None; - unsigned Level = 0; - const OMPDeclareTargetDeclAttr *FoundAttr = nullptr; - for (const auto *Attr : VD->specific_attrs()) { - if (Level < Attr->getLevel()) { - Level = Attr->getLevel(); - FoundAttr = Attr; - } - } - if (FoundAttr) - return FoundAttr->getRange().getBegin(); - + llvm::Optional ActiveAttr = getActiveAttr(VD); + if (ActiveAttr.hasValue()) + return ActiveAttr.getValue()->getRange().getBegin(); return llvm::None; } diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -2613,3 +2613,62 @@ void CodeGenModule::EmitOMPRequiresDecl(const OMPRequiresDecl *D) { getOpenMPRuntime().processRequiresDirective(D); } + +void CodeGenModule::EmitOMPAllocateDecl(const OMPAllocateDecl *D) { + for (Expr *E : const_cast(D)->varlists()) { + auto *DE = cast(E); + auto *VD = cast(DE->getDecl()); + + // Skipp all but globals. + if (!VD->hasGlobalStorage()) + continue; + + // Check if the global has been materialized yet or not. If not, we are done + // as any later generation will utilize the OMPAllocateDeclAttr. However, if + // we already emitted the global we might have done so before the + // OMPAllocateDeclAttr was attached, leading to the wrong address space + // (potentially). While not pretty, common practise is to remove the old IR + // global and generate a new one, so we do that here too. Uses are replaced + // properly. + StringRef MangledName = getMangledName(VD); + llvm::GlobalValue *Entry = GetGlobalValue(MangledName); + if (!Entry) + continue; + + // We can also keep the existing global if the address space is what we + // expect it to be, if not, it is replaced. + QualType ASTTy = VD->getType(); + clang::LangAS GVAS = GetGlobalVarAddressSpace(VD); + auto TargetAS = getContext().getTargetAddressSpace(GVAS); + + if (Entry->getType()->getAddressSpace() == TargetAS) + continue; + + // Make sure the variable declaration knows about the new address space as + // well, this is temporarlily necessary to allow proper code generation but + // undone as the base language declarations might not agree with this + // address space. + VD->setType(getContext().getAddrSpaceQualType(ASTTy, GVAS)); + + // Move the old entry aside so that we'll create a new one. + Entry->setName(StringRef()); + + // Make a new global with the correct type / address space. + llvm::PointerType *PTy = + llvm::PointerType::get(getTypes().ConvertTypeForMem(ASTTy), TargetAS); + llvm::Constant *GV = GetOrCreateLLVMGlobal(MangledName, PTy, VD); + + // Replace all uses of the old global with the new global + llvm::Constant *NewPtrForOldDecl = + llvm::ConstantExpr::getPointerBitCastOrAddrSpaceCast(GV, + Entry->getType()); + Entry->replaceAllUsesWith(NewPtrForOldDecl); + + // Erase the old global, since it is no longer used. + Entry->eraseFromParent(); + + // Reset the type to ensure a new base language declaration w/o address + // space is not going to conflict with the address space one. + VD->setType(ASTTy); + } +} diff --git a/clang/lib/CodeGen/CGOpenMPRuntime.cpp b/clang/lib/CodeGen/CGOpenMPRuntime.cpp --- a/clang/lib/CodeGen/CGOpenMPRuntime.cpp +++ b/clang/lib/CodeGen/CGOpenMPRuntime.cpp @@ -15,6 +15,7 @@ #include "CGCleanup.h" #include "CGRecordLayout.h" #include "CodeGenFunction.h" +#include "clang/AST/APValue.h" #include "clang/AST/Attr.h" #include "clang/AST/Decl.h" #include "clang/AST/OpenMPClause.h" @@ -2995,8 +2996,7 @@ if (CGM.getLangOpts().OpenMPIsDevice) { // This could happen if the device compilation is invoked standalone. if (!hasTargetRegionEntryInfo(DeviceID, FileID, ParentName, LineNum)) - initializeTargetRegionEntryInfo(DeviceID, FileID, ParentName, LineNum, - OffloadingEntriesNum); + return; auto &Entry = OffloadEntriesTargetRegion[DeviceID][FileID][ParentName][LineNum]; Entry.setAddress(Addr); @@ -3067,10 +3067,8 @@ if (CGM.getLangOpts().OpenMPIsDevice) { // This could happen if the device compilation is invoked standalone. if (!hasDeviceGlobalVarEntryInfo(VarName)) - initializeDeviceGlobalVarEntryInfo(VarName, Flags, OffloadingEntriesNum); + return; auto &Entry = OffloadEntriesDeviceGlobalVar[VarName]; - assert((!Entry.getAddress() || Entry.getAddress() == Addr) && - "Resetting with the new address."); if (Entry.getAddress() && hasDeviceGlobalVarEntryInfo(VarName)) { if (Entry.getVarSize().isZero()) { Entry.setVarSize(VarSize); @@ -3086,8 +3084,6 @@ auto &Entry = OffloadEntriesDeviceGlobalVar[VarName]; assert(Entry.isValid() && Entry.getFlags() == Flags && "Entry not initialized!"); - assert((!Entry.getAddress() || Entry.getAddress() == Addr) && - "Resetting with the new address."); if (Entry.getVarSize().isZero()) { Entry.setVarSize(VarSize); Entry.setLinkage(Linkage); @@ -10517,17 +10513,28 @@ scanForTargetRegionsFunctions(II, ParentName); } +static bool isAssumedToBeNotEmitted(const ValueDecl *VD, bool IsDevice) { + Optional DevTy = + OMPDeclareTargetDeclAttr::getDeviceType(VD); + if (!DevTy) + return false; + // Do not emit device_type(nohost) functions for the host. + if (!IsDevice && DevTy == OMPDeclareTargetDeclAttr::DT_NoHost) + return true; + // Do not emit device_type(host) functions for the device. + if (IsDevice && DevTy == OMPDeclareTargetDeclAttr::DT_Host) + return true; + return false; +} + bool CGOpenMPRuntime::emitTargetFunctions(GlobalDecl GD) { // If emitting code for the host, we do not process FD here. Instead we do // the normal code generation. if (!CGM.getLangOpts().OpenMPIsDevice) { - if (const auto *FD = dyn_cast(GD.getDecl())) { - Optional DevTy = - OMPDeclareTargetDeclAttr::getDeviceType(FD); - // Do not emit device_type(nohost) functions for the host. - if (DevTy && *DevTy == OMPDeclareTargetDeclAttr::DT_NoHost) + if (const auto *FD = dyn_cast(GD.getDecl())) + if (isAssumedToBeNotEmitted(cast(FD), + CGM.getLangOpts().OpenMPIsDevice)) return true; - } return false; } @@ -10536,10 +10543,8 @@ if (const auto *FD = dyn_cast(VD)) { StringRef Name = CGM.getMangledName(GD); scanForTargetRegionsFunctions(FD->getBody(), Name); - Optional DevTy = - OMPDeclareTargetDeclAttr::getDeviceType(FD); - // Do not emit device_type(nohost) functions for the host. - if (DevTy && *DevTy == OMPDeclareTargetDeclAttr::DT_Host) + if (isAssumedToBeNotEmitted(cast(FD), + CGM.getLangOpts().OpenMPIsDevice)) return true; } @@ -10549,6 +10554,10 @@ } bool CGOpenMPRuntime::emitTargetGlobalVariable(GlobalDecl GD) { + if (isAssumedToBeNotEmitted(cast(GD.getDecl()), + CGM.getLangOpts().OpenMPIsDevice)) + return true; + if (!CGM.getLangOpts().OpenMPIsDevice) return false; @@ -10621,6 +10630,13 @@ if (CGM.getLangOpts().OMPTargetTriples.empty() && !CGM.getLangOpts().OpenMPIsDevice) return; + + // If we have host/nohost variables, they do not need to be registered. + Optional DevTy = + OMPDeclareTargetDeclAttr::getDeviceType(VD); + if (DevTy && DevTy.getValue() != OMPDeclareTargetDeclAttr::DT_Any) + return; + llvm::Optional Res = OMPDeclareTargetDeclAttr::isDeclareTargetDeclaration(VD); if (!Res) { @@ -10651,6 +10667,10 @@ Linkage = CGM.getLLVMLinkageVarDefinition(VD, /*IsConstant=*/false); // Temp solution to prevent optimizations of the internal variables. if (CGM.getLangOpts().OpenMPIsDevice && !VD->isExternallyVisible()) { + // Do not create a "ref-variable" if the original is not also available + // on the host. + if (!OffloadEntriesInfoManager.hasDeviceGlobalVarEntryInfo(VarName)) + return; std::string RefName = getName({VarName, "ref"}); if (!CGM.GetGlobalValue(RefName)) { llvm::Constant *AddrRef = diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -1356,6 +1356,10 @@ /// \param D Requires declaration void EmitOMPRequiresDecl(const OMPRequiresDecl *D); + /// Emit a code for the allocate directive. + /// \param D The allocate declaration + void EmitOMPAllocateDecl(const OMPAllocateDecl *D); + /// Returns whether the given record has hidden LTO visibility and therefore /// may participate in (single-module) CFI and whole-program vtable /// optimization. diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -2669,19 +2669,24 @@ } bool CodeGenModule::MayBeEmittedEagerly(const ValueDecl *Global) { + // In OpenMP 5.0 variables and function may be marked as + // device_type(host/nohost) and we should not emit them eagerly unless we sure + // that they must be emitted on the host/device. To be sure we need to have + // seen a declare target with an explicit mentioning of the function, we know + // we have if the level of the declare target attribute is -1. Note that we + // check somewhere else if we should emit this at all. + if (LangOpts.OpenMP >= 50 && !LangOpts.OpenMPSimd) { + llvm::Optional ActiveAttr = + OMPDeclareTargetDeclAttr::getActiveAttr(Global); + if (!ActiveAttr || (*ActiveAttr)->getLevel() != (unsigned)-1) + return false; + } + if (const auto *FD = dyn_cast(Global)) { if (FD->getTemplateSpecializationKind() == TSK_ImplicitInstantiation) // Implicit template instantiations may change linkage if they are later // explicitly instantiated, so they should not be emitted eagerly. return false; - // In OpenMP 5.0 function may be marked as device_type(nohost) and we should - // not emit them eagerly unless we sure that the function must be emitted on - // the host. - if (LangOpts.OpenMP >= 50 && !LangOpts.OpenMPSimd && - !LangOpts.OpenMPIsDevice && - !OMPDeclareTargetDeclAttr::getDeviceType(FD) && - !FD->isUsed(/*CheckUsedAttr=*/false) && !FD->isReferenced()) - return false; } if (const auto *VD = dyn_cast(Global)) if (Context.getInlineVariableDefinitionKind(VD) == @@ -4362,7 +4367,8 @@ // Replace all uses of the old global with the new global llvm::Constant *NewPtrForOldDecl = - llvm::ConstantExpr::getBitCast(GV, Entry->getType()); + llvm::ConstantExpr::getPointerBitCastOrAddrSpaceCast(GV, + Entry->getType()); Entry->replaceAllUsesWith(NewPtrForOldDecl); // Erase the old global, since it is no longer used. @@ -5873,6 +5879,7 @@ break; case Decl::OMPAllocate: + EmitOMPAllocateDecl(cast(D)); break; case Decl::OMPDeclareReduction: diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -131,6 +131,7 @@ {OMPD_declare, OMPD_simd, OMPD_declare_simd}, {OMPD_declare, OMPD_target, OMPD_declare_target}, {OMPD_declare, OMPD_variant, OMPD_declare_variant}, + {OMPD_begin_declare, OMPD_target, OMPD_begin_declare_target}, {OMPD_begin_declare, OMPD_variant, OMPD_begin_declare_variant}, {OMPD_end_declare, OMPD_variant, OMPD_end_declare_variant}, {OMPD_distribute, OMPD_parallel, OMPD_distribute_parallel}, @@ -1664,30 +1665,41 @@ return SimpleClauseData(Type, Loc, LOpen, TypeLoc, RLoc); } -Parser::DeclGroupPtrTy Parser::ParseOMPDeclareTargetClauses() { - // OpenMP 4.5 syntax with list of entities. - Sema::NamedDeclSetType SameDirectiveDecls; - SmallVector, - 4> - DeclareTargetDecls; - OMPDeclareTargetDeclAttr::DevTypeTy DT = OMPDeclareTargetDeclAttr::DT_Any; +void Parser::ParseOMPDeclareTargetClauses( + Sema::DeclareTargetContextInfo &DTCI) { SourceLocation DeviceTypeLoc; + bool RequiresToOrLinkClause = false; + bool HasToOrLinkClause = false; while (Tok.isNot(tok::annot_pragma_openmp_end)) { OMPDeclareTargetDeclAttr::MapTypeTy MT = OMPDeclareTargetDeclAttr::MT_To; - if (Tok.is(tok::identifier)) { + bool HasIdentifier = Tok.is(tok::identifier); + if (HasIdentifier) { + // If we see any clause we need a to or link clause. + RequiresToOrLinkClause = true; IdentifierInfo *II = Tok.getIdentifierInfo(); StringRef ClauseName = II->getName(); bool IsDeviceTypeClause = getLangOpts().OpenMP >= 50 && getOpenMPClauseKind(ClauseName) == OMPC_device_type; - // Parse 'to|link|device_type' clauses. - if (!OMPDeclareTargetDeclAttr::ConvertStrToMapTypeTy(ClauseName, MT) && - !IsDeviceTypeClause) { + + bool IsToOrLinkClause = + OMPDeclareTargetDeclAttr::ConvertStrToMapTypeTy(ClauseName, MT); + assert((!IsDeviceTypeClause || !IsToOrLinkClause) && "Cannot be both!"); + + if (!IsDeviceTypeClause && DTCI.Kind == OMPD_begin_declare_target) { Diag(Tok, diag::err_omp_declare_target_unexpected_clause) - << ClauseName << (getLangOpts().OpenMP >= 50 ? 1 : 0); + << ClauseName << 0; break; } + if (!IsDeviceTypeClause && !IsToOrLinkClause) { + Diag(Tok, diag::err_omp_declare_target_unexpected_clause) + << ClauseName << (getLangOpts().OpenMP >= 50 ? 2 : 1); + break; + } + + if (IsToOrLinkClause) + HasToOrLinkClause = true; + // Parse 'device_type' clause and go to next clause if any. if (IsDeviceTypeClause) { Optional DevTypeData = @@ -1697,16 +1709,17 @@ // We already saw another device_type clause, diagnose it. Diag(DevTypeData.getValue().Loc, diag::warn_omp_more_one_device_type_clause); + break; } switch (static_cast(DevTypeData.getValue().Type)) { case OMPC_DEVICE_TYPE_any: - DT = OMPDeclareTargetDeclAttr::DT_Any; + DTCI.DT = OMPDeclareTargetDeclAttr::DT_Any; break; case OMPC_DEVICE_TYPE_host: - DT = OMPDeclareTargetDeclAttr::DT_Host; + DTCI.DT = OMPDeclareTargetDeclAttr::DT_Host; break; case OMPC_DEVICE_TYPE_nohost: - DT = OMPDeclareTargetDeclAttr::DT_NoHost; + DTCI.DT = OMPDeclareTargetDeclAttr::DT_NoHost; break; case OMPC_DEVICE_TYPE_unknown: llvm_unreachable("Unexpected device_type"); @@ -1717,37 +1730,47 @@ } ConsumeToken(); } - auto &&Callback = [this, MT, &DeclareTargetDecls, &SameDirectiveDecls]( - CXXScopeSpec &SS, DeclarationNameInfo NameInfo) { - NamedDecl *ND = Actions.lookupOpenMPDeclareTargetName( - getCurScope(), SS, NameInfo, SameDirectiveDecls); - if (ND) - DeclareTargetDecls.emplace_back(MT, NameInfo.getLoc(), ND); - }; - if (ParseOpenMPSimpleVarList(OMPD_declare_target, Callback, - /*AllowScopeSpecifier=*/true)) + + if (DTCI.Kind == OMPD_declare_target || HasIdentifier) { + auto &&Callback = [this, MT, &DTCI](CXXScopeSpec &SS, + DeclarationNameInfo NameInfo) { + NamedDecl *ND = + Actions.lookupOpenMPDeclareTargetName(getCurScope(), SS, NameInfo); + if (!ND) + return; + Sema::DeclareTargetContextInfo::MapInfo MI{MT, NameInfo.getLoc()}; + bool FirstMapping = DTCI.ExplicitlyMapped.try_emplace(ND, MI).second; + if (!FirstMapping) + Diag(NameInfo.getLoc(), diag::err_omp_declare_target_multiple) + << NameInfo.getName(); + }; + if (ParseOpenMPSimpleVarList(OMPD_declare_target, Callback, + /*AllowScopeSpecifier=*/true)) + break; + } + + if (Tok.is(tok::l_paren)) { + Diag(Tok, + diag::err_omp_begin_declare_target_unexpected_implicit_to_clause); + break; + } + if (!HasIdentifier && Tok.isNot(tok::annot_pragma_openmp_end)) { + Diag(Tok, + diag::err_omp_declare_target_unexpected_clause_after_implicit_to); break; + } // Consume optional ','. if (Tok.is(tok::comma)) ConsumeToken(); } + + // For declare target require at least 'to' or 'link' to be present. + if (DTCI.Kind == OMPD_declare_target && RequiresToOrLinkClause && + !HasToOrLinkClause) + Diag(DTCI.Loc, diag::err_omp_declare_target_missing_to_or_link_clause); + SkipUntil(tok::annot_pragma_openmp_end, StopBeforeMatch); - ConsumeAnyToken(); - for (auto &MTLocDecl : DeclareTargetDecls) { - OMPDeclareTargetDeclAttr::MapTypeTy MT; - SourceLocation Loc; - NamedDecl *ND; - std::tie(MT, Loc, ND) = MTLocDecl; - // device_type clause is applied only to functions. - Actions.ActOnOpenMPDeclareTargetName( - ND, Loc, MT, isa(ND) ? OMPDeclareTargetDeclAttr::DT_Any : DT); - } - SmallVector Decls(SameDirectiveDecls.begin(), - SameDirectiveDecls.end()); - if (Decls.empty()) - return DeclGroupPtrTy(); - return Actions.BuildDeclaratorGroup(Decls); } void Parser::skipUntilPragmaOpenMPEnd(OpenMPDirectiveKind DKind) { @@ -1784,10 +1807,11 @@ SkipUntil(tok::annot_pragma_openmp_end, StopBeforeMatch); } -void Parser::ParseOMPEndDeclareTargetDirective(OpenMPDirectiveKind DKind, +void Parser::ParseOMPEndDeclareTargetDirective(OpenMPDirectiveKind BeginDKind, + OpenMPDirectiveKind EndDKind, SourceLocation DKLoc) { - parseOMPEndDirective(OMPD_declare_target, OMPD_end_declare_target, DKind, - DKLoc, Tok.getLocation(), + parseOMPEndDirective(BeginDKind, OMPD_end_declare_target, EndDKind, DKLoc, + Tok.getLocation(), /* SkipUntilOpenMPEnd */ false); // Skip the last annot_pragma_openmp_end. if (Tok.is(tok::annot_pragma_openmp_end)) @@ -2101,53 +2125,41 @@ ParseOMPDeclareVariantClauses(Ptr, Toks, Loc); return Ptr; } + case OMPD_begin_declare_target: case OMPD_declare_target: { SourceLocation DTLoc = ConsumeAnyToken(); - if (Tok.isNot(tok::annot_pragma_openmp_end)) { - return ParseOMPDeclareTargetClauses(); - } + bool HasClauses = Tok.isNot(tok::annot_pragma_openmp_end); + bool HasImplicitMappings = + DKind == OMPD_begin_declare_target || !HasClauses; + Sema::DeclareTargetContextInfo DTCI(DKind, DTLoc); + if (HasClauses) + ParseOMPDeclareTargetClauses(DTCI); // Skip the last annot_pragma_openmp_end. ConsumeAnyToken(); - if (!Actions.ActOnStartOpenMPDeclareTargetDirective(DTLoc)) - return DeclGroupPtrTy(); - - ParsingOpenMPDirectiveRAII NormalScope(*this, /*Value=*/false); - llvm::SmallVector Decls; - while (Tok.isNot(tok::eof) && Tok.isNot(tok::r_brace)) { - if (Tok.isAnnotation() && Tok.is(tok::annot_pragma_openmp)) { - TentativeParsingAction TPA(*this); - ConsumeAnnotationToken(); - DKind = parseOpenMPDirectiveKind(*this); - if (DKind != OMPD_end_declare_target) - TPA.Revert(); - else - TPA.Commit(); - } - if (DKind == OMPD_end_declare_target) - break; - DeclGroupPtrTy Ptr; - // Here we expect to see some function declaration. - if (AS == AS_none) { - assert(TagType == DeclSpec::TST_unspecified); - MaybeParseCXX11Attributes(Attrs); - ParsingDeclSpec PDS(*this); - Ptr = ParseExternalDeclaration(Attrs, &PDS); - } else { - Ptr = - ParseCXXClassMemberDeclarationWithPragmas(AS, Attrs, TagType, Tag); - } - if (Ptr) { - DeclGroupRef Ref = Ptr.get(); - Decls.append(Ref.begin(), Ref.end()); - } + if (HasImplicitMappings) { + Actions.ActOnStartOpenMPDeclareTargetContext(DTCI); + return nullptr; } - ParseOMPEndDeclareTargetDirective(DKind, DTLoc); - Actions.ActOnFinishOpenMPDeclareTargetDirective(); + Actions.ActOnFinishedOpenMPDeclareTargetContext(DTCI); + llvm::SmallVector Decls; + for (auto &It : DTCI.ExplicitlyMapped) + Decls.push_back(It.first); return Actions.BuildDeclaratorGroup(Decls); } + case OMPD_end_declare_target: { + if (!Actions.isInOpenMPDeclareTargetContext()) { + Diag(Tok, diag::err_omp_unexpected_directive) + << 1 << getOpenMPDirectiveName(DKind); + break; + } + const Sema::DeclareTargetContextInfo &DTCI = + Actions.ActOnOpenMPEndDeclareTargetDirective(); + ParseOMPEndDeclareTargetDirective(DTCI.Kind, DKind, DTCI.Loc); + return nullptr; + } case OMPD_unknown: Diag(Tok, diag::err_omp_unknown_directive); break; @@ -2191,7 +2203,6 @@ case OMPD_parallel_master_taskloop: case OMPD_parallel_master_taskloop_simd: case OMPD_distribute: - case OMPD_end_declare_target: case OMPD_target_update: case OMPD_distribute_parallel_for: case OMPD_distribute_parallel_for_simd: @@ -2570,6 +2581,7 @@ } case OMPD_declare_simd: case OMPD_declare_target: + case OMPD_begin_declare_target: case OMPD_end_declare_target: case OMPD_requires: case OMPD_begin_declare_variant: diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -18436,7 +18436,7 @@ OMPDeclareTargetDeclAttr::getDeviceType(FD->getCanonicalDecl()); // DevTy may be changed later by // #pragma omp declare target to(*) device_type(*). - // Therefore DevTyhaving no value does not imply host. The emission status + // Therefore DevTy having no value does not imply host. The emission status // will be checked again at the end of compilation unit with Final = true. if (DevTy.hasValue()) if (*DevTy == OMPDeclareTargetDeclAttr::DT_Host) diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -2481,8 +2481,8 @@ Optional DevTy = OMPDeclareTargetDeclAttr::getDeviceType(Caller->getMostRecentDecl()); // Ignore host functions during device analyzis. - if (LangOpts.OpenMPIsDevice && DevTy && - *DevTy == OMPDeclareTargetDeclAttr::DT_Host) + if (LangOpts.OpenMPIsDevice && + (!DevTy || *DevTy == OMPDeclareTargetDeclAttr::DT_Host)) return; // Ignore nohost functions during host analyzis. if (!LangOpts.OpenMPIsDevice && DevTy && @@ -19972,7 +19972,8 @@ OMPDefaultmapClause(StartLoc, LParenLoc, MLoc, KindLoc, EndLoc, Kind, M); } -bool Sema::ActOnStartOpenMPDeclareTargetDirective(SourceLocation Loc) { +bool Sema::ActOnStartOpenMPDeclareTargetContext( + DeclareTargetContextInfo &DTCI) { DeclContext *CurLexicalContext = getCurLexicalContext(); if (!CurLexicalContext->isFileContext() && !CurLexicalContext->isExternCContext() && @@ -19981,23 +19982,30 @@ !isa(CurLexicalContext) && !isa(CurLexicalContext) && !isa(CurLexicalContext)) { - Diag(Loc, diag::err_omp_region_not_file_context); + Diag(DTCI.Loc, diag::err_omp_region_not_file_context); return false; } - DeclareTargetNesting.push_back(Loc); + DeclareTargetNesting.push_back(DTCI); return true; } -void Sema::ActOnFinishOpenMPDeclareTargetDirective() { +const Sema::DeclareTargetContextInfo +Sema::ActOnOpenMPEndDeclareTargetDirective() { assert(!DeclareTargetNesting.empty() && - "Unexpected ActOnFinishOpenMPDeclareTargetDirective"); - DeclareTargetNesting.pop_back(); + "check isInOpenMPDeclareTargetContext() first!"); + return DeclareTargetNesting.pop_back_val(); +} + +void Sema::ActOnFinishedOpenMPDeclareTargetContext( + DeclareTargetContextInfo &DTCI) { + for (auto &It : DTCI.ExplicitlyMapped) + ActOnOpenMPDeclareTargetName(It.first, It.second.Loc, It.second.MT, + DTCI.DT); } -NamedDecl * -Sema::lookupOpenMPDeclareTargetName(Scope *CurScope, CXXScopeSpec &ScopeSpec, - const DeclarationNameInfo &Id, - NamedDeclSetType &SameDirectiveDecls) { +NamedDecl *Sema::lookupOpenMPDeclareTargetName(Scope *CurScope, + CXXScopeSpec &ScopeSpec, + const DeclarationNameInfo &Id) { LookupResult Lookup(*this, Id, LookupOrdinaryName); LookupParsedName(Lookup, CurScope, &ScopeSpec, true); @@ -20026,8 +20034,6 @@ Diag(Id.getLoc(), diag::err_omp_invalid_target_decl) << Id.getName(); return nullptr; } - if (!SameDirectiveDecls.insert(cast(ND->getCanonicalDecl()))) - Diag(Id.getLoc(), diag::err_omp_declare_target_multiple) << Id.getName(); return ND; } @@ -20044,32 +20050,35 @@ (ND->isUsed(/*CheckUsedAttr=*/false) || ND->isReferenced())) Diag(Loc, diag::warn_omp_declare_target_after_first_use); + // Explicit declare target lists have precedence. + const unsigned Level = -1; + auto *VD = cast(ND); - Optional DevTy = - OMPDeclareTargetDeclAttr::getDeviceType(VD); - Optional AttrLoc = OMPDeclareTargetDeclAttr::getLocation(VD); - if (DevTy.hasValue() && *DevTy != DT && - (DeclareTargetNesting.empty() || - *AttrLoc != DeclareTargetNesting.back())) { + llvm::Optional ActiveAttr = + OMPDeclareTargetDeclAttr::getActiveAttr(VD); + if (ActiveAttr.hasValue() && ActiveAttr.getValue()->getDevType() != DT && + ActiveAttr.getValue()->getLevel() == Level) { Diag(Loc, diag::err_omp_device_type_mismatch) << OMPDeclareTargetDeclAttr::ConvertDevTypeTyToStr(DT) - << OMPDeclareTargetDeclAttr::ConvertDevTypeTyToStr(*DevTy); + << OMPDeclareTargetDeclAttr::ConvertDevTypeTyToStr( + ActiveAttr.getValue()->getDevType()); return; } - Optional Res = - OMPDeclareTargetDeclAttr::isDeclareTargetDeclaration(VD); - if (!Res || (!DeclareTargetNesting.empty() && - *AttrLoc == DeclareTargetNesting.back())) { - auto *A = OMPDeclareTargetDeclAttr::CreateImplicit( - Context, MT, DT, DeclareTargetNesting.size() + 1, - SourceRange(Loc, Loc)); - ND->addAttr(A); - if (ASTMutationListener *ML = Context.getASTMutationListener()) - ML->DeclarationMarkedOpenMPDeclareTarget(ND, A); - checkDeclIsAllowedInOpenMPTarget(nullptr, ND, Loc); - } else if (*Res != MT) { + if (ActiveAttr.hasValue() && ActiveAttr.getValue()->getMapType() != MT && + ActiveAttr.getValue()->getLevel() == Level) { Diag(Loc, diag::err_omp_declare_target_to_and_link) << ND; + return; } + + if (ActiveAttr.hasValue() && ActiveAttr.getValue()->getLevel() == Level) + return; + + auto *A = OMPDeclareTargetDeclAttr::CreateImplicit(Context, MT, DT, Level, + SourceRange(Loc, Loc)); + ND->addAttr(A); + if (ASTMutationListener *ML = Context.getASTMutationListener()) + ML->DeclarationMarkedOpenMPDeclareTarget(ND, A); + checkDeclIsAllowedInOpenMPTarget(nullptr, ND, Loc); } static void checkDeclInTargetContext(SourceLocation SL, SourceRange SR, @@ -20083,8 +20092,6 @@ (SemaRef.getCurLambda(/*IgnoreNonLambdaCapturingScope=*/true) || SemaRef.getCurBlock() || SemaRef.getCurCapturedRegion()) && VD->hasGlobalStorage()) { - llvm::Optional MapTy = - OMPDeclareTargetDeclAttr::isDeclareTargetDeclaration(VD); if (!MapTy || *MapTy != OMPDeclareTargetDeclAttr::MT_To) { // OpenMP 5.0, 2.12.7 declare target Directive, Restrictions // If a lambda declaration and definition appears between a @@ -20148,15 +20155,19 @@ if ((E || !VD->getType()->isIncompleteType()) && !checkValueDeclInTarget(SL, SR, *this, DSAStack, VD)) return; - if (!E && !OMPDeclareTargetDeclAttr::isDeclareTargetDeclaration(VD)) { + if (!E && isInOpenMPDeclareTargetContext()) { // Checking declaration inside declare target region. if (isa(D) || isa(D) || isa(D)) { + llvm::Optional ActiveAttr = + OMPDeclareTargetDeclAttr::getActiveAttr(VD); + unsigned Level = DeclareTargetNesting.size(); + if (ActiveAttr.hasValue() && ActiveAttr.getValue()->getLevel() >= Level) + return; + DeclareTargetContextInfo &DTCI = DeclareTargetNesting.back(); auto *A = OMPDeclareTargetDeclAttr::CreateImplicit( - Context, OMPDeclareTargetDeclAttr::MT_To, - OMPDeclareTargetDeclAttr::DT_Any, DeclareTargetNesting.size(), - SourceRange(DeclareTargetNesting.back(), - DeclareTargetNesting.back())); + Context, OMPDeclareTargetDeclAttr::MT_To, DTCI.DT, Level, + SourceRange(DTCI.Loc, DTCI.Loc)); D->addAttr(A); if (ASTMutationListener *ML = Context.getASTMutationListener()) ML->DeclarationMarkedOpenMPDeclareTarget(D, A); diff --git a/clang/test/OpenMP/allocate_codegen.cpp b/clang/test/OpenMP/allocate_codegen.cpp --- a/clang/test/OpenMP/allocate_codegen.cpp +++ b/clang/test/OpenMP/allocate_codegen.cpp @@ -30,6 +30,14 @@ KMP_ALLOCATOR_MAX_HANDLE = __UINTPTR_MAX__ }; +// Make sure EXT is `external` even though it was probably +// materialized before we encountered the allocate directive. +// CHECK-DAG: @Use = {{.*}}global i32* @EXT +// CHECK-DAG: @EXT = external {{.*}}global i32 +extern int EXT; +int *Use = &EXT; +#pragma omp allocate(EXT) allocator(omp_const_mem_alloc) + struct St{ int a; }; diff --git a/clang/test/OpenMP/declare_target_codegen.cpp b/clang/test/OpenMP/declare_target_codegen.cpp --- a/clang/test/OpenMP/declare_target_codegen.cpp +++ b/clang/test/OpenMP/declare_target_codegen.cpp @@ -40,7 +40,6 @@ // CHECK-DAG: @pair = {{.*}}addrspace(3) global %struct.PAIR undef // CHECK-DAG: @b ={{ hidden | }}global i32 15, // CHECK-DAG: @d ={{ hidden | }}global i32 0, -// CHECK-DAG: @c = external global i32, // CHECK-DAG: @globals ={{ hidden | }}global %struct.S zeroinitializer, // CHECK-DAG: [[STAT:@.+stat]] = internal global %struct.S zeroinitializer, // CHECK-DAG: [[STAT_REF:@.+]] = internal constant %struct.S* [[STAT]] @@ -252,17 +251,41 @@ // CHECK-DAG: !{{{.+}}virtual_foo #ifdef OMP5 -void host_fun() {} -#pragma omp declare target to(host_fun) device_type(host) -void device_fun() {} -#pragma omp declare target to(device_fun) device_type(nohost) -// HOST5-NOT: define {{.*}}void {{.*}}device_fun{{.*}} -// HOST5: define {{.*}}void {{.*}}host_fun{{.*}} -// HOST5-NOT: define {{.*}}void {{.*}}device_fun{{.*}} - -// DEV5-NOT: define {{.*}}void {{.*}}host_fun{{.*}} -// DEV5: define {{.*}}void {{.*}}device_fun{{.*}} -// DEV5-NOT: define {{.*}}void {{.*}}host_fun{{.*}} +void host_fun1() {} +#pragma omp declare target to(host_fun1) device_type(host) +void device_fun1() {} +#pragma omp declare target to(device_fun1) device_type(nohost) +// HOST5-NOT: define {{.*}}void {{.*}}device_fun1{{.*}} +// HOST5: define {{.*}}void {{.*}}host_fun1{{.*}} +// HOST5-NOT: define {{.*}}void {{.*}}device_fun1{{.*}} + +// DEV5-NOT: define {{.*}}void {{.*}}host_fun1{{.*}} +// DEV5: define {{.*}}void {{.*}}device_fun1{{.*}} +// DEV5-NOT: define {{.*}}void {{.*}}host_fun1{{.*}} + +// Implicit declaration with the "wrong" device type first, +// explicit declaration afterwards. +#pragma omp begin declare target device_type(nohost) +void host_fun2() {} +#pragma omp end declare target +#pragma omp begin declare target device_type(host) +void device_fun2() {} +#pragma omp end declare target + +// TODO: According to OpenMP 5.1 this is an OK use, arguably this +// is not really useful and makes it hard/costly to implement. +// We should remove the non-begin/end forms in OpenMP 6.0. +#pragma omp declare target to(host_fun2) device_type(host) +#pragma omp declare target to(device_fun2) device_type(nohost) + +// HOST5-NOT: define {{.*}}void {{.*}}device_fun2{{.*}} +// HOST5: define {{.*}}void {{.*}}host_fun2{{.*}} +// HOST5-NOT: define {{.*}}void {{.*}}device_fun2{{.*}} + +// DEV5-NOT: define {{.*}}void {{.*}}host_fun2{{.*}} +// DEV5: define {{.*}}void {{.*}}device_fun2{{.*}} +// DEV5-NOT: define {{.*}}void {{.*}}host_fun2{{.*}} + #endif // OMP5 struct PAIR { diff --git a/clang/test/OpenMP/declare_target_codegen_globalization.cpp b/clang/test/OpenMP/declare_target_codegen_globalization.cpp --- a/clang/test/OpenMP/declare_target_codegen_globalization.cpp +++ b/clang/test/OpenMP/declare_target_codegen_globalization.cpp @@ -37,11 +37,13 @@ // CHECK: define {{.*}}[[BAR]]() // CHECK: alloca i32, // CHECK: [[A_LOCAL_ADDR:%.+]] = alloca i32, +// CHECK: [[IS_IN_PAR:%.+]] = icmp eq i16 {{.*}}, 0 // CHECK: [[RES:%.+]] = call i8 @__kmpc_is_spmd_exec_mode() // CHECK: [[IS_SPMD:%.+]] = icmp ne i8 [[RES]], 0 // CHECK: br i1 [[IS_SPMD]], label // CHECK: br label -// CHECK: [[RES:%.+]] = call i8* @__kmpc_data_sharing_coalesced_push_stack(i64 128, i16 0) +// CHECK: [[STACK_SIZE:%.+]] = select i1 [[IS_IN_PAR]], i64 4, i64 128 +// CHECK: [[RES:%.+]] = call i8* @__kmpc_data_sharing_coalesced_push_stack(i64 [[STACK_SIZE]], i16 0) // CHECK: [[GLOBALS:%.+]] = bitcast i8* [[RES]] to [[GLOBAL_ST:%.+]]* // CHECK: br label // CHECK: [[ITEMS:%.+]] = phi [[GLOBAL_ST]]* [ null, {{.+}} ], [ [[GLOBALS]], {{.+}} ] @@ -49,7 +51,9 @@ // CHECK: [[TID:%.+]] = call i32 @llvm.nvvm.read.ptx.sreg.tid.x() // CHECK: [[LID:%.+]] = and i32 [[TID]], 31 // CHECK: [[A_GLOBAL_ADDR:%.+]] = getelementptr inbounds [32 x i32], [32 x i32]* [[A_ADDR]], i32 0, i32 [[LID]] -// CHECK: [[A_ADDR:%.+]] = select i1 [[IS_SPMD]], i32* [[A_LOCAL_ADDR]], i32* [[A_GLOBAL_ADDR]] +// CHECK: [[A_GLOBAL_ADDR_SINGLE:%.+]] = getelementptr inbounds %struct._globalized_locals_ty.0, %struct._globalized_locals_ty.0* %{{.*}}, i32 0, i32 0 +// CHECK: [[A_GLOBAL_ADDR_SEL:%.+]] = select i1 [[IS_IN_PAR]], i32* [[A_GLOBAL_ADDR_SINGLE]], i32* [[A_GLOBAL_ADDR]] +// CHECK: [[A_ADDR:%.+]] = select i1 [[IS_SPMD]], i32* [[A_LOCAL_ADDR]], i32* [[A_GLOBAL_ADDR_SEL]] // CHECK: call {{.*}}[[FOO]](i32* nonnull align {{[0-9]+}} dereferenceable{{.*}} [[A_ADDR]]) // CHECK: br i1 [[IS_SPMD]], label // CHECK: [[BC:%.+]] = bitcast [[GLOBAL_ST]]* [[ITEMS]] to i8* diff --git a/clang/test/OpenMP/declare_target_device_only_compilation.cpp b/clang/test/OpenMP/declare_target_device_only_compilation.cpp deleted file mode 100644 --- a/clang/test/OpenMP/declare_target_device_only_compilation.cpp +++ /dev/null @@ -1,20 +0,0 @@ -// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple powerpc64le-unknown-unknown -fopenmp-targets=powerpc64le-ibm-linux-gnu -emit-llvm-bc %s -o %t-ppc-host.bc -// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple powerpc64le-unknown-unknown -emit-llvm %s -fopenmp-is-device -fopenmp-host-ir-file-path %t-ppc-host.bc -o - | FileCheck %s - -// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple i386-pc-linux-gnu -fopenmp-targets=i386-pc-linux-gnu -emit-llvm-bc %s -o %t-i386-host.bc -// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple i386-pc-linux-gnu -emit-llvm %s -fopenmp-is-device -fopenmp-host-ir-file-path %t-i386-host.bc -o - | FileCheck %s - -// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple x86_64-unknown-linux-gnu -fopenmp-targets=x86_64-unknown-linux-gnu -emit-llvm-bc %s -o %t-x86_64-host.bc -// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple x86_64-unknown-linux-gnu -emit-llvm %s -fopenmp-is-device -fopenmp-host-ir-file-path %t-x86_64-host.bc -o - | FileCheck %s - -// expected-no-diagnostics - -#pragma omp declare target -#pragma omp begin declare variant match(device={kind(nohost)}) -int G1; -#pragma omp end declare variant -#pragma omp end declare target - -// CHECK: @[[G:.+]] = hidden {{.*}}global i32 0, align 4 -// CHECK: !omp_offload.info = !{!0} -// CHECK: !0 = !{i32 1, !"[[G]]", i32 0, i32 0} diff --git a/clang/test/OpenMP/declare_target_messages.cpp b/clang/test/OpenMP/declare_target_messages.cpp --- a/clang/test/OpenMP/declare_target_messages.cpp +++ b/clang/test/OpenMP/declare_target_messages.cpp @@ -17,13 +17,16 @@ void f(); #pragma omp end declare target shared(a) // expected-warning {{extra tokens at the end of '#pragma omp end declare target' are ignored}} -#pragma omp declare target map(a) // omp45-error {{unexpected 'map' clause, only 'to' or 'link' clauses expected}} omp5-error {{unexpected 'map' clause, only 'to', 'link' or 'device_type' clauses expected}} +#pragma omp declare target map(a) // expected-error {{expected at least one 'to' or 'link' clause}} omp45-error {{unexpected 'map' clause, only 'to' or 'link' clauses expected}} omp5-error {{unexpected 'map' clause, only 'to', 'link' or 'device_type' clauses expected}} #pragma omp declare target to(foo1) // expected-error {{use of undeclared identifier 'foo1'}} #pragma omp declare target link(foo2) // expected-error {{use of undeclared identifier 'foo2'}} -#pragma omp declare target to(f) device_type(any) device_type(any) device_type(host) // omp45-error {{unexpected 'device_type' clause, only 'to' or 'link' clauses expected}} omp5-warning 2 {{more than one 'device_type' clause is specified}} omp5-error {{'device_type(host)' does not match previously specified 'device_type(any)' for the same declaration}} +#pragma omp declare target to(f) device_type(host) // omp45-error {{unexpected 'device_type' clause, only 'to' or 'link' clauses expected}} dev5-note {{marked as 'device_type(host)' here}} + +void q(); +#pragma omp declare target to(q) device_type(any) device_type(any) device_type(host) // omp45-error {{unexpected 'device_type' clause, only 'to' or 'link' clauses expected}} omp5-warning {{more than one 'device_type' clause is specified}} void c(); @@ -118,7 +121,8 @@ g = object.method(); g += object.method1(); g += object1.method() + p; - f(); + f(); // dev5-error {{function with 'device_type(host)' is not available on device}} + q(); c(); } #pragma omp declare target @@ -150,10 +154,10 @@ } 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 +int x; +} // namespace +#pragma omp end declare target #pragma omp declare target link(S) // expected-error {{'S' used in declare target directive is not a variable or a function name}} @@ -187,4 +191,10 @@ void any7() {device();} // host5-error {{function with 'device_type(nohost)' is not available on host}} void any8() {any2();} -#pragma omp declare target // expected-error {{expected '#pragma omp end declare target'}} expected-note {{to match this '#pragma omp declare target'}} +int MultiDevTy; +#pragma omp declare target to(MultiDevTy) device_type(any) // omp45-error {{unexpected 'device_type' clause, only 'to' or 'link' clauses expected}} +#pragma omp declare target to(MultiDevTy) device_type(host) // omp45-error {{unexpected 'device_type' clause, only 'to' or 'link' clauses expected}} omp5-error {{'device_type(host)' does not match previously specified 'device_type(any)' for the same declaration}} +#pragma omp declare target to(MultiDevTy) device_type(nohost) // omp45-error {{unexpected 'device_type' clause, only 'to' or 'link' clauses expected}} omp5-error {{'device_type(nohost)' does not match previously specified 'device_type(any)' for the same declaration}} + +// TODO: Issue an error message error {{expected '#pragma omp end declare target'}} note {{to match this '#pragma omp declare target'}} +#pragma omp declare target diff --git a/clang/test/OpenMP/declare_target_only_one_side_compilation.cpp b/clang/test/OpenMP/declare_target_only_one_side_compilation.cpp new file mode 100644 --- /dev/null +++ b/clang/test/OpenMP/declare_target_only_one_side_compilation.cpp @@ -0,0 +1,75 @@ +// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple powerpc64le-unknown-unknown -fopenmp-targets=powerpc64le-ibm-linux-gnu -emit-llvm %s -o - | FileCheck %s --check-prefix HOST +// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple powerpc64le-unknown-unknown -fopenmp-targets=powerpc64le-ibm-linux-gnu -emit-llvm-bc %s -o %t-ppc-host.bc +// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple powerpc64le-unknown-unknown -emit-llvm %s -fopenmp-is-device -fopenmp-host-ir-file-path %t-ppc-host.bc -o - | FileCheck %s --check-prefix DEVICE + +// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple i386-pc-linux-gnu -fopenmp-targets=i386-pc-linux-gnu -emit-llvm %s -o - | FileCheck %s --check-prefix HOST +// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple i386-pc-linux-gnu -fopenmp-targets=i386-pc-linux-gnu -emit-llvm-bc %s -o %t-i386-host.bc +// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple i386-pc-linux-gnu -emit-llvm %s -fopenmp-is-device -fopenmp-host-ir-file-path %t-i386-host.bc -o - | FileCheck %s --check-prefix DEVICE + +// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple x86_64-unknown-linux-gnu -fopenmp-targets=x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s --check-prefix HOST +// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple x86_64-unknown-linux-gnu -fopenmp-targets=x86_64-unknown-linux-gnu -emit-llvm-bc %s -o %t-x86_64-host.bc +// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple x86_64-unknown-linux-gnu -emit-llvm %s -fopenmp-is-device -fopenmp-host-ir-file-path %t-x86_64-host.bc -o - | FileCheck %s --check-prefix DEVICE + +// expected-no-diagnostics + +#pragma omp declare target +#pragma omp begin declare variant match(device = {kind(nohost)}) +int G1; +static int G2; +#pragma omp end declare variant +#pragma omp end declare target + +#pragma omp begin declare target device_type(nohost) +int G3; +static int G4; +#pragma omp end declare target + +#pragma omp declare target +int G5; +static int G6; +#pragma omp end declare target + +#pragma omp declare target to(G5, G6) device_type(nohost) + +#pragma omp begin declare target device_type(host) +int G7; +static int G8; +#pragma omp end declare target + +#pragma omp declare target +int G9; +static int G10; +#pragma omp end declare target + +int G11; +static int G12; +#pragma omp declare target to(G9, G10, G11, G12) device_type(host) + +// TODO: The code below should probably work but it is not 100% clear. +#if 0 +#pragma omp declare target +#pragma omp begin declare variant match(device = {kind(host)}) +int GX; +static int GY; +#pragma omp end declare variant +#pragma omp end declare target +#endif + +// TODO: It is odd, probably wrong, that we don't mangle all variables. + +// DEVICE-DAG: @G1 = hidden {{.*}}global i32 0, align 4 +// DEVICE-DAG: @_ZL2G2 = internal {{.*}}global i32 0, align 4 +// DEVICE-DAG: @G3 = hidden {{.*}}global i32 0, align 4 +// DEVICE-DAG: @_ZL2G4 = internal {{.*}}global i32 0, align 4 +// DEVICE-DAG: @G5 = hidden {{.*}}global i32 0, align 4 +// DEVICE-DAG: @_ZL2G6 = internal {{.*}}global i32 0, align 4 +// DEVICE-NOT: ref +// DEVICE-NOT: llvm.used +// DEVICE-NOT: omp_offload + +// HOST-DAG: @G7 = dso_local global i32 0, align 4 +// HOST-DAG: @_ZL2G8 = internal global i32 0, align 4 +// HOST-DAG: @G9 = dso_local global i32 0, align 4 +// HOST-DAG: @_ZL3G10 = internal global i32 0, align 4 +// HOST-DAG: @G11 = dso_local global i32 0, align 4 +// HOST-DAG: @_ZL3G12 = internal global i32 0, align 4 diff --git a/clang/test/OpenMP/nvptx_allocate_codegen.cpp b/clang/test/OpenMP/nvptx_allocate_codegen.cpp --- a/clang/test/OpenMP/nvptx_allocate_codegen.cpp +++ b/clang/test/OpenMP/nvptx_allocate_codegen.cpp @@ -17,6 +17,35 @@ extern const omp_allocator_handle_t omp_cgroup_mem_alloc; extern const omp_allocator_handle_t omp_pteam_mem_alloc; extern const omp_allocator_handle_t omp_thread_mem_alloc; +#pragma omp end declare target + +// Make sure EXT1-3 are `external` and have address space 4 even though they +// might have been materialized before we encountered the allocate directive. +#pragma omp declare target +extern int EXT1; +int *Use1 = &EXT1; +#pragma omp end declare target +// allocate after the end declare target +#pragma omp allocate(EXT1) allocator(omp_const_mem_alloc) +// CHECK-DAG: @EXT1 = external addrspace(4) {{.*}}global i32 +// CHECK-DAG: @Use1 = {{.*}}global + +extern int EXT2; +#pragma omp declare target(EXT2) +int *Use2 = &EXT2; +#pragma omp declare target(Use2) +// allocate after the declare target +#pragma omp allocate(EXT2) allocator(omp_const_mem_alloc) +// CHECK-DAG: @EXT2 = external addrspace(4) {{.*}}global i32 +// CHECK-DAG: @Use2 = {{.*}}global + +#pragma omp declare target +extern int EXT3; +int *Use3 = &EXT3; +// allocate part of the declare target +#pragma omp allocate(EXT3) allocator(omp_const_mem_alloc) +// CHECK-DAG: @EXT3 = external addrspace(4) {{.*}}global i32 +// CHECK-DAG: @Use3 = {{.*}}global struct St{ int a; @@ -81,7 +110,19 @@ } } -#pragma omp end declare target +int LateAllocateDecl1; +#pragma omp declare target + +int LateAllocateDecl2; +#pragma omp declare target to(LateAllocateDecl2) + +int LateAllocateDecl3; +#pragma omp declare target(LateAllocateDecl3) + +#pragma omp allocate(LateAllocateDecl1) allocator(omp_const_mem_alloc) +#pragma omp allocate(LateAllocateDecl2) allocator(omp_const_mem_alloc) +#pragma omp allocate(LateAllocateDecl3) allocator(omp_const_mem_alloc) + #endif // CHECK-LABEL: define {{[^@]+}}@main // CHECK-SAME: () #[[ATTR0:[0-9]+]] { diff --git a/clang/test/OpenMP/nvptx_declare_target_var_ctor_dtor_codegen.cpp b/clang/test/OpenMP/nvptx_declare_target_var_ctor_dtor_codegen.cpp --- a/clang/test/OpenMP/nvptx_declare_target_var_ctor_dtor_codegen.cpp +++ b/clang/test/OpenMP/nvptx_declare_target_var_ctor_dtor_codegen.cpp @@ -34,21 +34,16 @@ #pragma omp declare target (bar) int caz() { return 0; } -// DEVICE-DAG: define{{ hidden | }}i32 [[FOO:@.*foo.*]]() // DEVICE-DAG: define{{ hidden | }}i32 [[BAR:@.*bar.*]]() -// DEVICE-DAG: define{{ hidden | }}i32 [[BAZ:@.*baz.*]]() -// DEVICE-DAG: define{{ hidden | }}i32 [[DOO:@.*doo.*]]() -// DEVICE-DAG: define{{ hidden | }}i32 [[CAR:@.*car.*]]() -// DEVICE-DAG: define{{ hidden | }}i32 [[CAZ:@.*caz.*]]() static int c = foo() + bar() + baz(); #pragma omp declare target (c) -// HOST-DAG: @[[C_CTOR:__omp_offloading__.+_c_l44_ctor]] = private constant i8 0 -// DEVICE-DAG: define internal void [[C_CTOR:@__omp_offloading__.+_c_l44_ctor]]() -// DEVICE-DAG: call i32 [[FOO]]() -// DEVICE-DAG: call i32 [[BAR]]() -// DEVICE-DAG: call i32 [[BAZ]]() -// DEVICE-DAG: ret void +// HOST-DAG: @[[C_CTOR:__omp_offloading__.+_c_l39_ctor]] = private constant i8 0 + +// DEVICE-DAG: define internal void [[C_CTOR:@__omp_offloading__.+_c_l39_ctor]]() +// DEVICE-DAG: define{{ hidden | }}i32 [[FOO:@.*foo.*]]() +// DEVICE-DAG: define{{ hidden | }}i32 [[BAZ:@.*baz.*]]() +// DEVICE-DAG: define{{ hidden | }}i32 [[DOO:@.*doo.*]]() struct S { int a; @@ -60,17 +55,14 @@ #pragma omp declare target S cd = doo() + car() + caz() + baz(); #pragma omp end declare target -// HOST-DAG: @[[CD_CTOR:__omp_offloading__.+_cd_l61_ctor]] = private constant i8 0 -// DEVICE-DAG: define internal void [[CD_CTOR:@__omp_offloading__.+_cd_l61_ctor]]() -// DEVICE-DAG: call i32 [[DOO]]() -// DEVICE-DAG: call i32 [[CAR]]() -// DEVICE-DAG: call i32 [[CAZ]]() -// DEVICE-DAG: ret void - -// HOST-DAG: @[[CD_DTOR:__omp_offloading__.+_cd_l61_dtor]] = private constant i8 0 -// DEVICE-DAG: define internal void [[CD_DTOR:@__omp_offloading__.+_cd_l61_dtor]]() -// DEVICE-DAG: call void -// DEVICE-DAG: ret void +// HOST-DAG: @[[CD_CTOR:__omp_offloading__.+_cd_l56_ctor]] = private constant i8 0 +// DEVICE-DAG: define internal void [[CD_CTOR:@__omp_offloading__.+_cd_l56_ctor]]() + +// DEVICE-DAG: define{{ hidden | }}i32 [[CAR:@.*car.*]]() +// DEVICE-DAG: define{{ hidden | }}i32 [[CAZ:@.*caz.*]]() + +// HOST-DAG: @[[CD_DTOR:__omp_offloading__.+_cd_l56_dtor]] = private constant i8 0 +// DEVICE-DAG: define internal void [[CD_DTOR:@__omp_offloading__.+_cd_l56_dtor]]() // HOST-DAG: @.omp_offloading.entry_name{{.*}} = internal unnamed_addr constant [{{[0-9]+}} x i8] c"[[C_ADDR]]\00" // HOST-DAG: @.omp_offloading.entry.[[C_ADDR]] = weak{{.*}} constant %struct.__tgt_offload_entry { i8* bitcast (i32* @[[C_ADDR]] to i8*), i8* getelementptr inbounds ([{{[0-9]+}} x i8], [{{[0-9]+}} x i8]* @.omp_offloading.entry_name{{.*}}, i32 0, i32 0), i64 4, i32 0, i32 0 }, section "omp_offloading_entries", align 1 @@ -91,13 +83,13 @@ return 0; } -// DEVICE: define weak{{.*}} void @__omp_offloading_{{.*}}_{{.*}}maini1{{.*}}_l[[@LINE-7]](i32* nonnull align {{[0-9]+}} dereferenceable{{[^,]*}} -// DEVICE: [[C:%.+]] = load i32, i32* [[C_ADDR]], -// DEVICE: store i32 [[C]], i32* % +// DEVICE-DAG: define weak{{.*}} void @__omp_offloading_{{.*}}_{{.*}}maini1{{.*}}_l[[@LINE-7]](i32* nonnull align {{[0-9]+}} dereferenceable{{[^,]*}} +// DEVICE-DAG: [[C:%.+]] = load i32, i32* [[C_ADDR]], +// DEVICE-DAG: store i32 [[C]], i32* % -// HOST: define internal void @__omp_offloading_{{.*}}_{{.*}}maini1{{.*}}_l[[@LINE-11]](i32* nonnull align {{[0-9]+}} dereferenceable{{.*}}) -// HOST: [[C:%.*]] = load i32, i32* @[[C_ADDR]], -// HOST: store i32 [[C]], i32* % +// HOST-DAG: define internal void @__omp_offloading_{{.*}}_{{.*}}maini1{{.*}}_l[[@LINE-11]](i32* nonnull align {{[0-9]+}} dereferenceable{{.*}}) +// HOST-DAG: [[C:%.*]] = load i32, i32* @[[C_ADDR]], +// HOST-DAG: store i32 [[C]], i32* % // HOST-DAG: !{i32 1, !"[[CD_ADDR]]", i32 0, i32 {{[0-9]+}}} // HOST-DAG: !{i32 1, !"[[C_ADDR]]", i32 0, i32 {{[0-9]+}}} diff --git a/clang/test/OpenMP/nvptx_declare_variant_name_mangling.cpp b/clang/test/OpenMP/nvptx_declare_variant_name_mangling.cpp --- a/clang/test/OpenMP/nvptx_declare_variant_name_mangling.cpp +++ b/clang/test/OpenMP/nvptx_declare_variant_name_mangling.cpp @@ -6,8 +6,8 @@ // CHECK-DAG: @_Z3barv // CHECK-DAG: @_Z3bazv -// CHECK-DAG: @"_Z53bar$ompvariant$S2$s7$Pnvptx$Pnvptx64$S3$s9$Pmatch_anyv" -// CHECK-DAG: @"_Z53baz$ompvariant$S2$s7$Pnvptx$Pnvptx64$S3$s9$Pmatch_anyv" +// CHECK-DAG: define{{.*}}@"_Z53bar$ompvariant$S2$s7$Pnvptx$Pnvptx64$S3$s9$Pmatch_anyv" +// CHECK-DAG: define{{.*}}@"_Z53baz$ompvariant$S2$s7$Pnvptx$Pnvptx64$S3$s9$Pmatch_anyv" // CHECK-DAG: call i32 @"_Z53bar$ompvariant$S2$s7$Pnvptx$Pnvptx64$S3$s9$Pmatch_anyv"() // CHECK-DAG: call i32 @"_Z53baz$ompvariant$S2$s7$Pnvptx$Pnvptx64$S3$s9$Pmatch_anyv"() diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llvm/include/llvm/Frontend/OpenMP/OMP.td --- a/llvm/include/llvm/Frontend/OpenMP/OMP.td +++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td @@ -922,6 +922,13 @@ VersionedClause ]; } +def OMP_BeginDeclareTarget : Directive<"begin declare target"> { + let allowedClauses = [ + VersionedClause, + VersionedClause, + VersionedClause, + ]; +} def OMP_DeclareTarget : Directive<"declare target"> { let allowedClauses = [ VersionedClause,