[OpenMP][Clang] Parsing support for num_teams lower bound (#180608)

According to OpenMP 5.2 the num_teams clause should support a
lower-bound as modifier for its argument. This PR adds Parsing support
for the lower bound in num_teams clause.
This commit is contained in:
ykhatav 2026-02-25 13:39:45 -05:00 committed by GitHub
parent b8743de6c5
commit 5524ce826d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 353 additions and 24 deletions

View File

@ -12622,6 +12622,10 @@ def err_omp_transparent_invalid_value : Error<"invalid value for transparent cla
" expected one of: omp_not_impex, omp_import, omp_export, omp_impex">;
def err_omp_transparent_invalid_type : Error<
"transparent clause cannot be applied to type: %0">;
def err_omp_num_teams_multi_expr_not_allowed
: Error<"only two expressions allowed in 'num_teams' clause">;
def err_omp_num_teams_lower_bound_larger
: Error<"lower bound is greater than upper bound in 'num_teams' clause">;
} // end of OpenMP category
let CategoryName = "Related Result Type Issue" in {

View File

@ -2290,8 +2290,20 @@ void OMPClausePrinter::VisitOMPDeviceClause(OMPDeviceClause *Node) {
void OMPClausePrinter::VisitOMPNumTeamsClause(OMPNumTeamsClause *Node) {
if (!Node->varlist_empty()) {
OS << "num_teams";
VisitOMPClauseList(Node, '(');
OS << "num_teams(";
// Handle lower-bound:upper-bound syntax when there are exactly 2
// expressions
if (Node->varlist_size() == 2) {
llvm::interleave(
Node->varlist(), OS,
[&](const auto *Expr) { Expr->printPretty(OS, nullptr, Policy, 0); },
":");
} else {
// For single expression or other cases, use comma-separated list
llvm::interleaveComma(Node->varlist(), OS, [&](const auto *Expr) {
Expr->printPretty(OS, nullptr, Policy, 0);
});
}
OS << ")";
}
}

View File

@ -5080,6 +5080,62 @@ bool Parser::ParseOpenMPVarList(OpenMPDirectiveKind DKind,
Diag(Tok, diag::err_modifier_expected_colon) << "fallback";
}
}
} // Handle num_teams clause with optional lower-bound:upper-bound syntax
if (Kind == OMPC_num_teams && !Tok.is(tok::r_paren) &&
!Tok.is(tok::annot_pragma_openmp_end)) {
ExprResult FirstExpr = ParseAssignmentExpression();
if (FirstExpr.isInvalid()) {
SkipUntil(tok::r_paren, tok::annot_pragma_openmp_end, StopBeforeMatch);
Data.RLoc = Tok.getLocation();
if (!T.consumeClose())
Data.RLoc = T.getCloseLocation();
return true;
}
if (Tok.is(tok::colon)) {
// Lower-bound:upper-bound syntax
ConsumeToken();
ExprResult UpperBound = ParseAssignmentExpression();
if (UpperBound.isInvalid()) {
SkipUntil(tok::r_paren, tok::annot_pragma_openmp_end, StopBeforeMatch);
Data.RLoc = Tok.getLocation();
if (!T.consumeClose())
Data.RLoc = T.getCloseLocation();
return true;
}
Vars.push_back(FirstExpr.get()); // lower-bound
Vars.push_back(UpperBound.get()); // upper-bound
Data.RLoc = Tok.getLocation();
if (!T.consumeClose())
Data.RLoc = T.getCloseLocation();
return false; // Success
}
if (Tok.is(tok::comma)) {
Vars.push_back(FirstExpr.get());
while (Tok.is(tok::comma)) {
ConsumeToken();
ExprResult NextExpr = ParseAssignmentExpression();
if (NextExpr.isUsable()) {
Vars.push_back(NextExpr.get());
} else {
SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end,
StopBeforeMatch);
break;
}
}
Data.RLoc = Tok.getLocation();
bool HadError = T.consumeClose();
if (!HadError)
Data.RLoc = T.getCloseLocation();
return HadError;
}
// Single value - parse closing paren
Vars.push_back(FirstExpr.get());
Data.RLoc = Tok.getLocation();
if (!T.consumeClose())
Data.RLoc = T.getCloseLocation();
return false; // Success
}
bool IsComma =

