]> granicus.if.org Git - clang/commitdiff
Implement "optimization" for lambda-to-block conversion which inlines the generated...
authorEli Friedman <eli.friedman@gmail.com>
Thu, 1 Mar 2012 04:01:32 +0000 (04:01 +0000)
committerEli Friedman <eli.friedman@gmail.com>
Thu, 1 Mar 2012 04:01:32 +0000 (04:01 +0000)
Note that this transformation has a substantial semantic effect outside of ARC: it gives the converted lambda lifetime semantics similar to a block literal.  With ARC, the effect is much less obvious because the lifetime of blocks is already managed.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@151797 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/Decl.h
include/clang/Sema/Sema.h
lib/CodeGen/CGBlocks.cpp
lib/CodeGen/CGClass.cpp
lib/CodeGen/CodeGenFunction.cpp
lib/CodeGen/CodeGenFunction.h
lib/Sema/SemaDeclCXX.cpp
lib/Sema/SemaExprCXX.cpp
lib/Sema/SemaLambda.cpp
test/CodeGenObjCXX/lambda-expressions.mm

index 1338657ff49764da57d954a543aa1473793eb36b..68fa374913d5257634aad8f6400d4a91924c0949 100644 (file)
@@ -3060,6 +3060,7 @@ private:
   bool IsVariadic : 1;
   bool CapturesCXXThis : 1;
   bool BlockMissingReturnType : 1;
+  bool IsConversionFromLambda : 1;
   /// ParamInfo - new[]'d array of pointers to ParmVarDecls for the formal
   /// parameters of this function.  This is null if a prototype or if there are
   /// no formals.
@@ -3076,7 +3077,7 @@ protected:
   BlockDecl(DeclContext *DC, SourceLocation CaretLoc)
     : Decl(Block, DC, CaretLoc), DeclContext(Block),
       IsVariadic(false), CapturesCXXThis(false),
-      BlockMissingReturnType(true),
+      BlockMissingReturnType(true), IsConversionFromLambda(false),
       ParamInfo(0), NumParams(0), Body(0),
       SignatureAsWritten(0), Captures(0), NumCaptures(0) {}
 
@@ -3138,6 +3139,9 @@ public:
   bool blockMissingReturnType() const { return BlockMissingReturnType; }
   void setBlockMissingReturnType(bool val) { BlockMissingReturnType = val; }
 
