llvm-project/llvm/test/Transforms/ObjCARC/test_autorelease_pool.ll
AZero13 d2dedcd11f
[ObjCARC] Delete empty autoreleasepools with no autoreleases in them (#144788)
Erase empty autorelease pools that have no autorelease in them
2025-07-23 08:02:07 -07:00

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]] = !{}
;.