Skip to content
This repository was archived by the owner on Nov 1, 2021. It is now read-only.

Commit 23f0267

Browse files
committed
Implement "optimization" for lambda-to-block conversion which inlines the generated block literal for lambdas which are immediately converted to block pointer type. This simplifies the AST, avoids an unnecessary copy of the lambda and makes it much easier to avoid copying the result onto the heap.
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
1 parent 5e4e58b commit 23f0267

File tree

10 files changed

+179
-92
lines changed

10 files changed

+179
-92
lines changed

Diff for: include/clang/AST/Decl.h

+5-1
Original file line numberDiff line numberDiff line change
@@ -3060,6 +3060,7 @@ class BlockDecl : public Decl, public DeclContext {
30603060
bool IsVariadic : 1;
30613061
bool CapturesCXXThis : 1;
30623062
bool BlockMissingReturnType : 1;
3063+
bool IsConversionFromLambda : 1;
30633064
/// ParamInfo - new[]'d array of pointers to ParmVarDecls for the formal
30643065
/// parameters of this function. This is null if a prototype or if there are
30653066
/// no formals.
@@ -3076,7 +3077,7 @@ class BlockDecl : public Decl, public DeclContext {
30763077
BlockDecl(DeclContext *DC, SourceLocation CaretLoc)
30773078
: Decl(Block, DC, CaretLoc), DeclContext(Block),
30783079
IsVariadic(false), CapturesCXXThis(false),
3079-
BlockMissingReturnType(true),
3080+
BlockMissingReturnType(true), IsConversionFromLambda(false),
30803081
ParamInfo(0), NumParams(0), Body(0),
30813082
SignatureAsWritten(0), Captures(0), NumCaptures(0) {}
30823083

@@ -3138,6 +3139,9 @@ class BlockDecl : public Decl, public DeclContext {
31383139
bool blockMissingReturnType() const { return BlockMissingReturnType; }
31393140
void setBlockMissingReturnType(bool val) { BlockMissingReturnType = val; }
31403141

3142+
bool isConversionFromLambda() const { return IsConversionFromLambda; }
3143+
void setIsConversionFromLambda(bool val) { IsConversionFromLambda = val; }
3144+
31413145
bool capturesVariable(const VarDecl *var) const;
31423146

31433147
void setCaptures(ASTContext &Context,

Diff for: include/clang/Sema/Sema.h

+6-1
Original file line numberDiff line numberDiff line change
@@ -3654,7 +3654,12 @@ class Sema {
36543654
/// block pointer conversion.
36553655
void DefineImplicitLambdaToBlockPointerConversion(SourceLocation CurrentLoc,
36563656
CXXConversionDecl *Conv);
3657-
3657+
3658+
ExprResult BuildBlockForLambdaConversion(SourceLocation CurrentLocation,
3659+
SourceLocation ConvLocation,
3660+
CXXConversionDecl *Conv,
3661+
Expr *Src);
3662+
36583663
// ParseObjCStringLiteral - Parse Objective-C string literals.
36593664
ExprResult ParseObjCStringLiteral(SourceLocation *AtLocs,
36603665
Expr **Strings,

Diff for: lib/CodeGen/CGBlocks.cpp

+18-6
Original file line numberDiff line numberDiff line change
@@ -622,10 +622,11 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const BlockExpr *blockExpr) {
622622

623623
llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) {
624624
// Using the computed layout, generate the actual block function.
625+
bool isLambdaConv = blockInfo.getBlockDecl()->isConversionFromLambda();
625626
llvm::Constant *blockFn
626627
= CodeGenFunction(CGM).GenerateBlockFunction(CurGD, blockInfo,
627628
CurFuncDecl, LocalDeclMap,
628-
InLambdaConversionToBlock);
629+
isLambdaConv);
629630
blockFn = llvm::ConstantExpr::getBitCast(blockFn, VoidPtrTy);
630631

631632
// 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) {
700701
src = Builder.CreateStructGEP(LoadBlockStruct(),
701702
enclosingCapture.getIndex(),
702703
"block.capture.addr");
703-
} else if (InLambdaConversionToBlock) {
704+
} else if (blockDecl->isConversionFromLambda()) {
704705
// The lambda capture in a lambda's conversion-to-block-pointer is
705-
// special; we know its argument is an lvalue we can simply emit.
706-
CXXConstructExpr *CE = cast<CXXConstructExpr>(ci->getCopyExpr());
707-
src = EmitLValue(CE->getArg(0)).getAddress();
706+
// special; we'll simply emit it directly.
707+
src = 0;
708708
} else {
709709
// This is a [[type]]*.
710710
src = LocalDeclMap[variable];
@@ -726,7 +726,19 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) {
726726

727727
// If we have a copy constructor, evaluate that into the block field.
728728
} else if (const Expr *copyExpr = ci->getCopyExpr()) {
729-
EmitSynthesizedCXXCopyCtor(blockField, src, copyExpr);
729+
if (blockDecl->isConversionFromLambda()) {
730+
// If we have a lambda conversion, emit the expression
731+
// directly into the block instead.
732+
CharUnits Align = getContext().getTypeAlignInChars(type);
733+
AggValueSlot Slot =
734+
AggValueSlot::forAddr(blockField, Align, Qualifiers(),
735+
AggValueSlot::IsDestructed,
736+
AggValueSlot::DoesNotNeedGCBarriers,
737+
AggValueSlot::IsNotAliased);
738+
EmitAggExpr(copyExpr, Slot);
739+
} else {
740+
EmitSynthesizedCXXCopyCtor(blockField, src, copyExpr);
741+
}
730742

731743
// If it's a reference variable, copy the reference into the block field.
732744
} else if (type->isReferenceType()) {

Diff for: lib/CodeGen/CGClass.cpp

-2
Original file line numberDiff line numberDiff line change
@@ -1795,9 +1795,7 @@ void CodeGenFunction::EmitLambdaToBlockPointerBody(FunctionArgList &Args) {
17951795
return;
17961796
}
17971797

1798-
InLambdaConversionToBlock = true;
17991798
EmitFunctionBody(Args);
1800-
InLambdaConversionToBlock = false;
18011799
}
18021800

18031801
void CodeGenFunction::EmitLambdaDelegatingInvokeBody(const CXXMethodDecl *MD) {

Diff for: lib/CodeGen/CodeGenFunction.cpp

+1-2
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,7 @@ CodeGenFunction::CodeGenFunction(CodeGenModule &cgm)
3232
Target(CGM.getContext().getTargetInfo()),
3333
Builder(cgm.getModule().getContext()),
3434
AutoreleaseResult(false), BlockInfo(0), BlockPointer(0),
35-
LambdaThisCaptureField(0), InLambdaConversionToBlock(false),
36-
NormalCleanupDest(0), NextCleanupDestIndex(1),
35+
LambdaThisCaptureField(0), NormalCleanupDest(0), NextCleanupDestIndex(1),
3736
FirstBlockInfo(0), EHResumeBlock(0), ExceptionSlot(0), EHSelectorSlot(0),
3837
DebugInfo(0), DisableDebugInfo(false), DidCallStackSave(false),
3938
IndirectBranch(0), SwitchInsn(0), CaseRangeBlock(0), UnreachableBlock(0),

Diff for: lib/CodeGen/CodeGenFunction.h

-1
Original file line numberDiff line numberDiff line change
@@ -601,7 +601,6 @@ class CodeGenFunction : public CodeGenTypeCache {
601601

602602
llvm::DenseMap<const VarDecl *, FieldDecl *> LambdaCaptureFields;
603603
FieldDecl *LambdaThisCaptureField;
604-
bool InLambdaConversionToBlock;
605604

606605
/// \brief A mapping from NRVO variables to the flags used to indicate
607606
/// when the NRVO has been applied to this variable.

Diff for: lib/Sema/SemaDeclCXX.cpp

+15-74
Original file line numberDiff line numberDiff line change
@@ -8825,15 +8825,6 @@ void Sema::DefineImplicitLambdaToBlockPointerConversion(
88258825
SourceLocation CurrentLocation,
88268826
CXXConversionDecl *Conv)
88278827
{
8828-
CXXRecordDecl *Lambda = Conv->getParent();
8829-
8830-
// Make sure that the lambda call operator is marked used.
8831-
CXXMethodDecl *CallOperator
8832-
= cast<CXXMethodDecl>(
8833-
*Lambda->lookup(
8834-
Context.DeclarationNames.getCXXOperatorName(OO_Call)).first);
8835-
CallOperator->setReferenced();
8836-
CallOperator->setUsed();
88378828
Conv->setUsed();
88388829

88398830
ImplicitlyDefinedFunctionScope Scope(*this, Conv);
@@ -8842,79 +8833,29 @@ void Sema::DefineImplicitLambdaToBlockPointerConversion(
88428833
// Copy-initialize the lambda object as needed to capture it.
88438834
Expr *This = ActOnCXXThis(CurrentLocation).take();
88448835
Expr *DerefThis =CreateBuiltinUnaryOp(CurrentLocation, UO_Deref, This).take();
8845-
ExprResult Init = PerformCopyInitialization(
8846-
InitializedEntity::InitializeBlock(CurrentLocation,
8847-
DerefThis->getType(),
8848-
/*NRVO=*/false),
8849-
CurrentLocation, DerefThis);
8850-
if (!Init.isInvalid())
8851-
Init = ActOnFinishFullExpr(Init.take());
88528836

8853-
if (Init.isInvalid()) {
8837+
ExprResult BuildBlock = BuildBlockForLambdaConversion(CurrentLocation,
8838+
Conv->getLocation(),
8839+
Conv, DerefThis);
8840+
8841+
// If we're not under ARC, make sure we still get the _Block_copy/autorelease
8842+
// behavior. Note that only the general conversion function does this
8843+
// (since it's unusable otherwise); in the case where we inline the
8844+
// block literal, it has block literal lifetime semantics.
8845+
if (!BuildBlock.isInvalid() && !getLangOptions().ObjCAutoRefCount)
8846+
BuildBlock = ImplicitCastExpr::Create(Context, BuildBlock.get()->getType(),
8847+
CK_CopyAndAutoreleaseBlockObject,
8848+
BuildBlock.get(), 0, VK_RValue);
8849+
8850+
if (BuildBlock.isInvalid()) {
88548851
Diag(CurrentLocation, diag::note_lambda_to_block_conv);
88558852
Conv->setInvalidDecl();
88568853
return;
88578854
}
8858-
8859-
// Create the new block to be returned.
8860-
BlockDecl *Block = BlockDecl::Create(Context, Conv, Conv->getLocation());
8861-
8862-
// Set the type information.
8863-
Block->setSignatureAsWritten(CallOperator->getTypeSourceInfo());
8864-
Block->setIsVariadic(CallOperator->isVariadic());
8865-
Block->setBlockMissingReturnType(false);
8866-
8867-
// Add parameters.
8868-
SmallVector<ParmVarDecl *, 4> BlockParams;
8869-
for (unsigned I = 0, N = CallOperator->getNumParams(); I != N; ++I) {
8870-
ParmVarDecl *From = CallOperator->getParamDecl(I);
8871-
BlockParams.push_back(ParmVarDecl::Create(Context, Block,
8872-
From->getLocStart(),
8873-
From->getLocation(),
8874-
From->getIdentifier(),
8875-
From->getType(),
8876-
From->getTypeSourceInfo(),
8877-
From->getStorageClass(),
8878-
From->getStorageClassAsWritten(),
8879-
/*DefaultArg=*/0));
8880-
}
8881-
Block->setParams(BlockParams);
8882-
8883-
// Add capture. The capture uses a fake variable, which doesn't correspond
8884-
// to any actual memory location. However, the initializer copy-initializes
8885-
// the lambda object.
8886-
TypeSourceInfo *CapVarTSI =
8887-
Context.getTrivialTypeSourceInfo(DerefThis->getType());
8888-
VarDecl *CapVar = VarDecl::Create(Context, Block, Conv->getLocation(),
8889-
Conv->getLocation(), 0,
8890-
DerefThis->getType(), CapVarTSI,
8891-
SC_None, SC_None);
8892-
BlockDecl::Capture Capture(/*Variable=*/CapVar, /*ByRef=*/false,
8893-
/*Nested=*/false, /*Copy=*/Init.take());
8894-
Block->setCaptures(Context, &Capture, &Capture + 1,
8895-
/*CapturesCXXThis=*/false);
8896-
8897-
// Add a fake function body to the block. IR generation is responsible
8898-
// for filling in the actual body, which cannot be expressed as an AST.
8899-
Block->setBody(new (Context) CompoundStmt(Context, 0, 0,
8900-
Conv->getLocation(),
8901-
Conv->getLocation()));
8902-
8903-
// Create the block literal expression.
8904-
Expr *BuildBlock = new (Context) BlockExpr(Block, Conv->getConversionType());
8905-
ExprCleanupObjects.push_back(Block);
8906-
ExprNeedsCleanups = true;
89078855

8908-
// If we're not under ARC, make sure we still get the _Block_copy/autorelease
8909-
// behavior.
8910-
if (!getLangOptions().ObjCAutoRefCount)
8911-
BuildBlock = ImplicitCastExpr::Create(Context, BuildBlock->getType(),
8912-
CK_CopyAndAutoreleaseBlockObject,
8913-
BuildBlock, 0, VK_RValue);
8914-
89158856
// Create the return statement that returns the block from the conversion
89168857
// function.
8917-
StmtResult Return = ActOnReturnStmt(Conv->getLocation(), BuildBlock);
8858+
StmtResult Return = ActOnReturnStmt(Conv->getLocation(), BuildBlock.get());
89188859
if (Return.isInvalid()) {
89198860
Diag(CurrentLocation, diag::note_lambda_to_block_conv);
89208861
Conv->setInvalidDecl();

Diff for: lib/Sema/SemaExprCXX.cpp

+37-5
Original file line numberDiff line numberDiff line change
@@ -4460,11 +4460,8 @@ ExprResult Sema::MaybeBindToTemporary(Expr *E) {
44604460
// For message sends and property references, we try to find an
44614461
// actual method. FIXME: we should infer retention by selector in
44624462
// cases where we don't have an actual method.
4463-
} else {
4464-
ObjCMethodDecl *D = 0;
4465-
if (ObjCMessageExpr *Send = dyn_cast<ObjCMessageExpr>(E)) {
4466-
D = Send->getMethodDecl();
4467-
}
4463+
} else if (ObjCMessageExpr *Send = dyn_cast<ObjCMessageExpr>(E)) {
4464+
ObjCMethodDecl *D = Send->getMethodDecl();
44684465

44694466
ReturnsRetained = (D && D->hasAttr<NSReturnsRetainedAttr>());
44704467

@@ -4474,6 +4471,13 @@ ExprResult Sema::MaybeBindToTemporary(Expr *E) {
44744471
if (!ReturnsRetained &&
44754472
D && D->getMethodFamily() == OMF_performSelector)
44764473
return Owned(E);
4474+
} else if (isa<CastExpr>(E) &&
4475+
isa<BlockExpr>(cast<CastExpr>(E)->getSubExpr())) {
4476+
// We hit this case with the lambda conversion-to-block optimization;
4477+
// we don't want any extra casts here.
4478+
return Owned(E);
4479+
} else {
4480+
ReturnsRetained = false;
44774481
}
44784482

44794483
// Don't reclaim an object of Class type.
@@ -5089,6 +5093,34 @@ ExprResult Sema::ActOnPseudoDestructorExpr(Scope *S, Expr *Base,
50895093
ExprResult Sema::BuildCXXMemberCallExpr(Expr *E, NamedDecl *FoundDecl,
50905094
CXXConversionDecl *Method,
50915095
bool HadMultipleCandidates) {
5096+
if (Method->getParent()->isLambda() &&
5097+
Method->getConversionType()->isBlockPointerType()) {
5098+
// This is a lambda coversion to block pointer; check if the argument
5099+
// is a LambdaExpr.
5100+
Expr *SubE = E;
5101+
CastExpr *CE = dyn_cast<CastExpr>(SubE);
5102+
if (CE && CE->getCastKind() == CK_NoOp)
5103+
SubE = CE->getSubExpr();
5104+
SubE = SubE->IgnoreParens();
5105+
if (CXXBindTemporaryExpr *BE = dyn_cast<CXXBindTemporaryExpr>(SubE))
5106+
SubE = BE->getSubExpr();
5107+
if (isa<LambdaExpr>(SubE)) {
5108+
// For the conversion to block pointer on a lambda expression, we
5109+
// construct a special BlockLiteral instead; this doesn't really make
5110+
// a difference in ARC, but outside of ARC the resulting block literal
5111+
// follows the normal lifetime rules for block literals instead of being
5112+
// autoreleased.
5113+
DiagnosticErrorTrap Trap(Diags);
5114+
ExprResult Exp = BuildBlockForLambdaConversion(E->getExprLoc(),
5115+
E->getExprLoc(),
5116+
Method, E);
5117+
if (Exp.isInvalid())
5118+
Diag(E->getExprLoc(), diag::note_lambda_to_block_conv);
5119+
return Exp;
5120+
}
5121+
}
5122+
5123+
50925124
ExprResult Exp = PerformObjectArgumentInitialization(E, /*Qualifier=*/0,
50935125
FoundDecl, Method);
50945126
if (Exp.isInvalid())

Diff for: lib/Sema/SemaLambda.cpp

+78
Original file line numberDiff line numberDiff line change
@@ -740,3 +740,81 @@ ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body,
740740

741741
return MaybeBindToTemporary(Lambda);
742742
}
743+
744+
ExprResult Sema::BuildBlockForLambdaConversion(SourceLocation CurrentLocation,
745+
SourceLocation ConvLocation,
746+
CXXConversionDecl *Conv,
747+
Expr *Src) {
748+
// Make sure that the lambda call operator is marked used.
749+
CXXRecordDecl *Lambda = Conv->getParent();
750+
CXXMethodDecl *CallOperator
751+
= cast<CXXMethodDecl>(
752+
*Lambda->lookup(
753+
Context.DeclarationNames.getCXXOperatorName(OO_Call)).first);
754+
CallOperator->setReferenced();
755+
CallOperator->setUsed();
756+
757+
ExprResult Init = PerformCopyInitialization(
758+
InitializedEntity::InitializeBlock(ConvLocation,
759+
Src->getType(),
760+
/*NRVO=*/false),
761+
CurrentLocation, Src);
762+
if (!Init.isInvalid())
763+
Init = ActOnFinishFullExpr(Init.take());
764+
765+
if (Init.isInvalid())
766+
return ExprError();
767+
768+
// Create the new block to be returned.
769+
BlockDecl *Block = BlockDecl::Create(Context, CurContext, ConvLocation);
770+
771+
// Set the type information.
772+
Block->setSignatureAsWritten(CallOperator->getTypeSourceInfo());
773+
Block->setIsVariadic(CallOperator->isVariadic());
774+
Block->setBlockMissingReturnType(false);
775+
776+
// Add parameters.
777+
SmallVector<ParmVarDecl *, 4> BlockParams;
778+
for (unsigned I = 0, N = CallOperator->getNumParams(); I != N; ++I) {
779+
ParmVarDecl *From = CallOperator->getParamDecl(I);
780+
BlockParams.push_back(ParmVarDecl::Create(Context, Block,
781+
From->getLocStart(),
782+
From->getLocation(),
783+
From->getIdentifier(),
784+
From->getType(),
785+
From->getTypeSourceInfo(),
786+
From->getStorageClass(),
787+
From->getStorageClassAsWritten(),
788+
/*DefaultArg=*/0));
789+
}
790+
Block->setParams(BlockParams);
791+
792+
Block->setIsConversionFromLambda(true);
793+
794+
// Add capture. The capture uses a fake variable, which doesn't correspond
795+
// to any actual memory location. However, the initializer copy-initializes
796+
// the lambda object.
797+
TypeSourceInfo *CapVarTSI =
798+
Context.getTrivialTypeSourceInfo(Src->getType());
799+
VarDecl *CapVar = VarDecl::Create(Context, Block, ConvLocation,
800+
ConvLocation, 0,
801+
Src->getType(), CapVarTSI,
802+
SC_None, SC_None);
803+
BlockDecl::Capture Capture(/*Variable=*/CapVar, /*ByRef=*/false,
804+
/*Nested=*/false, /*Copy=*/Init.take());
805+
Block->setCaptures(Context, &Capture, &Capture + 1,
806+
/*CapturesCXXThis=*/false);
807+
808+
// Add a fake function body to the block. IR generation is responsible
809+
// for filling in the actual body, which cannot be expressed as an AST.
810+
Block->setBody(new (Context) CompoundStmt(Context, 0, 0,
811+
ConvLocation,
812+
ConvLocation));
813+
814+
// Create the block literal expression.
815+
Expr *BuildBlock = new (Context) BlockExpr(Block, Conv->getConversionType());
816+
ExprCleanupObjects.push_back(Block);
817+
ExprNeedsCleanups = true;
818+
819+
return BuildBlock;
820+
}

0 commit comments

Comments
 (0)