//===----- SemaOpenACC.h - Semantic Analysis for OpenACC constructs -------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// \file /// This file declares semantic analysis for OpenACC constructs and /// clauses. /// //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_SEMA_SEMAOPENACC_H #define LLVM_CLANG_SEMA_SEMAOPENACC_H #include "clang/AST/DeclGroup.h" #include "clang/AST/OpenACCClause.h" #include "clang/AST/StmtOpenACC.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/OpenACCKinds.h" #include "clang/Basic/SourceLocation.h" #include "clang/Sema/Ownership.h" #include "clang/Sema/SemaBase.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Compiler.h" #include #include #include #include namespace clang { class IdentifierInfo; class OpenACCClause; class Scope; class SemaOpenACC : public SemaBase { public: using DeclGroupPtrTy = OpaquePtr; using RoutineRefListTy = std::pair; private: // We save a list of routine clauses that refer to a different function(that // is, routine-with-a-name) so that we can do the emission at the 'end'. We // have to do this, since functions can be emitted before they are referenced, // and the OpenACCRoutineDecl isn't necessarily emitted, as it might be in a // function/etc. So we do these emits at the end of the TU. llvm::SmallVector RoutineRefList; struct ComputeConstructInfo { /// Which type of compute construct we are inside of, which we can use to /// determine whether we should add loops to the above collection. We can /// also use it to diagnose loop construct clauses. OpenACCDirectiveKind Kind = OpenACCDirectiveKind::Invalid; // If we have an active compute construct, stores the list of clauses we've // prepared for it, so that we can diagnose limitations on child constructs. ArrayRef Clauses; } ActiveComputeConstructInfo; bool isInComputeConstruct() const { return ActiveComputeConstructInfo.Kind != OpenACCDirectiveKind::Invalid; } /// Certain clauses care about the same things that aren't specific to the /// individual clause, but can be shared by a few, so store them here. All /// require a 'no intervening constructs' rule, so we know they are all from /// the same 'place'. struct LoopCheckingInfo { /// Records whether we've seen the top level 'for'. We already diagnose /// later that the 'top level' is a for loop, so we use this to suppress the /// 'collapse inner loop not a 'for' loop' diagnostic. LLVM_PREFERRED_TYPE(bool) unsigned TopLevelLoopSeen : 1; /// Records whether this 'tier' of the loop has already seen a 'for' loop, /// used to diagnose if there are multiple 'for' loops at any one level. LLVM_PREFERRED_TYPE(bool) unsigned CurLevelHasLoopAlready : 1; } LoopInfo{/*TopLevelLoopSeen=*/false, /*CurLevelHasLoopAlready=*/false}; /// The 'collapse' clause requires quite a bit of checking while /// parsing/instantiating its body, so this structure/object keeps all of the /// necessary information as we do checking. This should rarely be directly /// modified, and typically should be controlled by the RAII objects. /// /// Collapse has an 'N' count that makes it apply to a number of loops 'below' /// it. struct CollapseCheckingInfo { const OpenACCCollapseClause *ActiveCollapse = nullptr; /// This is a value that maintains the current value of the 'N' on the /// current collapse, minus the depth that has already been traversed. When /// there is not an active collapse, or a collapse whose depth we don't know /// (for example, if it is a dependent value), this should be `nullopt`, /// else it should be 'N' minus the current depth traversed. std::optional CurCollapseCount; /// Records whether we've hit a CurCollapseCount of '0' on the way down, /// which allows us to diagnose if the value of 'N' is too large for the /// current number of 'for' loops. bool CollapseDepthSatisfied = true; /// Records the kind of the directive that this clause is attached to, which /// allows us to use it in diagnostics. OpenACCDirectiveKind DirectiveKind = OpenACCDirectiveKind::Invalid; } CollapseInfo; /// The 'tile' clause requires a bit of additional checking as well, so like /// the `CollapseCheckingInfo`, ensure we maintain information here too. struct TileCheckingInfo { OpenACCTileClause *ActiveTile = nullptr; /// This is the number of expressions on a 'tile' clause. This doesn't have /// to be an APSInt because it isn't the result of a constexpr, just by our /// own counting of elements. UnsignedOrNone CurTileCount = std::nullopt; /// Records whether we've hit a 'CurTileCount' of '0' on the way down, /// which allows us to diagnose if the number of arguments is too large for /// the current number of 'for' loops. bool TileDepthSatisfied = true; /// Records the kind of the directive that this clause is attached to, which /// allows us to use it in diagnostics. OpenACCDirectiveKind DirectiveKind = OpenACCDirectiveKind::Invalid; } TileInfo; /// The 'cache' var-list requires some additional work to track variable /// references to make sure they are on the 'other' side of a `loop`. This /// structure is used during parse time to track vardecl use while parsing a /// cache var list. struct CacheParseInfo { bool ParsingCacheVarList = false; bool IsInvalidCacheRef = false; } CacheInfo; /// A list of the active reduction clauses, which allows us to check that all /// vars on nested constructs for the same reduction var have the same /// reduction operator. Currently this is enforced against all constructs /// despite the rule being in the 'loop' section. By current reading, this /// should apply to all anyway, but we may need to make this more like the /// 'loop' clause enforcement, where this is 'blocked' by a compute construct. llvm::SmallVector ActiveReductionClauses; // Type to check the 'for' (or range-for) statement for compatibility with the // 'loop' directive. class ForStmtBeginChecker { SemaOpenACC &SemaRef; SourceLocation ForLoc; bool IsInstantiation = false; struct RangeForInfo { const CXXForRangeStmt *Uninstantiated = nullptr; const CXXForRangeStmt *CurrentVersion = nullptr; // GCC 7.x requires this constructor, else the construction of variant // doesn't work correctly. RangeForInfo() : Uninstantiated{nullptr}, CurrentVersion{nullptr} {} RangeForInfo(const CXXForRangeStmt *Uninst, const CXXForRangeStmt *Cur) : Uninstantiated{Uninst}, CurrentVersion{Cur} {} }; struct ForInfo { const Stmt *Init = nullptr; const Stmt *Condition = nullptr; const Stmt *Increment = nullptr; }; struct CheckForInfo { ForInfo Uninst; ForInfo Current; }; std::variant Info; // Prevent us from checking 2x, which can happen with collapse & tile. bool AlreadyChecked = false; void checkRangeFor(); bool checkForInit(const Stmt *InitStmt, const ValueDecl *&InitVar, bool Diag); bool checkForCond(const Stmt *CondStmt, const ValueDecl *InitVar, bool Diag); bool checkForInc(const Stmt *IncStmt, const ValueDecl *InitVar, bool Diag); void checkFor(); public: // Checking for non-instantiation version of a Range-for. ForStmtBeginChecker(SemaOpenACC &SemaRef, SourceLocation ForLoc, const CXXForRangeStmt *RangeFor) : SemaRef(SemaRef), ForLoc(ForLoc), IsInstantiation(false), Info(RangeForInfo{nullptr, RangeFor}) {} // Checking for an instantiation of the range-for. ForStmtBeginChecker(SemaOpenACC &SemaRef, SourceLocation ForLoc, const CXXForRangeStmt *OldRangeFor, const CXXForRangeStmt *RangeFor) : SemaRef(SemaRef), ForLoc(ForLoc), IsInstantiation(true), Info(RangeForInfo{OldRangeFor, RangeFor}) {} // Checking for a non-instantiation version of a traditional for. ForStmtBeginChecker(SemaOpenACC &SemaRef, SourceLocation ForLoc, const Stmt *Init, const Stmt *Cond, const Stmt *Inc) : SemaRef(SemaRef), ForLoc(ForLoc), IsInstantiation(false), Info(CheckForInfo{{}, {Init, Cond, Inc}}) {} // Checking for an instantiation version of a traditional for. ForStmtBeginChecker(SemaOpenACC &SemaRef, SourceLocation ForLoc, const Stmt *OldInit, const Stmt *OldCond, const Stmt *OldInc, const Stmt *Init, const Stmt *Cond, const Stmt *Inc) : SemaRef(SemaRef), ForLoc(ForLoc), IsInstantiation(true), Info(CheckForInfo{{OldInit, OldCond, OldInc}, {Init, Cond, Inc}}) {} void check(); }; /// Helper function for checking the 'for' and 'range for' stmts. void ForStmtBeginHelper(SourceLocation ForLoc, ForStmtBeginChecker &C); // The 'declare' construct requires only a single reference among ALL declare // directives in a context. We store existing references to check. Because the // rules prevent referencing the same variable from multiple declaration // contexts, we can just store the declaration and location of the reference. llvm::DenseMap DeclareVarReferences; // The 'routine' construct disallows magic-statics in a function referred to // by a 'routine' directive. So record any of these that we see so we can // check them later. llvm::SmallDenseMap MagicStaticLocs; OpenACCRoutineDecl *LastRoutineDecl = nullptr; void CheckLastRoutineDeclNameConflict(const NamedDecl *ND); bool DiagnoseRequiredClauses(OpenACCDirectiveKind DK, SourceLocation DirLoc, ArrayRef Clauses); bool DiagnoseAllowedClauses(OpenACCDirectiveKind DK, OpenACCClauseKind CK, SourceLocation ClauseLoc); bool CreateReductionCombinerRecipe( SourceLocation loc, OpenACCReductionOperator ReductionOperator, QualType VarTy, llvm::SmallVectorImpl &CombinerRecipes); public: // Needed from the visitor, so should be public. bool DiagnoseAllowedOnceClauses(OpenACCDirectiveKind DK, OpenACCClauseKind CK, SourceLocation ClauseLoc, ArrayRef Clauses); bool DiagnoseExclusiveClauses(OpenACCDirectiveKind DK, OpenACCClauseKind CK, SourceLocation ClauseLoc, ArrayRef Clauses); OpenACCPrivateRecipe CreatePrivateInitRecipe(const Expr *VarExpr); OpenACCFirstPrivateRecipe CreateFirstPrivateInitRecipe(const Expr *VarExpr); OpenACCReductionRecipeWithStorage CreateReductionInitRecipe(OpenACCReductionOperator ReductionOperator, const Expr *VarExpr); public: ComputeConstructInfo &getActiveComputeConstructInfo() { return ActiveComputeConstructInfo; } /// If there is a current 'active' loop construct with a 'gang' clause on a /// 'kernel' construct, this will have the source location for it, and the /// 'kernel kind'. This permits us to implement the restriction of no further /// 'gang' clauses. struct LoopGangOnKernelTy { SourceLocation Loc; OpenACCDirectiveKind DirKind = OpenACCDirectiveKind::Invalid; } LoopGangClauseOnKernel; /// If there is a current 'active' loop construct with a 'worker' clause on it /// (on any sort of construct), this has the source location for it. This /// permits us to implement the restriction of no further 'gang' or 'worker' /// clauses. SourceLocation LoopWorkerClauseLoc; /// If there is a current 'active' loop construct with a 'vector' clause on it /// (on any sort of construct), this has the source location for it. This /// permits us to implement the restriction of no further 'gang', 'vector', or /// 'worker' clauses. SourceLocation LoopVectorClauseLoc; /// If there is a current 'active' loop construct that does NOT have a 'seq' /// clause on it, this has that source location and loop Directive 'kind'. /// This permits us to implement the 'loop' restrictions on the loop variable. /// This can be extended via 'collapse', so we need to keep this around for a /// while. struct LoopWithoutSeqCheckingInfo { OpenACCDirectiveKind Kind = OpenACCDirectiveKind::Invalid; SourceLocation Loc; } LoopWithoutSeqInfo; // Redeclaration of the version in OpenACCClause.h. using DeviceTypeArgument = IdentifierLoc; /// A type to represent all the data for an OpenACC Clause that has been /// parsed, but not yet created/semantically analyzed. This is effectively a /// discriminated union on the 'Clause Kind', with all of the individual /// clause details stored in a std::variant. class OpenACCParsedClause { OpenACCDirectiveKind DirKind; OpenACCClauseKind ClauseKind; SourceRange ClauseRange; SourceLocation LParenLoc; struct DefaultDetails { OpenACCDefaultClauseKind DefaultClauseKind; }; struct ConditionDetails { Expr *ConditionExpr; }; struct IntExprDetails { SmallVector IntExprs; }; struct VarListDetails { SmallVector VarList; OpenACCModifierKind ModifierKind; }; struct WaitDetails { Expr *DevNumExpr; SourceLocation QueuesLoc; SmallVector QueueIdExprs; }; struct DeviceTypeDetails { SmallVector Archs; }; struct ReductionDetails { OpenACCReductionOperator Op; SmallVector VarList; }; struct CollapseDetails { bool IsForce; Expr *LoopCount; }; struct GangDetails { SmallVector GangKinds; SmallVector IntExprs; }; struct BindDetails { std::variant Argument; }; std::variant Details = std::monostate{}; public: OpenACCParsedClause(OpenACCDirectiveKind DirKind, OpenACCClauseKind ClauseKind, SourceLocation BeginLoc) : DirKind(DirKind), ClauseKind(ClauseKind), ClauseRange(BeginLoc, {}) {} OpenACCDirectiveKind getDirectiveKind() const { return DirKind; } OpenACCClauseKind getClauseKind() const { return ClauseKind; } SourceLocation getBeginLoc() const { return ClauseRange.getBegin(); } SourceLocation getLParenLoc() const { return LParenLoc; } SourceLocation getEndLoc() const { return ClauseRange.getEnd(); } OpenACCDefaultClauseKind getDefaultClauseKind() const { assert(ClauseKind == OpenACCClauseKind::Default && "Parsed clause is not a default clause"); return std::get(Details).DefaultClauseKind; } const Expr *getConditionExpr() const { return const_cast(this)->getConditionExpr(); } Expr *getConditionExpr() { assert((ClauseKind == OpenACCClauseKind::If || (ClauseKind == OpenACCClauseKind::Self && DirKind != OpenACCDirectiveKind::Update)) && "Parsed clause kind does not have a condition expr"); // 'self' has an optional ConditionExpr, so be tolerant of that. This will // assert in variant otherwise. if (ClauseKind == OpenACCClauseKind::Self && std::holds_alternative(Details)) return nullptr; return std::get(Details).ConditionExpr; } unsigned getNumIntExprs() const { assert((ClauseKind == OpenACCClauseKind::NumGangs || ClauseKind == OpenACCClauseKind::NumWorkers || ClauseKind == OpenACCClauseKind::Async || ClauseKind == OpenACCClauseKind::DeviceNum || ClauseKind == OpenACCClauseKind::DefaultAsync || ClauseKind == OpenACCClauseKind::Tile || ClauseKind == OpenACCClauseKind::Worker || ClauseKind == OpenACCClauseKind::Vector || ClauseKind == OpenACCClauseKind::VectorLength) && "Parsed clause kind does not have a int exprs"); // 'async', 'worker', 'vector', and 'wait' have an optional IntExpr, so be // tolerant of that. if ((ClauseKind == OpenACCClauseKind::Async || ClauseKind == OpenACCClauseKind::Worker || ClauseKind == OpenACCClauseKind::Vector || ClauseKind == OpenACCClauseKind::Wait) && std::holds_alternative(Details)) return 0; return std::get(Details).IntExprs.size(); } SourceLocation getQueuesLoc() const { assert(ClauseKind == OpenACCClauseKind::Wait && "Parsed clause kind does not have a queues location"); if (std::holds_alternative(Details)) return SourceLocation{}; return std::get(Details).QueuesLoc; } Expr *getDevNumExpr() const { assert(ClauseKind == OpenACCClauseKind::Wait && "Parsed clause kind does not have a device number expr"); if (std::holds_alternative(Details)) return nullptr; return std::get(Details).DevNumExpr; } ArrayRef getQueueIdExprs() const { assert(ClauseKind == OpenACCClauseKind::Wait && "Parsed clause kind does not have a queue id expr list"); if (std::holds_alternative(Details)) return ArrayRef(); return std::get(Details).QueueIdExprs; } ArrayRef getIntExprs() { assert((ClauseKind == OpenACCClauseKind::NumGangs || ClauseKind == OpenACCClauseKind::NumWorkers || ClauseKind == OpenACCClauseKind::Async || ClauseKind == OpenACCClauseKind::DeviceNum || ClauseKind == OpenACCClauseKind::DefaultAsync || ClauseKind == OpenACCClauseKind::Tile || ClauseKind == OpenACCClauseKind::Gang || ClauseKind == OpenACCClauseKind::Worker || ClauseKind == OpenACCClauseKind::Vector || ClauseKind == OpenACCClauseKind::VectorLength) && "Parsed clause kind does not have a int exprs"); if (ClauseKind == OpenACCClauseKind::Gang) { // There might not be any gang int exprs, as this is an optional // argument. if (std::holds_alternative(Details)) return {}; return std::get(Details).IntExprs; } return std::get(Details).IntExprs; } ArrayRef getIntExprs() const { return const_cast(this)->getIntExprs(); } OpenACCReductionOperator getReductionOp() const { return std::get(Details).Op; } ArrayRef getGangKinds() const { assert(ClauseKind == OpenACCClauseKind::Gang && "Parsed clause kind does not have gang kind"); // The args on gang are optional, so this might not actually hold // anything. if (std::holds_alternative(Details)) return {}; return std::get(Details).GangKinds; } ArrayRef getVarList() { assert((ClauseKind == OpenACCClauseKind::Private || ClauseKind == OpenACCClauseKind::NoCreate || ClauseKind == OpenACCClauseKind::Present || ClauseKind == OpenACCClauseKind::Copy || ClauseKind == OpenACCClauseKind::PCopy || ClauseKind == OpenACCClauseKind::PresentOrCopy || ClauseKind == OpenACCClauseKind::CopyIn || ClauseKind == OpenACCClauseKind::PCopyIn || ClauseKind == OpenACCClauseKind::PresentOrCopyIn || ClauseKind == OpenACCClauseKind::CopyOut || ClauseKind == OpenACCClauseKind::PCopyOut || ClauseKind == OpenACCClauseKind::PresentOrCopyOut || ClauseKind == OpenACCClauseKind::Create || ClauseKind == OpenACCClauseKind::PCreate || ClauseKind == OpenACCClauseKind::PresentOrCreate || ClauseKind == OpenACCClauseKind::Attach || ClauseKind == OpenACCClauseKind::Delete || ClauseKind == OpenACCClauseKind::UseDevice || ClauseKind == OpenACCClauseKind::Detach || ClauseKind == OpenACCClauseKind::DevicePtr || ClauseKind == OpenACCClauseKind::Reduction || ClauseKind == OpenACCClauseKind::Host || ClauseKind == OpenACCClauseKind::Device || ClauseKind == OpenACCClauseKind::DeviceResident || ClauseKind == OpenACCClauseKind::Link || (ClauseKind == OpenACCClauseKind::Self && DirKind == OpenACCDirectiveKind::Update) || ClauseKind == OpenACCClauseKind::FirstPrivate) && "Parsed clause kind does not have a var-list"); if (ClauseKind == OpenACCClauseKind::Reduction) return std::get(Details).VarList; return std::get(Details).VarList; } ArrayRef getVarList() const { return const_cast(this)->getVarList(); } OpenACCModifierKind getModifierList() const { return std::get(Details).ModifierKind; } bool isForce() const { assert(ClauseKind == OpenACCClauseKind::Collapse && "Only 'collapse' has a force tag"); return std::get(Details).IsForce; } Expr *getLoopCount() const { assert(ClauseKind == OpenACCClauseKind::Collapse && "Only 'collapse' has a loop count"); return std::get(Details).LoopCount; } ArrayRef getDeviceTypeArchitectures() const { assert((ClauseKind == OpenACCClauseKind::DeviceType || ClauseKind == OpenACCClauseKind::DType) && "Only 'device_type'/'dtype' has a device-type-arg list"); return std::get(Details).Archs; } std::variant getBindDetails() const { assert(ClauseKind == OpenACCClauseKind::Bind && "Only 'bind' has bind details"); return std::get(Details).Argument; } void setLParenLoc(SourceLocation EndLoc) { LParenLoc = EndLoc; } void setEndLoc(SourceLocation EndLoc) { ClauseRange.setEnd(EndLoc); } void setDefaultDetails(OpenACCDefaultClauseKind DefKind) { assert(ClauseKind == OpenACCClauseKind::Default && "Parsed clause is not a default clause"); Details = DefaultDetails{DefKind}; } void setConditionDetails(Expr *ConditionExpr) { assert((ClauseKind == OpenACCClauseKind::If || (ClauseKind == OpenACCClauseKind::Self && DirKind != OpenACCDirectiveKind::Update)) && "Parsed clause kind does not have a condition expr"); // In C++ we can count on this being a 'bool', but in C this gets left as // some sort of scalar that codegen will have to take care of converting. assert((!ConditionExpr || ConditionExpr->isInstantiationDependent() || ConditionExpr->getType()->isScalarType()) && "Condition expression type not scalar/dependent"); Details = ConditionDetails{ConditionExpr}; } void setIntExprDetails(ArrayRef IntExprs) { assert((ClauseKind == OpenACCClauseKind::NumGangs || ClauseKind == OpenACCClauseKind::NumWorkers || ClauseKind == OpenACCClauseKind::Async || ClauseKind == OpenACCClauseKind::DeviceNum || ClauseKind == OpenACCClauseKind::DefaultAsync || ClauseKind == OpenACCClauseKind::Tile || ClauseKind == OpenACCClauseKind::Worker || ClauseKind == OpenACCClauseKind::Vector || ClauseKind == OpenACCClauseKind::VectorLength) && "Parsed clause kind does not have a int exprs"); Details = IntExprDetails{{IntExprs.begin(), IntExprs.end()}}; } void setIntExprDetails(llvm::SmallVector &&IntExprs) { assert((ClauseKind == OpenACCClauseKind::NumGangs || ClauseKind == OpenACCClauseKind::NumWorkers || ClauseKind == OpenACCClauseKind::Async || ClauseKind == OpenACCClauseKind::DeviceNum || ClauseKind == OpenACCClauseKind::DefaultAsync || ClauseKind == OpenACCClauseKind::Tile || ClauseKind == OpenACCClauseKind::Worker || ClauseKind == OpenACCClauseKind::Vector || ClauseKind == OpenACCClauseKind::VectorLength) && "Parsed clause kind does not have a int exprs"); Details = IntExprDetails{std::move(IntExprs)}; } void setGangDetails(ArrayRef GKs, ArrayRef IntExprs) { assert(ClauseKind == OpenACCClauseKind::Gang && "Parsed Clause kind does not have gang details"); assert(GKs.size() == IntExprs.size() && "Mismatched kind/size?"); Details = GangDetails{{GKs.begin(), GKs.end()}, {IntExprs.begin(), IntExprs.end()}}; } void setGangDetails(llvm::SmallVector &&GKs, llvm::SmallVector &&IntExprs) { assert(ClauseKind == OpenACCClauseKind::Gang && "Parsed Clause kind does not have gang details"); assert(GKs.size() == IntExprs.size() && "Mismatched kind/size?"); Details = GangDetails{std::move(GKs), std::move(IntExprs)}; } void setVarListDetails(ArrayRef VarList, OpenACCModifierKind ModKind) { assert((ClauseKind == OpenACCClauseKind::Private || ClauseKind == OpenACCClauseKind::NoCreate || ClauseKind == OpenACCClauseKind::Present || ClauseKind == OpenACCClauseKind::Copy || ClauseKind == OpenACCClauseKind::PCopy || ClauseKind == OpenACCClauseKind::PresentOrCopy || ClauseKind == OpenACCClauseKind::CopyIn || ClauseKind == OpenACCClauseKind::PCopyIn || ClauseKind == OpenACCClauseKind::PresentOrCopyIn || ClauseKind == OpenACCClauseKind::CopyOut || ClauseKind == OpenACCClauseKind::PCopyOut || ClauseKind == OpenACCClauseKind::PresentOrCopyOut || ClauseKind == OpenACCClauseKind::Create || ClauseKind == OpenACCClauseKind::PCreate || ClauseKind == OpenACCClauseKind::PresentOrCreate || ClauseKind == OpenACCClauseKind::Attach || ClauseKind == OpenACCClauseKind::Delete || ClauseKind == OpenACCClauseKind::UseDevice || ClauseKind == OpenACCClauseKind::Detach || ClauseKind == OpenACCClauseKind::DevicePtr || ClauseKind == OpenACCClauseKind::Host || ClauseKind == OpenACCClauseKind::Device || ClauseKind == OpenACCClauseKind::DeviceResident || ClauseKind == OpenACCClauseKind::Link || (ClauseKind == OpenACCClauseKind::Self && DirKind == OpenACCDirectiveKind::Update) || ClauseKind == OpenACCClauseKind::FirstPrivate) && "Parsed clause kind does not have a var-list"); assert((ModKind == OpenACCModifierKind::Invalid || ClauseKind == OpenACCClauseKind::Copy || ClauseKind == OpenACCClauseKind::PCopy || ClauseKind == OpenACCClauseKind::PresentOrCopy || ClauseKind == OpenACCClauseKind::CopyIn || ClauseKind == OpenACCClauseKind::PCopyIn || ClauseKind == OpenACCClauseKind::PresentOrCopyIn || ClauseKind == OpenACCClauseKind::CopyOut || ClauseKind == OpenACCClauseKind::PCopyOut || ClauseKind == OpenACCClauseKind::PresentOrCopyOut || ClauseKind == OpenACCClauseKind::Create || ClauseKind == OpenACCClauseKind::PCreate || ClauseKind == OpenACCClauseKind::PresentOrCreate) && "Modifier Kind only valid on copy, copyin, copyout, create"); Details = VarListDetails{{VarList.begin(), VarList.end()}, ModKind}; } void setVarListDetails(llvm::SmallVector &&VarList, OpenACCModifierKind ModKind) { assert((ClauseKind == OpenACCClauseKind::Private || ClauseKind == OpenACCClauseKind::NoCreate || ClauseKind == OpenACCClauseKind::Present || ClauseKind == OpenACCClauseKind::Copy || ClauseKind == OpenACCClauseKind::PCopy || ClauseKind == OpenACCClauseKind::PresentOrCopy || ClauseKind == OpenACCClauseKind::CopyIn || ClauseKind == OpenACCClauseKind::PCopyIn || ClauseKind == OpenACCClauseKind::PresentOrCopyIn || ClauseKind == OpenACCClauseKind::CopyOut || ClauseKind == OpenACCClauseKind::PCopyOut || ClauseKind == OpenACCClauseKind::PresentOrCopyOut || ClauseKind == OpenACCClauseKind::Create || ClauseKind == OpenACCClauseKind::PCreate || ClauseKind == OpenACCClauseKind::PresentOrCreate || ClauseKind == OpenACCClauseKind::Attach || ClauseKind == OpenACCClauseKind::Delete || ClauseKind == OpenACCClauseKind::UseDevice || ClauseKind == OpenACCClauseKind::Detach || ClauseKind == OpenACCClauseKind::DevicePtr || ClauseKind == OpenACCClauseKind::Host || ClauseKind == OpenACCClauseKind::Device || ClauseKind == OpenACCClauseKind::DeviceResident || ClauseKind == OpenACCClauseKind::Link || (ClauseKind == OpenACCClauseKind::Self && DirKind == OpenACCDirectiveKind::Update) || ClauseKind == OpenACCClauseKind::FirstPrivate) && "Parsed clause kind does not have a var-list"); assert((ModKind == OpenACCModifierKind::Invalid || ClauseKind == OpenACCClauseKind::Copy || ClauseKind == OpenACCClauseKind::PCopy || ClauseKind == OpenACCClauseKind::PresentOrCopy || ClauseKind == OpenACCClauseKind::CopyIn || ClauseKind == OpenACCClauseKind::PCopyIn || ClauseKind == OpenACCClauseKind::PresentOrCopyIn || ClauseKind == OpenACCClauseKind::CopyOut || ClauseKind == OpenACCClauseKind::PCopyOut || ClauseKind == OpenACCClauseKind::PresentOrCopyOut || ClauseKind == OpenACCClauseKind::Create || ClauseKind == OpenACCClauseKind::PCreate || ClauseKind == OpenACCClauseKind::PresentOrCreate) && "Modifier Kind only valid on copy, copyin, copyout, create"); Details = VarListDetails{std::move(VarList), ModKind}; } void setReductionDetails(OpenACCReductionOperator Op, llvm::SmallVector &&VarList) { assert(ClauseKind == OpenACCClauseKind::Reduction && "reduction details only valid on reduction"); Details = ReductionDetails{Op, std::move(VarList)}; } void setWaitDetails(Expr *DevNum, SourceLocation QueuesLoc, llvm::SmallVector &&IntExprs) { assert(ClauseKind == OpenACCClauseKind::Wait && "Parsed clause kind does not have a wait-details"); Details = WaitDetails{DevNum, QueuesLoc, std::move(IntExprs)}; } void setDeviceTypeDetails(llvm::SmallVector &&Archs) { assert((ClauseKind == OpenACCClauseKind::DeviceType || ClauseKind == OpenACCClauseKind::DType) && "Only 'device_type'/'dtype' has a device-type-arg list"); Details = DeviceTypeDetails{std::move(Archs)}; } void setCollapseDetails(bool IsForce, Expr *LoopCount) { assert(ClauseKind == OpenACCClauseKind::Collapse && "Only 'collapse' has collapse details"); Details = CollapseDetails{IsForce, LoopCount}; } void setBindDetails( std::variant Arg) { assert(ClauseKind == OpenACCClauseKind::Bind && "Only 'bind' has bind details"); Details = BindDetails{Arg}; } }; SemaOpenACC(Sema &S); void ActOnEndOfTranslationUnit(TranslationUnitDecl *TU); // Called when we encounter a 'while' statement, before looking at its 'body'. void ActOnWhileStmt(SourceLocation WhileLoc); // Called when we encounter a 'do' statement, before looking at its 'body'. void ActOnDoStmt(SourceLocation DoLoc); // Called when we encounter a 'for' statement, before looking at its 'body', // for the 'range-for'. 'ActOnForStmtEnd' is used after the body. void ActOnRangeForStmtBegin(SourceLocation ForLoc, const Stmt *OldRangeFor, const Stmt *RangeFor); void ActOnRangeForStmtBegin(SourceLocation ForLoc, const Stmt *RangeFor); // Called when we encounter a 'for' statement, before looking at its 'body'. // 'ActOnForStmtEnd' is used after the body. void ActOnForStmtBegin(SourceLocation ForLoc, const Stmt *First, const Stmt *Second, const Stmt *Third); void ActOnForStmtBegin(SourceLocation ForLoc, const Stmt *OldFirst, const Stmt *First, const Stmt *OldSecond, const Stmt *Second, const Stmt *OldThird, const Stmt *Third); // Called when we encounter a 'for' statement, after we've consumed/checked // the body. This is necessary for a number of checks on the contents of the // 'for' statement. void ActOnForStmtEnd(SourceLocation ForLoc, StmtResult Body); /// Called after parsing an OpenACC Clause so that it can be checked. OpenACCClause *ActOnClause(ArrayRef ExistingClauses, OpenACCParsedClause &Clause); /// Called after the construct has been parsed, but clauses haven't been /// parsed. This allows us to diagnose not-implemented, as well as set up any /// state required for parsing the clauses. void ActOnConstruct(OpenACCDirectiveKind K, SourceLocation DirLoc); /// Called after the directive, including its clauses, have been parsed and /// parsing has consumed the 'annot_pragma_openacc_end' token. This DOES /// happen before any associated declarations or statements have been parsed. /// This function is only called when we are parsing a 'statement' context. bool ActOnStartStmtDirective(OpenACCDirectiveKind K, SourceLocation StartLoc, ArrayRef Clauses); /// Called after the directive, including its clauses, have been parsed and /// parsing has consumed the 'annot_pragma_openacc_end' token. This DOES /// happen before any associated declarations or statements have been parsed. /// This function is only called when we are parsing a 'Decl' context. bool ActOnStartDeclDirective(OpenACCDirectiveKind K, SourceLocation StartLoc, ArrayRef Clauses); /// Called when we encounter an associated statement for our construct, this /// should check legality of the statement as it appertains to this Construct. StmtResult ActOnAssociatedStmt(SourceLocation DirectiveLoc, OpenACCDirectiveKind K, OpenACCAtomicKind AtKind, ArrayRef Clauses, StmtResult AssocStmt); StmtResult ActOnAssociatedStmt(SourceLocation DirectiveLoc, OpenACCDirectiveKind K, ArrayRef Clauses, StmtResult AssocStmt) { return ActOnAssociatedStmt(DirectiveLoc, K, OpenACCAtomicKind::None, Clauses, AssocStmt); } /// Called to check the form of the `atomic` construct which has some fairly /// sizable restrictions. StmtResult CheckAtomicAssociatedStmt(SourceLocation AtomicDirLoc, OpenACCAtomicKind AtKind, StmtResult AssocStmt); /// Called after the directive has been completely parsed, including the /// declaration group or associated statement. /// DirLoc: Location of the actual directive keyword. /// LParenLoc: Location of the left paren, if it exists (not on all /// constructs). /// MiscLoc: First misc location, if necessary (not all constructs). /// Exprs: List of expressions on the construct itself, if necessary (not all /// constructs). /// FuncRef: used only for Routine, this is the function being referenced. /// AK: The atomic kind of the directive, if necessary (atomic only) /// RParenLoc: Location of the right paren, if it exists (not on all /// constructs). /// EndLoc: The last source location of the driective. /// Clauses: The list of clauses for the directive, if present. /// AssocStmt: The associated statement for this construct, if necessary. StmtResult ActOnEndStmtDirective( OpenACCDirectiveKind K, SourceLocation StartLoc, SourceLocation DirLoc, SourceLocation LParenLoc, SourceLocation MiscLoc, ArrayRef Exprs, OpenACCAtomicKind AK, SourceLocation RParenLoc, SourceLocation EndLoc, ArrayRef Clauses, StmtResult AssocStmt); /// Called after the directive has been completely parsed, including the /// declaration group or associated statement. DeclGroupRef ActOnEndDeclDirective(OpenACCDirectiveKind K, SourceLocation StartLoc, SourceLocation DirLoc, SourceLocation LParenLoc, SourceLocation RParenLoc, SourceLocation EndLoc, ArrayRef Clauses); // Helper functions for ActOnEndRoutine*Directive, which does all the checking // given the proper list of declarations. void CheckRoutineDecl(SourceLocation DirLoc, ArrayRef Clauses, Decl *NextParsedDecl); OpenACCRoutineDecl *CheckRoutineDecl(SourceLocation StartLoc, SourceLocation DirLoc, SourceLocation LParenLoc, Expr *FuncRef, SourceLocation RParenLoc, ArrayRef Clauses, SourceLocation EndLoc); OpenACCRoutineDeclAttr * mergeRoutineDeclAttr(const OpenACCRoutineDeclAttr &Old); DeclGroupRef ActOnEndRoutineDeclDirective(SourceLocation StartLoc, SourceLocation DirLoc, SourceLocation LParenLoc, Expr *ReferencedFunc, SourceLocation RParenLoc, ArrayRef Clauses, SourceLocation EndLoc, DeclGroupPtrTy NextDecl); StmtResult ActOnEndRoutineStmtDirective(SourceLocation StartLoc, SourceLocation DirLoc, SourceLocation LParenLoc, Expr *ReferencedFunc, SourceLocation RParenLoc, ArrayRef Clauses, SourceLocation EndLoc, Stmt *NextStmt); /// Called when encountering an 'int-expr' for OpenACC, and manages /// conversions and diagnostics to 'int'. ExprResult ActOnIntExpr(OpenACCDirectiveKind DK, OpenACCClauseKind CK, SourceLocation Loc, Expr *IntExpr); /// Called right before a 'var' is parsed, so we can set the state for parsing /// a 'cache' var. void ActOnStartParseVar(OpenACCDirectiveKind DK, OpenACCClauseKind CK); /// Called only if the parse of a 'var' was invalid, else 'ActOnVar' should be /// called. void ActOnInvalidParseVar(); /// Called when encountering a 'var' for OpenACC, ensures it is actually a /// declaration reference to a variable of the correct type. ExprResult ActOnVar(OpenACCDirectiveKind DK, OpenACCClauseKind CK, Expr *VarExpr); /// Helper function called by ActonVar that is used to check a 'cache' var. ExprResult ActOnCacheVar(Expr *VarExpr); /// Function called when a variable declarator is created, which lets us /// implement the 'routine' 'function static variables' restriction. void ActOnVariableDeclarator(VarDecl *VD); /// Called when a function decl is created, which lets us implement the /// 'routine' 'doesn't match next thing' warning. void ActOnFunctionDeclarator(FunctionDecl *FD); /// Called when a variable is initialized, so we can implement the 'routine /// 'doesn't match the next thing' warning for lambda init. void ActOnVariableInit(VarDecl *VD, QualType InitType); // Called after 'ActOnVar' specifically for a 'link' clause, which has to do // some minor additional checks. llvm::SmallVector CheckLinkClauseVarList(ArrayRef VarExpr); // Checking for the arguments specific to the declare-clause that need to be // checked during both phases of template translation. bool CheckDeclareClause(SemaOpenACC::OpenACCParsedClause &Clause, OpenACCModifierKind Mods); ExprResult ActOnRoutineName(Expr *RoutineName); /// Called while semantically analyzing the reduction clause, ensuring the var /// is the correct kind of reference. ExprResult CheckReductionVar(OpenACCDirectiveKind DirectiveKind, OpenACCReductionOperator ReductionOp, Expr *VarExpr); bool CheckReductionVarType(Expr *VarExpr); /// Called to check the 'var' type is a variable of pointer type, necessary /// for 'deviceptr' and 'attach' clauses. Returns true on success. bool CheckVarIsPointerType(OpenACCClauseKind ClauseKind, Expr *VarExpr); /// Checks and creates an Array Section used in an OpenACC construct/clause. ExprResult ActOnArraySectionExpr(Expr *Base, SourceLocation LBLoc, Expr *LowerBound, SourceLocation ColonLocFirst, Expr *Length, SourceLocation RBLoc); /// Checks the loop depth value for a collapse clause. ExprResult CheckCollapseLoopCount(Expr *LoopCount); /// Checks a single size expr for a tile clause. ExprResult CheckTileSizeExpr(Expr *SizeExpr); // Check a single expression on a gang clause. ExprResult CheckGangExpr(ArrayRef ExistingClauses, OpenACCDirectiveKind DK, OpenACCGangKind GK, Expr *E); // Called when a declaration is referenced, so that we can make sure certain // clauses don't do the 'wrong' thing/have incorrect references. void CheckDeclReference(SourceLocation Loc, Expr *E, Decl *D); // Does the checking for a 'gang' clause that needs to be done in dependent // and not dependent cases. OpenACCClause * CheckGangClause(OpenACCDirectiveKind DirKind, ArrayRef ExistingClauses, SourceLocation BeginLoc, SourceLocation LParenLoc, ArrayRef GangKinds, ArrayRef IntExprs, SourceLocation EndLoc); // Does the checking for a 'reduction ' clause that needs to be done in // dependent and not dependent cases. OpenACCClause * CheckReductionClause(ArrayRef ExistingClauses, OpenACCDirectiveKind DirectiveKind, SourceLocation BeginLoc, SourceLocation LParenLoc, OpenACCReductionOperator ReductionOp, ArrayRef Vars, ArrayRef Recipes, SourceLocation EndLoc); ExprResult BuildOpenACCAsteriskSizeExpr(SourceLocation AsteriskLoc); ExprResult ActOnOpenACCAsteriskSizeExpr(SourceLocation AsteriskLoc); /// Helper type to restore the state of various 'loop' constructs when we run /// into a loop (for, etc) inside the construct. class LoopInConstructRAII { SemaOpenACC &SemaRef; LoopCheckingInfo OldLoopInfo; CollapseCheckingInfo OldCollapseInfo; TileCheckingInfo OldTileInfo; bool PreserveDepth; public: LoopInConstructRAII(SemaOpenACC &SemaRef, bool PreserveDepth = true) : SemaRef(SemaRef), OldLoopInfo(SemaRef.LoopInfo), OldCollapseInfo(SemaRef.CollapseInfo), OldTileInfo(SemaRef.TileInfo), PreserveDepth(PreserveDepth) {} ~LoopInConstructRAII() { // The associated-statement level of this should NOT preserve this, as it // is a new construct, but other loop uses need to preserve the depth so // it makes it to the 'top level' for diagnostics. bool CollapseDepthSatisified = PreserveDepth ? SemaRef.CollapseInfo.CollapseDepthSatisfied : OldCollapseInfo.CollapseDepthSatisfied; bool TileDepthSatisfied = PreserveDepth ? SemaRef.TileInfo.TileDepthSatisfied : OldTileInfo.TileDepthSatisfied; bool CurLevelHasLoopAlready = PreserveDepth ? SemaRef.LoopInfo.CurLevelHasLoopAlready : OldLoopInfo.CurLevelHasLoopAlready; SemaRef.LoopInfo = OldLoopInfo; SemaRef.CollapseInfo = OldCollapseInfo; SemaRef.TileInfo = OldTileInfo; SemaRef.CollapseInfo.CollapseDepthSatisfied = CollapseDepthSatisified; SemaRef.TileInfo.TileDepthSatisfied = TileDepthSatisfied; SemaRef.LoopInfo.CurLevelHasLoopAlready = CurLevelHasLoopAlready; } }; /// Helper type for the registration/assignment of constructs that need to /// 'know' about their parent constructs and hold a reference to them, such as /// Loop needing its parent construct. class AssociatedStmtRAII { SemaOpenACC &SemaRef; ComputeConstructInfo OldActiveComputeConstructInfo; OpenACCDirectiveKind DirKind; LoopGangOnKernelTy OldLoopGangClauseOnKernel; SourceLocation OldLoopWorkerClauseLoc; SourceLocation OldLoopVectorClauseLoc; LoopWithoutSeqCheckingInfo OldLoopWithoutSeqInfo; llvm::SmallVector ActiveReductionClauses; LoopInConstructRAII LoopRAII; public: AssociatedStmtRAII(SemaOpenACC &, OpenACCDirectiveKind, SourceLocation, ArrayRef, ArrayRef); void SetCollapseInfoBeforeAssociatedStmt( ArrayRef UnInstClauses, ArrayRef Clauses); void SetTileInfoBeforeAssociatedStmt( ArrayRef UnInstClauses, ArrayRef Clauses); ~AssociatedStmtRAII(); }; }; } // namespace clang #endif // LLVM_CLANG_SEMA_SEMAOPENACC_H