[llvm] r178921 - Properly model precise lifetime when given an incomplete dataflow sequence.

Michael Gottesman mgottesman at apple.com
Fri Apr 5 15:54:28 PDT 2013


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    ;





More information about the llvm-commits mailing list