Skip to content

[clang][OpenMP] Fix/enforce order-concurrent-nestable rules #135463

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
Apr 18, 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
4 changes: 3 additions & 1 deletion clang/include/clang/Basic/OpenMPKinds.h
Original file line number Diff line number Diff line change
Expand Up @@ -411,9 +411,11 @@ bool isOpenMPCapturingDirective(OpenMPDirectiveKind DKind);
/// directive that can be nested within region corresponding to construct
/// on which order clause was specified with concurrent as ordering argument.
/// \param DKind Specified directive.
/// \param LangOpts Used for getting the OpenMP version.
/// \return true - if the above condition is met for this directive
/// otherwise - false.
bool isOpenMPOrderConcurrentNestableDirective(OpenMPDirectiveKind DKind);
bool isOpenMPOrderConcurrentNestableDirective(OpenMPDirectiveKind DKind,
const LangOptions &LangOpts);
}

template <>
Expand Down
17 changes: 14 additions & 3 deletions clang/lib/Basic/OpenMPKinds.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -768,9 +768,20 @@ bool clang::isOpenMPCapturingDirective(OpenMPDirectiveKind DKind) {
}

bool clang::isOpenMPOrderConcurrentNestableDirective(
OpenMPDirectiveKind DKind) {
return DKind == OMPD_atomic || DKind == OMPD_loop || DKind == OMPD_simd ||
DKind == OMPD_parallel || isOpenMPLoopTransformationDirective(DKind);
OpenMPDirectiveKind DKind, const LangOptions &LangOpts) {
// Directives strictly nestable in a construct with order(concurrent) are:
// OpenMP 5.x: loop, parallel, simd, combined directive starting with parallel
// OpenMP 6.0: above plus atomic and all loop-transformation directives

if (DKind == OMPD_loop || DKind == OMPD_parallel || DKind == OMPD_simd ||
isOpenMPCombinedParallelADirective(DKind))
return true;

if (LangOpts.OpenMP >= 60)
return DKind == OMPD_atomic ||
isOpenMPLoopTransformationDirective(DKind);

return false;
}

