[llvm] r178921 - Properly model precise lifetime when given an incomplete dataflow sequence.
Michael Gottesman
mgottesman at apple.com
Fri Apr 5 15:57:52 PDT 2013
I had a *DOH* moment here. This is rdar://12569789.
Michael
On Apr 5, 2013, at 3:54 PM, Michael Gottesman <mgottesman at apple.com> wrote:
> Author: mgottesman
> Date: Fri Apr 5 17:54:28 2013
> New Revision: 178921
>
> URL: http://llvm.org/viewvc/llvm-project?rev=178921&view=rev
> Log:
> 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.
>
> Modified:
> llvm/trunk/lib/Transforms/ObjCARC/ObjCARCOpts.cpp
> llvm/trunk/test/Transforms/ObjCARC/basic.ll
> llvm/trunk/test/Transforms/ObjCARC/intrinsic-use.ll
> llvm/trunk/test/Transforms/ObjCARC/retain-block-escape-analysis.ll
>
> Modified: llvm/trunk/lib/Transforms/ObjCARC/ObjCARCOpts.cpp
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/ObjCARC/ObjCARCOpts.cpp?rev=178921&r1=178920&r2=178921&view=diff
> ==============================================================================
> --- llvm/trunk/lib/Transforms/ObjCARC/ObjCARCOpts.cpp (original)
> +++ llvm/trunk/lib/Transforms/ObjCARC/ObjCARCOpts.cpp Fri Apr 5 17:54:28 2013
> @@ -408,6 +408,10 @@ namespace {
> KnownSafe(false), IsTailCallRelease(false), ReleaseMetadata(0) {}
>
> void clear();
> +
> + bool IsTrackingImpreciseReleases() {
> + return ReleaseMetadata != 0;
> + }
> };
> }
>
> @@ -1746,7 +1750,9 @@ ObjCARCOpt::VisitInstructionBottomUp(Ins
> 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(Ins
> 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(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);
>
> Modified: llvm/trunk/test/Transforms/ObjCARC/basic.ll
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/ObjCARC/basic.ll?rev=178921&r1=178920&r2=178921&view=diff
> ==============================================================================
> --- llvm/trunk/test/Transforms/ObjCARC/basic.ll (original)
> +++ llvm/trunk/test/Transforms/ObjCARC/basic.ll Fri Apr 5 17:54:28 2013
> @@ -20,6 +20,7 @@ declare void @callee()
> declare void @callee_fnptr(void ()*)
> declare void @invokee()
> declare i8* @returner()
> +declare void @bar(i32 ()*)
>
> declare void @llvm.dbg.value(metadata, i64, metadata)
>
> @@ -28,10 +29,11 @@ declare i8* @objc_msgSend(i8*, i8*, ...)
> ; Simple retain+release pair deletion, with some intervening control
> ; flow and harmless instructions.
>
> -; CHECK: define void @test0(
> -; CHECK-NOT: @objc_
> +; CHECK: define void @test0_precise(
> +; CHECK: @objc_retain
> +; CHECK: @objc_release
> ; CHECK: }
> -define void @test0(i32* %x, i1 %p) nounwind {
> +define void @test0_precise(i32* %x, i1 %p) nounwind {
> entry:
> %a = bitcast i32* %x to i8*
> %0 = call i8* @objc_retain(i8* %a) nounwind
> @@ -53,16 +55,41 @@ return:
> ret void
> }
>
> +; CHECK: define void @test0_imprecise(
> +; CHECK-NOT: @objc_
> +; CHECK: }
> +define void @test0_imprecise(i32* %x, i1 %p) nounwind {
> +entry:
> + %a = bitcast i32* %x to i8*
> + %0 = call i8* @objc_retain(i8* %a) nounwind
> + br i1 %p, label %t, label %f
> +
> +t:
> + store i8 3, i8* %a
> + %b = bitcast i32* %x to float*
> + store float 2.0, float* %b
> + br label %return
> +
> +f:
> + store i32 7, i32* %x
> + br label %return
> +
> +return:
> + %c = bitcast i32* %x to i8*
> + call void @objc_release(i8* %c) nounwind, !clang.imprecise_release !0
> + ret void
> +}
> +
> ; Like test0 but the release isn't always executed when the retain is,
> ; so the optimization is not safe.
>
> ; TODO: Make the objc_release's argument be %0.
>
> -; CHECK: define void @test1(
> +; CHECK: define void @test1_precise(
> ; CHECK: @objc_retain(i8* %a)
> ; CHECK: @objc_release
> ; CHECK: }
> -define void @test1(i32* %x, i1 %p, i1 %q) nounwind {
> +define void @test1_precise(i32* %x, i1 %p, i1 %q) nounwind {
> entry:
> %a = bitcast i32* %x to i8*
> %0 = call i8* @objc_retain(i8* %a) nounwind
> @@ -88,9 +115,69 @@ alt_return:
> ret void
> }
>
> +; CHECK: define void @test1_imprecise(
> +; CHECK: @objc_retain(i8* %a)
> +; CHECK: @objc_release
> +; CHECK: }
> +define void @test1_imprecise(i32* %x, i1 %p, i1 %q) nounwind {
> +entry:
> + %a = bitcast i32* %x to i8*
> + %0 = call i8* @objc_retain(i8* %a) nounwind
> + br i1 %p, label %t, label %f
> +
> +t:
> + store i8 3, i8* %a
> + %b = bitcast i32* %x to float*
> + store float 2.0, float* %b
> + br label %return
> +
> +f:
> + store i32 7, i32* %x
> + call void @callee()
> + br i1 %q, label %return, label %alt_return
> +
> +return:
> + %c = bitcast i32* %x to i8*
> + call void @objc_release(i8* %c) nounwind, !clang.imprecise_release !0
> + ret void
> +
> +alt_return:
> + ret void
> +}
> +
> +
> ; Don't do partial elimination into two different CFG diamonds.
>
> -; CHECK: define void @test1b(
> +; CHECK: define void @test1b_precise(
> +; CHECK: entry:
> +; CHECK: tail call i8* @objc_retain(i8* %x) [[NUW:#[0-9]+]]
> +; CHECK-NOT: @objc_
> +; CHECK: if.end5:
> +; CHECK: tail call void @objc_release(i8* %x) [[NUW]]
> +; CHECK-NOT: @objc_
> +; CHECK: }
> +define void @test1b_precise(i8* %x, i1 %p, i1 %q) {
> +entry:
> + tail call i8* @objc_retain(i8* %x) nounwind
> + br i1 %p, label %if.then, label %if.end
> +
> +if.then: ; preds = %entry
> + tail call void @callee()
> + br label %if.end
> +
> +if.end: ; preds = %if.then, %entry
> + br i1 %q, label %if.then3, label %if.end5
> +
> +if.then3: ; preds = %if.end
> + tail call void @use_pointer(i8* %x)
> + br label %if.end5
> +
> +if.end5: ; preds = %if.then3, %if.end
> + tail call void @objc_release(i8* %x) nounwind
> + ret void
> +}
> +
> +; CHECK: define void @test1b_imprecise(
> ; CHECK: entry:
> ; CHECK: tail call i8* @objc_retain(i8* %x) [[NUW:#[0-9]+]]
> ; CHECK-NOT: @objc_
> @@ -98,7 +185,7 @@ alt_return:
> ; CHECK: tail call void @objc_release(i8* %x) [[NUW]], !clang.imprecise_release !0
> ; CHECK-NOT: @objc_
> ; CHECK: }
> -define void @test1b(i8* %x, i1 %p, i1 %q) {
> +define void @test1b_imprecise(i8* %x, i1 %p, i1 %q) {
> entry:
> tail call i8* @objc_retain(i8* %x) nounwind
> br i1 %p, label %if.then, label %if.end
> @@ -119,14 +206,15 @@ if.end5:
> ret void
> }
>
> +
> ; Like test0 but the pointer is passed to an intervening call,
> ; so the optimization is not safe.
>
> -; CHECK: define void @test2(
> +; CHECK: define void @test2_precise(
> ; CHECK: @objc_retain(i8* %a)
> ; CHECK: @objc_release
> ; CHECK: }
> -define void @test2(i32* %x, i1 %p) nounwind {
> +define void @test2_precise(i32* %x, i1 %p) nounwind {
> entry:
> %a = bitcast i32* %x to i8*
> %0 = call i8* @objc_retain(i8* %a) nounwind
> @@ -151,16 +239,45 @@ return:
> ret void
> }
>
> +; CHECK: define void @test2_imprecise(
> +; CHECK: @objc_retain(i8* %a)
> +; CHECK: @objc_release
> +; CHECK: }
> +define void @test2_imprecise(i32* %x, i1 %p) nounwind {
> +entry:
> + %a = bitcast i32* %x to i8*
> + %0 = call i8* @objc_retain(i8* %a) nounwind
> + br i1 %p, label %t, label %f
> +
> +t:
> + store i8 3, i8* %a
> + %b = bitcast i32* %x to float*
> + store float 2.0, float* %b
> + br label %return
> +
> +f:
> + store i32 7, i32* %x
> + call void @use_pointer(i8* %0)
> + %d = bitcast i32* %x to float*
> + store float 3.0, float* %d
> + br label %return
> +
> +return:
> + %c = bitcast i32* %x to i8*
> + call void @objc_release(i8* %c) nounwind, !clang.imprecise_release !0
> + ret void
> +}
> +
> ; Like test0 but the release is in a loop,
> ; so the optimization is not safe.
>
> ; TODO: For now, assume this can't happen.
>
> -; CHECK: define void @test3(
> +; CHECK: define void @test3_precise(
> ; TODO: @objc_retain(i8* %a)
> ; TODO: @objc_release
> ; CHECK: }
> -define void @test3(i32* %x, i1* %q) nounwind {
> +define void @test3_precise(i32* %x, i1* %q) nounwind {
> entry:
> %a = bitcast i32* %x to i8*
> %0 = call i8* @objc_retain(i8* %a) nounwind
> @@ -176,16 +293,37 @@ return:
> ret void
> }
>
> +; CHECK: define void @test3_imprecise(
> +; TODO: @objc_retain(i8* %a)
> +; TODO: @objc_release
> +; CHECK: }
> +define void @test3_imprecise(i32* %x, i1* %q) nounwind {
> +entry:
> + %a = bitcast i32* %x to i8*
> + %0 = call i8* @objc_retain(i8* %a) nounwind
> + br label %loop
> +
> +loop:
> + %c = bitcast i32* %x to i8*
> + call void @objc_release(i8* %c) nounwind, !clang.imprecise_release !0
> + %j = load volatile i1* %q
> + br i1 %j, label %loop, label %return
> +
> +return:
> + ret void
> +}
> +
> +
> ; TODO: For now, assume this can't happen.
>
> ; Like test0 but the retain is in a loop,
> ; so the optimization is not safe.
>
> -; CHECK: define void @test4(
> +; CHECK: define void @test4_precise(
> ; TODO: @objc_retain(i8* %a)
> ; TODO: @objc_release
> ; CHECK: }
> -define void @test4(i32* %x, i1* %q) nounwind {
> +define void @test4_precise(i32* %x, i1* %q) nounwind {
> entry:
> br label %loop
>
> @@ -201,14 +339,35 @@ return:
> ret void
> }
>
> +; CHECK: define void @test4_imprecise(
> +; TODO: @objc_retain(i8* %a)
> +; TODO: @objc_release
> +; CHECK: }
> +define void @test4_imprecise(i32* %x, i1* %q) nounwind {
> +entry:
> + br label %loop
> +
> +loop:
> + %a = bitcast i32* %x to i8*
> + %0 = call i8* @objc_retain(i8* %a) nounwind
> + %j = load volatile i1* %q
> + br i1 %j, label %loop, label %return
> +
> +return:
> + %c = bitcast i32* %x to i8*
> + call void @objc_release(i8* %c) nounwind, !clang.imprecise_release !0
> + ret void
> +}
> +
> +
> ; Like test0 but the pointer is conditionally passed to an intervening call,
> ; so the optimization is not safe.
>
> -; CHECK: define void @test5(
> +; CHECK: define void @test5a(
> ; CHECK: @objc_retain(i8*
> ; CHECK: @objc_release
> ; CHECK: }
> -define void @test5(i32* %x, i1 %q, i8* %y) nounwind {
> +define void @test5a(i32* %x, i1 %q, i8* %y) nounwind {
> entry:
> %a = bitcast i32* %x to i8*
> %0 = call i8* @objc_retain(i8* %a) nounwind
> @@ -220,13 +379,98 @@ entry:
> ret void
> }
>
> +; CHECK: define void @test5b(
> +; CHECK: @objc_retain(i8*
> +; CHECK: @objc_release
> +; CHECK: }
> +define void @test5b(i32* %x, i1 %q, i8* %y) nounwind {
> +entry:
> + %a = bitcast i32* %x to i8*
> + %0 = call i8* @objc_retain(i8* %a) nounwind
> + %s = select i1 %q, i8* %y, i8* %0
> + call void @use_pointer(i8* %s)
> + store i32 7, i32* %x
> + %c = bitcast i32* %x to i8*
> + call void @objc_release(i8* %c) nounwind, !clang.imprecise_release !0
> + ret void
> +}
> +
> +
> ; retain+release pair deletion, where the release happens on two different
> ; flow paths.
>
> -; CHECK: define void @test6(
> +; CHECK: define void @test6a(
> +; CHECK: entry:
> +; CHECK: tail call i8* @objc_retain(
> +; CHECK: t:
> +; CHECK: call void @objc_release(
> +; CHECK: f:
> +; CHECK: call void @objc_release(
> +; CHECK: return:
> +; CHECK: }
> +define void @test6a(i32* %x, i1 %p) nounwind {
> +entry:
> + %a = bitcast i32* %x to i8*
> + %0 = call i8* @objc_retain(i8* %a) nounwind
> + br i1 %p, label %t, label %f
> +
> +t:
> + store i8 3, i8* %a
> + %b = bitcast i32* %x to float*
> + store float 2.0, float* %b
> + %ct = bitcast i32* %x to i8*
> + call void @objc_release(i8* %ct) nounwind
> + br label %return
> +
> +f:
> + store i32 7, i32* %x
> + call void @callee()
> + %cf = bitcast i32* %x to i8*
> + call void @objc_release(i8* %cf) nounwind
> + br label %return
> +
> +return:
> + ret void
> +}
> +
> +; CHECK: define void @test6b(
> ; CHECK-NOT: @objc_
> ; CHECK: }
> -define void @test6(i32* %x, i1 %p) nounwind {
> +define void @test6b(i32* %x, i1 %p) nounwind {
> +entry:
> + %a = bitcast i32* %x to i8*
> + %0 = call i8* @objc_retain(i8* %a) nounwind
> + br i1 %p, label %t, label %f
> +
> +t:
> + store i8 3, i8* %a
> + %b = bitcast i32* %x to float*
> + store float 2.0, float* %b
> + %ct = bitcast i32* %x to i8*
> + call void @objc_release(i8* %ct) nounwind, !clang.imprecise_release !0
> + br label %return
> +
> +f:
> + store i32 7, i32* %x
> + call void @callee()
> + %cf = bitcast i32* %x to i8*
> + call void @objc_release(i8* %cf) nounwind, !clang.imprecise_release !0
> + br label %return
> +
> +return:
> + ret void
> +}
> +
> +; CHECK: define void @test6c(
> +; CHECK: entry:
> +; CHECK: tail call i8* @objc_retain(
> +; CHECK: t:
> +; CHECK: call void @objc_release(
> +; CHECK: f:
> +; CHECK: call void @objc_release(
> +; CHECK: return:
> +; CHECK: }
> +define void @test6c(i32* %x, i1 %p) nounwind {
> entry:
> %a = bitcast i32* %x to i8*
> %0 = call i8* @objc_retain(i8* %a) nounwind
> @@ -244,6 +488,40 @@ f:
> store i32 7, i32* %x
> call void @callee()
> %cf = bitcast i32* %x to i8*
> + call void @objc_release(i8* %cf) nounwind, !clang.imprecise_release !0
> + br label %return
> +
> +return:
> + ret void
> +}
> +
> +; CHECK: define void @test6d(
> +; CHECK: entry:
> +; CHECK: tail call i8* @objc_retain(
> +; CHECK: t:
> +; CHECK: call void @objc_release(
> +; CHECK: f:
> +; CHECK: call void @objc_release(
> +; CHECK: return:
> +; CHECK: }
> +define void @test6d(i32* %x, i1 %p) nounwind {
> +entry:
> + %a = bitcast i32* %x to i8*
> + %0 = call i8* @objc_retain(i8* %a) nounwind
> + br i1 %p, label %t, label %f
> +
> +t:
> + store i8 3, i8* %a
> + %b = bitcast i32* %x to float*
> + store float 2.0, float* %b
> + %ct = bitcast i32* %x to i8*
> + call void @objc_release(i8* %ct) nounwind, !clang.imprecise_release !0
> + br label %return
> +
> +f:
> + store i32 7, i32* %x
> + call void @callee()
> + %cf = bitcast i32* %x to i8*
> call void @objc_release(i8* %cf) nounwind
> br label %return
>
> @@ -251,11 +529,19 @@ return:
> ret void
> }
>
> +
> ; retain+release pair deletion, where the retain happens on two different
> ; flow paths.
>
> -; CHECK: define void @test7(
> -; CHECK-NOT: @objc_
> +; CHECK: define void @test7(
> +; CHECK: entry:
> +; CHECK-NOT: objc_
> +; CHECK: t:
> +; CHECK: call i8* @objc_retain
> +; CHECK: f:
> +; CHECK: call i8* @objc_retain
> +; CHECK: return:
> +; CHECK: call void @objc_release
> ; CHECK: }
> define void @test7(i32* %x, i1 %p) nounwind {
> entry:
> @@ -281,17 +567,44 @@ return:
> ret void
> }
>
> +; CHECK: define void @test7b(
> +; CHECK-NOT: @objc_
> +; CHECK: }
> +define void @test7b(i32* %x, i1 %p) nounwind {
> +entry:
> + %a = bitcast i32* %x to i8*
> + br i1 %p, label %t, label %f
> +
> +t:
> + %0 = call i8* @objc_retain(i8* %a) nounwind
> + store i8 3, i8* %a
> + %b = bitcast i32* %x to float*
> + store float 2.0, float* %b
> + br label %return
> +
> +f:
> + %1 = call i8* @objc_retain(i8* %a) nounwind
> + store i32 7, i32* %x
> + call void @callee()
> + br label %return
> +
> +return:
> + %c = bitcast i32* %x to i8*
> + call void @objc_release(i8* %c) nounwind, !clang.imprecise_release !0
> + ret void
> +}
> +
> ; Like test7, but there's a retain/retainBlock mismatch. Don't delete!
>
> -; CHECK: define void @test7b
> +; CHECK: define void @test7c
> ; CHECK: t:
> -; CHECK: call i8* @objc_retainBlock
> +; CHECK: call i8* @objc_retainBlock
> ; CHECK: f:
> -; CHECK: call i8* @objc_retain
> +; CHECK: call i8* @objc_retain
> ; CHECK: return:
> -; CHECK: call void @objc_release
> +; CHECK: call void @objc_release
> ; CHECK: }
> -define void @test7b(i32* %x, i1 %p) nounwind {
> +define void @test7c(i32* %x, i1 %p) nounwind {
> entry:
> %a = bitcast i32* %x to i8*
> br i1 %p, label %t, label %f
> @@ -310,18 +623,162 @@ f:
> br label %return
>
> return:
> - %c = bitcast i32* %x to i8*
> - call void @objc_release(i8* %c) nounwind
> + %c = bitcast i32* %x to i8*
> + call void @objc_release(i8* %c) nounwind
> + ret void
> +}
> +
> +; retain+release pair deletion, where the retain and release both happen on
> +; different flow paths. Wild!
> +
> +; CHECK: define void @test8a(
> +; CHECK: entry:
> +; CHECK: t:
> +; CHECK: @objc_retain
> +; CHECK: f:
> +; CHECK: @objc_retain
> +; CHECK: mid:
> +; CHECK: u:
> +; CHECK: @objc_release
> +; CHECK: g:
> +; CHECK: @objc_release
> +; CHECK: return:
> +; CHECK: }
> +define void @test8a(i32* %x, i1 %p, i1 %q) nounwind {
> +entry:
> + %a = bitcast i32* %x to i8*
> + br i1 %p, label %t, label %f
> +
> +t:
> + %0 = call i8* @objc_retain(i8* %a) nounwind
> + store i8 3, i8* %a
> + %b = bitcast i32* %x to float*
> + store float 2.0, float* %b
> + br label %mid
> +
> +f:
> + %1 = call i8* @objc_retain(i8* %a) nounwind
> + store i32 7, i32* %x
> + br label %mid
> +
> +mid:
> + br i1 %q, label %u, label %g
> +
> +u:
> + call void @callee()
> + %cu = bitcast i32* %x to i8*
> + call void @objc_release(i8* %cu) nounwind
> + br label %return
> +
> +g:
> + %cg = bitcast i32* %x to i8*
> + call void @objc_release(i8* %cg) nounwind
> + br label %return
> +
> +return:
> + ret void
> +}
> +
> +; CHECK: define void @test8b(
> +; CHECK-NOT: @objc_
> +; CHECK: }
> +define void @test8b(i32* %x, i1 %p, i1 %q) nounwind {
> +entry:
> + %a = bitcast i32* %x to i8*
> + br i1 %p, label %t, label %f
> +
> +t:
> + %0 = call i8* @objc_retain(i8* %a) nounwind
> + store i8 3, i8* %a
> + %b = bitcast i32* %x to float*
> + store float 2.0, float* %b
> + br label %mid
> +
> +f:
> + %1 = call i8* @objc_retain(i8* %a) nounwind
> + store i32 7, i32* %x
> + br label %mid
> +
> +mid:
> + br i1 %q, label %u, label %g
> +
> +u:
> + call void @callee()
> + %cu = bitcast i32* %x to i8*
> + call void @objc_release(i8* %cu) nounwind, !clang.imprecise_release !0
> + br label %return
> +
> +g:
> + %cg = bitcast i32* %x to i8*
> + call void @objc_release(i8* %cg) nounwind, !clang.imprecise_release !0
> + br label %return
> +
> +return:
> + ret void
> +}
> +
> +; CHECK: define void @test8c(
> +; CHECK: entry:
> +; CHECK: t:
> +; CHECK: @objc_retain
> +; CHECK: f:
> +; CHECK: @objc_retain
> +; CHECK: mid:
> +; CHECK: u:
> +; CHECK: @objc_release
> +; CHECK: g:
> +; CHECK: @objc_release
> +; CHECK: return:
> +; CHECK: }
> +define void @test8c(i32* %x, i1 %p, i1 %q) nounwind {
> +entry:
> + %a = bitcast i32* %x to i8*
> + br i1 %p, label %t, label %f
> +
> +t:
> + %0 = call i8* @objc_retain(i8* %a) nounwind
> + store i8 3, i8* %a
> + %b = bitcast i32* %x to float*
> + store float 2.0, float* %b
> + br label %mid
> +
> +f:
> + %1 = call i8* @objc_retain(i8* %a) nounwind
> + store i32 7, i32* %x
> + br label %mid
> +
> +mid:
> + br i1 %q, label %u, label %g
> +
> +u:
> + call void @callee()
> + %cu = bitcast i32* %x to i8*
> + call void @objc_release(i8* %cu) nounwind
> + br label %return
> +
> +g:
> + %cg = bitcast i32* %x to i8*
> + call void @objc_release(i8* %cg) nounwind, !clang.imprecise_release !0
> + br label %return
> +
> +return:
> ret void
> }
>
> -; retain+release pair deletion, where the retain and release both happen on
> -; different flow paths. Wild!
> -
> -; CHECK: define void @test8(
> -; CHECK-NOT: @objc_
> +; CHECK: define void @test8d(
> +; CHECK: entry:
> +; CHECK: t:
> +; CHECK: @objc_retain
> +; CHECK: f:
> +; CHECK: @objc_retain
> +; CHECK: mid:
> +; CHECK: u:
> +; CHECK: @objc_release
> +; CHECK: g:
> +; CHECK: @objc_release
> +; CHECK: return:
> ; CHECK: }
> -define void @test8(i32* %x, i1 %p, i1 %q) nounwind {
> +define void @test8d(i32* %x, i1 %p, i1 %q) nounwind {
> entry:
> %a = bitcast i32* %x to i8*
> br i1 %p, label %t, label %f
> @@ -344,7 +801,7 @@ mid:
> u:
> call void @callee()
> %cu = bitcast i32* %x to i8*
> - call void @objc_release(i8* %cu) nounwind
> + call void @objc_release(i8* %cu) nounwind, !clang.imprecise_release !0
> br label %return
>
> g:
> @@ -583,7 +1040,9 @@ entry:
>
> ; CHECK: define void @test15b
> ; CHECK-NEXT: entry:
> +; CHECK-NEXT: @objc_retain
> ; CHECK-NEXT: @objc_autorelease
> +; CHECK-NEXT: @objc_release
> ; CHECK-NEXT: ret void
> ; CHECK-NEXT: }
> define void @test15b(i8* %x, i64 %n) {
> @@ -594,13 +1053,60 @@ entry:
> ret void
> }
>
> +; CHECK: define void @test15c
> +; CHECK-NEXT: entry:
> +; CHECK-NEXT: @objc_autorelease
> +; CHECK-NEXT: ret void
> +; CHECK-NEXT: }
> +define void @test15c(i8* %x, i64 %n) {
> +entry:
> + call i8* @objc_retain(i8* %x) nounwind
> + call i8* @objc_autorelease(i8* %x) nounwind
> + call void @objc_release(i8* %x) nounwind, !clang.imprecise_release !0
> + ret void
> +}
> +
> ; Retain+release pairs in diamonds, all dominated by a retain.
>
> -; CHECK: define void @test16(
> +; CHECK: define void @test16a(
> +; CHECK: @objc_retain(i8* %x)
> +; CHECK-NOT: @objc
> +; CHECK: }
> +define void @test16a(i1 %a, i1 %b, i8* %x) {
> +entry:
> + call i8* @objc_retain(i8* %x) nounwind
> + br i1 %a, label %red, label %orange
> +
> +red:
> + call i8* @objc_retain(i8* %x) nounwind
> + br label %yellow
> +
> +orange:
> + call i8* @objc_retain(i8* %x) nounwind
> + br label %yellow
> +
> +yellow:
> + call void @use_pointer(i8* %x)
> + call void @use_pointer(i8* %x)
> + br i1 %b, label %green, label %blue
> +
> +green:
> + call void @objc_release(i8* %x) nounwind
> + br label %purple
> +
> +blue:
> + call void @objc_release(i8* %x) nounwind
> + br label %purple
> +
> +purple:
> + ret void
> +}
> +
> +; CHECK: define void @test16b(
> ; CHECK: @objc_retain(i8* %x)
> ; CHECK-NOT: @objc
> ; CHECK: }
> -define void @test16(i1 %a, i1 %b, i8* %x) {
> +define void @test16b(i1 %a, i1 %b, i8* %x) {
> entry:
> call i8* @objc_retain(i8* %x) nounwind
> br i1 %a, label %red, label %orange
> @@ -619,17 +1125,86 @@ yellow:
> br i1 %b, label %green, label %blue
>
> green:
> + call void @objc_release(i8* %x) nounwind, !clang.imprecise_release !0
> + br label %purple
> +
> +blue:
> call void @objc_release(i8* %x) nounwind
> br label %purple
>
> +purple:
> + ret void
> +}
> +
> +; CHECK: define void @test16c(
> +; CHECK: @objc_retain(i8* %x)
> +; CHECK-NOT: @objc
> +; CHECK: }
> +define void @test16c(i1 %a, i1 %b, i8* %x) {
> +entry:
> + call i8* @objc_retain(i8* %x) nounwind
> + br i1 %a, label %red, label %orange
> +
> +red:
> + call i8* @objc_retain(i8* %x) nounwind
> + br label %yellow
> +
> +orange:
> + call i8* @objc_retain(i8* %x) nounwind
> + br label %yellow
> +
> +yellow:
> + call void @use_pointer(i8* %x)
> + call void @use_pointer(i8* %x)
> + br i1 %b, label %green, label %blue
> +
> +green:
> + call void @objc_release(i8* %x) nounwind, !clang.imprecise_release !0
> + br label %purple
> +
> blue:
> + call void @objc_release(i8* %x) nounwind, !clang.imprecise_release !0
> + br label %purple
> +
> +purple:
> + ret void
> +}
> +
> +; CHECK: define void @test16d(
> +; CHECK: @objc_retain(i8* %x)
> +; CHECK-NOT: @objc
> +; CHECK: }
> +define void @test16d(i1 %a, i1 %b, i8* %x) {
> +entry:
> + call i8* @objc_retain(i8* %x) nounwind
> + br i1 %a, label %red, label %orange
> +
> +red:
> + call i8* @objc_retain(i8* %x) nounwind
> + br label %yellow
> +
> +orange:
> + call i8* @objc_retain(i8* %x) nounwind
> + br label %yellow
> +
> +yellow:
> + call void @use_pointer(i8* %x)
> + call void @use_pointer(i8* %x)
> + br i1 %b, label %green, label %blue
> +
> +green:
> call void @objc_release(i8* %x) nounwind
> br label %purple
>
> +blue:
> + call void @objc_release(i8* %x) nounwind, !clang.imprecise_release !0
> + br label %purple
> +
> purple:
> ret void
> }
>
> +
> ; Retain+release pairs in diamonds, all post-dominated by a release.
>
> ; CHECK: define void @test17(
> @@ -1163,12 +1738,16 @@ done:
> ret void
> }
>
> -; Delete retain,release if there's just a possible dec.
> +; Delete retain,release if there's just a possible dec and we have imprecise
> +; releases.
>
> -; CHECK: define void @test34(
> -; CHECK-NOT: @objc_
> +; CHECK: define void @test34a(
> +; CHECK: call i8* @objc_retain
> +; CHECK: true:
> +; CHECK: done:
> +; CHECK: call void @objc_release
> ; CHECK: }
> -define void @test34(i8* %p, i1 %x, i8* %y) {
> +define void @test34a(i8* %p, i1 %x, i8* %y) {
> entry:
> %f0 = call i8* @objc_retain(i8* %p)
> br i1 %x, label %true, label %done
> @@ -1184,12 +1763,38 @@ done:
> ret void
> }
>
> -; Delete retain,release if there's just a use.
> -
> -; CHECK: define void @test35(
> +; CHECK: define void @test34b(
> ; CHECK-NOT: @objc_
> ; CHECK: }
> -define void @test35(i8* %p, i1 %x, i8* %y) {
> +define void @test34b(i8* %p, i1 %x, i8* %y) {
> +entry:
> + %f0 = call i8* @objc_retain(i8* %p)
> + br i1 %x, label %true, label %done
> +
> +true:
> + call void @callee()
> + br label %done
> +
> +done:
> + %g = bitcast i8* %p to i8*
> + %h = getelementptr i8* %g, i64 0
> + call void @objc_release(i8* %g), !clang.imprecise_release !0
> + ret void
> +}
> +
> +
> +; Delete retain,release if there's just a use and we do not have a precise
> +; release.
> +
> +; Precise.
> +; CHECK: define void @test35a(
> +; CHECK: entry:
> +; CHECK: call i8* @objc_retain
> +; CHECK: true:
> +; CHECK: done:
> +; CHECK: call void @objc_release
> +; CHECK: }
> +define void @test35a(i8* %p, i1 %x, i8* %y) {
> entry:
> %f0 = call i8* @objc_retain(i8* %p)
> br i1 %x, label %true, label %done
> @@ -1205,16 +1810,36 @@ done:
> ret void
> }
>
> -; Delete a retain,release if there's no actual use.
> -
> -; CHECK: define void @test36(
> +; Imprecise.
> +; CHECK: define void @test35b(
> ; CHECK-NOT: @objc_
> +; CHECK: }
> +define void @test35b(i8* %p, i1 %x, i8* %y) {
> +entry:
> + %f0 = call i8* @objc_retain(i8* %p)
> + br i1 %x, label %true, label %done
> +
> +true:
> + %v = icmp eq i8* %p, %y
> + br label %done
> +
> +done:
> + %g = bitcast i8* %p to i8*
> + %h = getelementptr i8* %g, i64 0
> + call void @objc_release(i8* %g), !clang.imprecise_release !0
> + ret void
> +}
> +
> +; Delete a retain,release if there's no actual use and we have precise release.
> +
> +; CHECK: define void @test36a(
> +; CHECK: @objc_retain
> ; CHECK: call void @callee()
> ; CHECK-NOT: @objc_
> ; CHECK: call void @callee()
> -; CHECK-NOT: @objc_
> +; CHECK: @objc_release
> ; CHECK: }
> -define void @test36(i8* %p) {
> +define void @test36a(i8* %p) {
> entry:
> call i8* @objc_retain(i8* %p)
> call void @callee()
> @@ -1225,10 +1850,10 @@ entry:
>
> ; Like test36, but with metadata.
>
> -; CHECK: define void @test37(
> +; CHECK: define void @test36b(
> ; CHECK-NOT: @objc_
> ; CHECK: }
> -define void @test37(i8* %p) {
> +define void @test36b(i8* %p) {
> entry:
> call i8* @objc_retain(i8* %p)
> call void @callee()
> @@ -1499,27 +2124,45 @@ define i8* @test49(i8* %p) nounwind {
> ret i8* %x
> }
>
> -; Do delete retain+release with intervening stores of the
> -; address value.
> +; Do delete retain+release with intervening stores of the address value if we
> +; have imprecise release attached to objc_release.
> +
> +; CHECK: define void @test50a(
> +; CHECK-NEXT: call i8* @objc_retain
> +; CHECK-NEXT: call void @callee
> +; CHECK-NEXT: store
> +; CHECK-NEXT: call void @objc_release
> +; CHECK-NEXT: ret void
> +; CHECK-NEXT: }
> +define void @test50a(i8* %p, i8** %pp) {
> + call i8* @objc_retain(i8* %p)
> + call void @callee()
> + store i8* %p, i8** %pp
> + call void @objc_release(i8* %p)
> + ret void
> +}
>
> -; CHECK: define void @test50(
> +; CHECK: define void @test50b(
> ; CHECK-NOT: @objc_
> ; CHECK: }
> -define void @test50(i8* %p, i8** %pp) {
> +define void @test50b(i8* %p, i8** %pp) {
> call i8* @objc_retain(i8* %p)
> call void @callee()
> store i8* %p, i8** %pp
> - call void @objc_release(i8* %p)
> + call void @objc_release(i8* %p), !clang.imprecise_release !0
> ret void
> }
>
> +
> ; Don't delete retain+release with intervening stores through the
> ; address value.
>
> -; CHECK: define void @test51(
> +; CHECK: define void @test51a(
> ; CHECK: call i8* @objc_retain(i8* %p)
> ; CHECK: call void @objc_release(i8* %p)
> -define void @test51(i8* %p) {
> +; CHECK: ret void
> +; CHECK: }
> +define void @test51a(i8* %p) {
> call i8* @objc_retain(i8* %p)
> call void @callee()
> store i8 0, i8* %p
> @@ -1527,15 +2170,30 @@ define void @test51(i8* %p) {
> ret void
> }
>
> +; CHECK: define void @test51b(
> +; CHECK: call i8* @objc_retain(i8* %p)
> +; CHECK: call void @objc_release(i8* %p)
> +; CHECK: ret void
> +; CHECK: }
> +define void @test51b(i8* %p) {
> + call i8* @objc_retain(i8* %p)
> + call void @callee()
> + store i8 0, i8* %p
> + call void @objc_release(i8* %p), !clang.imprecise_release !0
> + ret void
> +}
> +
> ; Don't delete retain+release with intervening use of a pointer of
> ; unknown provenance.
>
> -; CHECK: define void @test52(
> +; CHECK: define void @test52a(
> ; CHECK: call i8* @objc_retain
> ; CHECK: call void @callee()
> ; CHECK: call void @use_pointer(i8* %z)
> ; CHECK: call void @objc_release
> -define void @test52(i8** %zz, i8** %pp) {
> +; CHECK: ret void
> +; CHECK: }
> +define void @test52a(i8** %zz, i8** %pp) {
> %p = load i8** %pp
> %1 = call i8* @objc_retain(i8* %p)
> call void @callee()
> @@ -1545,6 +2203,23 @@ define void @test52(i8** %zz, i8** %pp)
> ret void
> }
>
> +; CHECK: define void @test52b(
> +; CHECK: call i8* @objc_retain
> +; CHECK: call void @callee()
> +; CHECK: call void @use_pointer(i8* %z)
> +; CHECK: call void @objc_release
> +; CHECK: ret void
> +; CHECK: }
> +define void @test52b(i8** %zz, i8** %pp) {
> + %p = load i8** %pp
> + %1 = call i8* @objc_retain(i8* %p)
> + call void @callee()
> + %z = load i8** %zz
> + call void @use_pointer(i8* %z)
> + call void @objc_release(i8* %p), !clang.imprecise_release !0
> + ret void
> +}
> +
> ; Like test52, but the pointer has function type, so it's assumed to
> ; be not reference counted.
> ; Oops. That's wrong. Clang sometimes uses function types gratuitously.
> @@ -1697,19 +2372,78 @@ entry:
> @constptr = external constant i8*
> @something = external global i8*
>
> -; CHECK: define void @test60(
> -; CHECK-NOT: @objc_
> +; We have a precise lifetime retain/release here. We can not remove them since
> +; @something is not constant.
> +
> +; CHECK: define void @test60a(
> +; CHECK: call i8* @objc_retain
> +; CHECK: call void @objc_release
> +; CHECK: }
> +define void @test60a() {
> + %t = load i8** @constptr
> + %s = load i8** @something
> + call i8* @objc_retain(i8* %s)
> + call void @callee()
> + call void @use_pointer(i8* %t)
> + call void @objc_release(i8* %s)
> + ret void
> +}
> +
> +; CHECK: define void @test60b(
> +; CHECK: call i8* @objc_retain
> +; CHECK-NOT: call i8* @objc_retain
> +; CHECK-NOT: call i8* @objc_rrelease
> ; CHECK: }
> -define void @test60() {
> +define void @test60b() {
> %t = load i8** @constptr
> %s = load i8** @something
> call i8* @objc_retain(i8* %s)
> + call i8* @objc_retain(i8* %s)
> call void @callee()
> call void @use_pointer(i8* %t)
> call void @objc_release(i8* %s)
> ret void
> }
>
> +; CHECK: define void @test60c(
> +; CHECK-NOT: @objc_
> +; CHECK: }
> +define void @test60c() {
> + %t = load i8** @constptr
> + %s = load i8** @something
> + call i8* @objc_retain(i8* %s)
> + call void @callee()
> + call void @use_pointer(i8* %t)
> + call void @objc_release(i8* %s), !clang.imprecise_release !0
> + ret void
> +}
> +
> +; CHECK: define void @test60d(
> +; CHECK-NOT: @objc_
> +; CHECK: }
> +define void @test60d() {
> + %t = load i8** @constptr
> + %s = load i8** @something
> + call i8* @objc_retain(i8* %t)
> + call void @callee()
> + call void @use_pointer(i8* %s)
> + call void @objc_release(i8* %t)
> + ret void
> +}
> +
> +; CHECK: define void @test60e(
> +; CHECK-NOT: @objc_
> +; CHECK: }
> +define void @test60e() {
> + %t = load i8** @constptr
> + %s = load i8** @something
> + call i8* @objc_retain(i8* %t)
> + call void @callee()
> + call void @use_pointer(i8* %s)
> + call void @objc_release(i8* %t), !clang.imprecise_release !0
> + ret void
> +}
> +
> ; Constant pointers to objects don't need to be considered related to other
> ; pointers.
>
> @@ -1873,32 +2607,6 @@ return:
> ret i8* %retval
> }
>
> -; An objc_retain can serve as a may-use for a different pointer.
> -; rdar://11931823
> -
> -; CHECK: define void @test66(
> -; CHECK: %tmp7 = tail call i8* @objc_retain(i8* %cond) [[NUW]]
> -; CHECK: tail call void @objc_release(i8* %cond) [[NUW]]
> -; CHECK: }
> -define void @test66(i8* %tmp5, i8* %bar, i1 %tobool, i1 %tobool1, i8* %call) {
> -entry:
> - br i1 %tobool, label %cond.true, label %cond.end
> -
> -cond.true:
> - br label %cond.end
> -
> -cond.end: ; preds = %cond.true, %entry
> - %cond = phi i8* [ %tmp5, %cond.true ], [ %call, %entry ]
> - %tmp7 = tail call i8* @objc_retain(i8* %cond) nounwind
> - tail call void @objc_release(i8* %call) nounwind
> - %tmp8 = select i1 %tobool1, i8* %cond, i8* %bar
> - %tmp9 = tail call i8* @objc_retain(i8* %tmp8) nounwind
> - tail call void @objc_release(i8* %cond) nounwind
> - ret void
> -}
> -
> -declare void @bar(i32 ()*)
> -
> ; A few real-world testcases.
>
> @.str4 = private unnamed_addr constant [33 x i8] c"-[A z] = { %f, %f, { %f, %f } }\0A\00"
>
> Modified: llvm/trunk/test/Transforms/ObjCARC/intrinsic-use.ll
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/ObjCARC/intrinsic-use.ll?rev=178921&r1=178920&r2=178921&view=diff
> ==============================================================================
> --- llvm/trunk/test/Transforms/ObjCARC/intrinsic-use.ll (original)
> +++ llvm/trunk/test/Transforms/ObjCARC/intrinsic-use.ll Fri Apr 5 17:54:28 2013
> @@ -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 !{}
> +
>
> Modified: llvm/trunk/test/Transforms/ObjCARC/retain-block-escape-analysis.ll
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/ObjCARC/retain-block-escape-analysis.ll?rev=178921&r1=178920&r2=178921&view=diff
> ==============================================================================
> --- llvm/trunk/test/Transforms/ObjCARC/retain-block-escape-analysis.ll (original)
> +++ llvm/trunk/test/Transforms/ObjCARC/retain-block-escape-analysis.ll Fri Apr 5 17:54:28 2013
> @@ -23,6 +23,23 @@ define void @bitcasttest(i8* %storage, v
> ; 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
> %t2 = tail call i8* @objc_retain(i8* %t1)
> ; CHECK: tail call i8* @objc_retainBlock
> @@ -31,14 +48,34 @@ 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
> %t2 = tail call i8* @objc_retain(i8* %t1)
> ; CHECK: tail call i8* @objc_retainBlock
> @@ -49,8 +86,9 @@ 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,
> @@ -58,6 +96,24 @@ define void @selecttest(void (...)** %st
> ; 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
> %t2 = tail call i8* @objc_retain(i8* %t1)
> ; CHECK: tail call i8* @objc_retainBlock
> @@ -66,8 +122,9 @@ 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,
> @@ -76,6 +133,36 @@ define void @phinodetest(void (...)** %s
> ; 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
> %t2 = tail call i8* @objc_retain(i8* %t1)
> ; CHECK: tail call i8* @objc_retainBlock
> @@ -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 ;
>
>
> _______________________________________________
> llvm-commits mailing list
> llvm-commits at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits
More information about the llvm-commits
mailing list