Don't eliminate objc_retainBlock calls on stack objects if the

objc_retainBlock call is potentially responsible for copying
the block to the heap to extend its lifetime. rdar://10209613.

llvm-svn: 140814
This commit is contained in:
Dan Gohman 2011-09-29 22:25:23 +00:00
parent c1663048e0
commit 2053a5dd64
2 changed files with 83 additions and 8 deletions

View File

@ -2338,6 +2338,11 @@ ObjCARCOpt::VisitBottomUp(BasicBlock *BB,
S.SetAtLeastOneRefCount();
S.DecrementNestCount();
// An objc_retainBlock call with just a use still needs to be kept,
// because it may be copying a block from the stack to the heap.
if (Class == IC_RetainBlock && S.GetSeq() == S_Use)
S.SetSeq(S_CanRelease);
switch (S.GetSeq()) {
case S_Stop:
case S_Release:
@ -2406,14 +2411,14 @@ ObjCARCOpt::VisitBottomUp(BasicBlock *BB,
case S_Release:
case S_MovableRelease:
if (CanUse(Inst, Ptr, PA, Class)) {
S.RRI.ReverseInsertPts.clear();
assert(S.RRI.ReverseInsertPts.empty());
S.RRI.ReverseInsertPts.insert(Inst);
S.SetSeq(S_Use);
} else if (Seq == S_Release &&
(Class == IC_User || Class == IC_CallOrUser)) {
// Non-movable releases depend on any possible objc pointer use.
S.SetSeq(S_Stop);
S.RRI.ReverseInsertPts.clear();
assert(S.RRI.ReverseInsertPts.empty());
S.RRI.ReverseInsertPts.insert(Inst);
}
break;
@ -2566,7 +2571,7 @@ ObjCARCOpt::VisitTopDown(BasicBlock *BB,
switch (Seq) {
case S_Retain:
S.SetSeq(S_CanRelease);
S.RRI.ReverseInsertPts.clear();
assert(S.RRI.ReverseInsertPts.empty());
S.RRI.ReverseInsertPts.insert(Inst);
// One call can't cause a transition from S_Retain to S_CanRelease
@ -2590,8 +2595,18 @@ ObjCARCOpt::VisitTopDown(BasicBlock *BB,
if (CanUse(Inst, Ptr, PA, Class))
S.SetSeq(S_Use);
break;
case S_Use:
case S_Retain:
// An objc_retainBlock call may be responsible for copying the block
// data from the stack to the heap. Model this by moving it straight
// from S_Retain to S_Use.
if (S.RRI.IsRetainBlock &&
CanUse(Inst, Ptr, PA, Class)) {
assert(S.RRI.ReverseInsertPts.empty());
S.RRI.ReverseInsertPts.insert(Inst);
S.SetSeq(S_Use);
}
break;
case S_Use:
case S_None:
break;
case S_Stop:
@ -2743,17 +2758,23 @@ ObjCARCOpt::PerformCodePlacement(DenseMap<const BasicBlock *, BBState>
SmallVector<Instruction *, 8> DeadInsts;
for (MapVector<Value *, RRInfo>::const_iterator I = Retains.begin(),
E = Retains.end(); I != E; ) {
Value *V = (I++)->first;
E = Retains.end(); I != E; ++I) {
Value *V = I->first;
if (!V) continue; // blotted
Instruction *Retain = cast<Instruction>(V);
Value *Arg = GetObjCArg(Retain);
// If the object being released is in static or stack storage, we know it's
// If the object being released is in static storage, we know it's
// not being managed by ObjC reference counting, so we can delete pairs
// regardless of what possible decrements or uses lie between them.
bool KnownSafe = isa<Constant>(Arg) || isa<AllocaInst>(Arg);
bool KnownSafe = isa<Constant>(Arg);
// Same for stack storage, unless this is an objc_retainBlock call,
// which is responsible for copying the block data from the stack to
// the heap.
if (!I->second.IsRetainBlock && isa<AllocaInst>(Arg))
KnownSafe = true;
// A constant pointer can't be pointing to an object on the heap. It may
// be reference-counted, but it won't be deleted.

View File

@ -0,0 +1,54 @@
; RUN: opt -S -objc-arc < %s | FileCheck %s
; rdar://10209613
; CHECK: define void @test
; CHECK: %3 = call i8* @objc_retainBlock(i8* %2) nounwind
; CHECK: @objc_msgSend
; CHECK-NEXT: @objc_release(i8* %3)
%0 = type opaque
%struct.__block_descriptor = type { i64, i64 }
@_NSConcreteStackBlock = external global i8*
@__block_descriptor_tmp = external hidden constant { i64, i64, i8*, i8*, i8*, i8* }
@"\01L_OBJC_SELECTOR_REFERENCES_" = external hidden global i8*, section "__DATA, __objc_selrefs, literal_pointers, no_dead_strip"
define void @test(%0* %array) uwtable {
entry:
%block = alloca <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>, align 8
%0 = bitcast %0* %array to i8*
%1 = tail call i8* @objc_retain(i8* %0) nounwind
%block.isa = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>* %block, i64 0, i32 0
store i8* bitcast (i8** @_NSConcreteStackBlock to i8*), i8** %block.isa, align 8
%block.flags = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>* %block, i64 0, i32 1
store i32 1107296256, i32* %block.flags, align 8
%block.reserved = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>* %block, i64 0, i32 2
store i32 0, i32* %block.reserved, align 4
%block.invoke = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>* %block, i64 0, i32 3
store i8* bitcast (void (i8*)* @__test_block_invoke_0 to i8*), i8** %block.invoke, align 8
%block.descriptor = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>* %block, i64 0, i32 4
store %struct.__block_descriptor* bitcast ({ i64, i64, i8*, i8*, i8*, i8* }* @__block_descriptor_tmp to %struct.__block_descriptor*), %struct.__block_descriptor** %block.descriptor, align 8
%block.captured = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>* %block, i64 0, i32 5
store %0* %array, %0** %block.captured, align 8
%2 = bitcast <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>* %block to i8*
%3 = call i8* @objc_retainBlock(i8* %2) nounwind
%tmp2 = load i8** @"\01L_OBJC_SELECTOR_REFERENCES_", align 8
call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*, i8*)*)(i8* %0, i8* %tmp2, i8* %3)
call void @objc_release(i8* %3) nounwind
%strongdestroy = load %0** %block.captured, align 8
%4 = bitcast %0* %strongdestroy to i8*
call void @objc_release(i8* %4) nounwind, !clang.imprecise_release !0
ret void
}
declare i8* @objc_retain(i8*)
declare void @__test_block_invoke_0(i8* nocapture) uwtable
declare i8* @objc_retainBlock(i8*)
declare i8* @objc_msgSend(i8*, i8*, ...) nonlazybind
declare void @objc_release(i8*)
!0 = metadata !{}