diff options
author | Michael Gottesman <mgottesman@apple.com> | 2013-05-13 23:49:42 +0000 |
---|---|---|
committer | Michael Gottesman <mgottesman@apple.com> | 2013-05-13 23:49:42 +0000 |
commit | acfb3584c58159ec20a8379c864c9d644f8d967e (patch) | |
tree | 304995d93e7f1dd7a3b21a8bbd85741543e1c98d /test/Transforms/ObjCARC | |
parent | 9b5e6c0943dcfced64980240e25427cdc06c9bad (diff) |
[objc-arc-opts] In the presense of an alloca unconditionally remove RR pairs if and only if we are both KnownSafeBU/KnownSafeTD rather than just either or.
In the presense of a block being initialized, the frontend will emit the
objc_retain on the original pointer and the release on the pointer loaded from
the alloca. The optimizer will through the provenance analysis realize that the
two are related (albiet different), but since we only require KnownSafe in one
direction, will match the inner retain on the original pointer with the guard
release on the original pointer. This is fixed by ensuring that in the presense
of allocas we only unconditionally remove pointers if both our retain and our
release are KnownSafe (i.e. we are KnownSafe in both directions) since we must
deal with the possibility that the frontend will emit what (to the optimizer)
appears to be unbalanced retain/releases.
An example of the miscompile is:
%A = alloca
retain(%x)
retain(%x) <--- Inner Retain
store %x, %A
%y = load %A
... DO STUFF ...
release(%y)
call void @use(%x)
release(%x) <--- Guarding Release
getting optimized to:
%A = alloca
retain(%x)
store %x, %A
%y = load %A
... DO STUFF ...
release(%y)
call void @use(%x)
rdar://13750319
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@181743 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'test/Transforms/ObjCARC')
-rw-r--r-- | test/Transforms/ObjCARC/allocas.ll | 203 |
1 files changed, 203 insertions, 0 deletions
diff --git a/test/Transforms/ObjCARC/allocas.ll b/test/Transforms/ObjCARC/allocas.ll new file mode 100644 index 0000000000..eabd54deb7 --- /dev/null +++ b/test/Transforms/ObjCARC/allocas.ll @@ -0,0 +1,203 @@ +; 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 +} + + +!0 = metadata !{} + +declare i32 @__gxx_personality_v0(...) |