[OpenMP] Allow data members in interop init/use/destroy clauses

Previously a diagnostic was given if the expression was not strictly a
DeclRef. Now also allow use of data members inside member functions.

Differential Revision: https://reviews.llvm.org/D131222
This commit is contained in:
Mike Rice 2022-08-04 15:16:43 -07:00
parent c6c5944d05
commit dd4c838da3
5 changed files with 273 additions and 32 deletions

View File

@ -10387,6 +10387,9 @@ def err_omp_unexpected_clause_value : Error<
"expected %0 in OpenMP clause '%1'">;
def err_omp_expected_var_name_member_expr : Error<
"expected variable name%select{| or data member of current class}0">;
def err_omp_expected_var_name_member_expr_with_type : Error<
"expected variable%select{| or static data member|, static data member, "
"or non-static data member of current class}0 of type '%1'">;
def err_omp_expected_var_name_member_expr_or_array_item : Error<
"expected variable name%select{|, data member of current class}0, array element or array section">;
def err_omp_expected_addressable_lvalue_or_array_item : Error<

View File

@ -2718,7 +2718,8 @@ void Sema::EndOpenMPClause() {
static std::pair<ValueDecl *, bool>
getPrivateItem(Sema &S, Expr *&RefExpr, SourceLocation &ELoc,
SourceRange &ERange, bool AllowArraySection = false);
SourceRange &ERange, bool AllowArraySection = false,
StringRef DiagType = "");
/// Check consistency of the reduction clauses.
static void checkReductionClauses(Sema &S, DSAStackTy *Stack,
@ -5279,7 +5280,8 @@ static bool checkIfClauses(Sema &S, OpenMPDirectiveKind Kind,
static std::pair<ValueDecl *, bool> getPrivateItem(Sema &S, Expr *&RefExpr,
SourceLocation &ELoc,
SourceRange &ERange,
bool AllowArraySection) {
bool AllowArraySection,
StringRef DiagType) {
if (RefExpr->isTypeDependent() || RefExpr->isValueDependent() ||
RefExpr->containsUnexpandedParameterPack())
return std::make_pair(nullptr, true);
@ -5324,6 +5326,12 @@ static std::pair<ValueDecl *, bool> getPrivateItem(Sema &S, Expr *&RefExpr,
if (IsArrayExpr != NoArrayExpr) {
S.Diag(ELoc, diag::err_omp_expected_base_var_name)
<< IsArrayExpr << ERange;
} else if (!DiagType.empty()) {
unsigned DiagSelect = S.getLangOpts().CPlusPlus
? (S.getCurrentThisType().isNull() ? 1 : 2)
: 0;
S.Diag(ELoc, diag::err_omp_expected_var_name_member_expr_with_type)
<< DiagSelect << DiagType << ERange;
} else {
S.Diag(ELoc,
AllowArraySection
@ -17249,32 +17257,28 @@ StmtResult Sema::ActOnOpenMPInteropDirective(ArrayRef<OMPClause *> Clauses,
// OpenMP 5.1 [2.15.1, interop Construct, Restrictions]
// Each interop-var may be specified for at most one action-clause of each
// interop construct.
llvm::SmallPtrSet<const VarDecl *, 4> InteropVars;
for (const OMPClause *C : Clauses) {
llvm::SmallPtrSet<const ValueDecl *, 4> InteropVars;
for (OMPClause *C : Clauses) {
OpenMPClauseKind ClauseKind = C->getClauseKind();
const DeclRefExpr *DRE = nullptr;
SourceLocation VarLoc;
std::pair<ValueDecl *, bool> DeclResult;
SourceLocation ELoc;
SourceRange ERange;
if (ClauseKind == OMPC_init) {
const auto *IC = cast<OMPInitClause>(C);
VarLoc = IC->getVarLoc();
DRE = dyn_cast_or_null<DeclRefExpr>(IC->getInteropVar());
auto *E = cast<OMPInitClause>(C)->getInteropVar();
DeclResult = getPrivateItem(*this, E, ELoc, ERange);
} else if (ClauseKind == OMPC_use) {
const auto *UC = cast<OMPUseClause>(C);
VarLoc = UC->getVarLoc();
DRE = dyn_cast_or_null<DeclRefExpr>(UC->getInteropVar());
auto *E = cast<OMPUseClause>(C)->getInteropVar();
DeclResult = getPrivateItem(*this, E, ELoc, ERange);
} else if (ClauseKind == OMPC_destroy) {
const auto *DC = cast<OMPDestroyClause>(C);
VarLoc = DC->getVarLoc();
DRE = dyn_cast_or_null<DeclRefExpr>(DC->getInteropVar());
auto *E = cast<OMPDestroyClause>(C)->getInteropVar();
DeclResult = getPrivateItem(*this, E, ELoc, ERange);
}
if (!DRE)
continue;
if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
if (!InteropVars.insert(VD->getCanonicalDecl()).second) {
Diag(VarLoc, diag::err_omp_interop_var_multiple_actions) << VD;
if (DeclResult.first) {
if (!InteropVars.insert(DeclResult.first).second) {
Diag(ELoc, diag::err_omp_interop_var_multiple_actions)
<< DeclResult.first;
return StmtError();
}
}
@ -17286,17 +17290,21 @@ StmtResult Sema::ActOnOpenMPInteropDirective(ArrayRef<OMPClause *> Clauses,
static bool isValidInteropVariable(Sema &SemaRef, Expr *InteropVarExpr,
SourceLocation VarLoc,
OpenMPClauseKind Kind) {
if (InteropVarExpr->isValueDependent() || InteropVarExpr->isTypeDependent() ||
InteropVarExpr->isInstantiationDependent() ||
InteropVarExpr->containsUnexpandedParameterPack())
return true;
SourceLocation ELoc;
SourceRange ERange;
Expr *RefExpr = InteropVarExpr;
auto Res =
getPrivateItem(SemaRef, RefExpr, ELoc, ERange,
/*AllowArraySection=*/false, /*DiagType=*/"omp_interop_t");
const auto *DRE = dyn_cast<DeclRefExpr>(InteropVarExpr);
if (!DRE || !isa<VarDecl>(DRE->getDecl())) {
SemaRef.Diag(VarLoc, diag::err_omp_interop_variable_expected) << 0;
return false;
if (Res.second) {
// It will be analyzed later.
return true;
}
if (!Res.first)
return false;
// Interop variable should be of type omp_interop_t.
bool HasError = false;
QualType InteropType;

View File

@ -23,6 +23,57 @@
typedef void *omp_interop_t;
struct S {
omp_interop_t o1;
omp_interop_t o2;
omp_interop_t o3;
static omp_interop_t so;
void foo();
S();
~S();
};
omp_interop_t S::so;
struct T {
static void static_member_func();
static omp_interop_t to;
};
omp_interop_t T::to;
void T::static_member_func() {
omp_interop_t o1;
//PRINT: #pragma omp interop init(target : o1)
#pragma omp interop init(target:o1)
//PRINT: #pragma omp interop init(target : to)
#pragma omp interop init(target: to)
//PRINT: #pragma omp interop init(target : T::to)
#pragma omp interop init(target: T::to)
//PRINT: #pragma omp interop init(target : S::so)
#pragma omp interop init(target: S::so)
}
S::S() {
//PRINT: #pragma omp interop init(target : this->o1)
#pragma omp interop init(target:o1)
//PRINT: #pragma omp interop use(this->o1) init(target : this->o2)
#pragma omp interop use(o1) init(target:o2)
//PRINT: #pragma omp interop use(this->o2) init(target : this->o3)
#pragma omp interop use(o2) init(target:o3)
}
S::~S() {
//PRINT: #pragma omp interop destroy(this->o1) destroy(this->o2) destroy(this->o3)
#pragma omp interop destroy(o1) destroy(o2) destroy(o3)
}
void S::foo() {
//PRINT: #pragma omp interop init(target : so)
#pragma omp interop init(target:so)
}
//PRINT-LABEL: void foo1(
//DUMP-LABEL: FunctionDecl {{.*}} foo1
void foo1(int *ap, int dev) {
@ -196,6 +247,9 @@ void foo1(int *ap, int dev) {
//DUMP: OMPUseClause
//DUMP: DeclRefExpr{{.*}}'omp_interop_t'{{.*}}Var{{.*}}'J'
#pragma omp interop destroy(I) use(J)
//PRINT: #pragma omp interop init(target : S::so)
#pragma omp interop init(target: S::so)
}
//DUMP: FunctionTemplateDecl{{.*}}fooTemp
@ -274,6 +328,7 @@ void bar()
fooTemp<3>();
omp_interop_t Ivar;
barTemp(Ivar);
S s;
}
#endif // HEADER

View File

@ -29,6 +29,34 @@ void test1() {
: D0, D1)
}
struct S {
omp_interop_t interop;
void member_test();
};
void S::member_test() {
int device_id = 4;
int D0, D1;
#pragma omp interop init(target \
: interop)
#pragma omp interop init(targetsync \
: interop)
#pragma omp interop init(target \
: interop) device(device_id)
#pragma omp interop init(targetsync \
: interop) device(device_id)
#pragma omp interop use(interop) depend(in \
: D0, D1) nowait
#pragma omp interop destroy(interop) depend(in \
: D0, D1)
}
// CHECK-LABEL: @_Z5test1v(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[DEVICE_ID:%.*]] = alloca i32, align 4
@ -94,3 +122,77 @@ void test1() {
// CHECK-NEXT: call void @__tgt_interop_destroy(%struct.ident_t* @[[GLOB1]], i32 [[OMP_GLOBAL_THREAD_NUM7]], i8** [[INTEROP]], i32 -1, i32 2, i8* [[TMP25]], i32 0)
// CHECK-NEXT: ret void
//
//
// CHECK-LABEL: @_ZN1S11member_testEv(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[THIS_ADDR:%.*]] = alloca %struct.S*, align 8
// CHECK-NEXT: [[DEVICE_ID:%.*]] = alloca i32, align 4
// CHECK-NEXT: [[D0:%.*]] = alloca i32, align 4
// CHECK-NEXT: [[D1:%.*]] = alloca i32, align 4
// CHECK-NEXT: [[DOTDEP_ARR_ADDR:%.*]] = alloca [2 x %struct.kmp_depend_info], align 8
// CHECK-NEXT: [[DEP_COUNTER_ADDR:%.*]] = alloca i64, align 8
// CHECK-NEXT: [[DOTDEP_ARR_ADDR10:%.*]] = alloca [2 x %struct.kmp_depend_info], align 8
// CHECK-NEXT: [[DEP_COUNTER_ADDR11:%.*]] = alloca i64, align 8
// CHECK-NEXT: store %struct.S* [[THIS:%.*]], %struct.S** [[THIS_ADDR]], align 8
// CHECK-NEXT: [[THIS1:%.*]] = load %struct.S*, %struct.S** [[THIS_ADDR]], align 8
// CHECK-NEXT: store i32 4, i32* [[DEVICE_ID]], align 4
// CHECK-NEXT: [[INTEROP:%.*]] = getelementptr inbounds [[STRUCT_S:%.*]], %struct.S* [[THIS1]], i32 0, i32 0
// CHECK-NEXT: [[OMP_GLOBAL_THREAD_NUM:%.*]] = call i32 @__kmpc_global_thread_num(%struct.ident_t* @[[GLOB1]])
// CHECK-NEXT: call void @__tgt_interop_init(%struct.ident_t* @[[GLOB1]], i32 [[OMP_GLOBAL_THREAD_NUM]], i8** [[INTEROP]], i64 1, i32 -1, i32 0, i8* null, i32 0)
// CHECK-NEXT: [[INTEROP2:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[THIS1]], i32 0, i32 0
// CHECK-NEXT: [[OMP_GLOBAL_THREAD_NUM3:%.*]] = call i32 @__kmpc_global_thread_num(%struct.ident_t* @[[GLOB1]])
// CHECK-NEXT: call void @__tgt_interop_init(%struct.ident_t* @[[GLOB1]], i32 [[OMP_GLOBAL_THREAD_NUM3]], i8** [[INTEROP2]], i64 2, i32 -1, i32 0, i8* null, i32 0)
// CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* [[DEVICE_ID]], align 4
// CHECK-NEXT: [[INTEROP4:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[THIS1]], i32 0, i32 0
// CHECK-NEXT: [[OMP_GLOBAL_THREAD_NUM5:%.*]] = call i32 @__kmpc_global_thread_num(%struct.ident_t* @[[GLOB1]])
// CHECK-NEXT: call void @__tgt_interop_init(%struct.ident_t* @[[GLOB1]], i32 [[OMP_GLOBAL_THREAD_NUM5]], i8** [[INTEROP4]], i64 1, i32 [[TMP0]], i32 0, i8* null, i32 0)
// CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* [[DEVICE_ID]], align 4
// CHECK-NEXT: [[INTEROP6:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[THIS1]], i32 0, i32 0
// CHECK-NEXT: [[OMP_GLOBAL_THREAD_NUM7:%.*]] = call i32 @__kmpc_global_thread_num(%struct.ident_t* @[[GLOB1]])
// CHECK-NEXT: call void @__tgt_interop_init(%struct.ident_t* @[[GLOB1]], i32 [[OMP_GLOBAL_THREAD_NUM7]], i8** [[INTEROP6]], i64 2, i32 [[TMP1]], i32 0, i8* null, i32 0)
// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds [2 x %struct.kmp_depend_info], [2 x %struct.kmp_depend_info]* [[DOTDEP_ARR_ADDR]], i64 0, i64 0
// CHECK-NEXT: [[TMP3:%.*]] = ptrtoint i32* [[D0]] to i64
// CHECK-NEXT: [[TMP4:%.*]] = getelementptr [[STRUCT_KMP_DEPEND_INFO:%.*]], %struct.kmp_depend_info* [[TMP2]], i64 0
// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds [[STRUCT_KMP_DEPEND_INFO]], %struct.kmp_depend_info* [[TMP4]], i32 0, i32 0
// CHECK-NEXT: store i64 [[TMP3]], i64* [[TMP5]], align 8
// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds [[STRUCT_KMP_DEPEND_INFO]], %struct.kmp_depend_info* [[TMP4]], i32 0, i32 1
// CHECK-NEXT: store i64 4, i64* [[TMP6]], align 8
// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds [[STRUCT_KMP_DEPEND_INFO]], %struct.kmp_depend_info* [[TMP4]], i32 0, i32 2
// CHECK-NEXT: store i8 1, i8* [[TMP7]], align 8
// CHECK-NEXT: [[TMP8:%.*]] = ptrtoint i32* [[D1]] to i64
// CHECK-NEXT: [[TMP9:%.*]] = getelementptr [[STRUCT_KMP_DEPEND_INFO]], %struct.kmp_depend_info* [[TMP2]], i64 1
// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds [[STRUCT_KMP_DEPEND_INFO]], %struct.kmp_depend_info* [[TMP9]], i32 0, i32 0
// CHECK-NEXT: store i64 [[TMP8]], i64* [[TMP10]], align 8
// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds [[STRUCT_KMP_DEPEND_INFO]], %struct.kmp_depend_info* [[TMP9]], i32 0, i32 1
// CHECK-NEXT: store i64 4, i64* [[TMP11]], align 8
// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds [[STRUCT_KMP_DEPEND_INFO]], %struct.kmp_depend_info* [[TMP9]], i32 0, i32 2
// CHECK-NEXT: store i8 1, i8* [[TMP12]], align 8
// CHECK-NEXT: store i64 2, i64* [[DEP_COUNTER_ADDR]], align 8
// CHECK-NEXT: [[TMP13:%.*]] = bitcast %struct.kmp_depend_info* [[TMP2]] to i8*
// CHECK-NEXT: [[INTEROP8:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[THIS1]], i32 0, i32 0
// CHECK-NEXT: [[OMP_GLOBAL_THREAD_NUM9:%.*]] = call i32 @__kmpc_global_thread_num(%struct.ident_t* @[[GLOB1]])
// CHECK-NEXT: call void @__tgt_interop_use(%struct.ident_t* @[[GLOB1]], i32 [[OMP_GLOBAL_THREAD_NUM9]], i8** [[INTEROP8]], i32 -1, i32 2, i8* [[TMP13]], i32 1)
// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds [2 x %struct.kmp_depend_info], [2 x %struct.kmp_depend_info]* [[DOTDEP_ARR_ADDR10]], i64 0, i64 0
// CHECK-NEXT: [[TMP15:%.*]] = ptrtoint i32* [[D0]] to i64
// CHECK-NEXT: [[TMP16:%.*]] = getelementptr [[STRUCT_KMP_DEPEND_INFO]], %struct.kmp_depend_info* [[TMP14]], i64 0
// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds [[STRUCT_KMP_DEPEND_INFO]], %struct.kmp_depend_info* [[TMP16]], i32 0, i32 0
// CHECK-NEXT: store i64 [[TMP15]], i64* [[TMP17]], align 8
// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds [[STRUCT_KMP_DEPEND_INFO]], %struct.kmp_depend_info* [[TMP16]], i32 0, i32 1
// CHECK-NEXT: store i64 4, i64* [[TMP18]], align 8
// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds [[STRUCT_KMP_DEPEND_INFO]], %struct.kmp_depend_info* [[TMP16]], i32 0, i32 2
// CHECK-NEXT: store i8 1, i8* [[TMP19]], align 8
// CHECK-NEXT: [[TMP20:%.*]] = ptrtoint i32* [[D1]] to i64
// CHECK-NEXT: [[TMP21:%.*]] = getelementptr [[STRUCT_KMP_DEPEND_INFO]], %struct.kmp_depend_info* [[TMP14]], i64 1
// CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds [[STRUCT_KMP_DEPEND_INFO]], %struct.kmp_depend_info* [[TMP21]], i32 0, i32 0
// CHECK-NEXT: store i64 [[TMP20]], i64* [[TMP22]], align 8
// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds [[STRUCT_KMP_DEPEND_INFO]], %struct.kmp_depend_info* [[TMP21]], i32 0, i32 1
// CHECK-NEXT: store i64 4, i64* [[TMP23]], align 8
// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds [[STRUCT_KMP_DEPEND_INFO]], %struct.kmp_depend_info* [[TMP21]], i32 0, i32 2
// CHECK-NEXT: store i8 1, i8* [[TMP24]], align 8
// CHECK-NEXT: store i64 2, i64* [[DEP_COUNTER_ADDR11]], align 8
// CHECK-NEXT: [[TMP25:%.*]] = bitcast %struct.kmp_depend_info* [[TMP14]] to i8*
// CHECK-NEXT: [[INTEROP12:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[THIS1]], i32 0, i32 0
// CHECK-NEXT: [[OMP_GLOBAL_THREAD_NUM13:%.*]] = call i32 @__kmpc_global_thread_num(%struct.ident_t* @[[GLOB1]])
// CHECK-NEXT: call void @__tgt_interop_destroy(%struct.ident_t* @[[GLOB1]], i32 [[OMP_GLOBAL_THREAD_NUM13]], i8** [[INTEROP12]], i32 -1, i32 2, i8* [[TMP25]], i32 0)
// CHECK-NEXT: ret void
//

View File

@ -1,5 +1,6 @@
// RUN: %clang_cc1 -verify -fopenmp -std=c++11 -o - -DWITHDEF %s
// RUN: %clang_cc1 -verify -fopenmp -std=c++11 -o - -DWITHOUTDEF %s
// RUN: %clang_cc1 -verify -fopenmp -std=c99 -x c -o - -DCTEST %s
#ifdef WITHDEF
typedef void *omp_interop_t;
@ -55,13 +56,13 @@ void foo(int *Ap) {
#pragma omp interop destroy(SVar) destroy(Another)
int a, b;
//expected-error@+1 {{expected variable of type 'omp_interop_t'}}
//expected-error@+1 {{expected variable or static data member of type 'omp_interop_t'}}
#pragma omp interop init(target:a+b) init(target:Another)
//expected-error@+1 {{expected variable of type 'omp_interop_t'}}
//expected-error@+1 {{expected variable or static data member of type 'omp_interop_t'}}
#pragma omp interop use(a+b) use(Another)
//expected-error@+1 {{expected variable of type 'omp_interop_t'}}
//expected-error@+1 {{expected variable or static data member of type 'omp_interop_t'}}
#pragma omp interop destroy(a+b) destroy(Another)
const omp_interop_t C = (omp_interop_t)5;
@ -115,6 +116,61 @@ void foo(int *Ap) {
//expected-error@+1 {{directive '#pragma omp interop' cannot contain more than one 'nowait' clause}}
#pragma omp interop nowait init(target:InteropVar) nowait
}
struct S {
void foo();
omp_interop_t InteropVar;
omp_interop_t func();
static omp_interop_t sfunc();
};
struct T {
static void static_member_func();
};
void T::static_member_func() {
S s;
omp_interop_t o;
//expected-error@+1 {{expected variable or static data member of type 'omp_interop_t'}}
#pragma omp interop init(target:s.InteropVar) init(target:o)
}
void S::foo() {
//expected-error@+1 {{interop variable 'InteropVar' used in multiple action clauses}}
#pragma omp interop init(target:InteropVar) init(target:InteropVar)
//expected-error@+1 {{interop variable 'InteropVar' used in multiple action clauses}}
#pragma omp interop use(InteropVar) use(InteropVar)
//expected-error@+1 {{interop variable 'InteropVar' used in multiple action clauses}}
#pragma omp interop destroy(InteropVar) destroy(InteropVar)
//expected-error@+1 {{interop variable 'InteropVar' used in multiple action clauses}}
#pragma omp interop init(target:InteropVar) use(InteropVar)
//expected-error@+1 {{interop variable 'InteropVar' used in multiple action clauses}}
#pragma omp interop init(target:InteropVar) destroy(InteropVar)
//expected-error@+1 {{interop variable 'InteropVar' used in multiple action clauses}}
#pragma omp interop use(InteropVar) destroy(InteropVar)
//expected-error@+1 {{expected variable, static data member, or non-static data member of current class of type 'omp_interop_t'}}
#pragma omp interop init(target:InteropVar) init(target:func())
//expected-error@+1 {{expected variable, static data member, or non-static data member of current class of type 'omp_interop_t'}}
#pragma omp interop init(target:InteropVar) init(target:sfunc())
}
void foo2() {
S s;
omp_interop_t another;
//expected-error@+1 {{expected variable or static data member of type 'omp_interop_t'}}
#pragma omp interop init(target:s.InteropVar) init(target:another)
//expected-error@+1 {{expected variable or static data member of type 'omp_interop_t'}}
#pragma omp interop init(target: S::sfunc()) init(target:another)
}
#endif
#ifdef WITHOUTDEF
void foo() {
@ -127,3 +183,20 @@ void foo() {
#pragma omp interop destroy(InteropVar) nowait
}
#endif
#ifdef CTEST
typedef void *omp_interop_t;
omp_interop_t bar();
struct S {
omp_interop_t o;
};
void foo() {
omp_interop_t o;
struct S s;
//expected-error@+1 {{expected variable of type 'omp_interop_t'}}
#pragma omp interop init(target:o) init(target:bar())
//expected-error@+1 {{expected variable of type 'omp_interop_t'}}
#pragma omp interop init(target:o) init(target:s.o)
}
#endif