Skip to content

Commit

Permalink
[OPENMP] Allow to use global variables as lcv in loop-based directives.
Browse files Browse the repository at this point in the history
For proper codegen we need to capture variable in the OpenMP region. In loop-based directives loop control variables are private by default and they must be captured in this region. There was a problem with capturing of globals, used as lcv, as they was not marked as private by default.
Differential Revision: http://reviews.llvm.org/D9336

llvm-svn: 236201
  • Loading branch information
alexey-bataev committed Apr 30, 2015
1 parent f8a16a9 commit 9c82103
Show file tree
Hide file tree
Showing 10 changed files with 119 additions and 33 deletions.
2 changes: 0 additions & 2 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Expand Up @@ -7421,8 +7421,6 @@ def note_omp_implicit_dsa : Note<
"implicitly determined as %0">;
def err_omp_loop_var_dsa : Error<
"loop iteration variable in the associated loop of 'omp %1' directive may not be %0, predetermined as %2">;
def err_omp_global_loop_var_dsa : Error<
"loop iteration variable in the associated loop of 'omp %1' directive may not be a variable with global storage without being explicitly marked as %0">;
def err_omp_not_for : Error<
"%select{statement after '#pragma omp %1' must be a for loop|"
"expected %2 for loops after '#pragma omp %1'%select{|, but found only %4}3}0">;
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Sema/Sema.h
Expand Up @@ -7424,6 +7424,12 @@ class Sema {
/// \brief Called on end of data sharing attribute block.
void EndOpenMPDSABlock(Stmt *CurDirective);

/// \brief Check if the current region is an OpenMP loop region and if it is,
/// mark loop control variable, used in \p Init for loop initialization, as
/// private by default.
/// \param Init First part of the for loop.
void ActOnOpenMPLoopInitialization(SourceLocation ForLoc, Stmt *Init);

// OpenMP directives and clauses.
/// \brief Called on correct id-expression from the '#pragma omp
/// threadprivate'.
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/Parse/ParseStmt.cpp
Expand Up @@ -1689,6 +1689,12 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) {
FirstPart.get(),
Collection.get(),
T.getCloseLocation());
} else {
// In OpenMP loop region loop control variable must be captured and be
// private. Perform analysis of first part (if any).
if (getLangOpts().OpenMP && FirstPart.isUsable()) {
Actions.ActOnOpenMPLoopInitialization(ForLoc, FirstPart.get());
}
}

// C99 6.8.5p5 - In C99, the body of the for statement is a scope, even if
Expand Down
86 changes: 66 additions & 20 deletions clang/lib/Sema/SemaOpenMP.cpp
Expand Up @@ -82,27 +82,32 @@ class DSAStackTy {
};
typedef llvm::SmallDenseMap<VarDecl *, DSAInfo, 64> DeclSAMapTy;
typedef llvm::SmallDenseMap<VarDecl *, DeclRefExpr *, 64> AlignedMapTy;
typedef llvm::DenseSet<VarDecl *> LoopControlVariablesSetTy;

struct SharingMapTy {
DeclSAMapTy SharingMap;
AlignedMapTy AlignedMap;
LoopControlVariablesSetTy LCVSet;
DefaultDataSharingAttributes DefaultAttr;
SourceLocation DefaultAttrLoc;
OpenMPDirectiveKind Directive;
DeclarationNameInfo DirectiveName;
Scope *CurScope;
SourceLocation ConstructLoc;
bool OrderedRegion;
unsigned CollapseNumber;
SourceLocation InnerTeamsRegionLoc;
SharingMapTy(OpenMPDirectiveKind DKind, DeclarationNameInfo Name,
Scope *CurScope, SourceLocation Loc)
: SharingMap(), AlignedMap(), DefaultAttr(DSA_unspecified),
: SharingMap(), AlignedMap(), LCVSet(), DefaultAttr(DSA_unspecified),
Directive(DKind), DirectiveName(std::move(Name)), CurScope(CurScope),
ConstructLoc(Loc), OrderedRegion(false), InnerTeamsRegionLoc() {}
ConstructLoc(Loc), OrderedRegion(false), CollapseNumber(1),
InnerTeamsRegionLoc() {}
SharingMapTy()
: SharingMap(), AlignedMap(), DefaultAttr(DSA_unspecified),
: SharingMap(), AlignedMap(), LCVSet(), DefaultAttr(DSA_unspecified),
Directive(OMPD_unknown), DirectiveName(), CurScope(nullptr),
ConstructLoc(), OrderedRegion(false), InnerTeamsRegionLoc() {}
ConstructLoc(), OrderedRegion(false), CollapseNumber(1),
InnerTeamsRegionLoc() {}
};

