320 lines
12 KiB
LLVM
320 lines
12 KiB
LLVM
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
|
|
; Test for autorelease pool optimizations
|
|
; RUN: opt -passes=objc-arc < %s -S | FileCheck %s
|
|
|
|
declare ptr @llvm.objc.autoreleasePoolPush()
|
|
declare void @llvm.objc.autoreleasePoolPop(ptr)
|
|
declare ptr @llvm.objc.autorelease(ptr)
|
|
declare ptr @llvm.objc.retain(ptr)
|
|
declare ptr @create_object()
|
|
declare void @use_object(ptr)
|
|
declare ptr @object_with_thing()
|
|
declare void @opaque_callee()
|
|
|
|
; Empty autorelease pool should be eliminated
|
|
define void @test_empty_pool() {
|
|
; CHECK-LABEL: define void @test_empty_pool() {
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%pool = call ptr @llvm.objc.autoreleasePoolPush()
|
|
call void @llvm.objc.autoreleasePoolPop(ptr %pool)
|
|
ret void
|
|
}
|
|
|
|
; Pool with only release should be removed
|
|
define void @test_autorelease_to_release() {
|
|
; CHECK-LABEL: define void @test_autorelease_to_release() {
|
|
; CHECK-NEXT: [[OBJ:%.*]] = call ptr @create_object()
|
|
; CHECK-NEXT: call void @llvm.objc.release(ptr [[OBJ]]) #[[ATTR0:[0-9]+]], !clang.imprecise_release [[META0:![0-9]+]]
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%obj = call ptr @create_object()
|
|
%pool = call ptr @llvm.objc.autoreleasePoolPush()
|
|
call ptr @llvm.objc.autorelease(ptr %obj)
|
|
call void @llvm.objc.autoreleasePoolPop(ptr %pool)
|
|
ret void
|
|
}
|
|
|
|
; Pool with autoreleases should not be optimized
|
|
define void @test_multiple_autoreleases() {
|
|
; CHECK-LABEL: define void @test_multiple_autoreleases() {
|
|
; CHECK-NEXT: [[OBJ1:%.*]] = call ptr @create_object()
|
|
; CHECK-NEXT: [[OBJ2:%.*]] = call ptr @create_object()
|
|
; CHECK-NEXT: [[POOL:%.*]] = call ptr @llvm.objc.autoreleasePoolPush() #[[ATTR0]]
|
|
; CHECK-NEXT: call void @use_object(ptr [[OBJ1]])
|
|
; CHECK-NEXT: [[TMP1:%.*]] = call ptr @llvm.objc.autorelease(ptr [[OBJ1]]) #[[ATTR0]]
|
|
; CHECK-NEXT: call void @use_object(ptr [[OBJ2]])
|
|
; CHECK-NEXT: [[TMP2:%.*]] = call ptr @llvm.objc.autorelease(ptr [[OBJ2]]) #[[ATTR0]]
|
|
; CHECK-NEXT: call void @llvm.objc.autoreleasePoolPop(ptr [[POOL]]) #[[ATTR0]]
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%obj1 = call ptr @create_object()
|
|
%obj2 = call ptr @create_object()
|
|
%pool = call ptr @llvm.objc.autoreleasePoolPush()
|
|
call void @use_object(ptr %obj1)
|
|
call ptr @llvm.objc.autorelease(ptr %obj1)
|
|
call void @use_object(ptr %obj2)
|
|
call ptr @llvm.objc.autorelease(ptr %obj2)
|
|
call void @llvm.objc.autoreleasePoolPop(ptr %pool)
|
|
ret void
|
|
}
|
|
|
|
; Pool with calls should not be optimized
|
|
define void @test_calls() {
|
|
; CHECK-LABEL: define void @test_calls() {
|
|
; CHECK-NEXT: [[POOL:%.*]] = call ptr @llvm.objc.autoreleasePoolPush() #[[ATTR0]]
|
|
; CHECK-NEXT: [[OBJ1:%.*]] = call ptr @object_with_thing()
|
|
; CHECK-NEXT: call void @use_object(ptr [[OBJ1]])
|
|
; CHECK-NEXT: call void @llvm.objc.autoreleasePoolPop(ptr [[POOL]]) #[[ATTR0]]
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%pool = call ptr @llvm.objc.autoreleasePoolPush()
|
|
%obj1 = call ptr @object_with_thing()
|
|
call void @use_object(ptr %obj1)
|
|
call void @llvm.objc.autoreleasePoolPop(ptr %pool)
|
|
ret void
|
|
}
|
|
|
|
; Pool with opaque call should not be optimized
|
|
define void @test_opaque_call() {
|
|
; CHECK-LABEL: define void @test_opaque_call() {
|
|
; CHECK-NEXT: [[POOL:%.*]] = call ptr @llvm.objc.autoreleasePoolPush() #[[ATTR0]]
|
|
; CHECK-NEXT: call void @opaque_callee()
|
|
; CHECK-NEXT: call void @llvm.objc.autoreleasePoolPop(ptr [[POOL]]) #[[ATTR0]]
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%pool = call ptr @llvm.objc.autoreleasePoolPush()
|
|
call void @opaque_callee()
|
|
call void @llvm.objc.autoreleasePoolPop(ptr %pool)
|
|
ret void
|
|
}
|
|
|
|
; Nested empty pools should be eliminated
|
|
define void @test_nested_empty_pools() {
|
|
; CHECK-LABEL: define void @test_nested_empty_pools() {
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%pool1 = call ptr @llvm.objc.autoreleasePoolPush()
|
|
%pool2 = call ptr @llvm.objc.autoreleasePoolPush()
|
|
call void @llvm.objc.autoreleasePoolPop(ptr %pool2)
|
|
call void @llvm.objc.autoreleasePoolPop(ptr %pool1)
|
|
ret void
|
|
}
|
|
|
|
; Empty pool with cast should be eliminated
|
|
define void @test_empty_pool_with_cast() {
|
|
; CHECK-LABEL: define void @test_empty_pool_with_cast() {
|
|
; CHECK-NEXT: [[CAST:%.*]] = bitcast ptr poison to ptr
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%pool = call ptr @llvm.objc.autoreleasePoolPush()
|
|
%cast = bitcast ptr %pool to ptr
|
|
call void @llvm.objc.autoreleasePoolPop(ptr %cast)
|
|
ret void
|
|
}
|
|
|
|
; Autorelease shadowing - autorelease in inner pool doesn't prevent outer optimization
|
|
define void @test_autorelease_shadowing_basic() {
|
|
; CHECK-LABEL: define void @test_autorelease_shadowing_basic() {
|
|
; CHECK-NEXT: [[OBJ:%.*]] = call ptr @create_object()
|
|
; CHECK-NEXT: call void @llvm.objc.release(ptr [[OBJ]]) #[[ATTR0]], !clang.imprecise_release [[META0]]
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%obj = call ptr @create_object()
|
|
%outer_pool = call ptr @llvm.objc.autoreleasePoolPush()
|
|
|
|
; Inner pool with autorelease - this should be shadowed
|
|
%inner_pool = call ptr @llvm.objc.autoreleasePoolPush()
|
|
call ptr @llvm.objc.autorelease(ptr %obj)
|
|
call void @llvm.objc.autoreleasePoolPop(ptr %inner_pool)
|
|
|
|
call void @llvm.objc.autoreleasePoolPop(ptr %outer_pool)
|
|
ret void
|
|
}
|
|
|
|
; Multiple nested levels with shadowing
|
|
define void @test_multiple_nested_shadowing() {
|
|
; CHECK-LABEL: define void @test_multiple_nested_shadowing() {
|
|
; CHECK-NEXT: [[OBJ1:%.*]] = call ptr @create_object()
|
|
; CHECK-NEXT: [[OBJ2:%.*]] = call ptr @create_object()
|
|
; CHECK-NEXT: call void @llvm.objc.release(ptr [[OBJ1]]) #[[ATTR0]], !clang.imprecise_release [[META0]]
|
|
; CHECK-NEXT: call void @llvm.objc.release(ptr [[OBJ2]]) #[[ATTR0]], !clang.imprecise_release [[META0]]
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%obj1 = call ptr @create_object()
|
|
%obj2 = call ptr @create_object()
|
|
%outer_pool = call ptr @llvm.objc.autoreleasePoolPush()
|
|
|
|
; First inner pool
|
|
%inner1_pool = call ptr @llvm.objc.autoreleasePoolPush()
|
|
call ptr @llvm.objc.autorelease(ptr %obj1)
|
|
call void @llvm.objc.autoreleasePoolPop(ptr %inner1_pool)
|
|
|
|
; Second inner pool with nested level
|
|
%inner2_pool = call ptr @llvm.objc.autoreleasePoolPush()
|
|
%inner3_pool = call ptr @llvm.objc.autoreleasePoolPush()
|
|
call ptr @llvm.objc.autorelease(ptr %obj2)
|
|
call void @llvm.objc.autoreleasePoolPop(ptr %inner3_pool)
|
|
call void @llvm.objc.autoreleasePoolPop(ptr %inner2_pool)
|
|
|
|
call void @llvm.objc.autoreleasePoolPop(ptr %outer_pool)
|
|
ret void
|
|
}
|
|
|
|
; Autorelease outside inner pool prevents optimization
|
|
define void @test_autorelease_outside_inner_pool() {
|
|
; CHECK-LABEL: define void @test_autorelease_outside_inner_pool() {
|
|
; CHECK-NEXT: [[OBJ1:%.*]] = call ptr @create_object()
|
|
; CHECK-NEXT: [[OBJ2:%.*]] = call ptr @create_object()
|
|
; CHECK-NEXT: call void @llvm.objc.release(ptr [[OBJ1]]) #[[ATTR0]], !clang.imprecise_release [[META0]]
|
|
; CHECK-NEXT: call void @llvm.objc.release(ptr [[OBJ2]]) #[[ATTR0]], !clang.imprecise_release [[META0]]
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%obj1 = call ptr @create_object()
|
|
%obj2 = call ptr @create_object()
|
|
%outer_pool = call ptr @llvm.objc.autoreleasePoolPush()
|
|
|
|
; This autorelease is NOT in an inner pool, so outer pool can't be optimized
|
|
call ptr @llvm.objc.autorelease(ptr %obj1)
|
|
|
|
; Inner pool with autorelease (shadowed)
|
|
%inner_pool = call ptr @llvm.objc.autoreleasePoolPush()
|
|
call ptr @llvm.objc.autorelease(ptr %obj2)
|
|
call void @llvm.objc.autoreleasePoolPop(ptr %inner_pool)
|
|
|
|
call void @llvm.objc.autoreleasePoolPop(ptr %outer_pool)
|
|
ret void
|
|
}
|
|
|
|
; Known ObjC functions don't prevent optimization
|
|
define void @test_known_objc_functions() {
|
|
; CHECK-LABEL: define void @test_known_objc_functions() {
|
|
; CHECK-NEXT: [[OBJ:%.*]] = call ptr @create_object()
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%obj = call ptr @create_object()
|
|
%pool = call ptr @llvm.objc.autoreleasePoolPush()
|
|
|
|
; These are all known ObjC runtime functions that don't produce autoreleases
|
|
%retained = call ptr @llvm.objc.retain(ptr %obj)
|
|
call void @llvm.objc.release(ptr %obj)
|
|
|
|
call void @llvm.objc.autoreleasePoolPop(ptr %pool)
|
|
ret void
|
|
}
|
|
|
|
; Complex shadowing with mixed autoreleases
|
|
define void @test_complex_shadowing() {
|
|
; CHECK-LABEL: define void @test_complex_shadowing() {
|
|
; CHECK-NEXT: [[OBJ1:%.*]] = call ptr @create_object()
|
|
; CHECK-NEXT: [[OBJ2:%.*]] = call ptr @create_object()
|
|
; CHECK-NEXT: [[OBJ3:%.*]] = call ptr @create_object()
|
|
; CHECK-NEXT: call void @llvm.objc.release(ptr [[OBJ1]]) #[[ATTR0]], !clang.imprecise_release [[META0]]
|
|
; CHECK-NEXT: call void @llvm.objc.release(ptr [[OBJ2]]) #[[ATTR0]], !clang.imprecise_release [[META0]]
|
|
; CHECK-NEXT: [[INNER2_POOL:%.*]] = call ptr @llvm.objc.autoreleasePoolPush() #[[ATTR0]]
|
|
; CHECK-NEXT: [[TMP1:%.*]] = call ptr @llvm.objc.autorelease(ptr [[OBJ3]]) #[[ATTR0]]
|
|
; CHECK-NEXT: call void @llvm.objc.autoreleasePoolPop(ptr [[INNER2_POOL]]) #[[ATTR0]]
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%obj1 = call ptr @create_object()
|
|
%obj2 = call ptr @create_object()
|
|
%obj3 = call ptr @create_object()
|
|
%outer_pool = call ptr @llvm.objc.autoreleasePoolPush()
|
|
|
|
; This autorelease is outside inner pools - prevents optimization
|
|
call ptr @llvm.objc.autorelease(ptr %obj1)
|
|
|
|
; Inner pool 1 with shadowed autorelease
|
|
%inner1_pool = call ptr @llvm.objc.autoreleasePoolPush()
|
|
call ptr @llvm.objc.autorelease(ptr %obj2)
|
|
call void @llvm.objc.autoreleasePoolPop(ptr %inner1_pool)
|
|
|
|
; Some safe ObjC operations
|
|
%retained = call ptr @llvm.objc.retain(ptr %obj3)
|
|
call void @llvm.objc.release(ptr %retained)
|
|
|
|
; Inner pool 2 with shadowed autorelease
|
|
%inner2_pool = call ptr @llvm.objc.autoreleasePoolPush()
|
|
call ptr @llvm.objc.autorelease(ptr %obj3)
|
|
call void @llvm.objc.autoreleasePoolPop(ptr %inner2_pool)
|
|
|
|
call void @llvm.objc.autoreleasePoolPop(ptr %outer_pool)
|
|
ret void
|
|
}
|
|
|
|
; Non-ObjC function that may autorelease prevents optimization
|
|
define void @test_non_objc_may_autorelease() {
|
|
; CHECK-LABEL: define void @test_non_objc_may_autorelease() {
|
|
; CHECK-NEXT: [[POOL:%.*]] = call ptr @llvm.objc.autoreleasePoolPush() #[[ATTR0]]
|
|
; CHECK-NEXT: [[TMP1:%.*]] = call ptr @function_that_might_autorelease()
|
|
; CHECK-NEXT: call void @llvm.objc.autoreleasePoolPop(ptr [[POOL]]) #[[ATTR0]]
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%pool = call ptr @llvm.objc.autoreleasePoolPush()
|
|
call ptr @function_that_might_autorelease()
|
|
call void @llvm.objc.autoreleasePoolPop(ptr %pool)
|
|
ret void
|
|
}
|
|
|
|
; Non-ObjC function that doesn't autorelease allows optimization
|
|
define void @test_non_objc_no_autorelease() {
|
|
; CHECK-LABEL: define void @test_non_objc_no_autorelease() {
|
|
; CHECK-NEXT: call void @safe_function()
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%pool = call ptr @llvm.objc.autoreleasePoolPush()
|
|
call void @safe_function()
|
|
call void @llvm.objc.autoreleasePoolPop(ptr %pool)
|
|
ret void
|
|
}
|
|
|
|
; Incomplete push/pop pairs across blocks - only inner pairs count
|
|
define void @test_incomplete_pairs_inner_shadowing() {
|
|
; CHECK-LABEL: define void @test_incomplete_pairs_inner_shadowing() {
|
|
; CHECK-NEXT: [[OBJ:%.*]] = call ptr @create_object()
|
|
; CHECK-NEXT: [[OUTER_POOL:%.*]] = call ptr @llvm.objc.autoreleasePoolPush() #[[ATTR0]]
|
|
; CHECK-NEXT: call void @llvm.objc.release(ptr [[OBJ]]) #[[ATTR0]], !clang.imprecise_release [[META0]]
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%obj = call ptr @create_object()
|
|
%outer_pool = call ptr @llvm.objc.autoreleasePoolPush()
|
|
|
|
; Inner complete pair - autorelease should be shadowed by this
|
|
%inner_pool = call ptr @llvm.objc.autoreleasePoolPush()
|
|
call ptr @llvm.objc.autorelease(ptr %obj) ; This SHOULD be shadowed by inner pair
|
|
call void @llvm.objc.autoreleasePoolPop(ptr %inner_pool) ; Completes the inner pair
|
|
|
|
; Note: %outer_pool pop is in a different block (common pattern)
|
|
; But the autorelease was shadowed by the complete inner pair
|
|
ret void
|
|
}
|
|
|
|
; Helper functions for testing interprocedural analysis
|
|
|
|
; Safe function that doesn't call autorelease
|
|
define void @safe_function() {
|
|
; Just some computation, no autoreleases
|
|
; CHECK-LABEL: define void @safe_function() {
|
|
; CHECK-NEXT: [[X:%.*]] = add i32 1, 2
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%x = add i32 1, 2
|
|
ret void
|
|
}
|
|
|
|
; Function that may produce autoreleases (simulated by calling autorelease)
|
|
define ptr @function_that_might_autorelease() {
|
|
; CHECK-LABEL: define ptr @function_that_might_autorelease() {
|
|
; CHECK-NEXT: [[OBJ:%.*]] = call ptr @create_object()
|
|
; CHECK-NEXT: [[AUTORELEASED:%.*]] = call ptr @llvm.objc.autorelease(ptr [[OBJ]]) #[[ATTR0]]
|
|
; CHECK-NEXT: ret ptr [[AUTORELEASED]]
|
|
;
|
|
%obj = call ptr @create_object()
|
|
%autoreleased = call ptr @llvm.objc.autorelease(ptr %obj)
|
|
ret ptr %autoreleased
|
|
}
|
|
|
|
;.
|
|
; CHECK: [[META0]] = !{}
|
|
;.
|