diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h --- a/clang/include/clang-c/Index.h +++ b/clang/include/clang-c/Index.h @@ -2576,7 +2576,11 @@ */ CXCursor_OMPCanonicalLoop = 289, - CXCursor_LastStmt = CXCursor_OMPCanonicalLoop, + /** OpenMP interop directive. + */ + CXCursor_OMPInteropDirective = 290, + + CXCursor_LastStmt = CXCursor_OMPInteropDirective, /** * Cursor that represents the translation unit itself. diff --git a/clang/include/clang/AST/OpenMPClause.h b/clang/include/clang/AST/OpenMPClause.h --- a/clang/include/clang/AST/OpenMPClause.h +++ b/clang/include/clang/AST/OpenMPClause.h @@ -7366,6 +7366,129 @@ } }; +/// This represents the 'init' clause in '#pragma omp ...' directives. +/// +/// \code +/// #pragma omp interop init(target:obj) +/// \endcode +class OMPInitClause final + : public OMPVarListClause, + private llvm::TrailingObjects { + friend class OMPClauseReader; + friend OMPVarListClause; + friend TrailingObjects; + + /// Location of interop variable. + SourceLocation VarLoc; + + bool IsTarget = false; + bool IsTargetSync = false; + + void setInteropVar(Expr *E) { varlist_begin()[0] = E; } + + void setIsTarget(bool V) { IsTarget = V; } + + void setIsTargetSync(bool V) { IsTargetSync = V; } + + /// Sets the location of the interop variable. + void setVarLoc(SourceLocation Loc) { VarLoc = Loc; } + + /// Build 'init' clause. + /// + /// \param IsTarget Uses the 'target' interop-type. + /// \param IsTargetSync Uses the 'targetsync' interop-type. + /// \param StartLoc Starting location of the clause. + /// \param LParenLoc Location of '('. + /// \param VarLoc Location of the interop variable. + /// \param EndLoc Ending location of the clause. + /// \param N Number of expressions. + OMPInitClause(bool IsTarget, bool IsTargetSync, SourceLocation StartLoc, + SourceLocation LParenLoc, SourceLocation VarLoc, + SourceLocation EndLoc, unsigned N) + : OMPVarListClause(llvm::omp::OMPC_init, StartLoc, + LParenLoc, EndLoc, N), + VarLoc(VarLoc), IsTarget(IsTarget), IsTargetSync(IsTargetSync) {} + + /// Build an empty clause. + OMPInitClause(unsigned N) + : OMPVarListClause(llvm::omp::OMPC_init, SourceLocation(), + SourceLocation(), SourceLocation(), N) { + } + +public: + /// Creates a fully specified clause. + /// + /// \param C AST context. + /// \param InteropVar The interop variable. + /// \param PrefExprs The list of preference expressions. + /// \param IsTarget Uses the 'target' interop-type. + /// \param IsTargetSync Uses the 'targetsync' interop-type. + /// \param StartLoc Starting location of the clause. + /// \param LParenLoc Location of '('. + /// \param VarLoc Location of the interop variable. + /// \param EndLoc Ending location of the clause. + static OMPInitClause *Create(const ASTContext &C, Expr *InteropVar, + ArrayRef PrefExprs, bool IsTarget, + bool IsTargetSync, SourceLocation StartLoc, + SourceLocation LParenLoc, SourceLocation VarLoc, + SourceLocation EndLoc); + + /// Creates an empty clause with \a N expressions. + /// + /// \param C AST context. + /// \param N Number of expression items. + static OMPInitClause *CreateEmpty(const ASTContext &C, unsigned N); + + /// Returns the location of the interop variable. + SourceLocation getVarLoc() const { return VarLoc; } + + /// Returns the interop variable. + Expr *getInteropVar() { return varlist_begin()[0]; } + const Expr *getInteropVar() const { return varlist_begin()[0]; } + + /// Returns true is interop-type 'target' is used. + bool getIsTarget() const { return IsTarget; } + + /// Returns true is interop-type 'targetsync' is used. + bool getIsTargetSync() const { return IsTargetSync; } + + child_range children() { + return child_range(reinterpret_cast(varlist_begin()), + reinterpret_cast(varlist_end())); + } + + const_child_range children() const { + auto Children = const_cast(this)->children(); + return const_child_range(Children.begin(), Children.end()); + } + + child_range used_children() { + return child_range(child_iterator(), child_iterator()); + } + const_child_range used_children() const { + return const_child_range(const_child_iterator(), const_child_iterator()); + } + + using prefs_iterator = MutableArrayRef::iterator; + using const_prefs_iterator = ArrayRef::iterator; + using prefs_range = llvm::iterator_range; + using const_prefs_range = llvm::iterator_range; + + prefs_range prefs() { + return prefs_range(reinterpret_cast(std::next(varlist_begin())), + reinterpret_cast(varlist_end())); + } + + const_prefs_range prefs() const { + auto Prefs = const_cast(this)->prefs(); + return const_prefs_range(Prefs.begin(), Prefs.end()); + } + + static bool classof(const OMPClause *T) { + return T->getClauseKind() == llvm::omp::OMPC_init; + } +}; + /// This represents 'destroy' clause in the '#pragma omp depobj' /// directive. /// diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -2968,6 +2968,9 @@ DEF_TRAVERSE_STMT(OMPTargetTeamsDistributeSimdDirective, { TRY_TO(TraverseOMPExecutableDirective(S)); }) +DEF_TRAVERSE_STMT(OMPInteropDirective, + { TRY_TO(TraverseOMPExecutableDirective(S)); }) + // OpenMP clauses. template bool RecursiveASTVisitor::TraverseOMPClause(OMPClause *C) { @@ -3194,6 +3197,12 @@ return true; } +template +bool RecursiveASTVisitor::VisitOMPInitClause(OMPInitClause *C) { + TRY_TO(VisitOMPClauseList(C)); + return true; +} + template bool RecursiveASTVisitor::VisitOMPDestroyClause(OMPDestroyClause *) { return true; diff --git a/clang/include/clang/AST/StmtOpenMP.h b/clang/include/clang/AST/StmtOpenMP.h --- a/clang/include/clang/AST/StmtOpenMP.h +++ b/clang/include/clang/AST/StmtOpenMP.h @@ -5086,6 +5086,59 @@ } }; +/// This represents '#pragma omp interop' directive. +/// +/// \code +/// #pragma omp interop init(target:obj) device(x) depend(inout:y) nowait +/// \endcode +/// In this example directive '#pragma omp interop' has +/// clauses 'init', 'device', 'depend' and 'nowait'. +/// +class OMPInteropDirective final : public OMPExecutableDirective { + friend class ASTStmtReader; + friend class OMPExecutableDirective; + + /// Build directive with the given start and end location. + /// + /// \param StartLoc Starting location of the directive. + /// \param EndLoc Ending location of the directive. + /// + OMPInteropDirective(SourceLocation StartLoc, SourceLocation EndLoc) + : OMPExecutableDirective(OMPInteropDirectiveClass, + llvm::omp::OMPD_interop, StartLoc, EndLoc) {} + + /// Build an empty directive. + /// + explicit OMPInteropDirective() + : OMPExecutableDirective(OMPInteropDirectiveClass, + llvm::omp::OMPD_interop, SourceLocation(), + SourceLocation()) {} + +public: + /// Creates directive. + /// + /// \param C AST context. + /// \param StartLoc Starting location of the directive. + /// \param EndLoc Ending Location of the directive. + /// \param Clauses The directive's clauses. + /// + static OMPInteropDirective *Create(const ASTContext &C, + SourceLocation StartLoc, + SourceLocation EndLoc, + ArrayRef Clauses); + + /// Creates an empty directive. + /// + /// \param C AST context. + /// + static OMPInteropDirective *CreateEmpty(const ASTContext &C, + unsigned NumClauses, EmptyShell); + + static bool classof(const Stmt *T) { + return T->getStmtClass() == OMPInteropDirectiveClass; + } +}; + } // end namespace clang #endif diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1400,6 +1400,13 @@ "only a single match extension allowed per OpenMP context selector">; def err_omp_invalid_dsa: Error< "data-sharing attribute '%0' in '%1' clause requires OpenMP version %2 or above">; +def err_omp_expected_punc_after_interop_mod : Error< + "expected ',' after interop modifier">; +def err_omp_expected_interop_type : Error< + "expected interop type: 'target' and/or 'targetsync'">; +def warn_omp_more_one_interop_type + : Warning<"interop type '%0' cannot be specified more than once">, + InGroup; // Pragma loop support. def err_pragma_loop_missing_argument : Error< diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10587,6 +10587,17 @@ : Note<"jump bypasses OpenMP structured block">; def note_omp_exits_structured_block : Note<"jump exits scope of OpenMP structured block">; +def err_omp_interop_variable_expected : Error< + "expected%select{| non-const}0 variable of type 'omp_interop_t'">; +def err_omp_interop_variable_wrong_type : Error< + "interop variable must be of type 'omp_interop_t'">; +def err_omp_interop_prefer_type : Error< + "prefer_list item must be a string literal or constant integral " + "expression">; +def err_omp_interop_bad_depend_clause : Error< + "'depend' clause requires the 'targetsync' interop type">; +def err_omp_interop_var_multiple_actions : Error< + "interop variable %0 used in multiple action clauses">; } // end of OpenMP category let CategoryName = "Related Result Type Issue" in { diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td --- a/clang/include/clang/Basic/StmtNodes.td +++ b/clang/include/clang/Basic/StmtNodes.td @@ -275,3 +275,4 @@ def OMPTargetTeamsDistributeParallelForDirective : StmtNode; def OMPTargetTeamsDistributeParallelForSimdDirective : StmtNode; def OMPTargetTeamsDistributeSimdDirective : StmtNode; +def OMPInteropDirective : StmtNode; diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -3292,6 +3292,14 @@ /// '(' { [ '(' ')' ] }+ ')' OMPClause *ParseOpenMPUsesAllocatorClause(OpenMPDirectiveKind DKind); + /// Parses clause with an interop variable of kind \a Kind. + /// + /// \param Kind Kind of current clause. + /// \param ParseOnly true to skip the clause's semantic actions and return + /// nullptr. + // + OMPClause *ParseOpenMPInteropClause(OpenMPClauseKind Kind, bool ParseOnly); + public: /// Parses simple expression in parens for single-expression clauses of OpenMP /// constructs. diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -10790,6 +10790,10 @@ StmtResult ActOnOpenMPTargetTeamsDistributeSimdDirective( ArrayRef Clauses, Stmt *AStmt, SourceLocation StartLoc, SourceLocation EndLoc, VarsWithInheritedDSAType &VarsWithImplicitDSA); + /// Called on well-formed '\#pragma omp interop'. + StmtResult ActOnOpenMPInteropDirective(ArrayRef Clauses, + SourceLocation StartLoc, + SourceLocation EndLoc); /// Checks correctness of linear modifiers. bool CheckOpenMPLinearModifier(OpenMPLinearClauseKind LinKind, @@ -10979,6 +10983,15 @@ /// Called on well-formed 'relaxed' clause. OMPClause *ActOnOpenMPRelaxedClause(SourceLocation StartLoc, SourceLocation EndLoc); + + /// Called on well-formed 'init' clause. + OMPClause *ActOnOpenMPInitClause(Expr *InteropVar, ArrayRef PrefExprs, + bool IsTarget, bool IsTargetSync, + SourceLocation StartLoc, + SourceLocation LParenLoc, + SourceLocation VarLoc, + SourceLocation EndLoc); + /// Called on well-formed 'destroy' clause. OMPClause *ActOnOpenMPDestroyClause(SourceLocation StartLoc, SourceLocation EndLoc); diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -1940,6 +1940,7 @@ STMT_OMP_TARGET_TEAMS_DISTRIBUTE_PARALLEL_FOR_DIRECTIVE, STMT_OMP_TARGET_TEAMS_DISTRIBUTE_PARALLEL_FOR_SIMD_DIRECTIVE, STMT_OMP_TARGET_TEAMS_DISTRIBUTE_SIMD_DIRECTIVE, + STMT_OMP_INTEROP_DIRECTIVE, EXPR_OMP_ARRAY_SECTION, EXPR_OMP_ARRAY_SHAPING, EXPR_OMP_ITERATOR, diff --git a/clang/lib/AST/OpenMPClause.cpp b/clang/lib/AST/OpenMPClause.cpp --- a/clang/lib/AST/OpenMPClause.cpp +++ b/clang/lib/AST/OpenMPClause.cpp @@ -1513,6 +1513,27 @@ return new (Mem) OMPAffinityClause(N); } +OMPInitClause *OMPInitClause::Create(const ASTContext &C, Expr *InteropVar, + ArrayRef PrefExprs, bool IsTarget, + bool IsTargetSync, SourceLocation StartLoc, + SourceLocation LParenLoc, + SourceLocation VarLoc, + SourceLocation EndLoc) { + + void *Mem = C.Allocate(totalSizeToAlloc(PrefExprs.size() + 1)); + auto *Clause = + new (Mem) OMPInitClause(IsTarget, IsTargetSync, StartLoc, LParenLoc, + VarLoc, EndLoc, PrefExprs.size() + 1); + Clause->setInteropVar(InteropVar); + llvm::copy(PrefExprs, Clause->getTrailingObjects() + 1); + return Clause; +} + +OMPInitClause *OMPInitClause::CreateEmpty(const ASTContext &C, unsigned N) { + void *Mem = C.Allocate(totalSizeToAlloc(N)); + return new (Mem) OMPInitClause(N); +} + //===----------------------------------------------------------------------===// // OpenMP clauses printing methods //===----------------------------------------------------------------------===// @@ -1755,6 +1776,31 @@ OS << ")"; } +void OMPClausePrinter::VisitOMPInitClause(OMPInitClause *Node) { + OS << "init("; + bool First = true; + for (const Expr *E : Node->prefs()) { + if (First) + OS << "prefer_type("; + else + OS << ","; + E->printPretty(OS, nullptr, Policy); + First = false; + } + if (!First) + OS << "), "; + if (Node->getIsTarget()) + OS << "target"; + if (Node->getIsTargetSync()) { + if (Node->getIsTarget()) + OS << ", "; + OS << "targetsync"; + } + OS << " : "; + Node->getInteropVar()->printPretty(OS, nullptr, Policy); + OS << ")"; +} + void OMPClausePrinter::VisitOMPDestroyClause(OMPDestroyClause *) { OS << "destroy"; } diff --git a/clang/lib/AST/StmtOpenMP.cpp b/clang/lib/AST/StmtOpenMP.cpp --- a/clang/lib/AST/StmtOpenMP.cpp +++ b/clang/lib/AST/StmtOpenMP.cpp @@ -1944,3 +1944,18 @@ numLoopChildren(CollapsedNum, OMPD_target_teams_distribute_simd), CollapsedNum); } + +OMPInteropDirective * +OMPInteropDirective::Create(const ASTContext &C, SourceLocation StartLoc, + SourceLocation EndLoc, + ArrayRef Clauses) { + return createDirective( + C, Clauses, /*AssociatedStmt=*/nullptr, /*NumChildren=*/0, StartLoc, + EndLoc); +} + +OMPInteropDirective *OMPInteropDirective::CreateEmpty(const ASTContext &C, + unsigned NumClauses, + EmptyShell) { + return createEmptyDirective(C, NumClauses); +} diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -962,6 +962,11 @@ PrintOMPExecutableDirective(Node); } +void StmtPrinter::VisitOMPInteropDirective(OMPInteropDirective *Node) { + Indent() << "#pragma omp interop"; + PrintOMPExecutableDirective(Node); +} + //===----------------------------------------------------------------------===// // Expr printing methods. //===----------------------------------------------------------------------===// diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -543,6 +543,10 @@ void OMPClauseProfiler::VisitOMPNogroupClause(const OMPNogroupClause *) {} +void OMPClauseProfiler::VisitOMPInitClause(const OMPInitClause *C) { + VisitOMPClauseList(C); +} + void OMPClauseProfiler::VisitOMPDestroyClause(const OMPDestroyClause *) {} template @@ -1128,6 +1132,10 @@ VisitOMPLoopDirective(S); } +void StmtProfiler::VisitOMPInteropDirective(const OMPInteropDirective *S) { + VisitOMPExecutableDirective(S); +} + void StmtProfiler::VisitExpr(const Expr *S) { VisitStmt(S); } diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -375,6 +375,9 @@ EmitOMPTargetTeamsDistributeSimdDirective( cast(*S)); break; + case Stmt::OMPInteropDirectiveClass: + llvm_unreachable("Interop directive not supported yet."); + break; } } diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -2378,6 +2378,7 @@ case OMPD_target_enter_data: case OMPD_target_exit_data: case OMPD_target_update: + case OMPD_interop: if ((StmtCtx & ParsedStmtContext::AllowStandaloneOpenMPDirectives) == ParsedStmtContext()) { Diag(Tok, diag::err_omp_immediate_directive) @@ -2928,6 +2929,9 @@ case OMPC_uses_allocators: Clause = ParseOpenMPUsesAllocatorClause(DKind); break; + case OMPC_init: + Clause = ParseOpenMPInteropClause(CKind, WrongDirective); + break; case OMPC_device_type: case OMPC_unknown: skipUntilPragmaOpenMPEnd(DKind); @@ -3024,6 +3028,137 @@ return Actions.ActOnOpenMPSingleExprClause(Kind, Val.get(), Loc, LLoc, RLoc); } +/// Parsing of OpenMP clauses that use an interop-var. +/// +/// init-clause: +/// init([interop-modifier, ]interop-type[[, interop-type] ... ]:interop-var) +/// +/// destroy-clause: +/// destroy(interop-var) +/// +/// use-clause: +/// use(interop-var) +/// +/// interop-modifier: +/// prefer_type(preference-list) +/// +/// preference-list: +/// foreign-runtime-id [, foreign-runtime-id]... +/// +/// foreign-runtime-id: +/// | +/// +/// interop-type: +/// target | targetsync +/// +OMPClause *Parser::ParseOpenMPInteropClause(OpenMPClauseKind Kind, + bool ParseOnly) { + SourceLocation Loc = ConsumeToken(); + // Parse '('. + BalancedDelimiterTracker T(*this, tok::l_paren, tok::annot_pragma_openmp_end); + if (T.expectAndConsume(diag::err_expected_lparen_after, + getOpenMPClauseName(Kind).data())) + return nullptr; + + bool IsTarget = false; + bool IsTargetSync = false; + SmallVector Prefs; + + if (Kind == OMPC_init) { + + // Parse optional interop-modifier. + if (Tok.is(tok::identifier) && PP.getSpelling(Tok) == "prefer_type") { + ConsumeToken(); + BalancedDelimiterTracker PT(*this, tok::l_paren, + tok::annot_pragma_openmp_end); + if (PT.expectAndConsume(diag::err_expected_lparen_after, "prefer_type")) + return nullptr; + + while (Tok.isNot(tok::r_paren)) { + SourceLocation Loc = Tok.getLocation(); + ExprResult LHS = ParseCastExpression(AnyCastExpr); + ExprResult PTExpr = Actions.CorrectDelayedTyposInExpr( + ParseRHSOfBinaryExpression(LHS, prec::Conditional)); + PTExpr = Actions.ActOnFinishFullExpr(PTExpr.get(), Loc, + /*DiscardedValue=*/false); + if (PTExpr.isUsable()) + Prefs.push_back(PTExpr.get()); + else + SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end, + StopBeforeMatch); + + if (Tok.is(tok::comma)) + ConsumeToken(); + } + PT.consumeClose(); + } + + if (!Prefs.empty()) { + if (Tok.is(tok::comma)) + ConsumeToken(); + else + Diag(Tok, diag::err_omp_expected_punc_after_interop_mod); + } + + // Parse the interop-types. + bool HasError = false; + while (Tok.is(tok::identifier)) { + if (PP.getSpelling(Tok) == "target") { + // OpenMP 5.1 [2.15.1, interop Construct, Restrictions] + // Each interop-type may be specified on an action-clause at most + // once. + if (IsTarget) + Diag(Tok, diag::warn_omp_more_one_interop_type) << "target"; + IsTarget = true; + } else if (PP.getSpelling(Tok) == "targetsync") { + if (IsTargetSync) + Diag(Tok, diag::warn_omp_more_one_interop_type) << "targetsync"; + IsTargetSync = true; + } else { + HasError = true; + Diag(Tok, diag::err_omp_expected_interop_type); + } + ConsumeToken(); + + if (!Tok.is(tok::comma)) + break; + ConsumeToken(); + } + if (!HasError && !IsTarget && !IsTargetSync) + Diag(Tok, diag::err_omp_expected_interop_type); + + if (Tok.is(tok::colon)) + ConsumeToken(); + else if (IsTarget || IsTargetSync) + Diag(Tok, diag::warn_pragma_expected_colon) << "interop types"; + } + + // Parse the variable. + SourceLocation VarLoc = Tok.getLocation(); + ExprResult InteropVarExpr = + Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression()); + if (!InteropVarExpr.isUsable()) { + SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end, + StopBeforeMatch); + } + + // Parse ')'. + SourceLocation RLoc = Tok.getLocation(); + if (!T.consumeClose()) + RLoc = T.getCloseLocation(); + + if (ParseOnly || !InteropVarExpr.isUsable() || + (Kind == OMPC_init && !IsTarget && !IsTargetSync)) + return nullptr; + + if (Kind == OMPC_init) + return Actions.ActOnOpenMPInitClause(InteropVarExpr.get(), Prefs, IsTarget, + IsTargetSync, Loc, T.getOpenLocation(), + VarLoc, RLoc); + + llvm_unreachable("Unexpected interop variable clause."); +} + /// Parsing of simple OpenMP clauses like 'default' or 'proc_bind'. /// /// default-clause: diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -1486,6 +1486,7 @@ case Stmt::OMPTeamsDistributeParallelForDirectiveClass: case Stmt::OMPTeamsDistributeParallelForSimdDirectiveClass: case Stmt::OMPTeamsDistributeSimdDirectiveClass: + case Stmt::OMPInteropDirectiveClass: case Stmt::ReturnStmtClass: case Stmt::SEHExceptStmtClass: case Stmt::SEHFinallyStmtClass: diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -6115,6 +6115,11 @@ if (LangOpts.OpenMP >= 50) AllowedNameModifiers.push_back(OMPD_simd); break; + case OMPD_interop: + assert(AStmt == nullptr && + "No associated statement allowed for 'omp interop' directive"); + Res = ActOnOpenMPInteropDirective(ClausesWithImplicit, StartLoc, EndLoc); + break; case OMPD_declare_target: case OMPD_end_declare_target: case OMPD_threadprivate: @@ -13347,6 +13352,7 @@ CaptureRegion = OMPD_task; break; case OMPD_target_data: + case OMPD_interop: // Do not capture device-clause expressions. break; case OMPD_teams_distribute_parallel_for: @@ -14598,6 +14604,155 @@ return new (Context) OMPDestroyClause(StartLoc, EndLoc); } +StmtResult Sema::ActOnOpenMPInteropDirective(ArrayRef Clauses, + SourceLocation StartLoc, + SourceLocation EndLoc) { + + // OpenMP 5.1 [2.15.1, interop Construct, Restrictions] + // At least one action-clause must appear on a directive. + // TODO: also add 'use' and 'destroy' here. + if (!hasClauses(Clauses, OMPC_init, OMPC_nowait)) { + StringRef Expected = "'init', 'use', 'destroy', or 'nowait'"; + Diag(StartLoc, diag::err_omp_no_clause_for_directive) + << Expected << getOpenMPDirectiveName(OMPD_interop); + return StmtError(); + } + + // OpenMP 5.1 [2.15.1, interop Construct, Restrictions] + // A depend clause can only appear on the directive if a targetsync + // interop-type is present or the interop-var was initialized with + // the targetsync interop-type. + + // If there is any 'init' clause diagnose if there is no 'init' clause with + // interop-type of 'targetsync'. Cases involving other directives cannot be + // diagnosed. + const OMPDependClause *DependClause = nullptr; + bool IsTargetSync = false; + for (const OMPClause *C : Clauses) { + if (IsTargetSync) + break; + if (const auto *InitClause = dyn_cast(C)) + IsTargetSync = InitClause->getIsTargetSync(); + else if (const auto *DC = dyn_cast(C)) + DependClause = DC; + } + if (DependClause && !IsTargetSync) { + Diag(DependClause->getBeginLoc(), diag::err_omp_interop_bad_depend_clause); + return StmtError(); + } + + // OpenMP 5.1 [2.15.1, interop Construct, Restrictions] + // Each interop-var may be specified for at most one action-clause of each + // interop construct. + llvm::SmallPtrSet InteropVars; + for (const OMPClause *C : Clauses) { + OpenMPClauseKind ClauseKind = C->getClauseKind(); + const DeclRefExpr *DRE = nullptr; + SourceLocation VarLoc; + + if (ClauseKind == OMPC_init) { + const auto *IC = cast(C); + VarLoc = IC->getVarLoc(); + DRE = dyn_cast_or_null(IC->getInteropVar()); + } + // TODO: 'use' and 'destroy' clauses to be added here. + + if (!DRE) + continue; + + if (const auto *VD = dyn_cast(DRE->getDecl())) { + if (!InteropVars.insert(VD->getCanonicalDecl()).second) { + Diag(VarLoc, diag::err_omp_interop_var_multiple_actions) << VD; + return StmtError(); + } + } + } + + return OMPInteropDirective::Create(Context, StartLoc, EndLoc, Clauses); +} + +static bool isValidInteropVariable(Sema &SemaRef, Expr *InteropVarExpr, + SourceLocation VarLoc, + OpenMPClauseKind Kind) { + if (InteropVarExpr->isValueDependent() || InteropVarExpr->isTypeDependent() || + InteropVarExpr->isInstantiationDependent() || + InteropVarExpr->containsUnexpandedParameterPack()) + return true; + + const auto *DRE = dyn_cast(InteropVarExpr); + if (!DRE || !isa(DRE->getDecl())) { + SemaRef.Diag(VarLoc, diag::err_omp_interop_variable_expected) << 0; + return false; + } + + // Interop variable should be of type omp_interop_t. + bool HasError = false; + QualType InteropType; + LookupResult Result(SemaRef, &SemaRef.Context.Idents.get("omp_interop_t"), + VarLoc, Sema::LookupOrdinaryName); + if (SemaRef.LookupName(Result, SemaRef.getCurScope())) { + NamedDecl *ND = Result.getFoundDecl(); + if (const auto *TD = dyn_cast(ND)) { + InteropType = QualType(TD->getTypeForDecl(), 0); + } else { + HasError = true; + } + } else { + HasError = true; + } + + if (HasError) { + SemaRef.Diag(VarLoc, diag::err_omp_implied_type_not_found) + << "omp_interop_t"; + return false; + } + + QualType VarType = InteropVarExpr->getType().getUnqualifiedType(); + if (!SemaRef.Context.hasSameType(InteropType, VarType)) { + SemaRef.Diag(VarLoc, diag::err_omp_interop_variable_wrong_type); + return false; + } + + // OpenMP 5.1 [2.15.1, interop Construct, Restrictions] + // The interop-var passed to init or destroy must be non-const. + // TODO: 'destroy' clause too. + if (Kind == OMPC_init && + isConstNotMutableType(SemaRef, InteropVarExpr->getType())) { + SemaRef.Diag(VarLoc, diag::err_omp_interop_variable_expected) + << /*non-const*/ 1; + return false; + } + return true; +} + +OMPClause * +Sema::ActOnOpenMPInitClause(Expr *InteropVar, ArrayRef PrefExprs, + bool IsTarget, bool IsTargetSync, + SourceLocation StartLoc, SourceLocation LParenLoc, + SourceLocation VarLoc, SourceLocation EndLoc) { + + if (!isValidInteropVariable(*this, InteropVar, VarLoc, OMPC_init)) + return nullptr; + + // Check prefer_type values. These foreign-runtime-id values are either + // string literals or constant integral expressions. + for (const Expr *E : PrefExprs) { + if (E->isValueDependent() || E->isTypeDependent() || + E->isInstantiationDependent() || E->containsUnexpandedParameterPack()) + continue; + if (E->isIntegerConstantExpr(Context)) + continue; + if (isa(E)) + continue; + Diag(E->getExprLoc(), diag::err_omp_interop_prefer_type); + return nullptr; + } + + return OMPInitClause::Create(Context, InteropVar, PrefExprs, IsTarget, + IsTargetSync, StartLoc, LParenLoc, VarLoc, + EndLoc); +} + OMPClause *Sema::ActOnOpenMPVarListClause( OpenMPClauseKind Kind, ArrayRef VarList, Expr *DepModOrTailExpr, const OMPVarListLocTy &Locs, SourceLocation ColonLoc, diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -2170,6 +2170,21 @@ LParenLoc, EndLoc); } + /// Build a new OpenMP 'init' clause. + /// + /// By default, performs semantic analysis to build the new OpenMP clause. + /// Subclasses may override this routine to provide different behavior. + OMPClause *RebuildOMPInitClause(Expr *InteropVar, ArrayRef PrefExprs, + bool IsTarget, bool IsTargetSync, + SourceLocation StartLoc, + SourceLocation LParenLoc, + SourceLocation VarLoc, + SourceLocation EndLoc) { + return getSema().ActOnOpenMPInitClause(InteropVar, PrefExprs, IsTarget, + IsTargetSync, StartLoc, LParenLoc, + VarLoc, EndLoc); + } + /// Rebuild the operand to an Objective-C \@synchronized statement. /// /// By default, performs semantic analysis to build the new statement. @@ -9020,6 +9035,16 @@ return Res; } +template +StmtResult +TreeTransform::TransformOMPInteropDirective(OMPInteropDirective *D) { + DeclarationNameInfo DirName; + getDerived().getSema().StartOpenMPDSABlock(OMPD_interop, DirName, nullptr, + D->getBeginLoc()); + StmtResult Res = getDerived().TransformOMPExecutableDirective(D); + getDerived().getSema().EndOpenMPDSABlock(Res.get()); + return Res; +} //===----------------------------------------------------------------------===// // OpenMP clause transformation @@ -9275,6 +9300,25 @@ return C; } +template +OMPClause *TreeTransform::TransformOMPInitClause(OMPInitClause *C) { + ExprResult IVR = getDerived().TransformExpr(C->getInteropVar()); + if (IVR.isInvalid()) + return nullptr; + + llvm::SmallVector PrefExprs; + PrefExprs.reserve(C->varlist_size() - 1); + for (Expr *E : llvm::drop_begin(C->varlists())) { + ExprResult ER = getDerived().TransformExpr(cast(E)); + if (ER.isInvalid()) + return nullptr; + PrefExprs.push_back(ER.get()); + } + return getDerived().RebuildOMPInitClause( + IVR.get(), PrefExprs, C->getIsTarget(), C->getIsTargetSync(), + C->getBeginLoc(), C->getLParenLoc(), C->getVarLoc(), C->getEndLoc()); +} + template OMPClause * TreeTransform::TransformOMPDestroyClause(OMPDestroyClause *C) { diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -11968,6 +11968,9 @@ case llvm::omp::OMPC_order: C = new (Context) OMPOrderClause(); break; + case llvm::omp::OMPC_init: + C = OMPInitClause::CreateEmpty(Context, Record.readInt()); + break; case llvm::omp::OMPC_destroy: C = new (Context) OMPDestroyClause(); break; @@ -12131,6 +12134,19 @@ void OMPClauseReader::VisitOMPNogroupClause(OMPNogroupClause *) {} +void OMPClauseReader::VisitOMPInitClause(OMPInitClause *C) { + unsigned NumVars = C->varlist_size(); + SmallVector Vars; + Vars.reserve(NumVars); + for (unsigned I = 0; I != NumVars; ++I) + Vars.push_back(Record.readSubExpr()); + C->setVarRefs(Vars); + C->setIsTarget(Record.readBool()); + C->setIsTargetSync(Record.readBool()); + C->setLParenLoc(Record.readSourceLocation()); + C->setVarLoc(Record.readSourceLocation()); +} + void OMPClauseReader::VisitOMPDestroyClause(OMPDestroyClause *) {} void OMPClauseReader::VisitOMPUnifiedAddressClause(OMPUnifiedAddressClause *) {} diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -2588,6 +2588,11 @@ VisitOMPLoopDirective(D); } +void ASTStmtReader::VisitOMPInteropDirective(OMPInteropDirective *D) { + VisitStmt(D); + VisitOMPExecutableDirective(D); +} + //===----------------------------------------------------------------------===// // ASTReader Implementation //===----------------------------------------------------------------------===// @@ -3503,6 +3508,11 @@ break; } + case STMT_OMP_INTEROP_DIRECTIVE: + S = OMPInteropDirective::CreateEmpty( + Context, Record[ASTStmtReader::NumStmtFields], Empty); + break; + case EXPR_CXX_OPERATOR_CALL: S = CXXOperatorCallExpr::CreateEmpty( Context, /*NumArgs=*/Record[ASTStmtReader::NumExprFields], diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -6215,6 +6215,16 @@ void OMPClauseWriter::VisitOMPNogroupClause(OMPNogroupClause *) {} +void OMPClauseWriter::VisitOMPInitClause(OMPInitClause *C) { + Record.push_back(C->varlist_size()); + for (Expr *VE : C->varlists()) + Record.AddStmt(VE); + Record.writeBool(C->getIsTarget()); + Record.writeBool(C->getIsTargetSync()); + Record.AddSourceLocation(C->getLParenLoc()); + Record.AddSourceLocation(C->getVarLoc()); +} + void OMPClauseWriter::VisitOMPDestroyClause(OMPDestroyClause *) {} void OMPClauseWriter::VisitOMPPrivateClause(OMPPrivateClause *C) { diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -2541,6 +2541,12 @@ Code = serialization::STMT_OMP_TARGET_TEAMS_DISTRIBUTE_SIMD_DIRECTIVE; } +void ASTStmtWriter::VisitOMPInteropDirective(OMPInteropDirective *D) { + VisitStmt(D); + VisitOMPExecutableDirective(D); + Code = serialization::STMT_OMP_INTEROP_DIRECTIVE; +} + //===----------------------------------------------------------------------===// // ASTWriter Implementation //===----------------------------------------------------------------------===// diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1294,6 +1294,7 @@ case Stmt::OMPTargetTeamsDistributeParallelForSimdDirectiveClass: case Stmt::OMPTargetTeamsDistributeSimdDirectiveClass: case Stmt::OMPTileDirectiveClass: + case Stmt::OMPInteropDirectiveClass: case Stmt::CapturedStmtClass: { const ExplodedNode *node = Bldr.generateSink(S, Pred, Pred->getState()); Engine.addAbortedBlock(node, currBldrCtx->getBlock()); diff --git a/clang/test/OpenMP/interop_ast_print.cpp b/clang/test/OpenMP/interop_ast_print.cpp new file mode 100644 --- /dev/null +++ b/clang/test/OpenMP/interop_ast_print.cpp @@ -0,0 +1,169 @@ +// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=51 \ +// RUN: -fsyntax-only -verify %s + +// expected-no-diagnostics + +// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=51 \ +// RUN: -ast-print %s | FileCheck %s --check-prefix=PRINT + +// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=51 \ +// RUN: -ast-dump %s | FileCheck %s --check-prefix=DUMP + +// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=51 \ +// RUN: -emit-pch -o %t %s + +// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=51 \ +// RUN: -include-pch %t -ast-dump-all %s | FileCheck %s --check-prefix=DUMP + +// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=51 \ +// RUN: -include-pch %t -ast-print %s | FileCheck %s --check-prefix=PRINT + +#ifndef HEADER +#define HEADER + +typedef void *omp_interop_t; + +//PRINT-LABEL: void foo1( +//DUMP-LABEL: FunctionDecl {{.*}} foo1 +void foo1(int *ap, int dev) { + omp_interop_t I; + omp_interop_t &IRef = I; + + //PRINT: #pragma omp interop init(target : I) + //DUMP: OMPInteropDirective + //DUMP: OMPInitClause + //DUMP: DeclRefExpr{{.*}}'omp_interop_t'{{.*}}Var{{.*}}'I' + #pragma omp interop init(target:I) + + //PRINT: #pragma omp interop init(target : IRef) + //DUMP: OMPInteropDirective + //DUMP: OMPInitClause + //DUMP: DeclRefExpr{{.*}}'omp_interop_t'{{.*}}Var{{.*}}'IRef' + #pragma omp interop init(target:IRef) + + //PRINT: #pragma omp interop device(dev) depend(inout : ap) init(targetsync : I) + //DUMP: OMPInteropDirective + //DUMP: OMPDeviceClause + //DUMP: DeclRefExpr{{.*}}'dev' 'int' + //DUMP: OMPDependClause + //DUMP: DeclRefExpr{{.*}}'ap' 'int *' + //DUMP: OMPInitClause + //DUMP: DeclRefExpr{{.*}}'omp_interop_t'{{.*}}Var{{.*}}'I' + #pragma omp interop device(dev) depend(inout:ap) init(targetsync:I) + + //PRINT: #pragma omp interop init(prefer_type(1,2,3,4,5,6), targetsync : I) + //DUMP: OMPInteropDirective + //DUMP: OMPInitClause + //DUMP: DeclRefExpr{{.*}}'omp_interop_t'{{.*}}Var{{.*}}'I' + //DUMP: IntegerLiteral{{.*}}1 + //DUMP: IntegerLiteral{{.*}}2 + //DUMP: IntegerLiteral{{.*}}3 + //DUMP: IntegerLiteral{{.*}}4 + //DUMP: IntegerLiteral{{.*}}5 + //DUMP: IntegerLiteral{{.*}}6 + #pragma omp interop init(prefer_type(1,2,3,4,5,6),targetsync:I) + + //PRINT: #pragma omp interop init(prefer_type(2,4,6,1), targetsync : I) + //DUMP: OMPInteropDirective + //DUMP: OMPInitClause + //DUMP: DeclRefExpr{{.*}}'omp_interop_t'{{.*}}Var{{.*}}'I' + //DUMP: IntegerLiteral{{.*}}2 + //DUMP: IntegerLiteral{{.*}}4 + //DUMP: IntegerLiteral{{.*}}6 + //DUMP: IntegerLiteral{{.*}}1 + #pragma omp interop init(prefer_type(2,4,6,1),targetsync:I) + + //PRINT: #pragma omp interop init(prefer_type("cuda","cuda_driver","opencl","sycl","hip","level_zero"), targetsync : I) + //DUMP: OMPInteropDirective + //DUMP: OMPInitClause + //DUMP: DeclRefExpr{{.*}}'omp_interop_t'{{.*}}Var{{.*}}'I' + //DUMP: StringLiteral{{.*}}"cuda" + //DUMP: StringLiteral{{.*}}"cuda_driver" + //DUMP: StringLiteral{{.*}}"opencl" + //DUMP: StringLiteral{{.*}}"sycl" + //DUMP: StringLiteral{{.*}}"hip" + //DUMP: StringLiteral{{.*}}"level_zero" + #pragma omp interop init( \ + prefer_type("cuda","cuda_driver","opencl","sycl","hip","level_zero"), \ + targetsync:I) + + //PRINT: #pragma omp interop init(prefer_type("level_zero",2,4), targetsync : I) + //DUMP: OMPInteropDirective + //DUMP: OMPInitClause + //DUMP: DeclRefExpr{{.*}}'omp_interop_t'{{.*}}Var{{.*}}'I' + //DUMP: StringLiteral{{.*}}"level_zero" + //DUMP: IntegerLiteral{{.*}}2 + //DUMP: IntegerLiteral{{.*}}4 + #pragma omp interop init(prefer_type("level_zero",2,4),targetsync:I) + + omp_interop_t J; + + //PRINT: #pragma omp interop init(target : I) init(targetsync : J) + //DUMP: OMPInteropDirective + //DUMP: OMPInitClause + //DUMP: DeclRefExpr{{.*}}'omp_interop_t'{{.*}}Var{{.*}}'I' + //DUMP: OMPInitClause + //DUMP: DeclRefExpr{{.*}}'omp_interop_t'{{.*}}Var{{.*}}'J' + #pragma omp interop init(target:I) init(targetsync:J) + +} + +//DUMP: FunctionTemplateDecl{{.*}}fooTemp +//DUMP-NEXT: NonTypeTemplateParmDecl{{.*}}'int{{.*}}I +template +void fooTemp() { + omp_interop_t interop_var; + //PRINT: #pragma omp interop init(prefer_type(I,4,"level_one"), target : interop_var) + //DUMP: FunctionDecl{{.*}}fooTemp + //DUMP: OMPInteropDirective + //DUMP: OMPInitClause + //DUMP: DeclRefExpr{{.*}}'omp_interop_t'{{.*}}'interop_var' + //DUMP: DeclRefExpr{{.*}}NonTypeTemplateParm{{.*}}'I' 'int' + //DUMP: IntegerLiteral{{.*}}'int' 4 + //DUMP: StringLiteral{{.*}}"level_one" + + //PRINT: #pragma omp interop init(prefer_type(3,4,"level_one"), target : interop_var) + //DUMP: FunctionDecl{{.*}}fooTemp + //DUMP: TemplateArgument integral 3 + //DUMP: OMPInteropDirective + //DUMP: OMPInitClause + //DUMP: DeclRefExpr{{.*}}'omp_interop_t'{{.*}}'interop_var' + //DUMP: SubstNonTypeTemplateParmExpr{{.*}}'int' + //DUMP: NonTypeTemplateParmDecl{{.*}}'int'{{.*}}I + //DUMP: IntegerLiteral{{.*}}'int' 3 + //DUMP: IntegerLiteral{{.*}}'int' 4 + //DUMP: StringLiteral{{.*}}"level_one" + #pragma omp interop init(prefer_type(I,4,"level_one"), target: interop_var) +} + +//DUMP: FunctionTemplateDecl{{.*}}barTemp +//DUMP-NEXT: TemplateTypeParmDecl{{.*}}typename{{.*}}T +template +void barTemp(T t) { + //PRINT: #pragma omp interop init(prefer_type(4,"level_one"), target : t) + //DUMP: FunctionDecl{{.*}}barTemp 'void (T)' + //DUMP: ParmVarDecl{{.*}}t 'T' + //DUMP: OMPInteropDirective + //DUMP: OMPInitClause + //DUMP: DeclRefExpr{{.*}}ParmVar{{.*}}'t' 'T' + //DUMP: IntegerLiteral{{.*}}'int' 4 + //DUMP: StringLiteral{{.*}}"level_one" + #pragma omp interop init(prefer_type(4,"level_one"), target: t) + + //DUMP: FunctionDecl{{.*}}barTemp 'void (void *)' + //DUMP: TemplateArgument type 'void *' + //DUMP: ParmVarDecl{{.*}}t 'void *' + //DUMP: OMPInteropDirective + //DUMP: OMPInitClause + //DUMP: DeclRefExpr{{.*}}ParmVar{{.*}}'t' 'void *' + //PRINT: #pragma omp interop init(prefer_type(4,"level_one"), target : t) +} + +void bar() +{ + fooTemp<3>(); + omp_interop_t Ivar; + barTemp(Ivar); +} + +#endif // HEADER diff --git a/clang/test/OpenMP/interop_messages.cpp b/clang/test/OpenMP/interop_messages.cpp new file mode 100644 --- /dev/null +++ b/clang/test/OpenMP/interop_messages.cpp @@ -0,0 +1,83 @@ +// RUN: %clang_cc1 -verify -fopenmp -std=c++11 -o - -DWITHDEF %s +// RUN: %clang_cc1 -verify -fopenmp -std=c++11 -o - -DWITHOUTDEF %s + +#ifdef WITHDEF +typedef void *omp_interop_t; + +void foo(int *Ap) { + omp_interop_t InteropVar; + omp_interop_t Another; + + //expected-error@+1 {{expected interop type: 'target' and/or 'targetsync'}} + #pragma omp interop init(target,foo:InteropVar) init(target:Another) + + //expected-error@+1 {{use of undeclared identifier 'NoDeclVar'}} + #pragma omp interop init(target:NoDeclVar) init(target:Another) + + //expected-error@+2 {{expected interop type: 'target' and/or 'targetsync'}} + //expected-error@+1 {{expected expression}} + #pragma omp interop init(InteropVar) init(target:Another) + + //expected-warning@+1 {{missing ':' after interop types}} + #pragma omp interop init(target InteropVar) + + //expected-error@+1 {{expected expression}} + #pragma omp interop init(prefer_type(1,+,3),target:InteropVar) \ + init(target:Another) + + int IntVar; + struct S { int I; } SVar; + + //expected-error@+1 {{interop variable must be of type 'omp_interop_t'}} + #pragma omp interop init(prefer_type(1,"sycl",3),target:IntVar) \ + init(target:Another) + + //expected-error@+1 {{interop variable must be of type 'omp_interop_t'}} + #pragma omp interop init(prefer_type(1,"sycl",3),target:SVar) \ + init(target:Another) + + int a, b; + //expected-error@+1 {{expected variable of type 'omp_interop_t'}} + #pragma omp interop init(target:a+b) init(target:Another) + + const omp_interop_t C = (omp_interop_t)5; + //expected-error@+1 {{expected non-const variable of type 'omp_interop_t'}} + #pragma omp interop init(target:C) init(target:Another) + + //expected-error@+1 {{prefer_list item must be a string literal or constant integral expression}} + #pragma omp interop init(prefer_type(1.0),target:InteropVar) \ + init(target:Another) + + //expected-error@+1 {{prefer_list item must be a string literal or constant integral expression}} + #pragma omp interop init(prefer_type(a),target:InteropVar) \ + init(target:Another) + + //expected-error@+1 {{expected at least one 'init', 'use', 'destroy', or 'nowait' clause for '#pragma omp interop'}} + #pragma omp interop device(0) + + //expected-warning@+1 {{interop type 'target' cannot be specified more than once}} + #pragma omp interop init(target,targetsync,target:InteropVar) + + //expected-error@+1 {{'depend' clause requires the 'targetsync' interop type}} + #pragma omp interop init(target:InteropVar) depend(inout:Ap) + + //expected-error@+1 {{interop variable 'InteropVar' used in multiple action clauses}} + #pragma omp interop init(target:InteropVar) init(target:InteropVar) + + //expected-error@+1 {{directive '#pragma omp interop' cannot contain more than one 'device' clause}} + #pragma omp interop init(target:InteropVar) device(0) device(1) + + //expected-error@+1 {{argument to 'device' clause must be a non-negative integer value}} + #pragma omp interop init(target:InteropVar) device(-4) + + //expected-error@+1 {{directive '#pragma omp interop' cannot contain more than one 'nowait' clause}} + #pragma omp interop nowait init(target:InteropVar) nowait +} +#endif +#ifdef WITHOUTDEF +void foo() { + int InteropVar; + //expected-error@+1 {{'omp_interop_t' type not found; include }} + #pragma omp interop init(prefer_type(1,"sycl",3),target:InteropVar) nowait +} +#endif diff --git a/clang/test/OpenMP/taskgroup_messages.cpp b/clang/test/OpenMP/taskgroup_messages.cpp --- a/clang/test/OpenMP/taskgroup_messages.cpp +++ b/clang/test/OpenMP/taskgroup_messages.cpp @@ -71,7 +71,7 @@ foo(); } -#pragma omp taskgroup init // expected-warning {{extra tokens at the end of '#pragma omp taskgroup' are ignored}} +#pragma omp taskgroup initi // expected-warning {{extra tokens at the end of '#pragma omp taskgroup' are ignored}} ; return 0; } diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -2278,6 +2278,10 @@ void OMPClauseEnqueue::VisitOMPNogroupClause(const OMPNogroupClause *) {} +void OMPClauseEnqueue::VisitOMPInitClause(const OMPInitClause *C) { + VisitOMPClauseList(C); +} + void OMPClauseEnqueue::VisitOMPDestroyClause(const OMPDestroyClause *) {} void OMPClauseEnqueue::VisitOMPUnifiedAddressClause( @@ -5655,6 +5659,8 @@ "OMPTargetTeamsDistributeParallelForSimdDirective"); case CXCursor_OMPTargetTeamsDistributeSimdDirective: return cxstring::createRef("OMPTargetTeamsDistributeSimdDirective"); + case CXCursor_OMPInteropDirective: + return cxstring::createRef("OMPInteropDirective"); case CXCursor_OverloadCandidate: return cxstring::createRef("OverloadCandidate"); case CXCursor_TypeAliasTemplateDecl: diff --git a/clang/tools/libclang/CXCursor.cpp b/clang/tools/libclang/CXCursor.cpp --- a/clang/tools/libclang/CXCursor.cpp +++ b/clang/tools/libclang/CXCursor.cpp @@ -807,6 +807,9 @@ case Stmt::OMPTargetTeamsDistributeSimdDirectiveClass: K = CXCursor_OMPTargetTeamsDistributeSimdDirective; break; + case Stmt::OMPInteropDirectiveClass: + K = CXCursor_OMPInteropDirective; + break; case Stmt::BuiltinBitCastExprClass: K = CXCursor_BuiltinBitCastExpr; } diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp --- a/flang/lib/Semantics/check-omp-structure.cpp +++ b/flang/lib/Semantics/check-omp-structure.cpp @@ -717,6 +717,7 @@ CHECK_SIMPLE_CLAUSE(Update, OMPC_update) CHECK_SIMPLE_CLAUSE(UseDeviceAddr, OMPC_use_device_addr) CHECK_SIMPLE_CLAUSE(Write, OMPC_write) +CHECK_SIMPLE_CLAUSE(Init, OMPC_init) CHECK_REQ_SCALAR_INT_CLAUSE(Allocator, OMPC_allocator) CHECK_REQ_SCALAR_INT_CLAUSE(Grainsize, OMPC_grainsize) diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llvm/include/llvm/Frontend/OpenMP/OMP.td --- a/llvm/include/llvm/Frontend/OpenMP/OMP.td +++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td @@ -268,6 +268,9 @@ OMP_ORDER_concurrent ]; } +def OMPC_Init : Clause<"init"> { + let clangClass = "OMPInitClause"; +} def OMPC_Destroy : Clause<"destroy"> { let clangClass = "OMPDestroyClause"; } @@ -1640,6 +1643,14 @@ VersionedClause ]; } +def OMP_interop : Directive<"interop"> { + let allowedClauses = [ + VersionedClause, + VersionedClause, + VersionedClause, + VersionedClause, + ]; +} def OMP_Unknown : Directive<"unknown"> { let isDefault = true; }