let Documentation = [Undocumented];
}
+def OMPDeclareVariant : Attr {
+ let Spellings = [Pragma<"omp", "declare variant">];
+ let Subjects = SubjectList<[Function]>;
+ let SemaHandler = 0;
+ let HasCustomParsing = 1;
+ let Documentation = [OMPDeclareVariantDocs];
+ let Args = [
+ ExprArgument<"VariantFuncRef">
+ ];
+ let AdditionalMembers = [{
+ void printPrettyPragma(raw_ostream & OS, const PrintingPolicy &Policy)
+ const {
+ if (const Expr *E = getVariantFuncRef()) {
+ OS << "(";
+ E->printPretty(OS, nullptr, Policy);
+ OS << ")";
+ }
+ // TODO: add printing of real context selectors.
+ OS << " match(unknown={})";
+ }
+ }];
+}
+
def InternalLinkage : InheritableAttr {
let Spellings = [Clang<"internal_linkage">];
let Subjects = SubjectList<[Var, Function, CXXRecord]>;
}];
}
+def OMPDeclareVariantDocs : Documentation {
+ let Category = DocCatFunction;
+ let Heading = "#pragma omp declare variant";
+ let Content = [{
+The `declare variant` directive declares a specialized variant of a base
+ function and specifies the context in which that specialized variant is used.
+ The declare variant directive is a declarative directive.
+The syntax of the `declare variant` construct is as follows:
+
+ .. code-block:: none
+
+ #pragma omp declare variant(variant-func-id) clause new-line
+ [#pragma omp declare variant(variant-func-id) clause new-line]
+ [...]
+ function definition or declaration
+
+where clause is one of the following:
+
+ .. code-block:: none
+
+ match(context-selector-specification)
+
+and where `variant-func-id` is the name of a function variant that is either a
+ base language identifier or, for C++, a template-id.
+
+ }];
+}
+
def NoStackProtectorDocs : Documentation {
let Category = DocCatFunction;
let Content = [{
def err_omp_declare_variant_incompat_types : Error<
"variant in '#pragma omp declare variant' with type %0 is incompatible with type %1"
>;
+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>;
+def note_omp_marked_declare_variant_here : Note<"marked as 'declare variant' here">;
} // end of OpenMP category
let CategoryName = "Related Result Type Issue" in {
MapT &Map, unsigned Selector = 0,
SourceRange SrcRange = SourceRange());
+ /// Marks all the functions that might be required for the currently active
+ /// OpenMP context.
+ void markOpenMPDeclareVariantFuncsReferenced(SourceLocation Loc,
+ FunctionDecl *Func,
+ bool MightBeOdrUse);
+
public:
/// Checks if the variant/multiversion functions are compatible.
bool areMultiversionVariantFunctionsCompatible(
}
if (LangOpts.OpenMP) {
+ markOpenMPDeclareVariantFuncsReferenced(Loc, Func, MightBeOdrUse);
if (LangOpts.OpenMPIsDevice)
checkOpenMPDeviceFunction(Loc, Func);
else
// Do not check templates, wait until instantiation.
if (VariantRef->isTypeDependent() || VariantRef->isValueDependent() ||
VariantRef->containsUnexpandedParameterPack() ||
- VariantRef->isInstantiationDependent() || FD->isDependentContext())
+ VariantRef->isInstantiationDependent() || FD->isDependentContext()) {
+ auto *NewAttr =
+ OMPDeclareVariantAttr::CreateImplicit(Context, VariantRef, SR);
+ FD->addAttr(NewAttr);
return DG;
+ }
// Convert VariantRef expression to the type of the original function to
// resolve possible conflicts.
return DG;
}
+ // Check if variant function is not marked with declare variant directive.
+ if (NewFD->hasAttrs() && NewFD->hasAttr<OMPDeclareVariantAttr>()) {
+ Diag(VariantRef->getExprLoc(),
+ diag::warn_omp_declare_variant_marked_as_declare_variant)
+ << VariantRef->getSourceRange();
+ SourceRange SR =
+ NewFD->specific_attr_begin<OMPDeclareVariantAttr>()->getRange();
+ Diag(SR.getBegin(), diag::note_omp_marked_declare_variant_here) << SR;
+ return DG;
+ }
+
enum DoesntSupport {
VirtFuncs = 1,
Constructors = 3,
/*TemplatesSupported=*/true, /*ConstexprSupported=*/false))
return DG;
+ auto *NewAttr = OMPDeclareVariantAttr::CreateImplicit(Context, DRE, SR);
+ FD->addAttr(NewAttr);
return DG;
}
+void Sema::markOpenMPDeclareVariantFuncsReferenced(SourceLocation Loc,
+ FunctionDecl *Func,
+ bool MightBeOdrUse) {
+ assert(LangOpts.OpenMP && "Expected OpenMP mode.");
+
+ if (!Func->isDependentContext() && Func->hasAttrs()) {
+ for (OMPDeclareVariantAttr *A :
+ Func->specific_attrs<OMPDeclareVariantAttr>()) {
+ // TODO: add checks for active OpenMP context where possible.
+ Expr *VariantRef = A->getVariantFuncRef();
+ auto *DRE = dyn_cast<DeclRefExpr>(VariantRef->IgnoreParenImpCasts());
+ auto *F = cast<FunctionDecl>(DRE->getDecl());
+ if (!F->isDefined() && F->isTemplateInstantiation())
+ InstantiateFunctionDefinition(Loc, F->getFirstDecl());
+ MarkFunctionReferenced(Loc, F, MightBeOdrUse);
+ }
+ }
+}
+
StmtResult Sema::ActOnOpenMPParallelDirective(ArrayRef<OMPClause *> Clauses,
Stmt *AStmt,
SourceLocation StartLoc,
Attr.getRange());
}
+/// Instantiation of 'declare variant' attribute and its arguments.
+static void instantiateOMPDeclareVariantAttr(
+ Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs,
+ const OMPDeclareVariantAttr &Attr, Decl *New) {
+ // Allow 'this' in clauses with varlists.
+ if (auto *FTD = dyn_cast<FunctionTemplateDecl>(New))
+ New = FTD->getTemplatedDecl();
+ auto *FD = cast<FunctionDecl>(New);
+ auto *ThisContext = dyn_cast_or_null<CXXRecordDecl>(FD->getDeclContext());
+
+ auto &&SubstExpr = [FD, ThisContext, &S, &TemplateArgs](Expr *E) {
+ if (auto *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts()))
+ if (auto *PVD = dyn_cast<ParmVarDecl>(DRE->getDecl())) {
+ Sema::ContextRAII SavedContext(S, FD);
+ LocalInstantiationScope Local(S);
+ if (FD->getNumParams() > PVD->getFunctionScopeIndex())
+ Local.InstantiatedLocal(
+ PVD, FD->getParamDecl(PVD->getFunctionScopeIndex()));
+ return S.SubstExpr(E, TemplateArgs);
+ }
+ Sema::CXXThisScopeRAII ThisScope(S, ThisContext, Qualifiers(),
+ FD->isCXXInstanceMember());
+ return S.SubstExpr(E, TemplateArgs);
+ };
+
+ // Substitute a single OpenMP clause, which is a potentially-evaluated
+ // full-expression.
+ auto &&Subst = [&SubstExpr, &S](Expr *E) {
+ EnterExpressionEvaluationContext Evaluated(
+ S, Sema::ExpressionEvaluationContext::PotentiallyEvaluated);
+ ExprResult Res = SubstExpr(E);
+ if (Res.isInvalid())
+ return Res;
+ return S.ActOnFinishFullExpr(Res.get(), false);
+ };
+
+ ExprResult VariantFuncRef;
+ if (Expr *E = Attr.getVariantFuncRef())
+ VariantFuncRef = Subst(E);
+
+ (void)S.ActOnOpenMPDeclareVariantDirective(
+ S.ConvertDeclToDeclGroup(New), VariantFuncRef.get(), Attr.getRange());
+}
+
static void instantiateDependentAMDGPUFlatWorkGroupSizeAttr(
Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs,
const AMDGPUFlatWorkGroupSizeAttr &Attr, Decl *New) {
continue;
}
+ if (const auto *OMPAttr = dyn_cast<OMPDeclareVariantAttr>(TmplAttr)) {
+ instantiateOMPDeclareVariantAttr(*this, TemplateArgs, *OMPAttr, New);
+ continue;
+ }
+
if (const auto *AMDGPUFlatWorkGroupSize =
dyn_cast<AMDGPUFlatWorkGroupSizeAttr>(TmplAttr)) {
instantiateDependentAMDGPUFlatWorkGroupSizeAttr(
--- /dev/null
+// RUN: %clang_cc1 -verify -fopenmp -x c -std=c99 -ast-print %s -o - | FileCheck %s
+
+// RUN: %clang_cc1 -verify -fopenmp-simd -x c -std=c99 -ast-print %s -o - | FileCheck %s
+
+// expected-no-diagnostics
+
+int foo(void);
+
+#pragma omp declare variant(foo) match(xxx={})
+#pragma omp declare variant(foo) match(xxx={vvv})
+int bar(void);
+
+// CHECK: int foo();
+// CHECK-NEXT: #pragma omp declare variant(foo) match(unknown={})
+// CHECK-NEXT: #pragma omp declare variant(foo) match(unknown={})
+// CHECK-NEXT: int bar();
--- /dev/null
+// RUN: %clang_cc1 -verify -fopenmp -x c++ -std=c++14 -fexceptions -fcxx-exceptions %s -ast-print -o - | FileCheck %s
+
+// RUN: %clang_cc1 -verify -fopenmp-simd -x c++ -std=c++14 -fexceptions -fcxx-exceptions %s -ast-print -o - | FileCheck %s
+
+// expected-no-diagnostics
+
+// CHECK: int foo();
+int foo();
+
+// CHECK: template <typename T> T foofoo() {
+// CHECK-NEXT: return T();
+// CHECK-NEXT: }
+template <typename T>
+T foofoo() { return T(); }
+
+// CHECK: template<> int foofoo<int>() {
+// CHECK-NEXT: return int();
+// CHECK-NEXT: }
+
+// CHECK: #pragma omp declare variant(foofoo<int>) match(unknown={})
+// CHECK-NEXT: #pragma omp declare variant(foofoo<int>) match(unknown={})
+// CHECK-NEXT: int bar();
+#pragma omp declare variant(foofoo <int>) match(xxx = {})
+#pragma omp declare variant(foofoo <int>) match(xxx = {vvv})
+int bar();
+
+// CHECK: #pragma omp declare variant(foofoo<T>) match(unknown={})
+// CHECK-NEXT: #pragma omp declare variant(foofoo<T>) match(unknown={})
+// CHECK-NEXT: #pragma omp declare variant(foofoo<T>) match(unknown={})
+// CHECK-NEXT: #pragma omp declare variant(foofoo<T>) match(unknown={})
+// CHECK-NEXT: #pragma omp declare variant(foofoo<T>) match(unknown={})
+// CHECK-NEXT: #pragma omp declare variant(foofoo<T>) match(unknown={})
+// CHECK-NEXT: template <typename T> T barbar();
+#pragma omp declare variant(foofoo <T>) match(xxx = {})
+#pragma omp declare variant(foofoo <T>) match(xxx = {vvv})
+#pragma omp declare variant(foofoo <T>) match(user = {score(<expr>) : condition(<expr>)})
+#pragma omp declare variant(foofoo <T>) match(user = {score(<expr>) : condition(<expr>)})
+#pragma omp declare variant(foofoo <T>) match(user = {condition(<expr>)})
+#pragma omp declare variant(foofoo <T>) match(user = {condition(<expr>)})
+template <typename T>
+T barbar();
+
+// CHECK: #pragma omp declare variant(foofoo<int>) match(unknown={})
+// CHECK-NEXT: #pragma omp declare variant(foofoo<int>) match(unknown={})
+// CHECK-NEXT: #pragma omp declare variant(foofoo<int>) match(unknown={})
+// CHECK-NEXT: #pragma omp declare variant(foofoo<int>) match(unknown={})
+// CHECK-NEXT: #pragma omp declare variant(foofoo<int>) match(unknown={})
+// CHECK-NEXT: #pragma omp declare variant(foofoo<int>) match(unknown={})
+// CHECK-NEXT: template<> int barbar<int>();
+
+// CHECK-NEXT: int baz() {
+// CHECK-NEXT: return barbar<int>();
+// CHECK-NEXT: }
+int baz() {
+ return barbar<int>();
+}
+
+// CHECK: template <class C> void h_ref(C *hp, C *hp2, C *hq, C *lin) {
+// CHECK-NEXT: }
+// CHECK-NEXT: template<> void h_ref<double>(double *hp, double *hp2, double *hq, double *lin) {
+// CHECK-NEXT: }
+// CHECK-NEXT: template<> void h_ref<float>(float *hp, float *hp2, float *hq, float *lin) {
+// CHECK-NEXT: }
+template <class C>
+void h_ref(C *hp, C *hp2, C *hq, C *lin) {
+}
+
+// CHECK: #pragma omp declare variant(h_ref<C>) match(unknown={})
+// CHECK-NEXT: template <class C> void h(C *hp, C *hp2, C *hq, C *lin) {
+// CHECK-NEXT: }
+#pragma omp declare variant(h_ref <C>) match(xxx = {})
+template <class C>
+void h(C *hp, C *hp2, C *hq, C *lin) {
+}
+
+// CHECK: #pragma omp declare variant(h_ref<float>) match(unknown={})
+// CHECK-NEXT: template<> void h<float>(float *hp, float *hp2, float *hq, float *lin) {
+// CHECK-NEXT: }
+
+// CHECK-NEXT: template<> void h<double>(double *hp, double *hp2, double *hq, double *lin) {
+// CHECK-NEXT: h((float *)hp, (float *)hp2, (float *)hq, (float *)lin);
+// CHECK-NEXT: }
+#pragma omp declare variant(h_ref <double>) match(xxx = {})
+template <>
+void h(double *hp, double *hp2, double *hq, double *lin) {
+ h((float *)hp, (float *)hp2, (float *)hq, (float *)lin);
+}
+
+// CHECK: int fn();
+int fn();
+// CHECK: int fn(int);
+int fn(int);
+// CHECK: #pragma omp declare variant(fn) match(unknown={})
+// CHECK-NEXT: int overload();
+#pragma omp declare variant(fn) match(xxx = {})
+int overload(void);
+
+// CHECK: int fn_deduced_variant() {
+// CHECK-NEXT: return 0;
+// CHECK-NEXT: }
+auto fn_deduced_variant() { return 0; }
+// CHECK: #pragma omp declare variant(fn_deduced_variant) match(unknown={})
+// CHECK-NEXT: int fn_deduced();
+#pragma omp declare variant(fn_deduced_variant) match(xxx = {})
+int fn_deduced();
+
+// CHECK: int fn_deduced_variant1();
+int fn_deduced_variant1();
+// CHECK: #pragma omp declare variant(fn_deduced_variant1) match(unknown={})
+// CHECK-NEXT: int fn_deduced1() {
+// CHECK-NEXT: return 0;
+// CHECK-NEXT: }
+#pragma omp declare variant(fn_deduced_variant1) match(xxx = {})
+auto fn_deduced1() { return 0; }
+
+// CHECK: struct SpecialFuncs {
+// CHECK-NEXT: void vd() {
+// CHECK-NEXT: }
+// CHECK-NEXT: SpecialFuncs();
+// CHECK-NEXT: ~SpecialFuncs() noexcept;
+// CHECK-NEXT: void baz() {
+// CHECK-NEXT: }
+// CHECK-NEXT: void bar() {
+// CHECK-NEXT: }
+// CHECK-NEXT: void bar(int) {
+// CHECK-NEXT: }
+// CHECK-NEXT: #pragma omp declare variant(SpecialFuncs::bar) match(unknown={})
+// CHECK-NEXT: #pragma omp declare variant(SpecialFuncs::baz) match(unknown={})
+// CHECK-NEXT: void foo1() {
+// CHECK-NEXT: }
+// CHECK-NEXT: } s;
+struct SpecialFuncs {
+ void vd() {}
+ SpecialFuncs();
+ ~SpecialFuncs();
+
+ void baz() {}
+ void bar() {}
+ void bar(int) {}
+#pragma omp declare variant(SpecialFuncs::baz) match(xxx = {})
+#pragma omp declare variant(SpecialFuncs::bar) match(xxx = {})
+ void foo1() {}
+} s;
+
+// CHECK: static void static_f_variant() {
+// CHECK-NEXT: }
+static void static_f_variant() {}
+// CHECK: #pragma omp declare variant(static_f_variant) match(unknown={})
+// CHECK-NEXT: static void static_f() {
+// CHECK-NEXT: }
+#pragma omp declare variant(static_f_variant) match(xxx = {})
+static void static_f() {}
+
+// CHECK: void bazzzz() {
+// CHECK-NEXT: s.foo1();
+// CHECK-NEXT: static_f();
+// CHECK-NEXT: }
+void bazzzz() {
+ s.foo1();
+ static_f();
+}
#pragma omp declare variant(diff_ret_variant) match(xxx={})
void diff_ret(void);
+void marked(void);
+void not_marked(void);
+// expected-note@+1 {{marked as 'declare variant' here}}
+#pragma omp declare variant(not_marked) match(xxx={})
+void marked_variant(void);
+// expected-warning@+1 {{variant function in '#pragma omp declare variant' is itself marked as '#pragma omp declare variant'}}
+#pragma omp declare variant(marked_variant) match(xxx={})
+void marked(void);
+
// expected-error@+1 {{function declaration is expected after 'declare variant' directive}}
#pragma omp declare variant
// expected-error@+1 {{function declaration is expected after 'declare variant' directive}}