; RUN: opt -S -objc-arc < %s | FileCheck %s

declare i8* @objc_retain(i8*) nonlazybind
declare void @objc_release(i8*) nonlazybind
declare i8* @objc_retainBlock(i8*)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Use by an instruction which copies the value is an escape if the             ;
; result is an escape. The current instructions with this property are:        ;
;                                                                              ;
; 1. BitCast.                                                                  ;
; 2. GEP.                                                                      ;
; 3. PhiNode.                                                                  ;
; 4. SelectInst.                                                               ;
;                                                                              ;
; Make sure that such instructions do not confuse the optimizer into removing  ;
; an objc_retainBlock that is needed.                                          ;
;                                                                              ;
; rdar://13273675. (With extra test cases to handle bitcast, phi, and select.  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

define void @bitcasttest(i8* %storage, void (...)* %block)  {
; CHECK: define void @bitcasttest
entry:
  %t1 = bitcast void (...)* %block to i8*
; CHECK: tail call i8* @objc_retain
  %t2 = tail call i8* @objc_retain(i8* %t1)
; CHECK: tail call i8* @objc_retainBlock
  %t3 = tail call i8* @objc_retainBlock(i8* %t1), !clang.arc.copy_on_escape !0
  %t4 = bitcast i8* %storage to void (...)**
  %t5 = bitcast i8* %t3 to void (...)*
  store void (...)* %t5, void (...)** %t4, align 8
; CHECK: call void @objc_release
  call void @objc_release(i8* %t1)
  ret void
; CHECK: }
}

define void @bitcasttest_a(i8* %storage, void (...)* %block)  {
; CHECK: define void @bitcasttest_a
entry:
  %t1 = bitcast void (...)* %block to i8*
; CHECK-NOT: tail call i8* @objc_retain
  %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-NOT: call void @objc_release
  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
  %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-NOT: call void @objc_release
  call void @objc_release(i8* %t1), !clang.imprecise_release !0
  ret void
; CHECK: }
}

define void @selecttest(void (...)** %store1, void (...)** %store2,
                        void (...)* %block) {
; CHECK: define void @selecttest
entry:
  %t1 = bitcast void (...)* %block to i8*
; CHECK: tail call i8* @objc_retain
  %t2 = tail call i8* @objc_retain(i8* %t1)
; CHECK: tail call i8* @objc_retainBlock
  %t3 = tail call i8* @objc_retainBlock(i8* %t1), !clang.arc.copy_on_escape !0
  %t4 = bitcast i8* %t3 to void (...)*
  %store = select i1 undef, void (...)** %store1, void (...)** %store2
  store void (...)* %t4, void (...)** %store, align 8
; CHECK: call void @objc_release
  call void @objc_release(i8* %t1)
  ret void
; CHECK: }
}

define void @selecttest_a(void (...)** %store1, void (...)** %store2,
                          void (...)* %block) {
; CHECK: define void @selecttest_a
entry:
  %t1 = bitcast void (...)* %block to i8*
; CHECK-NOT: tail call i8* @objc_retain
  %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-NOT: call void @objc_release
  call void @objc_release(i8* %t1), !clang.imprecise_release !0
  ret void
; CHECK: }
}

define void @phinodetest(void (...)** %storage1,
                         void (...)** %storage2,
                         void (...)* %block) {
; CHECK: define void @phinodetest
entry:
  %t1 = bitcast void (...)* %block to i8*
; CHECK: tail call i8* @objc_retain
  %t2 = tail call i8* @objc_retain(i8* %t1)
; CHECK: tail call i8* @objc_retainBlock
  %t3 = tail call i8* @objc_retainBlock(i8* %t1), !clang.arc.copy_on_escape !0
  %t4 = bitcast i8* %t3 to void (...)*
  br i1 undef, label %store1_set, label %store2_set
; CHECK: store1_set:

store1_set:
  br label %end

store2_set:
  br label %end

end:
; CHECK: end:
  %storage = phi void (...)** [ %storage1, %store1_set ], [ %storage2, %store2_set]
  store void (...)* %t4, void (...)** %storage, align 8
; CHECK: call void @objc_release
  call void @objc_release(i8* %t1)
  ret void
; CHECK: }
}

define void @phinodetest_a(void (...)** %storage1,
                           void (...)** %storage2,
                           void (...)* %block) {
; CHECK: define void @phinodetest_a
entry:
  %t1 = bitcast void (...)* %block to i8*
; CHECK-NOT: tail call i8* @objc_retain
  %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

store1_set:
  br label %end

store2_set:
  br label %end

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), !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    ;
; test case looks a little convoluted since it was produced by	     ;
; bugpoint.							     ;
; 								     ;
; bugzilla://14551						     ;
; rdar://12851911						     ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

define void @phinode_use_cycle(i8* %block) uwtable optsize ssp {
; CHECK: define void @phinode_use_cycle(i8* %block)
entry:
  br label %for.body

for.body:                                         ; preds = %if.then, %for.body, %entry
  %block.05 = phi void (...)* [ null, %entry ], [ %1, %if.then ], [ %block.05, %for.body ]
  br i1 undef, label %for.body, label %if.then

if.then:                                          ; preds = %for.body
  %0 = call i8* @objc_retainBlock(i8* %block), !clang.arc.copy_on_escape !0
  %1 = bitcast i8* %0 to void (...)*
  %2 = bitcast void (...)* %block.05 to i8*
  call void @objc_release(i8* %2) nounwind, !clang.imprecise_release !0
  br label %for.body
}

!0 = metadata !{}
