From: Alexey Bataev Date: Tue, 16 Dec 2014 07:00:22 +0000 (+0000) Subject: [OPENMP] Bugfix for processing of global variables in OpenMP regions. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=7c818bad8a72a60b7c86c90e9eca6bfa4aef2ce7;p=clang [OPENMP] Bugfix for processing of global variables in OpenMP regions. Currently, if global variable is marked as a private OpenMP variable, the compiler crashes in debug version or generates incorrect code in release version. It happens because in the OpenMP region the original global variable is used instead of the generated private copy. It happens because currently globals variables are not captured in the OpenMP region. This patch adds capturing of global variables iff private copy of the global variable must be used in the OpenMP region. Differential Revision: http://reviews.llvm.org/D6259 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@224323 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 1cfc45bcf6..10d8b03fcc 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -3448,6 +3448,9 @@ public: TryCaptureKind Kind = TryCapture_Implicit, SourceLocation EllipsisLoc = SourceLocation()); + /// \brief Checks if the variable must be captured. + bool NeedToCaptureVariable(VarDecl *Var, SourceLocation Loc); + /// \brief Given a variable, determine the type that a reference to that /// variable will have in the given scope. QualType getCapturedDeclRefType(VarDecl *Var, SourceLocation Loc); @@ -7482,6 +7485,10 @@ private: void DestroyDataSharingAttributesStack(); ExprResult VerifyPositiveIntegerConstantInClause(Expr *Op, OpenMPClauseKind CKind); + /// \brief Checks if the specified variable is used in one of the private + /// clauses in OpenMP constructs. + bool IsOpenMPCapturedVar(VarDecl *VD); + public: ExprResult PerformOpenMPImplicitIntegerConversion(SourceLocation OpLoc, Expr *Op); diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp index 782273f8ca..0d01b07334 100644 --- a/lib/CodeGen/CGExpr.cpp +++ b/lib/CodeGen/CGExpr.cpp @@ -1906,6 +1906,21 @@ LValue CodeGenFunction::EmitDeclRefLValue(const DeclRefExpr *E) { QualType T = E->getType(); if (const auto *VD = dyn_cast(ND)) { + // Check for captured variables. + if (E->refersToEnclosingLocal()) { + if (auto *FD = LambdaCaptureFields.lookup(VD)) + return EmitCapturedFieldLValue(*this, FD, CXXABIThisValue); + else if (CapturedStmtInfo) { + if (auto *V = LocalDeclMap.lookup(VD)) + return MakeAddrLValue(V, T, Alignment); + else + return EmitCapturedFieldLValue(*this, CapturedStmtInfo->lookup(VD), + CapturedStmtInfo->getContextValue()); + } else + return MakeAddrLValue(GetAddrOfBlockDecl(VD, VD->hasAttr()), + T, Alignment); + } + // Global Named registers access via intrinsics only if (VD->getStorageClass() == SC_Register && VD->hasAttr() && !VD->isLocalVarDecl()) @@ -1956,21 +1971,6 @@ LValue CodeGenFunction::EmitDeclRefLValue(const DeclRefExpr *E) { *this, VD, T, V, getTypes().ConvertTypeForMem(VD->getType()), Alignment, E->getExprLoc()); - // Use special handling for lambdas. - if (!V) { - if (FieldDecl *FD = LambdaCaptureFields.lookup(VD)) { - return EmitCapturedFieldLValue(*this, FD, CXXABIThisValue); - } else if (CapturedStmtInfo) { - if (const FieldDecl *FD = CapturedStmtInfo->lookup(VD)) - return EmitCapturedFieldLValue(*this, FD, - CapturedStmtInfo->getContextValue()); - } - - assert(isa(CurCodeDecl) && E->refersToEnclosingLocal()); - return MakeAddrLValue(GetAddrOfBlockDecl(VD, isBlockVariable), - T, Alignment); - } - assert(V && "DeclRefExpr not entered in LocalDeclMap?"); if (isBlockVariable) diff --git a/lib/CodeGen/CGStmtOpenMP.cpp b/lib/CodeGen/CGStmtOpenMP.cpp index fd4310cc63..73bb43654b 100644 --- a/lib/CodeGen/CGStmtOpenMP.cpp +++ b/lib/CodeGen/CGStmtOpenMP.cpp @@ -20,6 +20,35 @@ using namespace clang; using namespace CodeGen; +namespace { +/// \brief RAII for emitting code of CapturedStmt without function outlining. +class InlinedOpenMPRegion { + CodeGenFunction &CGF; + CodeGenFunction::CGCapturedStmtInfo *PrevCapturedStmtInfo; + const Decl *StoredCurCodeDecl; + + /// \brief A class to emit CapturedStmt construct as inlined statement without + /// generating a function for outlined code. + class CGInlinedOpenMPRegionInfo : public CodeGenFunction::CGCapturedStmtInfo { + public: + CGInlinedOpenMPRegionInfo() : CGCapturedStmtInfo() {} + }; + +public: + InlinedOpenMPRegion(CodeGenFunction &CGF, const Stmt *S) + : CGF(CGF), PrevCapturedStmtInfo(CGF.CapturedStmtInfo), + StoredCurCodeDecl(CGF.CurCodeDecl) { + CGF.CurCodeDecl = cast(S)->getCapturedDecl(); + CGF.CapturedStmtInfo = new CGInlinedOpenMPRegionInfo(); + } + ~InlinedOpenMPRegion() { + delete CGF.CapturedStmtInfo; + CGF.CapturedStmtInfo = PrevCapturedStmtInfo; + CGF.CurCodeDecl = StoredCurCodeDecl; + } +}; +} // namespace + //===----------------------------------------------------------------------===// // OpenMP Directive Emission //===----------------------------------------------------------------------===// @@ -417,6 +446,7 @@ void CodeGenFunction::EmitOMPSimdDirective(const OMPSimdDirective &S) { } } + InlinedOpenMPRegion Region(*this, S.getAssociatedStmt()); RunCleanupsScope DirectiveScope(*this); CGDebugInfo *DI = getDebugInfo(); @@ -561,6 +591,7 @@ void CodeGenFunction::EmitOMPWorksharingLoop(const OMPLoopDirective &S) { } void CodeGenFunction::EmitOMPForDirective(const OMPForDirective &S) { + InlinedOpenMPRegion Region(*this, S.getAssociatedStmt()); RunCleanupsScope DirectiveScope(*this); CGDebugInfo *DI = getDebugInfo(); @@ -593,8 +624,8 @@ void CodeGenFunction::EmitOMPSingleDirective(const OMPSingleDirective &) { } void CodeGenFunction::EmitOMPMasterDirective(const OMPMasterDirective &S) { - CGM.getOpenMPRuntime().EmitOMPMasterRegion( - *this, [&]() -> void { + CGM.getOpenMPRuntime().EmitOMPMasterRegion(*this, [&]() -> void { + InlinedOpenMPRegion Region(*this, S.getAssociatedStmt()); RunCleanupsScope Scope(*this); EmitStmt(cast(S.getAssociatedStmt())->getCapturedStmt()); EnsureInsertPoint(); @@ -604,8 +635,10 @@ void CodeGenFunction::EmitOMPMasterDirective(const OMPMasterDirective &S) { void CodeGenFunction::EmitOMPCriticalDirective(const OMPCriticalDirective &S) { CGM.getOpenMPRuntime().EmitOMPCriticalRegion( *this, S.getDirectiveName().getAsString(), [&]() -> void { + InlinedOpenMPRegion Region(*this, S.getAssociatedStmt()); RunCleanupsScope Scope(*this); - EmitStmt(cast(S.getAssociatedStmt())->getCapturedStmt()); + EmitStmt( + cast(S.getAssociatedStmt())->getCapturedStmt()); EnsureInsertPoint(); }, S.getLocStart()); } diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index 8a8827be10..2e1e400041 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -182,6 +182,8 @@ public: /// \brief API for captured statement code generation. class CGCapturedStmtInfo { public: + explicit CGCapturedStmtInfo(CapturedRegionKind K = CR_Default) + : Kind(K), ThisValue(nullptr), CXXThisFieldDecl(nullptr) {} explicit CGCapturedStmtInfo(const CapturedStmt &S, CapturedRegionKind K = CR_Default) : Kind(K), ThisValue(nullptr), CXXThisFieldDecl(nullptr) { @@ -614,7 +616,6 @@ public: addPrivate(const VarDecl *LocalVD, const std::function &PrivateGen) { assert(PerformCleanup && "adding private to dead scope"); - assert(LocalVD->isLocalVarDecl() && "privatizing non-local variable"); if (SavedLocals.count(LocalVD) > 0) return false; SavedLocals[LocalVD] = CGF.LocalDeclMap.lookup(LocalVD); CGF.LocalDeclMap.erase(LocalVD); diff --git a/lib/Frontend/Rewrite/RewriteModernObjC.cpp b/lib/Frontend/Rewrite/RewriteModernObjC.cpp index ffac51ed5f..0fec2bdbc2 100644 --- a/lib/Frontend/Rewrite/RewriteModernObjC.cpp +++ b/lib/Frontend/Rewrite/RewriteModernObjC.cpp @@ -4563,16 +4563,12 @@ void RewriteModernObjC::GetBlockDeclRefExprs(Stmt *S) { GetBlockDeclRefExprs(*CI); } // Handle specific things. - if (DeclRefExpr *DRE = dyn_cast(S)) { - if (DRE->refersToEnclosingLocal()) { + if (DeclRefExpr *DRE = dyn_cast(S)) + if (DRE->refersToEnclosingLocal() || + HasLocalVariableExternalStorage(DRE->getDecl())) // FIXME: Handle enums. - if (!isa(DRE->getDecl())) - BlockDeclRefs.push_back(DRE); - if (HasLocalVariableExternalStorage(DRE->getDecl())) - BlockDeclRefs.push_back(DRE); - } - } - + BlockDeclRefs.push_back(DRE); + return; } @@ -4595,11 +4591,11 @@ void RewriteModernObjC::GetInnerBlockDeclRefExprs(Stmt *S, } // Handle specific things. if (DeclRefExpr *DRE = dyn_cast(S)) { - if (DRE->refersToEnclosingLocal()) { - if (!isa(DRE->getDecl()) && - !InnerContexts.count(DRE->getDecl()->getDeclContext())) + if (DRE->refersToEnclosingLocal() || + HasLocalVariableExternalStorage(DRE->getDecl())) { + if (!InnerContexts.count(DRE->getDecl()->getDeclContext())) InnerBlockDeclRefs.push_back(DRE); - if (VarDecl *Var = dyn_cast(DRE->getDecl())) + if (VarDecl *Var = cast(DRE->getDecl())) if (Var->isFunctionOrMethodVarDecl()) ImportedLocalExternalDecls.insert(Var); } @@ -4776,7 +4772,8 @@ Stmt *RewriteModernObjC::RewriteBlockDeclRefExpr(DeclRefExpr *DeclRefExp) { // Rewrite the byref variable into BYREFVAR->__forwarding->BYREFVAR // for each DeclRefExp where BYREFVAR is name of the variable. ValueDecl *VD = DeclRefExp->getDecl(); - bool isArrow = DeclRefExp->refersToEnclosingLocal(); + bool isArrow = DeclRefExp->refersToEnclosingLocal() || + HasLocalVariableExternalStorage(DeclRefExp->getDecl()); FieldDecl *FD = FieldDecl::Create(*Context, nullptr, SourceLocation(), SourceLocation(), diff --git a/lib/Frontend/Rewrite/RewriteObjC.cpp b/lib/Frontend/Rewrite/RewriteObjC.cpp index 5fb237449a..5681201131 100644 --- a/lib/Frontend/Rewrite/RewriteObjC.cpp +++ b/lib/Frontend/Rewrite/RewriteObjC.cpp @@ -3671,16 +3671,12 @@ void RewriteObjC::GetBlockDeclRefExprs(Stmt *S) { GetBlockDeclRefExprs(*CI); } // Handle specific things. - if (DeclRefExpr *DRE = dyn_cast(S)) { - if (DRE->refersToEnclosingLocal()) { + if (DeclRefExpr *DRE = dyn_cast(S)) + if (DRE->refersToEnclosingLocal() || + HasLocalVariableExternalStorage(DRE->getDecl())) // FIXME: Handle enums. - if (!isa(DRE->getDecl())) - BlockDeclRefs.push_back(DRE); - if (HasLocalVariableExternalStorage(DRE->getDecl())) - BlockDeclRefs.push_back(DRE); - } - } - + BlockDeclRefs.push_back(DRE); + return; } @@ -3703,11 +3699,11 @@ void RewriteObjC::GetInnerBlockDeclRefExprs(Stmt *S, } // Handle specific things. if (DeclRefExpr *DRE = dyn_cast(S)) { - if (DRE->refersToEnclosingLocal()) { - if (!isa(DRE->getDecl()) && - !InnerContexts.count(DRE->getDecl()->getDeclContext())) + if (DRE->refersToEnclosingLocal() || + HasLocalVariableExternalStorage(DRE->getDecl())) { + if (!InnerContexts.count(DRE->getDecl()->getDeclContext())) InnerBlockDeclRefs.push_back(DRE); - if (VarDecl *Var = dyn_cast(DRE->getDecl())) + if (VarDecl *Var = cast(DRE->getDecl())) if (Var->isFunctionOrMethodVarDecl()) ImportedLocalExternalDecls.insert(Var); } @@ -3865,7 +3861,8 @@ Stmt *RewriteObjC::RewriteBlockDeclRefExpr(DeclRefExpr *DeclRefExp) { // Rewrite the byref variable into BYREFVAR->__forwarding->BYREFVAR // for each DeclRefExp where BYREFVAR is name of the variable. ValueDecl *VD = DeclRefExp->getDecl(); - bool isArrow = DeclRefExp->refersToEnclosingLocal(); + bool isArrow = DeclRefExp->refersToEnclosingLocal() || + HasLocalVariableExternalStorage(DeclRefExp->getDecl()); FieldDecl *FD = FieldDecl::Create(*Context, nullptr, SourceLocation(), SourceLocation(), diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index ad4fea84d3..f7756ba606 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -1602,10 +1602,8 @@ Sema::BuildDeclRefExpr(ValueDecl *D, QualType Ty, ExprValueKind VK, } bool refersToEnclosingScope = - (CurContext != D->getDeclContext() && - D->getDeclContext()->isFunctionOrMethod()) || - (isa(D) && - cast(D)->isInitCapture()); + isa(D) && + NeedToCaptureVariable(cast(D), NameInfo.getLoc()); DeclRefExpr *E; if (isa(D)) { @@ -11799,7 +11797,7 @@ static DeclContext *getParentOfCapturingContextOrNull(DeclContext *DC, VarDecl * const bool Diagnose, Sema &S) { if (isa(DC) || isa(DC) || isLambdaCallOperator(DC)) return getLambdaAwareParentOfDeclContext(DC); - else { + else if (Var->hasLocalStorage()) { if (Diagnose) diagnoseUncapturableValueReference(S, Loc, Var, DC); } @@ -12241,7 +12239,7 @@ bool Sema::tryCaptureVariable(VarDecl *Var, SourceLocation ExprLoc, QualType &CaptureType, QualType &DeclRefType, const unsigned *const FunctionScopeIndexToStopAt) { - bool Nested = false; + bool Nested = Var->isInitCapture(); DeclContext *DC = CurContext; const unsigned MaxFunctionScopesIndex = FunctionScopeIndexToStopAt @@ -12259,8 +12257,13 @@ bool Sema::tryCaptureVariable(VarDecl *Var, SourceLocation ExprLoc, // If the variable is declared in the current context (and is not an // init-capture), there is no need to capture it. - if (!Var->isInitCapture() && Var->getDeclContext() == DC) return true; - if (!Var->hasLocalStorage()) return true; + if (!Nested && Var->getDeclContext() == DC) return true; + + // Capture global variables if it is required to use private copy of this + // variable. + bool IsGlobal = !Var->hasLocalStorage(); + if (IsGlobal && !(LangOpts.OpenMP && IsOpenMPCapturedVar(Var))) + return true; // Walk up the stack to determine whether we can capture the variable, // performing the "simple" checks that don't depend on type. We stop when @@ -12281,8 +12284,17 @@ bool Sema::tryCaptureVariable(VarDecl *Var, SourceLocation ExprLoc, ExprLoc, BuildAndDiagnose, *this); - if (!ParentDC) return true; - + // We need to check for the parent *first* because, if we *have* + // private-captured a global variable, we need to recursively capture it in + // intermediate blocks, lambdas, etc. + if (!ParentDC) { + if (IsGlobal) { + FunctionScopesIndex = MaxFunctionScopesIndex - 1; + break; + } + return true; + } + FunctionScopeInfo *FSI = FunctionScopes[FunctionScopesIndex]; CapturingScopeInfo *CSI = cast(FSI); @@ -12508,6 +12520,14 @@ bool Sema::tryCaptureVariable(VarDecl *Var, SourceLocation Loc, DeclRefType, nullptr); } +bool Sema::NeedToCaptureVariable(VarDecl *Var, SourceLocation Loc) { + QualType CaptureType; + QualType DeclRefType; + return !tryCaptureVariable(Var, Loc, TryCapture_Implicit, SourceLocation(), + /*BuildAndDiagnose=*/false, CaptureType, + DeclRefType, nullptr); +} + QualType Sema::getCapturedDeclRefType(VarDecl *Var, SourceLocation Loc) { QualType CaptureType; QualType DeclRefType; diff --git a/lib/Sema/SemaOpenMP.cpp b/lib/Sema/SemaOpenMP.cpp index 995054321d..a1064d68b0 100644 --- a/lib/Sema/SemaOpenMP.cpp +++ b/lib/Sema/SemaOpenMP.cpp @@ -551,6 +551,19 @@ void Sema::InitDataSharingAttributesStack() { #define DSAStack static_cast(VarDataSharingAttributesStack) +bool Sema::IsOpenMPCapturedVar(VarDecl *VD) { + assert(LangOpts.OpenMP && "OpenMP is not allowed"); + if (DSAStack->getCurrentDirective() != OMPD_unknown) { + auto DVarPrivate = DSAStack->getTopDSA(VD, /*FromParent=*/false); + if (DVarPrivate.CKind != OMPC_unknown && isOpenMPPrivate(DVarPrivate.CKind)) + return true; + DVarPrivate = DSAStack->hasDSA(VD, isOpenMPPrivate, MatchesAlways(), + /*FromParent=*/false); + return DVarPrivate.CKind != OMPC_unknown; + } + return false; +} + void Sema::DestroyDataSharingAttributesStack() { delete DSAStack; } void Sema::StartOpenMPDSABlock(OpenMPDirectiveKind DKind, @@ -4378,7 +4391,7 @@ OMPClause *Sema::ActOnOpenMPFirstprivateClause(ArrayRef VarList, VDInitRefExpr = DeclRefExpr::Create( Context, /*QualifierLoc*/ NestedNameSpecifierLoc(), /*TemplateKWLoc*/ SourceLocation(), VDInit, - /*isEnclosingLocal*/ false, ELoc, Type, + /*isEnclosingLocal*/ true, ELoc, Type, /*VK*/ VK_LValue); VDInit->setIsUsed(); auto Init = DefaultLvalueConversion(VDInitRefExpr).get(); @@ -4392,8 +4405,14 @@ OMPClause *Sema::ActOnOpenMPFirstprivateClause(ArrayRef VarList, else VDPrivate->setInit(Result.getAs()); } else { - AddInitializerToDecl(VDPrivate, DefaultLvalueConversion(DE).get(), - /*DirectInit*/ false, /*TypeMayContainAuto*/ false); + AddInitializerToDecl( + VDPrivate, DefaultLvalueConversion( + DeclRefExpr::Create(Context, NestedNameSpecifierLoc(), + SourceLocation(), DE->getDecl(), + /*isEnclosingLocal=*/true, + DE->getExprLoc(), DE->getType(), + /*VK=*/VK_LValue)).get(), + /*DirectInit=*/false, /*TypeMayContainAuto=*/false); } if (VDPrivate->isInvalidDecl()) { if (IsImplicitClause) { diff --git a/test/OpenMP/parallel_firstprivate_codegen.cpp b/test/OpenMP/parallel_firstprivate_codegen.cpp index ecaa3c4f35..811f2df9df 100644 --- a/test/OpenMP/parallel_firstprivate_codegen.cpp +++ b/test/OpenMP/parallel_firstprivate_codegen.cpp @@ -1,6 +1,8 @@ // RUN: %clang_cc1 -verify -fopenmp=libiomp5 -x c++ -triple %itanium_abi_triple -emit-llvm %s -o - | FileCheck %s // RUN: %clang_cc1 -fopenmp=libiomp5 -x c++ -std=c++11 -triple %itanium_abi_triple -emit-pch -o %t %s // RUN: %clang_cc1 -fopenmp=libiomp5 -x c++ -triple %itanium_abi_triple -std=c++11 -include-pch %t -verify %s -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 -verify -fopenmp=libiomp5 -x c++ -std=c++11 -DLAMBDA -triple %itanium_abi_triple -emit-llvm %s -o - | FileCheck -check-prefix=LAMBDA %s +// RUN: %clang_cc1 -verify -fopenmp=libiomp5 -x c++ -fblocks -DBLOCKS -triple %itanium_abi_triple -emit-llvm %s -o - | FileCheck -check-prefix=BLOCKS %s // expected-no-diagnostics #ifndef HEADER #define HEADER @@ -12,7 +14,7 @@ struct St { ~St() {} }; -volatile int g; +volatile int g = 1212; template struct S { @@ -47,6 +49,83 @@ T tmain() { } int main() { +#ifdef LAMBDA + // LAMBDA: [[G:@.+]] = global i{{[0-9]+}} 1212, + // LAMBDA-LABEL: @main + // LAMBDA: call void [[OUTER_LAMBDA:@.+]]( + [&]() { + // LAMBDA: define{{.*}} internal{{.*}} void [[OUTER_LAMBDA]]( + // LAMBDA: [[G_LOCAL_REF:%.+]] = getelementptr inbounds %{{.+}}* [[AGG_CAPTURED:%.+]], i{{[0-9]+}} 0, i{{[0-9]+}} 0 + // LAMBDA: store i{{[0-9]+}}* [[G]], i{{[0-9]+}}** [[G_LOCAL_REF]] + // LAMBDA: [[ARG:%.+]] = bitcast %{{.+}}* [[AGG_CAPTURED]] to i8* + // LAMBDA: call void {{.+}}* @__kmpc_fork_call({{.+}}, i32 1, {{.+}}* [[OMP_REGION:@.+]] to {{.+}}, i8* [[ARG]]) +#pragma omp parallel firstprivate(g) + { + // LAMBDA: define{{.*}} internal{{.*}} void [[OMP_REGION]](i32* %{{.+}}, i32* %{{.+}}, %{{.+}}* [[ARG:%.+]]) + // LAMBDA: [[G_PRIVATE_ADDR:%.+]] = alloca i{{[0-9]+}}, + // LAMBDA: store %{{.+}}* [[ARG]], %{{.+}}** [[ARG_REF:%.+]], + // LAMBDA: [[ARG:%.+]] = load %{{.+}}** [[ARG_REF]] + // LAMBDA: [[G_REF_ADDR:%.+]] = getelementptr inbounds %{{.+}}* [[ARG]], i{{[0-9]+}} 0, i{{[0-9]+}} 0 + // LAMBDA: [[G_REF:%.+]] = load i{{[0-9]+}}** [[G_REF_ADDR]] + // LAMBDA: [[G_VAL:%.+]] = load volatile i{{[0-9]+}}* [[G_REF]] + // LAMBDA: store volatile i{{[0-9]+}} [[G_VAL]], i{{[0-9]+}}* [[G_PRIVATE_ADDR]] + // LAMBDA: call i32 @__kmpc_cancel_barrier( + g = 1; + // LAMBDA: store volatile i{{[0-9]+}} 1, i{{[0-9]+}}* [[G_PRIVATE_ADDR]], + // LAMBDA: [[G_PRIVATE_ADDR_REF:%.+]] = getelementptr inbounds %{{.+}}* [[ARG:%.+]], i{{[0-9]+}} 0, i{{[0-9]+}} 0 + // LAMBDA: store i{{[0-9]+}}* [[G_PRIVATE_ADDR]], i{{[0-9]+}}** [[G_PRIVATE_ADDR_REF]] + // LAMBDA: call void [[INNER_LAMBDA:@.+]](%{{.+}}* [[ARG]]) + [&]() { + // LAMBDA: define {{.+}} void [[INNER_LAMBDA]](%{{.+}}* [[ARG_PTR:%.+]]) + // LAMBDA: store %{{.+}}* [[ARG_PTR]], %{{.+}}** [[ARG_PTR_REF:%.+]], + g = 2; + // LAMBDA: [[ARG_PTR:%.+]] = load %{{.+}}** [[ARG_PTR_REF]] + // LAMBDA: [[G_PTR_REF:%.+]] = getelementptr inbounds %{{.+}}* [[ARG_PTR]], i{{[0-9]+}} 0, i{{[0-9]+}} 0 + // LAMBDA: [[G_REF:%.+]] = load i{{[0-9]+}}** [[G_PTR_REF]] + // LAMBDA: store volatile i{{[0-9]+}} 2, i{{[0-9]+}}* [[G_REF]] + }(); + } + }(); + return 0; +#elif defined(BLOCKS) + // BLOCKS: [[G:@.+]] = global i{{[0-9]+}} 1212, + // BLOCKS-LABEL: @main + // BLOCKS: call void {{%.+}}(i8* + ^{ + // BLOCKS: define{{.*}} internal{{.*}} void {{.+}}(i8* + // BLOCKS: [[G_LOCAL_REF:%.+]] = getelementptr inbounds %{{.+}}* [[AGG_CAPTURED:%.+]], i{{[0-9]+}} 0, i{{[0-9]+}} 0 + // BLOCKS: store i{{[0-9]+}}* [[G]], i{{[0-9]+}}** [[G_LOCAL_REF]] + // BLOCKS: [[ARG:%.+]] = bitcast %{{.+}}* [[AGG_CAPTURED]] to i8* + // BLOCKS: call void {{.+}}* @__kmpc_fork_call({{.+}}, i32 1, {{.+}}* [[OMP_REGION:@.+]] to {{.+}}, i8* [[ARG]]) +#pragma omp parallel firstprivate(g) + { + // BLOCKS: define{{.*}} internal{{.*}} void [[OMP_REGION]](i32* %{{.+}}, i32* %{{.+}}, %{{.+}}* [[ARG:%.+]]) + // BLOCKS: [[G_PRIVATE_ADDR:%.+]] = alloca i{{[0-9]+}}, + // BLOCKS: store %{{.+}}* [[ARG]], %{{.+}}** [[ARG_REF:%.+]], + // BLOCKS: [[ARG:%.+]] = load %{{.+}}** [[ARG_REF]] + // BLOCKS: [[G_REF_ADDR:%.+]] = getelementptr inbounds %{{.+}}* [[ARG]], i{{[0-9]+}} 0, i{{[0-9]+}} 0 + // BLOCKS: [[G_REF:%.+]] = load i{{[0-9]+}}** [[G_REF_ADDR]] + // BLOCKS: [[G_VAL:%.+]] = load volatile i{{[0-9]+}}* [[G_REF]] + // BLOCKS: store volatile i{{[0-9]+}} [[G_VAL]], i{{[0-9]+}}* [[G_PRIVATE_ADDR]] + // BLOCKS: call i32 @__kmpc_cancel_barrier( + g = 1; + // BLOCKS: store volatile i{{[0-9]+}} 1, i{{[0-9]+}}* [[G_PRIVATE_ADDR]], + // BLOCKS-NOT: [[G]]{{[[^:word:]]}} + // BLOCKS: i{{[0-9]+}}* [[G_PRIVATE_ADDR]] + // BLOCKS-NOT: [[G]]{{[[^:word:]]}} + // BLOCKS: call void {{%.+}}(i8* + ^{ + // BLOCKS: define {{.+}} void {{@.+}}(i8* + g = 2; + // BLOCKS-NOT: [[G]]{{[[^:word:]]}} + // BLOCKS: store volatile i{{[0-9]+}} 2, i{{[0-9]+}}* + // BLOCKS-NOT: [[G]]{{[[^:word:]]}} + // BLOCKS: ret + }(); + } + }(); + return 0; +#else S test; int t_var = 0; int vec[] = {1, 2}; @@ -58,6 +137,7 @@ int main() { s_arr[0] = var; } return tmain(); +#endif } // CHECK: define {{.*}}i{{[0-9]+}} @main() diff --git a/test/OpenMP/parallel_private_codegen.cpp b/test/OpenMP/parallel_private_codegen.cpp index 8298eee1f4..6911068250 100644 --- a/test/OpenMP/parallel_private_codegen.cpp +++ b/test/OpenMP/parallel_private_codegen.cpp @@ -1,6 +1,8 @@ // RUN: %clang_cc1 -verify -fopenmp=libiomp5 -x c++ -triple x86_64-unknown-unknown -emit-llvm %s -o - | FileCheck %s // RUN: %clang_cc1 -fopenmp=libiomp5 -x c++ -std=c++11 -triple x86_64-unknown-unknown -emit-pch -o %t %s // RUN: %clang_cc1 -fopenmp=libiomp5 -x c++ -triple x86_64-unknown-unknown -std=c++11 -include-pch %t -verify %s -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 -verify -fopenmp=libiomp5 -x c++ -std=c++11 -DLAMBDA -triple %itanium_abi_triple -emit-llvm %s -o - | FileCheck -check-prefix=LAMBDA %s +// RUN: %clang_cc1 -verify -fopenmp=libiomp5 -x c++ -fblocks -DBLOCKS -triple %itanium_abi_triple -emit-llvm %s -o - | FileCheck -check-prefix=BLOCKS %s // expected-no-diagnostics #ifndef HEADER #define HEADER @@ -14,6 +16,8 @@ struct S { ~S() {} }; +volatile int g = 1212; + // CHECK: [[S_FLOAT_TY:%.+]] = type { float } // CHECK: [[CAP_MAIN_TY:%.+]] = type { [2 x i{{[0-9]+}}]*, i{{[0-9]+}}*, [2 x [[S_FLOAT_TY]]]*, [[S_FLOAT_TY]]* } // CHECK: [[S_INT_TY:%.+]] = type { i{{[0-9]+}} } @@ -22,7 +26,7 @@ struct S { template T tmain() { S test; - T t_var; + T t_var = T(); T vec[] = {1, 2}; S s_arr[] = {1, 2}; S var(3); @@ -35,8 +39,75 @@ T tmain() { } int main() { +#ifdef LAMBDA + // LAMBDA: [[G:@.+]] = global i{{[0-9]+}} 1212, + // LAMBDA-LABEL: @main + // LAMBDA: call void [[OUTER_LAMBDA:@.+]]( + [&]() { + // LAMBDA: define{{.*}} internal{{.*}} void [[OUTER_LAMBDA]]( + // LAMBDA: [[G_LOCAL_REF:%.+]] = getelementptr inbounds %{{.+}}* [[AGG_CAPTURED:%.+]], i{{[0-9]+}} 0, i{{[0-9]+}} 0 + // LAMBDA: store i{{[0-9]+}}* [[G]], i{{[0-9]+}}** [[G_LOCAL_REF]] + // LAMBDA: [[ARG:%.+]] = bitcast %{{.+}}* [[AGG_CAPTURED]] to i8* + // LAMBDA: call void {{.+}}* @__kmpc_fork_call({{.+}}, i32 1, {{.+}}* [[OMP_REGION:@.+]] to {{.+}}, i8* [[ARG]]) +#pragma omp parallel private(g) + { + // LAMBDA: define{{.*}} internal{{.*}} void [[OMP_REGION]](i32* %{{.+}}, i32* %{{.+}}, %{{.+}}* [[ARG:%.+]]) + // LAMBDA: [[G_PRIVATE_ADDR:%.+]] = alloca i{{[0-9]+}}, + // LAMBDA: store %{{.+}}* [[ARG]], %{{.+}}** [[ARG_REF:%.+]], + // LAMBDA: call i32 @__kmpc_cancel_barrier( + g = 1; + // LAMBDA: store volatile i{{[0-9]+}} 1, i{{[0-9]+}}* [[G_PRIVATE_ADDR]], + // LAMBDA: [[G_PRIVATE_ADDR_REF:%.+]] = getelementptr inbounds %{{.+}}* [[ARG:%.+]], i{{[0-9]+}} 0, i{{[0-9]+}} 0 + // LAMBDA: store i{{[0-9]+}}* [[G_PRIVATE_ADDR]], i{{[0-9]+}}** [[G_PRIVATE_ADDR_REF]] + // LAMBDA: call void [[INNER_LAMBDA:@.+]](%{{.+}}* [[ARG]]) + [&]() { + // LAMBDA: define {{.+}} void [[INNER_LAMBDA]](%{{.+}}* [[ARG_PTR:%.+]]) + // LAMBDA: store %{{.+}}* [[ARG_PTR]], %{{.+}}** [[ARG_PTR_REF:%.+]], + g = 2; + // LAMBDA: [[ARG_PTR:%.+]] = load %{{.+}}** [[ARG_PTR_REF]] + // LAMBDA: [[G_PTR_REF:%.+]] = getelementptr inbounds %{{.+}}* [[ARG_PTR]], i{{[0-9]+}} 0, i{{[0-9]+}} 0 + // LAMBDA: [[G_REF:%.+]] = load i{{[0-9]+}}** [[G_PTR_REF]] + // LAMBDA: store volatile i{{[0-9]+}} 2, i{{[0-9]+}}* [[G_REF]] + }(); + } + }(); + return 0; +#elif defined(BLOCKS) + // BLOCKS: [[G:@.+]] = global i{{[0-9]+}} 1212, + // BLOCKS-LABEL: @main + // BLOCKS: call void {{%.+}}(i8* + ^{ + // BLOCKS: define{{.*}} internal{{.*}} void {{.+}}(i8* + // BLOCKS: [[G_LOCAL_REF:%.+]] = getelementptr inbounds %{{.+}}* [[AGG_CAPTURED:%.+]], i{{[0-9]+}} 0, i{{[0-9]+}} 0 + // BLOCKS: store i{{[0-9]+}}* [[G]], i{{[0-9]+}}** [[G_LOCAL_REF]] + // BLOCKS: [[ARG:%.+]] = bitcast %{{.+}}* [[AGG_CAPTURED]] to i8* + // BLOCKS: call void {{.+}}* @__kmpc_fork_call({{.+}}, i32 1, {{.+}}* [[OMP_REGION:@.+]] to {{.+}}, i8* [[ARG]]) +#pragma omp parallel private(g) + { + // BLOCKS: define{{.*}} internal{{.*}} void [[OMP_REGION]](i32* %{{.+}}, i32* %{{.+}}, %{{.+}}* [[ARG:%.+]]) + // BLOCKS: [[G_PRIVATE_ADDR:%.+]] = alloca i{{[0-9]+}}, + // BLOCKS: store %{{.+}}* [[ARG]], %{{.+}}** [[ARG_REF:%.+]], + // BLOCKS: call i32 @__kmpc_cancel_barrier( + g = 1; + // BLOCKS: store volatile i{{[0-9]+}} 1, i{{[0-9]+}}* [[G_PRIVATE_ADDR]], + // BLOCKS-NOT: [[G]]{{[[^:word:]]}} + // BLOCKS: i{{[0-9]+}}* [[G_PRIVATE_ADDR]] + // BLOCKS-NOT: [[G]]{{[[^:word:]]}} + // BLOCKS: call void {{%.+}}(i8* + ^{ + // BLOCKS: define {{.+}} void {{@.+}}(i8* + g = 2; + // BLOCKS-NOT: [[G]]{{[[^:word:]]}} + // BLOCKS: store volatile i{{[0-9]+}} 2, i{{[0-9]+}}* + // BLOCKS-NOT: [[G]]{{[[^:word:]]}} + // BLOCKS: ret + }(); + } + }(); + return 0; +#else S test; - int t_var; + int t_var = 0; int vec[] = {1, 2}; S s_arr[] = {1, 2}; S var(3); @@ -46,6 +117,7 @@ int main() { s_arr[0] = var; } return tmain(); +#endif } // CHECK: define i{{[0-9]+}} @main()