+  bool isConversionFromLambda() const { return IsConversionFromLambda; }
+  void setIsConversionFromLambda(bool val) { IsConversionFromLambda = val; }
+
   bool capturesVariable(const VarDecl *var) const;
 
   void setCaptures(ASTContext &Context,
index 967dc29c4d26dc578351a5b3410aa613a4aa2c93..7d9f835000023e742d2584514654e912239bbf6b 100644 (file)
@@ -3654,7 +3654,12 @@ public:
   /// block pointer conversion.
   void DefineImplicitLambdaToBlockPointerConversion(SourceLocation CurrentLoc,
                                                     CXXConversionDecl *Conv);
-  
+
+  ExprResult BuildBlockForLambdaConversion(SourceLocation CurrentLocation,
+                                           SourceLocation ConvLocation,
+                                           CXXConversionDecl *Conv,
+                                           Expr *Src);
+
   // ParseObjCStringLiteral - Parse Objective-C string literals.
   ExprResult ParseObjCStringLiteral(SourceLocation *AtLocs,
                                     Expr **Strings,
index f6aa9b4b48569e4b81843bad36159524a45a7b83..1b35fa0ee4de22e72542b078f73c0e99ebbe7eb5 100644 (file)
@@ -622,10 +622,11 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const BlockExpr *blockExpr) {
 
 llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) {
   // Using the computed layout, generate the actual block function.
+  bool isLambdaConv = blockInfo.getBlockDecl()->isConversionFromLambda();
   llvm::Constant *blockFn
     = CodeGenFunction(CGM).GenerateBlockFunction(CurGD, blockInfo,
                                                  CurFuncDecl, LocalDeclMap,
-                                                 InLambdaConversionToBlock);
+                                                 isLambdaConv);
   blockFn = llvm::ConstantExpr::getBitCast(blockFn, VoidPtrTy);
 
   // If there is nothing to capture, we can emit this as a global block.
@@ -700,11 +701,10 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) {
       src = Builder.CreateStructGEP(LoadBlockStruct(),
                                     enclosingCapture.getIndex(),
                                     "block.capture.addr");
-    } else if (InLambdaConversionToBlock) {
+    } else if (blockDecl->isConversionFromLambda()) {
       // The lambda capture in a lambda's conversion-to-block-pointer is
-      // special; we know its argument is an lvalue we can simply emit.
-      CXXConstructExpr *CE = cast<CXXConstructExpr>(ci->getCopyExpr());
-      src = EmitLValue(CE->getArg(0)).getAddress();
+      // special; we'll simply emit it directly.
+      src = 0;
     } else {
       // This is a [[type]]*.
       src = LocalDeclMap[variable];
@@ -726,7 +726,19 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) {
 
     // If we have a copy constructor, evaluate that into the block field.
     } else if (const Expr *copyExpr = ci->getCopyExpr()) {
-      EmitSynthesizedCXXCopyCtor(blockField, src, copyExpr);
+      if (blockDecl->isConversionFromLambda()) {
+        // If we have a lambda conversion, emit the expression
+        // directly into the block instead.
+        CharUnits Align = getContext().getTypeAlignInChars(type);
+        AggValueSlot Slot =
+            AggValueSlot::forAddr(blockField, Align, Qualifiers(),
+                                  AggValueSlot::IsDestructed,
+                                  AggValueSlot::DoesNotNeedGCBarriers,
+                                  AggValueSlot::IsNotAliased);
+        EmitAggExpr(copyExpr, Slot);
+      } else {
+        EmitSynthesizedCXXCopyCtor(blockField, src, copyExpr);
+      }
 
     // If it's a reference variable, copy the reference into the block field.
     } else if (type->isReferenceType()) {
index e3b5777387003a127bddc3feef6905ed519dfa08..6b5a1497109b9151998ddcf664b35c115b8daf0c 100644 (file)
@@ -1795,9 +1795,7 @@ void CodeGenFunction::EmitLambdaToBlockPointerBody(FunctionArgList &Args) {
     return;
   }
 
-  InLambdaConversionToBlock = true;
   EmitFunctionBody(Args);
-  InLambdaConversionToBlock = false;
 }
 
 void CodeGenFunction::EmitLambdaDelegatingInvokeBody(const CXXMethodDecl *MD) {
index 5d18169695b8ca34b338831715197b4df629e3a6..47176bd8ecc39ca916725aa637c4ce3d256b7c9a 100644 (file)
@@ -32,8 +32,7 @@ CodeGenFunction::CodeGenFunction(CodeGenModule &cgm)
     Target(CGM.getContext().getTargetInfo()),
     Builder(cgm.getModule().getContext()),
     AutoreleaseResult(false), BlockInfo(0), BlockPointer(0),
-    LambdaThisCaptureField(0), InLambdaConversionToBlock(false),
-    NormalCleanupDest(0), NextCleanupDestIndex(1),
+    LambdaThisCaptureField(0), NormalCleanupDest(0), NextCleanupDestIndex(1),
     FirstBlockInfo(0), EHResumeBlock(0), ExceptionSlot(0), EHSelectorSlot(0),
     DebugInfo(0), DisableDebugInfo(false), DidCallStackSave(false),
     IndirectBranch(0), SwitchInsn(0), CaseRangeBlock(0), UnreachableBlock(0),
index 42c9b7dc9b2d60731307bf9f148f46f5b651ba26..7f39868dd91ed2fc98f8143769ad36e2f8c4afb7 100644 (file)
@@ -601,7 +601,6 @@ public:
 
   llvm::DenseMap<const VarDecl *, FieldDecl *> LambdaCaptureFields;
   FieldDecl *LambdaThisCaptureField;
-  bool InLambdaConversionToBlock;
 
   /// \brief A mapping from NRVO variables to the flags used to indicate
   /// when the NRVO has been applied to this variable.
index 73df1c9457e30660b2c9cad92591ec78b58d6c6b..4f0e1609509ebc50be5ce2ec74d9443bddb76f80 100644 (file)
@@ -8825,15 +8825,6 @@ void Sema::DefineImplicitLambdaToBlockPointerConversion(
        SourceLocation CurrentLocation,
        CXXConversionDecl *Conv) 
 {
-  CXXRecordDecl *Lambda = Conv->getParent();
-  
-  // Make sure that the lambda call operator is marked used.
-  CXXMethodDecl *CallOperator 
-    = cast<CXXMethodDecl>(
-        *Lambda->lookup(
-          Context.DeclarationNames.getCXXOperatorName(OO_Call)).first);
-  CallOperator->setReferenced();
-  CallOperator->setUsed();
   Conv->setUsed();
   
   ImplicitlyDefinedFunctionScope Scope(*this, Conv);
@@ -8842,79 +8833,29 @@ void Sema::DefineImplicitLambdaToBlockPointerConversion(
   // Copy-initialize the lambda object as needed to capture it.
   Expr *This = ActOnCXXThis(CurrentLocation).take();
   Expr *DerefThis =CreateBuiltinUnaryOp(CurrentLocation, UO_Deref, This).take();
-  ExprResult Init = PerformCopyInitialization(
-                      InitializedEntity::InitializeBlock(CurrentLocation, 
-                                                         DerefThis->getType(), 
-                                                         /*NRVO=*/false),
-                      CurrentLocation, DerefThis);
-  if (!Init.isInvalid())
-    Init = ActOnFinishFullExpr(Init.take());
   
-  if (Init.isInvalid()) {
+  ExprResult BuildBlock = BuildBlockForLambdaConversion(CurrentLocation,
+                                                        Conv->getLocation(),
+                                                        Conv, DerefThis);
+
+  // If we're not under ARC, make sure we still get the _Block_copy/autorelease
+  // behavior.  Note that only the general conversion function does this
+  // (since it's unusable otherwise); in the case where we inline the
+  // block literal, it has block literal lifetime semantics.
+  if (!BuildBlock.isInvalid() && !getLangOptions().ObjCAutoRefCount)
+    BuildBlock = ImplicitCastExpr::Create(Context, BuildBlock.get()->getType(),
+                                          CK_CopyAndAutoreleaseBlockObject,
+                                          BuildBlock.get(), 0, VK_RValue);
+
+  if (BuildBlock.isInvalid()) {
     Diag(CurrentLocation, diag::note_lambda_to_block_conv);
     Conv->setInvalidDecl();
     return;
   }
-  
-  // Create the new block to be returned.
-  BlockDecl *Block = BlockDecl::Create(Context, Conv, Conv->getLocation());
-  
-  // Set the type information.
-  Block->setSignatureAsWritten(CallOperator->getTypeSourceInfo());
-  Block->setIsVariadic(CallOperator->isVariadic());
-  Block->setBlockMissingReturnType(false);
-  
-  // Add parameters.
-  SmallVector<ParmVarDecl *, 4> BlockParams;
-  for (unsigned I = 0, N = CallOperator->getNumParams(); I != N; ++I) {
-    ParmVarDecl *From = CallOperator->getParamDecl(I);
-    BlockParams.push_back(ParmVarDecl::Create(Context, Block,
-                                              From->getLocStart(),
-                                              From->getLocation(),
-                                              From->getIdentifier(),
-                                              From->getType(),
-                                              From->getTypeSourceInfo(),
-                                              From->getStorageClass(),
-                                            From->getStorageClassAsWritten(),
-                                              /*DefaultArg=*/0));
-  }
-  Block->setParams(BlockParams);
-  
-  // Add capture. The capture uses a fake variable, which doesn't correspond
-  // to any actual memory location. However, the initializer copy-initializes
-  // the lambda object.
-  TypeSourceInfo *CapVarTSI =
-      Context.getTrivialTypeSourceInfo(DerefThis->getType());
-  VarDecl *CapVar = VarDecl::Create(Context, Block, Conv->getLocation(),
-                                    Conv->getLocation(), 0,
-                                    DerefThis->getType(), CapVarTSI,
-                                    SC_None, SC_None);
-  BlockDecl::Capture Capture(/*Variable=*/CapVar, /*ByRef=*/false,
-                             /*Nested=*/false, /*Copy=*/Init.take());
-  Block->setCaptures(Context, &Capture, &Capture + 1, 
-                     /*CapturesCXXThis=*/false);
-  
-  // Add a fake function body to the block. IR generation is responsible
-  // for filling in the actual body, which cannot be expressed as an AST.
-  Block->setBody(new (Context) CompoundStmt(Context, 0, 0, 
-                                            Conv->getLocation(),
-                                            Conv->getLocation()));
-
-  // Create the block literal expression.
-  Expr *BuildBlock = new (Context) BlockExpr(Block, Conv->getConversionType());
-  ExprCleanupObjects.push_back(Block);
-  ExprNeedsCleanups = true;
 
-  // If we're not under ARC, make sure we still get the _Block_copy/autorelease
-  // behavior.
-  if (!getLangOptions().ObjCAutoRefCount)
-    BuildBlock = ImplicitCastExpr::Create(Context, BuildBlock->getType(),
-                                          CK_CopyAndAutoreleaseBlockObject,
-                                          BuildBlock, 0, VK_RValue);
-  
   // Create the return statement that returns the block from the conversion
   // function.
-  StmtResult Return = ActOnReturnStmt(Conv->getLocation(), BuildBlock);
+  StmtResult Return = ActOnReturnStmt(Conv->getLocation(), BuildBlock.get());
   if (Return.isInvalid()) {
     Diag(CurrentLocation, diag::note_lambda_to_block_conv);
     Conv->setInvalidDecl();
index 84afc032c0b49afbfbdd004294a3c53627d60eec..1893d6dfa1069887532f4230ccca3af251a02f9e 100644 (file)
@@ -4460,11 +4460,8 @@ ExprResult Sema::MaybeBindToTemporary(Expr *E) {
     // For message sends and property references, we try to find an
     // actual method.  FIXME: we should infer retention by selector in
     // cases where we don't have an actual method.
-    } else {
-      ObjCMethodDecl *D = 0;
-      if (ObjCMessageExpr *Send = dyn_cast<ObjCMessageExpr>(E)) {
-        D = Send->getMethodDecl();
-      }
+    } else if (ObjCMessageExpr *Send = dyn_cast<ObjCMessageExpr>(E)) {
+      ObjCMethodDecl *D = Send->getMethodDecl();
 
       ReturnsRetained = (D && D->hasAttr<NSReturnsRetainedAttr>());
 
@@ -4474,6 +4471,13 @@ ExprResult Sema::MaybeBindToTemporary(Expr *E) {
       if (!ReturnsRetained &&
           D && D->getMethodFamily() == OMF_performSelector)
         return Owned(E);
+    } else if (isa<CastExpr>(E) &&
+               isa<BlockExpr>(cast<CastExpr>(E)->getSubExpr())) {
+      // We hit this case with the lambda conversion-to-block optimization;
+      // we don't want any extra casts here.
+      return Owned(E);
+    } else {
+      ReturnsRetained = false;
     }
 
     // Don't reclaim an object of Class type.
@@ -5089,6 +5093,34 @@ ExprResult Sema::ActOnPseudoDestructorExpr(Scope *S, Expr *Base,
 ExprResult Sema::BuildCXXMemberCallExpr(Expr *E, NamedDecl *FoundDecl,
                                         CXXConversionDecl *Method,
                                         bool HadMultipleCandidates) {
+  if (Method->getParent()->isLambda() &&
+      Method->getConversionType()->isBlockPointerType()) {
+    // This is a lambda coversion to block pointer; check if the argument
+    // is a LambdaExpr.
+    Expr *SubE = E;
+    CastExpr *CE = dyn_cast<CastExpr>(SubE);
+    if (CE && CE->getCastKind() == CK_NoOp)
+      SubE = CE->getSubExpr();
+    SubE = SubE->IgnoreParens();
+    if (CXXBindTemporaryExpr *BE = dyn_cast<CXXBindTemporaryExpr>(SubE))
+      SubE = BE->getSubExpr();
+    if (isa<LambdaExpr>(SubE)) {
+      // For the conversion to block pointer on a lambda expression, we
+      // construct a special BlockLiteral instead; this doesn't really make
+      // a difference in ARC, but outside of ARC the resulting block literal
+      // follows the normal lifetime rules for block literals instead of being
+      // autoreleased.
+      DiagnosticErrorTrap Trap(Diags);
+      ExprResult Exp = BuildBlockForLambdaConversion(E->getExprLoc(),
+                                                     E->getExprLoc(),
+                                                     Method, E);
+      if (Exp.isInvalid())
+        Diag(E->getExprLoc(), diag::note_lambda_to_block_conv);
+      return Exp;
+    }
+  }
+      
+
   ExprResult Exp = PerformObjectArgumentInitialization(E, /*Qualifier=*/0,
                                           FoundDecl, Method);
   if (Exp.isInvalid())
index b0c6a61bce9f07f250acf642317c8c5d2f7c0f0f..3611350de0eb6dd696634b50cad6245030605bea 100644 (file)
@@ -740,3 +740,81 @@ ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body,
   
   return MaybeBindToTemporary(Lambda);
 }
+
+ExprResult Sema::BuildBlockForLambdaConversion(SourceLocation CurrentLocation,
+                                               SourceLocation ConvLocation,
+                                               CXXConversionDecl *Conv,
+                                               Expr *Src) {
+  // Make sure that the lambda call operator is marked used.
+  CXXRecordDecl *Lambda = Conv->getParent();
+  CXXMethodDecl *CallOperator 
+    = cast<CXXMethodDecl>(
+        *Lambda->lookup(
+          Context.DeclarationNames.getCXXOperatorName(OO_Call)).first);
+  CallOperator->setReferenced();
+  CallOperator->setUsed();
+
+  ExprResult Init = PerformCopyInitialization(
+                      InitializedEntity::InitializeBlock(ConvLocation, 
+                                                         Src->getType(), 
+                                                         /*NRVO=*/false),
+                      CurrentLocation, Src);
+  if (!Init.isInvalid())
+    Init = ActOnFinishFullExpr(Init.take());
+  
+  if (Init.isInvalid())
+    return ExprError();
+  
+  // Create the new block to be returned.
+  BlockDecl *Block = BlockDecl::Create(Context, CurContext, ConvLocation);
+
+  // Set the type information.
+  Block->setSignatureAsWritten(CallOperator->getTypeSourceInfo());
+  Block->setIsVariadic(CallOperator->isVariadic());
+  Block->setBlockMissingReturnType(false);
+
+  // Add parameters.
+  SmallVector<ParmVarDecl *, 4> BlockParams;
+  for (unsigned I = 0, N = CallOperator->getNumParams(); I != N; ++I) {
+    ParmVarDecl *From = CallOperator->getParamDecl(I);
+    BlockParams.push_back(ParmVarDecl::Create(Context, Block,
+                                              From->getLocStart(),
+                                              From->getLocation(),
+                                              From->getIdentifier(),
+                                              From->getType(),
+                                              From->getTypeSourceInfo(),
+                                              From->getStorageClass(),
+                                            From->getStorageClassAsWritten(),
+                                              /*DefaultArg=*/0));
+  }
+  Block->setParams(BlockParams);
+
+  Block->setIsConversionFromLambda(true);
+
+  // Add capture. The capture uses a fake variable, which doesn't correspond
+  // to any actual memory location. However, the initializer copy-initializes
+  // the lambda object.
+  TypeSourceInfo *CapVarTSI =
+      Context.getTrivialTypeSourceInfo(Src->getType());
+  VarDecl *CapVar = VarDecl::Create(Context, Block, ConvLocation,
+                                    ConvLocation, 0,
+                                    Src->getType(), CapVarTSI,
+                                    SC_None, SC_None);
+  BlockDecl::Capture Capture(/*Variable=*/CapVar, /*ByRef=*/false,
+                             /*Nested=*/false, /*Copy=*/Init.take());
+  Block->setCaptures(Context, &Capture, &Capture + 1, 
+                     /*CapturesCXXThis=*/false);
+
+  // Add a fake function body to the block. IR generation is responsible
+  // for filling in the actual body, which cannot be expressed as an AST.
+  Block->setBody(new (Context) CompoundStmt(Context, 0, 0, 
+                                            ConvLocation,
+                                            ConvLocation));
+
+  // Create the block literal expression.
+  Expr *BuildBlock = new (Context) BlockExpr(Block, Conv->getConversionType());
+  ExprCleanupObjects.push_back(Block);
+  ExprNeedsCleanups = true;
+
+  return BuildBlock;
+}
index c55699a2319c38abf44e3a765a16c449f957f310..36a1996ee87d1dd1f568eea5c05589d1e632f581 100644 (file)
@@ -4,6 +4,7 @@
 typedef int (^fp)();
 fp f() { auto x = []{ return 3; }; return x; }
 
+// MRC: define i32 ()* @_Z1fv(
 // MRC: define internal i32 ()* @"_ZZ1fvENK3$_0cvU13block_pointerFivEEv"
 // MRC: store i8* bitcast (i8** @_NSConcreteStackBlock to i8*)
 // MRC: store i8* bitcast (i32 (i8*)* @"___ZZ1fvENK3$_0cvU13block_pointerFivEEv_block_invoke_0" to i8*)
@@ -11,8 +12,26 @@ fp f() { auto x = []{ return 3; }; return x; }
 // MRC: call i32 ()* (i8*, i8*)* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to i32 ()* (i8*, i8*)*)
 // MRC: ret i32 ()*
 
+// ARC: define i32 ()* @_Z1fv(
 // ARC: define internal i32 ()* @"_ZZ1fvENK3$_0cvU13block_pointerFivEEv"
 // ARC: store i8* bitcast (i8** @_NSConcreteStackBlock to i8*)
 // ARC: store i8* bitcast (i32 (i8*)* @"___ZZ1fvENK3$_0cvU13block_pointerFivEEv_block_invoke_0" to i8*)
 // ARC: call i8* @objc_retainBlock
 // ARC: call i8* @objc_autoreleaseReturnValue
+
+typedef int (^fp)();
+fp global;
+void f2() { global = []{ return 3; }; }
+
+// MRC: define void @_Z2f2v() nounwind {
+// MRC: store i8* bitcast (i32 (i8*)* @__f2_block_invoke_0 to i8*),
+// MRC-NOT: call
+// MRC: ret void
+// ("global" contains a dangling pointer after this function runs.)
+
+// ARC: define void @_Z2f2v() nounwind {
+// ARC: store i8* bitcast (i32 (i8*)* @__f2_block_invoke_0 to i8*),
+// ARC: call i8* @objc_retainBlock
+// ARC: call void @objc_release
+// ARC: define internal i32 @__f2_block_invoke_0
+// ARC: call i32 @"_ZZ2f2vENK3$_1clEv