mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2025-01-01 00:33:09 +00:00
Properly model precise lifetime when given an incomplete dataflow sequence.
The normal dataflow sequence in the ARC optimizer consists of the following states: Retain -> CanRelease -> Use -> Release The optimizer before this patch stored the uses that determine the lifetime of the retainable object pointer when it bottom up hits a retain or when top down it hits a release. This is correct for an imprecise lifetime scenario since what we are trying to do is remove retains/releases while making sure that no ``CanRelease'' (which is usually a call) deallocates the given pointer before we get to the ``Use'' (since that would cause a segfault). If we are considering the precise lifetime scenario though, this is not correct. In such a situation, we *DO* care about the previous sequence, but additionally, we wish to track the uses resulting from the following incomplete sequences: Retain -> CanRelease -> Release (TopDown) Retain <- Use <- Release (BottomUp) *NOTE* This patch looks large but the most of it consists of updating test cases. Additionally this fix exposed an additional bug. I removed the test case that expressed said bug and will recommit it with the fix in a little bit. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@178921 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
2e80991a77
commit
e7ce2b3f75
@ -408,6 +408,10 @@ namespace {
|
||||
KnownSafe(false), IsTailCallRelease(false), ReleaseMetadata(0) {}
|
||||
|
||||
void clear();
|
||||
|
||||
bool IsTrackingImpreciseReleases() {
|
||||
return ReleaseMetadata != 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -1746,7 +1750,9 @@ ObjCARCOpt::VisitInstructionBottomUp(Instruction *Inst,
|
||||
bool NestingDetected = false;
|
||||
InstructionClass Class = GetInstructionClass(Inst);
|
||||
const Value *Arg = 0;
|
||||
|
||||
|
||||
DEBUG(dbgs() << "Class: " << Class << "\n");
|
||||
|
||||
switch (Class) {
|
||||
case IC_Release: {
|
||||
Arg = GetObjCArg(Inst);
|
||||
@ -1794,7 +1800,10 @@ ObjCARCOpt::VisitInstructionBottomUp(Instruction *Inst,
|
||||
case S_Release:
|
||||
case S_MovableRelease:
|
||||
case S_Use:
|
||||
S.RRI.ReverseInsertPts.clear();
|
||||
// If OldSeq is not S_Use or OldSeq is S_Use and we are tracking an
|
||||
// imprecise release, clear our reverse insertion points.
|
||||
if (OldSeq != S_Use || S.RRI.IsTrackingImpreciseReleases())
|
||||
S.RRI.ReverseInsertPts.clear();
|
||||
// FALL THROUGH
|
||||
case S_CanRelease:
|
||||
// Don't do retain+release tracking for IC_RetainRV, because it's
|
||||
@ -2017,14 +2026,19 @@ ObjCARCOpt::VisitInstructionTopDown(Instruction *Inst,
|
||||
|
||||
PtrState &S = MyStates.getPtrTopDownState(Arg);
|
||||
S.ClearKnownPositiveRefCount();
|
||||
|
||||
switch (S.GetSeq()) {
|
||||
|
||||
Sequence OldSeq = S.GetSeq();
|
||||
|
||||
MDNode *ReleaseMetadata = Inst->getMetadata(ImpreciseReleaseMDKind);
|
||||
|
||||
switch (OldSeq) {
|
||||
case S_Retain:
|
||||
case S_CanRelease:
|
||||
S.RRI.ReverseInsertPts.clear();
|
||||
if (OldSeq == S_Retain || ReleaseMetadata != 0)
|
||||
S.RRI.ReverseInsertPts.clear();
|
||||
// FALL THROUGH
|
||||
case S_Use:
|
||||
S.RRI.ReleaseMetadata = Inst->getMetadata(ImpreciseReleaseMDKind);
|
||||
S.RRI.ReleaseMetadata = ReleaseMetadata;
|
||||
S.RRI.IsTailCallRelease = cast<CallInst>(Inst)->isTailCall();
|
||||
Releases[Inst] = S.RRI;
|
||||
ANNOTATE_TOPDOWN(Inst, Arg, S.GetSeq(), S_None);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -34,7 +34,9 @@ declare void @test0_helper(i8*, i8**)
|
||||
; CHECK-NEXT: @objc_release(i8* [[VAL1]])
|
||||
; CHECK-NEXT: @objc_autorelease(i8* %x)
|
||||
; CHECK-NEXT: store i8* %x, i8** %out
|
||||
; CHECK-NEXT: @objc_retain(i8* %x)
|
||||
; CHECK-NEXT: @objc_release(i8* [[VAL2]])
|
||||
; CHECK-NEXT: @objc_release(i8* %x)
|
||||
; CHECK-NEXT: ret void
|
||||
define void @test0(i8** %out, i8* %x, i8* %y) {
|
||||
entry:
|
||||
@ -61,3 +63,52 @@ entry:
|
||||
call void @objc_release(i8* %x) nounwind
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK: define void @test0a(
|
||||
; CHECK: @objc_retain(i8* %x)
|
||||
; CHECK-NEXT: store i8* %y, i8** %temp0
|
||||
; CHECK-NEXT: @objc_retain(i8* %y)
|
||||
; CHECK-NEXT: call void @test0_helper
|
||||
; CHECK-NEXT: [[VAL1:%.*]] = load i8** %temp0
|
||||
; CHECK-NEXT: call void (...)* @clang.arc.use(i8* %y)
|
||||
; CHECK-NEXT: @objc_retain(i8* [[VAL1]])
|
||||
; CHECK-NEXT: @objc_release(i8* %y)
|
||||
; CHECK-NEXT: store i8* [[VAL1]], i8** %temp1
|
||||
; CHECK-NEXT: call void @test0_helper
|
||||
; CHECK-NEXT: [[VAL2:%.*]] = load i8** %temp1
|
||||
; CHECK-NEXT: call void (...)* @clang.arc.use(i8* [[VAL1]])
|
||||
; CHECK-NEXT: @objc_retain(i8* [[VAL2]])
|
||||
; CHECK-NEXT: @objc_release(i8* [[VAL1]])
|
||||
; CHECK-NEXT: @objc_autorelease(i8* %x)
|
||||
; CHECK-NEXT: @objc_release(i8* [[VAL2]])
|
||||
; CHECK-NEXT: store i8* %x, i8** %out
|
||||
; CHECK-NEXT: ret void
|
||||
define void @test0a(i8** %out, i8* %x, i8* %y) {
|
||||
entry:
|
||||
%temp0 = alloca i8*, align 8
|
||||
%temp1 = alloca i8*, align 8
|
||||
%0 = call i8* @objc_retain(i8* %x) nounwind
|
||||
%1 = call i8* @objc_retain(i8* %y) nounwind
|
||||
store i8* %y, i8** %temp0
|
||||
call void @test0_helper(i8* %x, i8** %temp0)
|
||||
%val1 = load i8** %temp0
|
||||
%2 = call i8* @objc_retain(i8* %val1) nounwind
|
||||
call void (...)* @clang.arc.use(i8* %y) nounwind
|
||||
call void @objc_release(i8* %y) nounwind, !clang.imprecise_release !0
|
||||
store i8* %val1, i8** %temp1
|
||||
call void @test0_helper(i8* %x, i8** %temp1)
|
||||
%val2 = load i8** %temp1
|
||||
%3 = call i8* @objc_retain(i8* %val2) nounwind
|
||||
call void (...)* @clang.arc.use(i8* %val1) nounwind
|
||||
call void @objc_release(i8* %val1) nounwind, !clang.imprecise_release !0
|
||||
%4 = call i8* @objc_retain(i8* %x) nounwind
|
||||
%5 = call i8* @objc_autorelease(i8* %x) nounwind
|
||||
store i8* %x, i8** %out
|
||||
call void @objc_release(i8* %val2) nounwind, !clang.imprecise_release !0
|
||||
call void @objc_release(i8* %x) nounwind, !clang.imprecise_release !0
|
||||
ret void
|
||||
}
|
||||
|
||||
|
||||
!0 = metadata !{}
|
||||
|
||||
|
@ -21,6 +21,23 @@ declare i8* @objc_retainBlock(i8*)
|
||||
|
||||
define void @bitcasttest(i8* %storage, void (...)* %block) {
|
||||
; CHECK: define void @bitcasttest
|
||||
entry:
|
||||
%t1 = bitcast void (...)* %block to i8*
|
||||
; CHECK: tail call i8* @objc_retain
|
||||
%t2 = tail call i8* @objc_retain(i8* %t1)
|
||||
; CHECK: tail call i8* @objc_retainBlock
|
||||
%t3 = tail call i8* @objc_retainBlock(i8* %t1), !clang.arc.copy_on_escape !0
|
||||
%t4 = bitcast i8* %storage to void (...)**
|
||||
%t5 = bitcast i8* %t3 to void (...)*
|
||||
store void (...)* %t5, void (...)** %t4, align 8
|
||||
; CHECK: call void @objc_release
|
||||
call void @objc_release(i8* %t1)
|
||||
ret void
|
||||
; CHECK: }
|
||||
}
|
||||
|
||||
define void @bitcasttest_a(i8* %storage, void (...)* %block) {
|
||||
; CHECK: define void @bitcasttest_a
|
||||
entry:
|
||||
%t1 = bitcast void (...)* %block to i8*
|
||||
; CHECK-NOT: tail call i8* @objc_retain
|
||||
@ -31,12 +48,32 @@ entry:
|
||||
%t5 = bitcast i8* %t3 to void (...)*
|
||||
store void (...)* %t5, void (...)** %t4, align 8
|
||||
; CHECK-NOT: call void @objc_release
|
||||
call void @objc_release(i8* %t1)
|
||||
call void @objc_release(i8* %t1), !clang.imprecise_release !0
|
||||
ret void
|
||||
; CHECK: }
|
||||
}
|
||||
|
||||
define void @geptest(void (...)** %storage_array, void (...)* %block) {
|
||||
; CHECK: define void @geptest
|
||||
entry:
|
||||
%t1 = bitcast void (...)* %block to i8*
|
||||
; CHECK: tail call i8* @objc_retain
|
||||
%t2 = tail call i8* @objc_retain(i8* %t1)
|
||||
; CHECK: tail call i8* @objc_retainBlock
|
||||
%t3 = tail call i8* @objc_retainBlock(i8* %t1), !clang.arc.copy_on_escape !0
|
||||
%t4 = bitcast i8* %t3 to void (...)*
|
||||
|
||||
%storage = getelementptr inbounds void (...)** %storage_array, i64 0
|
||||
|
||||
store void (...)* %t4, void (...)** %storage, align 8
|
||||
; CHECK: call void @objc_release
|
||||
call void @objc_release(i8* %t1)
|
||||
ret void
|
||||
; CHECK: }
|
||||
}
|
||||
|
||||
define void @geptest_a(void (...)** %storage_array, void (...)* %block) {
|
||||
; CHECK: define void @geptest_a
|
||||
entry:
|
||||
%t1 = bitcast void (...)* %block to i8*
|
||||
; CHECK-NOT: tail call i8* @objc_retain
|
||||
@ -49,13 +86,32 @@ entry:
|
||||
|
||||
store void (...)* %t4, void (...)** %storage, align 8
|
||||
; CHECK-NOT: call void @objc_release
|
||||
call void @objc_release(i8* %t1)
|
||||
call void @objc_release(i8* %t1), !clang.imprecise_release !0
|
||||
ret void
|
||||
; CHECK: }
|
||||
}
|
||||
|
||||
define void @selecttest(void (...)** %store1, void (...)** %store2,
|
||||
void (...)* %block) {
|
||||
; CHECK: define void @selecttest
|
||||
entry:
|
||||
%t1 = bitcast void (...)* %block to i8*
|
||||
; CHECK: tail call i8* @objc_retain
|
||||
%t2 = tail call i8* @objc_retain(i8* %t1)
|
||||
; CHECK: tail call i8* @objc_retainBlock
|
||||
%t3 = tail call i8* @objc_retainBlock(i8* %t1), !clang.arc.copy_on_escape !0
|
||||
%t4 = bitcast i8* %t3 to void (...)*
|
||||
%store = select i1 undef, void (...)** %store1, void (...)** %store2
|
||||
store void (...)* %t4, void (...)** %store, align 8
|
||||
; CHECK: call void @objc_release
|
||||
call void @objc_release(i8* %t1)
|
||||
ret void
|
||||
; CHECK: }
|
||||
}
|
||||
|
||||
define void @selecttest_a(void (...)** %store1, void (...)** %store2,
|
||||
void (...)* %block) {
|
||||
; CHECK: define void @selecttest_a
|
||||
entry:
|
||||
%t1 = bitcast void (...)* %block to i8*
|
||||
; CHECK-NOT: tail call i8* @objc_retain
|
||||
@ -66,14 +122,45 @@ entry:
|
||||
%store = select i1 undef, void (...)** %store1, void (...)** %store2
|
||||
store void (...)* %t4, void (...)** %store, align 8
|
||||
; CHECK-NOT: call void @objc_release
|
||||
call void @objc_release(i8* %t1)
|
||||
call void @objc_release(i8* %t1), !clang.imprecise_release !0
|
||||
ret void
|
||||
; CHECK: }
|
||||
}
|
||||
|
||||
define void @phinodetest(void (...)** %storage1,
|
||||
void (...)** %storage2,
|
||||
void (...)* %block) {
|
||||
; CHECK: define void @phinodetest
|
||||
entry:
|
||||
%t1 = bitcast void (...)* %block to i8*
|
||||
; CHECK: tail call i8* @objc_retain
|
||||
%t2 = tail call i8* @objc_retain(i8* %t1)
|
||||
; CHECK: tail call i8* @objc_retainBlock
|
||||
%t3 = tail call i8* @objc_retainBlock(i8* %t1), !clang.arc.copy_on_escape !0
|
||||
%t4 = bitcast i8* %t3 to void (...)*
|
||||
br i1 undef, label %store1_set, label %store2_set
|
||||
; CHECK: store1_set:
|
||||
|
||||
store1_set:
|
||||
br label %end
|
||||
|
||||
store2_set:
|
||||
br label %end
|
||||
|
||||
end:
|
||||
; CHECK: end:
|
||||
%storage = phi void (...)** [ %storage1, %store1_set ], [ %storage2, %store2_set]
|
||||
store void (...)* %t4, void (...)** %storage, align 8
|
||||
; CHECK: call void @objc_release
|
||||
call void @objc_release(i8* %t1)
|
||||
ret void
|
||||
; CHECK: }
|
||||
}
|
||||
|
||||
define void @phinodetest_a(void (...)** %storage1,
|
||||
void (...)** %storage2,
|
||||
void (...)* %block) {
|
||||
; CHECK: define void @phinodetest_a
|
||||
entry:
|
||||
%t1 = bitcast void (...)* %block to i8*
|
||||
; CHECK-NOT: tail call i8* @objc_retain
|
||||
@ -93,10 +180,11 @@ end:
|
||||
%storage = phi void (...)** [ %storage1, %store1_set ], [ %storage2, %store2_set]
|
||||
store void (...)* %t4, void (...)** %storage, align 8
|
||||
; CHECK-NOT: call void @objc_release
|
||||
call void @objc_release(i8* %t1)
|
||||
call void @objc_release(i8* %t1), !clang.imprecise_release !0
|
||||
ret void
|
||||
}
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; This test makes sure that we do not hang clang when visiting a use ;
|
||||
; cycle caused by phi nodes during objc-arc analysis. *NOTE* This ;
|
||||
|
Loading…
Reference in New Issue
Block a user