From 4525d78c65a76d70e92c1f1b5defe6200c9112fa Mon Sep 17 00:00:00 2001 From: erichkeane Date: Wed, 3 Sep 2025 15:14:31 -0700 Subject: [PATCH] [OpenACC][NFCI] Split up the init and decl from OpenACC recipes Expressions/references with 'bounds' are going to need to do initialization significantly differently, so we need to have the initializer and the declaration 'separate' in the future. This patch splits the AST node into two, and normalizes them a bit. Additionally, since this required significant work on the recipe generation, this patch also does a bit of a refactor to improve readability and future expansion, now that we have a good understanding of how these are going to look. --- clang/include/clang/AST/OpenACCClause.h | 72 +++- clang/include/clang/Sema/SemaOpenACC.h | 21 +- clang/lib/AST/OpenACCClause.cpp | 6 +- clang/lib/AST/StmtProfile.cpp | 17 +- clang/lib/CIR/CodeGen/CIRGenOpenACCClause.cpp | 27 +- clang/lib/Sema/SemaOpenACC.cpp | 381 +++++++++--------- clang/lib/Sema/SemaOpenACCClause.cpp | 16 +- clang/lib/Sema/TreeTransform.h | 37 +- clang/lib/Serialization/ASTReader.cpp | 19 +- clang/lib/Serialization/ASTWriter.cpp | 16 +- clang/tools/libclang/CIndex.cpp | 13 +- 11 files changed, 338 insertions(+), 287 deletions(-) diff --git a/clang/include/clang/AST/OpenACCClause.h b/clang/include/clang/AST/OpenACCClause.h index 2f4aba1cdcd90..081244fe0efb6 100644 --- a/clang/include/clang/AST/OpenACCClause.h +++ b/clang/include/clang/AST/OpenACCClause.h @@ -835,19 +835,40 @@ class OpenACCClauseWithVarList : public OpenACCClauseWithExprs { ArrayRef getVarList() const { return getExprs(); } }; +// Represents all the data needed for recipe generation. The declaration and +// init are stored separately, because in the case of subscripts, we do the +// alloca at the level of the base, and the init at the element level. +struct OpenACCPrivateRecipe { + VarDecl *AllocaDecl; + Expr *InitExpr; + + OpenACCPrivateRecipe(VarDecl *A, Expr *I) : AllocaDecl(A), InitExpr(I) { + assert(!AllocaDecl || AllocaDecl->getInit() == nullptr); + } + + bool isSet() const { return AllocaDecl; } + + static OpenACCPrivateRecipe Empty() { + return OpenACCPrivateRecipe(nullptr, nullptr); + } +}; + class OpenACCPrivateClause final : public OpenACCClauseWithVarList, - private llvm::TrailingObjects { + private llvm::TrailingObjects { friend TrailingObjects; OpenACCPrivateClause(SourceLocation BeginLoc, SourceLocation LParenLoc, ArrayRef VarList, - ArrayRef InitRecipes, SourceLocation EndLoc) + ArrayRef InitRecipes, + SourceLocation EndLoc) : OpenACCClauseWithVarList(OpenACCClauseKind::Private, BeginLoc, LParenLoc, EndLoc) { assert(VarList.size() == InitRecipes.size()); setExprs(getTrailingObjects(VarList.size()), VarList); - llvm::uninitialized_copy(InitRecipes, getTrailingObjects()); + llvm::uninitialized_copy(InitRecipes, + getTrailingObjects()); } public: @@ -856,19 +877,19 @@ class OpenACCPrivateClause final } // Gets a list of 'made up' `VarDecl` objects that can be used by codegen to // ensure that we properly initialize each of these variables. - ArrayRef getInitRecipes() { - return ArrayRef{getTrailingObjects(), - getExprs().size()}; + ArrayRef getInitRecipes() { + return ArrayRef{ + getTrailingObjects(), getExprs().size()}; } - ArrayRef getInitRecipes() const { - return ArrayRef{getTrailingObjects(), - getExprs().size()}; + ArrayRef getInitRecipes() const { + return ArrayRef{ + getTrailingObjects(), getExprs().size()}; } static OpenACCPrivateClause * Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc, - ArrayRef VarList, ArrayRef InitRecipes, + ArrayRef VarList, ArrayRef InitRecipes, SourceLocation EndLoc); size_t numTrailingObjects(OverloadToken) const { @@ -879,11 +900,20 @@ class OpenACCPrivateClause final // A 'pair' to stand in for the recipe. RecipeDecl is the main declaration, and // InitFromTemporary is the 'temp' declaration we put in to be 'copied from'. struct OpenACCFirstPrivateRecipe { - VarDecl *RecipeDecl, *InitFromTemporary; - OpenACCFirstPrivateRecipe(VarDecl *R, VarDecl *T) - : RecipeDecl(R), InitFromTemporary(T) {} - OpenACCFirstPrivateRecipe(std::pair p) - : RecipeDecl(p.first), InitFromTemporary(p.second) {} + VarDecl *AllocaDecl; + Expr *InitExpr; + VarDecl *InitFromTemporary; + OpenACCFirstPrivateRecipe(VarDecl *A, Expr *I, VarDecl *T) + : AllocaDecl(A), InitExpr(I), InitFromTemporary(T) { + assert(!AllocaDecl || AllocaDecl->getInit() == nullptr); + assert(!InitFromTemporary || InitFromTemporary->getInit() == nullptr); + } + + bool isSet() const { return AllocaDecl; } + + static OpenACCFirstPrivateRecipe Empty() { + return OpenACCFirstPrivateRecipe(nullptr, nullptr, nullptr); + } }; class OpenACCFirstPrivateClause final @@ -1253,8 +1283,18 @@ class OpenACCCreateClause final // A structure to stand in for the recipe on a reduction. RecipeDecl is the // 'main' declaration used for initializaiton, which is fixed. struct OpenACCReductionRecipe { - VarDecl *RecipeDecl; + VarDecl *AllocaDecl; + Expr *InitExpr; // TODO: OpenACC: this should eventually have the operations here too. + + OpenACCReductionRecipe(VarDecl *A, Expr *I) : AllocaDecl(A), InitExpr(I) { + assert(!AllocaDecl || AllocaDecl->getInit() == nullptr); + } + + bool isSet() const { return AllocaDecl; } + static OpenACCReductionRecipe Empty() { + return OpenACCReductionRecipe(nullptr, nullptr); + } }; class OpenACCReductionClause final diff --git a/clang/include/clang/Sema/SemaOpenACC.h b/clang/include/clang/Sema/SemaOpenACC.h index 42e86582c3b06..09fdf75fbbd09 100644 --- a/clang/include/clang/Sema/SemaOpenACC.h +++ b/clang/include/clang/Sema/SemaOpenACC.h @@ -15,6 +15,7 @@ #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" @@ -237,21 +238,11 @@ class SemaOpenACC : public SemaBase { SourceLocation ClauseLoc, ArrayRef Clauses); - // Creates a VarDecl with a proper default init for the purposes of a - // `private`/'firstprivate'/'reduction' clause, so it can be used to generate - // a recipe later. - // The first entry is the recipe itself, the second is any required - // 'temporary' created for the init (in the case of a copy), such as with - // firstprivate. - std::pair CreateInitRecipe(OpenACCClauseKind CK, - const Expr *VarExpr) { - assert(CK != OpenACCClauseKind::Reduction); - return CreateInitRecipe(CK, OpenACCReductionOperator::Invalid, VarExpr); - } - std::pair - CreateInitRecipe(OpenACCClauseKind CK, - OpenACCReductionOperator ReductionOperator, - const Expr *VarExpr); + OpenACCPrivateRecipe CreatePrivateInitRecipe(const Expr *VarExpr); + OpenACCFirstPrivateRecipe CreateFirstPrivateInitRecipe(const Expr *VarExpr); + OpenACCReductionRecipe + CreateReductionInitRecipe(OpenACCReductionOperator ReductionOperator, + const Expr *VarExpr); public: ComputeConstructInfo &getActiveComputeConstructInfo() { diff --git a/clang/lib/AST/OpenACCClause.cpp b/clang/lib/AST/OpenACCClause.cpp index 9a9ede467331e..6c4bc7c274eaa 100644 --- a/clang/lib/AST/OpenACCClause.cpp +++ b/clang/lib/AST/OpenACCClause.cpp @@ -317,11 +317,11 @@ OpenACCTileClause *OpenACCTileClause::Create(const ASTContext &C, OpenACCPrivateClause * OpenACCPrivateClause::Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc, ArrayRef VarList, - ArrayRef InitRecipes, + ArrayRef InitRecipes, SourceLocation EndLoc) { assert(VarList.size() == InitRecipes.size()); - void *Mem = - C.Allocate(OpenACCPrivateClause::totalSizeToAlloc( + void *Mem = C.Allocate( + OpenACCPrivateClause::totalSizeToAlloc( VarList.size(), InitRecipes.size())); return new (Mem) OpenACCPrivateClause(BeginLoc, LParenLoc, VarList, InitRecipes, EndLoc); diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index 2035fa7635f2a..1bc21062203cb 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -2636,8 +2636,11 @@ void OpenACCClauseProfiler::VisitPrivateClause( const OpenACCPrivateClause &Clause) { VisitClauseWithVarList(Clause); - for (auto *VD : Clause.getInitRecipes()) - Profiler.VisitDecl(VD); + for (auto &Recipe : Clause.getInitRecipes()) { + Profiler.VisitDecl(Recipe.AllocaDecl); + if (Recipe.InitExpr) + Profiler.VisitExpr(Recipe.InitExpr); + } } void OpenACCClauseProfiler::VisitFirstPrivateClause( @@ -2645,7 +2648,9 @@ void OpenACCClauseProfiler::VisitFirstPrivateClause( VisitClauseWithVarList(Clause); for (auto &Recipe : Clause.getInitRecipes()) { - Profiler.VisitDecl(Recipe.RecipeDecl); + Profiler.VisitDecl(Recipe.AllocaDecl); + if (Recipe.InitExpr) + Profiler.VisitExpr(Recipe.InitExpr); Profiler.VisitDecl(Recipe.InitFromTemporary); } } @@ -2750,11 +2755,13 @@ void OpenACCClauseProfiler::VisitReductionClause( VisitClauseWithVarList(Clause); for (auto &Recipe : Clause.getRecipes()) { - Profiler.VisitDecl(Recipe.RecipeDecl); + Profiler.VisitDecl(Recipe.AllocaDecl); + if (Recipe.InitExpr) + Profiler.VisitExpr(Recipe.InitExpr); // TODO: OpenACC: Make sure we remember to update this when we figure out // what we're adding for the operation recipe, in the meantime, a static // assert will make sure we don't add something. - static_assert(sizeof(OpenACCReductionRecipe) == sizeof(int *)); + static_assert(sizeof(OpenACCReductionRecipe) == 2 * sizeof(int *)); } } diff --git a/clang/lib/CIR/CodeGen/CIRGenOpenACCClause.cpp b/clang/lib/CIR/CodeGen/CIRGenOpenACCClause.cpp index 41834da8a86c3..0022befa3b562 100644 --- a/clang/lib/CIR/CodeGen/CIRGenOpenACCClause.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenOpenACCClause.cpp @@ -1280,10 +1280,16 @@ class OpenACCClauseCIREmitter final { mlir::OpBuilder::InsertionGuard guardCase(builder); + // TODO: OpenACC: At the moment this is a bit of a hacky way of doing + // this, and won't work when we get to bounds/etc. Do this for now to + // limit the scope of this refactor. + VarDecl *allocaDecl = varRecipe.AllocaDecl; + allocaDecl->setInit(varRecipe.InitExpr); + allocaDecl->setInitStyle(VarDecl::CallInit); + auto recipe = getOrCreateRecipe( - cgf.getContext(), varExpr, varRecipe, /*temporary=*/nullptr, + cgf.getContext(), varExpr, allocaDecl, /*temporary=*/nullptr, OpenACCReductionOperator::Invalid, - Decl::castToDeclContext(cgf.curFuncDecl), opInfo.baseType, privateOp.getResult()); // TODO: OpenACC: The dialect is going to change in the near future to @@ -1316,8 +1322,15 @@ class OpenACCClauseCIREmitter final { mlir::OpBuilder::InsertionGuard guardCase(builder); + // TODO: OpenACC: At the moment this is a bit of a hacky way of doing + // this, and won't work when we get to bounds/etc. Do this for now to + // limit the scope of this refactor. + VarDecl *allocaDecl = varRecipe.AllocaDecl; + allocaDecl->setInit(varRecipe.InitExpr); + allocaDecl->setInitStyle(VarDecl::CallInit); + auto recipe = getOrCreateRecipe( - cgf.getContext(), varExpr, varRecipe.RecipeDecl, + cgf.getContext(), varExpr, allocaDecl, varRecipe.InitFromTemporary, OpenACCReductionOperator::Invalid, Decl::castToDeclContext(cgf.curFuncDecl), opInfo.baseType, firstPrivateOp.getResult()); @@ -1353,9 +1366,15 @@ class OpenACCClauseCIREmitter final { mlir::OpBuilder::InsertionGuard guardCase(builder); + // TODO: OpenACC: At the moment this is a bit of a hacky way of doing + // this, and won't work when we get to bounds/etc. Do this for now to + // limit the scope of this refactor. + VarDecl *allocaDecl = varRecipe.AllocaDecl; + allocaDecl->setInit(varRecipe.InitExpr); + allocaDecl->setInitStyle(VarDecl::CallInit); auto recipe = getOrCreateRecipe( - cgf.getContext(), varExpr, varRecipe.RecipeDecl, + cgf.getContext(), varExpr, allocaDecl, /*temporary=*/nullptr, clause.getReductionOp(), Decl::castToDeclContext(cgf.curFuncDecl), opInfo.baseType, reductionOp.getResult()); diff --git a/clang/lib/Sema/SemaOpenACC.cpp b/clang/lib/Sema/SemaOpenACC.cpp index 5082e2c272ebd..fbd8022cd68ba 100644 --- a/clang/lib/Sema/SemaOpenACC.cpp +++ b/clang/lib/Sema/SemaOpenACC.cpp @@ -2590,9 +2590,11 @@ SemaOpenACC::ActOnOpenACCAsteriskSizeExpr(SourceLocation AsteriskLoc) { } namespace { -enum class InitKind { Zero, One, AllOnes, Least, Largest }; +enum class InitKind { Invalid, Zero, One, AllOnes, Least, Largest }; llvm::APFloat getInitFloatValue(ASTContext &Context, InitKind IK, QualType Ty) { switch (IK) { + case InitKind::Invalid: + llvm_unreachable("invalid init kind"); case InitKind::Zero: return llvm::APFloat::getZero(Context.getFloatTypeSemantics(Ty)); case InitKind::One: @@ -2604,13 +2606,14 @@ llvm::APFloat getInitFloatValue(ASTContext &Context, InitKind IK, QualType Ty) { /*Negative=*/true); case InitKind::Largest: return llvm::APFloat::getLargest(Context.getFloatTypeSemantics(Ty)); - break; } llvm_unreachable("unknown init kind"); } llvm::APInt getInitIntValue(ASTContext &Context, InitKind IK, QualType Ty) { switch (IK) { + case InitKind::Invalid: + llvm_unreachable("invalid init kind"); case InitKind::Zero: return llvm::APInt(Context.getIntWidth(Ty), 0); case InitKind::One: @@ -2625,7 +2628,6 @@ llvm::APInt getInitIntValue(ASTContext &Context, InitKind IK, QualType Ty) { if (Ty->isSignedIntegerOrEnumerationType()) return llvm::APInt::getSignedMaxValue(Context.getIntWidth(Ty)); return llvm::APInt::getMaxValue(Context.getIntWidth(Ty)); - break; } llvm_unreachable("unknown init kind"); } @@ -2635,6 +2637,16 @@ llvm::APInt getInitIntValue(ASTContext &Context, InitKind IK, QualType Ty) { Expr *GenerateReductionInitRecipeExpr(ASTContext &Context, SourceRange ExprRange, QualType Ty, InitKind IK) { + if (IK == InitKind::Invalid) + return nullptr; + + if (IK == InitKind::Zero) { + Expr *InitExpr = new (Context) + InitListExpr(Context, ExprRange.getBegin(), {}, ExprRange.getEnd()); + InitExpr->setType(Context.VoidTy); + return InitExpr; + } + Ty = Ty.getCanonicalType(); llvm::SmallVector Exprs; @@ -2712,219 +2724,202 @@ Expr *GenerateReductionInitRecipeExpr(ASTContext &Context, return InitExpr; } -} // namespace - -std::pair -SemaOpenACC::CreateInitRecipe(OpenACCClauseKind CK, - OpenACCReductionOperator ReductionOperator, - const Expr *VarExpr) { - // Strip off any array subscripts/array section exprs to get to the type of - // the variable. +const Expr *StripOffBounds(const Expr *VarExpr) { while (isa_and_present(VarExpr)) { if (const auto *AS = dyn_cast(VarExpr)) VarExpr = AS->getBase()->IgnoreParenImpCasts(); else if (const auto *Sub = dyn_cast(VarExpr)) VarExpr = Sub->getBase()->IgnoreParenImpCasts(); } + return VarExpr; +} + +VarDecl *CreateAllocaDecl(ASTContext &Ctx, DeclContext *DC, + SourceLocation BeginLoc, IdentifierInfo *VarName, + QualType VarTy) { + return VarDecl::Create(Ctx, DC, BeginLoc, BeginLoc, VarName, VarTy, + Ctx.getTrivialTypeSourceInfo(VarTy), SC_Auto); +} + +ExprResult FinishValueInit(Sema &S, InitializedEntity &Entity, + SourceLocation Loc, QualType VarTy, Expr *InitExpr) { + if (!InitExpr) + return ExprEmpty(); + + InitializationKind Kind = + InitializationKind::CreateForInit(Loc, /*DirectInit=*/true, InitExpr); + InitializationSequence InitSeq(S, Entity, Kind, InitExpr, + /*TopLevelOfInitList=*/false, + /*TreatUnavailableAsInvalid=*/false); + + return InitSeq.Perform(S, Entity, Kind, InitExpr, &VarTy); +} + +} // namespace + +OpenACCPrivateRecipe SemaOpenACC::CreatePrivateInitRecipe(const Expr *VarExpr) { + VarExpr = StripOffBounds(VarExpr); - // If for some reason the expression is invalid, or this is dependent, just - // fill in with nullptr. We'll count on TreeTransform to make this if - // necessary. if (!VarExpr || VarExpr->getType()->isDependentType()) - return {nullptr, nullptr}; + return OpenACCPrivateRecipe::Empty(); QualType VarTy = VarExpr->getType().getNonReferenceType().getUnqualifiedType(); - IdentifierInfo *VarName = [&]() { - switch (CK) { - case OpenACCClauseKind::Private: - return &getASTContext().Idents.get("openacc.private.init"); - case OpenACCClauseKind::FirstPrivate: - return &getASTContext().Idents.get("openacc.firstprivate.init"); - case OpenACCClauseKind::Reduction: - return &getASTContext().Idents.get("openacc.reduction.init"); - default: - llvm_unreachable("Unknown clause kind?"); - } - }(); + // TODO: OpenACC: for arrays/bounds versions, we're going to have to do a + // different initializer, but for now we can go ahead with this. - VarDecl *Recipe = VarDecl::Create( + VarDecl *AllocaDecl = CreateAllocaDecl( getASTContext(), SemaRef.getCurContext(), VarExpr->getBeginLoc(), - VarExpr->getBeginLoc(), VarName, VarTy, - getASTContext().getTrivialTypeSourceInfo(VarTy), SC_Auto); - - ExprResult Init; - VarDecl *Temporary = nullptr; - { - // Trap errors so we don't get weird ones here. If we can't init, we'll just - // swallow the errors. - Sema::TentativeAnalysisScope Trap{SemaRef}; - InitializedEntity Entity = InitializedEntity::InitializeVariable(Recipe); - - auto FinishValueInit = [&](Expr *InitExpr) { - if (InitExpr) { - InitializationKind Kind = InitializationKind::CreateForInit( - Recipe->getLocation(), /*DirectInit=*/true, InitExpr); - InitializationSequence InitSeq(SemaRef.SemaRef, Entity, Kind, InitExpr, - /*TopLevelOfInitList=*/false, - /*TreatUnavailableAsInvalid=*/false); - return InitSeq.Perform(SemaRef.SemaRef, Entity, Kind, InitExpr, &VarTy); - } - return ExprEmpty(); - }; + &getASTContext().Idents.get("openacc.private.init"), VarTy); - if (CK == OpenACCClauseKind::Private) { - InitializationKind Kind = - InitializationKind::CreateDefault(Recipe->getLocation()); - - InitializationSequence InitSeq(SemaRef.SemaRef, Entity, Kind, {}); - Init = InitSeq.Perform(SemaRef.SemaRef, Entity, Kind, {}); - } else if (CK == OpenACCClauseKind::FirstPrivate) { - // Create a VarDecl to be the 'copied-from' for the copy section of the - // recipe. This allows us to make the association so that we can use the - // standard 'generation' ability of the init. - Temporary = VarDecl::Create( - getASTContext(), SemaRef.getCurContext(), VarExpr->getBeginLoc(), - VarExpr->getBeginLoc(), &getASTContext().Idents.get("openacc.temp"), - VarTy, getASTContext().getTrivialTypeSourceInfo(VarTy), SC_Auto); - auto *TemporaryDRE = DeclRefExpr::Create( - getASTContext(), NestedNameSpecifierLoc{}, SourceLocation{}, - Temporary, - /*ReferstoEnclosingVariableOrCapture=*/false, - DeclarationNameInfo{DeclarationName{Temporary->getDeclName()}, - VarExpr->getBeginLoc()}, - VarTy, clang::VK_LValue, Temporary, nullptr, NOUR_None); - - Expr *InitExpr = nullptr; - - if (const auto *ArrTy = getASTContext().getAsConstantArrayType(VarTy)) { - // Arrays need to have each individual element initialized as there - // isn't a normal 'equals' feature in C/C++. This section sets these up - // as an init list after 'initializing' each individual element. - llvm::SmallVector Args; - - // Decay to pointer for the array subscript expression. - auto *CastToPtr = ImplicitCastExpr::Create( - getASTContext(), - getASTContext().getPointerType(ArrTy->getElementType()), - CK_ArrayToPointerDecay, TemporaryDRE, /*BasePath=*/nullptr, - clang::VK_LValue, FPOptionsOverride{}); - - for (std::size_t I = 0; I < ArrTy->getLimitedSize(); ++I) { - // Each element needs to be some sort of copy initialization from an - // array-index of the original temporary (referenced via a - // DeclRefExpr). - - auto *Idx = IntegerLiteral::Create( - getASTContext(), - llvm::APInt( - getASTContext().getTypeSize(getASTContext().getSizeType()), - I), - getASTContext().getSizeType(), VarExpr->getBeginLoc()); - - Expr *Subscript = new (getASTContext()) ArraySubscriptExpr( - CastToPtr, Idx, ArrTy->getElementType(), clang::VK_LValue, - OK_Ordinary, VarExpr->getBeginLoc()); - - // Generate a simple copy from the result of the subscript. This will - // do a bitwise copy or a copy-constructor, as necessary. - InitializedEntity CopyEntity = - InitializedEntity::InitializeElement(getASTContext(), I, Entity); - InitializationKind CopyKind = - InitializationKind::CreateCopy(VarExpr->getBeginLoc(), {}); - InitializationSequence CopySeq(SemaRef.SemaRef, CopyEntity, CopyKind, - Subscript, - /*TopLevelOfInitList=*/true); - - ExprResult ElemRes = - CopySeq.Perform(SemaRef.SemaRef, CopyEntity, CopyKind, Subscript); - Args.push_back(ElemRes.get()); - } + Sema::TentativeAnalysisScope Trap{SemaRef}; + InitializedEntity Entity = InitializedEntity::InitializeVariable(AllocaDecl); + InitializationKind Kind = + InitializationKind::CreateDefault(AllocaDecl->getLocation()); + InitializationSequence InitSeq(SemaRef.SemaRef, Entity, Kind, {}); + ExprResult Init = InitSeq.Perform(SemaRef.SemaRef, Entity, Kind, {}); - InitExpr = new (getASTContext()) - InitListExpr(getASTContext(), VarExpr->getBeginLoc(), Args, - VarExpr->getEndLoc()); - InitExpr->setType(VarTy); + return OpenACCPrivateRecipe(AllocaDecl, Init.get()); +} - } else { - // If this isn't an array, we can just do normal copy init from a simple - // variable reference, so set that up. - InitExpr = TemporaryDRE; - } +OpenACCFirstPrivateRecipe +SemaOpenACC::CreateFirstPrivateInitRecipe(const Expr *VarExpr) { + VarExpr = StripOffBounds(VarExpr); - Init = FinishValueInit(InitExpr); - } else if (CK == OpenACCClauseKind::Reduction) { - // How we initialize the reduction variable depends on the operator used, - // according to the chart in OpenACC 3.3 section 2.6.15. - - switch (ReductionOperator) { - case OpenACCReductionOperator::Invalid: - // This can only happen when there is an error, and since these inits - // are used for code generation, we can just ignore/not bother doing any - // initialization here. - break; - case OpenACCReductionOperator::Max: { - Expr *InitExpr = GenerateReductionInitRecipeExpr( - getASTContext(), VarExpr->getSourceRange(), VarTy, InitKind::Least); - - Init = FinishValueInit(InitExpr); - break; - } - case OpenACCReductionOperator::Min: { - Expr *InitExpr = GenerateReductionInitRecipeExpr( - getASTContext(), VarExpr->getSourceRange(), VarTy, - InitKind::Largest); + if (!VarExpr || VarExpr->getType()->isDependentType()) + return OpenACCFirstPrivateRecipe::Empty(); - Init = FinishValueInit(InitExpr); - break; - } - case OpenACCReductionOperator::BitwiseAnd: { - Expr *InitExpr = GenerateReductionInitRecipeExpr( - getASTContext(), VarExpr->getSourceRange(), VarTy, - InitKind::AllOnes); + QualType VarTy = + VarExpr->getType().getNonReferenceType().getUnqualifiedType(); - Init = FinishValueInit(InitExpr); - break; - } - case OpenACCReductionOperator::Multiplication: - case OpenACCReductionOperator::And: { - // '&&' initializes every field to 1. However, we need to loop through - // every field/element and generate an initializer for each of the - // elements. + // TODO: OpenACC: for arrays/bounds versions, we're going to have to do a + // different initializer, but for now we can go ahead with this. - Expr *InitExpr = GenerateReductionInitRecipeExpr( - getASTContext(), VarExpr->getSourceRange(), VarTy, InitKind::One); + VarDecl *AllocaDecl = CreateAllocaDecl( + getASTContext(), SemaRef.getCurContext(), VarExpr->getBeginLoc(), + &getASTContext().Idents.get("openacc.firstprivate.init"), VarTy); - Init = FinishValueInit(InitExpr); - break; - } - case OpenACCReductionOperator::Addition: - case OpenACCReductionOperator::BitwiseOr: - case OpenACCReductionOperator::BitwiseXOr: - case OpenACCReductionOperator::Or: { - // +, |, ^, and || all use 0 for their initializers, so we can just - // use 'zero init' here and not bother with the rest of the - // array/compound type/etc contents. - Expr *InitExpr = new (getASTContext()) InitListExpr( - getASTContext(), VarExpr->getBeginLoc(), {}, VarExpr->getEndLoc()); - // we set this to void so that the initialization sequence generation - // will get this type correct/etc. - InitExpr->setType(getASTContext().VoidTy); - - Init = FinishValueInit(InitExpr); - break; - } - } - } else { - llvm_unreachable("Unknown clause kind in CreateInitRecipe"); - } - } + VarDecl *Temporary = CreateAllocaDecl( + getASTContext(), SemaRef.getCurContext(), VarExpr->getBeginLoc(), + &getASTContext().Idents.get("openacc.temp"), VarTy); + + auto *TemporaryDRE = DeclRefExpr::Create( + getASTContext(), NestedNameSpecifierLoc{}, SourceLocation{}, Temporary, + /*ReferstoEnclosingVariableOrCapture=*/false, + DeclarationNameInfo{DeclarationName{Temporary->getDeclName()}, + VarExpr->getBeginLoc()}, + VarTy, clang::VK_LValue, Temporary, nullptr, NOUR_None); + + Sema::TentativeAnalysisScope Trap{SemaRef}; + InitializedEntity Entity = InitializedEntity::InitializeVariable(AllocaDecl); + + const auto *ArrTy = getASTContext().getAsConstantArrayType(VarTy); + if (!ArrTy) { + ExprResult Init = FinishValueInit( + SemaRef.SemaRef, Entity, VarExpr->getBeginLoc(), VarTy, TemporaryDRE); + return OpenACCFirstPrivateRecipe(AllocaDecl, Init.get(), Temporary); + } + + // Arrays need to have each individual element initialized as there + // isn't a normal 'equals' feature in C/C++. This section sets these up + // as an init list after 'initializing' each individual element. + llvm::SmallVector Args; + // Decay to pointer for the array subscript expression. + auto *CastToPtr = ImplicitCastExpr::Create( + getASTContext(), getASTContext().getPointerType(ArrTy->getElementType()), + CK_ArrayToPointerDecay, TemporaryDRE, /*BasePath=*/nullptr, + clang::VK_LValue, FPOptionsOverride{}); + + for (std::size_t I = 0; I < ArrTy->getLimitedSize(); ++I) { + // Each element needs to be some sort of copy initialization from an + // array-index of the original temporary (referenced via a + // DeclRefExpr). + auto *Idx = IntegerLiteral::Create( + getASTContext(), + llvm::APInt(getASTContext().getTypeSize(getASTContext().getSizeType()), + I), + getASTContext().getSizeType(), VarExpr->getBeginLoc()); + + Expr *Subscript = new (getASTContext()) ArraySubscriptExpr( + CastToPtr, Idx, ArrTy->getElementType(), clang::VK_LValue, OK_Ordinary, + VarExpr->getBeginLoc()); + // Generate a simple copy from the result of the subscript. This will + // do a bitwise copy or a copy-constructor, as necessary. + InitializedEntity CopyEntity = + InitializedEntity::InitializeElement(getASTContext(), I, Entity); + InitializationKind CopyKind = + InitializationKind::CreateCopy(VarExpr->getBeginLoc(), {}); + InitializationSequence CopySeq(SemaRef.SemaRef, CopyEntity, CopyKind, + Subscript, + /*TopLevelOfInitList=*/true); + ExprResult ElemRes = + CopySeq.Perform(SemaRef.SemaRef, CopyEntity, CopyKind, Subscript); + Args.push_back(ElemRes.get()); + } + + Expr *InitExpr = new (getASTContext()) InitListExpr( + getASTContext(), VarExpr->getBeginLoc(), Args, VarExpr->getEndLoc()); + InitExpr->setType(VarTy); + + ExprResult Init = FinishValueInit(SemaRef.SemaRef, Entity, + VarExpr->getBeginLoc(), VarTy, InitExpr); + + return OpenACCFirstPrivateRecipe(AllocaDecl, Init.get(), Temporary); +} +OpenACCReductionRecipe SemaOpenACC::CreateReductionInitRecipe( + OpenACCReductionOperator ReductionOperator, const Expr *VarExpr) { + VarExpr = StripOffBounds(VarExpr); - if (Init.get()) { - Recipe->setInit(Init.get()); - Recipe->setInitStyle(VarDecl::CallInit); + if (!VarExpr || VarExpr->getType()->isDependentType()) + return OpenACCReductionRecipe::Empty(); + + QualType VarTy = + VarExpr->getType().getNonReferenceType().getUnqualifiedType(); + + // TODO: OpenACC: for arrays/bounds versions, we're going to have to do a + // different initializer, but for now we can go ahead with this. + + VarDecl *AllocaDecl = CreateAllocaDecl( + getASTContext(), SemaRef.getCurContext(), VarExpr->getBeginLoc(), + &getASTContext().Idents.get("openacc.reduction.init"), VarTy); + + Sema::TentativeAnalysisScope Trap{SemaRef}; + InitializedEntity Entity = InitializedEntity::InitializeVariable(AllocaDecl); + + InitKind IK = InitKind::Invalid; + switch (ReductionOperator) { + case OpenACCReductionOperator::Invalid: + // This can only happen when there is an error, and since these inits + // are used for code generation, we can just ignore/not bother doing any + // initialization here. + IK = InitKind::Invalid; + break; + case OpenACCReductionOperator::Max: + IK = InitKind::Least; + break; + case OpenACCReductionOperator::Min: + IK = InitKind::Largest; + break; + case OpenACCReductionOperator::BitwiseAnd: + IK = InitKind::AllOnes; + break; + case OpenACCReductionOperator::Multiplication: + case OpenACCReductionOperator::And: + IK = InitKind::One; + break; + case OpenACCReductionOperator::Addition: + case OpenACCReductionOperator::BitwiseOr: + case OpenACCReductionOperator::BitwiseXOr: + case OpenACCReductionOperator::Or: + IK = InitKind::Zero; + break; } - return {Recipe, Temporary}; + Expr *InitExpr = GenerateReductionInitRecipeExpr( + getASTContext(), VarExpr->getSourceRange(), VarTy, IK); + + ExprResult Init = FinishValueInit(SemaRef.SemaRef, Entity, + VarExpr->getBeginLoc(), VarTy, InitExpr); + return OpenACCReductionRecipe(AllocaDecl, Init.get()); } diff --git a/clang/lib/Sema/SemaOpenACCClause.cpp b/clang/lib/Sema/SemaOpenACCClause.cpp index 36f79ba46caf3..b0869293c1664 100644 --- a/clang/lib/Sema/SemaOpenACCClause.cpp +++ b/clang/lib/Sema/SemaOpenACCClause.cpp @@ -795,12 +795,11 @@ OpenACCClause *SemaOpenACCClauseVisitor::VisitPrivateClause( // really isn't anything to do here. GCC does some duplicate-finding, though // it isn't apparent in the standard where this is justified. - llvm::SmallVector InitRecipes; + llvm::SmallVector InitRecipes; // Assemble the recipes list. for (const Expr *VarExpr : Clause.getVarList()) - InitRecipes.push_back( - SemaRef.CreateInitRecipe(OpenACCClauseKind::Private, VarExpr).first); + InitRecipes.push_back(SemaRef.CreatePrivateInitRecipe(VarExpr)); return OpenACCPrivateClause::Create( Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(), Clause.getVarList(), @@ -817,8 +816,7 @@ OpenACCClause *SemaOpenACCClauseVisitor::VisitFirstPrivateClause( // Assemble the recipes list. for (const Expr *VarExpr : Clause.getVarList()) - InitRecipes.push_back( - SemaRef.CreateInitRecipe(OpenACCClauseKind::FirstPrivate, VarExpr)); + InitRecipes.push_back(SemaRef.CreateFirstPrivateInitRecipe(VarExpr)); return OpenACCFirstPrivateClause::Create( Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(), Clause.getVarList(), @@ -1783,12 +1781,8 @@ OpenACCClause *SemaOpenACCClauseVisitor::VisitReductionClause( if (Res.isUsable()) { ValidVars.push_back(Res.get()); - VarDecl *InitRecipe = - SemaRef - .CreateInitRecipe(OpenACCClauseKind::Reduction, - Clause.getReductionOp(), Res.get()) - .first; - Recipes.push_back({InitRecipe}); + Recipes.push_back(SemaRef.CreateReductionInitRecipe( + Clause.getReductionOp(), Res.get())); } } diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index fbadb8d6881a5..0587a7decbd8d 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -11903,7 +11903,7 @@ template void OpenACCClauseTransform::VisitPrivateClause( const OpenACCPrivateClause &C) { llvm::SmallVector InstantiatedVarList; - llvm::SmallVector InitRecipes; + llvm::SmallVector InitRecipes; for (const auto [RefExpr, InitRecipe] : llvm::zip(C.getVarList(), C.getInitRecipes())) { @@ -11914,14 +11914,11 @@ void OpenACCClauseTransform::VisitPrivateClause( // We only have to create a new one if it is dependent, and Sema won't // make one of these unless the type is non-dependent. - if (InitRecipe) + if (InitRecipe.isSet()) InitRecipes.push_back(InitRecipe); else InitRecipes.push_back( - Self.getSema() - .OpenACC() - .CreateInitRecipe(OpenACCClauseKind::Private, VarRef.get()) - .first); + Self.getSema().OpenACC().CreatePrivateInitRecipe(VarRef.get())); } } ParsedClause.setVarListDetails(InstantiatedVarList, @@ -11972,11 +11969,12 @@ void OpenACCClauseTransform::VisitFirstPrivateClause( // We only have to create a new one if it is dependent, and Sema won't // make one of these unless the type is non-dependent. - if (InitRecipe.RecipeDecl) + if (InitRecipe.isSet()) InitRecipes.push_back(InitRecipe); else - InitRecipes.push_back(Self.getSema().OpenACC().CreateInitRecipe( - OpenACCClauseKind::FirstPrivate, VarRef.get())); + InitRecipes.push_back( + Self.getSema().OpenACC().CreateFirstPrivateInitRecipe( + VarRef.get())); } } ParsedClause.setVarListDetails(InstantiatedVarList, @@ -12434,27 +12432,18 @@ void OpenACCClauseTransform::VisitReductionClause( SmallVector ValidVars; llvm::SmallVector Recipes; - for (const auto [Var, OrigRecipes] : + for (const auto [Var, OrigRecipe] : llvm::zip(TransformedVars, C.getRecipes())) { ExprResult Res = Self.getSema().OpenACC().CheckReductionVar( ParsedClause.getDirectiveKind(), C.getReductionOp(), Var); if (Res.isUsable()) { ValidVars.push_back(Res.get()); - // TODO OpenACC: When the recipe changes, make sure we get these right - // too. We probably need something similar for the operation. - static_assert(sizeof(OpenACCReductionRecipe) == sizeof(int*)); - VarDecl *InitRecipe = nullptr; - if (OrigRecipes.RecipeDecl) - InitRecipe = OrigRecipes.RecipeDecl; - else - InitRecipe = Self.getSema() - .OpenACC() - .CreateInitRecipe(OpenACCClauseKind::Reduction, - C.getReductionOp(), Res.get()) - .first; - - Recipes.push_back({InitRecipe}); + if (OrigRecipe.isSet()) + Recipes.push_back(OrigRecipe); + else + Recipes.push_back(Self.getSema().OpenACC().CreateReductionInitRecipe( + C.getReductionOp(), Res.get())); } } diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 7268af698cfb4..9b0e699b1a829 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -12858,9 +12858,13 @@ OpenACCClause *ASTRecordReader::readOpenACCClause() { SourceLocation LParenLoc = readSourceLocation(); llvm::SmallVector VarList = readOpenACCVarList(); - llvm::SmallVector RecipeList; - for (unsigned I = 0; I < VarList.size(); ++I) - RecipeList.push_back(readDeclAs()); + llvm::SmallVector RecipeList; + for (unsigned I = 0; I < VarList.size(); ++I) { + static_assert(sizeof(OpenACCPrivateRecipe) == 2 * sizeof(int *)); + VarDecl *Alloca = readDeclAs(); + Expr *InitExpr = readSubExpr(); + RecipeList.push_back({Alloca, InitExpr}); + } return OpenACCPrivateClause::Create(getContext(), BeginLoc, LParenLoc, VarList, RecipeList, EndLoc); @@ -12882,9 +12886,11 @@ OpenACCClause *ASTRecordReader::readOpenACCClause() { llvm::SmallVector VarList = readOpenACCVarList(); llvm::SmallVector RecipeList; for (unsigned I = 0; I < VarList.size(); ++I) { + static_assert(sizeof(OpenACCFirstPrivateRecipe) == 3 * sizeof(int *)); VarDecl *Recipe = readDeclAs(); + Expr *InitExpr = readSubExpr(); VarDecl *RecipeTemp = readDeclAs(); - RecipeList.push_back({Recipe, RecipeTemp}); + RecipeList.push_back({Recipe, InitExpr, RecipeTemp}); } return OpenACCFirstPrivateClause::Create(getContext(), BeginLoc, LParenLoc, @@ -13005,9 +13011,10 @@ OpenACCClause *ASTRecordReader::readOpenACCClause() { llvm::SmallVector RecipeList; for (unsigned I = 0; I < VarList.size(); ++I) { + static_assert(sizeof(OpenACCReductionRecipe) == 2 * sizeof(int *)); VarDecl *Recipe = readDeclAs(); - static_assert(sizeof(OpenACCReductionRecipe) == sizeof(int *)); - RecipeList.push_back({Recipe}); + Expr *InitExpr = readSubExpr(); + RecipeList.push_back({Recipe, InitExpr}); } return OpenACCReductionClause::Create(getContext(), BeginLoc, LParenLoc, Op, diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 293b67aac0219..8e219e54bf251 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -8751,8 +8751,11 @@ void ASTRecordWriter::writeOpenACCClause(const OpenACCClause *C) { writeSourceLocation(PC->getLParenLoc()); writeOpenACCVarList(PC); - for (VarDecl *VD : PC->getInitRecipes()) - AddDeclRef(VD); + for (const OpenACCPrivateRecipe &R : PC->getInitRecipes()) { + static_assert(sizeof(R) == 2 * sizeof(int *)); + AddDeclRef(R.AllocaDecl); + AddStmt(const_cast(R.InitExpr)); + } return; } case OpenACCClauseKind::Host: { @@ -8773,7 +8776,9 @@ void ASTRecordWriter::writeOpenACCClause(const OpenACCClause *C) { writeOpenACCVarList(FPC); for (const OpenACCFirstPrivateRecipe &R : FPC->getInitRecipes()) { - AddDeclRef(R.RecipeDecl); + static_assert(sizeof(R) == 3 * sizeof(int *)); + AddDeclRef(R.AllocaDecl); + AddStmt(const_cast(R.InitExpr)); AddDeclRef(R.InitFromTemporary); } return; @@ -8895,8 +8900,9 @@ void ASTRecordWriter::writeOpenACCClause(const OpenACCClause *C) { writeOpenACCVarList(RC); for (const OpenACCReductionRecipe &R : RC->getRecipes()) { - static_assert(sizeof(OpenACCReductionRecipe) == sizeof(int *)); - AddDeclRef(R.RecipeDecl); + static_assert(sizeof(OpenACCReductionRecipe) == 2 * sizeof(int *)); + AddDeclRef(R.AllocaDecl); + AddStmt(const_cast(R.InitExpr)); } return; } diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index 858423a06576a..f255d751362ee 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -2837,8 +2837,10 @@ void OpenACCClauseEnqueue::VisitTileClause(const OpenACCTileClause &C) { void OpenACCClauseEnqueue::VisitPrivateClause(const OpenACCPrivateClause &C) { VisitVarList(C); - for (VarDecl *V : C.getInitRecipes()) - Visitor.AddDecl(V); + for (const OpenACCPrivateRecipe &R : C.getInitRecipes()) { + Visitor.AddDecl(R.AllocaDecl); + Visitor.AddStmt(R.InitExpr); + } } void OpenACCClauseEnqueue::VisitHostClause(const OpenACCHostClause &C) { @@ -2853,7 +2855,8 @@ void OpenACCClauseEnqueue::VisitFirstPrivateClause( const OpenACCFirstPrivateClause &C) { VisitVarList(C); for (const OpenACCFirstPrivateRecipe &R : C.getInitRecipes()) { - Visitor.AddDecl(R.RecipeDecl); + Visitor.AddDecl(R.AllocaDecl); + Visitor.AddStmt(R.InitExpr); Visitor.AddDecl(R.InitFromTemporary); } } @@ -2930,8 +2933,8 @@ void OpenACCClauseEnqueue::VisitReductionClause( const OpenACCReductionClause &C) { VisitVarList(C); for (const OpenACCReductionRecipe &R : C.getRecipes()) { - static_assert(sizeof(OpenACCReductionRecipe) == sizeof(int *)); - Visitor.AddDecl(R.RecipeDecl); + Visitor.AddDecl(R.AllocaDecl); + Visitor.AddStmt(R.InitExpr); } } void OpenACCClauseEnqueue::VisitAutoClause(const OpenACCAutoClause &C) {}