[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