declare void @callee_fnptr(void ()*)
declare void @invokee()
declare i8* @returner()
+declare void @bar(i32 ()*)
declare void @llvm.dbg.value(metadata, i64, metadata)
; 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
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
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_
; 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
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
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
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
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
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
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, !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()
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:
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
; 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 @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
+}
+
+; 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
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:
; 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) {
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
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(
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
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
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()
; 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()
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
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()
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.
@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.
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"