blob: 4d0d3137bc003990333beb3537b7e6777f2f5d4e [file] [log] [blame]
//===--- SemaOpenMP.cpp - Semantic Analysis for OpenMP constructs ---------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
/// \file
/// \brief This file implements semantic analysis for OpenMP directives and
/// clauses.
///
//===----------------------------------------------------------------------===//
#include "TreeTransform.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTMutationListener.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclOpenMP.h"
#include "clang/AST/StmtCXX.h"
#include "clang/AST/StmtOpenMP.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/Basic/OpenMPKinds.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/Initialization.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Scope.h"
#include "clang/Sema/ScopeInfo.h"
#include "clang/Sema/SemaInternal.h"
using namespace clang;
//===----------------------------------------------------------------------===//
// Stack of data-sharing attributes for variables
//===----------------------------------------------------------------------===//
namespace {
/// \brief Default data sharing attributes, which can be applied to directive.
enum DefaultDataSharingAttributes {
DSA_unspecified = 0, /// \brief Data sharing attribute not specified.
DSA_none = 1 << 0, /// \brief Default data sharing attribute 'none'.
DSA_shared = 1 << 1 /// \brief Default data sharing attribute 'shared'.
};
template <class T> struct MatchesAny {
explicit MatchesAny(ArrayRef<T> Arr) : Arr(std::move(Arr)) {}
bool operator()(T Kind) {
for (auto KindEl : Arr)
if (KindEl == Kind)
return true;
return false;
}
private:
ArrayRef<T> Arr;
};
struct MatchesAlways {
MatchesAlways() {}
template <class T> bool operator()(T) { return true; }
};
typedef MatchesAny<OpenMPClauseKind> MatchesAnyClause;
typedef MatchesAny<OpenMPDirectiveKind> MatchesAnyDirective;
/// \brief Stack for tracking declarations used in OpenMP directives and
/// clauses and their data-sharing attributes.
class DSAStackTy {
public:
struct DSAVarData {
OpenMPDirectiveKind DKind;
OpenMPClauseKind CKind;
DeclRefExpr *RefExpr;
SourceLocation ImplicitDSALoc;
DSAVarData()
: DKind(OMPD_unknown), CKind(OMPC_unknown), RefExpr(nullptr),
ImplicitDSALoc() {}
};
public:
struct MapInfo {
Expr *RefExpr;
};
private:
struct DSAInfo {
OpenMPClauseKind Attributes;
DeclRefExpr *RefExpr;
};
typedef llvm::SmallDenseMap<VarDecl *, DSAInfo, 64> DeclSAMapTy;
typedef llvm::SmallDenseMap<VarDecl *, DeclRefExpr *, 64> AlignedMapTy;
typedef llvm::DenseMap<VarDecl *, unsigned> LoopControlVariablesMapTy;
typedef llvm::SmallDenseMap<VarDecl *, MapInfo, 64> MappedDeclsTy;
typedef llvm::StringMap<std::pair<OMPCriticalDirective *, llvm::APSInt>>
CriticalsWithHintsTy;
struct SharingMapTy {
DeclSAMapTy SharingMap;
AlignedMapTy AlignedMap;
MappedDeclsTy MappedDecls;
LoopControlVariablesMapTy LCVMap;
DefaultDataSharingAttributes DefaultAttr;
SourceLocation DefaultAttrLoc;
OpenMPDirectiveKind Directive;
DeclarationNameInfo DirectiveName;
Scope *CurScope;
SourceLocation ConstructLoc;
/// \brief first argument (Expr *) contains optional argument of the
/// 'ordered' clause, the second one is true if the regions has 'ordered'
/// clause, false otherwise.
llvm::PointerIntPair<Expr *, 1, bool> OrderedRegion;
bool NowaitRegion;
bool CancelRegion;
unsigned AssociatedLoops;
SourceLocation InnerTeamsRegionLoc;
SharingMapTy(OpenMPDirectiveKind DKind, DeclarationNameInfo Name,
Scope *CurScope, SourceLocation Loc)
: SharingMap(), AlignedMap(), LCVMap(), DefaultAttr(DSA_unspecified),
Directive(DKind), DirectiveName(std::move(Name)), CurScope(CurScope),
ConstructLoc(Loc), OrderedRegion(), NowaitRegion(false),
CancelRegion(false), AssociatedLoops(1), InnerTeamsRegionLoc() {}
SharingMapTy()
: SharingMap(), AlignedMap(), LCVMap(), DefaultAttr(DSA_unspecified),
Directive(OMPD_unknown), DirectiveName(), CurScope(nullptr),
ConstructLoc(), OrderedRegion(), NowaitRegion(false),
CancelRegion(false), AssociatedLoops(1), InnerTeamsRegionLoc() {}
};
typedef SmallVector<SharingMapTy, 64> StackTy;
/// \brief Stack of used declaration and their data-sharing attributes.
StackTy Stack;
/// \brief true, if check for DSA must be from parent directive, false, if
/// from current directive.
OpenMPClauseKind ClauseKindMode;
Sema &SemaRef;
bool ForceCapturing;
CriticalsWithHintsTy Criticals;
typedef SmallVector<SharingMapTy, 8>::reverse_iterator reverse_iterator;
DSAVarData getDSA(StackTy::reverse_iterator Iter, VarDecl *D);
/// \brief Checks if the variable is a local for OpenMP region.
bool isOpenMPLocal(VarDecl *D, StackTy::reverse_iterator Iter);
public:
explicit DSAStackTy(Sema &S)
: Stack(1), ClauseKindMode(OMPC_unknown), SemaRef(S),
ForceCapturing(false) {}
bool isClauseParsingMode() const { return ClauseKindMode != OMPC_unknown; }
void setClauseParsingMode(OpenMPClauseKind K) { ClauseKindMode = K; }
bool isForceVarCapturing() const { return ForceCapturing; }
void setForceVarCapturing(bool V) { ForceCapturing = V; }
void push(OpenMPDirectiveKind DKind, const DeclarationNameInfo &DirName,
Scope *CurScope, SourceLocation Loc) {
Stack.push_back(SharingMapTy(DKind, DirName, CurScope, Loc));
Stack.back().DefaultAttrLoc = Loc;
}
void pop() {
assert(Stack.size() > 1 && "Data-sharing attributes stack is empty!");
Stack.pop_back();
}
void addCriticalWithHint(OMPCriticalDirective *D, llvm::APSInt Hint) {
Criticals[D->getDirectiveName().getAsString()] = std::make_pair(D, Hint);
}
const std::pair<OMPCriticalDirective *, llvm::APSInt>
getCriticalWithHint(const DeclarationNameInfo &Name) const {
auto I = Criticals.find(Name.getAsString());
if (I != Criticals.end())
return I->second;
return std::make_pair(nullptr, llvm::APSInt());
}
/// \brief If 'aligned' declaration for given variable \a D was not seen yet,
/// add it and return NULL; otherwise return previous occurrence's expression
/// for diagnostics.
DeclRefExpr *addUniqueAligned(VarDecl *D, DeclRefExpr *NewDE);
/// \brief Register specified variable as loop control variable.
void addLoopControlVariable(VarDecl *D);
/// \brief Check if the specified variable is a loop control variable for
/// current region.
/// \return The index of the loop control variable in the list of associated
/// for-loops (from outer to inner).
unsigned isLoopControlVariable(VarDecl *D);
/// \brief Check if the specified variable is a loop control variable for
/// parent region.
/// \return The index of the loop control variable in the list of associated
/// for-loops (from outer to inner).
unsigned isParentLoopControlVariable(VarDecl *D);
/// \brief Get the loop control variable for the I-th loop (or nullptr) in
/// parent directive.
VarDecl *getParentLoopControlVariable(unsigned I);
/// \brief Adds explicit data sharing attribute to the specified declaration.
void addDSA(VarDecl *D, DeclRefExpr *E, OpenMPClauseKind A);
/// \brief Returns data sharing attributes from top of the stack for the
/// specified declaration.
DSAVarData getTopDSA(VarDecl *D, bool FromParent);
/// \brief Returns data-sharing attributes for the specified declaration.
DSAVarData getImplicitDSA(VarDecl *D, bool FromParent);
/// \brief Checks if the specified variables has data-sharing attributes which
/// match specified \a CPred predicate in any directive which matches \a DPred
/// predicate.
template <class ClausesPredicate, class DirectivesPredicate>
DSAVarData hasDSA(VarDecl *D, ClausesPredicate CPred,
DirectivesPredicate DPred, bool FromParent);
/// \brief Checks if the specified variables has data-sharing attributes which
/// match specified \a CPred predicate in any innermost directive which
/// matches \a DPred predicate.
template <class ClausesPredicate, class DirectivesPredicate>
DSAVarData hasInnermostDSA(VarDecl *D, ClausesPredicate CPred,
DirectivesPredicate DPred,
bool FromParent);
/// \brief Checks if the specified variables has explicit data-sharing
/// attributes which match specified \a CPred predicate at the specified
/// OpenMP region.
bool hasExplicitDSA(VarDecl *D,
const llvm::function_ref<bool(OpenMPClauseKind)> &CPred,
unsigned Level);
/// \brief Returns true if the directive at level \Level matches in the
/// specified \a DPred predicate.
bool hasExplicitDirective(
const llvm::function_ref<bool(OpenMPDirectiveKind)> &DPred,
unsigned Level);
/// \brief Finds a directive which matches specified \a DPred predicate.
template <class NamedDirectivesPredicate>
bool hasDirective(NamedDirectivesPredicate DPred, bool FromParent);
/// \brief Returns currently analyzed directive.
OpenMPDirectiveKind getCurrentDirective() const {
return Stack.back().Directive;
}
/// \brief Returns parent directive.
OpenMPDirectiveKind getParentDirective() const {
if (Stack.size() > 2)
return Stack[Stack.size() - 2].Directive;
return OMPD_unknown;
}
/// \brief Return the directive associated with the provided scope.
OpenMPDirectiveKind getDirectiveForScope(const Scope *S) const;
/// \brief Set default data sharing attribute to none.
void setDefaultDSANone(SourceLocation Loc) {
Stack.back().DefaultAttr = DSA_none;
Stack.back().DefaultAttrLoc = Loc;
}
/// \brief Set default data sharing attribute to shared.
void setDefaultDSAShared(SourceLocation Loc) {
Stack.back().DefaultAttr = DSA_shared;
Stack.back().DefaultAttrLoc = Loc;
}
DefaultDataSharingAttributes getDefaultDSA() const {
return Stack.back().DefaultAttr;
}
SourceLocation getDefaultDSALocation() const {
return Stack.back().DefaultAttrLoc;
}
/// \brief Checks if the specified variable is a threadprivate.
bool isThreadPrivate(VarDecl *D) {
DSAVarData DVar = getTopDSA(D, false);
return isOpenMPThreadPrivate(DVar.CKind);
}
/// \brief Marks current region as ordered (it has an 'ordered' clause).
void setOrderedRegion(bool IsOrdered, Expr *Param) {
Stack.back().OrderedRegion.setInt(IsOrdered);
Stack.back().OrderedRegion.setPointer(Param);
}
/// \brief Returns true, if parent region is ordered (has associated
/// 'ordered' clause), false - otherwise.
bool isParentOrderedRegion() const {
if (Stack.size() > 2)
return Stack[Stack.size() - 2].OrderedRegion.getInt();
return false;
}
/// \brief Returns optional parameter for the ordered region.
Expr *getParentOrderedRegionParam() const {
if (Stack.size() > 2)
return Stack[Stack.size() - 2].OrderedRegion.getPointer();
return nullptr;
}
/// \brief Marks current region as nowait (it has a 'nowait' clause).
void setNowaitRegion(bool IsNowait = true) {
Stack.back().NowaitRegion = IsNowait;
}
/// \brief Returns true, if parent region is nowait (has associated
/// 'nowait' clause), false - otherwise.
bool isParentNowaitRegion() const {
if (Stack.size() > 2)
return Stack[Stack.size() - 2].NowaitRegion;
return false;
}
/// \brief Marks parent region as cancel region.
void setParentCancelRegion(bool Cancel = true) {
if (Stack.size() > 2)
Stack[Stack.size() - 2].CancelRegion =
Stack[Stack.size() - 2].CancelRegion || Cancel;
}
/// \brief Return true if current region has inner cancel construct.
bool isCancelRegion() const {
return Stack.back().CancelRegion;
}
/// \brief Set collapse value for the region.
void setAssociatedLoops(unsigned Val) { Stack.back().AssociatedLoops = Val; }
/// \brief Return collapse value for region.
unsigned getAssociatedLoops() const { return Stack.back().AssociatedLoops; }
/// \brief Marks current target region as one with closely nested teams
/// region.
void setParentTeamsRegionLoc(SourceLocation TeamsRegionLoc) {
if (Stack.size() > 2)
Stack[Stack.size() - 2].InnerTeamsRegionLoc = TeamsRegionLoc;
}
/// \brief Returns true, if current region has closely nested teams region.
bool hasInnerTeamsRegion() const {
return getInnerTeamsRegionLoc().isValid();
}
/// \brief Returns location of the nested teams region (if any).
SourceLocation getInnerTeamsRegionLoc() const {
if (Stack.size() > 1)
return Stack.back().InnerTeamsRegionLoc;
return SourceLocation();
}
Scope *getCurScope() const { return Stack.back().CurScope; }
Scope *getCurScope() { return Stack.back().CurScope; }
SourceLocation getConstructLoc() { return Stack.back().ConstructLoc; }
MapInfo getMapInfoForVar(VarDecl *VD) {
MapInfo VarMI = {0};
for (auto Cnt = Stack.size() - 1; Cnt > 0; --Cnt) {
if (Stack[Cnt].MappedDecls.count(VD)) {
VarMI = Stack[Cnt].MappedDecls[VD];
break;
}
}
return VarMI;
}
void addMapInfoForVar(VarDecl *VD, MapInfo MI) {
if (Stack.size() > 1) {
Stack.back().MappedDecls[VD] = MI;
}
}
MapInfo IsMappedInCurrentRegion(VarDecl *VD) {
assert(Stack.size() > 1 && "Target level is 0");
MapInfo VarMI = {0};
if (Stack.size() > 1 && Stack.back().MappedDecls.count(VD)) {
VarMI = Stack.back().MappedDecls[VD];
}
return VarMI;
}
};
bool isParallelOrTaskRegion(OpenMPDirectiveKind DKind) {
return isOpenMPParallelDirective(DKind) || DKind == OMPD_task ||
isOpenMPTeamsDirective(DKind) || DKind == OMPD_unknown ||
isOpenMPTaskLoopDirective(DKind);
}
} // namespace
DSAStackTy::DSAVarData DSAStackTy::getDSA(StackTy::reverse_iterator Iter,
VarDecl *D) {
D = D->getCanonicalDecl();
DSAVarData DVar;
if (Iter == std::prev(Stack.rend())) {
// OpenMP [2.9.1.1, Data-sharing Attribute Rules for Variables Referenced
// in a region but not in construct]
// File-scope or namespace-scope variables referenced in called routines
// in the region are shared unless they appear in a threadprivate
// directive.
if (!D->isFunctionOrMethodVarDecl() && !isa<ParmVarDecl>(D))
DVar.CKind = OMPC_shared;
// OpenMP [2.9.1.2, Data-sharing Attribute Rules for Variables Referenced
// in a region but not in construct]
// Variables with static storage duration that are declared in called
// routines in the region are shared.
if (D->hasGlobalStorage())
DVar.CKind = OMPC_shared;
return DVar;
}
DVar.DKind = Iter->Directive;
// OpenMP [2.9.1.1, Data-sharing Attribute Rules for Variables Referenced
// in a Construct, C/C++, predetermined, p.1]
// Variables with automatic storage duration that are declared in a scope
// inside the construct are private.
if (isOpenMPLocal(D, Iter) && D->isLocalVarDecl() &&
(D->getStorageClass() == SC_Auto || D->getStorageClass() == SC_None)) {
DVar.CKind = OMPC_private;
return DVar;
}
// Explicitly specified attributes and local variables with predetermined
// attributes.
if (Iter->SharingMap.count(D)) {
DVar.RefExpr = Iter->SharingMap[D].RefExpr;
DVar.CKind = Iter->SharingMap[D].Attributes;
DVar.ImplicitDSALoc = Iter->DefaultAttrLoc;
return DVar;
}
// OpenMP [2.9.1.1, Data-sharing Attribute Rules for Variables Referenced
// in a Construct, C/C++, implicitly determined, p.1]
// In a parallel or task construct, the data-sharing attributes of these
// variables are determined by the default clause, if present.
switch (Iter->DefaultAttr) {
case DSA_shared:
DVar.CKind = OMPC_shared;
DVar.ImplicitDSALoc = Iter->DefaultAttrLoc;
return DVar;
case DSA_none:
return DVar;
case DSA_unspecified:
// OpenMP [2.9.1.1, Data-sharing Attribute Rules for Variables Referenced
// in a Construct, implicitly determined, p.2]
// In a parallel construct, if no default clause is present, these
// variables are shared.
DVar.ImplicitDSALoc = Iter->DefaultAttrLoc;
if (isOpenMPParallelDirective(DVar.DKind) ||
isOpenMPTeamsDirective(DVar.DKind)) {
DVar.CKind = OMPC_shared;
return DVar;
}
// OpenMP [2.9.1.1, Data-sharing Attribute Rules for Variables Referenced
// in a Construct, implicitly determined, p.4]
// In a task construct, if no default clause is present, a variable that in
// the enclosing context is determined to be shared by all implicit tasks
// bound to the current team is shared.
if (DVar.DKind == OMPD_task) {
DSAVarData DVarTemp;
for (StackTy::reverse_iterator I = std::next(Iter), EE = Stack.rend();
I != EE; ++I) {
// OpenMP [2.9.1.1, Data-sharing Attribute Rules for Variables
// Referenced
// in a Construct, implicitly determined, p.6]
// In a task construct, if no default clause is present, a variable
// whose data-sharing attribute is not determined by the rules above is
// firstprivate.
DVarTemp = getDSA(I, D);
if (DVarTemp.CKind != OMPC_shared) {
DVar.RefExpr = nullptr;
DVar.DKind = OMPD_task;
DVar.CKind = OMPC_firstprivate;
return DVar;
}
if (isParallelOrTaskRegion(I->Directive))
break;
}
DVar.DKind = OMPD_task;
DVar.CKind =
(DVarTemp.CKind == OMPC_unknown) ? OMPC_firstprivate : OMPC_shared;
return DVar;
}
}
// OpenMP [2.9.1.1, Data-sharing Attribute Rules for Variables Referenced
// in a Construct, implicitly determined, p.3]
// For constructs other than task, if no default clause is present, these
// variables inherit their data-sharing attributes from the enclosing
// context.
return getDSA(std::next(Iter), D);
}
DeclRefExpr *DSAStackTy::addUniqueAligned(VarDecl *D, DeclRefExpr *NewDE) {
assert(Stack.size() > 1 && "Data sharing attributes stack is empty");
D = D->getCanonicalDecl();
auto It = Stack.back().AlignedMap.find(D);
if (It == Stack.back().AlignedMap.end()) {
assert(NewDE && "Unexpected nullptr expr to be added into aligned map");
Stack.back().AlignedMap[D] = NewDE;
return nullptr;
} else {
assert(It->second && "Unexpected nullptr expr in the aligned map");
return It->second;
}
return nullptr;
}
void DSAStackTy::addLoopControlVariable(VarDecl *D) {
assert(Stack.size() > 1 && "Data-sharing attributes stack is empty");
D = D->getCanonicalDecl();
Stack.back().LCVMap.insert(std::make_pair(D, Stack.back().LCVMap.size() + 1));
}
unsigned DSAStackTy::isLoopControlVariable(VarDecl *D) {
assert(Stack.size() > 1 && "Data-sharing attributes stack is empty");
D = D->getCanonicalDecl();
return Stack.back().LCVMap.count(D) > 0 ? Stack.back().LCVMap[D] : 0;
}
unsigned DSAStackTy::isParentLoopControlVariable(VarDecl *D) {
assert(Stack.size() > 2 && "Data-sharing attributes stack is empty");
D = D->getCanonicalDecl();
return Stack[Stack.size() - 2].LCVMap.count(D) > 0
? Stack[Stack.size() - 2].LCVMap[D]
: 0;
}
VarDecl *DSAStackTy::getParentLoopControlVariable(unsigned I) {
assert(Stack.size() > 2 && "Data-sharing attributes stack is empty");
if (Stack[Stack.size() - 2].LCVMap.size() < I)
return nullptr;
for (auto &Pair : Stack[Stack.size() - 2].LCVMap) {
if (Pair.second == I)
return Pair.first;
}
return nullptr;
}
void DSAStackTy::addDSA(VarDecl *D, DeclRefExpr *E, OpenMPClauseKind A) {
D = D->getCanonicalDecl();
if (A == OMPC_threadprivate) {
Stack[0].SharingMap[D].Attributes = A;
Stack[0].SharingMap[D].RefExpr = E;
} else {
assert(Stack.size() > 1 && "Data-sharing attributes stack is empty");
Stack.back().SharingMap[D].Attributes = A;
Stack.back().SharingMap[D].RefExpr = E;
}
}
bool DSAStackTy::isOpenMPLocal(VarDecl *D, StackTy::reverse_iterator Iter) {
D = D->getCanonicalDecl();
if (Stack.size() > 2) {
reverse_iterator I = Iter, E = std::prev(Stack.rend());
Scope *TopScope = nullptr;
while (I != E && !isParallelOrTaskRegion(I->Directive)) {
++I;
}
if (I == E)
return false;
TopScope = I->CurScope ? I->CurScope->getParent() : nullptr;
Scope *CurScope = getCurScope();
while (CurScope != TopScope && !CurScope->isDeclScope(D)) {
CurScope = CurScope->getParent();
}
return CurScope != TopScope;
}
return false;
}
/// \brief Build a variable declaration for OpenMP loop iteration variable.
static VarDecl *buildVarDecl(Sema &SemaRef, SourceLocation Loc, QualType Type,
StringRef Name, const AttrVec *Attrs = nullptr) {
DeclContext *DC = SemaRef.CurContext;
IdentifierInfo *II = &SemaRef.PP.getIdentifierTable().get(Name);
TypeSourceInfo *TInfo = SemaRef.Context.getTrivialTypeSourceInfo(Type, Loc);
VarDecl *Decl =
VarDecl::Create(SemaRef.Context, DC, Loc, Loc, II, Type, TInfo, SC_None);
if (Attrs) {
for (specific_attr_iterator<AlignedAttr> I(Attrs->begin()), E(Attrs->end());
I != E; ++I)
Decl->addAttr(*I);
}
Decl->setImplicit();
return Decl;
}
static DeclRefExpr *buildDeclRefExpr(Sema &S, VarDecl *D, QualType Ty,
SourceLocation Loc,
bool RefersToCapture = false) {
D->setReferenced();
D->markUsed(S.Context);
return DeclRefExpr::Create(S.getASTContext(), NestedNameSpecifierLoc(),
SourceLocation(), D, RefersToCapture, Loc, Ty,
VK_LValue);
}
DSAStackTy::DSAVarData DSAStackTy::getTopDSA(VarDecl *D, bool FromParent) {
D = D->getCanonicalDecl();
DSAVarData DVar;
// OpenMP [2.9.1.1, Data-sharing Attribute Rules for Variables Referenced
// in a Construct, C/C++, predetermined, p.1]
// Variables appearing in threadprivate directives are threadprivate.
if ((D->getTLSKind() != VarDecl::TLS_None &&
!(D->hasAttr<OMPThreadPrivateDeclAttr>() &&
SemaRef.getLangOpts().OpenMPUseTLS &&
SemaRef.getASTContext().getTargetInfo().isTLSSupported())) ||
(D->getStorageClass() == SC_Register && D->hasAttr<AsmLabelAttr>() &&
!D->isLocalVarDecl())) {
addDSA(D, buildDeclRefExpr(SemaRef, D, D->getType().getNonReferenceType(),
D->getLocation()),
OMPC_threadprivate);
}
if (Stack[0].SharingMap.count(D)) {
DVar.RefExpr = Stack[0].SharingMap[D].RefExpr;
DVar.CKind = OMPC_threadprivate;
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.
// OpenMP [2.9.1.1, Data-sharing Attribute Rules for Variables Referenced
// in a Construct, C/C++, predetermined, p.7]
// Variables with static storage duration that are declared in a scope
// inside the construct are shared.
if (D->isStaticDataMember()) {
DSAVarData DVarTemp =
hasDSA(D, isOpenMPPrivate, MatchesAlways(), FromParent);
if (DVarTemp.CKind != OMPC_unknown && DVarTemp.RefExpr)
return DVar;
DVar.CKind = OMPC_shared;
return DVar;
}
QualType Type = D->getType().getNonReferenceType().getCanonicalType();
bool IsConstant = Type.isConstant(SemaRef.getASTContext());
Type = SemaRef.getASTContext().getBaseElementType(Type);
// OpenMP [2.9.1.1, Data-sharing Attribute Rules for Variables Referenced
// in a Construct, C/C++, predetermined, p.6]
// Variables with const qualified type having no mutable member are
// shared.
CXXRecordDecl *RD =
SemaRef.getLangOpts().CPlusPlus ? Type->getAsCXXRecordDecl() : nullptr;
if (auto *CTSD = dyn_cast_or_null<ClassTemplateSpecializationDecl>(RD))
if (auto *CTD = CTSD->getSpecializedTemplate())
RD = CTD->getTemplatedDecl();
if (IsConstant &&
!(SemaRef.getLangOpts().CPlusPlus && RD && RD->hasMutableFields())) {
// Variables with const-qualified type having no mutable member may be
// listed in a firstprivate clause, even if they are static data members.
DSAVarData DVarTemp = hasDSA(D, MatchesAnyClause(OMPC_firstprivate),
MatchesAlways(), FromParent);
if (DVarTemp.CKind == OMPC_firstprivate && DVarTemp.RefExpr)
return DVar;
DVar.CKind = OMPC_shared;
return DVar;
}
// Explicitly specified attributes and local variables with predetermined
// attributes.
auto StartI = std::next(Stack.rbegin());
auto EndI = std::prev(Stack.rend());
if (FromParent && StartI != EndI) {
StartI = std::next(StartI);
}
auto I = std::prev(StartI);
if (I->SharingMap.count(D)) {
DVar.RefExpr = I->SharingMap[D].RefExpr;
DVar.CKind = I->SharingMap[D].Attributes;
DVar.ImplicitDSALoc = I->DefaultAttrLoc;
}
return DVar;
}
DSAStackTy::DSAVarData DSAStackTy::getImplicitDSA(VarDecl *D, bool FromParent) {
D = D->getCanonicalDecl();
auto StartI = Stack.rbegin();
auto EndI = std::prev(Stack.rend());
if (FromParent && StartI != EndI) {
StartI = std::next(StartI);
}
return getDSA(StartI, D);
}
template <class ClausesPredicate, class DirectivesPredicate>
DSAStackTy::DSAVarData DSAStackTy::hasDSA(VarDecl *D, ClausesPredicate CPred,
DirectivesPredicate DPred,
bool FromParent) {
D = D->getCanonicalDecl();
auto StartI = std::next(Stack.rbegin());
auto EndI = std::prev(Stack.rend());
if (FromParent && StartI != EndI) {
StartI = std::next(StartI);
}
for (auto I = StartI, EE = EndI; I != EE; ++I) {
if (!DPred(I->Directive) && !isParallelOrTaskRegion(I->Directive))
continue;
DSAVarData DVar = getDSA(I, D);
if (CPred(DVar.CKind))
return DVar;
}
return DSAVarData();
}
template <class ClausesPredicate, class DirectivesPredicate>
DSAStackTy::DSAVarData
DSAStackTy::hasInnermostDSA(VarDecl *D, ClausesPredicate CPred,
DirectivesPredicate DPred, bool FromParent) {
D = D->getCanonicalDecl();
auto StartI = std::next(Stack.rbegin());
auto EndI = std::prev(Stack.rend());
if (FromParent && StartI != EndI) {
StartI = std::next(StartI);
}
for (auto I = StartI, EE = EndI; I != EE; ++I) {
if (!DPred(I->Directive))
break;
DSAVarData DVar = getDSA(I, D);
if (CPred(DVar.CKind))
return DVar;
return DSAVarData();
}
return DSAVarData();
}
bool DSAStackTy::hasExplicitDSA(
VarDecl *D, const llvm::function_ref<bool(OpenMPClauseKind)> &CPred,
unsigned Level) {
if (CPred(ClauseKindMode))
return true;
if (isClauseParsingMode())
++Level;
D = D->getCanonicalDecl();
auto StartI = Stack.rbegin();
auto EndI = std::prev(Stack.rend());
if (std::distance(StartI, EndI) <= (int)Level)
return false;
std::advance(StartI, Level);
return (StartI->SharingMap.count(D) > 0) && StartI->SharingMap[D].RefExpr &&
CPred(StartI->SharingMap[D].Attributes);
}
bool DSAStackTy::hasExplicitDirective(
const llvm::function_ref<bool(OpenMPDirectiveKind)> &DPred,
unsigned Level) {
if (isClauseParsingMode())
++Level;
auto StartI = Stack.rbegin();
auto EndI = std::prev(Stack.rend());
if (std::distance(StartI, EndI) <= (int)Level)
return false;
std::advance(StartI, Level);
return DPred(StartI->Directive);
}
template <class NamedDirectivesPredicate>
bool DSAStackTy::hasDirective(NamedDirectivesPredicate DPred, bool FromParent) {
auto StartI = std::next(Stack.rbegin());
auto EndI = std::prev(Stack.rend());
if (FromParent && StartI != EndI) {
StartI = std::next(StartI);
}
for (auto I = StartI, EE = EndI; I != EE; ++I) {
if (DPred(I->Directive, I->DirectiveName, I->ConstructLoc))
return true;
}
return false;
}
OpenMPDirectiveKind DSAStackTy::getDirectiveForScope(const Scope *S) const {
for (auto I = Stack.rbegin(), EE = Stack.rend(); I != EE; ++I)
if (I->CurScope == S)
return I->Directive;
return OMPD_unknown;
}
void Sema::InitDataSharingAttributesStack() {
VarDataSharingAttributesStack = new DSAStackTy(*this);
}
#define DSAStack static_cast<DSAStackTy *>(VarDataSharingAttributesStack)
bool Sema::IsOpenMPCapturedByRef(VarDecl *VD,
const CapturedRegionScopeInfo *RSI) {
assert(LangOpts.OpenMP && "OpenMP is not allowed");
auto &Ctx = getASTContext();
bool IsByRef = true;
// Find the directive that is associated with the provided scope.
auto DKind = DSAStack->getDirectiveForScope(RSI->TheScope);
auto Ty = VD->getType();
if (isOpenMPTargetDirective(DKind)) {
// This table summarizes how a given variable should be passed to the device
// given its type and the clauses where it appears. This table is based on
// the description in OpenMP 4.5 [2.10.4, target Construct] and
// OpenMP 4.5 [2.15.5, Data-mapping Attribute Rules and Clauses].
//
// =========================================================================
// | type | defaultmap | pvt | first | is_device_ptr | map | res. |
// | |(tofrom:scalar)| | pvt | | | |
// =========================================================================
// | scl | | | | - | | bycopy|
// | scl | | - | x | - | - | bycopy|
// | scl | | x | - | - | - | null |
// | scl | x | | | - | | byref |
// | scl | x | - | x | - | - | bycopy|
// | scl | x | x | - | - | - | null |
// | scl | | - | - | - | x | byref |
// | scl | x | - | - | - | x | byref |
//
// | agg | n.a. | | | - | | byref |
// | agg | n.a. | - | x | - | - | byref |
// | agg | n.a. | x | - | - | - | null |
// | agg | n.a. | - | - | - | x | byref |
// | agg | n.a. | - | - | - | x[] | byref |
//
// | ptr | n.a. | | | - | | bycopy|
// | ptr | n.a. | - | x | - | - | bycopy|
// | ptr | n.a. | x | - | - | - | null |
// | ptr | n.a. | - | - | - | x | byref |
// | ptr | n.a. | - | - | - | x[] | bycopy|
// | ptr | n.a. | - | - | x | | bycopy|
// | ptr | n.a. | - | - | x | x | bycopy|
// | ptr | n.a. | - | - | x | x[] | bycopy|
// =========================================================================
// Legend:
// scl - scalar
// ptr - pointer
// agg - aggregate
// x - applies
// - - invalid in this combination
// [] - mapped with an array section
// byref - should be mapped by reference
// byval - should be mapped by value
// null - initialize a local variable to null on the device
//
// Observations:
// - All scalar declarations that show up in a map clause have to be passed
// by reference, because they may have been mapped in the enclosing data
// environment.
// - If the scalar value does not fit the size of uintptr, it has to be
// passed by reference, regardless the result in the table above.
// - For pointers mapped by value that have either an implicit map or an
// array section, the runtime library may pass the NULL value to the
// device instead of the value passed to it by the compiler.
// FIXME: Right now, only implicit maps are implemented. Properly mapping
// values requires having the map, private, and firstprivate clauses SEMA
// and parsing in place, which we don't yet.
if (Ty->isReferenceType())
Ty = Ty->castAs<ReferenceType>()->getPointeeType();
IsByRef = !Ty->isScalarType();
}
// When passing data by value, we need to make sure it fits the uintptr size
// and alignment, because the runtime library only deals with uintptr types.
// If it does not fit the uintptr size, we need to pass the data by reference
// instead.
if (!IsByRef &&
(Ctx.getTypeSizeInChars(Ty) >
Ctx.getTypeSizeInChars(Ctx.getUIntPtrType()) ||
Ctx.getDeclAlign(VD) > Ctx.getTypeAlignInChars(Ctx.getUIntPtrType())))
IsByRef = true;
return IsByRef;
}
bool Sema::IsOpenMPCapturedVar(VarDecl *VD) {
assert(LangOpts.OpenMP && "OpenMP is not allowed");
VD = VD->getCanonicalDecl();
// If we are attempting to capture a global variable in a directive with
// 'target' we return true so that this global is also mapped to the device.
//
// FIXME: If the declaration is enclosed in a 'declare target' directive,
// then it should not be captured. Therefore, an extra check has to be
// inserted here once support for 'declare target' is added.
//
if (!VD->hasLocalStorage()) {
if (DSAStack->getCurrentDirective() == OMPD_target &&
!DSAStack->isClauseParsingMode()) {
return true;
}
if (DSAStack->getCurScope() &&
DSAStack->hasDirective(
[](OpenMPDirectiveKind K, const DeclarationNameInfo &DNI,
SourceLocation Loc) -> bool {
return isOpenMPTargetDirective(K);
},
false)) {
return true;
}
}
if (DSAStack->getCurrentDirective() != OMPD_unknown &&
(!DSAStack->isClauseParsingMode() ||
DSAStack->getParentDirective() != OMPD_unknown)) {
if (DSAStack->isLoopControlVariable(VD) ||
(VD->hasLocalStorage() &&
isParallelOrTaskRegion(DSAStack->getCurrentDirective())) ||
DSAStack->isForceVarCapturing())
return true;
auto DVarPrivate = DSAStack->getTopDSA(VD, DSAStack->isClauseParsingMode());
if (DVarPrivate.CKind != OMPC_unknown && isOpenMPPrivate(DVarPrivate.CKind))
return true;
DVarPrivate = DSAStack->hasDSA(VD, isOpenMPPrivate, MatchesAlways(),
DSAStack->isClauseParsingMode());
return DVarPrivate.CKind != OMPC_unknown;
}
return false;
}
bool Sema::isOpenMPPrivateVar(VarDecl *VD, unsigned Level) {
assert(LangOpts.OpenMP && "OpenMP is not allowed");
return DSAStack->hasExplicitDSA(
VD, [](OpenMPClauseKind K) -> bool { return K == OMPC_private; }, Level);
}
bool Sema::isOpenMPTargetCapturedVar(VarDecl *VD, unsigned Level) {
assert(LangOpts.OpenMP && "OpenMP is not allowed");
// Return true if the current level is no longer enclosed in a target region.
return !VD->hasLocalStorage() &&
DSAStack->hasExplicitDirective(isOpenMPTargetDirective, Level);
}
void Sema::DestroyDataSharingAttributesStack() { delete DSAStack; }
void Sema::StartOpenMPDSABlock(OpenMPDirectiveKind DKind,
const DeclarationNameInfo &DirName,
Scope *CurScope, SourceLocation Loc) {
DSAStack->push(DKind, DirName, CurScope, Loc);
PushExpressionEvaluationContext(PotentiallyEvaluated);
}
void Sema::StartOpenMPClause(OpenMPClauseKind K) {
DSAStack->setClauseParsingMode(K);
}
void Sema::EndOpenMPClause() {
DSAStack->setClauseParsingMode(/*K=*/OMPC_unknown);
}
void Sema::EndOpenMPDSABlock(Stmt *CurDirective) {
// OpenMP [2.14.3.5, Restrictions, C/C++, p.1]
// A variable of class type (or array thereof) that appears in a lastprivate
// clause requires an accessible, unambiguous default constructor for the
// class type, unless the list item is also specified in a firstprivate
// clause.
if (auto D = dyn_cast_or_null<OMPExecutableDirective>(CurDirective)) {
for (auto *C : D->clauses()) {
if (auto *Clause = dyn_cast<OMPLastprivateClause>(C)) {
SmallVector<Expr *, 8> PrivateCopies;
for (auto *DE : Clause->varlists()) {
if (DE->isValueDependent() || DE->isTypeDependent()) {
PrivateCopies.push_back(nullptr);
continue;
}
auto *VD = cast<VarDecl>(cast<DeclRefExpr>(DE)->getDecl());
QualType Type = VD->getType().getNonReferenceType();
auto DVar = DSAStack->getTopDSA(VD, false);
if (DVar.CKind == OMPC_lastprivate) {
// Generate helper private variable and initialize it with the
// default value. The address of the original variable is replaced
// by the address of the new private variable in CodeGen. This new
// variable is not added to IdResolver, so the code in the OpenMP
// region uses original variable for proper diagnostics.
auto *VDPrivate = buildVarDecl(
*this, DE->getExprLoc(), Type.getUnqualifiedType(),
VD->getName(), VD->hasAttrs() ? &VD->getAttrs() : nullptr);
ActOnUninitializedDecl(VDPrivate, /*TypeMayContainAuto=*/false);
if (VDPrivate->isInvalidDecl())
continue;
PrivateCopies.push_back(buildDeclRefExpr(
*this, VDPrivate, DE->getType(), DE->getExprLoc()));
} else {
// The variable is also a firstprivate, so initialization sequence
// for private copy is generated already.
PrivateCopies.push_back(nullptr);
}
}
// Set initializers to private copies if no errors were found.
if (PrivateCopies.size() == Clause->varlist_size()) {
Clause->setPrivateCopies(PrivateCopies);
}
}
}
}
DSAStack->pop();
DiscardCleanupsInEvaluationContext();
PopExpressionEvaluationContext();
}
static bool FinishOpenMPLinearClause(OMPLinearClause &Clause, DeclRefExpr *IV,
Expr *NumIterations, Sema &SemaRef,
Scope *S);
namespace {
class VarDeclFilterCCC : public CorrectionCandidateCallback {
private:
Sema &SemaRef;
public:
explicit VarDeclFilterCCC(Sema &S) : SemaRef(S) {}
bool ValidateCandidate(const TypoCorrection &Candidate) override {
NamedDecl *ND = Candidate.getCorrectionDecl();
if (VarDecl *VD = dyn_cast_or_null<VarDecl>(ND)) {
return VD->hasGlobalStorage() &&
SemaRef.isDeclInScope(ND, SemaRef.getCurLexicalContext(),
SemaRef.getCurScope());
}
return false;
}
};
} // namespace
ExprResult Sema::ActOnOpenMPIdExpression(Scope *CurScope,
CXXScopeSpec &ScopeSpec,
const DeclarationNameInfo &Id) {
LookupResult Lookup(*this, Id, LookupOrdinaryName);
LookupParsedName(Lookup, CurScope, &ScopeSpec, true);
if (Lookup.isAmbiguous())
return ExprError();
VarDecl *VD;
if (!Lookup.isSingleResult()) {
if (TypoCorrection Corrected = CorrectTypo(
Id, LookupOrdinaryName, CurScope, nullptr,
llvm::make_unique<VarDeclFilterCCC>(*this), CTK_ErrorRecovery)) {
diagnoseTypo(Corrected,
PDiag(Lookup.empty()
? diag::err_undeclared_var_use_suggest
: diag::err_omp_expected_var_arg_suggest)
<< Id.getName());
VD = Corrected.getCorrectionDeclAs<VarDecl>();
} else {
Diag(Id.getLoc(), Lookup.empty() ? diag::err_undeclared_var_use
: diag::err_omp_expected_var_arg)
<< Id.getName();
return ExprError();
}
} else {
if (!(VD = Lookup.getAsSingle<VarDecl>())) {
Diag(Id.getLoc(), diag::err_omp_expected_var_arg) << Id.getName();
Diag(Lookup.getFoundDecl()->getLocation(), diag::note_declared_at);
return ExprError();
}
}
Lookup.suppressDiagnostics();
// OpenMP [2.9.2, Syntax, C/C++]
// Variables must be file-scope, namespace-scope, or static block-scope.
if (!VD->hasGlobalStorage()) {
Diag(Id.getLoc(), diag::err_omp_global_var_arg)
<< getOpenMPDirectiveName(OMPD_threadprivate) << !VD->isStaticLocal();
bool IsDecl =
VD->isThisDeclarationADefinition(Context) == VarDecl::DeclarationOnly;
Diag(VD->getLocation(),
IsDecl ? diag::note_previous_decl : diag::note_defined_here)
<< VD;
return ExprError();
}
VarDecl *CanonicalVD = VD->getCanonicalDecl();
NamedDecl *ND = cast<NamedDecl>(CanonicalVD);
// OpenMP [2.9.2, Restrictions, C/C++, p.2]
// A threadprivate directive for file-scope variables must appear outside
// any definition or declaration.
if (CanonicalVD->getDeclContext()->isTranslationUnit() &&
!getCurLexicalContext()->isTranslationUnit()) {
Diag(Id.getLoc(), diag::err_omp_var_scope)
<< getOpenMPDirectiveName(OMPD_threadprivate) << VD;
bool IsDecl =
VD->isThisDeclarationADefinition(Context) == VarDecl::DeclarationOnly;
Diag(VD->getLocation(),
IsDecl ? diag::note_previous_decl : diag::note_defined_here)
<< VD;
return ExprError();
}
// OpenMP [2.9.2, Restrictions, C/C++, p.3]
// A threadprivate directive for static class member variables must appear
// in the class definition, in the same scope in which the member
// variables are declared.
if (CanonicalVD->isStaticDataMember() &&
!CanonicalVD->getDeclContext()->Equals(getCurLexicalContext())) {
Diag(Id.getLoc(), diag::err_omp_var_scope)
<< getOpenMPDirectiveName(OMPD_threadprivate) << VD;
bool IsDecl =
VD->isThisDeclarationADefinition(Context) == VarDecl::DeclarationOnly;
Diag(VD->getLocation(),
IsDecl ? diag::note_previous_decl : diag::note_defined_here)
<< VD;
return ExprError();
}
// OpenMP [2.9.2, Restrictions, C/C++, p.4]
// A threadprivate directive for namespace-scope variables must appear
// outside any definition or declaration other than the namespace
// definition itself.
if (CanonicalVD->getDeclContext()->isNamespace() &&
(!getCurLexicalContext()->isFileContext() ||
!getCurLexicalContext()->Encloses(CanonicalVD->getDeclContext()))) {
Diag(Id.getLoc(), diag::err_omp_var_scope)
<< getOpenMPDirectiveName(OMPD_threadprivate) << VD;
bool IsDecl =
VD->isThisDeclarationADefinition(Context) == VarDecl::DeclarationOnly;
Diag(VD->getLocation(),
IsDecl ? diag::note_previous_decl : diag::note_defined_here)
<< VD;
return ExprError();
}
// OpenMP [2.9.2, Restrictions, C/C++, p.6]
// A threadprivate directive for static block-scope variables must appear
// in the scope of the variable and not in a nested scope.
if (CanonicalVD->isStaticLocal() && CurScope &&
!isDeclInScope(ND, getCurLexicalContext(), CurScope)) {
Diag(Id.getLoc(), diag::err_omp_var_scope)
<< getOpenMPDirectiveName(OMPD_threadprivate) << VD;
bool IsDecl =
VD->isThisDeclarationADefinition(Context) == VarDecl::DeclarationOnly;
Diag(VD->getLocation(),
IsDecl ? diag::note_previous_decl : diag::note_defined_here)
<< VD;
return ExprError();
}
// OpenMP [2.9.2, Restrictions, C/C++, p.2-6]
// A threadprivate directive must lexically precede all references to any
// of the variables in its list.
if (VD->isUsed() && !DSAStack->isThreadPrivate(VD)) {
Diag(Id.getLoc(), diag::err_omp_var_used)
<< getOpenMPDirectiveName(OMPD_threadprivate) << VD;
return ExprError();
}
QualType ExprType = VD->getType().getNonReferenceType();
ExprResult DE = buildDeclRefExpr(*this, VD, ExprType, Id.getLoc());
return DE;
}
Sema::DeclGroupPtrTy
Sema::ActOnOpenMPThreadprivateDirective(SourceLocation Loc,
ArrayRef<Expr *> VarList) {
if (OMPThreadPrivateDecl *D = CheckOMPThreadPrivateDecl(Loc, VarList)) {
CurContext->addDecl(D);
return DeclGroupPtrTy::make(DeclGroupRef(D));
}
return nullptr;
}
namespace {
class LocalVarRefChecker : public ConstStmtVisitor<LocalVarRefChecker, bool> {
Sema &SemaRef;
public:
bool VisitDeclRefExpr(const DeclRefExpr *E) {
if (auto VD = dyn_cast<VarDecl>(E->getDecl())) {
if (VD->hasLocalStorage()) {
SemaRef.Diag(E->getLocStart(),
diag::err_omp_local_var_in_threadprivate_init)
<< E->getSourceRange();
SemaRef.Diag(VD->getLocation(), diag::note_defined_here)
<< VD << VD->getSourceRange();
return true;
}
}
return false;
}
bool VisitStmt(const Stmt *S) {
for (auto Child : S->children()) {
if (Child && Visit(Child))
return true;
}
return false;
}
explicit LocalVarRefChecker(Sema &SemaRef) : SemaRef(SemaRef) {}
};
} // namespace
OMPThreadPrivateDecl *
Sema::CheckOMPThreadPrivateDecl(SourceLocation Loc, ArrayRef<Expr *> VarList) {
SmallVector<Expr *, 8> Vars;
for (auto &RefExpr : VarList) {
DeclRefExpr *DE = cast<DeclRefExpr>(RefExpr);
VarDecl *VD = cast<VarDecl>(DE->getDecl());
SourceLocation ILoc = DE->getExprLoc();
QualType QType = VD->getType();
if (QType->isDependentType() || QType->isInstantiationDependentType()) {
// It will be analyzed later.
Vars.push_back(DE);
continue;
}
// OpenMP [2.9.2, Restrictions, C/C++, p.10]
// A threadprivate variable must not have an incomplete type.
if (RequireCompleteType(ILoc, VD->getType(),
diag::err_omp_threadprivate_incomplete_type)) {
continue;
}
// OpenMP [2.9.2, Restrictions, C/C++, p.10]
// A threadprivate variable must not have a reference type.
if (VD->getType()->isReferenceType()) {
Diag(ILoc, diag::err_omp_ref_type_arg)
<< getOpenMPDirectiveName(OMPD_threadprivate) << VD->getType();
bool IsDecl =
VD->isThisDeclarationADefinition(Context) == VarDecl::DeclarationOnly;
Diag(VD->getLocation(),
IsDecl ? diag::note_previous_decl : diag::note_defined_here)
<< VD;
continue;
}
// Check if this is a TLS variable. If TLS is not being supported, produce
// the corresponding diagnostic.
if ((VD->getTLSKind() != VarDecl::TLS_None &&
!(VD->hasAttr<OMPThreadPrivateDeclAttr>() &&
getLangOpts().OpenMPUseTLS &&
getASTContext().getTargetInfo().isTLSSupported())) ||
(VD->getStorageClass() == SC_Register && VD->hasAttr<AsmLabelAttr>() &&
!VD->isLocalVarDecl())) {
Diag(ILoc, diag::err_omp_var_thread_local)
<< VD << ((VD->getTLSKind() != VarDecl::TLS_None) ? 0 : 1);
bool IsDecl =
VD->isThisDeclarationADefinition(Context) == VarDecl::DeclarationOnly;
Diag(VD->getLocation(),
IsDecl ? diag::note_previous_decl : diag::note_defined_here)
<< VD;
continue;
}
// Check if initial value of threadprivate variable reference variable with
// local storage (it is not supported by runtime).
if (auto Init = VD->getAnyInitializer()) {
LocalVarRefChecker Checker(*this);
if (Checker.Visit(Init))
continue;
}
Vars.push_back(RefExpr);
DSAStack->addDSA(VD, DE, OMPC_threadprivate);
VD->addAttr(OMPThreadPrivateDeclAttr::CreateImplicit(
Context, SourceRange(Loc, Loc)));
if (auto *ML = Context.getASTMutationListener())
ML->DeclarationMarkedOpenMPThreadPrivate(VD);
}
OMPThreadPrivateDecl *D = nullptr;
if (!Vars.empty()) {
D = OMPThreadPrivateDecl::Create(Context, getCurLexicalContext(), Loc,
Vars);
D->setAccess(AS_public);
}
return D;
}
static void ReportOriginalDSA(Sema &SemaRef, DSAStackTy *Stack,
const VarDecl *VD, DSAStackTy::DSAVarData DVar,
bool IsLoopIterVar = false) {
if (DVar.RefExpr) {
SemaRef.Diag(DVar.RefExpr->getExprLoc(), diag::note_omp_explicit_dsa)
<< getOpenMPClauseName(DVar.CKind);
return;
}
enum {
PDSA_StaticMemberShared,
PDSA_StaticLocalVarShared,
PDSA_LoopIterVarPrivate,
PDSA_LoopIterVarLinear,
PDSA_LoopIterVarLastprivate,
PDSA_ConstVarShared,
PDSA_GlobalVarShared,
PDSA_TaskVarFirstprivate,
PDSA_LocalVarPrivate,
PDSA_Implicit
} Reason = PDSA_Implicit;
bool ReportHint = false;
auto ReportLoc = VD->getLocation();
if (IsLoopIterVar) {
if (DVar.CKind == OMPC_private)
Reason = PDSA_LoopIterVarPrivate;
else if (DVar.CKind == OMPC_lastprivate)
Reason = PDSA_LoopIterVarLastprivate;
else
Reason = PDSA_LoopIterVarLinear;
} else if (DVar.DKind == OMPD_task && DVar.CKind == OMPC_firstprivate) {
Reason = PDSA_TaskVarFirstprivate;
ReportLoc = DVar.ImplicitDSALoc;
} else if (VD->isStaticLocal())
Reason = PDSA_StaticLocalVarShared;
else if (VD->isStaticDataMember())
Reason = PDSA_StaticMemberShared;
else if (VD->isFileVarDecl())
Reason = PDSA_GlobalVarShared;
else if (VD->getType().isConstant(SemaRef.getASTContext()))
Reason = PDSA_ConstVarShared;
else if (VD->isLocalVarDecl() && DVar.CKind == OMPC_private) {
ReportHint = true;
Reason = PDSA_LocalVarPrivate;
}
if (Reason != PDSA_Implicit) {
SemaRef.Diag(ReportLoc, diag::note_omp_predetermined_dsa)
<< Reason << ReportHint
<< getOpenMPDirectiveName(Stack->getCurrentDirective());
} else if (DVar.ImplicitDSALoc.isValid()) {
SemaRef.Diag(DVar.ImplicitDSALoc, diag::note_omp_implicit_dsa)
<< getOpenMPClauseName(DVar.CKind);
}
}
namespace {
class DSAAttrChecker : public StmtVisitor<DSAAttrChecker, void> {
DSAStackTy *Stack;
Sema &SemaRef;
bool ErrorFound;
CapturedStmt *CS;
llvm::SmallVector<Expr *, 8> ImplicitFirstprivate;
llvm::DenseMap<VarDecl *, Expr *> VarsWithInheritedDSA;
public:
void VisitDeclRefExpr(DeclRefExpr *E) {
if (auto *VD = dyn_cast<VarDecl>(E->getDecl())) {
// Skip internally declared variables.
if (VD->isLocalVarDecl() && !CS->capturesVariable(VD))
return;
auto DVar = Stack->getTopDSA(VD, false);
// Check if the variable has explicit DSA set and stop analysis if it so.
if (DVar.RefExpr) return;
auto ELoc = E->getExprLoc();
auto DKind = Stack->getCurrentDirective();
// The default(none) clause requires that each variable that is referenced
// in the construct, and does not have a predetermined data-sharing
// attribute, must have its data-sharing attribute explicitly determined
// by being listed in a data-sharing attribute clause.
if (DVar.CKind == OMPC_unknown && Stack->getDefaultDSA() == DSA_none &&
isParallelOrTaskRegion(DKind) &&
VarsWithInheritedDSA.count(VD) == 0) {
VarsWithInheritedDSA[VD] = E;
return;
}
// OpenMP [2.9.3.6, Restrictions, p.2]
// A list item that appears in a reduction clause of the innermost
// enclosing worksharing or parallel construct may not be accessed in an
// explicit task.
DVar = Stack->hasInnermostDSA(VD, MatchesAnyClause(OMPC_reduction),
[](OpenMPDirectiveKind K) -> bool {
return isOpenMPParallelDirective(K) ||
isOpenMPWorksharingDirective(K) ||
isOpenMPTeamsDirective(K);
},
false);
if (DKind == OMPD_task && DVar.CKind == OMPC_reduction) {
ErrorFound = true;
SemaRef.Diag(ELoc, diag::err_omp_reduction_in_task);
ReportOriginalDSA(SemaRef, Stack, VD, DVar);
return;
}
// Define implicit data-sharing attributes for task.
DVar = Stack->getImplicitDSA(VD, false);
if (DKind == OMPD_task && DVar.CKind != OMPC_shared)
ImplicitFirstprivate.push_back(E);
}
}
void VisitOMPExecutableDirective(OMPExecutableDirective *S) {
for (auto *C : S->clauses()) {
// Skip analysis of arguments of implicitly defined firstprivate clause
// for task directives.
if (C && (!isa<OMPFirstprivateClause>(C) || C->getLocStart().isValid()))
for (auto *CC : C->children()) {
if (CC)
Visit(CC);
}
}
}
void VisitStmt(Stmt *S) {
for (auto *C : S->children()) {
if (C && !isa<OMPExecutableDirective>(C))
Visit(C);
}
}
bool isErrorFound() { return ErrorFound; }
ArrayRef<Expr *> getImplicitFirstprivate() { return ImplicitFirstprivate; }
llvm::DenseMap<VarDecl *, Expr *> &getVarsWithInheritedDSA() {
return VarsWithInheritedDSA;
}
DSAAttrChecker(DSAStackTy *S, Sema &SemaRef, CapturedStmt *CS)
: Stack(S), SemaRef(SemaRef), ErrorFound(false), CS(CS) {}
};
} // namespace
void Sema::ActOnOpenMPRegionStart(OpenMPDirectiveKind DKind, Scope *CurScope) {
switch (DKind) {
case OMPD_parallel: {
QualType KmpInt32Ty = Context.getIntTypeForBitwidth(32, 1);
QualType KmpInt32PtrTy =
Context.getPointerType(KmpInt32Ty).withConst().withRestrict();
Sema::CapturedParamNameType Params[] = {
std::make_pair(".global_tid.", KmpInt32PtrTy),
std::make_pair(".bound_tid.", KmpInt32PtrTy),
std::make_pair(StringRef(), QualType()) // __context with shared vars
};
ActOnCapturedRegionStart(DSAStack->getConstructLoc(), CurScope, CR_OpenMP,
Params);
break;
}
case OMPD_simd: {
Sema::CapturedParamNameType Params[] = {
std::make_pair(StringRef(), QualType()) // __context with shared vars
};
ActOnCapturedRegionStart(DSAStack->getConstructLoc(), CurScope, CR_OpenMP,
Params);
break;
}
case OMPD_for: {
Sema::CapturedParamNameType Params[] = {
std::make_pair(StringRef(), QualType()) // __context with shared vars
};
ActOnCapturedRegionStart(DSAStack->getConstructLoc(), CurScope, CR_OpenMP,
Params);
break;
}
case OMPD_for_simd: {
Sema::CapturedParamNameType Params[] = {
std::make_pair(StringRef(), QualType()) // __context with shared vars
};
ActOnCapturedRegionStart(DSAStack->getConstructLoc(), CurScope, CR_OpenMP,
Params);
break;
}
case OMPD_sections: {
Sema::CapturedParamNameType Params[] = {
std::make_pair(StringRef(), QualType()) // __context with shared vars
};
ActOnCapturedRegionStart(DSAStack->getConstructLoc(), CurScope, CR_OpenMP,
Params);
break;
}
case OMPD_section: {
Sema::CapturedParamNameType Params[] = {
std::make_pair(StringRef(), QualType()) // __context with shared vars
};
ActOnCapturedRegionStart(DSAStack->getConstructLoc(), CurScope, CR_OpenMP,
Params);
break;
}
case OMPD_single: {
Sema::CapturedParamNameType Params[] = {
std::make_pair(StringRef(), QualType()) // __context with shared vars
};
ActOnCapturedRegionStart(DSAStack->getConstructLoc(), CurScope, CR_OpenMP,
Params);
break;
}
case OMPD_master: {
Sema::CapturedParamNameType Params[] = {
std::make_pair(StringRef(), QualType()) // __context with shared vars
};
ActOnCapturedRegionStart(DSAStack->getConstructLoc(), CurScope, CR_OpenMP,
Params);
break;
}
case OMPD_critical: {
Sema::CapturedParamNameType Params[] = {
std::make_pair(StringRef(), QualType()) // __context with shared vars
};
ActOnCapturedRegionStart(DSAStack->getConstructLoc(), CurScope, CR_OpenMP,
Params);
break;
}
case OMPD_parallel_for: {
QualType KmpInt32Ty = Context.getIntTypeForBitwidth(32, 1);
QualType KmpInt32PtrTy =
Context.getPointerType(KmpInt32Ty).withConst().withRestrict();
Sema::CapturedParamNameType Params[] = {
std::make_pair(".global_tid.", KmpInt32PtrTy),
std::make_pair(".bound_tid.", KmpInt32PtrTy),
std::make_pair(StringRef(), QualType()) // __context with shared vars
};
ActOnCapturedRegionStart(DSAStack->getConstructLoc(), CurScope, CR_OpenMP,
Params);
break;
}
case OMPD_parallel_for_simd: {
QualType KmpInt32Ty = Context.getIntTypeForBitwidth(32, 1);
QualType KmpInt32PtrTy =
Context.getPointerType(KmpInt32Ty).withConst().withRestrict();
Sema::CapturedParamNameType Params[] = {
std::make_pair(".global_tid.", KmpInt32PtrTy),
std::make_pair(".bound_tid.", KmpInt32PtrTy),
std::make_pair(StringRef(), QualType()) // __context with shared vars
};
ActOnCapturedRegionStart(DSAStack->getConstructLoc(), CurScope, CR_OpenMP,
Params);
break;
}
case OMPD_parallel_sections: {
QualType KmpInt32Ty = Context.getIntTypeForBitwidth(32, 1);
QualType KmpInt32PtrTy =
Context.getPointerType(KmpInt32Ty).withConst().withRestrict();
Sema::CapturedParamNameType Params[] = {
std::make_pair(".global_tid.", KmpInt32PtrTy),
std::make_pair(".bound_tid.", KmpInt32PtrTy),
std::make_pair(StringRef(), QualType()) // __context with shared vars
};
ActOnCapturedRegionStart(DSAStack->getConstructLoc(), CurScope, CR_OpenMP,
Params);
break;
}
case OMPD_task: {
QualType KmpInt32Ty = Context.getIntTypeForBitwidth(32, 1);
QualType Args[] = {Context.VoidPtrTy.withConst().withRestrict()};
FunctionProtoType::ExtProtoInfo EPI;
EPI.Variadic = true;
QualType CopyFnType = Context.getFunctionType(Context.VoidTy, Args, EPI);
Sema::CapturedParamNameType Params[] = {
std::make_pair(".global_tid.", KmpInt32Ty),
std::make_pair(".part_id.", KmpInt32Ty),
std::make_pair(".privates.",
Context.VoidPtrTy.withConst().withRestrict()),
std::make_pair(
".copy_fn.",
Context.getPointerType(CopyFnType).withConst().withRestrict()),
std::make_pair(StringRef(), QualType()) // __context with shared vars
};
ActOnCapturedRegionStart(DSAStack->getConstructLoc(), CurScope, CR_OpenMP,
Params);
// Mark this captured region as inlined, because we don't use outlined
// function directly.
getCurCapturedRegion()->TheCapturedDecl->addAttr(
AlwaysInlineAttr::CreateImplicit(
Context, AlwaysInlineAttr::Keyword_forceinline, SourceRange()));
break;
}
case OMPD_ordered: {
Sema::CapturedParamNameType Params[] = {
std::make_pair(StringRef(), QualType()) // __context with shared vars
};
ActOnCapturedRegionStart(DSAStack->getConstructLoc(), CurScope, CR_OpenMP,
Params);
break;
}
case OMPD_atomic: {
Sema::CapturedParamNameType Params[] = {
std::make_pair(StringRef(), QualType()) // __context with shared vars
};
ActOnCapturedRegionStart(DSAStack->getConstructLoc(), CurScope, CR_OpenMP,
Params);
break;
}
case OMPD_target_data:
case OMPD_target: {
Sema::CapturedParamNameType Params[] = {
std::make_pair(StringRef(), QualType()) // __context with shared vars
};
ActOnCapturedRegionStart(DSAStack->getConstructLoc(), CurScope, CR_OpenMP,
Params);
break;
}
case OMPD_teams: {
QualType KmpInt32Ty = Context.getIntTypeForBitwidth(32, 1);
QualType KmpInt32PtrTy =
Context.getPointerType(KmpInt32Ty).withConst().withRestrict();
Sema::CapturedParamNameType Params[] = {
std::make_pair(".global_tid.", KmpInt32PtrTy),
std::make_pair(".bound_tid.", KmpInt32PtrTy),
std::make_pair(StringRef(), QualType()) // __context with shared vars
};
ActOnCapturedRegionStart(DSAStack->getConstructLoc(), CurScope, CR_OpenMP,
Params);
break;
}
case OMPD_taskgroup: {
Sema::CapturedParamNameType Params[] = {
std::make_pair(StringRef(), QualType()) // __context with shared vars
};
ActOnCapturedRegionStart(DSAStack->getConstructLoc(), CurScope, CR_OpenMP,
Params);
break;
}
case OMPD_taskloop: {
Sema::CapturedParamNameType Params[] = {
std::make_pair(StringRef(), QualType()) // __context with shared vars
};
ActOnCapturedRegionStart(DSAStack->getConstructLoc(), CurScope, CR_OpenMP,
Params);
break;
}
case OMPD_taskloop_simd: {
Sema::CapturedParamNameType Params[] = {
std::make_pair(StringRef(), QualType()) // __context with shared vars
};
ActOnCapturedRegionStart(DSAStack->getConstructLoc(), CurScope, CR_OpenMP,
Params);
break;
}
case OMPD_distribute: {
Sema::CapturedParamNameType Params[] = {
std::make_pair(StringRef(), QualType()) // __context with shared vars
};
ActOnCapturedRegionStart(DSAStack->getConstructLoc(), CurScope, CR_OpenMP,
Params);
break;
}
case OMPD_threadprivate:
case OMPD_taskyield:
case OMPD_barrier:
case OMPD_taskwait:
case OMPD_cancellation_point:
case OMPD_cancel:
case OMPD_flush:
llvm_unreachable("OpenMP Directive is not allowed");
case OMPD_unknown:
llvm_unreachable("Unknown OpenMP directive");
}
}
StmtResult Sema::ActOnOpenMPRegionEnd(StmtResult S,
ArrayRef<OMPClause *> Clauses) {
if (!S.isUsable()) {
ActOnCapturedRegionError();
return StmtError();
}
OMPOrderedClause *OC = nullptr;
OMPScheduleClause *SC = nullptr;
SmallVector<OMPLinearClause *, 4> LCs;
// This is required for proper codegen.
for (auto *Clause : Clauses) {
if (isOpenMPPrivate(Clause->getClauseKind()) ||
Clause->getClauseKind() == OMPC_copyprivate ||
(getLangOpts().OpenMPUseTLS &&
getASTContext().getTargetInfo().isTLSSupported() &&
Clause->getClauseKind() == OMPC_copyin)) {
DSAStack->setForceVarCapturing(Clause->getClauseKind() == OMPC_copyin);
// Mark all variables in private list clauses as used in inner region.
for (auto *VarRef : Clause->children()) {
if (auto *E = cast_or_null<Expr>(VarRef)) {
MarkDeclarationsReferencedInExpr(E);
}
}
DSAStack->setForceVarCapturing(/*V=*/false);
} else if (isParallelOrTaskRegion(DSAStack->getCurrentDirective()) &&
Clause->getClauseKind() == OMPC_schedule) {
// Mark all variables in private list clauses as used in inner region.
// Required for proper codegen of combined directives.
// TODO: add processing for other clauses.
if (auto *E = cast_or_null<Expr>(
cast<OMPScheduleClause>(Clause)->getHelperChunkSize()))
MarkDeclarationsReferencedInExpr(E);
}
if (Clause->getClauseKind() == OMPC_schedule)
SC = cast<OMPScheduleClause>(Clause);
else if (Clause->getClauseKind() == OMPC_ordered)
OC = cast<OMPOrderedClause>(Clause);
else if (Clause->getClauseKind() == OMPC_linear)
LCs.push_back(cast<OMPLinearClause>(Clause));
}
bool ErrorFound = false;
// OpenMP, 2.7.1 Loop Construct, Restrictions
// The nonmonotonic modifier cannot be specified if an ordered clause is
// specified.
if (SC &&
(SC->getFirstScheduleModifier() == OMPC_SCHEDULE_MODIFIER_nonmonotonic ||
SC->getSecondScheduleModifier() ==
OMPC_SCHEDULE_MODIFIER_nonmonotonic) &&
OC) {
Diag(SC->getFirstScheduleModifier() == OMPC_SCHEDULE_MODIFIER_nonmonotonic
? SC->getFirstScheduleModifierLoc()
: SC->getSecondScheduleModifierLoc(),
diag::err_omp_schedule_nonmonotonic_ordered)
<< SourceRange(OC->getLocStart(), OC->getLocEnd());
ErrorFound = true;
}
if (!LCs.empty() && OC && OC->getNumForLoops()) {
for (auto *C : LCs) {
Diag(C->getLocStart(), diag::err_omp_linear_ordered)
<< SourceRange(OC->getLocStart(), OC->getLocEnd());
}
ErrorFound = true;
}
if (isOpenMPWorksharingDirective(DSAStack->getCurrentDirective()) &&
isOpenMPSimdDirective(DSAStack->getCurrentDirective()) && OC &&
OC->getNumForLoops()) {
Diag(OC->getLocStart(), diag::err_omp_ordered_simd)
<< getOpenMPDirectiveName(DSAStack->getCurrentDirective());
ErrorFound = true;
}
if (ErrorFound) {
ActOnCapturedRegionError();
return StmtError();
}
return ActOnCapturedRegionEnd(S.get());
}
static bool CheckNestingOfRegions(Sema &SemaRef, DSAStackTy *Stack,
OpenMPDirectiveKind CurrentRegion,
const DeclarationNameInfo &CurrentName,
OpenMPDirectiveKind CancelRegion,
SourceLocation StartLoc) {
// Allowed nesting of constructs
// +------------------+-----------------+------------------------------------+
// | Parent directive | Child directive | Closely (!), No-Closely(+), Both(*)|
// +------------------+-----------------+------------------------------------+
// | parallel | parallel | * |
// | parallel | for | * |
// | parallel | for simd | * |
// | parallel | master | * |
// | parallel | critical | * |
// | parallel | simd | * |
// | parallel | sections | * |
// | parallel | section | + |
// | parallel | single | * |
// | parallel | parallel for | * |
// | parallel |parallel for simd| * |
// | parallel |parallel sections| * |
// | parallel | task | * |
// | parallel | taskyield | * |
// | parallel | barrier | * |
// | parallel | taskwait | * |
// | parallel | taskgroup | * |
// | parallel | flush | * |
// | parallel | ordered | + |
// | parallel | atomic | * |
// | parallel | target | * |
// | parallel | teams | + |
// | parallel | cancellation | |
// | | point | ! |
// | parallel | cancel | ! |
// | parallel | taskloop | * |
// | parallel | taskloop simd | * |
// | parallel | distribute | |
// +------------------+-----------------+------------------------------------+
// | for | parallel | * |
// | for | for | + |
// | for | for simd | + |
// | for | master | + |
// | for | critical | * |
// | for | simd | * |
// | for | sections | + |
// | for | section | + |
// | for | single | + |
// | for | parallel for | * |
// | for |parallel for simd| * |
// | for |parallel sections| * |
// | for | task | * |
// | for | taskyield | * |
// | for | barrier | + |
// | for | taskwait | * |
// | for | taskgroup | * |
// | for | flush | * |
// | for | ordered | * (if construct is ordered) |
// | for | atomic | * |
// | for | target | * |
// | for | teams | + |
// | for | cancellation | |
// | | point | ! |
// | for | cancel | ! |
// | for | taskloop | * |
// | for | taskloop simd | * |
// | for | distribute | |
// +------------------+-----------------+------------------------------------+
// | master | parallel | * |
// | master | for | + |
// | master | for simd | + |
// | master | master | * |
// | master | critical | * |
// | master | simd | * |
// | master | sections | + |
// | master | section | + |
// | master | single | + |
// | master | parallel for | * |
// | master |parallel for simd| * |
// | master |parallel sections| * |
// | master | task | * |
// | master | taskyield | * |
// | master | barrier | + |
// | master | taskwait | * |
// | master | taskgroup | * |
// | master | flush | * |
// | master | ordered | + |
// | master | atomic | * |
// | master | target | * |
// | master | teams | + |
// | master | cancellation | |
// | | point | |
// | master | cancel | |
// | master | taskloop | * |
// | master | taskloop simd | * |
// | master | distribute | |
// +------------------+-----------------+------------------------------------+
// | critical | parallel | * |
// | critical | for | + |
// | critical | for simd | + |
// | critical | master | * |
// | critical | critical | * (should have different names) |
// | critical | simd | * |
// | critical | sections | + |
// | critical | section | + |
// | critical | single | + |
// | critical | parallel for | * |
// | critical |parallel for simd| * |
// | critical |parallel sections| * |
// | critical | task | * |
// | critical | taskyield | * |
// | critical | barrier | + |
// | critical | taskwait | * |
// | critical | taskgroup | * |
// | critical | ordered | + |
// | critical | atomic | * |
// | critical | target | * |
// | critical | teams | + |
// | critical | cancellation | |
// | | point | |
// | critical | cancel | |
// | critical | taskloop | * |
// | critical | taskloop simd | * |
// | critical | distribute | |
// +------------------+-----------------+------------------------------------+
// | simd | parallel | |
// | simd | for | |
// | simd | for simd | |
// | simd | master | |
// | simd | critical | |
// | simd | simd | |
// | simd | sections | |
// | simd | section | |
// | simd | single | |
// | simd | parallel for | |
// | simd |parallel for simd| |
// | simd |parallel sections| |
// | simd | task | |
// | simd | taskyield | |
// | simd | barrier | |
// | simd | taskwait | |
// | simd | taskgroup | |
// | simd | flush | |
// | simd | ordered | + (with simd clause) |
// | simd | atomic | |
// | simd | target | |
// | simd | teams | |
// | simd | cancellation | |
// | | point | |
// | simd | cancel | |
// | simd | taskloop | |
// | simd | taskloop simd | |
// | simd | distribute | |
// +------------------+-----------------+------------------------------------+
// | for simd | parallel | |
// | for simd | for | |
// | for simd | for simd | |
// | for simd | master | |
// | for simd | critical | |
// | for simd | simd | |
// | for simd | sections | |
// | for simd | section | |
// | for simd | single | |
// | for simd | parallel for | |
// | for simd |parallel for simd| |
// | for simd |parallel sections| |
// | for simd | task | |
// | for simd | taskyield | |
// | for simd | barrier | |
// | for simd | taskwait | |
// | for simd | taskgroup | |
// | for simd | flush | |
// | for simd | ordered | + (with simd clause) |
// | for simd | atomic | |
// | for simd | target | |
// | for simd | teams | |
// | for simd | cancellation | |
// | | point | |
// | for simd | cancel | |
// | for simd | taskloop | |
// | for simd | taskloop simd | |
// | for simd | distribute | |
// +------------------+-----------------+------------------------------------+
// | parallel for simd| parallel | |
// | parallel for simd| for | |
// | parallel for simd| for simd | |
// | parallel for simd| master | |
// | parallel for simd| critical | |
// | parallel for simd| simd | |
// | parallel for simd| sections | |
// | parallel for simd| section | |
// | parallel for simd| single | |
// | parallel for simd| parallel for | |
// | parallel for simd|parallel for simd| |
// | parallel for simd|parallel sections| |
// | parallel for simd| task | |
// | parallel for simd| taskyield | |
// | parallel for simd| barrier | |
// | parallel for simd| taskwait | |
// | parallel for simd| taskgroup | |
// | parallel for simd| flush | |
// | parallel for simd| ordered | + (with simd clause) |
// | parallel for simd| atomic | |
// | parallel for simd| target | |
// | parallel for simd| teams | |
// | parallel for simd| cancellation | |
// | | point | |
// | parallel for simd| cancel | |
// | parallel for simd| taskloop | |
// | parallel for simd| taskloop simd | |
// | parallel for simd| distribute | |
// +------------------+-----------------+------------------------------------+
// | sections | parallel | * |
// | sections | for | + |
// | sections | for simd | + |
// | sections | master | + |
// | sections | critical | * |
// | sections | simd | * |
// | sections | sections | + |
// | sections | section | * |
// | sections | single | + |
// | sections | parallel for | * |
// | sections |parallel for simd| * |
// | sections |parallel sections| * |
// | sections | task | * |
// | sections | taskyield | * |
// | sections | barrier | + |
// | sections | taskwait | * |
// | sections | taskgroup | * |
// | sections | flush | * |
// | sections | ordered | + |
// | sections | atomic | * |
// | sections | target | * |
// | sections | teams | + |
// | sections | cancellation | |
// | | point | ! |
// | sections | cancel | ! |
// | sections | taskloop | * |
// | sections | taskloop simd | * |
// | sections | distribute | |
// +------------------+-----------------+------------------------------------+
// | section | parallel | * |
// | section | for | + |
// | section | for simd | + |
// | section | master | + |
// | section | critical | * |
// | section | simd | * |
// | section | sections | + |
// | section | section | + |
// | section | single | + |
// | section | parallel for | * |
// | section |parallel for simd| * |
// | section |parallel sections| * |
// | section | task | * |
// | section | taskyield | * |
// | section | barrier | + |
// | section | taskwait | * |
// | section | taskgroup | * |
// | section | flush | * |
// | section | ordered | + |
// | section | atomic | * |
// | section | target | * |
// | section | teams | + |
// | section | cancellation | |
// | | point | ! |
// | section | cancel | ! |
// | section | taskloop | * |
// | section | taskloop simd | * |
// | section | distribute | |
// +------------------+-----------------+------------------------------------+
// | single | parallel | * |
// | single | for | + |
// | single | for simd | + |
// | single | master | + |
// | single | critical | * |
// | single | simd | * |
// | single | sections | + |
// | single | section | + |
// | single | single | + |
// | single | parallel for | * |
// | single |parallel for simd| * |
// | single |parallel sections| * |
// | single | task | * |
// | single | taskyield | * |
// | single | barrier | + |
// | single | taskwait | * |
// | single | taskgroup | * |
// | single | flush | * |
// | single | ordered | + |
// | single | atomic | * |
// | single | target | * |
// | single | teams | + |
// | single | cancellation | |
// | | point | |
// | single | cancel | |
// | single | taskloop | * |
// | single | taskloop simd | * |
// | single | distribute | |
// +------------------+-----------------+------------------------------------+
// | parallel for | parallel | * |
// | parallel for | for | + |
// | parallel for | for simd | + |
// | parallel for | master | + |
// | parallel for | critical | * |
// | parallel for | simd | * |
// | parallel for | sections | + |
// | parallel for | section | + |
// | parallel for | single | + |
// | parallel for | parallel for | * |
// | parallel for |parallel for simd| * |
// | parallel for |parallel sections| * |
// | parallel for | task | * |
// | parallel for | taskyield | * |
// | parallel for | barrier | + |
// | parallel for | taskwait | * |
// | parallel for | taskgroup | * |
// | parallel for | flush | * |
// | parallel for | ordered | * (if construct is ordered) |
// | parallel for | atomic | * |
// | parallel for | target | * |
// | parallel for | teams | + |
// | parallel for | cancellation | |
// | | point | ! |
// | parallel for | cancel | ! |
// | parallel for | taskloop | * |
// | parallel for | taskloop simd | * |
// | parallel for | distribute | |
// +------------------+-----------------+------------------------------------+
// | parallel sections| parallel | * |
// | parallel sections| for | + |
// | parallel sections| for simd | + |
// | parallel sections| master | + |
// | parallel sections| critical | + |
// | parallel sections| simd | * |
// | parallel sections| sections | + |
// | parallel sections| section | * |
// | parallel sections| single | + |
// | parallel sections| parallel for | * |
// | parallel sections|parallel for simd| * |
// | parallel sections|parallel sections| * |
// | parallel sections| task | * |
// | parallel sections| taskyield | * |
// | parallel sections| barrier | + |
// | parallel sections| taskwait | * |
// | parallel sections| taskgroup | * |
// | parallel sections| flush | * |
// | parallel sections| ordered | + |
// | parallel sections| atomic | * |
// | parallel sections| target | * |
// | parallel sections| teams | + |
// | parallel sections| cancellation | |
// | | point | ! |
// | parallel sections| cancel | ! |
// | parallel sections| taskloop | * |
// | parallel sections| taskloop simd | * |
// | parallel sections| distribute | |
// +------------------+-----------------+------------------------------------+
// | task | parallel | * |
// | task | for | + |
// | task | for simd | + |
// | task | master | + |
// | task | critical | * |
// | task | simd | * |
// | task | sections | + |
// | task | section | + |
// | task | single | + |
// | task | parallel for | * |
// | task |parallel for simd| * |
// | task |parallel sections| * |
// | task | task | * |
// | task | taskyield | * |
// | task | barrier | + |
// | task | taskwait | * |
// | task | taskgroup | * |
// | task | flush | * |
// | task | ordered | + |
// | task | atomic | * |
// | task | target | * |
// | task | teams | + |
// | task | cancellation | |
// | | point | ! |
// | task | cancel | ! |
// | task | taskloop | * |
// | task | taskloop simd | * |
// | task | distribute | |
// +------------------+-----------------+------------------------------------+
// | ordered | parallel | * |
// | ordered | for | + |
// | ordered | for simd | + |
// | ordered | master | * |
// | ordered | critical | * |
// | ordered | simd | * |
// | ordered | sections | + |
// | ordered | section | + |
// | ordered | single | + |
// | ordered | parallel for | * |
// | ordered |parallel for simd| * |
// | ordered |parallel sections| * |
// | ordered | task | * |
// | ordered | taskyield | * |
// | ordered | barrier | + |
// | ordered | taskwait | * |
// | ordered | taskgroup | * |
// | ordered | flush | * |
// | ordered | ordered | + |
// | ordered | atomic | * |
// | ordered | target | * |
// | ordered | teams | + |
// | ordered | cancellation | |
// | | point | |
// | ordered | cancel | |
// | ordered | taskloop | * |
// | ordered | taskloop simd | * |
// | ordered | distribute | |
// +------------------+-----------------+------------------------------------+
// | atomic | parallel | |
// | atomic | for | |
// | atomic | for simd | |
// | atomic | master | |
// | atomic | critical | |
// | atomic | simd | |
// | atomic | sections | |
// | atomic | section | |
// | atomic | single | |
// | atomic | parallel for | |
// | atomic |parallel for simd| |
// | atomic |parallel sections| |
// | atomic | task | |
// | atomic | taskyield | |
// | atomic | barrier | |
// | atomic | taskwait | |
// | atomic | taskgroup | |
// | atomic | flush | |
// | atomic | ordered | |
// | atomic | atomic | |
// | atomic | target | |
// | atomic | teams | |
// | atomic | cancellation | |
// | | point | |
// | atomic | cancel | |
// | atomic | taskloop | |
// | atomic | taskloop simd | |
// | atomic | distribute | |
// +------------------+-----------------+------------------------------------+
// | target | parallel | * |
// | target | for | * |
// | target | for simd | * |
// | target | master | * |
// | target | critical | * |
// | target | simd | * |
// | target | sections | * |
// | target | section | * |
// | target | single | * |
// | target | parallel for | * |
// | target |parallel for simd| * |
// | target |parallel sections| * |
// | target | task | * |
// | target | taskyield | * |
// | target | barrier | * |
// | target | taskwait | * |
// | target | taskgroup | * |
// | target | flush | * |
// | target | ordered | * |
// | target | atomic | * |
// | target | target | * |
// | target | teams | * |
// | target | cancellation | |
// | | point | |
// | target | cancel | |
// | target | taskloop | * |
// | target | taskloop simd | * |
// | target | distribute | |
// +------------------+-----------------+------------------------------------+
// | teams | parallel | * |
// | teams | for | + |
// | teams | for simd | + |
// | teams | master | + |
// | teams | critical | + |
// | teams | simd | + |
// | teams | sections | + |
// | teams | section | + |
// | teams | single | + |
// | teams | parallel for | * |
// | teams |parallel for simd| * |
// | teams |parallel sections| * |
// | teams | task | + |
// | teams | taskyield | + |
// | teams | barrier | + |
// | teams | taskwait | + |
// | teams | taskgroup | + |
// | teams | flush | + |
// | teams | ordered | + |
// | teams | atomic | + |
// | teams | target | + |
// | teams | teams | + |
// | teams | cancellation | |
// | | point | |
// | teams | cancel | |
// | teams | taskloop | + |
// | teams | taskloop simd | + |
// | teams | distribute | ! |
// +------------------+-----------------+------------------------------------+
// | taskloop | parallel | * |
// | taskloop | for | + |
// | taskloop | for simd | + |
// | taskloop | master | + |
// | taskloop | critical | * |
// | taskloop | simd | * |
// | taskloop | sections | + |
// | taskloop | section | + |
// | taskloop | single | + |
// | taskloop | parallel for | * |
// | taskloop |parallel for simd| * |
// | taskloop |parallel sections| * |
// | taskloop | task | * |
// | taskloop | taskyield | * |
// | taskloop | barrier | + |
// | taskloop | taskwait | * |
// | taskloop | taskgroup | * |
// | taskloop | flush | * |
// | taskloop | ordered | + |
// | taskloop | atomic | * |
// | taskloop | target | * |
// | taskloop | teams | + |
// | taskloop | cancellation | |
// | | point | |
// | taskloop | cancel | |
// | taskloop | taskloop | * |
// | taskloop | distribute | |
// +------------------+-----------------+------------------------------------+
// | taskloop simd | parallel | |
// | taskloop simd | for | |
// | taskloop simd | for simd | |
// | taskloop simd | master | |
// | taskloop simd | critical | |
// | taskloop simd | simd | |
// | taskloop simd | sections | |
// | taskloop simd | section | |
// | taskloop simd | single | |
// | taskloop simd | parallel for | |
// | taskloop simd |parallel for simd| |
// | taskloop simd |parallel sections| |
// | taskloop simd | task | |
// | taskloop simd | taskyield | |
// | taskloop simd | barrier | |
// | taskloop simd | taskwait | |
// | taskloop simd | taskgroup | |
// | taskloop simd | flush | |
// | taskloop simd | ordered | + (with simd clause) |
// | taskloop simd | atomic | |
// | taskloop simd | target | |
// | taskloop simd | teams | |
// | taskloop simd | cancellation | |
// | | point | |
// | taskloop simd | cancel | |
// | taskloop simd | taskloop | |
// | taskloop simd | taskloop simd | |
// | taskloop simd | distribute | |
// +------------------+-----------------+------------------------------------+
// | distribute | parallel | * |
// | distribute | for | * |
// | distribute | for simd | * |
// | distribute | master | * |
// | distribute | critical | * |
// | distribute | simd | * |
// | distribute | sections | * |
// | distribute | section | * |
// | distribute | single | * |
// | distribute | parallel for | * |
// | distribute |parallel for simd| * |
// | distribute |parallel sections| * |
// | distribute | task | * |
// | distribute | taskyield | * |
// | distribute | barrier | * |
// | distribute | taskwait | * |
// | distribute | taskgroup | * |
// | distribute | flush | * |
// | distribute | ordered | + |
// | distribute | atomic | * |
// | distribute | target | |
// | distribute | teams | |
// | distribute | cancellation | + |
// | | point | |
// | distribute | cancel | + |
// | distribute | taskloop | * |
// | distribute | taskloop simd | * |
// | distribute | distribute | |
// +------------------+-----------------+------------------------------------+
if (Stack->getCurScope()) {
auto ParentRegion = Stack->getParentDirective();
bool NestingProhibited = false;
bool CloseNesting = true;
enum {
NoRecommend,
ShouldBeInParallelRegion,
ShouldBeInOrderedRegion,
ShouldBeInTargetRegion,
ShouldBeInTeamsRegion
} Recommend = NoRecommend;
if (isOpenMPSimdDirective(ParentRegion) && CurrentRegion != OMPD_ordered) {
// OpenMP [2.16, Nesting of Regions]
// OpenMP constructs may not be nested inside a simd region.
// OpenMP [2.8.1,simd Construct, Restrictions]
// An ordered construct with the simd clause is the only OpenMP construct
// that can appear in the simd region.
SemaRef.Diag(StartLoc, diag::err_omp_prohibited_region_simd);
return true;
}
if (ParentRegion == OMPD_atomic) {
// OpenMP [2.16, Nesting of Regions]
// OpenMP constructs may not be nested inside an atomic region.
SemaRef.Diag(StartLoc, diag::err_omp_prohibited_region_atomic);
return true;
}
if (CurrentRegion == OMPD_section) {
// OpenMP [2.7.2, sections Construct, Restrictions]
// Orphaned section directives are prohibited. That is, the section
// directives must appear within the sections construct and must not be
// encountered elsewhere in the sections region.
if (ParentRegion != OMPD_sections &&
ParentRegion != OMPD_parallel_sections) {
SemaRef.Diag(StartLoc, diag::err_omp_orphaned_section_directive)
<< (ParentRegion != OMPD_unknown)
<< getOpenMPDirectiveName(ParentRegion);
return true;
}
return false;
}
// Allow some constructs to be orphaned (they could be used in functions,
// called from OpenMP regions with the required preconditions).
if (ParentRegion == OMPD_unknown)
return false;
if (CurrentRegion == OMPD_cancellation_point ||
CurrentRegion == OMPD_cancel) {
// OpenMP [2.16, Nesting of Regions]
// A cancellation point construct for which construct-type-clause is
// taskgroup must be nested inside a task construct. A cancellation
// point construct for which construct-type-clause is not taskgroup must
// be closely nested inside an OpenMP construct that matches the type
// specified in construct-type-clause.
// A cancel construct for which construct-type-clause is taskgroup must be
// nested inside a task construct. A cancel construct for which
// construct-type-clause is not taskgroup must be closely nested inside an
// OpenMP construct that matches the type specified in
// construct-type-clause.
NestingProhibited =
!((CancelRegion == OMPD_parallel && ParentRegion == OMPD_parallel) ||
(CancelRegion == OMPD_for &&
(ParentRegion == OMPD_for || ParentRegion == OMPD_parallel_for)) ||
(CancelRegion == OMPD_taskgroup && ParentRegion == OMPD_task) ||
(CancelRegion == OMPD_sections &&
(ParentRegion == OMPD_section || ParentRegion == OMPD_sections ||
ParentRegion == OMPD_parallel_sections)));
} else if (CurrentRegion == OMPD_master) {
// OpenMP [2.16, Nesting of Regions]
// A master region may not be closely nested inside a worksharing,
// atomic, or explicit task region.
NestingProhibited = isOpenMPWorksharingDirective(ParentRegion) ||
ParentRegion == OMPD_task ||
isOpenMPTaskLoopDirective(ParentRegion);
} else if (CurrentRegion == OMPD_critical && CurrentName.getName()) {
// OpenMP [2.16, Nesting of Regions]
// A critical region may not be nested (closely or otherwise) inside a
// critical region with the same name. Note that this restriction is not
// sufficient to prevent deadlock.
SourceLocation PreviousCriticalLoc;
bool DeadLock =
Stack->hasDirective([CurrentName, &PreviousCriticalLoc](
OpenMPDirectiveKind K,
const DeclarationNameInfo &DNI,
SourceLocation Loc)
->bool {
if (K == OMPD_critical &&
DNI.getName() == CurrentName.getName()) {
PreviousCriticalLoc = Loc;
return true;
} else
return false;
},
false /* skip top directive */);
if (DeadLock) {
SemaRef.Diag(StartLoc,
diag::err_omp_prohibited_region_critical_same_name)
<< CurrentName.getName();
if (PreviousCriticalLoc.isValid())
SemaRef.Diag(PreviousCriticalLoc,
diag::note_omp_previous_critical_region);
return true;
}
} else if (CurrentRegion == OMPD_barrier) {
// OpenMP [2.16, Nesting of Regions]
// A barrier region may not be closely nested inside a worksharing,
// explicit task, critical, ordered, atomic, or master region.
NestingProhibited =
isOpenMPWorksharingDirective(ParentRegion) ||
ParentRegion == OMPD_task || ParentRegion == OMPD_master ||
ParentRegion == OMPD_critical || ParentRegion == OMPD_ordered ||
isOpenMPTaskLoopDirective(ParentRegion);
} else if (isOpenMPWorksharingDirective(CurrentRegion) &&
!isOpenMPParallelDirective(CurrentRegion)) {
// OpenMP [2.16, Nesting of Regions]
// A worksharing region may not be closely nested inside a worksharing,
// explicit task, critical, ordered, atomic, or master region.
NestingProhibited =
isOpenMPWorksharingDirective(ParentRegion) ||
ParentRegion == OMPD_task || ParentRegion == OMPD_master ||
ParentRegion == OMPD_critical || ParentRegion == OMPD_ordered ||
isOpenMPTaskLoopDirective(ParentRegion);
Recommend = ShouldBeInParallelRegion;
} else if (CurrentRegion == OMPD_ordered) {
// OpenMP [2.16, Nesting of Regions]
// An ordered region may not be closely nested inside a critical,
// atomic, or explicit task region.
// An ordered region must be closely nested inside a loop region (or
// parallel loop region) with an ordered clause.
// OpenMP [2.8.1,simd Construct, Restrictions]
// An ordered construct with the simd clause is the only OpenMP construct
// that can appear in the simd region.
NestingProhibited = ParentRegion == OMPD_critical ||
ParentRegion == OMPD_task ||
isOpenMPTaskLoopDirective(ParentRegion) ||
!(isOpenMPSimdDirective(ParentRegion) ||
Stack->isParentOrderedRegion());
Recommend = ShouldBeInOrderedRegion;
} else if (isOpenMPTeamsDirective(CurrentRegion)) {
// OpenMP [2.16, Nesting of Regions]
// If specified, a teams construct must be contained within a target
// construct.
NestingProhibited = ParentRegion != OMPD_target;
Recommend = ShouldBeInTargetRegion;
Stack->setParentTeamsRegionLoc(Stack->getConstructLoc());
}
if (!NestingProhibited && isOpenMPTeamsDirective(ParentRegion)) {
// OpenMP [2.16, Nesting of Regions]
// distribute, parallel, parallel sections, parallel workshare, and the
// parallel loop and parallel loop SIMD constructs are the only OpenMP
// constructs that can be closely nested in the teams region.
NestingProhibited = !isOpenMPParallelDirective(CurrentRegion) &&
!isOpenMPDistributeDirective(CurrentRegion);
Recommend = ShouldBeInParallelRegion;
}
if (!NestingProhibited && isOpenMPDistributeDirective(CurrentRegion)) {
// OpenMP 4.5 [2.17 Nesting of Regions]
// The region associated with the distribute construct must be strictly
// nested inside a teams region
NestingProhibited = !isOpenMPTeamsDirective(ParentRegion);
Recommend = ShouldBeInTeamsRegion;
}
if (NestingProhibited) {
SemaRef.Diag(StartLoc, diag::err_omp_prohibited_region)
<< CloseNesting << getOpenMPDirectiveName(ParentRegion) << Recommend
<< getOpenMPDirectiveName(CurrentRegion);
return true;
}
}
return false;
}
static bool checkIfClauses(Sema &S, OpenMPDirectiveKind Kind,
ArrayRef<OMPClause *> Clauses,
ArrayRef<OpenMPDirectiveKind> AllowedNameModifiers) {
bool ErrorFound = false;
unsigned NamedModifiersNumber = 0;
SmallVector<const OMPIfClause *, OMPC_unknown + 1> FoundNameModifiers(
OMPD_unknown + 1);
SmallVector<SourceLocation, 4> NameModifierLoc;
for (const auto *C : Clauses) {
if (const auto *IC = dyn_cast_or_null<OMPIfClause>(C)) {
// At most one if clause without a directive-name-modifier can appear on
// the directive.
OpenMPDirectiveKind CurNM = IC->getNameModifier();
if (FoundNameModifiers[CurNM]) {
S.Diag(C->getLocStart(), diag::err_omp_more_one_clause)
<< getOpenMPDirectiveName(Kind) << getOpenMPClauseName(OMPC_if)
<< (CurNM != OMPD_unknown) << getOpenMPDirectiveName(CurNM);
ErrorFound = true;
} else if (CurNM != OMPD_unknown) {
NameModifierLoc.push_back(IC->getNameModifierLoc());
++NamedModifiersNumber;
}
FoundNameModifiers[CurNM] = IC;
if (CurNM == OMPD_unknown)
continue;
// Check if the specified name modifier is allowed for the current
// directive.
// At most one if clause with the particular directive-name-modifier can
// appear on the directive.
bool MatchFound = false;
for (auto NM : AllowedNameModifiers) {
if (CurNM == NM) {
MatchFound = true;
break;
}
}
if (!MatchFound) {
S.Diag(IC->getNameModifierLoc(),
diag::err_omp_wrong_if_directive_name_modifier)
<< getOpenMPDirectiveName(CurNM) << getOpenMPDirectiveName(Kind);
ErrorFound = true;
}
}
}
// If any if clause on the directive includes a directive-name-modifier then
// all if clauses on the directive must include a directive-name-modifier.
if (FoundNameModifiers[OMPD_unknown] && NamedModifiersNumber > 0) {
if (NamedModifiersNumber == AllowedNameModifiers.size()) {
S.Diag(Fou