Skip to content

Commit e95cedd

Browse files
authored
Reapply "[OpenACC] Sema changes for +*&|^ reduction combiner recipes (… (llvm/llvm-project#162920) (#163246)
This reverts commit llvm/llvm-project@8d9aecc. Additionally, this refactors how we're doing the AST storage to put it all in the trailing storage, which will hopefully prevent it from leaking. The problem was that the AST doesn't call destructors on things in ASTContext storage, so we weren't actually able to delete the combiner SmallVector (which I should have known...). This patch instead moves all of that SmallVector data into trailing storage, which shouldn't have the same problem with leaking as before.
1 parent 7f04ee1 commit e95cedd

21 files changed

+832
-1904
lines changed

clang/include/clang/AST/OpenACCClause.h

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1277,7 +1277,7 @@ class OpenACCCreateClause final
12771277
};
12781278

12791279
// A structure to stand in for the recipe on a reduction. RecipeDecl is the
1280-
// 'main' declaration used for initializaiton, which is fixed.
1280+
// 'main' declaration used for initializaiton, which is fixed.
12811281
struct OpenACCReductionRecipe {
12821282
VarDecl *AllocaDecl;
12831283

@@ -1297,36 +1297,69 @@ struct OpenACCReductionRecipe {
12971297
// -For a struct without the operator, this will be 1 element per field, which
12981298
// should be the combiner for that element.
12991299
// -For an array of any of the above, it will be the above for the element.
1300-
llvm::SmallVector<CombinerRecipe, 1> CombinerRecipes;
1300+
// Note: These are necessarily stored in either Trailing Storage (when in the
1301+
// AST), or in a separate collection when being semantically analyzed.
1302+
llvm::ArrayRef<CombinerRecipe> CombinerRecipes;
13011303

13021304
OpenACCReductionRecipe(VarDecl *A, llvm::ArrayRef<CombinerRecipe> Combiners)
13031305
: AllocaDecl(A), CombinerRecipes(Combiners) {}
13041306

13051307
bool isSet() const { return AllocaDecl; }
1306-
static OpenACCReductionRecipe Empty() {
1307-
return OpenACCReductionRecipe(/*AllocaDecl=*/nullptr, {});
1308+
};
1309+
1310+
// A version of the above that is used for semantic analysis, at a time before
1311+
// the OpenACCReductionClause node has been created. This one has storage for
1312+
// the CombinerRecipe, since Trailing storage for it doesn't exist yet.
1313+
struct OpenACCReductionRecipeWithStorage : OpenACCReductionRecipe {
1314+
llvm::SmallVector<CombinerRecipe, 1> CombinerRecipeStorage;
1315+
1316+
OpenACCReductionRecipeWithStorage(VarDecl *A,
1317+
llvm::ArrayRef<CombinerRecipe> Combiners)
1318+
: OpenACCReductionRecipe(A, {}), CombinerRecipeStorage(Combiners) {
1319+
CombinerRecipes = CombinerRecipeStorage;
1320+
}
1321+
static OpenACCReductionRecipeWithStorage Empty() {
1322+
return OpenACCReductionRecipeWithStorage(/*AllocaDecl=*/nullptr, {});
13081323
}
13091324
};
13101325

13111326
class OpenACCReductionClause final
13121327
: public OpenACCClauseWithVarList,
13131328
private llvm::TrailingObjects<OpenACCReductionClause, Expr *,
1314-
OpenACCReductionRecipe> {
1329+
OpenACCReductionRecipe,
1330+
OpenACCReductionRecipe::CombinerRecipe> {
13151331
friend TrailingObjects;
13161332
OpenACCReductionOperator Op;
13171333

13181334
OpenACCReductionClause(SourceLocation BeginLoc, SourceLocation LParenLoc,
13191335
OpenACCReductionOperator Operator,
13201336
ArrayRef<Expr *> VarList,
1321-
ArrayRef<OpenACCReductionRecipe> Recipes,
1337+
ArrayRef<OpenACCReductionRecipeWithStorage> Recipes,
13221338
SourceLocation EndLoc)
13231339
: OpenACCClauseWithVarList(OpenACCClauseKind::Reduction, BeginLoc,
13241340
LParenLoc, EndLoc),
13251341
Op(Operator) {
1326-
assert(VarList.size() == Recipes.size());
1342+
assert(VarList.size() == Recipes.size());
13271343
setExprs(getTrailingObjects<Expr *>(VarList.size()), VarList);
1328-
llvm::uninitialized_copy(Recipes, getTrailingObjects<
1329-
OpenACCReductionRecipe > ());
1344+
1345+
// Since we're using trailing storage on this node to store the 'combiner'
1346+
// recipes of the Reduction Recipes (which have a 1:M relationship), we need
1347+
// to ensure we get the ArrayRef of each of our combiner 'correct'.
1348+
OpenACCReductionRecipe::CombinerRecipe *CurCombinerLoc =
1349+
getTrailingObjects<OpenACCReductionRecipe::CombinerRecipe>();
1350+
for (const auto &[Idx, R] : llvm::enumerate(Recipes)) {
1351+
1352+
// ArrayRef to the 'correct' data location in trailing storage.
1353+
llvm::MutableArrayRef<OpenACCReductionRecipe::CombinerRecipe>
1354+
NewCombiners{CurCombinerLoc, R.CombinerRecipes.size()};
1355+
CurCombinerLoc += R.CombinerRecipes.size();
1356+
1357+
llvm::uninitialized_copy(R.CombinerRecipes, NewCombiners.begin());
1358+
1359+
// Placement new into the correct location in trailng storage.
1360+
new (&getTrailingObjects<OpenACCReductionRecipe>()[Idx])
1361+
OpenACCReductionRecipe(R.AllocaDecl, NewCombiners);
1362+
}
13301363
}
13311364

13321365
public:
@@ -1347,13 +1380,17 @@ class OpenACCReductionClause final
13471380
static OpenACCReductionClause *
13481381
Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc,
13491382
OpenACCReductionOperator Operator, ArrayRef<Expr *> VarList,
1350-
ArrayRef<OpenACCReductionRecipe> Recipes, SourceLocation EndLoc);
1383+
ArrayRef<OpenACCReductionRecipeWithStorage> Recipes,
1384+
SourceLocation EndLoc);
13511385

13521386
OpenACCReductionOperator getReductionOp() const { return Op; }
13531387

13541388
size_t numTrailingObjects(OverloadToken<Expr *>) const {
13551389
return getExprs().size();
13561390
}
1391+
size_t numTrailingObjects(OverloadToken<OpenACCReductionRecipe>) const {
1392+
return getExprs().size();
1393+
}
13571394
};
13581395

13591396
class OpenACCLinkClause final

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13665,6 +13665,11 @@ def warn_acc_var_referenced_lacks_op
1366513665
"reference has no effect">,
1366613666
InGroup<DiagGroup<"openacc-var-lacks-operation">>,
1366713667
DefaultError;
13668+
def err_acc_reduction_recipe_no_op
13669+
: Error<"variable of type %0 referenced in OpenACC 'reduction' clause does "
13670+
"not have a valid operation available">;
13671+
def note_acc_reduction_recipe_noop_field
13672+
: Note<"while forming combiner for compound type %0">;
1366813673

1366913674
// AMDGCN builtins diagnostics
1367013675
def err_amdgcn_load_lds_size_invalid_value : Error<"invalid size value">;

clang/include/clang/Sema/SemaOpenACC.h

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,11 @@ class SemaOpenACC : public SemaBase {
228228

229229
bool DiagnoseAllowedClauses(OpenACCDirectiveKind DK, OpenACCClauseKind CK,
230230
SourceLocation ClauseLoc);
231+
bool CreateReductionCombinerRecipe(
232+
SourceLocation loc, OpenACCReductionOperator ReductionOperator,
233+
QualType VarTy,
234+
llvm::SmallVectorImpl<OpenACCReductionRecipe::CombinerRecipe>
235+
&CombinerRecipes);
231236

232237
public:
233238
// Needed from the visitor, so should be public.
@@ -240,7 +245,7 @@ class SemaOpenACC : public SemaBase {
240245

241246
OpenACCPrivateRecipe CreatePrivateInitRecipe(const Expr *VarExpr);
242247
OpenACCFirstPrivateRecipe CreateFirstPrivateInitRecipe(const Expr *VarExpr);
243-
OpenACCReductionRecipe
248+
OpenACCReductionRecipeWithStorage
244249
CreateReductionInitRecipe(OpenACCReductionOperator ReductionOperator,
245250
const Expr *VarExpr);
246251

@@ -946,12 +951,14 @@ class SemaOpenACC : public SemaBase {
946951
ArrayRef<Expr *> IntExprs, SourceLocation EndLoc);
947952
// Does the checking for a 'reduction ' clause that needs to be done in
948953
// dependent and not dependent cases.
949-
OpenACCClause *CheckReductionClause(
950-
ArrayRef<const OpenACCClause *> ExistingClauses,
951-
OpenACCDirectiveKind DirectiveKind, SourceLocation BeginLoc,
952-
SourceLocation LParenLoc, OpenACCReductionOperator ReductionOp,
953-
ArrayRef<Expr *> Vars, ArrayRef<OpenACCReductionRecipe> Recipes,
954-
SourceLocation EndLoc);
954+
OpenACCClause *
955+
CheckReductionClause(ArrayRef<const OpenACCClause *> ExistingClauses,
956+
OpenACCDirectiveKind DirectiveKind,
957+
SourceLocation BeginLoc, SourceLocation LParenLoc,
958+
OpenACCReductionOperator ReductionOp,
959+
ArrayRef<Expr *> Vars,
960+
ArrayRef<OpenACCReductionRecipeWithStorage> Recipes,
961+
SourceLocation EndLoc);
955962

956963
ExprResult BuildOpenACCAsteriskSizeExpr(SourceLocation AsteriskLoc);
957964
ExprResult ActOnOpenACCAsteriskSizeExpr(SourceLocation AsteriskLoc);

clang/lib/AST/OpenACCClause.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -506,11 +506,17 @@ OpenACCDeviceTypeClause *OpenACCDeviceTypeClause::Create(
506506
OpenACCReductionClause *OpenACCReductionClause::Create(
507507
const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc,
508508
OpenACCReductionOperator Operator, ArrayRef<Expr *> VarList,
509-
ArrayRef<OpenACCReductionRecipe> Recipes,
509+
ArrayRef<OpenACCReductionRecipeWithStorage> Recipes,
510510
SourceLocation EndLoc) {
511-
void *Mem = C.Allocate(
512-
OpenACCReductionClause::totalSizeToAlloc<Expr *, OpenACCReductionRecipe>(
513-
VarList.size(), Recipes.size()));
511+
size_t NumCombiners = llvm::accumulate(
512+
Recipes, 0, [](size_t Num, const OpenACCReductionRecipe &R) {
513+
return Num + R.CombinerRecipes.size();
514+
});
515+
516+
void *Mem = C.Allocate(OpenACCReductionClause::totalSizeToAlloc<
517+
Expr *, OpenACCReductionRecipe,
518+
OpenACCReductionRecipe::CombinerRecipe>(
519+
VarList.size(), Recipes.size(), NumCombiners));
514520
return new (Mem) OpenACCReductionClause(BeginLoc, LParenLoc, Operator,
515521
VarList, Recipes, EndLoc);
516522
}

clang/lib/Sema/SemaOpenACC.cpp

Lines changed: 170 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2883,12 +2883,12 @@ SemaOpenACC::CreateFirstPrivateInitRecipe(const Expr *VarExpr) {
28832883
return OpenACCFirstPrivateRecipe(AllocaDecl, Temporary);
28842884
}
28852885

2886-
OpenACCReductionRecipe SemaOpenACC::CreateReductionInitRecipe(
2886+
OpenACCReductionRecipeWithStorage SemaOpenACC::CreateReductionInitRecipe(
28872887
OpenACCReductionOperator ReductionOperator, const Expr *VarExpr) {
28882888
// We don't strip bounds here, so that we are doing our recipe init at the
28892889
// 'lowest' possible level. Codegen is going to have to do its own 'looping'.
28902890
if (!VarExpr || VarExpr->getType()->isDependentType())
2891-
return OpenACCReductionRecipe::Empty();
2891+
return OpenACCReductionRecipeWithStorage::Empty();
28922892

28932893
QualType VarTy =
28942894
VarExpr->getType().getNonReferenceType().getUnqualifiedType();
@@ -2898,6 +2898,15 @@ OpenACCReductionRecipe SemaOpenACC::CreateReductionInitRecipe(
28982898
dyn_cast<ArraySectionExpr>(VarExpr->IgnoreParenImpCasts()))
28992899
VarTy = ASE->getElementType();
29002900

2901+
llvm::SmallVector<OpenACCReductionRecipe::CombinerRecipe, 1> CombinerRecipes;
2902+
2903+
// We use the 'set-ness' of the alloca-decl to determine whether the combiner
2904+
// is 'set' or not, so we can skip any attempts at it if we're going to fail
2905+
// at any of the combiners.
2906+
if (CreateReductionCombinerRecipe(VarExpr->getBeginLoc(), ReductionOperator,
2907+
VarTy, CombinerRecipes))
2908+
return OpenACCReductionRecipeWithStorage::Empty();
2909+
29012910
VarDecl *AllocaDecl = CreateAllocaDecl(
29022911
getASTContext(), SemaRef.getCurContext(), VarExpr->getBeginLoc(),
29032912
&getASTContext().Idents.get("openacc.reduction.init"), VarTy);
@@ -2946,5 +2955,163 @@ OpenACCReductionRecipe SemaOpenACC::CreateReductionInitRecipe(
29462955
AllocaDecl->setInit(Init.get());
29472956
AllocaDecl->setInitStyle(VarDecl::CallInit);
29482957
}
2949-
return OpenACCReductionRecipe(AllocaDecl, {});
2958+
2959+
return OpenACCReductionRecipeWithStorage(AllocaDecl, CombinerRecipes);
2960+
}
2961+
2962+
bool SemaOpenACC::CreateReductionCombinerRecipe(
2963+
SourceLocation Loc, OpenACCReductionOperator ReductionOperator,
2964+
QualType VarTy,
2965+
llvm::SmallVectorImpl<OpenACCReductionRecipe::CombinerRecipe>
2966+
&CombinerRecipes) {
2967+
// Now we can try to generate the 'combiner' recipe. This is a little
2968+
// complicated in that if the 'VarTy' is an array type, we want to take its
2969+
// element type so we can generate that. Additionally, if this is a struct,
2970+
// we have two options: If there is overloaded operators, we want to take
2971+
// THOSE, else we want to do the individual elements.
2972+
2973+
BinaryOperatorKind BinOp;
2974+
switch (ReductionOperator) {
2975+
case OpenACCReductionOperator::Invalid:
2976+
// This can only happen when there is an error, and since these inits
2977+
// are used for code generation, we can just ignore/not bother doing any
2978+
// initialization here.
2979+
CombinerRecipes.push_back({nullptr, nullptr, nullptr});
2980+
return false;
2981+
case OpenACCReductionOperator::Addition:
2982+
BinOp = BinaryOperatorKind::BO_AddAssign;
2983+
break;
2984+
case OpenACCReductionOperator::Multiplication:
2985+
BinOp = BinaryOperatorKind::BO_MulAssign;
2986+
break;
2987+
case OpenACCReductionOperator::BitwiseAnd:
2988+
BinOp = BinaryOperatorKind::BO_AndAssign;
2989+
break;
2990+
case OpenACCReductionOperator::BitwiseOr:
2991+
BinOp = BinaryOperatorKind::BO_OrAssign;
2992+
break;
2993+
case OpenACCReductionOperator::BitwiseXOr:
2994+
BinOp = BinaryOperatorKind::BO_XorAssign;
2995+
break;
2996+
2997+
case OpenACCReductionOperator::Max:
2998+
case OpenACCReductionOperator::Min:
2999+
case OpenACCReductionOperator::And:
3000+
case OpenACCReductionOperator::Or:
3001+
// We just want a 'NYI' error in the backend, so leave an empty combiner
3002+
// recipe, and claim success.
3003+
CombinerRecipes.push_back({nullptr, nullptr, nullptr});
3004+
return false;
3005+
}
3006+
3007+
// If VarTy is an array type, at the top level only, we want to do our
3008+
// compares/decomp/etc at the element level.
3009+
if (auto *AT = getASTContext().getAsArrayType(VarTy))
3010+
VarTy = AT->getElementType();
3011+
3012+
assert(!VarTy->isArrayType() && "Only 1 level of array allowed");
3013+
3014+
auto tryCombiner = [&, this](DeclRefExpr *LHSDRE, DeclRefExpr *RHSDRE,
3015+
bool IncludeTrap) {
3016+
// TODO: OpenACC: we have to figure out based on the bin-op how to do the
3017+
// ones that we can't just use compound operators for. So &&, ||, max, and
3018+
// min aren't really clear what we could do here.
3019+
if (IncludeTrap) {
3020+
// Trap all of the errors here, we'll emit our own at the end.
3021+
Sema::TentativeAnalysisScope Trap{SemaRef};
3022+
3023+
return SemaRef.BuildBinOp(SemaRef.getCurScope(), Loc, BinOp, LHSDRE,
3024+
RHSDRE,
3025+
/*ForFoldExpr=*/false);
3026+
} else {
3027+
return SemaRef.BuildBinOp(SemaRef.getCurScope(), Loc, BinOp, LHSDRE,
3028+
RHSDRE,
3029+
/*ForFoldExpr=*/false);
3030+
}
3031+
};
3032+
3033+
struct CombinerAttemptTy {
3034+
VarDecl *LHS;
3035+
DeclRefExpr *LHSDRE;
3036+
VarDecl *RHS;
3037+
DeclRefExpr *RHSDRE;
3038+
Expr *Op;
3039+
};
3040+
3041+
auto formCombiner = [&, this](QualType Ty) -> CombinerAttemptTy {
3042+
VarDecl *LHSDecl = CreateAllocaDecl(
3043+
getASTContext(), SemaRef.getCurContext(), Loc,
3044+
&getASTContext().Idents.get("openacc.reduction.combiner.lhs"), Ty);
3045+
auto *LHSDRE = DeclRefExpr::Create(
3046+
getASTContext(), NestedNameSpecifierLoc{}, SourceLocation{}, LHSDecl,
3047+
/*ReferstoEnclosingVariableOrCapture=*/false,
3048+
DeclarationNameInfo{DeclarationName{LHSDecl->getDeclName()},
3049+
LHSDecl->getBeginLoc()},
3050+
Ty, clang::VK_LValue, LHSDecl, nullptr, NOUR_None);
3051+
VarDecl *RHSDecl = CreateAllocaDecl(
3052+
getASTContext(), SemaRef.getCurContext(), Loc,
3053+
&getASTContext().Idents.get("openacc.reduction.combiner.lhs"), Ty);
3054+
auto *RHSDRE = DeclRefExpr::Create(
3055+
getASTContext(), NestedNameSpecifierLoc{}, SourceLocation{}, RHSDecl,
3056+
/*ReferstoEnclosingVariableOrCapture=*/false,
3057+
DeclarationNameInfo{DeclarationName{RHSDecl->getDeclName()},
3058+
RHSDecl->getBeginLoc()},
3059+
Ty, clang::VK_LValue, RHSDecl, nullptr, NOUR_None);
3060+
3061+
ExprResult BinOpResult = tryCombiner(LHSDRE, RHSDRE, /*IncludeTrap=*/true);
3062+
3063+
return {LHSDecl, LHSDRE, RHSDecl, RHSDRE, BinOpResult.get()};
3064+
};
3065+
3066+
CombinerAttemptTy TopLevelCombinerInfo = formCombiner(VarTy);
3067+
3068+
if (TopLevelCombinerInfo.Op) {
3069+
if (!TopLevelCombinerInfo.Op->containsErrors() &&
3070+
TopLevelCombinerInfo.Op->isInstantiationDependent()) {
3071+
// If this is instantiation dependent, we're just going to 'give up' here
3072+
// and count on us to get it right during instantaition.
3073+
CombinerRecipes.push_back({nullptr, nullptr, nullptr});
3074+
return false;
3075+
} else if (!TopLevelCombinerInfo.Op->containsErrors()) {
3076+
// Else, we succeeded, we can just return this combiner.
3077+
CombinerRecipes.push_back({TopLevelCombinerInfo.LHS,
3078+
TopLevelCombinerInfo.RHS,
3079+
TopLevelCombinerInfo.Op});
3080+
return false;
3081+
}
3082+
}
3083+
3084+
// Since the 'root' level didn't fail, the only thing that could be successful
3085+
// is a struct that we decompose on its individual fields.
3086+
3087+
RecordDecl *RD = VarTy->getAsRecordDecl();
3088+
if (!RD) {
3089+
Diag(Loc, diag::err_acc_reduction_recipe_no_op) << VarTy;
3090+
tryCombiner(TopLevelCombinerInfo.LHSDRE, TopLevelCombinerInfo.RHSDRE,
3091+
/*IncludeTrap=*/false);
3092+
return true;
3093+
}
3094+
3095+
for (const FieldDecl *FD : RD->fields()) {
3096+
CombinerAttemptTy FieldCombinerInfo = formCombiner(FD->getType());
3097+
3098+
if (!FieldCombinerInfo.Op || FieldCombinerInfo.Op->containsErrors()) {
3099+
Diag(Loc, diag::err_acc_reduction_recipe_no_op) << FD->getType();
3100+
Diag(FD->getBeginLoc(), diag::note_acc_reduction_recipe_noop_field) << RD;
3101+
tryCombiner(FieldCombinerInfo.LHSDRE, FieldCombinerInfo.RHSDRE,
3102+
/*IncludeTrap=*/false);
3103+
return true;
3104+
}
3105+
3106+
if (FieldCombinerInfo.Op->isInstantiationDependent()) {
3107+
// If this is instantiation dependent, we're just going to 'give up' here
3108+
// and count on us to get it right during instantaition.
3109+
CombinerRecipes.push_back({nullptr, nullptr, nullptr});
3110+
} else {
3111+
CombinerRecipes.push_back(
3112+
{FieldCombinerInfo.LHS, FieldCombinerInfo.RHS, FieldCombinerInfo.Op});
3113+
}
3114+
}
3115+
3116+
return false;
29503117
}

clang/lib/Sema/SemaOpenACCClause.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1772,7 +1772,7 @@ OpenACCClause *SemaOpenACCClauseVisitor::VisitReductionClause(
17721772
}
17731773

17741774
SmallVector<Expr *> ValidVars;
1775-
SmallVector<OpenACCReductionRecipe> Recipes;
1775+
SmallVector<OpenACCReductionRecipeWithStorage> Recipes;
17761776

17771777
for (Expr *Var : Clause.getVarList()) {
17781778
ExprResult Res = SemaRef.CheckReductionVar(Clause.getDirectiveKind(),
@@ -2196,7 +2196,7 @@ OpenACCClause *SemaOpenACC::CheckReductionClause(
21962196
ArrayRef<const OpenACCClause *> ExistingClauses,
21972197
OpenACCDirectiveKind DirectiveKind, SourceLocation BeginLoc,
21982198
SourceLocation LParenLoc, OpenACCReductionOperator ReductionOp,
2199-
ArrayRef<Expr *> Vars, ArrayRef<OpenACCReductionRecipe> Recipes,
2199+
ArrayRef<Expr *> Vars, ArrayRef<OpenACCReductionRecipeWithStorage> Recipes,
22002200
SourceLocation EndLoc) {
22012201
if (DirectiveKind == OpenACCDirectiveKind::Loop ||
22022202
isOpenACCCombinedDirectiveKind(DirectiveKind)) {

0 commit comments

Comments
 (0)