//===--- ParseOpenMP.cpp - OpenMP directives parsing ----------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// /// \file /// \brief This file implements parsing of all OpenMP directives and clauses. /// //===----------------------------------------------------------------------===// #include "RAIIObjectsForParser.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/StmtOpenMP.h" #include "clang/Parse/ParseDiagnostic.h" #include "clang/Parse/Parser.h" #include "clang/Sema/Scope.h" #include "llvm/ADT/PointerIntPair.h" using namespace clang; //===----------------------------------------------------------------------===// // OpenMP declarative directives. //===----------------------------------------------------------------------===// static OpenMPDirectiveKind ParseOpenMPDirectiveKind(Parser &P) { auto Tok = P.getCurToken(); auto DKind = Tok.isAnnotation() ? OMPD_unknown : getOpenMPDirectiveKind(P.getPreprocessor().getSpelling(Tok)); if (DKind == OMPD_parallel) { Tok = P.getPreprocessor().LookAhead(0); auto SDKind = Tok.isAnnotation() ? OMPD_unknown : getOpenMPDirectiveKind(P.getPreprocessor().getSpelling(Tok)); if (SDKind == OMPD_for) { P.ConsumeToken(); DKind = OMPD_parallel_for; } else if (SDKind == OMPD_sections) { P.ConsumeToken(); DKind = OMPD_parallel_sections; } } return DKind; } /// \brief Parsing of declarative OpenMP directives. /// /// threadprivate-directive: /// annot_pragma_openmp 'threadprivate' simple-variable-list /// Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirective() { assert(Tok.is(tok::annot_pragma_openmp) && "Not an OpenMP directive!"); ParenBraceBracketBalancer BalancerRAIIObj(*this); SourceLocation Loc = ConsumeToken(); SmallVector<Expr *, 5> Identifiers; auto DKind = ParseOpenMPDirectiveKind(*this); switch (DKind) { case OMPD_threadprivate: ConsumeToken(); if (!ParseOpenMPSimpleVarList(OMPD_threadprivate, Identifiers, true)) { // The last seen token is annot_pragma_openmp_end - need to check for // extra tokens. if (Tok.isNot(tok::annot_pragma_openmp_end)) { Diag(Tok, diag::warn_omp_extra_tokens_at_eol) << getOpenMPDirectiveName(OMPD_threadprivate); SkipUntil(tok::annot_pragma_openmp_end, StopBeforeMatch); } // Skip the last annot_pragma_openmp_end. ConsumeToken(); return Actions.ActOnOpenMPThreadprivateDirective(Loc, Identifiers); } break; case OMPD_unknown: Diag(Tok, diag::err_omp_unknown_directive); break; case OMPD_parallel: case OMPD_simd: case OMPD_task: case OMPD_for: case OMPD_sections: case OMPD_section: case OMPD_single: case OMPD_parallel_for: case OMPD_parallel_sections: Diag(Tok, diag::err_omp_unexpected_directive) << getOpenMPDirectiveName(DKind); break; } SkipUntil(tok::annot_pragma_openmp_end); return DeclGroupPtrTy(); } /// \brief Parsing of declarative or executable OpenMP directives. /// /// threadprivate-directive: /// annot_pragma_openmp 'threadprivate' simple-variable-list /// annot_pragma_openmp_end /// /// executable-directive: /// annot_pragma_openmp 'parallel' | 'simd' | 'for' | 'sections' | /// 'section' | 'single' | 'parallel for' | 'parallel sections' {clause} /// annot_pragma_openmp_end /// StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective() { assert(Tok.is(tok::annot_pragma_openmp) && "Not an OpenMP directive!"); ParenBraceBracketBalancer BalancerRAIIObj(*this); SmallVector<Expr *, 5> Identifiers; SmallVector<OMPClause *, 5> Clauses; SmallVector<llvm::PointerIntPair<OMPClause *, 1, bool>, OMPC_unknown + 1> FirstClauses(OMPC_unknown + 1); unsigned ScopeFlags = Scope::FnScope | Scope::DeclScope | Scope::OpenMPDirectiveScope; SourceLocation Loc = ConsumeToken(), EndLoc; auto DKind = ParseOpenMPDirectiveKind(*this); // Name of critical directive. DeclarationNameInfo DirName; StmtResult Directive = StmtError(); switch (DKind) { case OMPD_threadprivate: ConsumeToken(); if (!ParseOpenMPSimpleVarList(OMPD_threadprivate, Identifiers, false)) { // The last seen token is annot_pragma_openmp_end - need to check for // extra tokens. if (Tok.isNot(tok::annot_pragma_openmp_end)) { Diag(Tok, diag::warn_omp_extra_tokens_at_eol) << getOpenMPDirectiveName(OMPD_threadprivate); SkipUntil(tok::annot_pragma_openmp_end, StopBeforeMatch); } DeclGroupPtrTy Res = Actions.ActOnOpenMPThreadprivateDirective(Loc, Identifiers); Directive = Actions.ActOnDeclStmt(Res, Loc, Tok.getLocation()); } SkipUntil(tok::annot_pragma_openmp_end); break; case OMPD_parallel: case OMPD_simd: case OMPD_for: case OMPD_sections: case OMPD_single: case OMPD_section: case OMPD_parallel_for: case OMPD_parallel_sections: { ConsumeToken(); if (isOpenMPLoopDirective(DKind)) ScopeFlags |= Scope::OpenMPLoopDirectiveScope; if (isOpenMPSimdDirective(DKind)) ScopeFlags |= Scope::OpenMPSimdDirectiveScope; ParseScope OMPDirectiveScope(this, ScopeFlags); Actions.StartOpenMPDSABlock(DKind, DirName, Actions.getCurScope(), Loc); while (Tok.isNot(tok::annot_pragma_openmp_end)) { OpenMPClauseKind CKind = Tok.isAnnotation() ? OMPC_unknown : getOpenMPClauseKind(PP.getSpelling(Tok)); OMPClause *Clause = ParseOpenMPClause(DKind, CKind, !FirstClauses[CKind].getInt()); FirstClauses[CKind].setInt(true); if (Clause) { FirstClauses[CKind].setPointer(Clause); Clauses.push_back(Clause); } // Skip ',' if any. if (Tok.is(tok::comma)) ConsumeToken(); } // End location of the directive. EndLoc = Tok.getLocation(); // Consume final annot_pragma_openmp_end. ConsumeToken(); StmtResult AssociatedStmt; bool CreateDirective = true; { // The body is a block scope like in Lambdas and Blocks. Sema::CompoundScopeRAII CompoundScope(Actions); Actions.ActOnOpenMPRegionStart(DKind, getCurScope()); Actions.ActOnStartOfCompoundStmt(); // Parse statement AssociatedStmt = ParseStatement(); Actions.ActOnFinishOfCompoundStmt(); if (!AssociatedStmt.isUsable()) { Actions.ActOnCapturedRegionError(); CreateDirective = false; } else { AssociatedStmt = Actions.ActOnCapturedRegionEnd(AssociatedStmt.get()); CreateDirective = AssociatedStmt.isUsable(); } } if (CreateDirective) Directive = Actions.ActOnOpenMPExecutableDirective( DKind, Clauses, AssociatedStmt.get(), Loc, EndLoc); // Exit scope. Actions.EndOpenMPDSABlock(Directive.get()); OMPDirectiveScope.Exit(); break; } case OMPD_unknown: Diag(Tok, diag::err_omp_unknown_directive); SkipUntil(tok::annot_pragma_openmp_end); break; case OMPD_task: Diag(Tok, diag::err_omp_unexpected_directive) << getOpenMPDirectiveName(DKind); SkipUntil(tok::annot_pragma_openmp_end); break; } return Directive; } /// \brief Parses list of simple variables for '#pragma omp threadprivate' /// directive. /// /// simple-variable-list: /// '(' id-expression {, id-expression} ')' /// bool Parser::ParseOpenMPSimpleVarList(OpenMPDirectiveKind Kind, SmallVectorImpl<Expr *> &VarList, bool AllowScopeSpecifier) { VarList.clear(); // Parse '('. BalancedDelimiterTracker T(*this, tok::l_paren, tok::annot_pragma_openmp_end); if (T.expectAndConsume(diag::err_expected_lparen_after, getOpenMPDirectiveName(Kind))) return true; bool IsCorrect = true; bool NoIdentIsFound = true; // Read tokens while ')' or annot_pragma_openmp_end is not found. while (Tok.isNot(tok::r_paren) && Tok.isNot(tok::annot_pragma_openmp_end)) { CXXScopeSpec SS; SourceLocation TemplateKWLoc; UnqualifiedId Name; // Read var name. Token PrevTok = Tok; NoIdentIsFound = false; if (AllowScopeSpecifier && getLangOpts().CPlusPlus && ParseOptionalCXXScopeSpecifier(SS, ParsedType(), false)) { IsCorrect = false; SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end, StopBeforeMatch); } else if (ParseUnqualifiedId(SS, false, false, false, ParsedType(), TemplateKWLoc, Name)) { IsCorrect = false; SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end, StopBeforeMatch); } else if (Tok.isNot(tok::comma) && Tok.isNot(tok::r_paren) && Tok.isNot(tok::annot_pragma_openmp_end)) { IsCorrect = false; SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end, StopBeforeMatch); Diag(PrevTok.getLocation(), diag::err_expected) << tok::identifier << SourceRange(PrevTok.getLocation(), PrevTokLocation); } else { DeclarationNameInfo NameInfo = Actions.GetNameFromUnqualifiedId(Name); ExprResult Res = Actions.ActOnOpenMPIdExpression(getCurScope(), SS, NameInfo); if (Res.isUsable()) VarList.push_back(Res.get()); } // Consume ','. if (Tok.is(tok::comma)) { ConsumeToken(); } } if (NoIdentIsFound) { Diag(Tok, diag::err_expected) << tok::identifier; IsCorrect = false; } // Parse ')'. IsCorrect = !T.consumeClose() && IsCorrect; return !IsCorrect && VarList.empty(); } /// \brief Parsing of OpenMP clauses. /// /// clause: /// if-clause | num_threads-clause | safelen-clause | default-clause | /// private-clause | firstprivate-clause | shared-clause | linear-clause | /// aligned-clause | collapse-clause | lastprivate-clause | /// reduction-clause | proc_bind-clause | schedule-clause | /// copyin-clause | copyprivate-clause /// OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind DKind, OpenMPClauseKind CKind, bool FirstClause) { OMPClause *Clause = nullptr; bool ErrorFound = false; // Check if clause is allowed for the given directive. if (CKind != OMPC_unknown && !isAllowedClauseForDirective(DKind, CKind)) { Diag(Tok, diag::err_omp_unexpected_clause) << getOpenMPClauseName(CKind) << getOpenMPDirectiveName(DKind); ErrorFound = true; } switch (CKind) { case OMPC_if: case OMPC_num_threads: case OMPC_safelen: case OMPC_collapse: // OpenMP [2.5, Restrictions] // At most one if clause can appear on the directive. // At most one num_threads clause can appear on the directive. // OpenMP [2.8.1, simd construct, Restrictions] // Only one safelen clause can appear on a simd directive. // Only one collapse clause can appear on a simd directive. if (!FirstClause) { Diag(Tok, diag::err_omp_more_one_clause) << getOpenMPDirectiveName(DKind) << getOpenMPClauseName(CKind); } Clause = ParseOpenMPSingleExprClause(CKind); break; case OMPC_default: case OMPC_proc_bind: // OpenMP [2.14.3.1, Restrictions] // Only a single default clause may be specified on a parallel, task or // teams directive. // OpenMP [2.5, parallel Construct, Restrictions] // At most one proc_bind clause can appear on the directive. if (!FirstClause) { Diag(Tok, diag::err_omp_more_one_clause) << getOpenMPDirectiveName(DKind) << getOpenMPClauseName(CKind); } Clause = ParseOpenMPSimpleClause(CKind); break; case OMPC_schedule: // OpenMP [2.7.1, Restrictions, p. 3] // Only one schedule clause can appear on a loop directive. if (!FirstClause) { Diag(Tok, diag::err_omp_more_one_clause) << getOpenMPDirectiveName(DKind) << getOpenMPClauseName(CKind); } Clause = ParseOpenMPSingleExprWithArgClause(CKind); break; case OMPC_ordered: case OMPC_nowait: // OpenMP [2.7.1, Restrictions, p. 9] // Only one ordered clause can appear on a loop directive. // OpenMP [2.7.1, Restrictions, C/C++, p. 4] // Only one nowait clause can appear on a for directive. if (!FirstClause) { Diag(Tok, diag::err_omp_more_one_clause) << getOpenMPDirectiveName(DKind) << getOpenMPClauseName(CKind); } Clause = ParseOpenMPClause(CKind); break; case OMPC_private: case OMPC_firstprivate: case OMPC_lastprivate: case OMPC_shared: case OMPC_reduction: case OMPC_linear: case OMPC_aligned: case OMPC_copyin: case OMPC_copyprivate: Clause = ParseOpenMPVarListClause(CKind); break; case OMPC_unknown: Diag(Tok, diag::warn_omp_extra_tokens_at_eol) << getOpenMPDirectiveName(DKind); SkipUntil(tok::annot_pragma_openmp_end, StopBeforeMatch); break; case OMPC_threadprivate: Diag(Tok, diag::err_omp_unexpected_clause) << getOpenMPClauseName(CKind) << getOpenMPDirectiveName(DKind); SkipUntil(tok::comma, tok::annot_pragma_openmp_end, StopBeforeMatch); break; } return ErrorFound ? nullptr : Clause; } /// \brief Parsing of OpenMP clauses with single expressions like 'if', /// 'collapse', 'safelen', 'num_threads', 'simdlen', 'num_teams' or /// 'thread_limit'. /// /// if-clause: /// 'if' '(' expression ')' /// /// num_threads-clause: /// 'num_threads' '(' expression ')' /// /// safelen-clause: /// 'safelen' '(' expression ')' /// /// collapse-clause: /// 'collapse' '(' expression ')' /// OMPClause *Parser::ParseOpenMPSingleExprClause(OpenMPClauseKind Kind) { SourceLocation Loc = ConsumeToken(); BalancedDelimiterTracker T(*this, tok::l_paren, tok::annot_pragma_openmp_end); if (T.expectAndConsume(diag::err_expected_lparen_after, getOpenMPClauseName(Kind))) return nullptr; ExprResult LHS(ParseCastExpression(false, false, NotTypeCast)); ExprResult Val(ParseRHSOfBinaryExpression(LHS, prec::Conditional)); // Parse ')'. T.consumeClose(); if (Val.isInvalid()) return nullptr; return Actions.ActOnOpenMPSingleExprClause( Kind, Val.get(), Loc, T.getOpenLocation(), T.getCloseLocation()); } /// \brief Parsing of simple OpenMP clauses like 'default' or 'proc_bind'. /// /// default-clause: /// 'default' '(' 'none' | 'shared' ') /// /// proc_bind-clause: /// 'proc_bind' '(' 'master' | 'close' | 'spread' ') /// OMPClause *Parser::ParseOpenMPSimpleClause(OpenMPClauseKind Kind) { SourceLocation Loc = Tok.getLocation(); SourceLocation LOpen = ConsumeToken(); // Parse '('. BalancedDelimiterTracker T(*this, tok::l_paren, tok::annot_pragma_openmp_end); if (T.expectAndConsume(diag::err_expected_lparen_after, getOpenMPClauseName(Kind))) return nullptr; unsigned Type = getOpenMPSimpleClauseType( Kind, Tok.isAnnotation() ? "" : PP.getSpelling(Tok)); SourceLocation TypeLoc = Tok.getLocation(); if (Tok.isNot(tok::r_paren) && Tok.isNot(tok::comma) && Tok.isNot(tok::annot_pragma_openmp_end)) ConsumeAnyToken(); // Parse ')'. T.consumeClose(); return Actions.ActOnOpenMPSimpleClause(Kind, Type, TypeLoc, LOpen, Loc, Tok.getLocation()); } /// \brief Parsing of OpenMP clauses like 'ordered'. /// /// ordered-clause: /// 'ordered' /// /// nowait-clause: /// 'nowait' /// OMPClause *Parser::ParseOpenMPClause(OpenMPClauseKind Kind) { SourceLocation Loc = Tok.getLocation(); ConsumeAnyToken(); return Actions.ActOnOpenMPClause(Kind, Loc, Tok.getLocation()); } /// \brief Parsing of OpenMP clauses with single expressions and some additional /// argument like 'schedule' or 'dist_schedule'. /// /// schedule-clause: /// 'schedule' '(' kind [',' expression ] ')' /// OMPClause *Parser::ParseOpenMPSingleExprWithArgClause(OpenMPClauseKind Kind) { SourceLocation Loc = ConsumeToken(); SourceLocation CommaLoc; // Parse '('. BalancedDelimiterTracker T(*this, tok::l_paren, tok::annot_pragma_openmp_end); if (T.expectAndConsume(diag::err_expected_lparen_after, getOpenMPClauseName(Kind))) return nullptr; ExprResult Val; unsigned Type = getOpenMPSimpleClauseType( Kind, Tok.isAnnotation() ? "" : PP.getSpelling(Tok)); SourceLocation KLoc = Tok.getLocation(); if (Tok.isNot(tok::r_paren) && Tok.isNot(tok::comma) && Tok.isNot(tok::annot_pragma_openmp_end)) ConsumeAnyToken(); if (Kind == OMPC_schedule && (Type == OMPC_SCHEDULE_static || Type == OMPC_SCHEDULE_dynamic || Type == OMPC_SCHEDULE_guided) && Tok.is(tok::comma)) { CommaLoc = ConsumeAnyToken(); ExprResult LHS(ParseCastExpression(false, false, NotTypeCast)); Val = ParseRHSOfBinaryExpression(LHS, prec::Conditional); if (Val.isInvalid()) return nullptr; } // Parse ')'. T.consumeClose(); return Actions.ActOnOpenMPSingleExprWithArgClause( Kind, Type, Val.get(), Loc, T.getOpenLocation(), KLoc, CommaLoc, T.getCloseLocation()); } static bool ParseReductionId(Parser &P, CXXScopeSpec &ReductionIdScopeSpec, UnqualifiedId &ReductionId) { SourceLocation TemplateKWLoc; if (ReductionIdScopeSpec.isEmpty()) { auto OOK = OO_None; switch (P.getCurToken().getKind()) { case tok::plus: OOK = OO_Plus; break; case tok::minus: OOK = OO_Minus; break; case tok::star: OOK = OO_Star; break; case tok::amp: OOK = OO_Amp; break; case tok::pipe: OOK = OO_Pipe; break; case tok::caret: OOK = OO_Caret; break; case tok::ampamp: OOK = OO_AmpAmp; break; case tok::pipepipe: OOK = OO_PipePipe; break; default: break; } if (OOK != OO_None) { SourceLocation OpLoc = P.ConsumeToken(); SourceLocation SymbolLocations[] = {OpLoc, OpLoc, SourceLocation()}; ReductionId.setOperatorFunctionId(OpLoc, OOK, SymbolLocations); return false; } } return P.ParseUnqualifiedId(ReductionIdScopeSpec, /*EnteringContext*/ false, /*AllowDestructorName*/ false, /*AllowConstructorName*/ false, ParsedType(), TemplateKWLoc, ReductionId); } /// \brief Parsing of OpenMP clause 'private', 'firstprivate', 'lastprivate', /// 'shared', 'copyin', or 'reduction'. /// /// private-clause: /// 'private' '(' list ')' /// firstprivate-clause: /// 'firstprivate' '(' list ')' /// lastprivate-clause: /// 'lastprivate' '(' list ')' /// shared-clause: /// 'shared' '(' list ')' /// linear-clause: /// 'linear' '(' list [ ':' linear-step ] ')' /// aligned-clause: /// 'aligned' '(' list [ ':' alignment ] ')' /// reduction-clause: /// 'reduction' '(' reduction-identifier ':' list ')' /// OMPClause *Parser::ParseOpenMPVarListClause(OpenMPClauseKind Kind) { SourceLocation Loc = Tok.getLocation(); SourceLocation LOpen = ConsumeToken(); SourceLocation ColonLoc = SourceLocation(); // Optional scope specifier and unqualified id for reduction identifier. CXXScopeSpec ReductionIdScopeSpec; UnqualifiedId ReductionId; bool InvalidReductionId = false; // Parse '('. BalancedDelimiterTracker T(*this, tok::l_paren, tok::annot_pragma_openmp_end); if (T.expectAndConsume(diag::err_expected_lparen_after, getOpenMPClauseName(Kind))) return nullptr; // Handle reduction-identifier for reduction clause. if (Kind == OMPC_reduction) { ColonProtectionRAIIObject ColonRAII(*this); if (getLangOpts().CPlusPlus) { ParseOptionalCXXScopeSpecifier(ReductionIdScopeSpec, ParsedType(), false); } InvalidReductionId = ParseReductionId(*this, ReductionIdScopeSpec, ReductionId); if (InvalidReductionId) { SkipUntil(tok::colon, tok::r_paren, tok::annot_pragma_openmp_end, StopBeforeMatch); } if (Tok.is(tok::colon)) { ColonLoc = ConsumeToken(); } else { Diag(Tok, diag::warn_pragma_expected_colon) << "reduction identifier"; } } SmallVector<Expr *, 5> Vars; bool IsComma = !InvalidReductionId; const bool MayHaveTail = (Kind == OMPC_linear || Kind == OMPC_aligned); while (IsComma || (Tok.isNot(tok::r_paren) && Tok.isNot(tok::colon) && Tok.isNot(tok::annot_pragma_openmp_end))) { ColonProtectionRAIIObject ColonRAII(*this, MayHaveTail); // Parse variable ExprResult VarExpr = ParseAssignmentExpression(); if (VarExpr.isUsable()) { Vars.push_back(VarExpr.get()); } else { SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end, StopBeforeMatch); } // Skip ',' if any IsComma = Tok.is(tok::comma); if (IsComma) ConsumeToken(); else if (Tok.isNot(tok::r_paren) && Tok.isNot(tok::annot_pragma_openmp_end) && (!MayHaveTail || Tok.isNot(tok::colon))) Diag(Tok, diag::err_omp_expected_punc) << getOpenMPClauseName(Kind); } // Parse ':' linear-step (or ':' alignment). Expr *TailExpr = nullptr; const bool MustHaveTail = MayHaveTail && Tok.is(tok::colon); if (MustHaveTail) { ColonLoc = Tok.getLocation(); ConsumeToken(); ExprResult Tail = ParseAssignmentExpression(); if (Tail.isUsable()) TailExpr = Tail.get(); else SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end, StopBeforeMatch); } // Parse ')'. T.consumeClose(); if (Vars.empty() || (MustHaveTail && !TailExpr) || InvalidReductionId) return nullptr; return Actions.ActOnOpenMPVarListClause( Kind, Vars, TailExpr, Loc, LOpen, ColonLoc, Tok.getLocation(), ReductionIdScopeSpec, ReductionId.isValid() ? Actions.GetNameFromUnqualifiedId(ReductionId) : DeclarationNameInfo()); }