View File

@ -13508,7 +13508,8 @@ StmtResult SemaOpenMP::ActOnOpenMPTeamsDirective(ArrayRef<OMPClause *> Clauses,
return StmtError();
if (!checkNumExprsInClause<OMPNumTeamsClause>(
*this, Clauses, /*MaxNum=*/1, diag::err_omp_multi_expr_not_allowed) ||
*this, Clauses, /*MaxNum=*/2,
diag::err_omp_num_teams_multi_expr_not_allowed) ||
!checkNumExprsInClause<OMPThreadLimitClause>(
*this, Clauses, /*MaxNum=*/1, diag::err_omp_multi_expr_not_allowed))
return StmtError();
@ -14287,16 +14288,20 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetTeamsDirective(
return StmtError();
}
unsigned ClauseMaxNumExprs = HasBareClause ? 3 : 1;
unsigned DiagNo = HasBareClause
? diag::err_ompx_more_than_three_expr_not_allowed
: diag::err_omp_multi_expr_not_allowed;
if (!checkNumExprsInClause<OMPNumTeamsClause>(*this, Clauses,
ClauseMaxNumExprs, DiagNo) ||
!checkNumExprsInClause<OMPThreadLimitClause>(*this, Clauses,
ClauseMaxNumExprs, DiagNo))
return StmtError();
unsigned ClauseMaxNumExprs = HasBareClause ? 3 : 2;
unsigned NumTeamsDiag = HasBareClause
? diag::err_ompx_more_than_three_expr_not_allowed
: diag::err_omp_num_teams_multi_expr_not_allowed;
unsigned ThreadLimitDiag =
HasBareClause ? diag::err_ompx_more_than_three_expr_not_allowed
: diag::err_omp_multi_expr_not_allowed;
if (!checkNumExprsInClause<OMPNumTeamsClause>(
*this, Clauses, ClauseMaxNumExprs, NumTeamsDiag) ||
!checkNumExprsInClause<OMPThreadLimitClause>(
*this, Clauses, ClauseMaxNumExprs, ThreadLimitDiag)) {
return StmtError();
}
return OMPTargetTeamsDirective::Create(getASTContext(), StartLoc, EndLoc,
Clauses, AStmt);
}
@ -14308,7 +14313,8 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetTeamsDistributeDirective(
return StmtError();
if (!checkNumExprsInClause<OMPNumTeamsClause>(
*this, Clauses, /*MaxNum=*/1, diag::err_omp_multi_expr_not_allowed) ||
*this, Clauses, /*MaxNum=*/2,
diag::err_omp_num_teams_multi_expr_not_allowed) ||
!checkNumExprsInClause<OMPThreadLimitClause>(
*this, Clauses, /*MaxNum=*/1, diag::err_omp_multi_expr_not_allowed))
return StmtError();
@ -14340,7 +14346,8 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetTeamsDistributeParallelForDirective(
return StmtError();
if (!checkNumExprsInClause<OMPNumTeamsClause>(
*this, Clauses, /*MaxNum=*/1, diag::err_omp_multi_expr_not_allowed) ||
*this, Clauses, /*MaxNum=*/2,
diag::err_omp_num_teams_multi_expr_not_allowed) ||
!checkNumExprsInClause<OMPThreadLimitClause>(
*this, Clauses, /*MaxNum=*/1, diag::err_omp_multi_expr_not_allowed))
return StmtError();
@ -14373,7 +14380,8 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetTeamsDistributeParallelForSimdDirective(
return StmtError();
if (!checkNumExprsInClause<OMPNumTeamsClause>(
*this, Clauses, /*MaxNum=*/1, diag::err_omp_multi_expr_not_allowed) ||
*this, Clauses, /*MaxNum=*/2,
diag::err_omp_num_teams_multi_expr_not_allowed) ||
!checkNumExprsInClause<OMPThreadLimitClause>(
*this, Clauses, /*MaxNum=*/1, diag::err_omp_multi_expr_not_allowed))
return StmtError();
@ -14409,7 +14417,8 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetTeamsDistributeSimdDirective(
return StmtError();
if (!checkNumExprsInClause<OMPNumTeamsClause>(
*this, Clauses, /*MaxNum=*/1, diag::err_omp_multi_expr_not_allowed) ||
*this, Clauses, /*MaxNum=*/2,
diag::err_omp_num_teams_multi_expr_not_allowed) ||
!checkNumExprsInClause<OMPThreadLimitClause>(
*this, Clauses, /*MaxNum=*/1, diag::err_omp_multi_expr_not_allowed))
return StmtError();
@ -23880,6 +23889,31 @@ OMPClause *SemaOpenMP::ActOnOpenMPNumTeamsClause(ArrayRef<Expr *> VarList,
return nullptr;
}
// OpenMP 5.2: Validate lower-bound ≤ upper-bound constraint
if (VarList.size() == 2) {
Expr *LowerBound = VarList[0];
Expr *UpperBound = VarList[1];
// Check if both are compile-time constants for validation
if (!LowerBound->isValueDependent() && !UpperBound->isValueDependent() &&
LowerBound->isIntegerConstantExpr(getASTContext()) &&
UpperBound->isIntegerConstantExpr(getASTContext())) {
// Get the actual constant values
llvm::APSInt LowerVal =
LowerBound->EvaluateKnownConstInt(getASTContext());
llvm::APSInt UpperVal =
UpperBound->EvaluateKnownConstInt(getASTContext());
if (LowerVal > UpperVal) {
Diag(LowerBound->getExprLoc(),
diag::err_omp_num_teams_lower_bound_larger)
<< LowerBound->getSourceRange() << UpperBound->getSourceRange();
return nullptr;
}
}
}
OpenMPDirectiveKind DKind = DSAStack->getCurrentDirective();
OpenMPDirectiveKind CaptureRegion = getOpenMPCaptureRegionForClause(
DKind, OMPC_num_teams, getLangOpts().OpenMP);

