Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -13662,6 +13662,11 @@ def warn_acc_var_referenced_lacks_op
"reference has no effect">,
InGroup<DiagGroup<"openacc-var-lacks-operation">>,
DefaultError;
def err_acc_reduction_recipe_no_op
: Error<"variable of type %0 referenced in OpenACC 'reduction' clause does "
"not have a valid operation available">;
def note_acc_reduction_recipe_noop_field
: Note<"while forming combiner for compound type %0">;

// AMDGCN builtins diagnostics
def err_amdgcn_load_lds_size_invalid_value : Error<"invalid size value">;
Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/Sema/SemaOpenACC.h
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,11 @@ class SemaOpenACC : public SemaBase {

bool DiagnoseAllowedClauses(OpenACCDirectiveKind DK, OpenACCClauseKind CK,
SourceLocation ClauseLoc);
bool CreateReductionCombinerRecipe(
SourceLocation loc, OpenACCReductionOperator ReductionOperator,
QualType VarTy,
llvm::SmallVectorImpl<OpenACCReductionRecipe::CombinerRecipe>
&CombinerRecipes);

public:
// Needed from the visitor, so should be public.
Expand Down
167 changes: 166 additions & 1 deletion clang/lib/Sema/SemaOpenACC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2898,6 +2898,15 @@ OpenACCReductionRecipe SemaOpenACC::CreateReductionInitRecipe(
dyn_cast<ArraySectionExpr>(VarExpr->IgnoreParenImpCasts()))
VarTy = ArraySectionExpr::getBaseOriginalType(ASE);

llvm::SmallVector<OpenACCReductionRecipe::CombinerRecipe, 1> CombinerRecipes;

// We use the 'set-ness' of the alloca-decl to determine whether the combiner
// is 'set' or not, so we can skip any attempts at it if we're going to fail
// at any of the combiners.
if (CreateReductionCombinerRecipe(VarExpr->getBeginLoc(), ReductionOperator,
VarTy, CombinerRecipes))
return OpenACCReductionRecipe::Empty();

VarDecl *AllocaDecl = CreateAllocaDecl(
getASTContext(), SemaRef.getCurContext(), VarExpr->getBeginLoc(),
&getASTContext().Idents.get("openacc.reduction.init"), VarTy);
Expand Down Expand Up @@ -2946,5 +2955,161 @@ OpenACCReductionRecipe SemaOpenACC::CreateReductionInitRecipe(
AllocaDecl->setInit(Init.get());
AllocaDecl->setInitStyle(VarDecl::CallInit);
}
return OpenACCReductionRecipe(AllocaDecl, {});

return OpenACCReductionRecipe(AllocaDecl, CombinerRecipes);
}

bool SemaOpenACC::CreateReductionCombinerRecipe(
SourceLocation Loc, OpenACCReductionOperator ReductionOperator,
QualType VarTy,
llvm::SmallVectorImpl<OpenACCReductionRecipe::CombinerRecipe>
&CombinerRecipes) {
// Now we can try to generate the 'combiner' recipe. This is a little
// complicated in that if the 'VarTy' is an array type, we want to take its
// element type so we can generate that. Additionally, if this is a struct,
// we have two options: If there is overloaded operators, we want to take
// THOSE, else we want to do the individual elements.

BinaryOperatorKind BinOp;
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.
CombinerRecipes.push_back({nullptr, nullptr, nullptr});
return false;
case OpenACCReductionOperator::Addition:
BinOp = BinaryOperatorKind::BO_AddAssign;
break;
case OpenACCReductionOperator::Multiplication:
BinOp = BinaryOperatorKind::BO_MulAssign;
break;
case OpenACCReductionOperator::BitwiseAnd:
BinOp = BinaryOperatorKind::BO_AndAssign;
break;
case OpenACCReductionOperator::BitwiseOr:
BinOp = BinaryOperatorKind::BO_OrAssign;
break;
case OpenACCReductionOperator::BitwiseXOr:
BinOp = BinaryOperatorKind::BO_XorAssign;
break;

case OpenACCReductionOperator::Max:
case OpenACCReductionOperator::Min:
case OpenACCReductionOperator::And:
case OpenACCReductionOperator::Or:
// We just want a 'NYI' error in the backend, so leave an empty combiner
// recipe, and claim success.
CombinerRecipes.push_back({nullptr, nullptr, nullptr});
return false;
}

