2013-05-13 23:49:42 +00:00
|
|
|
; RUN: opt -objc-arc -S < %s | FileCheck %s
|
|
|
|
|
|
|
|
declare i8* @objc_retain(i8*)
|
|
|
|
declare i8* @objc_retainAutoreleasedReturnValue(i8*)
|
|
|
|
declare void @objc_release(i8*)
|
|
|
|
declare i8* @objc_autorelease(i8*)
|
|
|
|
declare i8* @objc_autoreleaseReturnValue(i8*)
|
|
|
|
declare void @objc_autoreleasePoolPop(i8*)
|
|
|
|
declare i8* @objc_autoreleasePoolPush()
|
|
|
|
declare i8* @objc_retainBlock(i8*)
|
|
|
|
|
|
|
|
declare i8* @objc_retainedObject(i8*)
|
|
|
|
declare i8* @objc_unretainedObject(i8*)
|
|
|
|
declare i8* @objc_unretainedPointer(i8*)
|
|
|
|
|
|
|
|
declare void @use_pointer(i8*)
|
|
|
|
declare void @callee()
|
|
|
|
declare void @callee_fnptr(void ()*)
|
|
|
|
declare void @invokee()
|
|
|
|
declare i8* @returner()
|
|
|
|
declare void @bar(i32 ()*)
|
|
|
|
declare void @use_alloca(i8**)
|
|
|
|
|
|
|
|
declare void @llvm.dbg.value(metadata, i64, metadata)
|
|
|
|
|
|
|
|
declare i8* @objc_msgSend(i8*, i8*, ...)
|
|
|
|
|
|
|
|
|
|
|
|
; In the presense of allocas, unconditionally remove retain/release pairs only
|
|
|
|
; if they are known safe in both directions. This prevents matching up an inner
|
|
|
|
; retain with the boundary guarding release in the following situation:
|
|
|
|
;
|
|
|
|
; %A = alloca
|
|
|
|
; retain(%x)
|
|
|
|
; retain(%x) <--- Inner Retain
|
|
|
|
; store %x, %A
|
|
|
|
; %y = load %A
|
|
|
|
; ... DO STUFF ...
|
|
|
|
; release(%y)
|
|
|
|
; release(%x) <--- Guarding Release
|
|
|
|
;
|
|
|
|
; rdar://13750319
|
|
|
|
|
|
|
|
; CHECK: define void @test1a(i8* %x)
|
|
|
|
; CHECK: @objc_retain(i8* %x)
|
|
|
|
; CHECK: @objc_retain(i8* %x)
|
|
|
|
; CHECK: @objc_release(i8* %y)
|
|
|
|
; CHECK: @objc_release(i8* %x)
|
|
|
|
; CHECK: ret void
|
|
|
|
; CHECK: }
|
|
|
|
define void @test1a(i8* %x) {
|
|
|
|
entry:
|
|
|
|
%A = alloca i8*
|
|
|
|
tail call i8* @objc_retain(i8* %x)
|
|
|
|
tail call i8* @objc_retain(i8* %x)
|
|
|
|
store i8* %x, i8** %A, align 8
|
|
|
|
%y = load i8** %A
|
|
|
|
call void @use_alloca(i8** %A)
|
|
|
|
call void @objc_release(i8* %y), !clang.imprecise_release !0
|
|
|
|
call void @use_pointer(i8* %x)
|
|
|
|
call void @objc_release(i8* %x), !clang.imprecise_release !0
|
|
|
|
ret void
|
|
|
|
}
|
|
|
|
|
|
|
|
; CHECK: define void @test1b(i8* %x)
|
|
|
|
; CHECK: @objc_retain(i8* %x)
|
|
|
|
; CHECK: @objc_retain(i8* %x)
|
|
|
|
; CHECK: @objc_release(i8* %y)
|
|
|
|
; CHECK: @objc_release(i8* %x)
|
|
|
|
; CHECK: ret void
|
|
|
|
; CHECK: }
|
|
|
|
define void @test1b(i8* %x) {
|
|
|
|
entry:
|
|
|
|
%A = alloca i8*
|
|
|
|
%gep = getelementptr i8** %A, i32 0
|
|
|
|
tail call i8* @objc_retain(i8* %x)
|
|
|
|
tail call i8* @objc_retain(i8* %x)
|
|
|
|
store i8* %x, i8** %gep, align 8
|
|
|
|
%y = load i8** %A
|
|
|
|
call void @use_alloca(i8** %A)
|
|
|
|
call void @objc_release(i8* %y), !clang.imprecise_release !0
|
|
|
|
call void @use_pointer(i8* %x)
|
|
|
|
call void @objc_release(i8* %x), !clang.imprecise_release !0
|
|
|
|
ret void
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
; CHECK: define void @test1c(i8* %x)
|
|
|
|
; CHECK: @objc_retain(i8* %x)
|
|
|
|
; CHECK: @objc_retain(i8* %x)
|
|
|
|
; CHECK: @objc_release(i8* %y)
|
|
|
|
; CHECK: @objc_release(i8* %x)
|
|
|
|
; CHECK: ret void
|
|
|
|
; CHECK: }
|
|
|
|
define void @test1c(i8* %x) {
|
|
|
|
entry:
|
|
|
|
%A = alloca i8*, i32 3
|
|
|
|
%gep = getelementptr i8** %A, i32 2
|
|
|
|
tail call i8* @objc_retain(i8* %x)
|
|
|
|
tail call i8* @objc_retain(i8* %x)
|
|
|
|
store i8* %x, i8** %gep, align 8
|
|
|
|
%y = load i8** %gep
|
|
|
|
call void @use_alloca(i8** %A)
|
|
|
|
call void @objc_release(i8* %y), !clang.imprecise_release !0
|
|
|
|
call void @use_pointer(i8* %x)
|
|
|
|
call void @objc_release(i8* %x), !clang.imprecise_release !0
|
|
|
|
ret void
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
; CHECK: define void @test1d(i8* %x)
|
|
|
|
; CHECK: @objc_retain(i8* %x)
|
|
|
|
; CHECK: @objc_retain(i8* %x)
|
|
|
|
; CHECK: @objc_release(i8* %y)
|
|
|
|
; CHECK: @objc_release(i8* %x)
|
|
|
|
; CHECK: ret void
|
|
|
|
; CHECK: }
|
|
|
|
define void @test1d(i8* %x) {
|
|
|
|
entry:
|
|
|
|
br i1 undef, label %use_allocaA, label %use_allocaB
|
|
|
|
|
|
|
|
use_allocaA:
|
|
|
|
%allocaA = alloca i8*
|
|
|
|
br label %exit
|
|
|
|
|
|
|
|
use_allocaB:
|
|
|
|
%allocaB = alloca i8*
|
|
|
|
br label %exit
|
|
|
|
|
|
|
|
exit:
|
|
|
|
%A = phi i8** [ %allocaA, %use_allocaA ], [ %allocaB, %use_allocaB ]
|
|
|
|
%gep = getelementptr i8** %A, i32 0
|
|
|
|
tail call i8* @objc_retain(i8* %x)
|
|
|
|
tail call i8* @objc_retain(i8* %x)
|
|
|
|
store i8* %x, i8** %gep, align 8
|
|
|
|
%y = load i8** %gep
|
|
|
|
call void @use_alloca(i8** %A)
|
|
|
|
call void @objc_release(i8* %y), !clang.imprecise_release !0
|
|
|
|
call void @use_pointer(i8* %x)
|
|
|
|
call void @objc_release(i8* %x), !clang.imprecise_release !0
|
|
|
|
ret void
|
|
|
|
}
|
|
|
|
|
|
|
|
; CHECK: define void @test1e(i8* %x)
|
|
|
|
; CHECK: @objc_retain(i8* %x)
|
|
|
|
; CHECK: @objc_retain(i8* %x)
|
|
|
|
; CHECK: @objc_release(i8* %y)
|
|
|
|
; CHECK: @objc_release(i8* %x)
|
|
|
|
; CHECK: ret void
|
|
|
|
; CHECK: }
|
|
|
|
define void @test1e(i8* %x) {
|
|
|
|
entry:
|
|
|
|
br i1 undef, label %use_allocaA, label %use_allocaB
|
|
|
|
|
|
|
|
use_allocaA:
|
|
|
|
%allocaA = alloca i8*, i32 4
|
|
|
|
br label %exit
|
|
|
|
|
|
|
|
use_allocaB:
|
|
|
|
%allocaB = alloca i8*, i32 4
|
|
|
|
br label %exit
|
|
|
|
|
|
|
|
exit:
|
|
|
|
%A = phi i8** [ %allocaA, %use_allocaA ], [ %allocaB, %use_allocaB ]
|
|
|
|
%gep = getelementptr i8** %A, i32 2
|
|
|
|
tail call i8* @objc_retain(i8* %x)
|
|
|
|
tail call i8* @objc_retain(i8* %x)
|
|
|
|
store i8* %x, i8** %gep, align 8
|
|
|
|
%y = load i8** %gep
|
|
|
|
call void @use_alloca(i8** %A)
|
|
|
|
call void @objc_release(i8* %y), !clang.imprecise_release !0
|
|
|
|
call void @use_pointer(i8* %x)
|
|
|
|
call void @objc_release(i8* %x), !clang.imprecise_release !0
|
|
|
|
ret void
|
|
|
|
}
|
|
|
|
|
|
|
|
; CHECK: define void @test1f(i8* %x)
|
|
|
|
; CHECK: @objc_retain(i8* %x)
|
|
|
|
; CHECK: @objc_retain(i8* %x)
|
|
|
|
; CHECK: @objc_release(i8* %y)
|
|
|
|
; CHECK: @objc_release(i8* %x)
|
|
|
|
; CHECK: ret void
|
|
|
|
; CHECK: }
|
|
|
|
define void @test1f(i8* %x) {
|
|
|
|
entry:
|
|
|
|
%allocaOne = alloca i8*
|
|
|
|
%allocaTwo = alloca i8*
|
|
|
|
%A = select i1 undef, i8** %allocaOne, i8** %allocaTwo
|
|
|
|
tail call i8* @objc_retain(i8* %x)
|
|
|
|
tail call i8* @objc_retain(i8* %x)
|
|
|
|
store i8* %x, i8** %A, align 8
|
|
|
|
%y = load i8** %A
|
|
|
|
call void @use_alloca(i8** %A)
|
|
|
|
call void @objc_release(i8* %y), !clang.imprecise_release !0
|
|
|
|
call void @use_pointer(i8* %x)
|
|
|
|
call void @objc_release(i8* %x), !clang.imprecise_release !0
|
|
|
|
ret void
|
|
|
|
}
|
|
|
|
|
2013-05-24 20:44:02 +00:00
|
|
|
; Make sure that if a store is in a different basic block we handle known safe
|
|
|
|
; conservatively.
|
|
|
|
|
|
|
|
|
|
|
|
; CHECK: define void @test2a(i8* %x)
|
|
|
|
; CHECK: @objc_retain(i8* %x)
|
|
|
|
; CHECK: @objc_retain(i8* %x)
|
|
|
|
; CHECK: @objc_release(i8* %y)
|
|
|
|
; CHECK: @objc_release(i8* %x)
|
|
|
|
; CHECK: ret void
|
|
|
|
; CHECK: }
|
|
|
|
define void @test2a(i8* %x) {
|
|
|
|
entry:
|
|
|
|
%A = alloca i8*
|
|
|
|
store i8* %x, i8** %A, align 8
|
|
|
|
%y = load i8** %A
|
|
|
|
br label %bb1
|
|
|
|
|
|
|
|
bb1:
|
|
|
|
br label %bb2
|
|
|
|
|
|
|
|
bb2:
|
|
|
|
br label %bb3
|
|
|
|
|
|
|
|
bb3:
|
|
|
|
tail call i8* @objc_retain(i8* %x)
|
|
|
|
tail call i8* @objc_retain(i8* %x)
|
|
|
|
call void @use_alloca(i8** %A)
|
|
|
|
call void @objc_release(i8* %y), !clang.imprecise_release !0
|
|
|
|
call void @use_pointer(i8* %x)
|
|
|
|
call void @objc_release(i8* %x), !clang.imprecise_release !0
|
|
|
|
ret void
|
|
|
|
}
|
|
|
|
|
|
|
|
; CHECK: define void @test2b(i8* %x)
|
|
|
|
; CHECK: @objc_retain(i8* %x)
|
|
|
|
; CHECK: @objc_retain(i8* %x)
|
|
|
|
; CHECK: @objc_release(i8* %y)
|
|
|
|
; CHECK: @objc_release(i8* %x)
|
|
|
|
; CHECK: ret void
|
|
|
|
; CHECK: }
|
|
|
|
define void @test2b(i8* %x) {
|
|
|
|
entry:
|
|
|
|
%A = alloca i8*
|
|
|
|
%gep1 = getelementptr i8** %A, i32 0
|
|
|
|
store i8* %x, i8** %gep1, align 8
|
|
|
|
%gep2 = getelementptr i8** %A, i32 0
|
|
|
|
%y = load i8** %gep2
|
|
|
|
br label %bb1
|
|
|
|
|
|
|
|
bb1:
|
|
|
|
br label %bb2
|
|
|
|
|
|
|
|
bb2:
|
|
|
|
br label %bb3
|
|
|
|
|
|
|
|
bb3:
|
|
|
|
tail call i8* @objc_retain(i8* %x)
|
|
|
|
tail call i8* @objc_retain(i8* %x)
|
|
|
|
call void @use_alloca(i8** %A)
|
|
|
|
call void @objc_release(i8* %y), !clang.imprecise_release !0
|
|
|
|
call void @use_pointer(i8* %x)
|
|
|
|
call void @objc_release(i8* %x), !clang.imprecise_release !0
|
|
|
|
ret void
|
|
|
|
}
|
|
|
|
|
|
|
|
; CHECK: define void @test2c(i8* %x)
|
|
|
|
; CHECK: @objc_retain(i8* %x)
|
|
|
|
; CHECK: @objc_retain(i8* %x)
|
|
|
|
; CHECK: @objc_release(i8* %y)
|
|
|
|
; CHECK: @objc_release(i8* %x)
|
|
|
|
; CHECK: ret void
|
|
|
|
; CHECK: }
|
|
|
|
define void @test2c(i8* %x) {
|
|
|
|
entry:
|
|
|
|
%A = alloca i8*, i32 3
|
|
|
|
%gep1 = getelementptr i8** %A, i32 2
|
|
|
|
store i8* %x, i8** %gep1, align 8
|
|
|
|
%gep2 = getelementptr i8** %A, i32 2
|
|
|
|
%y = load i8** %gep2
|
|
|
|
tail call i8* @objc_retain(i8* %x)
|
|
|
|
br label %bb1
|
|
|
|
|
|
|
|
bb1:
|
|
|
|
br label %bb2
|
|
|
|
|
|
|
|
bb2:
|
|
|
|
br label %bb3
|
|
|
|
|
|
|
|
bb3:
|
|
|
|
tail call i8* @objc_retain(i8* %x)
|
|
|
|
call void @use_alloca(i8** %A)
|
|
|
|
call void @objc_release(i8* %y), !clang.imprecise_release !0
|
|
|
|
call void @use_pointer(i8* %x)
|
|
|
|
call void @objc_release(i8* %x), !clang.imprecise_release !0
|
|
|
|
ret void
|
|
|
|
}
|
2013-05-13 23:49:42 +00:00
|
|
|
|
|
|
|
!0 = metadata !{}
|
|
|
|
|
|
|
|
declare i32 @__gxx_personality_v0(...)
|