[llvm] d869939 - [OPENMP51]Initial parsing/sema for append_args clause for 'declare variant'

Mike Rice via llvm-commits llvm-commits at lists.llvm.org
Mon Oct 25 09:43:21 PDT 2021


Author: Mike Rice
Date: 2021-10-25T09:38:50-07:00
New Revision: d8699391a431af5730fe36ac4b05840020c42203

URL: https://github.com/llvm/llvm-project/commit/d8699391a431af5730fe36ac4b05840020c42203
DIFF: https://github.com/llvm/llvm-project/commit/d8699391a431af5730fe36ac4b05840020c42203.diff

LOG: [OPENMP51]Initial parsing/sema for append_args clause for 'declare variant'

Adds initial parsing and sema for the 'append_args' clause.

Note that an AST clause is not created as it instead adds its values
to the OMPDeclareVariantAttr.

Differential Revision: https://reviews.llvm.org/D111854

Added: 
    

Modified: 
    clang/include/clang/Basic/Attr.td
    clang/include/clang/Basic/DiagnosticParseKinds.td
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/include/clang/Parse/Parser.h
    clang/include/clang/Sema/Sema.h
    clang/lib/AST/AttrImpl.cpp
    clang/lib/Basic/OpenMPKinds.cpp
    clang/lib/CodeGen/CGStmtOpenMP.cpp
    clang/lib/Parse/ParseOpenMP.cpp
    clang/lib/Sema/SemaOpenMP.cpp
    clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
    clang/test/OpenMP/declare_variant_clauses_ast_print.cpp
    clang/test/OpenMP/declare_variant_clauses_messages.cpp
    clang/test/OpenMP/declare_variant_messages.cpp
    flang/lib/Semantics/check-omp-structure.cpp
    llvm/include/llvm/Frontend/OpenMP/OMP.td

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index f58217f8f44a..31ca7d21b902 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -3692,7 +3692,10 @@ def OMPDeclareVariant : InheritableAttr {
     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; }

diff  --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 367da91afbae..297e974c6488 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1359,9 +1359,11 @@ def err_omp_mapper_illegal_identifier : Error<
   "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 
diff erent score">;

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 7cee98c8a64c..370ccd01ee8e 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10747,9 +10747,14 @@ def err_omp_declare_variant_
diff  : Error<
   "function with '#pragma omp declare variant' has a 
diff erent %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 <omp.h>">;
 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<SourceUsesOpenMP>;
@@ -10818,6 +10823,8 @@ def err_omp_adjust_arg_multiple_clauses : Error<
   "'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 {

