Index: include/clang/AST/Expr.h =================================================================== --- include/clang/AST/Expr.h +++ include/clang/AST/Expr.h @@ -886,9 +886,9 @@ /// DeclRefExprBits.HasTemplateKWAndArgsInfo: /// Specifies when this declaration reference expression has an explicit /// C++ template keyword and/or template argument list. -/// DeclRefExprBits.RefersToEnclosingLocal +/// DeclRefExprBits.RefersToCapturedVariable /// Specifies when this declaration reference expression (validly) -/// refers to a local variable from a different function. +/// refers to a captured variable. class DeclRefExpr : public Expr { /// \brief The declaration that we are referencing. ValueDecl *D; @@ -933,7 +933,7 @@ DeclRefExpr(const ASTContext &Ctx, NestedNameSpecifierLoc QualifierLoc, SourceLocation TemplateKWLoc, - ValueDecl *D, bool refersToEnclosingLocal, + ValueDecl *D, bool RefersToCapturedVariable, const DeclarationNameInfo &NameInfo, NamedDecl *FoundD, const TemplateArgumentListInfo *TemplateArgs, @@ -948,7 +948,7 @@ void computeDependence(const ASTContext &C); public: - DeclRefExpr(ValueDecl *D, bool refersToEnclosingLocal, QualType T, + DeclRefExpr(ValueDecl *D, bool RefersToCapturedVariable, QualType T, ExprValueKind VK, SourceLocation L, const DeclarationNameLoc &LocInfo = DeclarationNameLoc()) : Expr(DeclRefExprClass, T, VK, OK_Ordinary, false, false, false, false), @@ -957,22 +957,22 @@ DeclRefExprBits.HasTemplateKWAndArgsInfo = 0; DeclRefExprBits.HasFoundDecl = 0; DeclRefExprBits.HadMultipleCandidates = 0; - DeclRefExprBits.RefersToEnclosingLocal = refersToEnclosingLocal; + DeclRefExprBits.RefersToCapturedVariable = RefersToCapturedVariable; computeDependence(D->getASTContext()); } static DeclRefExpr * Create(const ASTContext &Context, NestedNameSpecifierLoc QualifierLoc, - SourceLocation TemplateKWLoc, ValueDecl *D, bool isEnclosingLocal, - SourceLocation NameLoc, QualType T, ExprValueKind VK, - NamedDecl *FoundD = nullptr, + SourceLocation TemplateKWLoc, ValueDecl *D, + bool RefersToCapturedVariable, SourceLocation NameLoc, QualType T, + ExprValueKind VK, NamedDecl *FoundD = nullptr, const TemplateArgumentListInfo *TemplateArgs = nullptr); static DeclRefExpr * Create(const ASTContext &Context, NestedNameSpecifierLoc QualifierLoc, - SourceLocation TemplateKWLoc, ValueDecl *D, bool isEnclosingLocal, - const DeclarationNameInfo &NameInfo, QualType T, ExprValueKind VK, - NamedDecl *FoundD = nullptr, + SourceLocation TemplateKWLoc, ValueDecl *D, + bool RefersToCapturedVariable, const DeclarationNameInfo &NameInfo, + QualType T, ExprValueKind VK, NamedDecl *FoundD = nullptr, const TemplateArgumentListInfo *TemplateArgs = nullptr); /// \brief Construct an empty declaration reference expression. @@ -1144,10 +1144,9 @@ DeclRefExprBits.HadMultipleCandidates = V; } - /// Does this DeclRefExpr refer to a local declaration from an - /// enclosing function scope? - bool refersToEnclosingLocal() const { - return DeclRefExprBits.RefersToEnclosingLocal; + /// Does this DeclRefExpr refer to a captured variable? + bool refersToCapturedVariable() const { + return DeclRefExprBits.RefersToCapturedVariable; } static bool classof(const Stmt *T) { Index: include/clang/AST/Stmt.h =================================================================== --- include/clang/AST/Stmt.h +++ include/clang/AST/Stmt.h @@ -212,7 +212,7 @@ unsigned HasTemplateKWAndArgsInfo : 1; unsigned HasFoundDecl : 1; unsigned HadMultipleCandidates : 1; - unsigned RefersToEnclosingLocal : 1; + unsigned RefersToCapturedVariable : 1; }; class CastExprBitfields { Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -3427,6 +3427,9 @@ 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); @@ -7453,6 +7456,10 @@ 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); Index: lib/AST/ASTImporter.cpp =================================================================== --- lib/AST/ASTImporter.cpp +++ lib/AST/ASTImporter.cpp @@ -4375,7 +4375,7 @@ Importer.Import(E->getQualifierLoc()), Importer.Import(E->getTemplateKeywordLoc()), ToD, - E->refersToEnclosingLocal(), + E->refersToCapturedVariable(), Importer.Import(E->getLocation()), T, E->getValueKind(), FoundD, Index: lib/AST/Expr.cpp =================================================================== --- lib/AST/Expr.cpp +++ lib/AST/Expr.cpp @@ -322,7 +322,7 @@ DeclRefExpr::DeclRefExpr(const ASTContext &Ctx, NestedNameSpecifierLoc QualifierLoc, SourceLocation TemplateKWLoc, - ValueDecl *D, bool RefersToEnclosingLocal, + ValueDecl *D, bool RefersToCapturedVariable, const DeclarationNameInfo &NameInfo, NamedDecl *FoundD, const TemplateArgumentListInfo *TemplateArgs, @@ -343,7 +343,7 @@ getInternalFoundDecl() = FoundD; DeclRefExprBits.HasTemplateKWAndArgsInfo = (TemplateArgs || TemplateKWLoc.isValid()) ? 1 : 0; - DeclRefExprBits.RefersToEnclosingLocal = RefersToEnclosingLocal; + DeclRefExprBits.RefersToCapturedVariable = RefersToCapturedVariable; if (TemplateArgs) { bool Dependent = false; bool InstantiationDependent = false; @@ -367,14 +367,14 @@ NestedNameSpecifierLoc QualifierLoc, SourceLocation TemplateKWLoc, ValueDecl *D, - bool RefersToEnclosingLocal, + bool RefersToCapturedVariable, SourceLocation NameLoc, QualType T, ExprValueKind VK, NamedDecl *FoundD, const TemplateArgumentListInfo *TemplateArgs) { return Create(Context, QualifierLoc, TemplateKWLoc, D, - RefersToEnclosingLocal, + RefersToCapturedVariable, DeclarationNameInfo(D->getDeclName(), NameLoc), T, VK, FoundD, TemplateArgs); } @@ -383,7 +383,7 @@ NestedNameSpecifierLoc QualifierLoc, SourceLocation TemplateKWLoc, ValueDecl *D, - bool RefersToEnclosingLocal, + bool RefersToCapturedVariable, const DeclarationNameInfo &NameInfo, QualType T, ExprValueKind VK, @@ -405,7 +405,7 @@ void *Mem = Context.Allocate(Size, llvm::alignOf()); return new (Mem) DeclRefExpr(Context, QualifierLoc, TemplateKWLoc, D, - RefersToEnclosingLocal, + RefersToCapturedVariable, NameInfo, FoundD, TemplateArgs, T, VK); } Index: lib/Analysis/BodyFarm.cpp =================================================================== --- lib/Analysis/BodyFarm.cpp +++ lib/Analysis/BodyFarm.cpp @@ -115,7 +115,7 @@ /* QualifierLoc = */ NestedNameSpecifierLoc(), /* TemplateKWLoc = */ SourceLocation(), /* D = */ const_cast(D), - /* isEnclosingLocal = */ false, + /* RefersToCapturedVariable = */ false, /* NameLoc = */ SourceLocation(), /* T = */ D->getType(), /* VK = */ VK_LValue); Index: lib/CodeGen/CGExpr.cpp =================================================================== --- lib/CodeGen/CGExpr.cpp +++ lib/CodeGen/CGExpr.cpp @@ -1897,6 +1897,21 @@ QualType T = E->getType(); if (const auto *VD = dyn_cast(ND)) { + // Check for captured variables. + if (E->refersToCapturedVariable()) { + 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()) @@ -1947,21 +1962,6 @@ *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) Index: lib/CodeGen/CGStmtOpenMP.cpp =================================================================== --- lib/CodeGen/CGStmtOpenMP.cpp +++ 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 @@ } } + InlinedOpenMPRegion Region(*this, S.getAssociatedStmt()); RunCleanupsScope DirectiveScope(*this); CGDebugInfo *DI = getDebugInfo(); @@ -505,8 +535,10 @@ CGM.getOpenMPRuntime().EmitOMPCriticalRegionStart(*this, Lock, S.getLocStart()); { + InlinedOpenMPRegion Region(*this, S.getAssociatedStmt()); RunCleanupsScope Scope(*this); - EmitStmt(cast(S.getAssociatedStmt())->getCapturedStmt()); + EmitStmt( + cast(S.getAssociatedStmt())->getCapturedStmt()); EnsureInsertPoint(); } CGM.getOpenMPRuntime().EmitOMPCriticalRegionEnd(*this, Lock, S.getLocEnd()); Index: lib/CodeGen/CodeGenFunction.h =================================================================== --- lib/CodeGen/CodeGenFunction.h +++ lib/CodeGen/CodeGenFunction.h @@ -182,6 +182,8 @@ /// \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 @@ 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); Index: lib/Frontend/Rewrite/RewriteModernObjC.cpp =================================================================== --- lib/Frontend/Rewrite/RewriteModernObjC.cpp +++ lib/Frontend/Rewrite/RewriteModernObjC.cpp @@ -4563,16 +4563,12 @@ GetBlockDeclRefExprs(*CI); } // Handle specific things. - if (DeclRefExpr *DRE = dyn_cast(S)) { - if (DRE->refersToEnclosingLocal()) { + if (DeclRefExpr *DRE = dyn_cast(S)) + if (DRE->refersToCapturedVariable() || + 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 @@ } // Handle specific things. if (DeclRefExpr *DRE = dyn_cast(S)) { - if (DRE->refersToEnclosingLocal()) { - if (!isa(DRE->getDecl()) && - !InnerContexts.count(DRE->getDecl()->getDeclContext())) + if (DRE->refersToCapturedVariable() || + 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 @@ // 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->refersToCapturedVariable() || + HasLocalVariableExternalStorage(DeclRefExp->getDecl()); FieldDecl *FD = FieldDecl::Create(*Context, nullptr, SourceLocation(), SourceLocation(), Index: lib/Frontend/Rewrite/RewriteObjC.cpp =================================================================== --- lib/Frontend/Rewrite/RewriteObjC.cpp +++ lib/Frontend/Rewrite/RewriteObjC.cpp @@ -3671,16 +3671,12 @@ GetBlockDeclRefExprs(*CI); } // Handle specific things. - if (DeclRefExpr *DRE = dyn_cast(S)) { - if (DRE->refersToEnclosingLocal()) { + if (DeclRefExpr *DRE = dyn_cast(S)) + if (DRE->refersToCapturedVariable() || + 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 @@ } // Handle specific things. if (DeclRefExpr *DRE = dyn_cast(S)) { - if (DRE->refersToEnclosingLocal()) { - if (!isa(DRE->getDecl()) && - !InnerContexts.count(DRE->getDecl()->getDeclContext())) + if (DRE->refersToCapturedVariable() || + 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 @@ // 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->refersToCapturedVariable() || + HasLocalVariableExternalStorage(DeclRefExp->getDecl()); FieldDecl *FD = FieldDecl::Create(*Context, nullptr, SourceLocation(), SourceLocation(), Index: lib/Sema/SemaChecking.cpp =================================================================== --- lib/Sema/SemaChecking.cpp +++ lib/Sema/SemaChecking.cpp @@ -4858,7 +4858,7 @@ DeclRefExpr *DR = cast(E); // If we leave the immediate function, the lifetime isn't about to end. - if (DR->refersToEnclosingLocal()) + if (DR->refersToCapturedVariable()) return nullptr; if (VarDecl *V = dyn_cast(DR->getDecl())) @@ -5025,7 +5025,7 @@ DeclRefExpr *DR = cast(E); // If we leave the immediate function, the lifetime isn't about to end. - if (DR->refersToEnclosingLocal()) + if (DR->refersToCapturedVariable()) return nullptr; if (VarDecl *V = dyn_cast(DR->getDecl())) { Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -10159,7 +10159,7 @@ QualType CaptureType = VD->getType(); const bool ByRef = C.getCaptureKind() == LCK_ByRef; LSI->addCapture(VD, /*IsBlock*/false, ByRef, - /*RefersToEnclosingLocal*/true, C.getLocation(), + /*RefersToCapturedVariable*/true, C.getLocation(), /*EllipsisLoc*/C.isPackExpansion() ? C.getEllipsisLoc() : SourceLocation(), CaptureType, /*Expr*/ nullptr); Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -1601,11 +1601,9 @@ } } - bool refersToEnclosingScope = - (CurContext != D->getDeclContext() && - D->getDeclContext()->isFunctionOrMethod()) || - (isa(D) && - cast(D)->isInitCapture()); + bool RefersToCapturedVariable = + isa(D) && + NeedToCaptureVariable(cast(D), NameInfo.getLoc()); DeclRefExpr *E; if (isa(D)) { @@ -1615,15 +1613,15 @@ E = DeclRefExpr::Create( Context, SS ? SS->getWithLocInContext(Context) : NestedNameSpecifierLoc(), - VarSpec->getTemplateKeywordLoc(), D, refersToEnclosingScope, + VarSpec->getTemplateKeywordLoc(), D, RefersToCapturedVariable, NameInfo.getLoc(), Ty, VK, FoundD, TemplateArgs); } else { assert(!TemplateArgs && "No template arguments for non-variable" " template specialization references"); - E = DeclRefExpr::Create( - Context, - SS ? SS->getWithLocInContext(Context) : NestedNameSpecifierLoc(), - SourceLocation(), D, refersToEnclosingScope, NameInfo, Ty, VK, FoundD); + E = DeclRefExpr::Create(Context, SS ? SS->getWithLocInContext(Context) + : NestedNameSpecifierLoc(), + SourceLocation(), D, RefersToCapturedVariable, + NameInfo, Ty, VK, FoundD); } MarkDeclRefReferenced(E); @@ -8462,7 +8460,7 @@ // Must be a reference to a declaration from an enclosing scope. DeclRefExpr *DRE = dyn_cast(E); if (!DRE) return NCCK_None; - if (!DRE->refersToEnclosingLocal()) return NCCK_None; + if (!DRE->refersToCapturedVariable()) return NCCK_None; // The declaration must be a variable which is not declared 'const'. VarDecl *var = dyn_cast(DRE->getDecl()); @@ -11654,7 +11652,7 @@ 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); } @@ -11822,7 +11820,7 @@ const bool BuildAndDiagnose, QualType &CaptureType, QualType &DeclRefType, - const bool RefersToEnclosingLocal, + const bool RefersToCapturedVariable, Sema &S) { // By default, capture variables by reference. @@ -11844,7 +11842,7 @@ Field->setAccess(AS_private); RD->addDecl(Field); - CopyExpr = new (S.Context) DeclRefExpr(Var, RefersToEnclosingLocal, + CopyExpr = new (S.Context) DeclRefExpr(Var, RefersToCapturedVariable, DeclRefType, VK_LValue, Loc); Var->setReferenced(true); Var->markUsed(S.Context); @@ -11852,7 +11850,7 @@ // Actually capture the variable. if (BuildAndDiagnose) - RSI->addCapture(Var, /*isBlock*/false, ByRef, RefersToEnclosingLocal, Loc, + RSI->addCapture(Var, /*isBlock*/false, ByRef, RefersToCapturedVariable, Loc, SourceLocation(), CaptureType, CopyExpr); @@ -11866,7 +11864,7 @@ VarDecl *Var, QualType FieldType, QualType DeclRefType, SourceLocation Loc, - bool RefersToEnclosingLocal) { + bool RefersToCapturedVariable) { CXXRecordDecl *Lambda = LSI->Lambda; // Build the non-static data member. @@ -11895,7 +11893,7 @@ // C++ [expr.prim.labda]p12: // An entity captured by a lambda-expression is odr-used (3.2) in // the scope containing the lambda-expression. - Expr *Ref = new (S.Context) DeclRefExpr(Var, RefersToEnclosingLocal, + Expr *Ref = new (S.Context) DeclRefExpr(Var, RefersToCapturedVariable, DeclRefType, VK_LValue, Loc); Var->setReferenced(true); Var->markUsed(S.Context); @@ -11989,7 +11987,7 @@ const bool BuildAndDiagnose, QualType &CaptureType, QualType &DeclRefType, - const bool RefersToEnclosingLocal, + const bool RefersToCapturedVariable, const Sema::TryCaptureKind Kind, SourceLocation EllipsisLoc, const bool IsTopScope, @@ -12063,7 +12061,7 @@ if (BuildAndDiagnose) { ExprResult Result = addAsFieldToClosureType(S, LSI, Var, CaptureType, DeclRefType, Loc, - RefersToEnclosingLocal); + RefersToCapturedVariable); if (!Result.isInvalid()) CopyExpr = Result.get(); } @@ -12084,7 +12082,7 @@ // Add the capture. if (BuildAndDiagnose) - LSI->addCapture(Var, /*IsBlock=*/false, ByRef, RefersToEnclosingLocal, + LSI->addCapture(Var, /*IsBlock=*/false, ByRef, RefersToCapturedVariable, Loc, EllipsisLoc, CaptureType, CopyExpr); return true; @@ -12096,7 +12094,7 @@ QualType &CaptureType, QualType &DeclRefType, const unsigned *const FunctionScopeIndexToStopAt) { - bool Nested = false; + bool Nested = Var->isInitCapture(); DeclContext *DC = CurContext; const unsigned MaxFunctionScopesIndex = FunctionScopeIndexToStopAt @@ -12114,8 +12112,13 @@ // 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 @@ -12136,8 +12139,17 @@ 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); @@ -12363,6 +12375,14 @@ 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; Index: lib/Sema/SemaOpenMP.cpp =================================================================== --- lib/Sema/SemaOpenMP.cpp +++ lib/Sema/SemaOpenMP.cpp @@ -551,6 +551,19 @@ #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, @@ -4016,7 +4029,7 @@ auto VDPrivateRefExpr = DeclRefExpr::Create( Context, /*QualifierLoc*/ NestedNameSpecifierLoc(), /*TemplateKWLoc*/ SourceLocation(), VDPrivate, - /*isEnclosingLocal*/ false, /*NameLoc*/ SourceLocation(), DE->getType(), + /*RefersToCapturedVariable*/ false, /*NameLoc*/ SourceLocation(), DE->getType(), /*VK*/ VK_LValue); DSAStack->addDSA(VD, DE, OMPC_private); @@ -4236,10 +4249,9 @@ VD->getTypeSourceInfo(), /*S*/ SC_Auto); CurContext->addHiddenDecl(VDInit); VDInitRefExpr = DeclRefExpr::Create( - Context, /*QualifierLoc*/ NestedNameSpecifierLoc(), - /*TemplateKWLoc*/ SourceLocation(), VDInit, - /*isEnclosingLocal*/ false, ELoc, Type, - /*VK*/ VK_LValue); + Context, NestedNameSpecifierLoc(), SourceLocation(), VDInit, + /*RefersToCapturedVariable=*/true, ELoc, Type, + /*VK=*/VK_LValue); VDInit->setIsUsed(); auto Init = DefaultLvalueConversion(VDInitRefExpr).get(); InitializedEntity Entity = InitializedEntity::InitializeVariable(VDInit); @@ -4252,8 +4264,14 @@ 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(), + /*RefersToCapturedVariable=*/true, + DE->getExprLoc(), DE->getType(), + /*VK=*/VK_LValue)).get(), + /*DirectInit=*/false, /*TypeMayContainAuto=*/false); } if (VDPrivate->isInvalidDecl()) { if (IsImplicitClause) { @@ -4264,10 +4282,9 @@ } CurContext->addDecl(VDPrivate); auto VDPrivateRefExpr = DeclRefExpr::Create( - Context, /*QualifierLoc*/ NestedNameSpecifierLoc(), - /*TemplateKWLoc*/ SourceLocation(), VDPrivate, - /*isEnclosingLocal*/ false, DE->getLocStart(), DE->getType(), - /*VK*/ VK_LValue); + Context, NestedNameSpecifierLoc(), SourceLocation(), VDPrivate, + /*RefersToCapturedVariable=*/false, DE->getLocStart(), DE->getType(), + /*VK=*/VK_LValue); DSAStack->addDSA(VD, DE, OMPC_firstprivate); Vars.push_back(DE); PrivateCopies.push_back(VDPrivateRefExpr); Index: lib/Sema/SemaStmt.cpp =================================================================== --- lib/Sema/SemaStmt.cpp +++ lib/Sema/SemaStmt.cpp @@ -2447,7 +2447,7 @@ // - in a return statement in a function [where] ... // ... the expression is the name of a non-volatile automatic object ... DeclRefExpr *DR = dyn_cast(E->IgnoreParens()); - if (!DR || DR->refersToEnclosingLocal()) + if (!DR || DR->refersToCapturedVariable()) return nullptr; VarDecl *VD = dyn_cast(DR->getDecl()); if (!VD) Index: lib/Serialization/ASTReaderStmt.cpp =================================================================== --- lib/Serialization/ASTReaderStmt.cpp +++ lib/Serialization/ASTReaderStmt.cpp @@ -433,7 +433,7 @@ E->DeclRefExprBits.HasFoundDecl = Record[Idx++]; E->DeclRefExprBits.HasTemplateKWAndArgsInfo = Record[Idx++]; E->DeclRefExprBits.HadMultipleCandidates = Record[Idx++]; - E->DeclRefExprBits.RefersToEnclosingLocal = Record[Idx++]; + E->DeclRefExprBits.RefersToCapturedVariable = Record[Idx++]; unsigned NumTemplateArgs = 0; if (E->hasTemplateKWAndArgsInfo()) NumTemplateArgs = Record[Idx++]; Index: lib/Serialization/ASTWriterDecl.cpp =================================================================== --- lib/Serialization/ASTWriterDecl.cpp +++ lib/Serialization/ASTWriterDecl.cpp @@ -1842,7 +1842,8 @@ Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); //GetDeclFound Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); //ExplicitTemplateArgs Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); //HadMultipleCandidates - Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); //RefersToEnclosingLocal + Abv->Add( + BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // RefersToCapturedVariable Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // DeclRef Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Location DeclRefExprAbbrev = Stream.EmitAbbrev(Abv); Index: lib/Serialization/ASTWriterStmt.cpp =================================================================== --- lib/Serialization/ASTWriterStmt.cpp +++ lib/Serialization/ASTWriterStmt.cpp @@ -344,7 +344,7 @@ Record.push_back(E->getDecl() != E->getFoundDecl()); Record.push_back(E->hasTemplateKWAndArgsInfo()); Record.push_back(E->hadMultipleCandidates()); - Record.push_back(E->refersToEnclosingLocal()); + Record.push_back(E->refersToCapturedVariable()); if (E->hasTemplateKWAndArgsInfo()) { unsigned NumTemplateArgs = E->getNumTemplateArgs(); Index: test/OpenMP/parallel_firstprivate_codegen.cpp =================================================================== --- test/OpenMP/parallel_firstprivate_codegen.cpp +++ 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 @@ ~St() {} }; -volatile int g; +volatile int g = 1212; template struct S { @@ -47,6 +49,83 @@ } 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 void @__kmpc_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 void @__kmpc_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 @@ s_arr[0] = var; } return tmain(); +#endif } // CHECK: define {{.*}}i{{[0-9]+}} @main() Index: test/OpenMP/parallel_private_codegen.cpp =================================================================== --- test/OpenMP/parallel_private_codegen.cpp +++ 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 @@ ~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 @@ 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 @@ } 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 void @__kmpc_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 void @__kmpc_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 @@ s_arr[0] = var; } return tmain(); +#endif } // CHECK: define i{{[0-9]+}} @main()