blob: 705e6f5ef0a35c924a4df382d174d180db28520a [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() {}
};
private:
struct DSAInfo {
OpenMPClauseKind Attributes;
DeclRefExpr *RefExpr;
};
typedef llvm::SmallDenseMap<VarDecl *, DSAInfo, 64> DeclSAMapTy;
typedef llvm::SmallDenseMap<VarDecl *, DeclRefExpr *, 64> AlignedMapTy;
typedef llvm::DenseSet<VarDecl *> LoopControlVariablesSetTy;
struct SharingMapTy {
DeclSAMapTy SharingMap;
AlignedMapTy AlignedMap;
LoopControlVariablesSetTy LCVSet;
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 CollapseNumber;
SourceLocation InnerTeamsRegionLoc;
SharingMapTy(OpenMPDirectiveKind DKind, DeclarationNameInfo Name,
Scope *CurScope, SourceLocation Loc)
: SharingMap(), AlignedMap(), LCVSet(), DefaultAttr(DSA_unspecified),
Directive(DKind), DirectiveName(std::move(Name)), CurScope(CurScope),
ConstructLoc(Loc), OrderedRegion(), NowaitRegion(false),
CancelRegion(false), CollapseNumber(1), InnerTeamsRegionLoc() {}
SharingMapTy()
: SharingMap(), AlignedMap(), LCVSet(), DefaultAttr(DSA_unspecified),
Directive(OMPD_unknown), DirectiveName(), CurScope(nullptr),
ConstructLoc(), OrderedRegion(), NowaitRegion(false),
CancelRegion(false), CollapseNumber(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;
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();
}
/// \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.
bool isLoopControlVariable(VarDecl *D);
/// \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 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 setCollapseNumber(unsigned Val) { Stack.back().CollapseNumber = Val; }
/// \brief Return collapse value for region.
unsigned getCollapseNumber() const {
return Stack.back().CollapseNumber;
}
/// \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; }
};
bool isParallelOrTaskRegion(OpenMPDirectiveKind DKind) {
return isOpenMPParallelDirective(DKind) || DKind == OMPD_task ||
isOpenMPTeamsDirective(DKind) || DKind == OMPD_unknown;
}
} // 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().LCVSet.insert(D);
}
bool DSAStackTy::isLoopControlVariable(VarDecl *D) {
assert(Stack.size() > 1 && "Data-sharing attributes stack is empty");
D = D->getCanonicalDecl();
return Stack.back().LCVSet.count(D) > 0;
}
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.1]
// Variables with automatic storage duration that are declared in a scope
// inside the construct are private.
OpenMPDirectiveKind Kind =
FromParent ? getParentDirective() : getCurrentDirective();
auto StartI = std::next(Stack.rbegin());
auto EndI = std::prev(Stack.rend());
if (FromParent && StartI != EndI) {
StartI = std::next(StartI);
}
if (!isParallelOrTaskRegion(Kind)) {
if (isOpenMPLocal(D, StartI) &&
((D->isLocalVarDecl() && (D->getStorageClass() == SC_Auto ||
D->getStorageClass() == SC_None)) ||
isa<ParmVarDecl>(D))) {
DVar.CKind = OMPC_private;
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 (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 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;
}
void Sema::InitDataSharingAttributesStack() {
VarDataSharingAttributesStack = new DSAStackTy(*this);
}
#define DSAStack static_cast<DSAStackTy *>(VarDataSharingAttributesStack)
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 DeclGroupPtrTy();
}
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_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();
}
// 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);
}
}
}
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 | ! |
// +------------------+-----------------+------------------------------------+
// | 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 | ! |
// +------------------+-----------------+------------------------------------+
// | 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 | |
// +------------------+-----------------+------------------------------------+
// | 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 | |
// +------------------+-----------------+------------------------------------+
// | 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 | |
// +------------------+-----------------+------------------------------------+
// | 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 | |
// +------------------+-----------------+------------------------------------+
// | 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 | |
// +------------------+-----------------+------------------------------------+
// | 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 | ! |
// +------------------+-----------------+------------------------------------+
// | 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 | ! |
// +------------------+-----------------+------------------------------------+
// | 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 | |
// +------------------+-----------------+------------------------------------+
// | 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 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 | ! |
// +------------------+-----------------+------------------------------------+
// | 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 | ! |
// +------------------+-----------------+------------------------------------+
// | 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 | |
// +------------------+-----------------+------------------------------------+
// | 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 | |
// +------------------+-----------------+------------------------------------+
// | 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 | |
// +------------------+-----------------+------------------------------------+
// | 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 | |
// +------------------+-----------------+------------------------------------+
if (Stack->getCurScope()) {
auto ParentRegion = Stack->getParentDirective();
bool NestingProhibited = false;
bool CloseNesting = true;
enum {
NoRecommend,
ShouldBeInParallelRegion,
ShouldBeInOrderedRegion,
ShouldBeInTargetRegion
} 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;
} 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;
} 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;
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 ||
!(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.
// TODO: add distribute directive.
NestingProhibited = !isOpenMPParallelDirective(CurrentRegion);
Recommend = ShouldBeInParallelRegion;
}
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(FoundNameModifiers[OMPD_unknown]->getLocStart(),
diag::err_omp_no_more_if_clause);
} else {
std::string Values;
std::string Sep(", ");
unsigned AllowedCnt = 0;
unsigned TotalAllowedNum =
AllowedNameModifiers.size() - NamedModifiersNumber;
for (unsigned Cnt = 0, End = AllowedNameModifiers.size(); Cnt < End;
++Cnt) {
OpenMPDirectiveKind NM = AllowedNameModifiers[Cnt];
if (!FoundNameModifiers[NM]) {
Values += "'";
Values += getOpenMPDirectiveName(NM);
Values += "'";
if (AllowedCnt + 2 == TotalAllowedNum)
Values += " or ";
else if (AllowedCnt + 1 != TotalAllowedNum)
Values += Sep;
++AllowedCnt;
}
}
S.Diag(FoundNameModifiers[OMPD_unknown]->getCondition()->getLocStart(),
diag::err_omp_unnamed_if_clause)
<< (TotalAllowedNum > 1) << Values;
}
for (auto Loc : NameModifierLoc) {
S.Diag(Loc, diag::note_omp_previous_named_if_clause);
}
ErrorFound = true;
}
return ErrorFound;
}
StmtResult Sema::ActOnOpenMPExecutableDirective(
OpenMPDirectiveKind Kind, const DeclarationNameInfo &DirName,
OpenMPDirectiveKind CancelRegion, ArrayRef<OMPClause *> Clauses,
Stmt *AStmt, SourceLocation StartLoc, SourceLocation EndLoc) {
StmtResult Res = StmtError();
if (CheckNestingOfRegions(*this, DSAStack, Kind, DirName, CancelRegion,
StartLoc))
return StmtError();
llvm::SmallVector<OMPClause *, 8> ClausesWithImplicit;
llvm::DenseMap<VarDecl *, Expr *> VarsWithInheritedDSA;
bool ErrorFound = false;
ClausesWithImplicit.append(Clauses.begin(), Clauses.end());
if (AStmt) {
assert(isa<CapturedStmt>(AStmt) && "Captured statement expected");
// Check default data sharing attributes for referenced variables.
DSAAttrChecker DSAChecker(DSAStack, *this, cast<CapturedStmt>(AStmt));
DSAChecker.Visit(cast<CapturedStmt>(AStmt)->getCapturedStmt());
if (DSAChecker.isErrorFound())
return StmtError();
// Generate list of implicitly defined firstprivate variables.
VarsWithInheritedDSA = DSAChecker.getVarsWithInheritedDSA();
if (!DSAChecker.getImplicitFirstprivate().empty()) {
if (OMPClause *Implicit = ActOnOpenMPFirstprivateClause(
DSAChecker.getImplicitFirstprivate(), SourceLocation(),
SourceLocation(), SourceLocation())) {
ClausesWithImplicit.push_back(Implicit);
ErrorFound = cast<OMPFirstprivateClause>(Implicit)->varlist_size() !=
DSAChecker.getImplicitFirstprivate().size();
} else
ErrorFound = true;
}
}
llvm::SmallVector<OpenMPDirectiveKind, 4> AllowedNameModifiers;
switch (Kind) {
case OMPD_parallel:
Res = ActOnOpenMPParallelDirective(ClausesWithImplicit, AStmt, StartLoc,
EndLoc);
AllowedNameModifiers.push_back(OMPD_parallel);
break;
case OMPD_simd:
Res = ActOnOpenMPSimdDirective(ClausesWithImplicit, AStmt, StartLoc, EndLoc,
VarsWithInheritedDSA);
break;
case OMPD_for:
Res = ActOnOpenMPForDirective(ClausesWithImplicit, AStmt, StartLoc, EndLoc,
VarsWithInheritedDSA);
break;
case OMPD_for_simd:
Res = ActOnOpenMPForSimdDirective(ClausesWithImplicit, AStmt, StartLoc,
EndLoc, VarsWithInheritedDSA);
break;
case OMPD_sections:
Res = ActOnOpenMPSectionsDirective(ClausesWithImplicit, AStmt, StartLoc,
EndLoc);
break;
case OMPD_section:
assert(ClausesWithImplicit.empty() &&
"No clauses are allowed for 'omp section' directive");
Res = ActOnOpenMPSectionDirective(AStmt, StartLoc, EndLoc);
break;
case OMPD_single:
Res = ActOnOpenMPSingleDirective(ClausesWithImplicit, AStmt, StartLoc,
EndLoc);
break;
case OMPD_master:
assert(ClausesWithImplicit.empty() &&
"No clauses are allowed for 'omp master' directive");
Res = ActOnOpenMPMasterDirective(AStmt, StartLoc, EndLoc);
break;
case OMPD_critical:
assert(ClausesWithImplicit.empty() &&
"No clauses are allowed for 'omp critical' directive");
Res = ActOnOpenMPCriticalDirective(DirName, AStmt, StartLoc, EndLoc);
break;
case OMPD_parallel_for:
Res = ActOnOpenMPParallelForDirective(ClausesWithImplicit, AStmt, StartLoc,
EndLoc, VarsWithInheritedDSA);
AllowedNameModifiers.push_back(OMPD_parallel);
break;
case OMPD_parallel_for_simd:
Res = ActOnOpenMPParallelForSimdDirective(
ClausesWithImplicit, AStmt, StartLoc, EndLoc, VarsWithInheritedDSA);
AllowedNameModifiers.push_back(OMPD_parallel);
break;
case OMPD_parallel_sections:
Res = ActOnOpenMPParallelSectionsDirective(ClausesWithImplicit, AStmt,
StartLoc, EndLoc);
AllowedNameModifiers.push_back(OMPD_parallel);
break;
case OMPD_task:
Res =
ActOnOpenMPTaskDirective(ClausesWithImplicit, AStmt, StartLoc, EndLoc);
AllowedNameModifiers.push_back(OMPD_task);
break;
case OMPD_taskyield:
assert(ClausesWithImplicit.empty() &&
"No clauses are allowed for 'omp taskyield' directive");
assert(AStmt == nullptr &&
"No associated statement allowed for 'omp taskyield' directive");
Res = ActOnOpenMPTaskyieldDirective(StartLoc, EndLoc);
break;
case OMPD_barrier:
assert(ClausesWithImplicit.empty() &&
"No clauses are allowed for 'omp barrier' directive");
assert(AStmt == nullptr &&
"No associated statement allowed for 'omp barrier' directive");
Res = ActOnOpenMPBarrierDirective(StartLoc, EndLoc);
break;
case OMPD_taskwait:
assert(ClausesWithImplicit.empty() &&
"No clauses are allowed for 'omp taskwait' directive");
assert(AStmt == nullptr &&
"No associated statement allowed for 'omp taskwait' directive");
Res = ActOnOpenMPTaskwaitDirective(StartLoc, EndLoc);
break;
case OMPD_taskgroup:
assert(ClausesWithImplicit.empty() &&
"No clauses are allowed for 'omp taskgroup' directive");
Res = ActOnOpenMPTaskgroupDirective(AStmt, StartLoc, EndLoc);
break;
case OMPD_flush:
assert(AStmt == nullptr &&
"No associated statement allowed for 'omp flush' directive");
Res = ActOnOpenMPFlushDirective(ClausesWithImplicit, StartLoc, EndLoc);
break;
case OMPD_ordered:
Res = ActOnOpenMPOrderedDirective(ClausesWithImplicit, AStmt, StartLoc,
EndLoc);
break;
case OMPD_atomic:
Res = ActOnOpenMPAtomicDirective(ClausesWithImplicit, AStmt, StartLoc,
EndLoc);
break;
case OMPD_teams:
Res =
ActOnOpenMPTeamsDirective(ClausesWithImplicit, AStmt, StartLoc, EndLoc);
break;
case OMPD_target:
Res = ActOnOpenMPTargetDirective(ClausesWithImplicit, AStmt, StartLoc,
EndLoc);
AllowedNameModifiers.push_back(OMPD_target);
break;
case OMPD_cancellation_point:
assert(ClausesWithImplicit.empty() &&
"No clauses are allowed for 'omp cancellation point' directive");
assert(AStmt == nullptr && "No associated statement allowed for 'omp "
"cancellation point' directive");
Res = ActOnOpenMPCancellationPointDirective(StartLoc, EndLoc, CancelRegion);
break;
case OMPD_cancel:
assert(AStmt == nullptr &&
"No associated statement allowed for 'omp cancel' directive");
Res = ActOnOpenMPCancelDirective(ClausesWithImplicit, StartLoc, EndLoc,
CancelRegion);
AllowedNameModifiers.push_back(OMPD_cancel);
break;
case OMPD_target_data:
Res = ActOnOpenMPTargetDataDirective(ClausesWithImplicit, AStmt, StartLoc,
EndLoc);
AllowedNameModifiers.push_back(OMPD_target_data);
break;
case OMPD_threadprivate:
llvm_unreachable("OpenMP Directive is not allowed");
case OMPD_unknown:
llvm_unreachable("Unknown OpenMP directive");
}
for (auto P : VarsWithInheritedDSA) {
Diag(P.second->getExprLoc(), diag::err_omp_no_dsa_for_variable)
<< P.first << P.second->getSourceRange();
}
ErrorFound = !VarsWithInheritedDSA.empty() || ErrorFound;
if (!AllowedNameModifiers.empty())
ErrorFound = checkIfClauses(*this, Kind, Clauses, AllowedNameModifiers) ||
ErrorFound;
if (ErrorFound)
return StmtError();
return Res;
}
StmtResult Sema::ActOnOpenMPParallelDirective(ArrayRef<OMPClause *> Clauses,
Stmt *AStmt,
SourceLocation StartLoc,
SourceLocation EndLoc) {
if (!AStmt)
return StmtError();
CapturedStmt *CS = cast<CapturedStmt>(AStmt);
// 1.2.2 OpenMP Language Terminology
// Structured block - An executable statement with a single entry at the
// top and a single exit at the bottom.
// The point of exit cannot be a branch out of the structured block.
// longjmp() and throw() must not violate the entry/exit criteria.
CS->getCapturedDecl()->setNothrow();
getCurFunction()->setHasBranchProtectedScope();
return OMPParallelDirective::Create(Context, StartLoc, EndLoc, Clauses, AStmt,
DSAStack->isCancelRegion());
}
namespace {
/// \brief Helper class for checking canonical form of the OpenMP loops and
/// extracting iteration space of each loop in the loop nest, that will be used
/// for IR generation.
class OpenMPIterationSpaceChecker {
/// \brief Reference to Sema.
Sema &SemaRef;
/// \brief A location for diagnostics (when there is no some better location).
SourceLocation DefaultLoc;
/// \brief A location for diagnostics (when increment is not compatible).
SourceLocation ConditionLoc;
/// \brief A source location for referring to loop init later.
SourceRange InitSrcRange;
/// \brief A source location for referring to condition later.
SourceRange ConditionSrcRange;
/// \brief A source location for referring to increment later.
SourceRange IncrementSrcRange;
/// \brief Loop variable.
VarDecl *Var;
/// \brief Reference to loop variable.
DeclRefExpr *VarRef;
/// \brief Lower bound (initializer for the var).
Expr *LB;
/// \brief Upper bound.
Expr *UB;
/// \brief Loop step (increment).
Expr *Step;
/// \brief This flag is true when condition is one of:
/// Var < UB
/// Var <= UB
/// UB > Var
/// UB >= Var
bool TestIsLessOp;
/// \brief This flag is true when condition is strict ( < or > ).
bool TestIsStrictOp;
/// \brief This flag is true when step is subtracted on each iteration.
bool SubtractStep;
public:
OpenMPIterationSpaceChecker(Sema &SemaRef, SourceLocation DefaultLoc)
: SemaRef(SemaRef), DefaultLoc(DefaultLoc), ConditionLoc(DefaultLoc),
InitSrcRange(SourceRange()), ConditionSrcRange(SourceRange()),
IncrementSrcRange(SourceRange()), Var(nullptr), VarRef(nullptr),
LB(nullptr), UB(nullptr), Step(nullptr), TestIsLessOp(false),
TestIsStrictOp(false), SubtractStep(false) {}
/// \brief Check init-expr for canonical loop form and save loop counter
/// variable - #Var and its initialization value - #LB.
bool CheckInit(Stmt *S, bool EmitDiags = true);
/// \brief Check test-expr for canonical form, save upper-bound (#UB), flags
/// for less/greater and for strict/non-strict comparison.
bool CheckCond(Expr *S);
/// \brief Check incr-expr for canonical loop form and return true if it
/// does not conform, otherwise save loop step (#Step).
bool CheckInc(Expr *S);
/// \brief Return the loop counter variable.
VarDecl *GetLoopVar() const { return Var; }
/// \brief Return the reference expression to loop counter variable.
DeclRefExpr *GetLoopVarRefExpr() const { return VarRef; }
/// \brief Source range of the loop init.
SourceRange GetInitSrcRange() const { return InitSrcRange; }
/// \brief Source range of the loop condition.
SourceRange GetConditionSrcRange() const { return ConditionSrcRange; }
/// \brief Source range of the loop increment.
SourceRange GetIncrementSrcRange() const { return IncrementSrcRange; }
/// \brief True if the step should be subtracted.
bool ShouldSubtractStep() const { return SubtractStep; }
/// \brief Build the expression to calculate the number of iterations.
Expr *BuildNumIterations(Scope *S, const bool LimitedType) const;
/// \brief Build the precondition expression for the loops.
Expr *BuildPreCond(Scope *S, Expr *Cond) const;
/// \brief Build reference expression to the counter be used for codegen.
Expr *BuildCounterVar() const;
/// \brief Build reference expression to the private counter be used for
/// codegen.
Expr *BuildPrivateCounterVar() const;
/// \brief Build initization of the counter be used for codegen.
Expr *BuildCounterInit() const;
/// \brief Build step of the counter be used for codegen.
Expr *BuildCounterStep() const;
/// \brief Return true if any expression is dependent.
bool Dependent() const;
private:
/// \brief Check the right-hand side of an assignment in the increment
/// expression.
bool CheckIncRHS(Expr *RHS);
/// \brief Helper to set loop counter variable and its initializer.
bool SetVarAndLB(VarDecl *NewVar, DeclRefExpr *NewVarRefExpr, Expr *NewLB);
/// \brief Helper to set upper bound.
bool SetUB(Expr *NewUB, bool LessOp, bool StrictOp, SourceRange SR,
SourceLocation SL);
/// \brief Helper to set loop increment.
bool SetStep(Expr *NewStep, bool Subtract);
};
bool OpenMPIterationSpaceChecker::Dependent() const {
if (!Var) {
assert(!LB && !UB && !Step);
return false;
}
return Var->getType()->isDependentType() || (LB && LB->isValueDependent()) ||
(UB && UB->isValueDependent()) || (Step && Step->isValueDependent());
}
template <typename T>
static T *getExprAsWritten(T *E) {
if (auto *ExprTemp = dyn_cast<ExprWithCleanups>(E))
E = ExprTemp->getSubExpr();
if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(E))
E = MTE->GetTemporaryExpr();
while (auto *Binder = dyn_cast<CXXBindTemporaryExpr>(E))
E = Binder->getSubExpr();
if (auto *ICE = dyn_cast<ImplicitCastExpr>(E))
E = ICE->getSubExprAsWritten();
return E->IgnoreParens();
}
bool OpenMPIterationSpaceChecker::SetVarAndLB(VarDecl *NewVar,
DeclRefExpr *NewVarRefExpr,
Expr *NewLB) {
// State consistency checking to ensure correct usage.
assert(Var == nullptr && LB == nullptr && VarRef == nullptr &&
UB == nullptr && Step == nullptr && !TestIsLessOp && !TestIsStrictOp);
if (!NewVar || !NewLB)
return true;
Var = NewVar;
VarRef = NewVarRefExpr;
if (auto *CE = dyn_cast_or_null<CXXConstructExpr>(NewLB))
if (const CXXConstructorDecl *Ctor = CE->getConstructor())
if ((Ctor->isCopyOrMoveConstructor() ||
Ctor->isConvertingConstructor(/*AllowExplicit=*/false)) &&
CE->getNumArgs() > 0 && CE->getArg(0) != nullptr)
NewLB = CE->getArg(0)->IgnoreParenImpCasts();
LB = NewLB;
return false;
}
bool OpenMPIterationSpaceChecker::SetUB(Expr *NewUB, bool LessOp, bool StrictOp,
SourceRange SR, SourceLocation SL) {
// State consistency checking to ensure correct usage.
assert(Var != nullptr && LB != nullptr && UB == nullptr && Step == nullptr &&
!TestIsLessOp && !TestIsStrictOp);
if (!NewUB)
return true;
UB = NewUB;
TestIsLessOp = LessOp;
TestIsStrictOp = StrictOp;
ConditionSrcRange = SR;
ConditionLoc = SL;
return false;
}
bool OpenMPIterationSpaceChecker::SetStep(Expr *NewStep, bool Subtract) {
// State consistency checking to ensure correct usage.
assert(Var != nullptr && LB != nullptr && Step == nullptr);
if (!NewStep)
return true;
if (!NewStep->isValueDependent()) {
// Check that the step is integer expression.
SourceLocation StepLoc = NewStep->getLocStart();
ExprResult Val =
SemaRef.PerformOpenMPImplicitIntegerConversion(StepLoc, NewStep);
if (Val.isInvalid())
return true;
NewStep = Val.get();
// OpenMP [2.6, Canonical Loop Form, Restrictions]
// If test-expr is of form var relational-op b and relational-op is < or
// <= then incr-expr must cause var to increase on each iteration of the
// loop. If test-expr is of form var relational-op b and relational-op is
// > or >= then incr-expr must cause var to decrease on each iteration of
// the loop.
// If test-expr is of form b relational-op var and relational-op is < or
// <= then incr-expr must cause var to decrease on each iteration of the
// loop. If test-expr is of form b relational-op var and relational-op is
// > or >= then incr-expr must cause var to increase on each iteration of
// the loop.
llvm::APSInt Result;
bool IsConstant = NewStep->isIntegerConstantExpr(Result, SemaRef.Context);
bool IsUnsigned = !NewStep->getType()->hasSignedIntegerRepresentation();
bool IsConstNeg =
IsConstant && Result.isSigned() && (Subtract != Result.isNegative());
bool IsConstPos =
IsConstant && Result.isSigned() && (Subtract == Result.isNegative());
bool IsConstZero = IsConstant && !Result.getBoolValue();
if (UB && (IsConstZero ||
(TestIsLessOp ? (IsConstNeg || (IsUnsigned && Subtract))
: (IsConstPos || (IsUnsigned && !Subtract))))) {
SemaRef.Diag(NewStep->getExprLoc(),
diag::err_omp_loop_incr_not_compatible)
<< Var << TestIsLessOp << NewStep->getSourceRange();
SemaRef.Diag(ConditionLoc,
diag::note_omp_loop_cond_requres_compatible_incr)
<< TestIsLessOp << ConditionSrcRange;
return true;
}
if (TestIsLessOp == Subtract) {
NewStep = SemaRef.CreateBuiltinUnaryOp(NewStep->getExprLoc(), UO_Minus,
NewStep).get();
Subtract = !Subtract;
}
}
Step = NewStep;
SubtractStep = Subtract;
return false;
}
bool OpenMPIterationSpaceChecker::CheckInit(Stmt *S, bool EmitDiags) {
// Check init-expr for canonical loop form and save loop counter
// variable - #Var and its initialization value - #LB.
// OpenMP [2.6] Canonical loop form. init-expr may be one of the following:
// var = lb
// integer-type var = lb
// random-access-iterator-type var = lb
// pointer-type var = lb
//
if (!S) {
if (EmitDiags) {
SemaRef.Diag(DefaultLoc, diag::err_omp_loop_not_canonical_init);
}
return true;
}
InitSrcRange = S->getSourceRange();
if (Expr *E = dyn_cast<Expr>(S))
S = E->IgnoreParens();
if (auto BO = dyn_cast<BinaryOperator>(S)) {
if (BO->getOpcode() == BO_Assign)
if (auto DRE = dyn_cast<DeclRefExpr>(BO->getLHS()->IgnoreParens()))
return SetVarAndLB(dyn_cast<VarDecl>(DRE->getDecl()), DRE,
BO->getRHS());
} else if (auto DS = dyn_cast<DeclStmt>(S)) {
if (DS->isSingleDecl()) {
if (auto Var = dyn_cast_or_null<VarDecl>(DS->getSingleDecl())) {
if (Var->hasInit() && !Var->getType()->isReferenceType()) {
// Accept non-canonical init form here but emit ext. warning.
if (Var->getInitStyle() != VarDecl::CInit && EmitDiags)
SemaRef.Diag(S->getLocStart(),
diag::ext_omp_loop_not_canonical_init)
<< S->getSourceRange();
return SetVarAndLB(Var, nullptr, Var->getInit());
}
}
}
} else if (auto CE = dyn_cast<CXXOperatorCallExpr>(S))
if (CE->getOperator() == OO_Equal)
if (auto DRE = dyn_cast<DeclRefExpr>(CE->getArg(0)))
return SetVarAndLB(dyn_cast<VarDecl>(DRE->getDecl()), DRE,
CE->getArg(1));
if (EmitDiags) {
SemaRef.Diag(S->getLocStart(), diag::err_omp_loop_not_canonical_init)
<< S->getSourceRange();
}
return true;
}
/// \brief Ignore parenthesizes, implicit casts, copy constructor and return the
/// variable (which may be the loop variable) if possible.
static const VarDecl *GetInitVarDecl(const Expr *E) {
if (!E)
return nullptr;
E = getExprAsWritten(E);
if (auto *CE = dyn_cast_or_null<CXXConstructExpr>(E))
if (const CXXConstructorDecl *Ctor = CE->getConstructor())
if ((Ctor->isCopyOrMoveConstructor() ||
Ctor->isConvertingConstructor(/*AllowExplicit=*/false)) &&
CE->getNumArgs() > 0 && CE->getArg(0) != nullptr)
E = CE->getArg(0)->IgnoreParenImpCasts();
auto DRE = dyn_cast_or_null<DeclRefExpr>(E);
if (!DRE)
return nullptr;
return dyn_cast<VarDecl>(DRE->getDecl());
}
bool OpenMPIterationSpaceChecker::CheckCond(Expr *S) {
// Check test-expr for canonical form, save upper-bound UB, flags for
// less/greater and for strict/non-strict comparison.
// OpenMP [2.6] Canonical loop form. Test-expr may be one of the following:
// var relational-op b
// b relational-op var
//
if (!S) {
SemaRef.Diag(DefaultLoc, diag::err_omp_loop_not_canonical_cond) << Var;
return true;
}
S = getExprAsWritten(S);
SourceLocation CondLoc = S->getLocStart();
if (auto BO = dyn_cast<BinaryOperator>(S)) {
if (BO->isRelationalOp()) {
if (GetInitVarDecl(BO->getLHS()) == Var)
return SetUB(BO->getRHS(),
(BO->getOpcode() == BO_LT || BO->getOpcode() == BO_LE),
(BO->getOpcode() == BO_LT || BO->getOpcode() == BO_GT),
BO->getSourceRange(), BO->getOperatorLoc());
if (GetInitVarDecl(BO->getRHS()) == Var)
return SetUB(BO->getLHS(),
(BO->getOpcode() == BO_GT || BO->getOpcode() == BO_GE),
(BO->getOpcode() == BO_LT || BO->getOpcode() == BO_GT),
BO->getSourceRange(), BO->getOperatorLoc());
}
} else if (auto CE = dyn_cast<CXXOperatorCallExpr>(S)) {
if (CE->getNumArgs() == 2) {
auto Op = CE->getOperator();
switch (Op) {
case OO_Greater:
case OO_GreaterEqual:
case OO_Less:
case OO_LessEqual:
if (GetInitVarDecl(CE->getArg(0)) == Var)
return SetUB(CE->getArg(1), Op == OO_Less || Op == OO_LessEqual,
Op == OO_Less || Op == OO_Greater, CE->getSourceRange(),
CE->getOperatorLoc());
if (GetInitVarDecl(CE->getArg(1)) == Var)
return SetUB(CE->getArg(0), Op == OO_Greater || Op == OO_GreaterEqual,
Op == OO_Less || Op == OO_Greater, CE->getSourceRange(),
CE->getOperatorLoc());
break;
default:
break;
}
}
}
SemaRef.Diag(CondLoc, diag::err_omp_loop_not_canonical_cond)