Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -54,6 +54,7 @@ #include "llvm/ADT/SmallBitVector.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringSet.h" #include "llvm/ADT/TinyPtrVector.h" #include #include @@ -8622,6 +8623,10 @@ void *VarDataSharingAttributesStack; /// Set to true inside '#pragma omp declare target' region. bool IsInOpenMPDeclareTargetContext = false; + /// Functions that were 'declare target' during host CodeGen. + llvm::StringSet<> DeclareTargetFunctionsHost; + void ReadDeclareTargetMetadata(); + /// Initialization of data-sharing attributes stack. void InitDataSharingAttributesStack(); void DestroyDataSharingAttributesStack(); @@ -8759,11 +8764,14 @@ } /// Return true inside OpenMP target region. bool isInOpenMPTargetExecutionDirective() const; - /// Return true if (un)supported features for the current target should be - /// diagnosed if OpenMP (offloading) is enabled. - bool shouldDiagnoseTargetSupportFromOpenMP() const { - return !getLangOpts().OpenMPIsDevice || isInOpenMPDeclareTargetContext() || - isInOpenMPTargetExecutionDirective(); + /// Return true if checks should be skipped during device compilation. + bool skipChecksDuringOpenMPDeviceCompilation(FunctionDecl *FD) const { + if (!getLangOpts().OpenMPIsDevice) + return false; + + bool InDeclareTargetFunction = + FD ? FD->hasAttr() : false; + return !InDeclareTargetFunction && !isInOpenMPTargetExecutionDirective(); } /// Return the number of captured regions created for an OpenMP directive. Index: lib/CodeGen/CGOpenMPRuntime.h =================================================================== --- lib/CodeGen/CGOpenMPRuntime.h +++ lib/CodeGen/CGOpenMPRuntime.h @@ -598,6 +598,8 @@ OffloadEntriesDeviceGlobalVarTy OffloadEntriesDeviceGlobalVar; }; OffloadEntriesInfoManagerTy OffloadEntriesInfoManager; + /// List of 'declare target' functions. + llvm::SmallDenseSet DeclareTargetFunctions; bool ShouldMarkAsGlobal = true; llvm::SmallDenseSet AlreadyEmittedTargetFunctions; @@ -1467,6 +1469,16 @@ virtual void emitDeclareSimdFunction(const FunctionDecl *FD, llvm::Function *Fn); + /// Marks function \a Fn and called functions as 'declare target' to limit + /// Sema analysis during device compilation. + /// \param Fn LLVM function that must be marked with 'declare target' + /// attributes. + virtual void emitDeclareTargetFunction(llvm::Function *Fn); + + /// Creates metadata for all 'declare target' functions. This includes both + /// explicit and as well as implicit ones. + virtual void createDeclareTargetMetadata(); + /// Emit initialization for doacross loop nesting support. /// \param D Loop-based construct used in doacross nesting construct. virtual void emitDoacrossInit(CodeGenFunction &CGF, const OMPLoopDirective &D, Index: lib/CodeGen/CGOpenMPRuntime.cpp =================================================================== --- lib/CodeGen/CGOpenMPRuntime.cpp +++ lib/CodeGen/CGOpenMPRuntime.cpp @@ -11,23 +11,25 @@ // //===----------------------------------------------------------------------===// +#include "CGOpenMPRuntime.h" #include "CGCXXABI.h" #include "CGCleanup.h" -#include "CGOpenMPRuntime.h" #include "CGRecordLayout.h" #include "CodeGenFunction.h" -#include "clang/CodeGen/ConstantInitBuilder.h" #include "clang/AST/Decl.h" #include "clang/AST/StmtOpenMP.h" #include "clang/Basic/BitmaskEnum.h" +#include "clang/CodeGen/ConstantInitBuilder.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/IR/CallSite.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/GlobalValue.h" +#include "llvm/IR/Metadata.h" #include "llvm/IR/Value.h" #include "llvm/Support/Format.h" #include "llvm/Support/raw_ostream.h" #include +#include using namespace clang; using namespace CodeGen; @@ -3606,6 +3608,7 @@ Entry.setID(ID); Entry.setFlags(Flags); } else { + assert(isa(Addr) && "Expecting function in host code"); OffloadEntryInfoTargetRegion Entry(OffloadingEntriesNum, Addr, ID, Flags); OffloadEntriesTargetRegion[DeviceID][FileID][ParentName][LineNum] = Entry; ++OffloadingEntriesNum; @@ -8752,6 +8755,61 @@ } } +void CGOpenMPRuntime::emitDeclareTargetFunction(llvm::Function *Fn) { + assert(!CGM.getLangOpts().OpenMPIsDevice && "Expecting host codegen!"); + DeclareTargetFunctions.insert(Fn); +} + +void CGOpenMPRuntime::createDeclareTargetMetadata() { + if (CGM.getLangOpts().OpenMPIsDevice) + return; + + // Determine all referenced functions. These are implicitly 'declare target'. + std::deque Functions; + + OffloadEntriesInfoManager.actOnTargetRegionEntriesInfo( + [&Functions]( + unsigned, unsigned, StringRef, unsigned, + const OffloadEntriesInfoManagerTy::OffloadEntryInfoTargetRegion &E) { + llvm::Function *F = dyn_cast(E.getAddress()); + assert(F && "Expecting outlined function"); + Functions.push_back(F); + }); + for (auto &F : DeclareTargetFunctions) { + Functions.push_back(F); + } + + while (!Functions.empty()) { + auto &F = Functions.front(); + Functions.pop_front(); + + // Iterate over all instructions that may reference other functions. + for (auto &BB : *F) { + for (auto &I : BB) { + for (auto &Op : I.operands()) { + auto *ReferencedF = dyn_cast(Op->stripPointerCasts()); + if (ReferencedF && !ReferencedF->empty() && + DeclareTargetFunctions.insert(ReferencedF).second) + Functions.push_back(ReferencedF); + } + } + } + } + + // Emit metadata. + llvm::Module &M = CGM.getModule(); + llvm::LLVMContext &C = M.getContext(); + llvm::NamedMDNode *MD = + M.getOrInsertNamedMetadata("omp_offload.declare_target"); + + for (auto &F : DeclareTargetFunctions) { + if (F->hasName()) { + llvm::MDString *Name = llvm::MDString::get(C, F->getName()); + MD->addOperand(llvm::MDNode::get(C, Name)); + } + } +} + namespace { /// Cleanup action for doacross support. class DoacrossCleanupTy final : public EHScopeStack::Cleanup { Index: lib/CodeGen/CodeGenModule.cpp =================================================================== --- lib/CodeGen/CodeGenModule.cpp +++ lib/CodeGen/CodeGenModule.cpp @@ -409,6 +409,7 @@ AddGlobalCtor(CudaCtorFunction); } if (OpenMPRuntime) { + OpenMPRuntime->createDeclareTargetMetadata(); if (llvm::Function *OpenMPRegistrationFunction = OpenMPRuntime->emitRegistrationFunction()) { auto ComdatKey = OpenMPRegistrationFunction->hasComdat() ? @@ -1563,8 +1564,14 @@ if (!CodeGenOpts.SanitizeCfiCrossDso) CreateFunctionTypeMetadataForIcall(FD, F); - if (getLangOpts().OpenMP && FD->hasAttr()) - getOpenMPRuntime().emitDeclareSimdFunction(FD, F); + if (getLangOpts().OpenMP) { + if (FD->hasAttr()) + getOpenMPRuntime().emitDeclareSimdFunction(FD, F); + + if (!getLangOpts().OpenMPIsDevice && + FD->hasAttr()) + getOpenMPRuntime().emitDeclareTargetFunction(F); + } } void CodeGenModule::addUsedGlobal(llvm::GlobalValue *GV) { Index: lib/Sema/CMakeLists.txt =================================================================== --- lib/Sema/CMakeLists.txt +++ lib/Sema/CMakeLists.txt @@ -1,4 +1,5 @@ set(LLVM_LINK_COMPONENTS + Core Support ) Index: lib/Sema/Sema.cpp =================================================================== --- lib/Sema/Sema.cpp +++ lib/Sema/Sema.cpp @@ -169,6 +169,8 @@ // Initilization of data sharing attributes stack for OpenMP InitDataSharingAttributesStack(); + // Read metadata about 'declare target' functions. + ReadDeclareTargetMetadata(); std::unique_ptr Callbacks = llvm::make_unique(); Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -44,6 +44,8 @@ #include "clang/Sema/Template.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/Triple.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Module.h" #include #include #include @@ -5530,7 +5532,15 @@ CurContext->addHiddenDecl(New); } - if (isInOpenMPDeclareTargetContext()) + auto *FD = dyn_cast(New); + if (getLangOpts().OpenMPIsDevice && FD && + DeclareTargetFunctionsHost.size() > 0) { + // FIXME: This should be mangled. + std::string Name = FD->getNameAsString(); + if (DeclareTargetFunctionsHost.count(Name) != 0) { + checkDeclIsAllowedInOpenMPTarget(nullptr, New); + } + } else if (isInOpenMPDeclareTargetContext()) checkDeclIsAllowedInOpenMPTarget(nullptr, New); return New; Index: lib/Sema/SemaOpenMP.cpp =================================================================== --- lib/Sema/SemaOpenMP.cpp +++ lib/Sema/SemaOpenMP.cpp @@ -29,6 +29,8 @@ #include "clang/Sema/ScopeInfo.h" #include "clang/Sema/SemaInternal.h" #include "llvm/ADT/PointerEmbeddedInt.h" +#include "llvm/IR/Metadata.h" +#include "llvm/IR/Module.h" using namespace clang; //===----------------------------------------------------------------------===// @@ -10598,8 +10600,9 @@ if ((OASE && !ConstantLengthOASE) || (!OASE && !ASE && D->getType().getNonReferenceType()->isVariablyModifiedType())) { + auto *FD = S.getCurFunctionDecl(); if (!Context.getTargetInfo().isVLASupported() && - S.shouldDiagnoseTargetSupportFromOpenMP()) { + !S.skipChecksDuringOpenMPDeviceCompilation(FD)) { S.Diag(ELoc, diag::err_omp_reduction_vla_unsupported) << !!OASE; S.Diag(ELoc, diag::note_vla_unsupported); continue; @@ -13021,6 +13024,29 @@ } } +void Sema::ReadDeclareTargetMetadata() { + if (!getLangOpts().OpenMPIsDevice) + return; + + const llvm::Module *OpenMPHostIR = getASTContext().getOpenMPHostIR(); + if (!OpenMPHostIR) + return; + + llvm::NamedMDNode *MD = + OpenMPHostIR->getNamedMetadata("omp_offload.declare_target"); + if (!MD) + return; + + for (auto &&NameNode : MD->operands()) { + // Skip empty nodes (which should never happen...) + if (NameNode->getNumOperands() == 0) + continue; + auto *FuncName = dyn_cast(NameNode->getOperand(0).get()); + if (FuncName) + DeclareTargetFunctionsHost.insert(FuncName->getString().str()); + } +} + static void checkDeclInTargetContext(SourceLocation SL, SourceRange SR, Sema &SemaRef, Decl *D) { if (!D) Index: lib/Sema/SemaStmtAsm.cpp =================================================================== --- lib/Sema/SemaStmtAsm.cpp +++ lib/Sema/SemaStmtAsm.cpp @@ -209,7 +209,12 @@ // If we're compiling CUDA file and function attributes indicate that it's not // for this compilation side, skip all the checks. - if (!DeclAttrsMatchCUDAMode(getLangOpts(), getCurFunctionDecl())) { + // Likewise for OpenMP during device compilation: Host compilation passes a + // list of functions that are declare target and need to be checked. The other + // case is that we are in a target region. + auto *FD = getCurFunctionDecl(); + if (!DeclAttrsMatchCUDAMode(getLangOpts(), FD) || + skipChecksDuringOpenMPDeviceCompilation(FD)) { GCCAsmStmt *NS = new (Context) GCCAsmStmt( Context, AsmLoc, IsSimple, IsVolatile, NumOutputs, NumInputs, Names, Constraints, Exprs.data(), AsmString, NumClobbers, Clobbers, RParenLoc); Index: lib/Sema/SemaType.cpp =================================================================== --- lib/Sema/SemaType.cpp +++ lib/Sema/SemaType.cpp @@ -2196,8 +2196,7 @@ if (getLangOpts().CUDA) { // CUDA device code doesn't support VLAs. CUDADiagIfDeviceCode(Loc, diag::err_cuda_vla) << CurrentCUDATarget(); - } else if (!getLangOpts().OpenMP || - shouldDiagnoseTargetSupportFromOpenMP()) { + } else if (!skipChecksDuringOpenMPDeviceCompilation(getCurFunctionDecl())) { // Some targets don't support VLAs. Diag(Loc, diag::err_vla_unsupported); return QualType();