// If VarTy is an array type, at the top level only, we want to do our
// compares/decomp/etc at the element level.
if (VarTy->isArrayType())
VarTy = QualType{VarTy->getPointeeOrArrayElementType(), 0};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, if this is a multi-dimensional array you want the lowest level element type?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but only 1 level :) Only 1 level of arrays are allowed elsewhere in Sema. I should probably replace this with a single-unwrap + an assert though.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Woops! I made the same problem in the Sema checking as well! So I'll push another patch to fix this, then update this to do the right thing. Thanks!


auto tryCombiner = [&, this](DeclRefExpr *LHSDRE, DeclRefExpr *RHSDRE,
bool IncludeTrap) {
// TODO: OpenACC: we have to figure out based on the bin-op how to do the
// ones that we can't just use compound operators for. So &&, ||, max, and
// min aren't really clear what we could do here.
if (IncludeTrap) {
// Trap all of the errors here, we'll emit our own at the end.
Sema::TentativeAnalysisScope Trap{SemaRef};

return SemaRef.BuildBinOp(SemaRef.getCurScope(), Loc, BinOp, LHSDRE,
RHSDRE,
/*ForFoldExpr=*/false);
} else {
return SemaRef.BuildBinOp(SemaRef.getCurScope(), Loc, BinOp, LHSDRE,
RHSDRE,
/*ForFoldExpr=*/false);
}
};

struct CombinerAttemptTy {
VarDecl *LHS;
DeclRefExpr *LHSDRE;
VarDecl *RHS;
DeclRefExpr *RHSDRE;
Expr *Op;
};

auto formCombiner = [&, this](QualType Ty) -> CombinerAttemptTy {
VarDecl *LHSDecl = CreateAllocaDecl(
getASTContext(), SemaRef.getCurContext(), Loc,
&getASTContext().Idents.get("openacc.reduction.combiner.lhs"), Ty);
auto *LHSDRE = DeclRefExpr::Create(
getASTContext(), NestedNameSpecifierLoc{}, SourceLocation{}, LHSDecl,
/*ReferstoEnclosingVariableOrCapture=*/false,
DeclarationNameInfo{DeclarationName{LHSDecl->getDeclName()},
LHSDecl->getBeginLoc()},
Ty, clang::VK_LValue, LHSDecl, nullptr, NOUR_None);
VarDecl *RHSDecl = CreateAllocaDecl(
getASTContext(), SemaRef.getCurContext(), Loc,
&getASTContext().Idents.get("openacc.reduction.combiner.lhs"), Ty);
auto *RHSDRE = DeclRefExpr::Create(
getASTContext(), NestedNameSpecifierLoc{}, SourceLocation{}, RHSDecl,
/*ReferstoEnclosingVariableOrCapture=*/false,
DeclarationNameInfo{DeclarationName{RHSDecl->getDeclName()},
RHSDecl->getBeginLoc()},
Ty, clang::VK_LValue, RHSDecl, nullptr, NOUR_None);

ExprResult BinOpResult = tryCombiner(LHSDRE, RHSDRE, /*IncludeTrap=*/true);

return {LHSDecl, LHSDRE, RHSDecl, RHSDRE, BinOpResult.get()};
};

CombinerAttemptTy TopLevelCombinerInfo = formCombiner(VarTy);

if (TopLevelCombinerInfo.Op) {
if (!TopLevelCombinerInfo.Op->containsErrors() &&
TopLevelCombinerInfo.Op->isInstantiationDependent()) {
// If this is instantiation dependent, we're just going to 'give up' here
// and count on us to get it right during instantaition.
CombinerRecipes.push_back({nullptr, nullptr, nullptr});
return false;
} else if (!TopLevelCombinerInfo.Op->containsErrors()) {
// Else, we succeeded, we can just return this combiner.
CombinerRecipes.push_back({TopLevelCombinerInfo.LHS,
TopLevelCombinerInfo.RHS,
TopLevelCombinerInfo.Op});
return false;
}
}

// Since the 'root' level didn't fail, the only thing that could be successful
// is a struct that we decompose on its individual fields.

