r181536 - CodeGen for CapturedStmts

Ben Langmuir ben.langmuir at intel.com
Thu May 9 12:17:12 PDT 2013


Author: benlangmuir
Date: Thu May  9 14:17:11 2013
New Revision: 181536

URL: http://llvm.org/viewvc/llvm-project?rev=181536&view=rev
Log:
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

Added:
    cfe/trunk/test/CodeGen/captured-statements-nested.c
    cfe/trunk/test/CodeGen/captured-statements.c
    cfe/trunk/test/CodeGenCXX/captured-statements.cpp
Modified:
    cfe/trunk/include/clang/AST/GlobalDecl.h
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/lib/AST/ItaniumMangle.cpp
    cfe/trunk/lib/AST/MicrosoftMangle.cpp
    cfe/trunk/lib/CodeGen/CGExpr.cpp
    cfe/trunk/lib/CodeGen/CGStmt.cpp
    cfe/trunk/lib/CodeGen/CodeGenFunction.cpp
    cfe/trunk/lib/CodeGen/CodeGenFunction.h
    cfe/trunk/lib/Sema/SemaExpr.cpp
    cfe/trunk/test/Sema/captured-statements.c

Modified: cfe/trunk/include/clang/AST/GlobalDecl.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/GlobalDecl.h?rev=181536&r1=181535&r2=181536&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/GlobalDecl.h (original)
+++ cfe/trunk/include/clang/AST/GlobalDecl.h Thu May  9 14:17:11 2013
@@ -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)

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=181536&r1=181535&r2=181536&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Thu May  9 14:17:11 2013
@@ -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">;

Modified: cfe/trunk/lib/AST/ItaniumMangle.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ItaniumMangle.cpp?rev=181536&r1=181535&r2=181536&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ItaniumMangle.cpp (original)
+++ cfe/trunk/lib/AST/ItaniumMangle.cpp Thu May  9 14:17:11 2013
@@ -1419,6 +1419,10 @@ void CXXNameMangler::manglePrefix(const
     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);  

Modified: cfe/trunk/lib/AST/MicrosoftMangle.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/MicrosoftMangle.cpp?rev=181536&r1=181535&r2=181536&view=diff
==============================================================================
--- cfe/trunk/lib/AST/MicrosoftMangle.cpp (original)
+++ cfe/trunk/lib/AST/MicrosoftMangle.cpp Thu May  9 14:17:11 2013
@@ -541,6 +541,10 @@ void MicrosoftCXXNameMangler::manglePost
     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)))

Modified: cfe/trunk/lib/CodeGen/CGExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGExpr.cpp?rev=181536&r1=181535&r2=181536&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGExpr.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGExpr.cpp Thu May  9 14:17:11 2013
@@ -1793,6 +1793,13 @@ static LValue EmitFunctionDeclLValue(Cod
   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::EmitDeclRefLValu
     // 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());

Modified: cfe/trunk/lib/CodeGen/CGStmt.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGStmt.cpp?rev=181536&r1=181535&r2=181536&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGStmt.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGStmt.cpp Thu May  9 14:17:11 2013
@@ -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 Stm
   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
   }
 }
 
-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;
 }

Modified: cfe/trunk/lib/CodeGen/CodeGenFunction.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenFunction.cpp?rev=181536&r1=181535&r2=181536&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CodeGenFunction.cpp (original)
+++ cfe/trunk/lib/CodeGen/CodeGenFunction.cpp Thu May  9 14:17:11 2013
@@ -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::EmitFieldA
 
   return V;
 }
+
+CodeGenFunction::CGCapturedStmtInfo::~CGCapturedStmtInfo() { }

Modified: cfe/trunk/lib/CodeGen/CodeGenFunction.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenFunction.h?rev=181536&r1=181535&r2=181536&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CodeGenFunction.h (original)
+++ cfe/trunk/lib/CodeGen/CodeGenFunction.h Thu May  9 14:17:11 2013
@@ -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
   //===--------------------------------------------------------------------===//

Modified: cfe/trunk/lib/Sema/SemaExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=181536&r1=181535&r2=181536&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExpr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExpr.cpp Thu May  9 14:17:11 2013
@@ -11212,12 +11212,12 @@ bool Sema::tryCaptureVariable(VarDecl *V
         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();
       }

Added: cfe/trunk/test/CodeGen/captured-statements-nested.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/captured-statements-nested.c?rev=181536&view=auto
==============================================================================
--- cfe/trunk/test/CodeGen/captured-statements-nested.c (added)
+++ cfe/trunk/test/CodeGen/captured-statements-nested.c Thu May  9 14:17:11 2013
@@ -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*
+}

Added: cfe/trunk/test/CodeGen/captured-statements.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/captured-statements.c?rev=181536&view=auto
==============================================================================
--- cfe/trunk/test/CodeGen/captured-statements.c (added)
+++ cfe/trunk/test/CodeGen/captured-statements.c Thu May  9 14:17:11 2013
@@ -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

Added: cfe/trunk/test/CodeGenCXX/captured-statements.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/captured-statements.cpp?rev=181536&view=auto
==============================================================================
--- cfe/trunk/test/CodeGenCXX/captured-statements.cpp (added)
+++ cfe/trunk/test/CodeGenCXX/captured-statements.cpp Thu May  9 14:17:11 2013
@@ -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*
+}

Modified: cfe/trunk/test/Sema/captured-statements.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/captured-statements.c?rev=181536&r1=181535&r2=181536&view=diff
==============================================================================
--- cfe/trunk/test/Sema/captured-statements.c (original)
+++ cfe/trunk/test/Sema/captured-statements.c Thu May  9 14:17:11 2013
@@ -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)}}
     }();
   }





More information about the cfe-commits mailing list