Skip to content

[OpenACC] Implement better dupe catching for device_type clauses #138196

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
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
10 changes: 10 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -12990,6 +12990,13 @@ def err_acc_duplicate_clause_disallowed
: Error<"OpenACC '%1' clause cannot appear more than once on a '%0' "
"directive">;
def note_acc_previous_clause_here : Note<"previous clause is here">;
// TODO(OpenACC): Combine these with the one above, and decide between a
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'll fix this as NFC when this gets merged, but the size of this review would explode if I were to do it now.

// 'select' to split between showing the clause name, or just always printing
// the name.
def note_acc_previous_named_clause_here : Note<"previous '%0' clause is here">;
def note_acc_device_type_here
: Note<"%enum_select<ACCDeviceTypeApp>{%Active{active}|%Applies{which "
"applies to}}0 'device_type' clause here">;
def note_acc_previous_expr_here : Note<"previous expression is here">;
def note_acc_previous_reference : Note<"previous reference is here">;
def err_acc_branch_in_out_compute_construct
Expand Down Expand Up @@ -13075,6 +13082,9 @@ def err_acc_clause_routine_one_of_in_region
def err_acc_clause_since_last_device_type
: Error<"OpenACC '%0' clause cannot appear more than once%select{| in a "
"'device_type' region}2 on a '%1' directive">;
def err_acc_clause_conflicts_prev_dev_type
: Error<"OpenACC '%0' clause applies to 'device_type' '%1', which "
"conflicts with previous '%0' clause">;