RecordDecl *RD = VarTy->getAsRecordDecl();
if (!RD) {
Diag(Loc, diag::err_acc_reduction_recipe_no_op) << VarTy;
tryCombiner(TopLevelCombinerInfo.LHSDRE, TopLevelCombinerInfo.RHSDRE,
/*IncludeTrap=*/false);
return true;
}

for (const FieldDecl *FD : RD->fields()) {
CombinerAttemptTy FieldCombinerInfo = formCombiner(FD->getType());

if (!FieldCombinerInfo.Op || FieldCombinerInfo.Op->containsErrors()) {
Diag(Loc, diag::err_acc_reduction_recipe_no_op) << FD->getType();
Diag(FD->getBeginLoc(), diag::note_acc_reduction_recipe_noop_field) << RD;
tryCombiner(FieldCombinerInfo.LHSDRE, FieldCombinerInfo.RHSDRE,
/*IncludeTrap=*/false);
return true;
}

if (FieldCombinerInfo.Op->isInstantiationDependent()) {
// If this is instantiation dependent, we're just going to 'give up' here
// and count on us to get it right during instantaition.
CombinerRecipes.push_back({nullptr, nullptr, nullptr});
} else {
CombinerRecipes.push_back(
{FieldCombinerInfo.LHS, FieldCombinerInfo.RHS, FieldCombinerInfo.Op});
}
}

return false;
}
38 changes: 17 additions & 21 deletions clang/lib/Sema/SemaOpenACCClause.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1975,30 +1975,26 @@ ExprResult SemaOpenACC::CheckReductionVar(OpenACCDirectiveKind DirectiveKind,
Diag(Loc, PD);

Diag(VarLoc, diag::note_acc_reduction_type_summary);
return ExprError();
};

// If the type is already scalar, or is dependent, just give up.
if (IsValidMemberOfComposite(CurType)) {
// Nothing to do here, is valid.
} else if (auto *RD = CurType->getAsRecordDecl()) {
if (!RD->isStruct() && !RD->isClass()) {
EmitDiags(VarLoc, PDiag(diag::err_acc_reduction_type)
<< RD << diag::OACCReductionTy::NotClassStruct);
return ExprError();
}
if (!RD->isStruct() && !RD->isClass())
return EmitDiags(VarLoc, PDiag(diag::err_acc_reduction_type)
<< RD
<< diag::OACCReductionTy::NotClassStruct);

if (!RD->isCompleteDefinition()) {
EmitDiags(VarLoc, PDiag(diag::err_acc_reduction_type)
<< RD << diag::OACCReductionTy::NotComplete);
return ExprError();
}
if (!RD->isCompleteDefinition())
return EmitDiags(VarLoc, PDiag(diag::err_acc_reduction_type)
<< RD << diag::OACCReductionTy::NotComplete);

if (const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD);
CXXRD && !CXXRD->isAggregate()) {
EmitDiags(VarLoc, PDiag(diag::err_acc_reduction_type)
<< CXXRD << diag::OACCReductionTy::NotAgg);
return ExprError();
}
CXXRD && !CXXRD->isAggregate())
return EmitDiags(VarLoc, PDiag(diag::err_acc_reduction_type)
<< CXXRD << diag::OACCReductionTy::NotAgg);

for (FieldDecl *FD : RD->fields()) {
if (!IsValidMemberOfComposite(FD->getType())) {
Expand All @@ -2007,15 +2003,15 @@ ExprResult SemaOpenACC::CheckReductionVar(OpenACCDirectiveKind DirectiveKind,
<< FD->getName() << RD->getName();
Notes.push_back({FD->getBeginLoc(), PD});
// TODO: member here.note_acc_reduction_member_of_composite
EmitDiags(VarLoc, PDiag(diag::err_acc_reduction_type)
<< FD->getType()
<< diag::OACCReductionTy::MemberNotScalar);
return ExprError();
return EmitDiags(VarLoc, PDiag(diag::err_acc_reduction_type)
<< FD->getType()
<< diag::OACCReductionTy::MemberNotScalar);
}
}
} else {
EmitDiags(VarLoc, PDiag(diag::err_acc_reduction_type)
<< CurType << diag::OACCReductionTy::NotScalar);
return EmitDiags(VarLoc, PDiag(diag::err_acc_reduction_type)
<< CurType
<< diag::OACCReductionTy::NotScalar);
}

// OpenACC3.3: 2.9.11: Reduction clauses on nested constructs for the same
Expand Down
Loading