View File

@ -0,0 +1,103 @@
// RUN: %clang_cc1 -verify -fopenmp -ast-print %s | FileCheck %s
// RUN: %clang_cc1 -fopenmp -x c++ -std=c++11 -emit-pch -o %t %s
// RUN: %clang_cc1 -fopenmp -std=c++11 -include-pch %t -verify %s -ast-print | FileCheck %s
// expected-no-diagnostics
#ifndef HEADER
#define HEADER
void foo();
// CHECK-LABEL: void test_lower_upper_bound()
void test_lower_upper_bound() {
int lower = 2, upper = 8;
// CHECK: #pragma omp teams num_teams(2:8)
#pragma omp teams num_teams(2:8)
{ foo(); }
// CHECK: #pragma omp teams num_teams(lower:upper)
#pragma omp teams num_teams(lower:upper)
{ foo(); }
// CHECK: #pragma omp target teams num_teams(1:10)
#pragma omp target teams num_teams(1:10)
{ foo(); }
// CHECK: #pragma omp target teams distribute num_teams(3:6)
#pragma omp target teams distribute num_teams(3:6)
for (int i = 0; i < 100; ++i) { }
}
// CHECK-LABEL: void test_various_directives()
void test_various_directives() {
int lb = 4, ub = 12;
// CHECK: #pragma omp target teams distribute parallel for num_teams(lb:ub)
#pragma omp target teams distribute parallel for num_teams(lb:ub)
for (int i = 0; i < 100; ++i) { }
// CHECK: #pragma omp target teams distribute parallel for simd num_teams(2:16)
#pragma omp target teams distribute parallel for simd num_teams(2:16)
for (int i = 0; i < 100; ++i) { }
// CHECK: #pragma omp target teams distribute simd num_teams(1:8)
#pragma omp target teams distribute simd num_teams(1:8)
for (int i = 0; i < 100; ++i) { }
}
// CHECK-LABEL: void test_nested_expressions()
void test_nested_expressions() {
int arr[10][10];
int x = 5, y = 10;
// CHECK: #pragma omp teams num_teams(arr[0][0]:arr[1][1])
#pragma omp teams num_teams(arr[0][0]:arr[1][1])
{ foo(); }
// CHECK: #pragma omp teams num_teams(arr[x][0]:arr[y][1])
#pragma omp teams num_teams(arr[x][0]:arr[y][1])
{ foo(); }
// CHECK: #pragma omp teams num_teams((x + 1):(y - 1))
#pragma omp teams num_teams((x + 1):(y - 1))
{ foo(); }
}
// CHECK-LABEL: void test_multi_level_matching_delimiters()
void test_multi_level_matching_delimiters() {
int arr[10][10];
int x = 5, y = 10;
// CHECK: #pragma omp teams num_teams(((x + 1) * 2):((y - 1) * 3))
#pragma omp teams num_teams(((x + 1) * 2):((y - 1) * 3))
{ foo(); }
// CHECK: #pragma omp teams num_teams(arr[arr[0][0]][0]:arr[arr[1][1]][1])
#pragma omp teams num_teams(arr[arr[0][0]][0]:arr[arr[1][1]][1])
{ foo(); }
// CHECK: #pragma omp teams num_teams((arr[x][y] + 1):(arr[y][x] - 1))
#pragma omp teams num_teams((arr[x][y] + 1):(arr[y][x] - 1))
{ foo(); }
// CHECK: #pragma omp teams num_teams((x + (y * 2)):((x * 2) + y))
#pragma omp teams num_teams((x + (y * 2)):((x * 2) + y))
{ foo(); }
// CHECK: #pragma omp teams num_teams((arr[0][1] + arr[2][3]):(arr[4][5] + arr[6][7]))
#pragma omp teams num_teams((arr[0][1] + arr[2][3]):(arr[4][5] + arr[6][7]))
{ foo(); }
}
// Template tests
// CHECK-LABEL: template <typename T> void test_template_type(T lower, T upper)
template<typename T>
void test_template_type(T lower, T upper) {
// CHECK: #pragma omp teams num_teams(lower:upper)
#pragma omp teams num_teams(lower:upper)
{}
}
#endif