def err_acc_reduction_num_gangs_conflict
: Error<"OpenACC '%1' clause %select{|with more than 1 argument }0may not "
Expand Down
101 changes: 89 additions & 12 deletions clang/lib/Sema/SemaOpenACCClause.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -335,29 +335,106 @@ class SemaOpenACCClauseVisitor {

// For 'tile' and 'collapse', only allow 1 per 'device_type'.
// Also applies to num_worker, num_gangs, vector_length, and async.
// This does introspection into the actual device-types to prevent duplicates
// across device types as well.
template <typename TheClauseTy>
bool DisallowSinceLastDeviceType(SemaOpenACC::OpenACCParsedClause &Clause) {
auto LastDeviceTypeItr =
std::find_if(ExistingClauses.rbegin(), ExistingClauses.rend(),
llvm::IsaPred<OpenACCDeviceTypeClause>);

auto Last = std::find_if(ExistingClauses.rbegin(), LastDeviceTypeItr,
llvm::IsaPred<TheClauseTy>);
auto LastSinceDevTy =
std::find_if(ExistingClauses.rbegin(), LastDeviceTypeItr,
llvm::IsaPred<TheClauseTy>);

if (Last == LastDeviceTypeItr)
// In this case there is a duplicate since the last device_type/lack of a
// device_type. Diagnose these as duplicates.
if (LastSinceDevTy != LastDeviceTypeItr) {
SemaRef.Diag(Clause.getBeginLoc(),
diag::err_acc_clause_since_last_device_type)
<< Clause.getClauseKind() << Clause.getDirectiveKind()
<< (LastDeviceTypeItr != ExistingClauses.rend());
SemaRef.Diag((*LastSinceDevTy)->getBeginLoc(),
diag::note_acc_previous_clause_here);

// Mention the last device_type as well.
if (LastDeviceTypeItr != ExistingClauses.rend())
SemaRef.Diag((*LastDeviceTypeItr)->getBeginLoc(),
diag::note_acc_previous_clause_here);
return true;
}

// If this isn't in a device_type, and we didn't diagnose that there are
// dupes above, just give up, no sense in searching for previous device_type
// regions as they don't exist.
if (LastDeviceTypeItr == ExistingClauses.rend())
return false;

SemaRef.Diag(Clause.getBeginLoc(),
diag::err_acc_clause_since_last_device_type)
<< Clause.getClauseKind() << Clause.getDirectiveKind()
<< (LastDeviceTypeItr != ExistingClauses.rend());
SemaRef.Diag((*Last)->getBeginLoc(), diag::note_acc_previous_clause_here);
// The device-type that is active for us, so we can compare to the previous
// ones.
const auto &ActiveDeviceTypeClause =
cast<OpenACCDeviceTypeClause>(**LastDeviceTypeItr);

if (LastDeviceTypeItr != ExistingClauses.rend())
SemaRef.Diag((*LastDeviceTypeItr)->getBeginLoc(),
diag::note_acc_previous_clause_here);
auto PrevDeviceTypeItr = LastDeviceTypeItr;
auto CurDevTypeItr = LastDeviceTypeItr;

return true;
while ((CurDevTypeItr = std::find_if(
std::next(PrevDeviceTypeItr), ExistingClauses.rend(),
llvm::IsaPred<OpenACCDeviceTypeClause>)) !=
ExistingClauses.rend()) {
// At this point, we know that we have a region between two device_types,
// as specified by CurDevTypeItr and PrevDeviceTypeItr.

auto CurClauseKindItr = std::find_if(PrevDeviceTypeItr, CurDevTypeItr,
llvm::IsaPred<TheClauseTy>);

// There are no clauses of the current kind between these device_types, so
// continue.
if (CurClauseKindItr == CurDevTypeItr)
continue;

// At this point, we know that this device_type region has a collapse. So
// diagnose if the two device_types have any overlap in their
// architectures.
const auto &CurDeviceTypeClause =
cast<OpenACCDeviceTypeClause>(**CurDevTypeItr);

for (const DeviceTypeArgument &arg :
ActiveDeviceTypeClause.getArchitectures()) {
for (const DeviceTypeArgument &prevArg :
CurDeviceTypeClause.getArchitectures()) {

// This should catch duplicates * regions, duplicate same-text (thanks
// to identifier equiv.) and case insensitive dupes.
if (arg.getIdentifierInfo() == prevArg.getIdentifierInfo() ||
(arg.getIdentifierInfo() && prevArg.getIdentifierInfo() &&
StringRef{arg.getIdentifierInfo()->getName()}.equals_insensitive(
prevArg.getIdentifierInfo()->getName()))) {
SemaRef.Diag(Clause.getBeginLoc(),
diag::err_acc_clause_conflicts_prev_dev_type)
<< Clause.getClauseKind()
<< (arg.getIdentifierInfo() ? arg.getIdentifierInfo()->getName()
: "*");
// mention the active device type.
SemaRef.Diag(ActiveDeviceTypeClause.getBeginLoc(),
diag::note_acc_device_type_here)
<< diag::ACCDeviceTypeApp::Active;
// mention the previous clause.
SemaRef.Diag((*CurClauseKindItr)->getBeginLoc(),
diag::note_acc_previous_named_clause_here)
<< (*CurClauseKindItr)->getClauseKind();
// mention the previous device type.
SemaRef.Diag(CurDeviceTypeClause.getBeginLoc(),
diag::note_acc_device_type_here)
<< diag::ACCDeviceTypeApp::Applies;
return true;
}
}
}

PrevDeviceTypeItr = CurDevTypeItr;
}
return false;
}

public:
Expand Down
15 changes: 15 additions & 0 deletions clang/test/SemaOpenACC/compute-construct-num_gangs-clause.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,18 @@ void Test() {
#pragma acc loop num_gangs(1)
for(int i = 5; i < 10;++i);
}

void no_dupes_since_last_device_type() {
// expected-error@+4{{OpenACC 'num_gangs' clause applies to 'device_type' 'radeon', which conflicts with previous 'num_gangs' clause}}
// expected-note@+3{{active 'device_type' clause here}}
// expected-note@+2{{previous 'num_gangs' clause is here}}
// expected-note@+1{{which applies to 'device_type' clause here}}
#pragma acc parallel device_type(nvidia, radeon) num_gangs(getS()) device_type(radeon) num_gangs(getS())
;
// expected-error@+4{{OpenACC 'num_gangs' clause applies to 'device_type' 'nvidia', which conflicts with previous 'num_gangs' clause}}
// expected-note@+3{{active 'device_type' clause here}}
// expected-note@+2{{previous 'num_gangs' clause is here}}
// expected-note@+1{{which applies to 'device_type' clause here}}
#pragma acc parallel device_type(nvidia) num_gangs(getS()) device_type(nvidia, radeon) num_gangs(getS())
;
}
15 changes: 15 additions & 0 deletions clang/test/SemaOpenACC/compute-construct-num_workers-clause.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,18 @@ void Test() {
#pragma acc loop num_workers(1)
for(int i = 5; i < 10;++i);
}

void no_dupes_since_last_device_type() {
// expected-error@+4{{OpenACC 'num_workers' clause applies to 'device_type' 'radEon', which conflicts with previous 'num_workers' clause}}
// expected-note@+3{{active 'device_type' clause here}}
// expected-note@+2{{previous 'num_workers' clause is here}}
// expected-note@+1{{which applies to 'device_type' clause here}}
#pragma acc parallel device_type(nvidia, radeon) num_workers(getS()) device_type(radEon) num_workers(getS())
;
// expected-error@+4{{OpenACC 'num_workers' clause applies to 'device_type' 'nvidia', which conflicts with previous 'num_workers' clause}}
// expected-note@+3{{active 'device_type' clause here}}
// expected-note@+2{{previous 'num_workers' clause is here}}
// expected-note@+1{{which applies to 'device_type' clause here}}
#pragma acc parallel device_type(nvidia) num_workers(getS()) device_type(nvidia, radeon) num_workers(getS())
;
}
15 changes: 15 additions & 0 deletions clang/test/SemaOpenACC/compute-construct-vector_length-clause.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,18 @@ void Test() {
#pragma acc loop vector_length(1)
for(int i = 5; i < 10;++i);
}

void no_dupes_since_last_device_type() {
// expected-error@+4{{OpenACC 'vector_length' clause applies to 'device_type' 'radeon', which conflicts with previous 'vector_length' clause}}
// expected-note@+3{{active 'device_type' clause here}}
// expected-note@+2{{previous 'vector_length' clause is here}}
// expected-note@+1{{which applies to 'device_type' clause here}}
#pragma acc parallel device_type(nvidia, radeon) vector_length(getS()) device_type(radeon) vector_length(getS())
;
// expected-error@+4{{OpenACC 'vector_length' clause applies to 'device_type' 'nvidia', which conflicts with previous 'vector_length' clause}}
// expected-note@+3{{active 'device_type' clause here}}
// expected-note@+2{{previous 'vector_length' clause is here}}
// expected-note@+1{{which applies to 'device_type' clause here}}
#pragma acc parallel device_type(nvidia) vector_length(getS()) device_type(nvidia, radeon) vector_length(getS())
;
}
33 changes: 33 additions & 0 deletions clang/test/SemaOpenACC/loop-construct-collapse-clause.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -539,4 +539,37 @@ void no_dupes_since_last_device_type() {
#pragma acc loop collapse(1) device_type(*) collapse(1) device_type(nvidia) collapse(2)
for(unsigned i = 0; i < 5; ++i)
for(unsigned j = 0; j < 5; ++j);

// This one is ok, despite * being the 'all' value.
#pragma acc loop device_type(*) collapse(1) device_type(nvidia) collapse(2)
for(unsigned i = 0; i < 5; ++i)
for(unsigned j = 0; j < 5; ++j);

#pragma acc loop device_type(nvidia) collapse(1) device_type(*) collapse(2)
for(unsigned i = 0; i < 5; ++i)
for(unsigned j = 0; j < 5; ++j);

// expected-error@+4{{OpenACC 'collapse' clause applies to 'device_type' '*', which conflicts with previous 'collapse' clause}}
// expected-note@+3{{active 'device_type' clause here}}
// expected-note@+2{{previous 'collapse' clause is here}}
// expected-note@+1{{which applies to 'device_type' clause here}}
#pragma acc loop device_type(*) collapse(1) device_type(*) collapse(2)
for(unsigned i = 0; i < 5; ++i)
for(unsigned j = 0; j < 5; ++j);

// expected-error@+4{{OpenACC 'collapse' clause applies to 'device_type' 'nvidia', which conflicts with previous 'collapse' clause}}
// expected-note@+3{{active 'device_type' clause here}}
// expected-note@+2{{previous 'collapse' clause is here}}
// expected-note@+1{{which applies to 'device_type' clause here}}
#pragma acc loop device_type(nviDia, radeon) collapse(1) device_type(nvidia) collapse(2)
for(unsigned i = 0; i < 5; ++i)
for(unsigned j = 0; j < 5; ++j);

// expected-error@+4{{OpenACC 'collapse' clause applies to 'device_type' 'radeon', which conflicts with previous 'collapse' clause}}
// expected-note@+3{{active 'device_type' clause here}}
// expected-note@+2{{previous 'collapse' clause is here}}
// expected-note@+1{{which applies to 'device_type' clause here}}
#pragma acc loop device_type(radeon) collapse(1) device_type(nvidia, radeon) collapse(2)
for(unsigned i = 0; i < 5; ++i)
for(unsigned j = 0; j < 5; ++j);
}
16 changes: 16 additions & 0 deletions clang/test/SemaOpenACC/loop-construct-tile-clause.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -419,4 +419,20 @@ void no_dupes_since_last_device_type() {
#pragma acc loop tile(1) device_type(*) tile(1) device_type(nvidia) tile(2)
for(unsigned i = 0; i < 5; ++i)
for(unsigned j = 0; j < 5; ++j);

// expected-error@+4{{OpenACC 'tile' clause applies to 'device_type' 'nvidiA', which conflicts with previous 'tile' clause}}
// expected-note@+3{{active 'device_type' clause here}}
// expected-note@+2{{previous 'tile' clause is here}}
// expected-note@+1{{which applies to 'device_type' clause here}}
#pragma acc loop device_type(nvidia, radeon) tile(1) device_type(nvidiA) tile(2)
for(unsigned i = 0; i < 5; ++i)
for(unsigned j = 0; j < 5; ++j);

// expected-error@+4{{OpenACC 'tile' clause applies to 'device_type' 'radeon', which conflicts with previous 'tile' clause}}
// expected-note@+3{{active 'device_type' clause here}}
// expected-note@+2{{previous 'tile' clause is here}}
// expected-note@+1{{which applies to 'device_type' clause here}}
#pragma acc loop device_type(radeon) tile(1) device_type(nvidia, radeon) tile(2)
for(unsigned i = 0; i < 5; ++i)
for(unsigned j = 0; j < 5; ++j);
}