diff options
-rw-r--r-- | clang/include/clang/AST/GlobalDecl.h | 1 | ||||
-rw-r--r-- | clang/include/clang/Basic/DiagnosticSemaKinds.td | 5 | ||||
-rw-r--r-- | clang/lib/AST/ItaniumMangle.cpp | 4 | ||||
-rw-r--r-- | clang/lib/AST/MicrosoftMangle.cpp | 4 | ||||
-rw-r--r-- | clang/lib/CodeGen/CGExpr.cpp | 16 | ||||
-rw-r--r-- | clang/lib/CodeGen/CGStmt.cpp | 95 | ||||
-rw-r--r-- | clang/lib/CodeGen/CodeGenFunction.cpp | 3 | ||||
-rw-r--r-- | clang/lib/CodeGen/CodeGenFunction.h | 65 | ||||
-rw-r--r-- | clang/lib/Sema/SemaExpr.cpp | 10 | ||||
-rw-r--r-- | clang/test/CodeGen/captured-statements-nested.c | 126 | ||||
-rw-r--r-- | clang/test/CodeGen/captured-statements.c | 80 | ||||
-rw-r--r-- | clang/test/CodeGenCXX/captured-statements.cpp | 97 | ||||
-rw-r--r-- | clang/test/Sema/captured-statements.c | 12 |
13 files changed, 497 insertions, 21 deletions
diff --git a/clang/include/clang/AST/GlobalDecl.h b/clang/include/clang/AST/GlobalDecl.h index c43e44c26f3..54c9d88c9b2 100644 --- a/clang/include/clang/AST/GlobalDecl.h +++ b/clang/include/clang/AST/GlobalDecl.h @@ -41,6 +41,7 @@ public: GlobalDecl(const VarDecl *D) { Init(D);} GlobalDecl(const FunctionDecl *D) { Init(D); } GlobalDecl(const BlockDecl *D) { Init(D); } + GlobalDecl(const CapturedDecl *D) { Init(D); } GlobalDecl(const ObjCMethodDecl *D) { Init(D); } GlobalDecl(const CXXConstructorDecl *D, CXXCtorType Type) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 0c308469fc7..11fd91ab5bd 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -4848,8 +4848,6 @@ let CategoryName = "Lambda Issue" in { "duration">; def err_this_capture : Error< "'this' cannot be %select{implicitly |}0captured in this context">; - def err_lambda_capture_block : Error< - "__block variable %0 cannot be captured in a lambda expression">; def err_lambda_capture_anonymous_var : Error< "unnamed variable cannot be implicitly captured in a lambda expression">; def err_lambda_capture_vm_type : Error< @@ -4893,6 +4891,9 @@ let CategoryName = "Lambda Issue" in { def err_return_in_captured_stmt : Error< "cannot return from %0">; +def err_capture_block_variable : Error< + "__block variable %0 cannot be captured in a " + "%select{lambda expression|captured statement}1">; def err_operator_arrow_circular : Error< "circular pointer delegation detected">; diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 5ad8021fac9..d28aced172f 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -1419,6 +1419,10 @@ void CXXNameMangler::manglePrefix(const DeclContext *DC, bool NoFunction) { NameStream.flush(); Out << Name.size() << Name; return; + } else if (isa<CapturedDecl>(DC)) { + // Skip CapturedDecl context. + manglePrefix(getEffectiveParentContext(DC), NoFunction); + return; } const NamedDecl *ND = cast<NamedDecl>(DC); diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp index 1785063d7b1..7c797eef4d5 100644 --- a/clang/lib/AST/MicrosoftMangle.cpp +++ b/clang/lib/AST/MicrosoftMangle.cpp @@ -541,6 +541,10 @@ void MicrosoftCXXNameMangler::manglePostfix(const DeclContext *DC, Context.mangleBlock(BD, Out); Out << '@'; return manglePostfix(DC->getParent(), NoFunction); + } else if (isa<CapturedDecl>(DC)) { + // Skip CapturedDecl context. + manglePostfix(DC->getParent(), NoFunction); + return; } if (NoFunction && (isa<FunctionDecl>(DC) || isa<ObjCMethodDecl>(DC))) diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 64670c5e81e..8ba5a1ce522 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -1793,6 +1793,13 @@ static LValue EmitFunctionDeclLValue(CodeGenFunction &CGF, return CGF.MakeAddrLValue(V, E->getType(), Alignment); } +static LValue EmitCapturedFieldLValue(CodeGenFunction &CGF, const FieldDecl *FD, + llvm::Value *ThisValue) { + QualType TagType = CGF.getContext().getTagDeclType(FD->getParent()); + LValue LV = CGF.MakeNaturalAlignAddrLValue(ThisValue, TagType); + return CGF.EmitLValueForField(LV, FD); +} + LValue CodeGenFunction::EmitDeclRefLValue(const DeclRefExpr *E) { const NamedDecl *ND = E->getDecl(); CharUnits Alignment = getContext().getDeclAlign(ND); @@ -1844,10 +1851,11 @@ LValue CodeGenFunction::EmitDeclRefLValue(const DeclRefExpr *E) { // Use special handling for lambdas. if (!V) { if (FieldDecl *FD = LambdaCaptureFields.lookup(VD)) { - QualType LambdaTagType = getContext().getTagDeclType(FD->getParent()); - LValue LambdaLV = MakeNaturalAlignAddrLValue(CXXABIThisValue, - LambdaTagType); - return EmitLValueForField(LambdaLV, FD); + return EmitCapturedFieldLValue(*this, FD, CXXABIThisValue); + } else if (CapturedStmtInfo) { + if (const FieldDecl *FD = CapturedStmtInfo->lookup(VD)) + return EmitCapturedFieldLValue(*this, FD, + CapturedStmtInfo->getContextValue()); } assert(isa<BlockDecl>(CurCodeDecl) && E->refersToEnclosingLocal()); diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp index 208455c88c7..353d6c3d396 100644 --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -22,6 +22,7 @@ #include "llvm/IR/DataLayout.h" #include "llvm/IR/InlineAsm.h" #include "llvm/IR/Intrinsics.h" +#include "llvm/Support/CallSite.h" using namespace clang; using namespace CodeGen; @@ -137,7 +138,7 @@ void CodeGenFunction::EmitStmt(const Stmt *S) { case Stmt::GCCAsmStmtClass: // Intentional fall-through. case Stmt::MSAsmStmtClass: EmitAsmStmt(cast<AsmStmt>(*S)); break; case Stmt::CapturedStmtClass: - EmitCapturedStmt(cast<CapturedStmt>(*S)); + EmitCapturedStmt(cast<CapturedStmt>(*S), CR_Default); break; case Stmt::ObjCAtTryStmtClass: EmitObjCAtTryStmt(cast<ObjCAtTryStmt>(*S)); @@ -1750,6 +1751,94 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) { } } -void CodeGenFunction::EmitCapturedStmt(const CapturedStmt &S) { - llvm_unreachable("not implemented yet"); +static LValue InitCapturedStruct(CodeGenFunction &CGF, const CapturedStmt &S) { + const RecordDecl *RD = S.getCapturedRecordDecl(); + QualType RecordTy = CGF.getContext().getRecordType(RD); + + // Initialize the captured struct. + LValue SlotLV = CGF.MakeNaturalAlignAddrLValue( + CGF.CreateMemTemp(RecordTy, "agg.captured"), RecordTy); + + RecordDecl::field_iterator CurField = RD->field_begin(); + for (CapturedStmt::capture_init_iterator I = S.capture_init_begin(), + E = S.capture_init_end(); + I != E; ++I, ++CurField) { + LValue LV = CGF.EmitLValueForFieldInitialization(SlotLV, *CurField); + CGF.EmitInitializerForField(*CurField, LV, *I, ArrayRef<VarDecl *>()); + } + + return SlotLV; +} + +/// Generate an outlined function for the body of a CapturedStmt, store any +/// captured variables into the captured struct, and call the outlined function. +llvm::Function * +CodeGenFunction::EmitCapturedStmt(const CapturedStmt &S, CapturedRegionKind K) { + const CapturedDecl *CD = S.getCapturedDecl(); + const RecordDecl *RD = S.getCapturedRecordDecl(); + assert(CD->hasBody() && "missing CapturedDecl body"); + + LValue CapStruct = InitCapturedStruct(*this, S); + + // Emit the CapturedDecl + CodeGenFunction CGF(CGM, true); + CGF.CapturedStmtInfo = new CGCapturedStmtInfo(S, K); + llvm::Function *F = CGF.GenerateCapturedStmtFunction(CD, RD); + delete CGF.CapturedStmtInfo; + + // Emit call to the helper function. + EmitCallOrInvoke(F, CapStruct.getAddress()); + + return F; +} + +/// Creates the outlined function for a CapturedStmt. +llvm::Function * +CodeGenFunction::GenerateCapturedStmtFunction(const CapturedDecl *CD, + const RecordDecl *RD) { + assert(CapturedStmtInfo && + "CapturedStmtInfo should be set when generating the captured function"); + + // Check if we should generate debug info for this function. + maybeInitializeDebugInfo(); + + // Build the argument list. + ASTContext &Ctx = CGM.getContext(); + FunctionArgList Args; + Args.append(CD->param_begin(), CD->param_end()); + + // Create the function declaration. + FunctionType::ExtInfo ExtInfo; + const CGFunctionInfo &FuncInfo = + CGM.getTypes().arrangeFunctionDeclaration(Ctx.VoidTy, Args, ExtInfo, + /*IsVariadic=*/false); + llvm::FunctionType *FuncLLVMTy = CGM.getTypes().GetFunctionType(FuncInfo); + + llvm::Function *F = + llvm::Function::Create(FuncLLVMTy, llvm::GlobalValue::InternalLinkage, + CapturedStmtInfo->getHelperName(), &CGM.getModule()); + CGM.SetInternalFunctionAttributes(CD, F, FuncInfo); + + // Generate the function. + StartFunction(CD, Ctx.VoidTy, F, FuncInfo, Args, CD->getBody()->getLocStart()); + + // Set the context parameter in CapturedStmtInfo. + llvm::Value *DeclPtr = LocalDeclMap[CD->getContextParam()]; + assert(DeclPtr && "missing context parameter for CapturedStmt"); + CapturedStmtInfo->setContextValue(Builder.CreateLoad(DeclPtr)); + + // If 'this' is captured, load it into CXXThisValue. + if (CapturedStmtInfo->isCXXThisExprCaptured()) { + FieldDecl *FD = CapturedStmtInfo->getThisFieldDecl(); + LValue LV = MakeNaturalAlignAddrLValue(CapturedStmtInfo->getContextValue(), + Ctx.getTagDeclType(RD)); + LValue ThisLValue = EmitLValueForField(LV, FD); + + CXXThisValue = EmitLoadOfLValue(ThisLValue).getScalarVal(); + } + + CapturedStmtInfo->EmitBody(*this, CD->getBody()); + FinishFunction(CD->getBodyRBrace()); + + return F; } diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index 75c60edbba5..071c08e6bf2 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -33,6 +33,7 @@ using namespace CodeGen; CodeGenFunction::CodeGenFunction(CodeGenModule &cgm, bool suppressNewContext) : CodeGenTypeCache(cgm), CGM(cgm), Target(cgm.getTarget()), Builder(cgm.getModule().getContext()), + CapturedStmtInfo(0), SanitizePerformTypeCheck(CGM.getSanOpts().Null | CGM.getSanOpts().Alignment | CGM.getSanOpts().ObjectSize | @@ -1447,3 +1448,5 @@ llvm::Value *CodeGenFunction::EmitFieldAnnotations(const FieldDecl *D, return V; } + +CodeGenFunction::CGCapturedStmtInfo::~CGCapturedStmtInfo() { } diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index ff74c15c38c..6caf1689a9d 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -23,6 +23,7 @@ #include "clang/AST/ExprObjC.h" #include "clang/AST/Type.h" #include "clang/Basic/ABI.h" +#include "clang/Basic/CapturedStmt.h" #include "clang/Basic/TargetInfo.h" #include "clang/Frontend/CodeGenOptions.h" #include "llvm/ADT/ArrayRef.h" @@ -606,6 +607,65 @@ public: /// we prefer to insert allocas. llvm::AssertingVH<llvm::Instruction> AllocaInsertPt; + /// \brief API for captured statement code generation. + class CGCapturedStmtInfo { + public: + explicit CGCapturedStmtInfo(const CapturedStmt &S, + CapturedRegionKind K = CR_Default) + : Kind(K), ThisValue(0), CXXThisFieldDecl(0) { + + RecordDecl::field_iterator Field = + S.getCapturedRecordDecl()->field_begin(); + for (CapturedStmt::const_capture_iterator I = S.capture_begin(), + E = S.capture_end(); + I != E; ++I, ++Field) { + if (I->capturesThis()) + CXXThisFieldDecl = *Field; + else + CaptureFields[I->getCapturedVar()] = *Field; + } + } + + virtual ~CGCapturedStmtInfo(); + + CapturedRegionKind getKind() const { return Kind; } + + void setContextValue(llvm::Value *V) { ThisValue = V; } + // \brief Retrieve the value of the context parameter. + llvm::Value *getContextValue() const { return ThisValue; } + + /// \brief Lookup the captured field decl for a variable. + const FieldDecl *lookup(const VarDecl *VD) const { + return CaptureFields.lookup(VD); + } + + bool isCXXThisExprCaptured() const { return CXXThisFieldDecl != 0; } + FieldDecl *getThisFieldDecl() const { return CXXThisFieldDecl; } + + /// \brief Emit the captured statement body. + virtual void EmitBody(CodeGenFunction &CGF, Stmt *S) { + CGF.EmitStmt(S); + } + + /// \brief Get the name of the capture helper. + virtual StringRef getHelperName() const { return "__captured_stmt"; } + + private: + /// \brief The kind of captured statement being generated. + CapturedRegionKind Kind; + + /// \brief Keep the map between VarDecl and FieldDecl. + llvm::SmallDenseMap<const VarDecl *, FieldDecl *> CaptureFields; + + /// \brief The base address of the captured record, passed in as the first + /// argument of the parallel region function. + llvm::Value *ThisValue; + + /// \brief Captured 'this' type. + FieldDecl *CXXThisFieldDecl; + }; + CGCapturedStmtInfo *CapturedStmtInfo; + /// BoundsChecking - Emit run-time bounds checks. Higher values mean /// potentially higher performance penalties. unsigned char BoundsChecking; @@ -2188,7 +2248,6 @@ public: void EmitCaseStmt(const CaseStmt &S); void EmitCaseStmtRange(const CaseStmt &S); void EmitAsmStmt(const AsmStmt &S); - void EmitCapturedStmt(const CapturedStmt &S); void EmitObjCForCollectionStmt(const ObjCForCollectionStmt &S); void EmitObjCAtTryStmt(const ObjCAtTryStmt &S); @@ -2204,6 +2263,10 @@ public: void EmitCXXTryStmt(const CXXTryStmt &S); void EmitCXXForRangeStmt(const CXXForRangeStmt &S); + llvm::Function *EmitCapturedStmt(const CapturedStmt &S, CapturedRegionKind K); + llvm::Function *GenerateCapturedStmtFunction(const CapturedDecl *CD, + const RecordDecl *RD); + //===--------------------------------------------------------------------===// // LValue Expression Emission //===--------------------------------------------------------------------===// diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index dd05b822364..19ed4bcc0fb 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -11212,12 +11212,12 @@ bool Sema::tryCaptureVariable(VarDecl *Var, SourceLocation Loc, return true; } } - // Lambdas are not allowed to capture __block variables; they don't - // support the expected semantics. - if (IsLambda && HasBlocksAttr) { + // Lambdas and captured statements are not allowed to capture __block + // variables; they don't support the expected semantics. + if (HasBlocksAttr && (IsLambda || isa<CapturedRegionScopeInfo>(CSI))) { if (BuildAndDiagnose) { - Diag(Loc, diag::err_lambda_capture_block) - << Var->getDeclName(); + Diag(Loc, diag::err_capture_block_variable) + << Var->getDeclName() << !IsLambda; Diag(Var->getLocation(), diag::note_previous_decl) << Var->getDeclName(); } diff --git a/clang/test/CodeGen/captured-statements-nested.c b/clang/test/CodeGen/captured-statements-nested.c new file mode 100644 index 00000000000..d8ec74692ad --- /dev/null +++ b/clang/test/CodeGen/captured-statements-nested.c @@ -0,0 +1,126 @@ +// RUN: %clang_cc1 -fblocks -emit-llvm %s -o %t +// RUN: FileCheck %s -input-file=%t -check-prefix=CHECK1 +// RUN: FileCheck %s -input-file=%t -check-prefix=CHECK2 + +struct A { + int a; + float b; + char c; +}; + +void test_nest_captured_stmt(int param) { + int w; + // CHECK1: %struct.anon{{.*}} = type { i32*, i32* } + // CHECK1: %struct.anon{{.*}} = type { i32*, i32*, i32**, i32* } + // CHECK1: [[T:%struct.anon.*]] = type { i32*, i32*, %struct.A*, i32**, i32* } + #pragma clang __debug captured + { + int x; + int *y = &w; + #pragma clang __debug captured + { + struct A z; + #pragma clang __debug captured + { + w = x = z.a = 1; + *y = param; + z.b = 0.1f; + z.c = 'c'; + + // CHECK1: define internal void @__captured_stmt{{.*}}([[T]] + // + // CHECK1: getelementptr inbounds [[T]]* {{.*}}, i32 0, i32 2 + // CHECK1-NEXT: load %struct.A** + // CHECK1-NEXT: getelementptr inbounds %struct.A* + // CHECK1-NEXT: store i32 1 + // + // CHECK1: getelementptr inbounds [[T]]* {{.*}}, i32 0, i32 1 + // CHECK1-NEXT: load i32** + // CHECK1-NEXT: store i32 1 + // + // CHECK1: getelementptr inbounds [[T]]* {{.*}}, i32 0, i32 0 + // CHECK1-NEXT: load i32** + // CHECK1-NEXT: store i32 1 + // + // CHECK1: getelementptr inbounds [[T]]* {{.*}}, i32 0, i32 4 + // CHECK1-NEXT: load i32** + // CHECK1-NEXT: load i32* + // CHECK1-NEXT: getelementptr inbounds [[T]]* {{.*}}, i32 0, i32 3 + // CHECK1-NEXT: load i32*** + // CHECK1-NEXT: load i32** + // CHECK1-NEXT: store i32 + // + // CHECK1: getelementptr inbounds [[T]]* {{.*}}, i32 0, i32 2 + // CHECK1-NEXT: load %struct.A** + // CHECK1-NEXT: getelementptr inbounds %struct.A* + // CHECK1-NEXT: store float + // + // CHECK1: getelementptr inbounds [[T]]* {{.*}}, i32 0, i32 2 + // CHECK1-NEXT: load %struct.A** + // CHECK1-NEXT: getelementptr inbounds %struct.A* + // CHECK1-NEXT: store i8 99 + } + } + } +} + +void test_nest_block() { + __block int x; + int y; + ^{ + int z; + x = z; + #pragma clang __debug captured + { + z = y; // OK + } + }(); + + // CHECK2: define internal void @{{.*}}test_nest_block_block_invoke + // + // CHECK2: [[Z:%[0-9a-z_]*]] = alloca i32 + // CHECK2: alloca %struct.anon{{.*}} + // + // CHECK2: store i32 + // CHECK2: store i32* [[Z]] + // + // CHECK2: getelementptr inbounds %struct.anon + // CHECK2-NEXT: getelementptr inbounds + // CHECK2-NEXT: store i32* + // + // CHECK2: call void @__captured_stmt + + int a; + #pragma clang __debug captured + { + __block int b; + int c; + __block int d; + ^{ + b = a; + b = c; + b = d; + }(); + } + + // CHECK2: alloca %struct.__block_byref_b + // CHECK2-NEXT: [[C:%[0-9a-z_]*]] = alloca i32 + // CHECK2-NEXT: alloca %struct.__block_byref_d + // + // CHECK2: bitcast %struct.__block_byref_b* + // CHECK2-NEXT: store i8* + // + // CHECK2: [[CapA:%[0-9a-z_.]*]] = getelementptr inbounds {{.*}}, i32 0, i32 7 + // + // CHECK2: getelementptr inbounds %struct.anon{{.*}}, i32 0, i32 0 + // CHECK2: load i32** + // CHECK2: load i32* + // CHECK2: store i32 {{.*}}, i32* [[CapA]] + // + // CHECK2: [[CapC:%[0-9a-z_.]*]] = getelementptr inbounds {{.*}}, i32 0, i32 8 + // CHECK2-NEXT: [[Val:%[0-9a-z_]*]] = load i32* [[C]] + // CHECK2-NEXT: store i32 [[Val]], i32* [[CapC]] + // + // CHECK2: bitcast %struct.__block_byref_d* + // CHECK2-NEXT: store i8* +} diff --git a/clang/test/CodeGen/captured-statements.c b/clang/test/CodeGen/captured-statements.c new file mode 100644 index 00000000000..c87c1873506 --- /dev/null +++ b/clang/test/CodeGen/captured-statements.c @@ -0,0 +1,80 @@ +// RUN: %clang_cc1 -emit-llvm %s -o %t +// RUN: FileCheck %s -input-file=%t -check-prefix=CHECK-GLOBALS +// RUN: FileCheck %s -input-file=%t -check-prefix=CHECK-1 +// RUN: FileCheck %s -input-file=%t -check-prefix=CHECK-2 +// RUN: FileCheck %s -input-file=%t -check-prefix=CHECK-3 + +int foo(); +int global; + +// Single statement +void test1() { + int i = 0; + #pragma clang __debug captured + { + i++; + } + // CHECK-1: %struct.anon = type { i32* } + // + // CHECK-1: test1 + // CHECK-1: alloca %struct.anon + // CHECK-1: getelementptr inbounds %struct.anon* + // CHECK-1: store i32* %i + // CHECK-1: call void @[[HelperName:__captured_stmt[0-9]+]] +} + +// CHECK-1: define internal void @[[HelperName]](%struct.anon +// CHECK-1: getelementptr inbounds %struct.anon{{.*}}, i32 0, i32 0 +// CHECK-1: load i32** +// CHECK-1: load i32* +// CHECK-1: add nsw i32 +// CHECK-1: store i32 + +// Compound statement with local variable +void test2(int x) { + #pragma clang __debug captured + { + int i; + for (i = 0; i < x; i++) + foo(); + } + // CHECK-2: test2 + // CHECK-2-NOT: %i + // CHECK-2: call void @[[HelperName:__captured_stmt[0-9]+]] +} + +// CHECK-2: define internal void @[[HelperName]] +// CHECK-2-NOT: } +// CHECK-2: %i = alloca i32 + +// Capture array +void test3() { + int arr[] = {1, 2, 3, 4, 5}; + #pragma clang __debug captured + { + arr[2] = arr[1]; + } + // CHECK-3: test3 + // CHECK-3: alloca [5 x i32] + // CHECK-3: call void @__captured_stmt +} + +void dont_capture_global() { + static int s; + extern int e; + #pragma clang __debug captured + { + global++; + s++; + e++; + } + + // CHECK-GLOBALS: %[[Capture:struct\.anon[\.0-9]*]] = type {} + // CHECK-GLOBALS: call void @__captured_stmt[[HelperName:[0-9]+]](%[[Capture]] +} + +// CHECK-GLOBALS: define internal void @__captured_stmt[[HelperName]] +// CHECK-GLOBALS-NOT: ret +// CHECK-GLOBALS: load i32* @global +// CHECK-GLOBALS: load i32* @ +// CHECK-GLOBALS: load i32* @e diff --git a/clang/test/CodeGenCXX/captured-statements.cpp b/clang/test/CodeGenCXX/captured-statements.cpp new file mode 100644 index 00000000000..1d5ab205f65 --- /dev/null +++ b/clang/test/CodeGenCXX/captured-statements.cpp @@ -0,0 +1,97 @@ +// RUN: %clang_cc1 -std=c++11 -emit-llvm %s -o %t +// RUN: FileCheck %s -input-file=%t -check-prefix=CHECK-1 +// RUN: FileCheck %s -input-file=%t -check-prefix=CHECK-2 +// RUN: FileCheck %s -input-file=%t -check-prefix=CHECK-3 +// RUN: FileCheck %s -input-file=%t -check-prefix=CHECK-4 + +struct Foo { + int x; + float y; + ~Foo() {} +}; + +struct TestClass { + int x; + + TestClass() : x(0) {}; + void MemberFunc() { + Foo f; + #pragma clang __debug captured + { + f.y = x; + } + } +}; + +void test1() { + TestClass c; + c.MemberFunc(); + // CHECK-1: %[[Capture:struct\.anon[\.0-9]*]] = type { %struct.Foo*, %struct.TestClass* } + + // CHECK-1: define {{.*}} void @_ZN9TestClass10MemberFuncEv + // CHECK-1: alloca %struct.anon + // CHECK-1: getelementptr inbounds %[[Capture]]* %{{[^,]*}}, i32 0, i32 0 + // CHECK-1: store %struct.Foo* %f, %struct.Foo** + // CHECK-1: getelementptr inbounds %[[Capture]]* %{{[^,]*}}, i32 0, i32 1 + // CHECK-1: call void @[[HelperName:[A-Za-z0-9_]+]](%[[Capture]]* + // CHECK-1: call void @_ZN3FooD1Ev + // CHECK-1: ret +} + +// CHECK-1: define internal void @[[HelperName]] +// CHECK-1: getelementptr inbounds %[[Capture]]* {{[^,]*}}, i32 0, i32 1 +// CHECK-1: getelementptr inbounds %struct.TestClass* {{[^,]*}}, i32 0, i32 0 +// CHECK-1: getelementptr inbounds %[[Capture]]* {{[^,]*}}, i32 0, i32 0 + +void test2(int x) { + int y = [&]() { + #pragma clang __debug captured + { + x++; + } + return x; + }(); + + // CHECK-2: define void @_Z5test2i + // CHECK-2: call i32 @[[Lambda:["$\w]+]] + // + // CHECK-2: define internal i32 @[[Lambda]] + // CHECK-2: call void @[[HelperName:["$_A-Za-z0-9]+]](%[[Capture:.*]]* + // + // CHECK-2: define internal void @[[HelperName]] + // CHECK-2: getelementptr inbounds %[[Capture]]* + // CHECK-2: load i32** + // CHECK-2: load i32* +} + +void test3(int x) { + #pragma clang __debug captured + { + x = [=]() { return x + 1; } (); + } + + // CHECK-3: %[[Capture:struct\.anon[\.0-9]*]] = type { i32* } + + // CHECK-3: define void @_Z5test3i(i32 %x) + // CHECK-3: store i32* + // CHECK-3: call void @{{.*}}__captured_stmt + // CHECK-3: ret void +} + +void test4() { + #pragma clang __debug captured + { + Foo f; + f.x = 5; + } + // CHECK-4: %[[Capture:struct\.anon[\.0-9]*]] = type { i32* } + + // CHECK-4: define void @_Z5test3i(i32 %x) + // CHECK-4: store i32* + // CHECK-4: call void @[[HelperName:["$_A-Za-z0-9]+]](%[[Capture:.*]]* + // CHECK-4: ret void + // + // CHECK-4: define internal void @[[HelperName]] + // CHECK-4: store i32 5, i32* + // CHECK-4: call void @{{.*}}FooD1Ev(%struct.Foo* +} diff --git a/clang/test/Sema/captured-statements.c b/clang/test/Sema/captured-statements.c index 9285a7802d5..86e9273944b 100644 --- a/clang/test/Sema/captured-statements.c +++ b/clang/test/Sema/captured-statements.c @@ -49,29 +49,29 @@ void test_nest() { } void test_nest_block() { - __block int x; + __block int x; // expected-note {{'x' declared here}} int y; ^{ int z; #pragma clang __debug captured { - x = y; // OK + x = y; // expected-error{{__block variable 'x' cannot be captured in a captured statement}} y = z; // expected-error{{variable is not assignable (missing __block type specifier)}} z = y; // OK } }(); - __block int a; + __block int a; // expected-note 2 {{'a' declared here}} int b; #pragma clang __debug captured { __block int c; int d; ^{ - a = b; // OK - a = c; // OK + a = b; // expected-error{{__block variable 'a' cannot be captured in a captured statement}} b = d; // OK - Consistent with block inside a lambda - c = a; // OK + c = a; // expected-error{{__block variable 'a' cannot be captured in a captured statement}} + c = d; // OK d = b; // expected-error{{variable is not assignable (missing __block type specifier)}} }(); } |