]> granicus.if.org Git - clang/commitdiff
CodeGen for CapturedStmts
authorBen Langmuir <ben.langmuir@intel.com>
Thu, 9 May 2013 19:17:11 +0000 (19:17 +0000)
committerBen Langmuir <ben.langmuir@intel.com>
Thu, 9 May 2013 19:17:11 +0000 (19:17 +0000)
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

13 files changed:
include/clang/AST/GlobalDecl.h
include/clang/Basic/DiagnosticSemaKinds.td
lib/AST/ItaniumMangle.cpp
lib/AST/MicrosoftMangle.cpp
lib/CodeGen/CGExpr.cpp
lib/CodeGen/CGStmt.cpp
lib/CodeGen/CodeGenFunction.cpp
lib/CodeGen/CodeGenFunction.h
lib/Sema/SemaExpr.cpp
test/CodeGen/captured-statements-nested.c [new file with mode: 0644]
test/CodeGen/captured-statements.c [new file with mode: 0644]
test/CodeGenCXX/captured-statements.cpp [new file with mode: 0644]
test/Sema/captured-statements.c

index c43e44c26f3155a2b1c5d68b5a5b117b2ceb7608..54c9d88c9b2ee21d1bbac089105b84a9b8a76e20 100644 (file)
@@ -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)
index 0c308469fc7eafcbe63e3d7b3ca4165004830fec..11fd91ab5bddad811435806e6adb3e587af0a129 100644 (file)
@@ -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">;
index 5ad8021fac92ba6a7290a14b1af35cce4773f819..d28aced172ff88cfcfdc6053aa8580a5ffd6b080 100644 (file)
@@ -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);  
index 1785063d7b1a9a041cb41e23c17dafc6e9e39a45..7c797eef4d5f39925db9dd62146244a19418a305 100644 (file)
@@ -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)))
index 64670c5e81e45611733a4679c2e770c5587fc01b..8ba5a1ce5223a68f7431203b588b7a353bd2a754 100644 (file)
@@ -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());
index 208455c88c7da6964a8e0c52bb427ecffc0b7a5f..353d6c3d396721bda9aaf1a76c70d763f6e692dc 100644 (file)
@@ -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;
 }
index 75c60edbba531e0754eda5217a6bb328ab42c14d..071c08e6bf2e6c6e2ef4adddb1f99d218da938f5 100644 (file)
@@ -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() { }
index ff74c15c38c3a59af7a5d66efdcb7b2deee75ec3..6caf1689a9daeab9e33d6cc1d95feab94c95694b 100644 (file)
@@ -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
   //===--------------------------------------------------------------------===//
index dd05b822364d518c61d6ae29f411445560d145f6..19ed4bcc0fb7733d25fd2f21e3c605c40259a8a3 100644 (file)
@@ -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/test/CodeGen/captured-statements-nested.c b/test/CodeGen/captured-statements-nested.c
new file mode 100644 (file)
index 0000000..d8ec746
--- /dev/null
@@ -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 (file)
index 0000000..c87c187
--- /dev/null
@@ -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 (file)
index 0000000..1d5ab20
--- /dev/null
@@ -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*
+}
index 9285a7802d502ca765a491afe9ea38634c42116c..86e9273944bf30cc1e8c0dce6782ac908ffe8a18 100644 (file)
@@ -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)}}
     }();
   }