diff  --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 62c0a0d4c0a8..92a703b42173 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -3205,6 +3205,10 @@ class Parser : public CodeCompletionHandler {
   /// Parses OpenMP context selectors.
   bool parseOMPContextSelectors(SourceLocation Loc, OMPTraitInfo &TI);
 
+  /// Parse an 'append_args' clause for '#pragma omp declare variant'.
+  bool parseOpenMPAppendArgs(
+      SmallVectorImpl<OMPDeclareVariantAttr::InteropType> &InterOpTypes);
+
   /// Parse a `match` clause for an '#pragma omp declare variant'. Return true
   /// if there was an error.
   bool parseOMPDeclareVariantMatchClause(SourceLocation Loc, OMPTraitInfo &TI,

diff  --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 4d5f9919ce9e..c3d7c14f53d3 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -10964,11 +10964,14 @@ class Sema final {
   /// \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<std::pair<FunctionDecl *, Expr *>>
   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 @@ class Sema final {
   /// \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<Expr *> AdjustArgsNothing,
-      ArrayRef<Expr *> AdjustArgsNeedDevicePtr, SourceRange SR);
+      ArrayRef<Expr *> AdjustArgsNeedDevicePtr,
+      ArrayRef<OMPDeclareVariantAttr::InteropType> AppendArgs,
+      SourceLocation AdjustArgsLoc, SourceLocation AppendArgsLoc,
+      SourceRange SR);
 
   OMPClause *ActOnOpenMPSingleExprClause(OpenMPClauseKind Kind,
                                          Expr *Expr,

diff  --git a/clang/lib/AST/AttrImpl.cpp b/clang/lib/AST/AttrImpl.cpp
index e7b9828b4e34..a3b46752c511 100644
--- a/clang/lib/AST/AttrImpl.cpp
+++ b/clang/lib/AST/AttrImpl.cpp
@@ -214,6 +214,21 @@ void OMPDeclareVariantAttr::printPrettyPragma(
     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"

diff  --git a/clang/lib/Basic/OpenMPKinds.cpp b/clang/lib/Basic/OpenMPKinds.cpp
index 4c209fb9cf6f..d9ddb7ea0fc3 100644
--- a/clang/lib/Basic/OpenMPKinds.cpp
+++ b/clang/lib/Basic/OpenMPKinds.cpp
@@ -191,6 +191,7 @@ unsigned clang::getOpenMPSimpleClauseType(OpenMPClauseKind Kind, StringRef Str,
   case OMPC_uses_allocators:
   case OMPC_affinity:
   case OMPC_when:
+  case OMPC_append_args:
     break;
   default:
     break;
@@ -445,6 +446,7 @@ const char *clang::getOpenMPSimpleClauseTypeName(OpenMPClauseKind Kind,
   case OMPC_uses_allocators:
   case OMPC_affinity:
   case OMPC_when:
+  case OMPC_append_args:
     break;
   default:
     break;

diff  --git a/clang/lib/CodeGen/CGStmtOpenMP.cpp b/clang/lib/CodeGen/CGStmtOpenMP.cpp
index 8ae45cae2d43..a3b509f91414 100644
--- a/clang/lib/CodeGen/CGStmtOpenMP.cpp
+++ b/clang/lib/CodeGen/CGStmtOpenMP.cpp
@@ -5990,6 +5990,7 @@ static void emitOMPAtomicExpr(CodeGenFunction &CGF, OpenMPClauseKind Kind,
   case OMPC_filter:
   case OMPC_when:
   case OMPC_adjust_args:
+  case OMPC_append_args:
     llvm_unreachable("Clause is not allowed in 'omp atomic'.");
   }
 }

diff  --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp
index 68683b0f2bd9..d38e088a4706 100644
--- a/clang/lib/Parse/ParseOpenMP.cpp
+++ b/clang/lib/Parse/ParseOpenMP.cpp
@@ -1404,6 +1404,8 @@ void Parser::ParseOMPDeclareVariantClauses(Parser::DeclGroupPtrTy Ptr,
   OMPTraitInfo &TI = ASTCtx.getNewOMPTraitInfo();
   SmallVector<Expr *, 6> AdjustNothing;
   SmallVector<Expr *, 6> AdjustNeedDevicePtr;
+  SmallVector<OMPDeclareVariantAttr::InteropType, 3> AppendArgs;
+  SourceLocation AdjustArgsLoc, AppendArgsLoc;
 
   // At least one clause is required.
   if (Tok.is(tok::annot_pragma_openmp_end)) {
@@ -1428,6 +1430,7 @@ void Parser::ParseOMPDeclareVariantClauses(Parser::DeclGroupPtrTy Ptr,
         IsError = parseOMPDeclareVariantMatchClause(Loc, TI, ParentTI);
         break;
       case OMPC_adjust_args: {
+        AdjustArgsLoc = Tok.getLocation();
         ConsumeToken();
         Parser::OpenMPVarListDataTy Data;
         SmallVector<Expr *> Vars;
@@ -1440,6 +1443,19 @@ void Parser::ParseOMPDeclareVariantClauses(Parser::DeclGroupPtrTy Ptr,
                              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 @@ void Parser::ParseOMPDeclareVariantClauses(Parser::DeclGroupPtrTy Ptr,
 
   Optional<std::pair<FunctionDecl *, Expr *>> 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<OMPDeclareVariantAttr::InteropType>
+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<OMPDeclareVariantAttr::InteropType> &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<OMPDeclareVariantAttr::InteropType> 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 @@ OMPClause *Parser::ParseOpenMPInteropClause(OpenMPClauseKind Kind,
     }
 
     // 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<OMPDeclareVariantAttr::InteropType> 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.

diff  --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index ccd17fc5102d..3ee913206457 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -6803,7 +6803,8 @@ void Sema::ActOnFinishedFunctionDefinitionInOpenMPDeclareVariantScope(
   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);
 }
@@ -6917,6 +6918,7 @@ ExprResult Sema::ActOnOpenMPCall(ExprResult Call, Scope *Scope,
 Optional<std::pair<FunctionDecl *, Expr *>>
 Sema::checkOpenMPDeclareVariantFunction(Sema::DeclGroupPtrTy DG,
                                         Expr *VariantRef, OMPTraitInfo &TI,
+                                        unsigned NumAppendArgs,
                                         SourceRange SR) {
   if (!DG || DG.get().isNull())
     return None;
@@ -7004,6 +7006,39 @@ Sema::checkOpenMPDeclareVariantFunction(Sema::DeclGroupPtrTy DG,
   if (TI.anyScoreOrCondition(HandleNonConstantScoresAndConditions))
     return None;
 
+  QualType AdjustedFnType = FD->getType();
+  if (NumAppendArgs) {
+    if (isa<FunctionNoProtoType>(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<TypeDecl>(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<FunctionProtoType>(FD->getType());
+    if (PTy->isVariadic()) {
+      Diag(FD->getLocation(), diag::err_omp_append_args_with_varargs) << SR;
+      return None;
+    }
+    llvm::SmallVector<QualType, 8> 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;
@@ -7013,7 +7048,7 @@ Sema::checkOpenMPDeclareVariantFunction(Sema::DeclGroupPtrTy DG,
     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
@@ -7029,7 +7064,7 @@ Sema::checkOpenMPDeclareVariantFunction(Sema::DeclGroupPtrTy DG,
       }
       VariantRef = ER.get();
     } else {
-      FnPtrType = Context.getPointerType(FD->getType());
+      FnPtrType = Context.getPointerType(AdjustedFnType);
     }
     QualType VarianPtrType = Context.getPointerType(VariantRef->getType());
     if (VarianPtrType.getUnqualifiedType() != FnPtrType.getUnqualifiedType()) {
@@ -7044,7 +7079,7 @@ Sema::checkOpenMPDeclareVariantFunction(Sema::DeclGroupPtrTy DG,
              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(
@@ -7086,11 +7121,12 @@ Sema::checkOpenMPDeclareVariantFunction(Sema::DeclGroupPtrTy DG,
   // 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()) {
@@ -7179,7 +7215,10 @@ Sema::checkOpenMPDeclareVariantFunction(Sema::DeclGroupPtrTy DG,
 void Sema::ActOnOpenMPDeclareVariantDirective(
     FunctionDecl *FD, Expr *VariantRef, OMPTraitInfo &TI,
     ArrayRef<Expr *> AdjustArgsNothing,
-    ArrayRef<Expr *> AdjustArgsNeedDevicePtr, SourceRange SR) {
+    ArrayRef<Expr *> AdjustArgsNeedDevicePtr,
+    ArrayRef<OMPDeclareVariantAttr::InteropType> 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
@@ -7190,15 +7229,18 @@ void Sema::ActOnOpenMPDeclareVariantDirective(
   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;
     }
   }
@@ -7235,7 +7277,9 @@ void Sema::ActOnOpenMPDeclareVariantDirective(
       Context, VariantRef, &TI, const_cast<Expr **>(AdjustArgsNothing.data()),
       AdjustArgsNothing.size(),
       const_cast<Expr **>(AdjustArgsNeedDevicePtr.data()),
-      AdjustArgsNeedDevicePtr.size(), SR);
+      AdjustArgsNeedDevicePtr.size(),
+      const_cast<OMPDeclareVariantAttr::InteropType *>(AppendArgs.data()),
+      AppendArgs.size(), SR);
   FD->addAttr(NewAttr);
 }
 

diff  --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 5ddac3c98c25..7142e6ae56bb 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -441,6 +441,7 @@ static void instantiateOMPDeclareVariantAttr(
   // begin declare variant` (which use implicit attributes).
   Optional<std::pair<FunctionDecl *, Expr *>> DeclVarData =
       S.checkOpenMPDeclareVariantFunction(S.ConvertDeclToDeclGroup(New), E, TI,
+                                          Attr.appendArgs_size(),
                                           Attr.getRange());
 
   if (!DeclVarData)
@@ -483,6 +484,8 @@ static void instantiateOMPDeclareVariantAttr(
 
   SmallVector<Expr *, 8> NothingExprs;
   SmallVector<Expr *, 8> NeedDevicePtrExprs;
+  SmallVector<OMPDeclareVariantAttr::InteropType, 8> AppendArgs;
+
   for (Expr *E : Attr.adjustArgsNothing()) {
     ExprResult ER = Subst(E);
     if (ER.isInvalid())
@@ -495,8 +498,12 @@ static void instantiateOMPDeclareVariantAttr(
       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(

diff  --git a/clang/test/OpenMP/declare_variant_clauses_ast_print.cpp b/clang/test/OpenMP/declare_variant_clauses_ast_print.cpp
index 2644842651dd..9d3785c2f16b 100644
--- a/clang/test/OpenMP/declare_variant_clauses_ast_print.cpp
+++ b/clang/test/OpenMP/declare_variant_clauses_ast_print.cpp
@@ -104,4 +104,82 @@ void func()
   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 <typename T> void templatefoo_v1(const T& t, omp_interop_t I);
+template <typename T> void templatebar(const T& t) {}
+
+//PRINT: #pragma omp declare variant(templatefoo_v1<int>) 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<int>) match(construct={dispatch}) \
+  append_args(interop(target))
+void templatebar(const int &t) {}
 #endif // HEADER

diff  --git a/clang/test/OpenMP/declare_variant_clauses_messages.cpp b/clang/test/OpenMP/declare_variant_clauses_messages.cpp
index 6d68e68b02a0..648a1c1201c4 100644
--- a/clang/test/OpenMP/declare_variant_clauses_messages.cpp
+++ b/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 at +1 {{'omp_interop_t' must be defined when 'append_args' clause is used; include <omp.h>}}
+#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 at +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 at +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 at +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 at +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 at +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 at +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 at +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 at +1 {{'#pragma omp declare variant' does not support virtual functions}}
+  virtual void pvmemberbar(float *A, float *B, int *I) = 0;
+};
+
+template <typename T> void templatefoo_v0(const T& t);
+template <typename T> void templatefoo_v1(const T& t, omp_interop_t I);
+template <typename T> void templatebar(const T& t) {}
+
+// expected-error at +1 {{variant in '#pragma omp declare variant' with type '<overloaded function type>' is incompatible with type 'void (const int &)' with appended arguments}}
+#pragma omp declare variant(templatefoo_v0<int>) match(construct={dispatch}) \
+                        append_args(interop(target))
+
+// expected-error at +1 {{variant in '#pragma omp declare variant' with type '<overloaded function type>' is incompatible with type 'void (const int &)' with appended arguments}}
+#pragma omp declare variant(templatefoo_v1<int>) 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 at +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 at +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 at +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 @@ void foo_v3(float *AAA, float *BBB, int *I) { return; }
 // expected-error at +2 {{'adjust_args' clause requires 'dispatch' context selector}}
 #pragma omp declare variant(foo_v3)                          \
    adjust_args(nothing:BBB) match(device={arch(ppc)})
+
+// expected-error at +1 {{expected 'match', 'adjust_args', or 'append_args' clause on 'omp declare variant' directive}}
+#pragma omp declare variant(foo_v1)
+
+// expected-error at +1 {{expected 'match', 'adjust_args', or 'append_args' clause on 'omp declare variant' directive}}
+#pragma omp declare variant(foo_v1) other
+
+// expected-error at +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 at +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 at +2 {{'append_args' clause requires 'dispatch' context selector}}
+#pragma omp declare variant(foo_v4)                          \
+                    append_args(interop(target)) match(construct={target})
+
+// expected-error at +2 {{'append_args' clause requires 'dispatch' context selector}}
+#pragma omp declare variant(foo_v4)                          \
+                    match(construct={target}) append_args(interop(target))
+
+// expected-warning at +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 at +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 at +2 {{expected interop type: 'target' and/or 'targetsync'}}
+#pragma omp declare variant(foo_v4) match(construct={dispatch}) \
+                                    append_args(interop())
+
+// expected-error at +2 {{expected interop type: 'target' and/or 'targetsync'}}
+#pragma omp declare variant(foo_v4) match(construct={dispatch}) \
+                                    append_args(interop(somethingelse))
+
+// expected-error at +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 at +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 at +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 at +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 at +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 at +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 at +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

diff  --git a/clang/test/OpenMP/declare_variant_messages.cpp b/clang/test/OpenMP/declare_variant_messages.cpp
index b5091dda4fd1..9671df189e31 100644
--- a/clang/test/OpenMP/declare_variant_messages.cpp
+++ b/clang/test/OpenMP/declare_variant_messages.cpp
@@ -278,6 +278,12 @@ struct SpecialFuncs {
   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}}
 

diff  --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index 5e17f7a3b977..d173d679d440 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -1478,6 +1478,7 @@ CHECK_SIMPLE_CLAUSE(Nocontext, OMPC_nocontext)
 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)

diff  --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llvm/include/llvm/Frontend/OpenMP/OMP.td
index 67b97d2a64ba..551ed6915162 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMP.td
+++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td
@@ -315,6 +315,7 @@ def OMPC_Uniform : Clause<"uniform"> {
 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 @@ def OMP_DeclareVariant : Directive<"declare variant"> {
     VersionedClause<OMPC_Match>
   ];
   let allowedExclusiveClauses = [
-    VersionedClause<OMPC_AdjustArgs, 51>
+    VersionedClause<OMPC_AdjustArgs, 51>,
+    VersionedClause<OMPC_AppendArgs, 51>
   ];
 }
 def OMP_MasterTaskloop : Directive<"master taskloop"> {


        


More information about the llvm-commits mailing list