forked from OSchip/llvm-project
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:
parent
c1663048e0
commit
2053a5dd64
|
@ -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.
|
||||
|
|
|
@ -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 !{}
|
Loading…
Reference in New Issue