From 524387ae3dfc0c4cf2b095f83f9e47aa549b7e55 Mon Sep 17 00:00:00 2001 From: Ben Langmuir Date: Thu, 9 May 2013 19:17:11 +0000 Subject: [PATCH] CodeGen for CapturedStmts EmitCapturedStmt creates a captured struct containing all of the captured variables, and then emits a call to the outlined function. This is similar in principle to EmitBlockLiteral. GenerateCapturedFunction actually produces the outlined function. It is based on GenerateBlockFunction, but is much simpler. The function type is determined by the parameters that are in the CapturedDecl. Some changes have been added to this patch that were reviewed as part of the serialization patch and moving the parameters to the captured decl. Differential Revision: http://llvm-reviews.chandlerc.com/D640 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181536 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/GlobalDecl.h | 1 + include/clang/Basic/DiagnosticSemaKinds.td | 5 +- lib/AST/ItaniumMangle.cpp | 4 + lib/AST/MicrosoftMangle.cpp | 4 + lib/CodeGen/CGExpr.cpp | 16 ++- lib/CodeGen/CGStmt.cpp | 95 +++++++++++++++- lib/CodeGen/CodeGenFunction.cpp | 3 + lib/CodeGen/CodeGenFunction.h | 65 ++++++++++- lib/Sema/SemaExpr.cpp | 10 +- test/CodeGen/captured-statements-nested.c | 126 +++++++++++++++++++++ test/CodeGen/captured-statements.c | 80 +++++++++++++ test/CodeGenCXX/captured-statements.cpp | 97 ++++++++++++++++ test/Sema/captured-statements.c | 12 +- 13 files changed, 497 insertions(+), 21 deletions(-) create mode 100644 test/CodeGen/captured-statements-nested.c create mode 100644 test/CodeGen/captured-statements.c create mode 100644 test/CodeGenCXX/captured-statements.cpp diff --git a/include/clang/AST/GlobalDecl.h b/include/clang/AST/GlobalDecl.h index c43e44c26f..54c9d88c9b 100644 --- a/include/clang/AST/GlobalDecl.h +++ b/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/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 0c308469fc..11fd91ab5b 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/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/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp index 5ad8021fac..d28aced172 100644 --- a/lib/AST/ItaniumMangle.cpp +++ b/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(DC)) { + // Skip CapturedDecl context. + manglePrefix(getEffectiveParentContext(DC), NoFunction); + return; } const NamedDecl *ND = cast(DC); diff --git a/lib/AST/MicrosoftMangle.cpp b/lib/AST/MicrosoftMangle.cpp index 1785063d7b..7c797eef4d 100644 --- a/lib/AST/MicrosoftMangle.cpp +++ b/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(DC)) { + // Skip CapturedDecl context. + manglePostfix(DC->getParent(), NoFunction); + return; } if (NoFunction && (isa(DC) || isa(DC))) diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp index 64670c5e81..8ba5a1ce52 100644 --- a/lib/CodeGen/CGExpr.cpp +++ b/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(CurCodeDecl) && E->refersToEnclosingLocal()); diff --git a/lib/CodeGen/CGStmt.cpp b/lib/CodeGen/CGStmt.cpp index 208455c88c..353d6c3d39 100644 --- a/lib/CodeGen/CGStmt.cpp +++ b/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(*S)); break; case Stmt::CapturedStmtClass: - EmitCapturedStmt(cast(*S)); + EmitCapturedStmt(cast(*S), CR_Default); break; case Stmt::ObjCAtTryStmtClass: EmitObjCAtTryStmt(cast(*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()); + } + + 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/lib/CodeGen/CodeGenFunction.cpp b/lib/CodeGen/CodeGenFunction.cpp index 75c60edbba..071c08e6bf 100644 --- a/lib/CodeGen/CodeGenFunction.cpp +++ b/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/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index ff74c15c38..6caf1689a9 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/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 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 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/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index dd05b82236..19ed4bcc0f 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/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(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/test/CodeGen/captured-statements-nested.c b/test/CodeGen/captured-statements-nested.c new file mode 100644 index 0000000000..d8ec74692a --- /dev/null +++ b/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/test/CodeGen/captured-statements.c b/test/CodeGen/captured-statements.c new file mode 100644 index 0000000000..c87c187350 --- /dev/null +++ b/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/test/CodeGenCXX/captured-statements.cpp b/test/CodeGenCXX/captured-statements.cpp new file mode 100644 index 0000000000..1d5ab205f6 --- /dev/null +++ b/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/test/Sema/captured-statements.c b/test/Sema/captured-statements.c index 9285a7802d..86e9273944 100644 --- a/test/Sema/captured-statements.c +++ b/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)}} }(); }