41 changes: 41 additions & 0 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/Type.h"
#include "clang/Basic/CharInfo.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TargetBuiltins.h"
#include "clang/Basic/TargetInfo.h"
Expand All @@ -38,6 +39,7 @@
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/IR/Assumptions.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/raw_ostream.h"

Expand Down Expand Up @@ -1712,6 +1714,42 @@ void Sema::AddAllocAlignAttr(Decl *D, const AttributeCommonInfo &CI,
D->addAttr(::new (Context) AllocAlignAttr(Context, CI, Idx));
}

/// Check if \p AssumptionStr is a known assumption and warn if not.
static void checkAssumptionAttr(Sema &S, SourceLocation Loc,
StringRef AssumptionStr) {
if (llvm::KnownAssumptionStrings.count(AssumptionStr))
return;

unsigned BestEditDistance = 3;
StringRef Suggestion;
for (const auto &KnownAssumptionIt : llvm::KnownAssumptionStrings) {
unsigned EditDistance =
AssumptionStr.edit_distance(KnownAssumptionIt.getKey());
if (EditDistance < BestEditDistance) {
Suggestion = KnownAssumptionIt.getKey();
BestEditDistance = EditDistance;
}
}

if (!Suggestion.empty())
S.Diag(Loc, diag::warn_assume_attribute_string_unknown_suggested)
<< AssumptionStr << Suggestion;
else
S.Diag(Loc, diag::warn_assume_attribute_string_unknown) << AssumptionStr;
}

static void handleAssumumptionAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
// Handle the case where the attribute has a text message.
StringRef Str;
SourceLocation AttrStrLoc;
if (!S.checkStringLiteralArgumentAttr(AL, 0, Str, &AttrStrLoc))
return;

checkAssumptionAttr(S, AttrStrLoc, Str);

D->addAttr(::new (S.Context) AssumptionAttr(S.Context, AL, Str));
}

