Index: clang/include/clang/Basic/Attr.td =================================================================== --- clang/include/clang/Basic/Attr.td +++ clang/include/clang/Basic/Attr.td @@ -3691,7 +3691,10 @@ ExprArgument<"VariantFuncRef">, OMPTraitInfoArgument<"TraitInfos">, VariadicExprArgument<"AdjustArgsNothing">, - VariadicExprArgument<"AdjustArgsNeedDevicePtr"> + VariadicExprArgument<"AdjustArgsNeedDevicePtr">, + VariadicEnumArgument<"AppendArgs", "InteropType", + ["target", "targetsync", "target,targetsync"], + ["Target", "TargetSync", "Target_TargetSync"]> ]; let AdditionalMembers = [{ OMPTraitInfo &getTraitInfo() { return *traitInfos; } Index: clang/include/clang/Basic/DiagnosticParseKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticParseKinds.td +++ clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1359,9 +1359,11 @@ "illegal OpenMP user-defined mapper identifier">; def err_omp_mapper_expected_declarator : Error< "expected declarator on 'omp declare mapper' directive">; +def err_omp_unexpected_append_op : Error< + "unexpected operation specified in 'append_args' clause, expected 'interop'">; def err_omp_declare_variant_wrong_clause : Error< - "expected %select{'match'|'match' or 'adjust_args'}0 clause on " - "'omp declare variant' directive">; + "expected %select{'match'|'match', 'adjust_args', or 'append_args'}0 clause " + "on 'omp declare variant' directive">; def err_omp_declare_variant_duplicate_nested_trait : Error< "nested OpenMP context selector contains duplicated trait '%0'" " in selector '%1' and set '%2' with different score">; Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10750,9 +10750,14 @@ "function with '#pragma omp declare variant' has a different %select{calling convention" "|return type|constexpr specification|inline specification|storage class|" "linkage}0">; +def err_omp_declare_variant_prototype_required : Error< + "function with '#pragma omp declare variant' must have a prototype when " + "'append_args' is used">; +def err_omp_interop_type_not_found : Error< + "'omp_interop_t' must be defined when 'append_args' clause is used; include ">; def err_omp_declare_variant_incompat_types : Error< - "variant in '#pragma omp declare variant' with type %0 is incompatible with type %1" - >; + "variant in '#pragma omp declare variant' with type %0 is incompatible with" + " type %1%select{| with appended arguments}2">; def warn_omp_declare_variant_marked_as_declare_variant : Warning< "variant function in '#pragma omp declare variant' is itself marked as '#pragma omp declare variant'" >, InGroup; @@ -10821,6 +10826,8 @@ "'adjust_arg' argument %0 used in multiple clauses">; def err_omp_clause_requires_dispatch_construct : Error< "'%0' clause requires 'dispatch' context selector">; +def err_omp_append_args_with_varargs : Error< + "'append_args' is not allowed with varargs functions">; } // end of OpenMP category let CategoryName = "Related Result Type Issue" in { Index: clang/include/clang/Parse/Parser.h =================================================================== --- clang/include/clang/Parse/Parser.h +++ clang/include/clang/Parse/Parser.h @@ -3205,6 +3205,10 @@ /// Parses OpenMP context selectors. bool parseOMPContextSelectors(SourceLocation Loc, OMPTraitInfo &TI); + /// Parse an 'append_args' clause for '#pragma omp declare variant'. + bool parseOpenMPAppendArgs( + SmallVectorImpl &InterOpTypes); + /// Parse a `match` clause for an '#pragma omp declare variant'. Return true /// if there was an error. bool parseOMPDeclareVariantMatchClause(SourceLocation Loc, OMPTraitInfo &TI, Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -10964,11 +10964,14 @@ /// \param VariantRef Expression that references the variant function, which /// must be used instead of the original one, specified in \p DG. /// \param TI The trait info object representing the match clause. + /// \param NumAppendArgs The number of omp_interop_t arguments to account for + /// in checking. /// \returns None, if the function/variant function are not compatible with /// the pragma, pair of original function/variant ref expression otherwise. Optional> checkOpenMPDeclareVariantFunction(DeclGroupPtrTy DG, Expr *VariantRef, - OMPTraitInfo &TI, SourceRange SR); + OMPTraitInfo &TI, unsigned NumAppendArgs, + SourceRange SR); /// Called on well-formed '\#pragma omp declare variant' after parsing of /// the associated method/function. @@ -10977,10 +10980,19 @@ /// \param VariantRef Expression that references the variant function, which /// must be used instead of the original one, specified in \p DG. /// \param TI The context traits associated with the function variant. + /// \param AdjustArgsNothing The list of 'nothing' arguments. + /// \param AdjustArgsNeedDevicePtr The list of 'need_device_ptr' arguments. + /// \param AppendArgs The list of 'append_args' arguments. + /// \param AdjustArgsLoc The Location of an 'adjust_args' clause. + /// \param AppendArgsLoc The Location of an 'append_args' clause. + /// \param SR The SourceRange of the 'declare variant' directive. void ActOnOpenMPDeclareVariantDirective( FunctionDecl *FD, Expr *VariantRef, OMPTraitInfo &TI, ArrayRef AdjustArgsNothing, - ArrayRef AdjustArgsNeedDevicePtr, SourceRange SR); + ArrayRef AdjustArgsNeedDevicePtr, + ArrayRef AppendArgs, + SourceLocation AdjustArgsLoc, SourceLocation AppendArgsLoc, + SourceRange SR); OMPClause *ActOnOpenMPSingleExprClause(OpenMPClauseKind Kind, Expr *Expr, Index: clang/lib/AST/AttrImpl.cpp =================================================================== --- clang/lib/AST/AttrImpl.cpp +++ clang/lib/AST/AttrImpl.cpp @@ -214,6 +214,21 @@ PrintExprs(adjustArgsNeedDevicePtr_begin(), adjustArgsNeedDevicePtr_end()); OS << ")"; } + + auto PrintInteropTypes = [&OS](InteropType *Begin, InteropType *End) { + for (InteropType *I = Begin; I != End; ++I) { + if (I != Begin) + OS << ", "; + OS << "interop("; + OS << ConvertInteropTypeToStr(*I); + OS << ")"; + } + }; + if (appendArgs_size()) { + OS << " append_args("; + PrintInteropTypes(appendArgs_begin(), appendArgs_end()); + OS << ")"; + } } #include "clang/AST/AttrImpl.inc" Index: clang/lib/Basic/OpenMPKinds.cpp =================================================================== --- clang/lib/Basic/OpenMPKinds.cpp +++ clang/lib/Basic/OpenMPKinds.cpp @@ -191,6 +191,7 @@ case OMPC_uses_allocators: case OMPC_affinity: case OMPC_when: + case OMPC_append_args: break; default: break; @@ -445,6 +446,7 @@ case OMPC_uses_allocators: case OMPC_affinity: case OMPC_when: + case OMPC_append_args: break; default: break; Index: clang/lib/CodeGen/CGStmtOpenMP.cpp =================================================================== --- clang/lib/CodeGen/CGStmtOpenMP.cpp +++ clang/lib/CodeGen/CGStmtOpenMP.cpp @@ -5990,6 +5990,7 @@ case OMPC_filter: case OMPC_when: case OMPC_adjust_args: + case OMPC_append_args: llvm_unreachable("Clause is not allowed in 'omp atomic'."); } } Index: clang/lib/Parse/ParseOpenMP.cpp =================================================================== --- clang/lib/Parse/ParseOpenMP.cpp +++ clang/lib/Parse/ParseOpenMP.cpp @@ -1404,6 +1404,8 @@ OMPTraitInfo &TI = ASTCtx.getNewOMPTraitInfo(); SmallVector AdjustNothing; SmallVector AdjustNeedDevicePtr; + SmallVector AppendArgs; + SourceLocation AdjustArgsLoc, AppendArgsLoc; // At least one clause is required. if (Tok.is(tok::annot_pragma_openmp_end)) { @@ -1428,6 +1430,7 @@ IsError = parseOMPDeclareVariantMatchClause(Loc, TI, ParentTI); break; case OMPC_adjust_args: { + AdjustArgsLoc = Tok.getLocation(); ConsumeToken(); Parser::OpenMPVarListDataTy Data; SmallVector Vars; @@ -1440,6 +1443,19 @@ Vars); break; } + case OMPC_append_args: + if (!AppendArgs.empty()) { + Diag(AppendArgsLoc, diag::err_omp_more_one_clause) + << getOpenMPDirectiveName(OMPD_declare_variant) + << getOpenMPClauseName(CKind) << 0; + IsError = true; + } + if (!IsError) { + AppendArgsLoc = Tok.getLocation(); + ConsumeToken(); + IsError = parseOpenMPAppendArgs(AppendArgs); + } + break; default: llvm_unreachable("Unexpected clause for declare variant."); } @@ -1458,18 +1474,106 @@ Optional> DeclVarData = Actions.checkOpenMPDeclareVariantFunction( - Ptr, AssociatedFunction.get(), TI, + Ptr, AssociatedFunction.get(), TI, AppendArgs.size(), SourceRange(Loc, Tok.getLocation())); if (DeclVarData && !TI.Sets.empty()) Actions.ActOnOpenMPDeclareVariantDirective( DeclVarData->first, DeclVarData->second, TI, AdjustNothing, - AdjustNeedDevicePtr, SourceRange(Loc, Tok.getLocation())); + AdjustNeedDevicePtr, AppendArgs, AdjustArgsLoc, AppendArgsLoc, + SourceRange(Loc, Tok.getLocation())); // Skip the last annot_pragma_openmp_end. (void)ConsumeAnnotationToken(); } +/// Parse a list of interop-types. These are 'target' and 'targetsync'. Both +/// are allowed but duplication of either is not meaningful. +static Optional +parseInteropTypeList(Parser &P) { + const Token &Tok = P.getCurToken(); + bool HasError = false; + bool IsTarget = false; + bool IsTargetSync = false; + + while (Tok.is(tok::identifier)) { + if (Tok.getIdentifierInfo()->isStr("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) + P.Diag(Tok, diag::warn_omp_more_one_interop_type) << "target"; + IsTarget = true; + } else if (Tok.getIdentifierInfo()->isStr("targetsync")) { + if (IsTargetSync) + P.Diag(Tok, diag::warn_omp_more_one_interop_type) << "targetsync"; + IsTargetSync = true; + } else { + HasError = true; + P.Diag(Tok, diag::err_omp_expected_interop_type); + } + P.ConsumeToken(); + + if (!Tok.is(tok::comma)) + break; + P.ConsumeToken(); + } + if (HasError) + return None; + + if (!IsTarget && !IsTargetSync) { + P.Diag(Tok, diag::err_omp_expected_interop_type); + return None; + } + + // As of OpenMP 5.1,there are two interop-types, "target" and + // "targetsync". Either or both are allowed for a single interop. + if (IsTarget && IsTargetSync) + return OMPDeclareVariantAttr::Target_TargetSync; + if (IsTarget) + return OMPDeclareVariantAttr::Target; + return OMPDeclareVariantAttr::TargetSync; +} + +bool Parser::parseOpenMPAppendArgs( + SmallVectorImpl &InterOpTypes) { + bool HasError = false; + // Parse '('. + BalancedDelimiterTracker T(*this, tok::l_paren, tok::annot_pragma_openmp_end); + if (T.expectAndConsume(diag::err_expected_lparen_after, + getOpenMPClauseName(OMPC_append_args).data())) + return true; + + // Parse the list of append-ops, each is; + // interop(interop-type[,interop-type]...) + while (Tok.is(tok::identifier) && Tok.getIdentifierInfo()->isStr("interop")) { + ConsumeToken(); + BalancedDelimiterTracker IT(*this, tok::l_paren, + tok::annot_pragma_openmp_end); + if (IT.expectAndConsume(diag::err_expected_lparen_after, "interop")) + return true; + + // Parse the interop-types. + if (Optional IType = + parseInteropTypeList(*this)) + InterOpTypes.push_back(IType.getValue()); + else + HasError = true; + + IT.consumeClose(); + if (Tok.is(tok::comma)) + ConsumeToken(); + } + if (!HasError && InterOpTypes.empty()) { + HasError = true; + Diag(Tok.getLocation(), diag::err_omp_unexpected_append_op); + SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end, + StopBeforeMatch); + } + HasError = T.consumeClose() || HasError; + return HasError; +} + bool Parser::parseOMPDeclareVariantMatchClause(SourceLocation Loc, OMPTraitInfo &TI, OMPTraitInfo *ParentTI) { @@ -3342,36 +3446,15 @@ } // 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 (Optional IType = + parseInteropTypeList(*this)) { + IsTarget = IType != OMPDeclareVariantAttr::TargetSync; + IsTargetSync = IType != OMPDeclareVariantAttr::Target; + if (Tok.isNot(tok::colon)) + Diag(Tok, diag::warn_pragma_expected_colon) << "interop types"; } - 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. Index: clang/lib/Sema/SemaOpenMP.cpp =================================================================== --- clang/lib/Sema/SemaOpenMP.cpp +++ clang/lib/Sema/SemaOpenMP.cpp @@ -6810,7 +6810,8 @@ auto *OMPDeclareVariantA = OMPDeclareVariantAttr::CreateImplicit( Context, VariantFuncRef, DVScope.TI, /*NothingArgs=*/nullptr, /*NothingArgsSize=*/0, - /*NeedDevicePtrArgs=*/nullptr, /*NeedDevicePtrArgsSize=*/0); + /*NeedDevicePtrArgs=*/nullptr, /*NeedDevicePtrArgsSize=*/0, + /*AppendArgs=*/nullptr, /*AppendArgsSize=*/0); for (FunctionDecl *BaseFD : Bases) BaseFD->addAttr(OMPDeclareVariantA); } @@ -6924,6 +6925,7 @@ Optional> Sema::checkOpenMPDeclareVariantFunction(Sema::DeclGroupPtrTy DG, Expr *VariantRef, OMPTraitInfo &TI, + unsigned NumAppendArgs, SourceRange SR) { if (!DG || DG.get().isNull()) return None; @@ -7011,6 +7013,39 @@ if (TI.anyScoreOrCondition(HandleNonConstantScoresAndConditions)) return None; + QualType AdjustedFnType = FD->getType(); + if (NumAppendArgs) { + if (isa(FD->getType())) { + Diag(FD->getLocation(), diag::err_omp_declare_variant_prototype_required) + << SR; + return None; + } + // Adjust the function type to account for an extra omp_interop_t for each + // specified in the append_args clause. + const TypeDecl *TD = nullptr; + LookupResult Result(*this, &Context.Idents.get("omp_interop_t"), + SR.getBegin(), Sema::LookupOrdinaryName); + if (LookupName(Result, getCurScope())) { + NamedDecl *ND = Result.getFoundDecl(); + TD = dyn_cast_or_null(ND); + } + if (!TD) { + Diag(SR.getBegin(), diag::err_omp_interop_type_not_found) << SR; + return None; + } + QualType InteropType = QualType(TD->getTypeForDecl(), 0); + auto *PTy = cast(FD->getType()); + if (PTy->isVariadic()) { + Diag(FD->getLocation(), diag::err_omp_append_args_with_varargs) << SR; + return None; + } + llvm::SmallVector Params; + Params.append(PTy->param_type_begin(), PTy->param_type_end()); + Params.insert(Params.end(), NumAppendArgs, InteropType); + AdjustedFnType = Context.getFunctionType(PTy->getReturnType(), Params, + PTy->getExtProtoInfo()); + } + // Convert VariantRef expression to the type of the original function to // resolve possible conflicts. ExprResult VariantRefCast = VariantRef; @@ -7020,7 +7055,7 @@ if (Method && !Method->isStatic()) { const Type *ClassType = Context.getTypeDeclType(Method->getParent()).getTypePtr(); - FnPtrType = Context.getMemberPointerType(FD->getType(), ClassType); + FnPtrType = Context.getMemberPointerType(AdjustedFnType, ClassType); ExprResult ER; { // Build adrr_of unary op to correctly handle type checks for member @@ -7036,7 +7071,7 @@ } VariantRef = ER.get(); } else { - FnPtrType = Context.getPointerType(FD->getType()); + FnPtrType = Context.getPointerType(AdjustedFnType); } QualType VarianPtrType = Context.getPointerType(VariantRef->getType()); if (VarianPtrType.getUnqualifiedType() != FnPtrType.getUnqualifiedType()) { @@ -7051,7 +7086,7 @@ diag::err_omp_declare_variant_incompat_types) << VariantRef->getType() << ((Method && !Method->isStatic()) ? FnPtrType : FD->getType()) - << VariantRef->getSourceRange(); + << (NumAppendArgs ? 1 : 0) << VariantRef->getSourceRange(); return None; } VariantRefCast = PerformImplicitConversion( @@ -7093,11 +7128,12 @@ // Check if function types are compatible in C. if (!LangOpts.CPlusPlus) { QualType NewType = - Context.mergeFunctionTypes(FD->getType(), NewFD->getType()); + Context.mergeFunctionTypes(AdjustedFnType, NewFD->getType()); if (NewType.isNull()) { Diag(VariantRef->getExprLoc(), diag::err_omp_declare_variant_incompat_types) - << NewFD->getType() << FD->getType() << VariantRef->getSourceRange(); + << NewFD->getType() << FD->getType() << (NumAppendArgs ? 1 : 0) + << VariantRef->getSourceRange(); return None; } if (NewType->isFunctionProtoType()) { @@ -7186,7 +7222,10 @@ void Sema::ActOnOpenMPDeclareVariantDirective( FunctionDecl *FD, Expr *VariantRef, OMPTraitInfo &TI, ArrayRef AdjustArgsNothing, - ArrayRef AdjustArgsNeedDevicePtr, SourceRange SR) { + ArrayRef AdjustArgsNeedDevicePtr, + ArrayRef AppendArgs, + SourceLocation AdjustArgsLoc, SourceLocation AppendArgsLoc, + SourceRange SR) { // OpenMP 5.1 [2.3.5, declare variant directive, Restrictions] // An adjust_args clause or append_args clause can only be specified if the @@ -7197,15 +7236,18 @@ llvm::append_range(AllAdjustArgs, AdjustArgsNothing); llvm::append_range(AllAdjustArgs, AdjustArgsNeedDevicePtr); - if (!AllAdjustArgs.empty()) { + if (!AllAdjustArgs.empty() || !AppendArgs.empty()) { VariantMatchInfo VMI; TI.getAsVariantMatchInfo(Context, VMI); if (!llvm::is_contained( VMI.ConstructTraits, llvm::omp::TraitProperty::construct_dispatch_dispatch)) { - Diag(AllAdjustArgs[0]->getExprLoc(), - diag::err_omp_clause_requires_dispatch_construct) - << getOpenMPClauseName(OMPC_adjust_args); + if (!AllAdjustArgs.empty()) + Diag(AdjustArgsLoc, diag::err_omp_clause_requires_dispatch_construct) + << getOpenMPClauseName(OMPC_adjust_args); + if (!AppendArgs.empty()) + Diag(AppendArgsLoc, diag::err_omp_clause_requires_dispatch_construct) + << getOpenMPClauseName(OMPC_append_args); return; } } @@ -7242,7 +7284,9 @@ Context, VariantRef, &TI, const_cast(AdjustArgsNothing.data()), AdjustArgsNothing.size(), const_cast(AdjustArgsNeedDevicePtr.data()), - AdjustArgsNeedDevicePtr.size(), SR); + AdjustArgsNeedDevicePtr.size(), + const_cast(AppendArgs.data()), + AppendArgs.size(), SR); FD->addAttr(NewAttr); } Index: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -441,6 +441,7 @@ // begin declare variant` (which use implicit attributes). Optional> DeclVarData = S.checkOpenMPDeclareVariantFunction(S.ConvertDeclToDeclGroup(New), E, TI, + Attr.appendArgs_size(), Attr.getRange()); if (!DeclVarData) @@ -483,6 +484,8 @@ SmallVector NothingExprs; SmallVector NeedDevicePtrExprs; + SmallVector AppendArgs; + for (Expr *E : Attr.adjustArgsNothing()) { ExprResult ER = Subst(E); if (ER.isInvalid()) @@ -495,8 +498,12 @@ continue; NeedDevicePtrExprs.push_back(ER.get()); } - S.ActOnOpenMPDeclareVariantDirective(FD, E, TI, NothingExprs, - NeedDevicePtrExprs, Attr.getRange()); + for (auto A : Attr.appendArgs()) + AppendArgs.push_back(A); + + S.ActOnOpenMPDeclareVariantDirective( + FD, E, TI, NothingExprs, NeedDevicePtrExprs, AppendArgs, SourceLocation(), + SourceLocation(), Attr.getRange()); } static void instantiateDependentAMDGPUFlatWorkGroupSizeAttr( Index: clang/test/OpenMP/declare_variant_clauses_ast_print.cpp =================================================================== --- clang/test/OpenMP/declare_variant_clauses_ast_print.cpp +++ clang/test/OpenMP/declare_variant_clauses_ast_print.cpp @@ -104,4 +104,82 @@ Foo(A, B); } +typedef void *omp_interop_t; + +void bar_v1(float* F1, float *F2, omp_interop_t); +void bar_v2(float* F1, float *F2, omp_interop_t, omp_interop_t); + +//PRINT: #pragma omp declare variant(bar_v1) match(construct={dispatch}) append_args(interop(target,targetsync)) +//DUMP: FunctionDecl{{.*}}bar1 'void (float *, float *)' +//DUMP: OMPDeclareVariantAttr{{.*}}construct={dispatch} Target_TargetSync +//DUMP: DeclRefExpr{{.*}}bar_v1 +#pragma omp declare variant(bar_v1) match(construct={dispatch}) \ + append_args(interop(target,targetsync)) +void bar1(float *FF1, float *FF2) { return; } + +//PRINT: #pragma omp declare variant(bar_v1) match(construct={dispatch}) append_args(interop(targetsync)) +//DUMP: FunctionDecl{{.*}}bar2 'void (float *, float *)' +//DUMP: OMPDeclareVariantAttr{{.*}}construct={dispatch} TargetSync +//DUMP: DeclRefExpr{{.*}}bar_v1 +#pragma omp declare variant(bar_v1) match(construct={dispatch}) \ + append_args(interop(targetsync)) +void bar2(float *FF1, float *FF2) { return; } + +//PRINT: #pragma omp declare variant(bar_v1) match(construct={dispatch}) append_args(interop(target)) +//DUMP: FunctionDecl{{.*}}bar3 'void (float *, float *)' +//DUMP: OMPDeclareVariantAttr{{.*}}construct={dispatch} Target +//DUMP: DeclRefExpr{{.*}}bar_v1 +#pragma omp declare variant(bar_v1) match(construct={dispatch}) \ + append_args(interop(target)) +void bar3(float *FF1, float *FF2) { return; } + +//PRINT: #pragma omp declare variant(bar_v2) match(construct={dispatch}) append_args(interop(target), interop(targetsync)) +//DUMP: FunctionDecl{{.*}}bar4 'void (float *, float *)' +//DUMP: OMPDeclareVariantAttr{{.*}}construct={dispatch} Target TargetSync +//DUMP: DeclRefExpr{{.*}}bar_v2 +#pragma omp declare variant(bar_v2) match(construct={dispatch}) \ + append_args(interop(target), interop(targetsync)) +void bar4(float *FF1, float *FF2) { return; } + +//PRINT: #pragma omp declare variant(bar_v2) match(construct={dispatch}) append_args(interop(targetsync), interop(target)) +//DUMP: FunctionDecl{{.*}}bar5 'void (float *, float *)' +//DUMP: OMPDeclareVariantAttr{{.*}}construct={dispatch} TargetSync Target +//DUMP: DeclRefExpr{{.*}}bar_v2 +#pragma omp declare variant(bar_v2) match(construct={dispatch}) \ + append_args(interop(targetsync), interop(target)) +void bar5(float *FF1, float *FF2) { return; } + +//PRINT: class A { +//DUMP: CXXRecordDecl{{.*}}class A definition +class A { +public: + void memberfoo_v1(float *A, float *B, int *I, omp_interop_t IOp); + //PRINT: #pragma omp declare variant(memberfoo_v1) match(construct={dispatch}) append_args(interop(target)) + //DUMP: CXXMethodDecl{{.*}}memberbar 'void (float *, float *, int *)' + //DUMP: OMPDeclareVariantAttr{{.*}}Implicit construct={dispatch} Target + //DUMP: DeclRefExpr{{.*}}'memberfoo_v1' 'void (float *, float *, int *, omp_interop_t)' + #pragma omp declare variant(memberfoo_v1) match(construct={dispatch}) \ + append_args(interop(target)) + void memberbar(float *A, float *B, int *I) { return; } + + static void smemberfoo_v1(float *A, float *B, int *I, omp_interop_t IOp); + //PRINT: #pragma omp declare variant(smemberfoo_v1) match(construct={dispatch}) append_args(interop(target)) + //DUMP: CXXMethodDecl{{.*}}smemberbar 'void (float *, float *, int *)' static + //DUMP: OMPDeclareVariantAttr{{.*}}Implicit construct={dispatch} Target + //DUMP: DeclRefExpr{{.*}}'smemberfoo_v1' 'void (float *, float *, int *, omp_interop_t)' + #pragma omp declare variant(smemberfoo_v1) match(construct={dispatch}) \ + append_args(interop(target)) + static void smemberbar(float *A, float *B, int *I) { return; } +}; + +template void templatefoo_v1(const T& t, omp_interop_t I); +template void templatebar(const T& t) {} + +//PRINT: #pragma omp declare variant(templatefoo_v1) match(construct={dispatch}) append_args(interop(target)) +//DUMP: FunctionDecl{{.*}}templatebar 'void (const int &)' +//DUMP: OMPDeclareVariantAttr{{.*}}Implicit construct={dispatch} Target +//DUMP: DeclRefExpr{{.*}}'templatefoo_v1' 'void (const int &, omp_interop_t)' +#pragma omp declare variant(templatefoo_v1) match(construct={dispatch}) \ + append_args(interop(target)) +void templatebar(const int &t) {} #endif // HEADER Index: clang/test/OpenMP/declare_variant_clauses_messages.cpp =================================================================== --- clang/test/OpenMP/declare_variant_clauses_messages.cpp +++ clang/test/OpenMP/declare_variant_clauses_messages.cpp @@ -1,14 +1,107 @@ // RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=51 -DOMP51 -std=c++11 -o - %s - +// RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=51 -DOMP51 -std=c++11 \ +// RUN: -DNO_INTEROP_T_DEF -o - %s // RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=50 -DOMP50 -std=c++11 -o - %s +// RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=51 -DOMP51 -DC -x c -o - %s + +#ifdef NO_INTEROP_T_DEF +void foo_v1(float *, void *); +// expected-error@+1 {{'omp_interop_t' must be defined when 'append_args' clause is used; include }} +#pragma omp declare variant(foo_v1) append_args(interop(target)) \ + match(construct={dispatch}) +void foo_v1(float *); +#else +typedef void *omp_interop_t; int Other; +#ifdef OMP51 +#ifdef __cplusplus +class A { +public: + void memberfoo_v0(float *A, float *B, int *I); + void memberfoo_v1(float *A, float *B, int *I, omp_interop_t IOp); + + // expected-error@+1 {{variant in '#pragma omp declare variant' with type 'void (A::*)(float *, float *, int *)' is incompatible with type 'void (A::*)(float *, float *, int *, omp_interop_t)' with appended arguments}} + #pragma omp declare variant(memberfoo_v0) match(construct={dispatch}) \ + append_args(interop(target)) + + // expected-error@+1 {{variant in '#pragma omp declare variant' with type 'void (A::*)(float *, float *, int *, omp_interop_t)' is incompatible with type 'void (A::*)(float *, float *, int *, omp_interop_t, omp_interop_t)' with appended arguments}} + #pragma omp declare variant(memberfoo_v1) match(construct={dispatch}) \ + append_args(interop(target),interop(target)) + void memberbar(float *A, float *B, int *I) { return; } + + static void smemberfoo_v0(float *A, float *B, int *I); + static void smemberfoo_v1(float *A, float *B, int *I, omp_interop_t IOp); + + // expected-error@+1 {{variant in '#pragma omp declare variant' with type 'void (float *, float *, int *)' is incompatible with type 'void (float *, float *, int *)' with appended arguments}} + #pragma omp declare variant(smemberfoo_v0) match(construct={dispatch}) \ + append_args(interop(target)) + + // expected-error@+1 {{variant in '#pragma omp declare variant' with type 'void (float *, float *, int *, omp_interop_t)' (aka 'void (float *, float *, int *, void *)') is incompatible with type 'void (float *, float *, int *)' with appended arguments}} + #pragma omp declare variant(smemberfoo_v1) match(construct={dispatch}) \ + append_args(interop(target),interop(target)) + static void smemberbar(float *A, float *B, int *I) { return; } + + virtual void vmemberfoo_v0(float *A, float *B, int *I); + virtual void vmemberfoo_v1(float *A, float *B, int *I, omp_interop_t IOp); + + // expected-error@+1 {{variant in '#pragma omp declare variant' with type 'void (A::*)(float *, float *, int *)' is incompatible with type 'void (A::*)(float *, float *, int *, omp_interop_t)' with appended arguments}} + #pragma omp declare variant(vmemberfoo_v0) match(construct={dispatch}) \ + append_args(interop(target)) + + #pragma omp declare variant(vmemberfoo_v1) match(construct={dispatch}) \ + append_args(interop(target)) + + // expected-error@+1 {{'#pragma omp declare variant' does not support virtual functions}} + virtual void vmemberbar(float *A, float *B, int *I) { return; } + + virtual void pvmemberfoo_v0(float *A, float *B, int *I) = 0; + virtual void pvmemberfoo_v1(float *A, float *B, int *I, omp_interop_t IOp) = 0; + + // expected-error@+1 {{variant in '#pragma omp declare variant' with type 'void (A::*)(float *, float *, int *)' is incompatible with type 'void (A::*)(float *, float *, int *, omp_interop_t)' with appended arguments}} + #pragma omp declare variant(pvmemberfoo_v0) match(construct={dispatch}) \ + append_args(interop(target)) + + #pragma omp declare variant(pvmemberfoo_v1) match(construct={dispatch}) \ + append_args(interop(target)) + + // expected-error@+1 {{'#pragma omp declare variant' does not support virtual functions}} + virtual void pvmemberbar(float *A, float *B, int *I) = 0; +}; + +template void templatefoo_v0(const T& t); +template void templatefoo_v1(const T& t, omp_interop_t I); +template void templatebar(const T& t) {} + +// expected-error@+1 {{variant in '#pragma omp declare variant' with type '' is incompatible with type 'void (const int &)' with appended arguments}} +#pragma omp declare variant(templatefoo_v0) match(construct={dispatch}) \ + append_args(interop(target)) + +// expected-error@+1 {{variant in '#pragma omp declare variant' with type '' is incompatible with type 'void (const int &)' with appended arguments}} +#pragma omp declare variant(templatefoo_v1) match(construct={dispatch}) \ + append_args(interop(target),interop(target)) +void templatebar(const int &t) {} +#endif // __cplusplus +#endif // OMP51 + void foo_v1(float *AAA, float *BBB, int *I) { return; } void foo_v2(float *AAA, float *BBB, int *I) { return; } void foo_v3(float *AAA, float *BBB, int *I) { return; } +void foo_v4(float *AAA, float *BBB, int *I, omp_interop_t IOp) { return; } #ifdef OMP51 +void vararg_foo(const char *fmt, omp_interop_t it, ...); +// expected-error@+3 {{'append_args' is not allowed with varargs functions}} +#pragma omp declare variant(vararg_foo) match(construct={dispatch}) \ + append_args(interop(target)) +void vararg_bar(const char *fmt, ...) { return; } + +// expected-error@+1 {{variant in '#pragma omp declare variant' with type 'void (const char *, omp_interop_t, ...)' (aka 'void (const char *, void *, ...)') is incompatible with type 'void (const char *)' with appended arguments}} +#pragma omp declare variant(vararg_foo) match(construct={dispatch}) \ + append_args(interop(target)) +void vararg_bar2(const char *fmt) { return; } + // expected-error@+3 {{'adjust_arg' argument 'AAA' used in multiple clauses}} #pragma omp declare variant(foo_v1) \ match(construct={dispatch}, device={arch(arm)}) \ @@ -36,12 +129,79 @@ // expected-error@+2 {{'adjust_args' clause requires 'dispatch' context selector}} #pragma omp declare variant(foo_v3) \ adjust_args(nothing:BBB) match(device={arch(ppc)}) + +// expected-error@+1 {{expected 'match', 'adjust_args', or 'append_args' clause on 'omp declare variant' directive}} +#pragma omp declare variant(foo_v1) + +// expected-error@+1 {{expected 'match', 'adjust_args', or 'append_args' clause on 'omp declare variant' directive}} +#pragma omp declare variant(foo_v1) other + +// expected-error@+2 {{unexpected operation specified in 'append_args' clause, expected 'interop'}} +#pragma omp declare variant(foo_v1) match(construct={dispatch}) \ + append_args(foobar(target)) + +// expected-error@+2 {{directive '#pragma omp declare variant' cannot contain more than one 'append_args' clause}} +#pragma omp declare variant(foo_v1) match(construct={dispatch}) \ + append_args(interop(target)) \ + append_args(interop(targetsync)) + +// expected-error@+2 {{'append_args' clause requires 'dispatch' context selector}} +#pragma omp declare variant(foo_v4) \ + append_args(interop(target)) match(construct={target}) + +// expected-error@+2 {{'append_args' clause requires 'dispatch' context selector}} +#pragma omp declare variant(foo_v4) \ + match(construct={target}) append_args(interop(target)) + +// expected-warning@+2 {{interop type 'target' cannot be specified more than once}} +#pragma omp declare variant(foo_v4) match(construct={dispatch}) \ + append_args(interop(target,target)) + +// expected-warning@+2 {{interop type 'targetsync' cannot be specified more than once}} +#pragma omp declare variant(foo_v4) match(construct={dispatch}) \ + append_args(interop(targetsync,targetsync)) + +// expected-error@+2 {{expected interop type: 'target' and/or 'targetsync'}} +#pragma omp declare variant(foo_v4) match(construct={dispatch}) \ + append_args(interop()) + +// expected-error@+2 {{expected interop type: 'target' and/or 'targetsync'}} +#pragma omp declare variant(foo_v4) match(construct={dispatch}) \ + append_args(interop(somethingelse)) + +// expected-error@+1 {{variant in '#pragma omp declare variant' with type 'void (float *, float *, int *)' is incompatible with type 'void (float *, float *, int *)' with appended arguments}} +#pragma omp declare variant(foo_v1) match(construct={dispatch}) \ + append_args(interop(target)) + +// expected-error@+1 {{variant in '#pragma omp declare variant' with type 'void (float *, float *, int *)' is incompatible with type 'void (float *, float *, int *)' with appended arguments}} +#pragma omp declare variant(foo_v1) match(construct={dispatch}) \ + append_args(interop(target),interop(targetsync)) + +// expected-error@+1 {{variant in '#pragma omp declare variant' with type 'void (float *, float *, int *, omp_interop_t)' (aka 'void (float *, float *, int *, void *)') is incompatible with type 'void (float *, float *, int *)' with appended arguments}} +#pragma omp declare variant(foo_v4) match(construct={dispatch}) \ + append_args(interop(target),interop(targetsync)) + +// expected-error@+1 {{variant in '#pragma omp declare variant' with type 'void (float *, float *, int *, omp_interop_t)' (aka 'void (float *, float *, int *, void *)') is incompatible with type 'void (float *, float *, int *)'}} +#pragma omp declare variant(foo_v4) match(construct={dispatch}) + #endif // OMP51 #ifdef OMP50 // expected-error@+2 {{expected 'match' clause on 'omp declare variant' directive}} #pragma omp declare variant(foo_v1) \ adjust_args(need_device_ptr:AAA) match(device={arch(arm)}) +// expected-error@+2 {{expected 'match' clause on 'omp declare variant' directive}} +#pragma omp declare variant(foo_v1) \ + append_args(interop(target)) match(device={arch(arm)}) #endif // OMP50 void foo(float *AAA, float *BBB, int *I) { return; } +#endif // NO_INTEROP_T_DEF + +#ifdef C +void c_variant(omp_interop_t); +// expected-error@+3 {{function with '#pragma omp declare variant' must have a prototype when 'append_args' is used}} +#pragma omp declare variant(foo_v1) \ + append_args(interop(target)) match(construct={dispatch}) +void c_base() {} +#endif Index: clang/test/OpenMP/declare_variant_messages.cpp =================================================================== --- clang/test/OpenMP/declare_variant_messages.cpp +++ clang/test/OpenMP/declare_variant_messages.cpp @@ -278,6 +278,12 @@ void baz(); void bar(); void bar(int); + virtual void car(); + virtual void dar() = 0; + +#pragma omp declare variant(SpecialFuncs::car) match(construct={dispatch}) // expected-error {{'#pragma omp declare variant' does not support virtual functions}} +#pragma omp declare variant(SpecialFuncs::dar) match(construct={dispatch}) // expected-error {{'#pragma omp declare variant' does not support virtual functions}} + #pragma omp declare variant(SpecialFuncs::baz) match(implementation = {}) // expected-warning {{expected identifier or string literal describing a context selector; selector skipped}} expected-note {{context selector options are: 'vendor' 'extension' 'unified_address' 'unified_shared_memory' 'reverse_offload' 'dynamic_allocators' 'atomic_default_mem_order'}} expected-note {{the ignored selector spans until here}} #pragma omp declare variant(SpecialFuncs::bar) match(xxx = {}) // expected-warning {{'xxx' is not a valid context set in a `declare variant`; set ignored}} expected-note {{context set options are: 'construct' 'device' 'implementation' 'user'}} expected-note {{the ignored set spans until here}} Index: flang/lib/Semantics/check-omp-structure.cpp =================================================================== --- flang/lib/Semantics/check-omp-structure.cpp +++ flang/lib/Semantics/check-omp-structure.cpp @@ -1478,6 +1478,7 @@ CHECK_SIMPLE_CLAUSE(Filter, OMPC_filter) CHECK_SIMPLE_CLAUSE(When, OMPC_when) CHECK_SIMPLE_CLAUSE(AdjustArgs, OMPC_adjust_args) +CHECK_SIMPLE_CLAUSE(AppendArgs, OMPC_append_args) CHECK_REQ_SCALAR_INT_CLAUSE(Grainsize, OMPC_grainsize) CHECK_REQ_SCALAR_INT_CLAUSE(NumTasks, OMPC_num_tasks) Index: llvm/include/llvm/Frontend/OpenMP/OMP.td =================================================================== --- llvm/include/llvm/Frontend/OpenMP/OMP.td +++ llvm/include/llvm/Frontend/OpenMP/OMP.td @@ -315,6 +315,7 @@ def OMPC_DeviceType : Clause<"device_type"> {} def OMPC_Match : Clause<"match"> {} def OMPC_AdjustArgs : Clause<"adjust_args"> { } +def OMPC_AppendArgs : Clause<"append_args"> { } def OMPC_Depobj : Clause<"depobj"> { let clangClass = "OMPDepobjClause"; let isImplicit = true; @@ -1520,7 +1521,8 @@ VersionedClause ]; let allowedExclusiveClauses = [ - VersionedClause + VersionedClause, + VersionedClause ]; } def OMP_MasterTaskloop : Directive<"master taskloop"> {