void clang::getOpenMPCaptureRegions(
Expand Down
57 changes: 21 additions & 36 deletions clang/lib/Sema/SemaOpenMP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4789,26 +4789,12 @@ static bool checkNestingOfRegions(Sema &SemaRef, const DSAStackTy *Stack,
getLeafOrCompositeConstructs(ParentRegion, LeafOrComposite);
OpenMPDirectiveKind EnclosingConstruct = ParentLOC.back();

if (Stack->isParentOrderConcurrent()) {
bool InvalidOrderNesting = false;
if ((SemaRef.LangOpts.OpenMP == 51 || SemaRef.LangOpts.OpenMP == 52) &&
CurrentRegion != OMPD_simd && CurrentRegion != OMPD_loop &&
CurrentRegion != OMPD_parallel &&
!isOpenMPCombinedParallelADirective(CurrentRegion)) {
InvalidOrderNesting = true;
} else if (SemaRef.LangOpts.OpenMP >= 60 &&
!isOpenMPOrderConcurrentNestableDirective(CurrentRegion)) {
// OpenMP 6.0 [12.3 order Clause, Restrictions]
// Only regions that correspond to order-concurrent-nestable constructs
// or order-concurrent-nestable routines may be strictly nested regions
// of regions that correspond to constructs on which the order clause is
// specified with concurrent as the ordering argument.
InvalidOrderNesting = true;
}
if (InvalidOrderNesting) {
SemaRef.Diag(StartLoc, diag::err_omp_prohibited_region_order)
<< getOpenMPDirectiveName(CurrentRegion);
}
if (SemaRef.LangOpts.OpenMP >= 50 && Stack->isParentOrderConcurrent() &&
!isOpenMPOrderConcurrentNestableDirective(CurrentRegion,
SemaRef.LangOpts)) {
SemaRef.Diag(StartLoc, diag::err_omp_prohibited_region_order)
<< getOpenMPDirectiveName(CurrentRegion);
return true;
}
if (isOpenMPSimdDirective(ParentRegion) &&
((SemaRef.LangOpts.OpenMP <= 45 && CurrentRegion != OMPD_ordered) ||
Expand Down Expand Up @@ -7132,7 +7118,7 @@ ExprResult SemaOpenMP::ActOnOpenMPCall(ExprResult Call, Scope *Scope,
if (!CalleeFnDecl)
return Call;

if (getLangOpts().OpenMP >= 51 && getLangOpts().OpenMP < 60 &&
if (getLangOpts().OpenMP >= 50 && getLangOpts().OpenMP <= 60 &&
Copy link
Contributor Author

Choose a reason for hiding this comment

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

OpenMP 5.0 and 6.0 also have the same restrictions as 5.1 and 5.2 w.r.t. OpenMP runtime APIs (e.g., omp_get_thread_num()) inside a construct specified with the order(concurrent) clause.

CalleeFnDecl->getIdentifier() &&
CalleeFnDecl->getName().starts_with_insensitive("omp_")) {
// checking for any calls inside an Order region
Expand Down Expand Up @@ -16373,21 +16359,20 @@ OMPClause *SemaOpenMP::ActOnOpenMPOrderClause(
<< getOpenMPClauseName(OMPC_order);
return nullptr;
}
if (getLangOpts().OpenMP >= 51) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Original implementation did not call setRegionHasOrderConcurrent(true) for OpenMP 5.0, causing Clang to ignore all order-concurrent nestable rules for -fopenmp-version=50.

if (Modifier == OMPC_ORDER_MODIFIER_unknown && MLoc.isValid()) {
Diag(MLoc, diag::err_omp_unexpected_clause_value)
<< getListOfPossibleValues(OMPC_order,
/*First=*/OMPC_ORDER_MODIFIER_unknown + 1,
/*Last=*/OMPC_ORDER_MODIFIER_last)
<< getOpenMPClauseName(OMPC_order);
} else {
DSAStack->setRegionHasOrderConcurrent(/*HasOrderConcurrent=*/true);
if (DSAStack->getCurScope()) {
// mark the current scope with 'order' flag
unsigned existingFlags = DSAStack->getCurScope()->getFlags();
DSAStack->getCurScope()->setFlags(existingFlags |
Scope::OpenMPOrderClauseScope);
}
if (getLangOpts().OpenMP >= 51 && Modifier == OMPC_ORDER_MODIFIER_unknown &&
MLoc.isValid()) {
Diag(MLoc, diag::err_omp_unexpected_clause_value)
<< getListOfPossibleValues(OMPC_order,
/*First=*/OMPC_ORDER_MODIFIER_unknown + 1,
/*Last=*/OMPC_ORDER_MODIFIER_last)
<< getOpenMPClauseName(OMPC_order);
} else if (getLangOpts().OpenMP >= 50) {
DSAStack->setRegionHasOrderConcurrent(/*HasOrderConcurrent=*/true);
if (DSAStack->getCurScope()) {
// mark the current scope with 'order' flag
unsigned existingFlags = DSAStack->getCurScope()->getFlags();
DSAStack->getCurScope()->setFlags(existingFlags |
Scope::OpenMPOrderClauseScope);
}
}
return new (getASTContext()) OMPOrderClause(
Expand Down
34 changes: 23 additions & 11 deletions clang/test/OpenMP/for_order_messages.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,35 +8,47 @@
// RUN: %clang_cc1 -fsyntax-only -fopenmp-simd -fopenmp-version=52 -triple x86_64-unknown-unknown -verify=expected,omp51 %s -Wuninitialized
// RUN: %clang_cc1 -fsyntax-only -fopenmp-simd -fopenmp-version=60 -triple x86_64-unknown-unknown -verify=expected,omp60 %s -Wuninitialized

// Constructs strictly nestable in a construct with order(concurrent) specified vary by OpenMP version:
// OMP5.0,5.1,5.2: loop, parallel, simd, and combined constructs with parallel as the first component.
// OMP6.0: in addition to the ones allowed in OMP5.x, also atomic and all loop-transformation constructs.

extern int omp_get_num_threads (void);

int main(int argc, char **argv) {
int A = 0;
#pragma omp parallel for order(concurrent)
for (int i = 0; i < 10; ++i)
omp_get_num_threads(); // omp51-error {{calls to OpenMP runtime API are not allowed within a region that corresponds to a construct with an order clause that specifies concurrent}}
omp_get_num_threads(); // omp50-error {{calls to OpenMP runtime API are not allowed within a region that corresponds to a construct with an order clause that specifies concurrent}} omp51-error {{calls to OpenMP runtime API are not allowed within a region that corresponds to a construct with an order clause that specifies concurrent}} omp60-error {{calls to OpenMP runtime API are not allowed within a region that corresponds to a construct with an order clause that specifies concurrent}}

#pragma omp parallel for order(reproducible:concurrent) // omp50-error {{expected 'concurrent' in OpenMP clause 'order'}}
for (int i = 0; i < 10; ++i)
omp_get_num_threads(); // omp51-error {{calls to OpenMP runtime API are not allowed within a region that corresponds to a construct with an order clause that specifies concurrent}}
omp_get_num_threads(); // omp51-error {{calls to OpenMP runtime API are not allowed within a region that corresponds to a construct with an order clause that specifies concurrent}} omp60-error {{calls to OpenMP runtime API are not allowed within a region that corresponds to a construct with an order clause that specifies concurrent}}

#pragma omp parallel for order(unconstrained:concurrent) // omp50-error {{expected 'concurrent' in OpenMP clause 'order'}}
for (int i = 0; i < 10; ++i)
omp_get_num_threads(); // omp51-error {{calls to OpenMP runtime API are not allowed within a region that corresponds to a construct with an order clause that specifies concurrent}}
omp_get_num_threads(); // omp51-error {{calls to OpenMP runtime API are not allowed within a region that corresponds to a construct with an order clause that specifies concurrent}} omp60-error {{calls to OpenMP runtime API are not allowed within a region that corresponds to a construct with an order clause that specifies concurrent}}

#pragma omp parallel for order(concurrent)
for (int i = 0; i < 10; ++i) {
for (int j = 0; j < 10; ++j) {
omp_get_num_threads(); // omp51-error {{calls to OpenMP runtime API are not allowed within a region that corresponds to a construct with an order clause that specifies concurrent}}
omp_get_num_threads(); // omp50-error {{calls to OpenMP runtime API are not allowed within a region that corresponds to a construct with an order clause that specifies concurrent}} omp51-error {{calls to OpenMP runtime API are not allowed within a region that corresponds to a construct with an order clause that specifies concurrent}} omp60-error {{calls to OpenMP runtime API are not allowed within a region that corresponds to a construct with an order clause that specifies concurrent}}
}
}

// nested atomic: OK in OMP6.0 but not in OMP5.x
#pragma omp parallel for order(concurrent)
for (int i = 0; i < 10; ++i) {
#pragma omp atomic //omp51-error {{construct 'atomic' not allowed in a region associated with a directive with 'order' clause}}
#pragma omp atomic //omp50-error {{construct 'atomic' not allowed in a region associated with a directive with 'order' clause}} omp51-error {{construct 'atomic' not allowed in a region associated with a directive with 'order' clause}}
A++;
}

// nested loop-transformation construct: OK in OMP6.0 but not in OMP5.x
#pragma omp parallel for order(concurrent)
for (int i = 0; i < 10; ++i) {
#pragma omp unroll //omp50-error {{construct 'unroll' not allowed in a region associated with a directive with 'order' clause}} omp51-error {{construct 'unroll' not allowed in a region associated with a directive with 'order' clause}}
for (int j = 0; j < 10; ++j);
}

#pragma omp parallel for order(reproducible: concurrent) // omp50-error {{expected 'concurrent' in OpenMP clause 'order'}}
for (int i = 0; i < 10; ++i) {
#pragma omp target //omp51-error {{construct 'target' not allowed in a region associated with a directive with 'order' clause}} omp60-error {{construct 'target' not allowed in a region associated with a directive with 'order' clause}}
Expand All @@ -51,47 +63,47 @@ int main(int argc, char **argv) {

#pragma omp loop bind(parallel) order(concurrent)
for (int i = 0; i < 10; ++i) {
#pragma omp parallel for //omp60-error {{construct 'parallel for' not allowed in a region associated with a directive with 'order' clause}}
#pragma omp parallel for
for (int j = 0; j < 10; ++j) {
A += j;
}
}

#pragma omp distribute order(concurrent)
for (int i = 0; i < 10; ++i) {
#pragma omp parallel for simd //omp60-error {{construct 'parallel for simd' not allowed in a region associated with a directive with 'order' clause}}
#pragma omp parallel for simd
for (int j = 0; j < 10; ++j) {
A += j;
}
}

#pragma omp for order(concurrent)
for (int i = 0; i < 10; ++i) {
#pragma omp parallel master //omp60-error {{construct 'parallel master' not allowed in a region associated with a directive with 'order' clause}}
#pragma omp parallel master
for (int j = 0; j < 10; ++j) {
A += j;
}
}

#pragma omp for order(concurrent)
for (int i = 0; i < 10; ++i) {
#pragma omp parallel master taskloop //omp60-error {{construct 'parallel master taskloop' not allowed in a region associated with a directive with 'order' clause}}
#pragma omp parallel master taskloop
for (int j = 0; j < 10; ++j) {
A += j;
}
}

#pragma omp for order(concurrent)
for (int i = 0; i < 10; ++i) {
#pragma omp parallel master taskloop simd //omp60-error {{construct 'parallel master taskloop simd' not allowed in a region associated with a directive with 'order' clause}}
#pragma omp parallel master taskloop simd
for (int j = 0; j < 10; ++j) {
A += j;
}
}

#pragma omp for order(concurrent)
for (int i = 0; i < 10; ++i) {
#pragma omp parallel sections //omp60-error {{construct 'parallel sections' not allowed in a region associated with a directive with 'order' clause}}
#pragma omp parallel sections
{
A++;
}
Expand Down