/// Normalize the attribute, __foo__ becomes foo.
/// Returns true if normalization was applied.
static bool normalizeName(StringRef &AttrName) {
Expand Down Expand Up @@ -7845,6 +7883,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
case ParsedAttr::AT_Unavailable:
handleAttrWithMessage<UnavailableAttr>(S, D, AL);
break;
case ParsedAttr::AT_Assumption:
handleAssumumptionAttr(S, D, AL);
break;
case ParsedAttr::AT_ObjCDirect:
handleObjCDirectAttr(S, D, AL);
break;
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Sema/SemaLambda.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -998,6 +998,10 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
if (getLangOpts().CUDA)
CUDASetLambdaAttrs(Method);

// OpenMP lambdas might get assumumption attributes.
if (LangOpts.OpenMP)
ActOnFinishedFunctionDefinitionInOpenMPAssumeScope(Method);

// Number the lambda for linkage purposes if necessary.
handleLambdaNumbering(Class, Method);

Expand Down
80 changes: 80 additions & 0 deletions clang/lib/Sema/SemaOpenMP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include "llvm/ADT/IndexedMap.h"
#include "llvm/ADT/PointerEmbeddedInt.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Frontend/OpenMP/OMPConstants.h"
#include <set>

Expand Down Expand Up @@ -3195,6 +3196,64 @@ Sema::ActOnOpenMPRequiresDirective(SourceLocation Loc,
return DeclGroupPtrTy::make(DeclGroupRef(D));
}

void Sema::ActOnOpenMPAssumesDirective(SourceLocation Loc,
OpenMPDirectiveKind DKind,
ArrayRef<StringRef> Assumptions,
bool SkippedClauses) {
if (!SkippedClauses && Assumptions.empty())
Diag(Loc, diag::err_omp_no_clause_for_directive)
<< llvm::omp::getAllAssumeClauseOptions()
<< llvm::omp::getOpenMPDirectiveName(DKind);

auto *AA = AssumptionAttr::Create(Context, llvm::join(Assumptions, ","), Loc);
if (DKind == llvm::omp::Directive::OMPD_begin_assumes) {
OMPAssumeScoped.push_back(AA);
return;
}

// Global assumes without assumption clauses are ignored.
if (Assumptions.empty())
return;

assert(DKind == llvm::omp::Directive::OMPD_assumes &&
"Unexpected omp assumption directive!");
OMPAssumeGlobal.push_back(AA);

// The OMPAssumeGlobal scope above will take care of new declarations but
// we also want to apply the assumption to existing ones, e.g., to
// declarations in included headers. To this end, we traverse all existing
// declaration contexts and annotate function declarations here.
SmallVector<DeclContext *, 8> DeclContexts;
auto *Ctx = CurContext;
while (Ctx->getLexicalParent())
Ctx = Ctx->getLexicalParent();
DeclContexts.push_back(Ctx);
while (!DeclContexts.empty()) {
DeclContext *DC = DeclContexts.pop_back_val();
for (auto *SubDC : DC->decls()) {
if (SubDC->isInvalidDecl())
continue;
if (auto *CTD = dyn_cast<ClassTemplateDecl>(SubDC)) {
DeclContexts.push_back(CTD->getTemplatedDecl());
for (auto *S : CTD->specializations())
DeclContexts.push_back(S);
continue;
}
if (auto *DC = dyn_cast<DeclContext>(SubDC))
DeclContexts.push_back(DC);
if (auto *F = dyn_cast<FunctionDecl>(SubDC)) {
F->addAttr(AA);
continue;
}
}
}
}

void Sema::ActOnOpenMPEndAssumesDirective() {
assert(isInOpenMPAssumeScope() && "Not in OpenMP assumes scope!");
OMPAssumeScoped.pop_back();
}

OMPRequiresDecl *Sema::CheckOMPRequiresDecl(SourceLocation Loc,
ArrayRef<OMPClause *> ClauseList) {
/// For target specific clauses, the requires directive cannot be
Expand Down Expand Up @@ -5936,6 +5995,27 @@ static void setPrototype(Sema &S, FunctionDecl *FD, FunctionDecl *FDWithProto,
FD->setParams(Params);
}

void Sema::ActOnFinishedFunctionDefinitionInOpenMPAssumeScope(Decl *D) {
if (D->isInvalidDecl())
return;
FunctionDecl *FD = nullptr;
if (auto *UTemplDecl = dyn_cast<FunctionTemplateDecl>(D))
FD = UTemplDecl->getTemplatedDecl();
else
FD = cast<FunctionDecl>(D);
assert(FD && "Expected a function declaration!");

// If we are intantiating templates we do *not* apply scoped assumptions but
// only global ones. We apply scoped assumption to the template definition
// though.
if (!inTemplateInstantiation()) {
for (AssumptionAttr *AA : OMPAssumeScoped)
FD->addAttr(AA);
}
for (AssumptionAttr *AA : OMPAssumeGlobal)
FD->addAttr(AA);
}

Sema::OMPDeclareVariantScope::OMPDeclareVariantScope(OMPTraitInfo &TI)
: TI(&TI), NameSuffix(TI.getMangledName()) {}

Expand Down
58 changes: 58 additions & 0 deletions clang/test/CodeGen/assume_attr.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// RUN: %clang_cc1 -emit-llvm -triple i386-linux-gnu %s -o - | FileCheck %s
// RUN: %clang_cc1 -x c -emit-pch -o %t %s
// RUN: %clang_cc1 -include-pch %t %s -emit-llvm -o - | FileCheck %s

// TODO: for "foo" and "bar", "after" is not added as it appears "after" the first use or definition respectively. There might be a way to allow that.

// CHECK: define{{.*}} void @bar() #0
// CHECK: define{{.*}} void @baz() #1
// CHECK: declare{{.*}} void @foo() #2
// CHECK: attributes #0
// CHECK-SAME: "llvm.assume"="bar:before1,bar:before2,bar:before3,bar:def1,bar:def2"
// CHECK: attributes #1
// CHECK-SAME: "llvm.assume"="baz:before1,baz:before2,baz:before3,baz:def1,baz:def2,baz:after"
// CHECK: attributes #2
// CHECK-SAME: "llvm.assume"="foo:before1,foo:before2,foo:before3"

#ifndef HEADER
#define HEADER

/// foo: declarations only

__attribute__((assume("foo:before1"))) void foo(void);

__attribute__((assume("foo:before2")))
__attribute__((assume("foo:before3"))) void
foo(void);

/// baz: static function declarations and a definition

__attribute__((assume("baz:before1"))) static void baz(void);

__attribute__((assume("baz:before2")))
__attribute__((assume("baz:before3"))) static void
baz(void);

// Definition
__attribute__((assume("baz:def1,baz:def2"))) static void baz(void) { foo(); }

__attribute__((assume("baz:after"))) static void baz(void);

/// bar: external function declarations and a definition

__attribute__((assume("bar:before1"))) void bar(void);

__attribute__((assume("bar:before2")))
__attribute__((assume("bar:before3"))) void
bar(void);

// Definition
__attribute__((assume("bar:def1,bar:def2"))) void bar(void) { baz(); }

__attribute__((assume("bar:after"))) void bar(void);

/// back to foo

__attribute__((assume("foo:after"))) void foo(void);

#endif
120 changes: 120 additions & 0 deletions clang/test/CodeGenCXX/assume_attr.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// RUN: %clang_cc1 -emit-llvm -triple i386-linux-gnu %s -o - | FileCheck %s
// RUN: %clang_cc1 -x c++ -emit-pch -triple i386-linux-gnu -o %t %s
// RUN: %clang_cc1 -include-pch %t %s -triple i386-linux-gnu -emit-llvm -o - | FileCheck %s
// expected-no-diagnostics

#ifndef HEADER
#define HEADER

/// foo: declarations only

__attribute__((assume("foo:before1"))) void foo();

__attribute__((assume("foo:before2")))
__attribute__((assume("foo:before3"))) void
foo();

/// baz: static function declarations and a definition

__attribute__((assume("baz:before1"))) static void baz();

__attribute__((assume("baz:before2")))
__attribute__((assume("baz:before3"))) static void
baz();

// Definition
__attribute__((assume("baz:def1,baz:def2"))) static void baz() { foo(); }

__attribute__((assume("baz:after"))) static void baz();

/// bar: external function declarations and a definition

__attribute__((assume("bar:before1"))) void bar();

__attribute__((assume("bar:before2")))
__attribute__((assume("bar:before3"))) void
bar();

// Definition
__attribute__((assume("bar:def1,bar:def2"))) void bar() { baz(); }

__attribute__((assume("bar:after"))) void bar();

/// back to foo

__attribute__((assume("foo:after"))) void foo();

/// class tests
class C {
__attribute__((assume("C:private_method"))) void private_method();
__attribute__((assume("C:private_static"))) static void private_static();

public:
__attribute__((assume("C:public_method1"))) void public_method();
__attribute__((assume("C:public_static1"))) static void public_static();
};

__attribute__((assume("C:public_method2"))) void C::public_method() {
private_method();
}

__attribute__((assume("C:public_static2"))) void C::public_static() {
private_static();
}

/// template tests
template <typename T>
__attribute__((assume("template_func<T>"))) void template_func() {}

template <>
__attribute__((assume("template_func<float>"))) void template_func<float>() {}

template <>
void template_func<int>() {}

template <typename T>
struct S {
__attribute__((assume("S<T>::method"))) void method();
};

template <>
__attribute__((assume("S<float>::method"))) void S<float>::method() {}

template <>
void S<int>::method() {}

// CHECK: define{{.*}} void @_Z3barv() #0
// CHECK: define{{.*}} void @_ZL3bazv() #1
// CHECK: define{{.*}} void @_ZN1C13public_methodEv({{.*}}) #2
// CHECK: declare{{.*}} void @_ZN1C14private_methodEv({{.*}}) #3
// CHECK: define{{.*}} void @_ZN1C13public_staticEv() #4
// CHECK: declare{{.*}} void @_ZN1C14private_staticEv() #5
// CHECK: define{{.*}} void @_Z13template_funcIfEvv() #6
// CHECK: define{{.*}} void @_Z13template_funcIiEvv() #7
// CHECK: define{{.*}} void @_ZN1SIfE6methodEv({{.*}}) #8
// CHECK: define{{.*}} void @_ZN1SIiE6methodEv({{.*}}) #9
// CHECK: declare{{.*}} void @_Z3foov() #10
// CHECK: attributes #0
// CHECK-SAME: "llvm.assume"="bar:before1,bar:before2,bar:before3,bar:def1,bar:def2"
// CHECK: attributes #1
// CHECK-SAME: "llvm.assume"="baz:before1,baz:before2,baz:before3,baz:def1,baz:def2,baz:after"
// CHECK: attributes #2
// CHECK-SAME: "llvm.assume"="C:public_method1,C:public_method2"
// CHECK: attributes #3
// CHECK-SAME: "llvm.assume"="C:private_method"
// CHECK: attributes #4
// CHECK-SAME: "llvm.assume"="C:public_static1,C:public_static2"
// CHECK: attributes #5
// CHECK-SAME: "llvm.assume"="C:private_static"
// CHECK: attributes #6
// CHECK-SAME: "llvm.assume"="template_func<T>,template_func<float>"
// CHECK: attributes #7
// CHECK-SAME: "llvm.assume"="template_func<T>"
// CHECK: attributes #8
// CHECK-SAME: "llvm.assume"="S<T>::method,S<float>::method"
// CHECK: attributes #9
// CHECK-SAME: "llvm.assume"="S<T>::method"
// CHECK: attributes #10
// CHECK-SAME: "llvm.assume"="foo:before1,foo:before2,foo:before3"

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
// CHECK-NEXT: ArcWeakrefUnavailable (SubjectMatchRule_objc_interface)
// CHECK-NEXT: ArmBuiltinAlias (SubjectMatchRule_function)
// CHECK-NEXT: AssumeAligned (SubjectMatchRule_objc_method, SubjectMatchRule_function)
// CHECK-NEXT: Assumption (SubjectMatchRule_function SubjectMatchRule_objc_method)
// CHECK-NEXT: Availability ((SubjectMatchRule_record, SubjectMatchRule_enum, SubjectMatchRule_enum_constant, SubjectMatchRule_field, SubjectMatchRule_function, SubjectMatchRule_namespace, SubjectMatchRule_objc_category, SubjectMatchRule_objc_implementation, SubjectMatchRule_objc_interface, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property, SubjectMatchRule_objc_protocol, SubjectMatchRule_record, SubjectMatchRule_type_alias, SubjectMatchRule_variable))
// CHECK-NEXT: BPFPreserveAccessIndex (SubjectMatchRule_record)
// CHECK-NEXT: CFAuditedTransfer (SubjectMatchRule_function)
Expand Down
165 changes: 165 additions & 0 deletions clang/test/OpenMP/assumes_codegen.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
// RUN: %clang_cc1 -verify -fopenmp -x c++ -emit-llvm %s -fexceptions -fcxx-exceptions -o - | FileCheck %s
// RUN: %clang_cc1 -verify -fopenmp -ast-print %s | FileCheck %s --check-prefix=AST
// RUN: %clang_cc1 -fopenmp -x c++ -std=c++11 -triple x86_64-unknown-unknown -fexceptions -fcxx-exceptions -emit-pch -verify -o %t %s
// RUN: %clang_cc1 -fopenmp -x c++ -triple x86_64-unknown-unknown -fexceptions -fcxx-exceptions -std=c++11 -include-pch %t -verify=pch %s -emit-llvm -o - | FileCheck %s
// RUN: %clang_cc1 -verify -triple x86_64-apple-darwin10 -fopenmp -fexceptions -fcxx-exceptions -debug-info-kind=line-tables-only -x c++ -emit-llvm %s -o - | FileCheck %s
// RUN: %clang_cc1 -verify -fopenmp -fopenmp-enable-irbuilder -x c++ -emit-llvm %s -fexceptions -fcxx-exceptions -o - | FileCheck %s
// RUN: %clang_cc1 -fopenmp -fopenmp-enable-irbuilder -x c++ -std=c++11 -triple x86_64-unknown-unknown -fexceptions -fcxx-exceptions -emit-pch -verify -o %t %s
// RUN: %clang_cc1 -fopenmp -fopenmp-enable-irbuilder -x c++ -triple x86_64-unknown-unknown -fexceptions -fcxx-exceptions -std=c++11 -include-pch %t -verify=pch %s -emit-llvm -o - | FileCheck %s

// pch-no-diagnostics

#ifndef HEADER
#define HEADER

void foo() {
}

#pragma omp assumes no_openmp_routines warning ext_another_warning(1) ext_after_invalid_clauses // expected-warning {{valid assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; token will be ignored}} expected-warning {{'ext_another_warning' clause should not be followed by arguments; tokens will be ignored}} expected-note {{the ignored tokens spans until here}}

#pragma omp assumes no_openmp

#pragma omp begin assumes ext_range_bar_only

#pragma omp begin assumes ext_range_bar_only_2

class BAR {
public:
BAR() {}

void bar1() {
}

static void bar2() {
}
};

void bar() { BAR b; }

#pragma omp end assumes
#pragma omp end assumes

#pragma omp begin assumes ext_not_seen
#pragma omp end assumes

#pragma omp begin assumes ext_1234
void baz();

template<typename T>
class BAZ {
public:
BAZ() {}

void baz1() {
}

static void baz2() {
}
};

void baz() { BAZ<float> b; }
#pragma omp end assumes

#pragma omp begin assumes ext_lambda_assumption
int lambda_outer() {
auto lambda_inner = []() { return 42; };
return lambda_inner();
}
#pragma omp end assumes

// AST: void foo() __attribute__((assume("no_openmp_routines,another_warning,after_invalid_clauses"))) __attribute__((assume("no_openmp"))) {
// AST-NEXT: }
// AST-NEXT: class BAR {
// AST-NEXT: public:
// AST-NEXT: BAR() __attribute__((assume("range_bar_only"))) __attribute__((assume("range_bar_only_2"))) __attribute__((assume("no_openmp_routines,another_warning,after_invalid_clauses"))) __attribute__((assume("no_openmp"))) {
// AST-NEXT: }
// AST-NEXT: void bar1() __attribute__((assume("range_bar_only"))) __attribute__((assume("range_bar_only_2"))) __attribute__((assume("no_openmp_routines,another_warning,after_invalid_clauses"))) __attribute__((assume("no_openmp"))) {
// AST-NEXT: }
// AST-NEXT: static void bar2() __attribute__((assume("range_bar_only"))) __attribute__((assume("range_bar_only_2"))) __attribute__((assume("no_openmp_routines,another_warning,after_invalid_clauses"))) __attribute__((assume("no_openmp"))) {
// AST-NEXT: }
// AST-NEXT: };
// AST-NEXT: void bar() __attribute__((assume("range_bar_only"))) __attribute__((assume("range_bar_only_2"))) __attribute__((assume("no_openmp_routines,another_warning,after_invalid_clauses"))) __attribute__((assume("no_openmp"))) {
// AST-NEXT: BAR b;
// AST-NEXT: }
// AST-NEXT: void baz() __attribute__((assume("1234"))) __attribute__((assume("no_openmp_routines,another_warning,after_invalid_clauses"))) __attribute__((assume("no_openmp")));
// AST-NEXT: template <typename T> class BAZ {
// AST-NEXT: public:
// AST-NEXT: BAZ<T>() __attribute__((assume("1234"))) __attribute__((assume("no_openmp_routines,another_warning,after_invalid_clauses"))) __attribute__((assume("no_openmp"))) {
// AST-NEXT: }
// AST-NEXT: void baz1() __attribute__((assume("1234"))) __attribute__((assume("no_openmp_routines,another_warning,after_invalid_clauses"))) __attribute__((assume("no_openmp"))) {
// AST-NEXT: }
// AST-NEXT: static void baz2() __attribute__((assume("1234"))) __attribute__((assume("no_openmp_routines,another_warning,after_invalid_clauses"))) __attribute__((assume("no_openmp"))) {
// AST-NEXT: }
// AST-NEXT: };
// AST-NEXT: template<> class BAZ<float> {
// AST-NEXT: public:
// AST-NEXT: BAZ() __attribute__((assume("1234"))) __attribute__((assume("no_openmp_routines,another_warning,after_invalid_clauses"))) __attribute__((assume("no_openmp"))) __attribute__((assume("no_openmp_routines,another_warning,after_invalid_clauses"))) __attribute__((assume("no_openmp"))) {
// AST-NEXT: }
// AST-NEXT: void baz1() __attribute__((assume("1234"))) __attribute__((assume("no_openmp_routines,another_warning,after_invalid_clauses"))) __attribute__((assume("no_openmp"))) __attribute__((assume("no_openmp_routines,another_warning,after_invalid_clauses"))) __attribute__((assume("no_openmp")));
// AST-NEXT: static void baz2() __attribute__((assume("1234"))) __attribute__((assume("no_openmp_routines,another_warning,after_invalid_clauses"))) __attribute__((assume("no_openmp"))) __attribute__((assume("no_openmp_routines,another_warning,after_invalid_clauses"))) __attribute__((assume("no_openmp")));
// AST-NEXT: };
// AST-NEXT: void baz() __attribute__((assume("1234"))) __attribute__((assume("no_openmp_routines,another_warning,after_invalid_clauses"))) __attribute__((assume("no_openmp"))) {
// AST-NEXT: BAZ<float> b;
// AST-NEXT: }
// AST-NEXT: int lambda_outer() __attribute__((assume("lambda_assumption"))) __attribute__((assume("no_openmp_routines,another_warning,after_invalid_clauses"))) __attribute__((assume("no_openmp"))) {
// AST-NEXT: auto lambda_inner = []() {
// AST-NEXT: return 42;
// AST-NEXT: };
// AST-NEXT: return lambda_inner();
// AST-NEXT: }

#endif

// CHECK: define{{.*}} void @_Z3foov()
// CHECK-SAME: [[attr0:#[0-9]]]
// CHECK: define{{.*}} void @_Z3barv()
// CHECK-SAME: [[attr1:#[0-9]]]
// CHECK: call void @_ZN3BARC1Ev(%class.BAR*{{.*}} %b)
// CHECK-SAME: [[attr9:#[0-9]]]
// CHECK: define{{.*}} void @_ZN3BARC1Ev(%class.BAR*{{.*}} %this)
// CHECK-SAME: [[attr2:#[0-9]]]
// CHECK: call void @_ZN3BARC2Ev(%class.BAR*{{.*}} %this1)
// CHECK-SAME: [[attr9]]
// CHECK: define{{.*}} void @_ZN3BARC2Ev(%class.BAR*{{.*}} %this)
// CHECK-SAME: [[attr3:#[0-9]]]
// CHECK: define{{.*}} void @_Z3bazv()
// CHECK-SAME: [[attr4:#[0-9]]]
// CHECK: call void @_ZN3BAZIfEC1Ev(%class.BAZ*{{.*}} %b)
// CHECK-SAME: [[attr10:#[0-9]]]
// CHECK: define{{.*}} void @_ZN3BAZIfEC1Ev(%class.BAZ*{{.*}} %this)
// CHECK-SAME: [[attr5:#[0-9]]]
// CHECK: call void @_ZN3BAZIfEC2Ev(%class.BAZ*{{.*}} %this1)
// CHECK-SAME: [[attr10]]
// CHECK: define{{.*}} void @_ZN3BAZIfEC2Ev(%class.BAZ*{{.*}} %this)
// CHECK-SAME: [[attr6:#[0-9]]]
// CHECK: define{{.*}} i32 @_Z12lambda_outerv()
// CHECK-SAME: [[attr7:#[0-9]]]
// CHECK: call i32 @"_ZZ12lambda_outervENK3$_0clEv"
// CHECK-SAME: [[attr11:#[0-9]]]
// CHECK: define{{.*}} i32 @"_ZZ12lambda_outervENK3$_0clEv"(%class.anon*{{.*}} %this)
// CHECK-SAME: [[attr8:#[0-9]]]

// CHECK: attributes [[attr0]]
// CHECK-SAME: "llvm.assume"="no_openmp_routines,another_warning,after_invalid_clauses,no_openmp"
// CHECK: attributes [[attr1]]
// CHECK-SAME: "llvm.assume"="range_bar_only,range_bar_only_2,no_openmp_routines,another_warning,after_invalid_clauses,no_openmp"
// CHECK: attributes [[attr2]]
// CHECK-SAME: "llvm.assume"="range_bar_only,range_bar_only_2,no_openmp_routines,another_warning,after_invalid_clauses,no_openmp"
// CHECK: attributes [[attr3]]
// CHECK-SAME: "llvm.assume"="range_bar_only,range_bar_only_2,no_openmp_routines,another_warning,after_invalid_clauses,no_openmp"
// CHECK: attributes [[attr4]]
// CHECK-SAME: "llvm.assume"="1234,no_openmp_routines,another_warning,after_invalid_clauses,no_openmp,1234,no_openmp_routines,another_warning,after_invalid_clauses,no_openmp"
// CHECK: attributes [[attr5]]
// CHECK-SAME: "llvm.assume"="1234,no_openmp_routines,another_warning,after_invalid_clauses,no_openmp,no_openmp_routines,another_warning,after_invalid_clauses,no_openmp"
// CHECK: attributes [[attr6]]
// CHECK-SAME: "llvm.assume"="1234,no_openmp_routines,another_warning,after_invalid_clauses,no_openmp,no_openmp_routines,another_warning,after_invalid_clauses,no_openmp"
// CHECK: attributes [[attr7]]
// CHECK-SAME: "llvm.assume"="lambda_assumption,no_openmp_routines,another_warning,after_invalid_clauses,no_openmp"
// CHECK: attributes [[attr8]]
// CHECK-SAME: "llvm.assume"="lambda_assumption,no_openmp_routines,another_warning,after_invalid_clauses,no_openmp"
// CHECK: attributes [[attr9]]
// CHECK-SAME: "llvm.assume"="range_bar_only,range_bar_only_2,no_openmp_routines,another_warning,after_invalid_clauses,no_openmp"
// CHECK: attributes [[attr10]]
// CHECK-SAME: "llvm.assume"="1234,no_openmp_routines,another_warning,after_invalid_clauses,no_openmp,no_openmp_routines,another_warning,after_invalid_clauses,no_openmp"
// CHECK: attributes [[attr11]]
// CHECK-SAME: "llvm.assume"="lambda_assumption,no_openmp_routines,another_warning,after_invalid_clauses,no_openmp"
70 changes: 70 additions & 0 deletions clang/test/OpenMP/assumes_include_nvptx.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// RUN: %clang_cc1 -x c++ -O1 -disable-llvm-optzns -verify -fopenmp -internal-isystem %S/../Headers/Inputs/include -internal-isystem %S/../../lib/Headers/openmp_wrappers -include __clang_openmp_device_functions.h -triple powerpc64le-unknown-unknown -fopenmp-targets=nvptx64-nvidia-cuda -emit-llvm-bc %s -o %t-ppc-host.bc
// RUN: %clang_cc1 -x c++ -O1 -disable-llvm-optzns -verify -fopenmp -internal-isystem %S/../Headers/Inputs/include -internal-isystem %S/../../lib/Headers/openmp_wrappers -include __clang_openmp_device_functions.h -triple nvptx64-unknown-unknown -aux-triple powerpc64le-unknown-unknown -fopenmp-targets=nvptx64-nvidia-cuda -emit-llvm %s -fopenmp-is-device -fopenmp-host-ir-file-path %t-ppc-host.bc -o - | FileCheck %s
// RUN: %clang_cc1 -x c++ -O1 -disable-llvm-optzns -verify -fopenmp -internal-isystem %S/../Headers/Inputs/include -internal-isystem %S/../../lib/Headers/openmp_wrappers -include __clang_openmp_device_functions.h -triple i386-unknown-unknown -fopenmp-targets=nvptx-nvidia-cuda -emit-llvm-bc %s -o %t-x86-host.bc
// RUN: %clang_cc1 -x c++ -O1 -disable-llvm-optzns -verify -fopenmp -internal-isystem %S/../Headers/Inputs/include -internal-isystem %S/../../lib/Headers/openmp_wrappers -include __clang_openmp_device_functions.h -triple nvptx64-unknown-unknown -aux-triple powerpc64le-unknown-unknown -fopenmp-targets=nvptx64-nvidia-cuda -emit-llvm %s -fopenmp-is-device -fopenmp-host-ir-file-path %t-x86-host.bc -o - | FileCheck %s
// RUN: %clang_cc1 -x c++ -O1 -disable-llvm-optzns -verify -fopenmp -internal-isystem %S/../Headers/Inputs/include -internal-isystem %S/../../lib/Headers/openmp_wrappers -include __clang_openmp_device_functions.h -fexceptions -fcxx-exceptions -aux-triple powerpc64le-unknown-unknown -triple nvptx64-unknown-unknown -fopenmp-targets=nvptx64-nvidia-cuda -emit-llvm %s -fopenmp-is-device -fopenmp-host-ir-file-path %t-x86-host.bc -o - | FileCheck %s
// expected-no-diagnostics
#ifndef HEADER
#define HEADER

#include <cmath>

// TODO: Think about teaching the OMPIRBuilder about default attributes as well so the __kmpc* declarations are annotated.

// CHECK: define internal void @__omp_offloading_{{.*}}__Z17complex_reductionIfEvv_{{.*}}_worker() [[attr0:#[0-9]*]]
// CHECK: define weak void @__omp_offloading_{{.*}}__Z17complex_reductionIfEvv_{{.*}}() [[attr0]]
// CHECK: %call = call float @_Z3sinf(float 0.000000e+00) [[attr5:#[0-9]*]]
// CHECK: declare i32 @llvm.nvvm.read.ptx.sreg.warpsize() [[attr1:#[0-9]*]]
// CHECK: declare i32 @llvm.nvvm.read.ptx.sreg.ntid.x() [[attr1]]
// CHECK: declare i32 @llvm.nvvm.read.ptx.sreg.tid.x() [[attr1]]
// CHECK: declare void @__kmpc_kernel_init(i32, i16)
// CHECK-NOT: #
// CHECK: declare void @__kmpc_data_sharing_init_stack()
// CHECK-NOT: #
// CHECK: declare float @_Z3sinf(float) [[attr2:#[0-9]*]]
// CHECK: declare void @__kmpc_kernel_deinit(i16)
// CHECK-NOT: #
// CHECK: declare void @__kmpc_barrier_simple_spmd(%struct.ident_t*, i32) [[attr3:#[0-9]*]]
// CHECK: declare i1 @__kmpc_kernel_parallel(i8**)
// CHECK-NOT: #
// CHECK: declare i32 @__kmpc_global_thread_num(%struct.ident_t*) [[attr4:#[0-9]*]]
// CHECK: declare void @__kmpc_kernel_end_parallel()
// CHECK-NOT: #
// CHECK: define internal void @__omp_offloading_{{.*}}__Z17complex_reductionIdEvv_{{.*}}_worker() [[attr0]]
// CHECK: define weak void @__omp_offloading_{{.*}}__Z17complex_reductionIdEvv_{{.*}}() [[attr0]]
// CHECK: %call = call double @_Z3sind(double 0.000000e+00) [[attr5]]
// CHECK: declare double @_Z3sind(double) [[attr2]]

// CHECK: attributes [[attr0]]
// CHECK-NOT: "llvm.assume"
// CHECK: attributes [[attr1]]
// CHECK-NOT: "llvm.assume"
// CHECK: attributes [[attr2]]
// CHECK-SAME: "llvm.assume"="check_that_this_is_attached_to_included_functions_and_template_instantiations"
// CHECK: attributes [[attr3]]
// CHECK-NOT: "llvm.assume"
// CHECK: attributes [[attr4]]
// CHECK-NOT: "llvm.assume"
// CHECK: attributes [[attr5]]
// CHECK-SAME: "llvm.assume"="check_that_this_is_attached_to_included_functions_and_template_instantiations"


template <typename T>
void foo() {
cos(T(0));
}

template <typename T>
void complex_reduction() {
foo<T>();
#pragma omp target
sin(T(0));
}

#pragma omp assumes ext_check_that_this_is_attached_to_included_functions_and_template_instantiations

void test() {
complex_reduction<float>();
complex_reduction<double>();
}
#endif
69 changes: 69 additions & 0 deletions clang/test/OpenMP/assumes_messages.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// RUN: %clang_cc1 -triple=x86_64-pc-win32 -verify -fopenmp -x c -std=c99 -fms-extensions -Wno-pragma-pack %s

// RUN: %clang_cc1 -triple=x86_64-pc-win32 -verify -fopenmp-simd -x c -std=c99 -fms-extensions -Wno-pragma-pack %s

#pragma omp assumes // expected-error {{expected at least one 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism' clause for '#pragma omp assumes'}}
#pragma omp begin // expected-error {{expected an OpenMP directive}}
#pragma omp begin assumes // expected-error {{expected at least one 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism' clause for '#pragma omp begin assumes'}}
#pragma omp end assumes

#pragma omp assumes foobar // expected-warning {{valid assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; token will be ignored}}
#pragma omp begin assumes foobar // expected-warning {{valid begin assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; token will be ignored}}
#pragma omp end assumes

#pragma omp begin assumes foobar(foo 2 no_openmp // expected-error {{expected ')'}} expected-warning {{valid begin assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; tokens will be ignored}} expected-note {{to match this '('}}
#pragma omp assumes foobar(foo 2 no_openmp // expected-error {{expected ')'}} expected-warning {{valid assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; tokens will be ignored}} expected-note {{to match this '('}}
#pragma omp end assumes

#pragma omp begin assumes foobar(foo 2 baz) // expected-warning {{valid begin assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; tokens will be ignored}} expected-note {{the ignored tokens spans until here}}
#pragma omp assumes foobar(foo 2 baz) // expected-warning {{valid assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; tokens will be ignored}} expected-note {{the ignored tokens spans until here}}
#pragma omp end assumes

#pragma omp begin assumes foobar foo 2 baz) bar // expected-warning {{valid begin assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; token will be ignored}} expected-warning {{valid begin assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; token will be ignored}} expected-warning {{valid begin assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; token will be ignored}} expected-warning {{valid begin assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; token will be ignored}} expected-warning {{valid begin assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; token will be ignored}} expected-warning {{valid begin assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; token will be ignored}}
#pragma omp assumes foobar foo 2 baz) bar // expected-warning {{valid assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; token will be ignored}} expected-warning {{valid assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; token will be ignored}} expected-warning {{valid assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; token will be ignored}} expected-warning {{valid assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; token will be ignored}} expected-warning {{valid assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; token will be ignored}} expected-warning {{valid assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; token will be ignored}}
#pragma omp end assumes

#pragma omp assumes no_openmp(1) // expected-warning {{'no_openmp' clause should not be followed by arguments; tokens will be ignored}} expected-note {{the ignored tokens spans until here}}
#pragma omp begin assumes no_openmp(1 2 3) // expected-warning {{'no_openmp' clause should not be followed by arguments; tokens will be ignored}} expected-note {{the ignored tokens spans until here}}
#pragma omp end assumes no_openmp(1)

#pragma omp assumes foobar no_openmp bazbaz // expected-warning {{valid assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; token will be ignored}} expected-warning {{valid assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; token will be ignored}}
#pragma omp begin assumes foobar no_openmp bazbaz // expected-warning {{valid begin assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; token will be ignored}} expected-warning {{valid begin assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; token will be ignored}}
#pragma omp end assumes

#pragma omp begin assumes foobar(foo 2 baz) no_openmp bazbaz(foo 2 baz) // expected-warning {{valid begin assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; tokens will be ignored}} expected-warning {{valid begin assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; tokens will be ignored}} expected-note {{the ignored tokens spans until here}} expected-note {{the ignored tokens spans until here}}
#pragma omp assumes foobar(foo 2 baz) no_openmp bazbaz(foo 2 baz) // expected-warning {{valid assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; tokens will be ignored}} expected-warning {{valid assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; tokens will be ignored}} expected-note {{the ignored tokens spans until here}} expected-note {{the ignored tokens spans until here}}
#pragma omp end assumes

#pragma omp begin assumes foobar(foo (2) baz) no_openmp bazbaz(foo (2)) baz) // expected-warning {{valid begin assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; tokens will be ignored}} expected-warning {{valid begin assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; tokens will be ignored}} expected-warning {{valid begin assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; token will be ignored}} expected-warning {{valid begin assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; token will be ignored}} expected-note {{the ignored tokens spans until here}} expected-note {{the ignored tokens spans until here}}
#pragma omp assumes foobar(foo () baz) no_openmp bazbaz(foo ((2) baz) // expected-error {{expected ')'}} expected-warning {{valid assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; tokens will be ignored}} expected-warning {{valid assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; tokens will be ignored}} expected-note {{the ignored tokens spans until here}} expected-note {{to match this '('}}
#pragma omp end assumes

#pragma omp assumes no_openmp foobar no_openmp // expected-warning {{valid assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; token will be ignored}}
#pragma omp begin assumes no_openmp foobar no_openmp // expected-warning {{valid begin assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; token will be ignored}}
#pragma omp end assumes

#pragma omp assumes holds(1, 2 3)
#pragma omp begin assumes holds(1, 2 3)
#pragma omp end assumes

#pragma omp assumes absent(1, 2 3)
#pragma omp begin assumes absent(1, 2 3)
#pragma omp end assumes

#pragma omp assumes contains(1, 2 3)
#pragma omp begin assumes contains(1, 2 3)
#pragma omp end assumes

#pragma omp assumes ext // expected-warning {{valid assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; token will be ignored}}
#pragma omp begin assumes ext // expected-warning {{valid begin assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; token will be ignored}}
#pragma omp end assumes

#pragma omp assumes ext_123(not allowed) // expected-warning {{'ext_123' clause should not be followed by arguments; tokens will be ignored}} expected-note {{the ignored tokens spans until here}}
#pragma omp begin assumes ext_123(not allowed) // expected-warning {{'ext_123' clause should not be followed by arguments; tokens will be ignored}} expected-note {{the ignored tokens spans until here}}
#pragma omp end assumes

#pragma omp end assumes // expected-error {{'#pragma omp end assumes' with no matching '#pragma omp begin assumes'}}

// TODO: we should emit a warning at least.
#pragma omp begin assumes ext_abc
44 changes: 44 additions & 0 deletions clang/test/OpenMP/assumes_print.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// RUN: %clang_cc1 -verify -fopenmp -ast-print %s | FileCheck %s
// RUN: %clang_cc1 -fopenmp -x c++ -std=c++11 -emit-pch -o %t %s
// RUN: %clang_cc1 -fopenmp -std=c++11 -include-pch %t -fsyntax-only -verify %s -ast-print | FileCheck %s

// RUN: %clang_cc1 -verify -fopenmp-simd -ast-print %s | FileCheck %s
// RUN: %clang_cc1 -fopenmp-simd -x c++ -std=c++11 -emit-pch -o %t %s
// RUN: %clang_cc1 -fopenmp-simd -std=c++11 -include-pch %t -fsyntax-only -verify %s -ast-print | FileCheck %s
// expected-no-diagnostics

#ifndef HEADER
#define HEADER

void foo() {
}

#pragma omp assumes no_openmp_routines

namespace inner {
#pragma omp assumes no_openmp
} // namespace inner

#pragma omp begin assumes ext_range_bar_only

#pragma omp begin assumes ext_range_bar_only_2

void bar() {
}

#pragma omp end assumes
#pragma omp end assumes

#pragma omp begin assumes ext_not_seen
#pragma omp end assumes

#pragma omp begin assumes ext_1234
void baz() {
}
#pragma omp end assumes

// CHECK: void foo() __attribute__((assume("no_openmp_routines"))) __attribute__((assume("no_openmp")))
// CHECK: void bar() __attribute__((assume("range_bar_only"))) __attribute__((assume("range_bar_only_2"))) __attribute__((assume("no_openmp_routines"))) __attribute__((assume("no_openmp")))
// CHECK: void baz() __attribute__((assume("1234"))) __attribute__((assume("no_openmp_routines"))) __attribute__((assume("no_openmp")))

#endif
91 changes: 91 additions & 0 deletions clang/test/OpenMP/assumes_template_print.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// RUN: %clang_cc1 -verify -fopenmp -ast-print %s | FileCheck %s
// RUN: %clang_cc1 -fopenmp -x c++ -std=c++11 -emit-pch -o %t %s
// RUN: %clang_cc1 -fopenmp -std=c++11 -include-pch %t -fsyntax-only -verify %s -ast-print | FileCheck %s

// RUN: %clang_cc1 -verify -fopenmp-simd -ast-print %s | FileCheck %s
// RUN: %clang_cc1 -fopenmp-simd -x c++ -std=c++11 -emit-pch -o %t %s
// RUN: %clang_cc1 -fopenmp-simd -std=c++11 -include-pch %t -fsyntax-only -verify %s -ast-print | FileCheck %s
// expected-no-diagnostics

// It is unclear if we want to annotate the template instantiations, e.g., S<int>::foo, or not in the two
// situations shown below. Since it is always fair to drop assumptions, we do that for now.

#ifndef HEADER
#define HEADER

template <typename T>
struct S {
int a;
// CHECK: template <typename T> struct S {
// CHECK: void foo() __attribute__((assume("global_assumption"))) {
void foo() {
#pragma omp parallel
{}
}
};

// CHECK: template<> struct S<int> {
// CHECK: void foo() __attribute__((assume("global_assumption"))) {

#pragma omp begin assumes no_openmp
// CHECK: void S_with_assumes_no_call() __attribute__((assume("no_openmp"))) __attribute__((assume("global_assumption"))) {
void S_with_assumes_no_call() {
S<int> s;
s.a = 0;
}
// CHECK: void S_with_assumes_call() __attribute__((assume("no_openmp"))) __attribute__((assume("global_assumption"))) {
void S_with_assumes_call() {
S<int> s;
s.a = 0;
// If this is executed we have UB!
s.foo();
}
#pragma omp end assumes

// CHECK: void S_without_assumes() __attribute__((assume("global_assumption"))) {
void S_without_assumes() {
S<int> s;
s.foo();
}

#pragma omp assumes ext_global_assumption

// Same as the struct S above but the order in which we instantiate P is different, first outside of an assumes.
template <typename T>
struct P {
// CHECK: template <typename T> struct P {
// CHECK: void foo() __attribute__((assume("global_assumption"))) {
int a;
void foo() {
#pragma omp parallel
{}
}
};

// TODO: Avoid the duplication here:

// CHECK: template<> struct P<int> {
// CHECK: void foo() __attribute__((assume("global_assumption"))) __attribute__((assume("global_assumption"))) {

// CHECK: void P_without_assumes() __attribute__((assume("global_assumption"))) {
void P_without_assumes() {
P<int> p;
p.foo();
}

#pragma omp begin assumes no_openmp
// CHECK: void P_with_assumes_no_call() __attribute__((assume("no_openmp"))) __attribute__((assume("global_assumption"))) {
void P_with_assumes_no_call() {
P<int> p;
p.a = 0;
}
// CHECK: void P_with_assumes_call() __attribute__((assume("no_openmp"))) __attribute__((assume("global_assumption"))) {
void P_with_assumes_call() {
P<int> p;
p.a = 0;
// If this is executed we have UB!
p.foo();
}
#pragma omp end assumes

#endif
14 changes: 14 additions & 0 deletions clang/test/Sema/attr-assume.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// RUN: %clang_cc1 -triple i386-apple-darwin9 -fsyntax-only -verify %s

void f1() __attribute__((assume(3))); // expected-error {{'assume' attribute requires a string}}
void f2() __attribute__((assume(int))); // expected-error {{expected expression}}
void f3() __attribute__((assume(for))); // expected-error {{expected expression}}
void f4() __attribute__((assume("QQQQ"))); // expected-warning {{unknown assumption string 'QQQQ'; attribute is potentially ignored}}
void f5() __attribute__((assume("omp_no_openmp")));
void f6() __attribute__((assume("omp_noopenmp"))); // expected-warning {{unknown assumption string 'omp_noopenmp' may be misspelled; attribute is potentially ignored, did you mean 'omp_no_openmp'?}}
void f7() __attribute__((assume("omp_no_openmp_routine"))); // expected-warning {{unknown assumption string 'omp_no_openmp_routine' may be misspelled; attribute is potentially ignored, did you mean 'omp_no_openmp_routines'?}}
void f8() __attribute__((assume("omp_no_openmp1"))); // expected-warning {{unknown assumption string 'omp_no_openmp1' may be misspelled; attribute is potentially ignored, did you mean 'omp_no_openmp'?}}
void f9() __attribute__((assume("omp_no_openmp", "omp_no_openmp"))); // expected-error {{'assume' attribute takes one argument}}

int g1 __attribute__((assume(0))); // expected-warning {{'assume' attribute only applies to functions and Objective-C methods}}
int g2 __attribute__((assume("omp_no_openmp"))); // expected-warning {{'assume' attribute only applies to functions and Objective-C methods}}
3 changes: 3 additions & 0 deletions llvm/include/llvm/Frontend/OpenMP/OMP.td
Original file line number Diff line number Diff line change
Expand Up @@ -1593,6 +1593,9 @@ def OMP_Scan : Directive<"scan"> {
VersionedClause<OMPC_Exclusive, 50>
];
}
def OMP_Assumes : Directive<"assumes"> {}
def OMP_BeginAssumes : Directive<"begin assumes"> {}
def OMP_EndAssumes : Directive<"end assumes"> {}
def OMP_BeginDeclareVariant : Directive<"begin declare variant"> {}
def OMP_EndDeclareVariant : Directive<"end declare variant"> {}
def OMP_ParallelWorkshare : Directive<"parallel workshare"> {
Expand Down
28 changes: 28 additions & 0 deletions llvm/include/llvm/Frontend/OpenMP/OMPConstants.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#include "llvm/ADT/BitmaskEnum.h"

#include "llvm/ADT/StringRef.h"
#include "llvm/Frontend/OpenMP/OMP.h.inc"

namespace llvm {
Expand Down Expand Up @@ -79,6 +80,33 @@ enum class IdentFlag {
#define OMP_IDENT_FLAG(Enum, ...) constexpr auto Enum = omp::IdentFlag::Enum;
#include "llvm/Frontend/OpenMP/OMPKinds.def"

/// Helper to describe assume clauses.
struct AssumptionClauseMappingInfo {
/// The identifier describing the (beginning of the) clause.
llvm::StringLiteral Identifier;
/// Flag to determine if the identifier is a full name or the start of a name.
bool StartsWith;
/// Flag to determine if a directive lists follows.
bool HasDirectiveList;
/// Flag to determine if an expression follows.
bool HasExpression;
};

/// All known assume clauses.
static constexpr AssumptionClauseMappingInfo AssumptionClauseMappings[] = {
#define OMP_ASSUME_CLAUSE(Identifier, StartsWith, HasDirectiveList, \
HasExpression) \
{Identifier, StartsWith, HasDirectiveList, HasExpression},
#include "llvm/Frontend/OpenMP/OMPKinds.def"
};

inline std::string getAllAssumeClauseOptions() {
std::string S;
for (const AssumptionClauseMappingInfo &ACMI : AssumptionClauseMappings)
S += (S.empty() ? "'" : "', '") + ACMI.Identifier.str();
return S + "'";
}

} // end namespace omp

} // end namespace llvm
Expand Down
24 changes: 24 additions & 0 deletions llvm/include/llvm/Frontend/OpenMP/OMPKinds.def
Original file line number Diff line number Diff line change
Expand Up @@ -1224,3 +1224,27 @@ OMP_LAST_TRAIT_PROPERTY(
#undef __OMP_REQUIRES_TRAIT
#undef OMP_REQUIRES_TRAIT
///}


/// Assumption clauses
///
///{

#ifdef OMP_ASSUME_CLAUSE
#define __OMP_ASSUME_CLAUSE(Identifier, StartsWith, HasDirectiveList, HasExpression) \
OMP_ASSUME_CLAUSE(Identifier, StartsWith, HasDirectiveList, HasExpression)
#else
#define __OMP_ASSUME_CLAUSE(...)
#endif

__OMP_ASSUME_CLAUSE("ext_", true, false, false)
__OMP_ASSUME_CLAUSE("absent", false, true, false)
__OMP_ASSUME_CLAUSE("contains", false, true, false)
__OMP_ASSUME_CLAUSE("holds", false, false, true)
__OMP_ASSUME_CLAUSE("no_openmp", false, false, false)
__OMP_ASSUME_CLAUSE("no_openmp_routines", false, false, false)
__OMP_ASSUME_CLAUSE("no_parallelism", false, false, false)

#undef __OMP_ASSUME_CLAUSE
#undef OMP_ASSUME_CLAUSE
///}
50 changes: 50 additions & 0 deletions llvm/include/llvm/IR/Assumptions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//===--- Assumptions.h - Assumption handling and organization ---*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// String assumptions that are known to optimization passes should be placed in
// the KnownAssumptionStrings set. This can be done in various ways, i.a.,
// via a static KnownAssumptionString object.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_IR_ASSUMPTIONS_H
#define LLVM_IR_ASSUMPTIONS_H

#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSet.h"

namespace llvm {

class Function;

/// The key we use for assumption attributes.
constexpr StringRef AssumptionAttrKey = "llvm.assume";

/// A set of known assumption strings that are accepted without warning and
/// which can be recommended as typo correction.
extern StringSet<> KnownAssumptionStrings;

/// Helper that allows to insert a new assumption string in the known assumption
/// set by creating a (static) object.
struct KnownAssumptionString {
KnownAssumptionString(StringRef AssumptionStr)
: AssumptionStr(AssumptionStr) {
KnownAssumptionStrings.insert(AssumptionStr);
}
operator StringRef() const { return AssumptionStr; }

private:
StringRef AssumptionStr;
};

/// Return true if \p F has the assumption \p AssumptionStr attached.
bool hasAssumption(Function &F, const KnownAssumptionString &AssumptionStr);

} // namespace llvm

#endif
36 changes: 36 additions & 0 deletions llvm/lib/IR/Assumptions.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//===- Assumptions.cpp ------ Collection of helpers for assumptions -------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===----------------------------------------------------------------------===//

#include "llvm/IR/Assumptions.h"
#include "llvm/IR/Attributes.h"
#include "llvm/IR/Function.h"

using namespace llvm;

bool llvm::hasAssumption(Function &F,
const KnownAssumptionString &AssumptionStr) {
const Attribute &A = F.getFnAttribute(AssumptionAttrKey);
if (!A.isValid())
return false;
assert(A.isStringAttribute() && "Expected a string attribute!");

SmallVector<StringRef, 8> Strings;
A.getValueAsString().split(Strings, ",");

return llvm::any_of(Strings, [=](StringRef Assumption) {
return Assumption == AssumptionStr;
});
}

StringSet<> llvm::KnownAssumptionStrings({
"omp_no_openmp", // OpenMP 5.1
"omp_no_openmp_routines", // OpenMP 5.1
"omp_no_parallelism", // OpenMP 5.1
});
1 change: 1 addition & 0 deletions llvm/lib/IR/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
add_llvm_component_library(LLVMCore
AbstractCallSite.cpp
AsmWriter.cpp
Assumptions.cpp
Attributes.cpp
AutoUpgrade.cpp
BasicBlock.cpp
Expand Down
11 changes: 5 additions & 6 deletions llvm/lib/Transforms/IPO/OpenMPOpt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ static cl::opt<bool> HideMemoryTransferLatency(
" transfers"),
cl::Hidden, cl::init(false));


STATISTIC(NumOpenMPRuntimeCallsDeduplicated,
"Number of OpenMP runtime calls deduplicated");
STATISTIC(NumOpenMPParallelRegionsDeleted,
Expand Down Expand Up @@ -426,8 +425,7 @@ struct OffloadArray {
/// instruction \p Before is reached.
bool getValues(AllocaInst &Array, Instruction &Before) {
// Initialize container.
const uint64_t NumValues =
Array.getAllocatedType()->getArrayNumElements();
const uint64_t NumValues = Array.getAllocatedType()->getArrayNumElements();
StoredValues.assign(NumValues, nullptr);
LastAccesses.assign(NumValues, nullptr);

Expand All @@ -449,8 +447,8 @@ struct OffloadArray {

auto *S = cast<StoreInst>(&I);
int64_t Offset = -1;
auto *Dst = GetPointerBaseWithConstantOffset(S->getPointerOperand(),
Offset, DL);
auto *Dst =
GetPointerBaseWithConstantOffset(S->getPointerOperand(), Offset, DL);
if (Dst == &Array) {
int64_t Idx = Offset / PointerSize;
StoredValues[Idx] = getUnderlyingObject(S->getValueOperand());
Expand Down Expand Up @@ -1770,7 +1768,8 @@ struct AAICVTrackerFunction : public AAICVTracker {
InternalControlVar &ICV) const {

const auto *CB = dyn_cast<CallBase>(I);
if (!CB)
if (!CB || CB->hasFnAttr("no_openmp") ||
CB->hasFnAttr("no_openmp_routines"))
return None;

auto &OMPInfoCache = static_cast<OMPInformationCache &>(A.getInfoCache());
Expand Down
44 changes: 44 additions & 0 deletions llvm/test/Transforms/OpenMP/icv_tracking.ll
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,22 @@ define i32 @bad_use(i32 %0) {
ret i32 %2
}

define i32 @ok_use_assume(i32 %0) {
; CHECK-LABEL: define {{[^@]+}}@ok_use_assume
; CHECK-SAME: (i32 [[TMP0:%.*]]) {
; CHECK-NEXT: call void @use(i32 [[TMP0]]) [[ATTR1:#.*]]
; CHECK-NEXT: call void @use(i32 [[TMP0]]) [[ATTR2:#.*]]
; CHECK-NEXT: call void @no_openmp_use(i32 [[TMP0]])
; CHECK-NEXT: [[TMP2:%.*]] = add nsw i32 [[TMP0]], 1
; CHECK-NEXT: ret i32 [[TMP2]]
;
call void @use(i32 %0) "no_openmp"
call void @use(i32 %0) "no_openmp_routines"
call void @no_openmp_use(i32 %0)
%2 = add nsw i32 %0, 1
ret i32 %2
}

define void @indirect_call(void ()* %0) {
; CHECK-LABEL: define {{[^@]+}}@indirect_call
; CHECK-SAME: (void ()* [[TMP0:%.*]])
Expand Down Expand Up @@ -77,6 +93,7 @@ declare dso_local void @omp_set_num_threads(i32)
declare dso_local i32 @omp_get_max_threads()

declare dso_local void @use(i32)
declare dso_local void @no_openmp_use(i32) "no_openmp"

define internal void @.omp_outlined.(i32* %0, i32* %1) {
; CHECK-LABEL: define {{[^@]+}}@.omp_outlined.
Expand Down Expand Up @@ -266,6 +283,33 @@ define void @bad_use_test(i1 %0) {
ret void
}

define void @ok_use_assume_test(i1 %0) {
; CHECK-LABEL: define {{[^@]+}}@ok_use_assume_test
; CHECK-SAME: (i1 [[TMP0:%.*]]) {
; CHECK-NEXT: call void @omp_set_num_threads(i32 2)
; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i1 [[TMP0]], false
; CHECK-NEXT: br i1 [[TMP2]], label [[TMP5:%.*]], label [[TMP3:%.*]]
; CHECK: 3:
; CHECK-NEXT: [[TMP4:%.*]] = call i32 @ok_use_assume(i32 10)
; CHECK-NEXT: br label [[TMP5]]
; CHECK: 5:
; CHECK-NEXT: call void @use(i32 2)
; CHECK-NEXT: ret void
;
call void @omp_set_num_threads(i32 2)
%2 = icmp eq i1 %0, 0
br i1 %2, label %5, label %3

3: ; preds = %1
%4 = call i32 @ok_use_assume(i32 10)
br label %5

5: ; preds = %3, %1
%6 = call i32 @omp_get_max_threads()
call void @use(i32 %6)
ret void
}

define weak void @weak_known_unique_icv(i1 %0) {
; CHECK-LABEL: define {{[^@]+}}@weak_known_unique_icv
; CHECK-SAME: (i1 [[TMP0:%.*]])
Expand Down