Skip to content

Commit c5792aa

Browse files
committed
Avoid needlessly copying a block to the heap when a block literal
initializes a local auto variable or is assigned to a local auto variable that is declared in the scope that introduced the block literal. rdar://problem/13289333 https://reviews.llvm.org/D58514 llvm-svn: 355012
1 parent 69bec61 commit c5792aa

File tree

12 files changed

+234
-11
lines changed

12 files changed

+234
-11
lines changed

clang/include/clang/AST/Decl.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4008,6 +4008,13 @@ class BlockDecl : public Decl, public DeclContext {
40084008
bool doesNotEscape() const { return BlockDeclBits.DoesNotEscape; }
40094009
void setDoesNotEscape(bool B = true) { BlockDeclBits.DoesNotEscape = B; }
40104010

4011+
bool canAvoidCopyToHeap() const {
4012+
return BlockDeclBits.CanAvoidCopyToHeap;
4013+
}
4014+
void setCanAvoidCopyToHeap(bool B = true) {
4015+
BlockDeclBits.CanAvoidCopyToHeap = B;
4016+
}
4017+
40114018
bool capturesVariable(const VarDecl *var) const;
40124019

40134020
void setCaptures(ASTContext &Context, ArrayRef<Capture> Captures,

clang/include/clang/AST/DeclBase.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1665,6 +1665,11 @@ class DeclContext {
16651665
/// A bit that indicates this block is passed directly to a function as a
16661666
/// non-escaping parameter.
16671667
uint64_t DoesNotEscape : 1;
1668+
1669+
/// A bit that indicates whether it's possible to avoid coying this block to
1670+
/// the heap when it initializes or is assigned to a local variable with
1671+
/// automatic storage.
1672+
uint64_t CanAvoidCopyToHeap : 1;
16681673
};
16691674

16701675
/// Number of non-inherited bits in BlockDeclBitfields.

clang/lib/AST/Decl.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4265,6 +4265,7 @@ BlockDecl::BlockDecl(DeclContext *DC, SourceLocation CaretLoc)
42654265
setBlockMissingReturnType(true);
42664266
setIsConversionFromLambda(false);
42674267
setDoesNotEscape(false);
4268+
setCanAvoidCopyToHeap(false);
42684269
}
42694270

42704271
void BlockDecl::setParams(ArrayRef<ParmVarDecl *> NewParamInfo) {

clang/lib/CodeGen/CGObjC.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2871,6 +2871,7 @@ template <typename Impl, typename Result> class ARCExprEmitter {
28712871
Result visit(const Expr *e);
28722872
Result visitCastExpr(const CastExpr *e);
28732873
Result visitPseudoObjectExpr(const PseudoObjectExpr *e);
2874+
Result visitBlockExpr(const BlockExpr *e);
28742875
Result visitBinaryOperator(const BinaryOperator *e);
28752876
Result visitBinAssign(const BinaryOperator *e);
28762877
Result visitBinAssignUnsafeUnretained(const BinaryOperator *e);
@@ -2946,6 +2947,12 @@ ARCExprEmitter<Impl,Result>::visitPseudoObjectExpr(const PseudoObjectExpr *E) {
29462947
return result;
29472948
}
29482949

2950+
template <typename Impl, typename Result>
2951+
Result ARCExprEmitter<Impl, Result>::visitBlockExpr(const BlockExpr *e) {
2952+
// The default implementation just forwards the expression to visitExpr.
2953+
return asImpl().visitExpr(e);
2954+
}
2955+
29492956
template <typename Impl, typename Result>
29502957
Result ARCExprEmitter<Impl,Result>::visitCastExpr(const CastExpr *e) {
29512958
switch (e->getCastKind()) {
@@ -3089,7 +3096,8 @@ Result ARCExprEmitter<Impl,Result>::visit(const Expr *e) {
30893096
// Look through pseudo-object expressions.
30903097
} else if (const PseudoObjectExpr *pseudo = dyn_cast<PseudoObjectExpr>(e)) {
30913098
return asImpl().visitPseudoObjectExpr(pseudo);
3092-
}
3099+
} else if (auto *be = dyn_cast<BlockExpr>(e))
3100+
return asImpl().visitBlockExpr(be);
30933101

30943102
return asImpl().visitExpr(e);
30953103
}
@@ -3124,6 +3132,15 @@ struct ARCRetainExprEmitter :
31243132
return TryEmitResult(result, true);
31253133
}
31263134

3135+
TryEmitResult visitBlockExpr(const BlockExpr *e) {
3136+
TryEmitResult result = visitExpr(e);
3137+
// Avoid the block-retain if this is a block literal that doesn't need to be
3138+
// copied to the heap.
3139+
if (e->getBlockDecl()->canAvoidCopyToHeap())
3140+
result.setInt(true);
3141+
return result;
3142+
}
3143+
31273144
/// Block extends are net +0. Naively, we could just recurse on
31283145
/// the subexpression, but actually we need to ensure that the
31293146
/// value is copied as a block, so there's a little filter here.

clang/lib/Sema/SemaDecl.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11257,6 +11257,11 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) {
1125711257
<< Culprit->getSourceRange();
1125811258
}
1125911259
}
11260+
11261+
if (auto *E = dyn_cast<ExprWithCleanups>(Init))
11262+
if (auto *BE = dyn_cast<BlockExpr>(E->getSubExpr()->IgnoreParens()))
11263+
if (VDecl->hasLocalStorage())
11264+
BE->getBlockDecl()->setCanAvoidCopyToHeap();
1126011265
} else if (VDecl->isStaticDataMember() && !VDecl->isInline() &&
1126111266
VDecl->getLexicalDeclContext()->isRecord()) {
1126211267
// This is an in-class initialization for a static data member, e.g.,

clang/lib/Sema/SemaExpr.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12443,6 +12443,25 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc,
1244312443
if (!ResultTy.isNull()) {
1244412444
DiagnoseSelfAssignment(*this, LHS.get(), RHS.get(), OpLoc, true);
1244512445
DiagnoseSelfMove(LHS.get(), RHS.get(), OpLoc);
12446+
12447+
// Avoid copying a block to the heap if the block is assigned to a local
12448+
// auto variable that is declared in the same scope as the block. This
12449+
// optimization is unsafe if the local variable is declared in an outer
12450+
// scope. For example:
12451+
//
12452+
// BlockTy b;
12453+
// {
12454+
// b = ^{...};
12455+
// }
12456+
// // It is unsafe to invoke the block here if it wasn't copied to the
12457+
// // heap.
12458+
// b();
12459+
12460+
if (auto *BE = dyn_cast<BlockExpr>(RHS.get()->IgnoreParens()))
12461+
if (auto *DRE = dyn_cast<DeclRefExpr>(LHS.get()->IgnoreParens()))
12462+
if (auto *VD = dyn_cast<VarDecl>(DRE->getDecl()))
12463+
if (VD->hasLocalStorage() && getCurScope()->isDeclScope(VD))
12464+
BE->getBlockDecl()->setCanAvoidCopyToHeap();
1244612465
}
1244712466
RecordModifiableNonNullParam(*this, LHS.get());
1244812467
break;

clang/lib/Serialization/ASTReaderDecl.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1479,6 +1479,7 @@ void ASTDeclReader::VisitBlockDecl(BlockDecl *BD) {
14791479
BD->setBlockMissingReturnType(Record.readInt());
14801480
BD->setIsConversionFromLambda(Record.readInt());
14811481
BD->setDoesNotEscape(Record.readInt());
1482+
BD->setCanAvoidCopyToHeap(Record.readInt());
14821483

14831484
bool capturesCXXThis = Record.readInt();
14841485
unsigned numCaptures = Record.readInt();

clang/lib/Serialization/ASTWriterDecl.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,6 +1110,7 @@ void ASTDeclWriter::VisitBlockDecl(BlockDecl *D) {
11101110
Record.push_back(D->blockMissingReturnType());
11111111
Record.push_back(D->isConversionFromLambda());
11121112
Record.push_back(D->doesNotEscape());
1113+
Record.push_back(D->canAvoidCopyToHeap());
11131114
Record.push_back(D->capturesCXXThis());
11141115
Record.push_back(D->getNumCaptures());
11151116
for (const auto &capture : D->captures()) {

clang/test/CodeGenObjC/arc-block-copy-escape.m

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@
99
void test0(int i) {
1010
block_t block = ^{ use_int(i); };
1111
// CHECK-LABEL: define {{.*}}void @test0(
12-
// CHECK: call {{.*}}i8* @llvm.objc.retainBlock(i8* {{%.*}}) [[NUW:#[0-9]+]], !clang.arc.copy_on_escape
12+
// CHECK-NOT: @llvm.objc.retainBlock(
1313
// CHECK: ret void
1414
}
1515

1616
void test1(int i) {
1717
id block = ^{ use_int(i); };
1818
// CHECK-LABEL: define {{.*}}void @test1(
19-
// CHECK: call {{.*}}i8* @llvm.objc.retainBlock(i8* {{%.*}}) [[NUW]]
19+
// CHECK: call {{.*}}i8* @llvm.objc.retainBlock(i8* {{%.*}}) [[NUW:#[0-9]+]]
2020
// CHECK-NOT: !clang.arc.copy_on_escape
2121
// CHECK: ret void
2222
}

clang/test/CodeGenObjC/arc-blocks.m

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -338,20 +338,19 @@ void test10a(void) {
338338
__block void (^block)(void) = ^{ block(); };
339339
// CHECK-LABEL: define void @test10a()
340340
// CHECK: [[BYREF:%.*]] = alloca [[BYREF_T:%.*]],
341+
// CHECK: [[BLOCK1:%.*]] = alloca <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>, align 8
341342

342343
// Zero-initialization before running the initializer.
343344
// CHECK: [[T0:%.*]] = getelementptr inbounds [[BYREF_T]], [[BYREF_T]]* [[BYREF]], i32 0, i32 6
344345
// CHECK-NEXT: store void ()* null, void ()** [[T0]], align 8
345346

346347
// Run the initializer as an assignment.
347-
// CHECK: [[T0:%.*]] = bitcast void ()* {{%.*}} to i8*
348-
// CHECK-NEXT: [[T1:%.*]] = call i8* @llvm.objc.retainBlock(i8* [[T0]])
349-
// CHECK-NEXT: [[T2:%.*]] = bitcast i8* [[T1]] to void ()*
348+
// CHECK: [[T2:%.*]] = bitcast <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>* [[BLOCK1]] to void ()*
350349
// CHECK-NEXT: [[T3:%.*]] = getelementptr inbounds [[BYREF_T]], [[BYREF_T]]* [[BYREF]], i32 0, i32 1
351350
// CHECK-NEXT: [[T4:%.*]] = load [[BYREF_T]]*, [[BYREF_T]]** [[T3]]
352351
// CHECK-NEXT: [[T5:%.*]] = getelementptr inbounds [[BYREF_T]], [[BYREF_T]]* [[T4]], i32 0, i32 6
353352
// CHECK-NEXT: [[T6:%.*]] = load void ()*, void ()** [[T5]], align 8
354-
// CHECK-NEXT: store void ()* {{%.*}}, void ()** [[T5]], align 8
353+
// CHECK-NEXT: store void ()* [[T2]], void ()** [[T5]], align 8
355354
// CHECK-NEXT: [[T7:%.*]] = bitcast void ()* [[T6]] to i8*
356355
// CHECK-NEXT: call void @llvm.objc.release(i8* [[T7]])
357356

@@ -401,6 +400,7 @@ void test10b(void) {
401400

402401
// CHECK-LABEL: define void @test10b()
403402
// CHECK: [[BYREF:%.*]] = alloca [[BYREF_T:%.*]],
403+
// CHECK: [[BLOCK3:%.*]] = alloca <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>, align 8
404404

405405
// Zero-initialize.
406406
// CHECK: [[T0:%.*]] = getelementptr inbounds [[BYREF_T]], [[BYREF_T]]* [[BYREF]], i32 0, i32 6
@@ -409,14 +409,12 @@ void test10b(void) {
409409
// CHECK-NEXT: [[SLOT:%.*]] = getelementptr inbounds [[BYREF_T]], [[BYREF_T]]* [[BYREF]], i32 0, i32 6
410410

411411
// The assignment.
412-
// CHECK: [[T0:%.*]] = bitcast void ()* {{%.*}} to i8*
413-
// CHECK-NEXT: [[T1:%.*]] = call i8* @llvm.objc.retainBlock(i8* [[T0]])
414-
// CHECK-NEXT: [[T2:%.*]] = bitcast i8* [[T1]] to void ()*
412+
// CHECK: [[T2:%.*]] = bitcast <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>* [[BLOCK3]] to void ()*
415413
// CHECK-NEXT: [[T3:%.*]] = getelementptr inbounds [[BYREF_T]], [[BYREF_T]]* [[BYREF]], i32 0, i32 1
416414
// CHECK-NEXT: [[T4:%.*]] = load [[BYREF_T]]*, [[BYREF_T]]** [[T3]]
417415
// CHECK-NEXT: [[T5:%.*]] = getelementptr inbounds [[BYREF_T]], [[BYREF_T]]* [[T4]], i32 0, i32 6
418416
// CHECK-NEXT: [[T6:%.*]] = load void ()*, void ()** [[T5]], align 8
419-
// CHECK-NEXT: store void ()* {{%.*}}, void ()** [[T5]], align 8
417+
// CHECK-NEXT: store void ()* [[T2]], void ()** [[T5]], align 8
420418
// CHECK-NEXT: [[T7:%.*]] = bitcast void ()* [[T6]] to i8*
421419
// CHECK-NEXT: call void @llvm.objc.release(i8* [[T7]])
422420

0 commit comments

Comments
 (0)