https://github.com/zahiraam updated https://github.com/llvm/llvm-project/pull/192455
>From 95d0b4b3826a65b11b7f05d48a39b23bd5b5b0a5 Mon Sep 17 00:00:00 2001 From: Zahira Ammarguellat <[email protected]> Date: Mon, 17 Aug 2026 14:21:21 -0700 Subject: [PATCH 1/2] Implement phase 1 of plan --- clang/include/clang/AST/OpenMPClause.h | 13 ++ clang/include/clang/Sema/SemaOpenMP.h | 8 ++ clang/lib/AST/OpenMPClause.cpp | 4 +- clang/lib/Parse/ParseOpenMP.cpp | 91 +++++++++++- clang/lib/Sema/SemaOpenMP.cpp | 9 ++ .../metadirective_user_condition_parse.cpp | 134 ++++++++++++++++++ .../include/llvm/Frontend/OpenMP/OMPContext.h | 4 + 7 files changed, 260 insertions(+), 3 deletions(-) create mode 100644 clang/test/OpenMP/metadirective_user_condition_parse.cpp diff --git a/clang/include/clang/AST/OpenMPClause.h b/clang/include/clang/AST/OpenMPClause.h index 9e9295e1a0c54..c1e69b6ef3468 100644 --- a/clang/include/clang/AST/OpenMPClause.h +++ b/clang/include/clang/AST/OpenMPClause.h @@ -10019,6 +10019,19 @@ class OMPTraitInfo { return false; } + /// Check if this trait info contains any user conditions. + bool hasUserCondition() const { + for (const OMPTraitSet &Set : Sets) { + if (Set.Kind != llvm::omp::TraitSet::user) + continue; + for (const OMPTraitSelector &Selector : Set.Selectors) { + if (Selector.Kind == llvm::omp::TraitSelector::user_condition) + return true; + } + } + return false; + } + /// Print a human readable representation into \p OS. void print(llvm::raw_ostream &OS, const PrintingPolicy &Policy) const; }; diff --git a/clang/include/clang/Sema/SemaOpenMP.h b/clang/include/clang/Sema/SemaOpenMP.h index 361473140e236..e6cd31b9c456f 100644 --- a/clang/include/clang/Sema/SemaOpenMP.h +++ b/clang/include/clang/Sema/SemaOpenMP.h @@ -217,6 +217,14 @@ class SemaOpenMP : public SemaBase { Stmt *AStmt, SourceLocation StartLoc, SourceLocation EndLoc); + /// Called for metadirectives with user conditions that may require runtime + /// selection. + StmtResult ActOnOpenMPMetaDirective( + SourceLocation StartLoc, SourceLocation EndLoc, + ArrayRef<OMPTraitInfo *> TraitInfos, + ArrayRef<OpenMPClauseKind> ClauseKinds, + ArrayRef<OpenMPDirectiveKind> DirectiveKinds, Stmt *AssociatedStmt); + // OpenMP directives and clauses. /// Called on correct id-expression from the '#pragma omp /// threadprivate'. diff --git a/clang/lib/AST/OpenMPClause.cpp b/clang/lib/AST/OpenMPClause.cpp index cac701994def8..ce1d624249de8 100644 --- a/clang/lib/AST/OpenMPClause.cpp +++ b/clang/lib/AST/OpenMPClause.cpp @@ -3086,8 +3086,10 @@ void OMPTraitInfo::getAsVariantMatchInfo(ASTContext &ASTCtx, VMI.addTrait(CondVal->isZero() ? TraitProperty::user_condition_false : TraitProperty::user_condition_true, "<condition>"); - else + else { VMI.addTrait(TraitProperty::user_condition_false, "<condition>"); + VMI.HasNonConstantUserCondition = true; + } continue; } diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp index 30b6c64e69f4c..96a877a444351 100644 --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -108,6 +108,22 @@ static OpenMPDirectiveKind parseOpenMPDirectiveKind(Parser &P) { return checkOpenMPDirectiveName(P, Loc, S->Value, Concat); } +/// Skip tokens until reaching the matching closing parenthesis. +/// Handles nested parentheses correctly. +static void skipToMatchingParen(Parser &P) { + int ParenDepth = 0; + while ((P.getCurToken().isNot(tok::r_paren) || ParenDepth != 0) && + P.getCurToken().isNot(tok::annot_pragma_openmp_end) && + P.getCurToken().isNot(tok::eof)) { + if (P.getCurToken().is(tok::l_paren)) + ParenDepth++; + if (P.getCurToken().is(tok::r_paren) && ParenDepth > 0) + ParenDepth--; + if (ParenDepth > 0 || P.getCurToken().isNot(tok::r_paren)) + P.ConsumeAnyToken(); + } +} + static DeclarationName parseOpenMPReductionId(Parser &P) { Token Tok = P.getCurToken(); Sema &Actions = P.getActions(); @@ -2602,10 +2618,11 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective( case OMPD_metadirective: { ConsumeToken(); SmallVector<VariantMatchInfo, 4> VMIs; + SmallVector<OMPTraitInfo *, 4> TraitInfos; // First iteration of parsing all clauses of metadirective. - // This iteration only parses and collects all context selector ignoring the - // associated directives. + // This iteration only parses and collects all context selectors ignoring + // the associated directives. TentativeParsingAction TPA(*this); ASTContext &ASTContext = Actions.getASTContext(); @@ -2681,6 +2698,7 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective( TI.getAsVariantMatchInfo(ASTContext, VMI); VMIs.push_back(VMI); + TraitInfos.push_back(&TI); } TPA.Revert(); @@ -2700,6 +2718,75 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective( // A single match is returned for OpenMP 5.0 int BestIdx = getBestVariantMatchForContext(VMIs, OMPCtx); + // Check if we have user conditions with non-constant expressions that + // require runtime selection. + bool HasUserCondition = false; + for (const VariantMatchInfo &VMI : VMIs) { + if (VMI.HasNonConstantUserCondition) { + HasUserCondition = true; + break; + } + } + + // If we have user conditions that couldn't be resolved at compile time, + // parse all variants and the body. + if (HasUserCondition) { + SmallVector<OpenMPDirectiveKind, 4> DirectiveKinds; + SmallVector<OpenMPClauseKind, 4> ClauseKinds; + + // TODO: Phase 2 - Parse directive clauses and store them. + // For now in Phase 1, we only extract directive kinds. + // Sema will extract conditions from TraitInfos. + + BalancedDelimiterTracker T(*this, tok::l_paren, + tok::annot_pragma_openmp_end); + while (Tok.isNot(tok::annot_pragma_openmp_end)) { + OpenMPClauseKind CKind = + Tok.isAnnotation() ? OMPC_unknown + : getOpenMPClauseKind(PP.getSpelling(Tok)); + SourceLocation ClauseLoc = ConsumeToken(); + + // Parse '('. + T.consumeOpen(); + + if (CKind == OMPC_when) { + OMPTraitInfo &TI = Actions.getASTContext().getNewOMPTraitInfo(); + parseOMPContextSelectors(ClauseLoc, TI); + + // Parse ':'. + if (Tok.is(tok::colon)) + ConsumeAnyToken(); + } + + // Parse directive kind only for now. + OpenMPDirectiveKind DKind = OMPD_unknown; + if (!Tok.is(tok::r_paren)) { + DKind = parseOpenMPDirectiveKind(*this); + skipToMatchingParen(*this); + } + + // Parse ')'. + if (Tok.is(tok::r_paren)) + T.consumeClose(); + + DirectiveKinds.push_back(DKind); + ClauseKinds.push_back(CKind); + } + + SourceLocation EndLoc = Tok.getLocation(); + ConsumeAnnotationToken(); + + // Parse the body statement. + StmtResult AssociatedStmt = ParseStatement(); + if (AssociatedStmt.isInvalid()) + return StmtError(); + + // Pass to Sema for Phase 2 processing. + return Actions.OpenMP().ActOnOpenMPMetaDirective( + Loc, EndLoc, TraitInfos, ClauseKinds, DirectiveKinds, + AssociatedStmt.get()); + } + int Idx = 0; // In OpenMP 5.0 metadirective is either replaced by another directive or // ignored. diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp index b39dd853ab378..5bf6ea4128b71 100644 --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -3768,6 +3768,15 @@ StmtResult SemaOpenMP::ActOnOpenMPAssumeDirective(ArrayRef<OMPClause *> Clauses, AStmt); } +StmtResult SemaOpenMP::ActOnOpenMPMetaDirective( + SourceLocation StartLoc, SourceLocation EndLoc, + ArrayRef<OMPTraitInfo *> TraitInfos, ArrayRef<OpenMPClauseKind> ClauseKinds, + ArrayRef<OpenMPDirectiveKind> DirectiveKinds, Stmt *AssociatedStmt) { + // Stub for Phase 1 (Parser) testing. + // Sema will extract conditions from TraitInfos in Phase 2. + return AssociatedStmt; +} + OMPRequiresDecl * SemaOpenMP::CheckOMPRequiresDecl(SourceLocation Loc, ArrayRef<OMPClause *> ClauseList) { diff --git a/clang/test/OpenMP/metadirective_user_condition_parse.cpp b/clang/test/OpenMP/metadirective_user_condition_parse.cpp new file mode 100644 index 0000000000000..7306a5ab107fa --- /dev/null +++ b/clang/test/OpenMP/metadirective_user_condition_parse.cpp @@ -0,0 +1,134 @@ +// RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=52 -std=c++11 \ +// RUN: -fsyntax-only %s + +// expected-no-diagnostics + +void test_runtime_condition(int flag) { +#pragma omp metadirective \ + when(user={condition(flag)}: parallel) \ + otherwise(single) + { + int x = 0; + } +} + +void test_two_conditions(int flag1, int flag2) { +#pragma omp metadirective \ + when(user={condition(flag1)}: parallel) \ + when(user={condition(flag2)}: single) \ + otherwise() + { + int y = 1; + } +} + +void test_complex_condition(int a, int b) { +#pragma omp metadirective \ + when(user={condition(a > b)}: parallel) \ + otherwise(single) + { + int z = 2; + } +} + +void test_logical_condition(bool flag1, bool flag2) { +#pragma omp metadirective \ + when(user={condition(flag1 && flag2)}: parallel) \ + otherwise() + { + int w = 3; + } +} + +void test_multiple_variants(int flag1, int flag2, int flag3) { +#pragma omp metadirective \ + when(user={condition(flag1)}: parallel) \ + when(user={condition(flag2)}: single) \ + when(user={condition(flag3)}: teams) \ + otherwise() + { + int v = 4; + } +} + +void test_otherwise_only() { +#pragma omp metadirective otherwise(parallel) + { + int u = 5; + } +} + +void test_different_directives(int flag) { +#pragma omp metadirective \ + when(user={condition(flag)}: teams) \ + otherwise(task) + { + int t = 6; + } +} + +void test_nested_statement(int flag) { +#pragma omp metadirective \ + when(user={condition(flag)}: parallel) \ + otherwise() + { + for (int i = 0; i < 10; ++i) { + int s = i; + } + } +} + +template <int N> +void test_nontype_template(int flag) { +#pragma omp metadirective \ + when(user={condition(N > 0)}: parallel) \ + otherwise(single) + { + int x = N; + } +} + +template <int Threshold> +void test_threshold_condition(int value) { +#pragma omp metadirective \ + when(user={condition(value > Threshold)}: parallel) \ + otherwise() + { + int y = value; + } +} + +template <bool UseParallel> +void test_bool_template() { +#pragma omp metadirective \ + when(user={condition(UseParallel)}: parallel) \ + otherwise(single) + { + int z = 0; + } +} + +template <typename T> +void test_sizeof_condition(T* ptr) { +#pragma omp metadirective \ + when(user={condition(sizeof(T) > 4)}: parallel) \ + otherwise(single) + { + T val = *ptr; + } +} + +void instantiate_templates() { + int flag = 1; + int value = 10; + int iptr; + double dptr; + + test_nontype_template<5>(flag); + test_nontype_template<-3>(flag); + test_threshold_condition<100>(value); + test_bool_template<true>(); + test_bool_template<false>(); + test_sizeof_condition<int>(&iptr); + test_sizeof_condition<double>(&dptr); +} diff --git a/llvm/include/llvm/Frontend/OpenMP/OMPContext.h b/llvm/include/llvm/Frontend/OpenMP/OMPContext.h index 7849d32665994..7c069721ba677 100644 --- a/llvm/include/llvm/Frontend/OpenMP/OMPContext.h +++ b/llvm/include/llvm/Frontend/OpenMP/OMPContext.h @@ -156,6 +156,10 @@ struct VariantMatchInfo { SmallVector<StringRef, 8> ISATraits; SmallVector<TraitProperty, 8> ConstructTraits; SmallDenseMap<TraitProperty, APInt> ScoreMap; + + /// True if this variant has a user condition that could not be evaluated at + /// compile time (non-constant expression). + bool HasNonConstantUserCondition = false; }; /// The context for a source location is made up of active property traits, >From cc17edd7f94a68a79b1869cc05408f8e10b2168b Mon Sep 17 00:00:00 2001 From: Zahira Ammarguellat <[email protected]> Date: Tue, 18 Aug 2026 06:08:45 -0700 Subject: [PATCH 2/2] Fix format --- clang/include/clang/Sema/SemaOpenMP.h | 11 ++++++----- clang/lib/Parse/ParseOpenMP.cpp | 6 +++--- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/clang/include/clang/Sema/SemaOpenMP.h b/clang/include/clang/Sema/SemaOpenMP.h index e6cd31b9c456f..4298ec7d86db7 100644 --- a/clang/include/clang/Sema/SemaOpenMP.h +++ b/clang/include/clang/Sema/SemaOpenMP.h @@ -219,11 +219,12 @@ class SemaOpenMP : public SemaBase { /// Called for metadirectives with user conditions that may require runtime /// selection. - StmtResult ActOnOpenMPMetaDirective( - SourceLocation StartLoc, SourceLocation EndLoc, - ArrayRef<OMPTraitInfo *> TraitInfos, - ArrayRef<OpenMPClauseKind> ClauseKinds, - ArrayRef<OpenMPDirectiveKind> DirectiveKinds, Stmt *AssociatedStmt); + StmtResult + ActOnOpenMPMetaDirective(SourceLocation StartLoc, SourceLocation EndLoc, + ArrayRef<OMPTraitInfo *> TraitInfos, + ArrayRef<OpenMPClauseKind> ClauseKinds, + ArrayRef<OpenMPDirectiveKind> DirectiveKinds, + Stmt *AssociatedStmt); // OpenMP directives and clauses. /// Called on correct id-expression from the '#pragma omp diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp index 96a877a444351..7dde8b01ecce8 100644 --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -2741,9 +2741,9 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective( BalancedDelimiterTracker T(*this, tok::l_paren, tok::annot_pragma_openmp_end); while (Tok.isNot(tok::annot_pragma_openmp_end)) { - OpenMPClauseKind CKind = - Tok.isAnnotation() ? OMPC_unknown - : getOpenMPClauseKind(PP.getSpelling(Tok)); + OpenMPClauseKind CKind = Tok.isAnnotation() + ? OMPC_unknown + : getOpenMPClauseKind(PP.getSpelling(Tok)); SourceLocation ClauseLoc = ConsumeToken(); // Parse '('. _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