typedef SmallVector<SharingMapTy, 64> StackTy;
Expand Down Expand Up @@ -137,6 +142,12 @@ class DSAStackTy {
/// for diagnostics.
DeclRefExpr *addUniqueAligned(VarDecl *D, DeclRefExpr *NewDE);

/// \brief Register specified variable as loop control variable.
void addLoopControlVariable(VarDecl *D);
/// \brief Check if the specified variable is a loop control variable for
/// current region.
bool isLoopControlVariable(VarDecl *D);

/// \brief Adds explicit data sharing attribute to the specified declaration.
void addDSA(VarDecl *D, DeclRefExpr *E, OpenMPClauseKind A);

Expand Down Expand Up @@ -209,6 +220,13 @@ class DSAStackTy {
return false;
}

/// \brief Set collapse value for the region.
void setCollapseNumber(unsigned Val) { Stack.back().CollapseNumber = Val; }
/// \brief Return collapse value for region.
unsigned getCollapseNumber() const {
return Stack.back().CollapseNumber;
}

/// \brief Marks current target region as one with closely nested teams
/// region.
void setParentTeamsRegionLoc(SourceLocation TeamsRegionLoc) {
Expand Down Expand Up @@ -356,6 +374,18 @@ DeclRefExpr *DSAStackTy::addUniqueAligned(VarDecl *D, DeclRefExpr *NewDE) {
return nullptr;
}

void DSAStackTy::addLoopControlVariable(VarDecl *D) {
assert(Stack.size() > 1 && "Data-sharing attributes stack is empty");
D = D->getCanonicalDecl();
Stack.back().LCVSet.insert(D);
}

bool DSAStackTy::isLoopControlVariable(VarDecl *D) {
assert(Stack.size() > 1 && "Data-sharing attributes stack is empty");
D = D->getCanonicalDecl();
return Stack.back().LCVSet.count(D) > 0;
}

void DSAStackTy::addDSA(VarDecl *D, DeclRefExpr *E, OpenMPClauseKind A) {
D = D->getCanonicalDecl();
if (A == OMPC_threadprivate) {
Expand Down Expand Up @@ -556,6 +586,8 @@ bool Sema::IsOpenMPCapturedVar(VarDecl *VD) {
assert(LangOpts.OpenMP && "OpenMP is not allowed");
VD = VD->getCanonicalDecl();
if (DSAStack->getCurrentDirective() != OMPD_unknown) {
if (DSAStack->isLoopControlVariable(VD))
return true;
auto DVarPrivate = DSAStack->getTopDSA(VD, /*FromParent=*/false);
if (DVarPrivate.CKind != OMPC_unknown && isOpenMPPrivate(DVarPrivate.CKind))
return true;
Expand Down Expand Up @@ -1956,7 +1988,7 @@ class OpenMPIterationSpaceChecker {
TestIsStrictOp(false), SubtractStep(false) {}
/// \brief Check init-expr for canonical loop form and save loop counter
/// variable - #Var and its initialization value - #LB.
bool CheckInit(Stmt *S);
bool CheckInit(Stmt *S, bool EmitDiags = true);
/// \brief Check test-expr for canonical form, save upper-bound (#UB), flags
/// for less/greater and for strict/non-strict comparison.
bool CheckCond(Expr *S);
Expand Down Expand Up @@ -2096,7 +2128,7 @@ bool OpenMPIterationSpaceChecker::SetStep(Expr *NewStep, bool Subtract) {
return false;
}

bool OpenMPIterationSpaceChecker::CheckInit(Stmt *S) {
bool OpenMPIterationSpaceChecker::CheckInit(Stmt *S, bool EmitDiags) {
// Check init-expr for canonical loop form and save loop counter
// variable - #Var and its initialization value - #LB.
// OpenMP [2.6] Canonical loop form. init-expr may be one of the following:
Expand All @@ -2106,7 +2138,9 @@ bool OpenMPIterationSpaceChecker::CheckInit(Stmt *S) {
// pointer-type var = lb
//
if (!S) {
SemaRef.Diag(DefaultLoc, diag::err_omp_loop_not_canonical_init);
if (EmitDiags) {
SemaRef.Diag(DefaultLoc, diag::err_omp_loop_not_canonical_init);
}
return true;
}
InitSrcRange = S->getSourceRange();
Expand All @@ -2122,7 +2156,7 @@ bool OpenMPIterationSpaceChecker::CheckInit(Stmt *S) {
if (auto Var = dyn_cast_or_null<VarDecl>(DS->getSingleDecl())) {
if (Var->hasInit()) {
// Accept non-canonical init form here but emit ext. warning.
if (Var->getInitStyle() != VarDecl::CInit)
if (Var->getInitStyle() != VarDecl::CInit && EmitDiags)
SemaRef.Diag(S->getLocStart(),
diag::ext_omp_loop_not_canonical_init)
<< S->getSourceRange();
Expand All @@ -2136,8 +2170,10 @@ bool OpenMPIterationSpaceChecker::CheckInit(Stmt *S) {
return SetVarAndLB(dyn_cast<VarDecl>(DRE->getDecl()), DRE,
CE->getArg(1));

SemaRef.Diag(S->getLocStart(), diag::err_omp_loop_not_canonical_init)
<< S->getSourceRange();
if (EmitDiags) {
SemaRef.Diag(S->getLocStart(), diag::err_omp_loop_not_canonical_init)
<< S->getSourceRange();
}
return true;
}

Expand Down Expand Up @@ -2398,7 +2434,8 @@ Expr *OpenMPIterationSpaceChecker::BuildPreCond(Scope *S, Expr *Cond) const {
/// \brief Build reference expression to the counter be used for codegen.
Expr *OpenMPIterationSpaceChecker::BuildCounterVar() const {
return DeclRefExpr::Create(SemaRef.Context, NestedNameSpecifierLoc(),
GetIncrementSrcRange().getBegin(), Var, false,
GetIncrementSrcRange().getBegin(), Var,
/*RefersToEnclosingVariableOrCapture=*/true,
DefaultLoc, Var->getType(), VK_LValue);
}

Expand Down Expand Up @@ -2434,6 +2471,20 @@ struct LoopIterationSpace {

} // namespace

void Sema::ActOnOpenMPLoopInitialization(SourceLocation ForLoc, Stmt *Init) {
assert(getLangOpts().OpenMP && "OpenMP is not active.");
assert(Init && "Expected loop in canonical form.");
unsigned CollapseIteration = DSAStack->getCollapseNumber();
if (CollapseIteration > 0 &&
isOpenMPLoopDirective(DSAStack->getCurrentDirective())) {
OpenMPIterationSpaceChecker ISC(*this, ForLoc);
if (!ISC.CheckInit(Init, /*EmitDiags=*/false)) {
DSAStack->addLoopControlVariable(ISC.GetLoopVar());
}
DSAStack->setCollapseNumber(CollapseIteration - 1);
}
}

/// \brief Called on a for stmt to check and extract its iteration space
/// for further processing (such as collapsing).
static bool CheckOpenMPIterationSpace(
Expand Down Expand Up @@ -2526,18 +2577,10 @@ static bool CheckOpenMPIterationSpace(
// Make the loop iteration variable private (for worksharing constructs),
// linear (for simd directives with the only one associated loop) or
// lastprivate (for simd directives with several collapsed loops).
// FIXME: the next check and error message must be removed once the
// capturing of global variables in loops is fixed.
if (DVar.CKind == OMPC_unknown)
DVar = DSA.hasDSA(Var, isOpenMPPrivate, MatchesAlways(),
/*FromParent=*/false);
if (!Var->hasLocalStorage() && DVar.CKind == OMPC_unknown) {
SemaRef.Diag(Init->getLocStart(), diag::err_omp_global_loop_var_dsa)
<< getOpenMPClauseName(PredeterminedCKind)
<< getOpenMPDirectiveName(DKind);
HasErrors = true;
} else
DSA.addDSA(Var, LoopVarRefExpr, PredeterminedCKind);
DSA.addDSA(Var, LoopVarRefExpr, PredeterminedCKind);
}

assert(isOpenMPLoopDirective(DKind) && "DSA for non-loop vars");
Expand Down Expand Up @@ -4210,6 +4253,9 @@ ExprResult Sema::VerifyPositiveIntegerConstantInClause(Expr *E,
<< E->getSourceRange();
return ExprError();
}
if (CKind == OMPC_collapse) {
DSAStack->setCollapseNumber(Result.getExtValue());
}
return ICE;
}

Expand Down
42 changes: 41 additions & 1 deletion clang/test/OpenMP/for_codegen.cpp
Expand Up @@ -8,7 +8,11 @@
#define HEADER

// CHECK: [[IDENT_T_TY:%.+]] = type { i32, i32, i32, i32, i8* }
// CHECK: [[IMPLICIT_BARRIER_LOC:@.+]] = private unnamed_addr constant %{{.+}} { i32 0, i32 66, i32 0, i32 0, i8*
// CHECK-DAG: [[IMPLICIT_BARRIER_LOC:@.+]] = private unnamed_addr constant %{{.+}} { i32 0, i32 66, i32 0, i32 0, i8*
// CHECK-DAG: [[I:@.+]] = global i8 1,
// CHECK-DAG: [[J:@.+]] = global i8 2,
// CHECK-DAG: [[K:@.+]] = global i8 3,

// CHECK-LABEL: define {{.*void}} @{{.*}}without_schedule_clause{{.*}}(float* {{.+}}, float* {{.+}}, float* {{.+}}, float* {{.+}})
void without_schedule_clause(float *a, float *b, float *c, float *d) {
// CHECK: [[GTID:%.+]] = call i32 @__kmpc_global_thread_num([[IDENT_T_TY]]* [[DEFAULT_LOC:[@%].+]])
Expand Down Expand Up @@ -365,5 +369,41 @@ void parallel_for(float *a) {
// TERM_DEBUG-DAG: [[DBG_LOC_END]] = !DILocation(line: [[@LINE-16]],
// TERM_DEBUG-DAG: [[DBG_LOC_CANCEL]] = !DILocation(line: [[@LINE-17]],

char i = 1, j = 2, k = 3;
// CHECK-LABEL: for_with_global_lcv
void for_with_global_lcv() {
// CHECK: [[I_ADDR:%.+]] = alloca i8,
// CHECK: [[J_ADDR:%.+]] = alloca i8,

// CHECK: call void @__kmpc_for_static_init_4(
// CHECK-NOT: [[I]]
// CHECK: store i8 %{{.+}}, i8* [[I_ADDR]]
// CHECK-NOT: [[I]]
// CHECK: [[I_VAL:%.+]] = load i8, i8* [[I_ADDR]],
// CHECK-NOT: [[I]]
// CHECK: store i8 [[I_VAL]], i8* [[K]]
// CHECK-NOT: [[I]]
// CHECK: call void @__kmpc_for_static_fini(
#pragma omp for
for (i = 0; i < 2; ++i) {
k = i;
}
// CHECK: call void @__kmpc_for_static_init_4(
// CHECK-NOT: [[J]]
// CHECK: store i8 %{{.+}}, i8* [[J_ADDR]]
// CHECK-NOT: [[J]]
// CHECK: [[J_VAL:%.+]] = load i8, i8* [[J_ADDR]],
// CHECK-NOT: [[J]]
// CHECK: store i8 [[J_VAL]], i8* [[K]]
// CHECK-NOT: [[J]]
// CHECK: call void @__kmpc_for_static_fini(
#pragma omp for collapse(2)
for (int i = 0; i < 2; ++i)
for (j = 0; j < 2; ++j) {
k = i;
k = j;
}
}

#endif // HEADER

2 changes: 0 additions & 2 deletions clang/test/OpenMP/for_loop_messages.cpp
Expand Up @@ -313,15 +313,13 @@ int test_iteration_spaces() {

#pragma omp parallel
{
// expected-error@+2 {{loop iteration variable in the associated loop of 'omp for' directive may not be a variable with global storage without being explicitly marked as private}}
#pragma omp for
for (globalii = 0; globalii < 10; globalii += 1)
c[globalii] = a[globalii];
}

#pragma omp parallel
{
// expected-error@+3 {{loop iteration variable in the associated loop of 'omp for' directive may not be a variable with global storage without being explicitly marked as private}}
#pragma omp for collapse(2)
for (ii = 0; ii < 10; ii += 1)
for (globalii = 0; globalii < 10; globalii += 1)
Expand Down
2 changes: 0 additions & 2 deletions clang/test/OpenMP/for_simd_loop_messages.cpp
Expand Up @@ -314,15 +314,13 @@ int test_iteration_spaces() {

#pragma omp parallel
{
// expected-error@+2 {{loop iteration variable in the associated loop of 'omp for simd' directive may not be a variable with global storage without being explicitly marked as linear}}
#pragma omp for simd
for (globalii = 0; globalii < 10; globalii += 1)
c[globalii] = a[globalii];
}

#pragma omp parallel
{
// expected-error@+3 {{loop iteration variable in the associated loop of 'omp for simd' directive may not be a variable with global storage without being explicitly marked as lastprivate}}
#pragma omp for simd collapse(2)
for (ii = 0; ii < 10; ii += 1)
for (globalii = 0; globalii < 10; globalii += 1)
Expand Down
2 changes: 0 additions & 2 deletions clang/test/OpenMP/parallel_for_loop_messages.cpp
Expand Up @@ -265,14 +265,12 @@ int test_iteration_spaces() {
}

{
// expected-error@+2 {{loop iteration variable in the associated loop of 'omp parallel for' directive may not be a variable with global storage without being explicitly marked as private}}
#pragma omp parallel for
for (globalii = 0; globalii < 10; globalii += 1)
c[globalii] = a[globalii];
}

{
// expected-error@+3 {{loop iteration variable in the associated loop of 'omp parallel for' directive may not be a variable with global storage without being explicitly marked as private}}
#pragma omp parallel for collapse(2)
for (ii = 0; ii < 10; ii += 1)
for (globalii = 0; globalii < 10; globalii += 1)
Expand Down
2 changes: 0 additions & 2 deletions clang/test/OpenMP/parallel_for_simd_loop_messages.cpp
Expand Up @@ -266,14 +266,12 @@ int test_iteration_spaces() {
}

{
// expected-error@+2 {{loop iteration variable in the associated loop of 'omp parallel for simd' directive may not be a variable with global storage without being explicitly marked as linear}}
#pragma omp parallel for simd
for (globalii = 0; globalii < 10; globalii += 1)
c[globalii] = a[globalii];
}

{
// expected-error@+3 {{loop iteration variable in the associated loop of 'omp parallel for simd' directive may not be a variable with global storage without being explicitly marked as lastprivate}}
#pragma omp parallel for simd collapse(2)
for (ii = 0; ii < 10; ii += 1)
for (globalii = 0; globalii < 10; globalii += 1)
Expand Down
2 changes: 0 additions & 2 deletions clang/test/OpenMP/simd_loop_messages.cpp
Expand Up @@ -260,15 +260,13 @@ int test_iteration_spaces() {

#pragma omp parallel
{
// expected-error@+2 {{loop iteration variable in the associated loop of 'omp simd' directive may not be a variable with global storage without being explicitly marked as linear}}
#pragma omp simd
for (globalii = 0; globalii < 10; globalii+=1)
c[globalii] = a[globalii];
}

#pragma omp parallel
{
// expected-error@+3 {{loop iteration variable in the associated loop of 'omp simd' directive may not be a variable with global storage without being explicitly marked as lastprivate}}
#pragma omp simd collapse(2)
for (ii = 0; ii < 10; ii += 1)
for (globalii = 0; globalii < 10; globalii += 1)
Expand Down

0 comments on commit 9c82103

Please sign in to comment.