View File

@ -95,7 +95,7 @@ T tmain(T argc, T *argv) {
// CHECK-NEXT: foo()
// CHECK-NEXT: #pragma omp target teams allocate(my_allocator: f) reduction(^: e,f) reduction(&&: g) uses_allocators(my_allocator(traits))
// CHECK-NEXT: foo()
// CHECK-NEXT: #pragma omp target teams ompx_bare num_teams(1,1,1) thread_limit(d * 1,d * 1,d * 1)
// CHECK-NEXT: #pragma omp target teams ompx_bare num_teams(1, 1, 1) thread_limit(d * 1,d * 1,d * 1)
// CHECK-NEXT: foo();
enum Enum { };
@ -116,7 +116,7 @@ int main (int argc, char **argv) {
a=3;
// CHECK-NEXT: a = 3;
#pragma omp target teams ompx_bare num_teams(1, 2, 3) thread_limit(2, 4, 6)
// CHECK-NEXT: #pragma omp target teams ompx_bare num_teams(1,2,3) thread_limit(2,4,6)
// CHECK-NEXT: #pragma omp target teams ompx_bare num_teams(1, 2, 3) thread_limit(2,4,6)
a=4;
// CHECK-NEXT: a = 4;
#pragma omp target teams default(none), private(argc,b) num_teams(f) firstprivate(argv) reduction(| : c, d) reduction(* : e) thread_limit(f+g)

View File

@ -44,7 +44,7 @@ T tmain(T argc) {
#pragma omp target teams distribute num_teams(3.14) // expected-error 2 {{expression must have integral or unscoped enumeration type, not 'double'}}
for (int i=0; i<100; i++) foo();
#pragma omp target teams distribute num_teams(1, 2, 3) // expected-error {{only one expression allowed in 'num_teams' clause}}
#pragma omp target teams distribute num_teams(1, 2, 3) // expected-error {{only two expressions allowed in 'num_teams' clause}}
for (int i=0; i<100; i++) foo();
#pragma omp target teams distribute thread_limit(1, 2, 3) // expected-error {{only one expression allowed in 'thread_limit' clause}}
@ -97,7 +97,7 @@ int main(int argc, char **argv) {
#pragma omp target teams distribute num_teams (3.14) // expected-error {{expression must have integral or unscoped enumeration type, not 'double'}}
for (int i=0; i<100; i++) foo();
#pragma omp target teams distribute num_teams(1, 2, 3) // expected-error {{only one expression allowed in 'num_teams' clause}}
#pragma omp target teams distribute num_teams(1, 2, 3) // expected-error {{only two expressions allowed in 'num_teams' clause}}
for (int i=0; i<100; i++) foo();
#pragma omp target teams distribute thread_limit(1, 2, 3) // expected-error {{only one expression allowed in 'thread_limit' clause}}

View File

@ -43,7 +43,7 @@ T tmain(T argc) {
for (int i=0; i<100; i++) foo();
#pragma omp target teams distribute parallel for num_teams(3.14) // expected-error 2 {{expression must have integral or unscoped enumeration type, not 'double'}}
for (int i=0; i<100; i++) foo();
#pragma omp target teams distribute parallel for num_teams(1, 2, 3) // expected-error {{only one expression allowed in 'num_teams' clause}}
#pragma omp target teams distribute parallel for num_teams(1, 2, 3) // expected-error {{only two expressions allowed in 'num_teams' clause}}
for (int i=0; i<100; i++) foo();
#pragma omp target teams distribute parallel for thread_limit(1, 2, 3) // expected-error {{only one expression allowed in 'thread_limit' clause}}
for (int i=0; i<100; i++) foo();
@ -89,7 +89,7 @@ int main(int argc, char **argv) {
#pragma omp target teams distribute parallel for num_teams (3.14) // expected-error {{expression must have integral or unscoped enumeration type, not 'double'}}
for (int i=0; i<100; i++) foo();
#pragma omp target teams distribute parallel for num_teams(1, 2, 3) // expected-error {{only one expression allowed in 'num_teams' clause}}
#pragma omp target teams distribute parallel for num_teams(1, 2, 3) // expected-error {{only two expressions allowed in 'num_teams' clause}}
for (int i=0; i<100; i++) foo();
#pragma omp target teams distribute parallel for thread_limit(1, 2, 3) // expected-error {{only one expression allowed in 'thread_limit' clause}}

View File

@ -58,7 +58,7 @@ T tmain(T argc) {
#pragma omp teams num_teams(3.14) // expected-error 2 {{expression must have integral or unscoped enumeration type, not 'double'}}
foo();
#pragma omp target
#pragma omp teams num_teams (1, 2, 3) // expected-error {{only one expression allowed in 'num_teams' clause}}
#pragma omp teams num_teams (1, 2, 3) // expected-error {{only two expressions allowed in 'num_teams' clause}}
foo();
#pragma omp target
#pragma omp teams thread_limit(1, 2, 3) // expected-error {{only one expression allowed in 'thread_limit' clause}}
@ -118,7 +118,7 @@ int main(int argc, char **argv) {
foo();
#pragma omp target
#pragma omp teams num_teams (1, 2, 3) // expected-error {{only one expression allowed in 'num_teams' clause}}
#pragma omp teams num_teams (1, 2, 3) // expected-error {{only two expressions allowed in 'num_teams' clause}}
foo();
#pragma omp target
@ -127,3 +127,123 @@ int main(int argc, char **argv) {
return tmain<int, 10>(argc); // expected-note {{in instantiation of function template specialization 'tmain<int, 10>' requested here}}
}
// Test invalid syntax cases for num_teams lower-bound:upper-bound
void test_invalid_syntax() {
int a = 1, b = 2, c = 3;
// expected-error@+1 {{only two expressions allowed in 'num_teams' clause}}
#pragma omp teams num_teams(a, b, c)
{ }
// expected-error@+1 {{lower bound is greater than upper bound in 'num_teams' clause}}
#pragma omp teams num_teams(10:5)
{ }
// expected-error@+1 {{only two expressions allowed in 'num_teams' clause}}
#pragma omp target teams num_teams(a, b, c)
{ }
// expected-error@+1 {{lower bound is greater than upper bound in 'num_teams' clause}}
#pragma omp target teams num_teams(8:3)
{ }
// expected-error@+1 {{only two expressions allowed in 'num_teams' clause}}
#pragma omp target teams distribute num_teams(a, b, c)
for (int i = 0; i < 100; ++i) { }
// expected-error@+1 {{lower bound is greater than upper bound in 'num_teams' clause}}
#pragma omp target teams distribute num_teams(15:7)
for (int i = 0; i < 100; ++i) { }
// expected-error@+1 {{only two expressions allowed in 'num_teams' clause}}
#pragma omp target teams distribute parallel for num_teams(a, b, c)
for (int i = 0; i < 100; ++i) { }
// expected-error@+1 {{lower bound is greater than upper bound in 'num_teams' clause}}
#pragma omp target teams distribute parallel for num_teams(12:4)
for (int i = 0; i < 100; ++i) { }
// Test target teams distribute parallel for simd directive
// expected-error@+1 {{only two expressions allowed in 'num_teams' clause}}
#pragma omp target teams distribute parallel for simd num_teams(a, b, c)
for (int i = 0; i < 100; ++i) { }
// expected-error@+1 {{lower bound is greater than upper bound in 'num_teams' clause}}
#pragma omp target teams distribute parallel for simd num_teams(20:6)
for (int i = 0; i < 100; ++i) { }
// expected-error@+1 {{only two expressions allowed in 'num_teams' clause}}
#pragma omp target teams distribute simd num_teams(a, b, c)
for (int i = 0; i < 100; ++i) { }
// expected-error@+1 {{lower bound is greater than upper bound in 'num_teams' clause}}
#pragma omp target teams distribute simd num_teams(9:2)
for (int i = 0; i < 100; ++i) { }
}
// Test non-matching parentheses and brackets
void test_non_matching_delimiters() {
int arr[10];
int x = 5;
// expected-error@+4 {{expected ')'}}
// expected-error@+3 {{expected ')'}}
// expected-note@+2 {{to match this '('}}
// expected-note@+1 {{to match this '('}}
#pragma omp teams num_teams((x + 1:10)
{ }
// expected-error@+2 {{expected ']'}}
// expected-note@+1 {{to match this '['}}
#pragma omp teams num_teams(arr[0:10)
{ }
// expected-error@+2 {{expected ')'}}
// expected-note@+1 {{to match this '('}}
#pragma omp teams num_teams(x:((10 + 1))
{ }
}
// Test multi-level non-matching parentheses and brackets
void test_multi_level_non_matching_delimiters() {
int arr[10][10];
int x = 5, y = 10;
// expected-error@+4 {{expected ')'}}
// expected-note@+3 {{to match this '('}}
// expected-error@+2 {{expected ')'}}
// expected-note@+1 {{to match this '('}}
#pragma omp teams num_teams(((x + 1) * 2:10)
{ }
// expected-error@+4 {{expected ')'}}
// expected-note@+3 {{to match this '('}}
// expected-error@+2 {{expected ')'}}
// expected-note@+1 {{to match this '('}}
#pragma omp teams num_teams((x + (y - 1):10)
{ }
// expected-error@+2 {{expected ']'}}
// expected-note@+1 {{to match this '['}}
#pragma omp teams num_teams((arr[0 + 1):10)
{ }
// expected-error@+2 {{expected ')'}}
// expected-note@+1 {{to match this '('}}
#pragma omp teams num_teams(x:((y + 1) * 2)
{ }
// expected-error@+4 {{expected ']'}}
// expected-note@+3 {{to match this '['}}
// expected-error@+2 {{expected ']'}}
// expected-note@+1 {{to match this '['}}
#pragma omp teams num_teams(arr[0][1:arr[2][3)
{ }
}
template<int Lower, int Upper>
void test_template_type_constants() {
// expected-error@+1 {{lower bound is greater than upper bound in 'num_teams' clause}}
#pragma omp teams num_teams(Lower:Upper)
{}
}
void instantiate_template_invalid() {
test_template_type_constants<10, 5>(); // expected-note {{in instantiation of function template specialization 'test_template_type_constants<10, 5>' requested here}}
}