320 lines · plain
1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 52; Test for autorelease pool optimizations3; RUN: opt -passes=objc-arc < %s -S | FileCheck %s4 5declare ptr @llvm.objc.autoreleasePoolPush()6declare void @llvm.objc.autoreleasePoolPop(ptr)7declare ptr @llvm.objc.autorelease(ptr)8declare ptr @llvm.objc.retain(ptr)9declare ptr @create_object()10declare void @use_object(ptr)11declare ptr @object_with_thing()12declare void @opaque_callee()13 14; Empty autorelease pool should be eliminated15define void @test_empty_pool() {16; CHECK-LABEL: define void @test_empty_pool() {17; CHECK-NEXT: ret void18;19 %pool = call ptr @llvm.objc.autoreleasePoolPush()20 call void @llvm.objc.autoreleasePoolPop(ptr %pool)21 ret void22}23 24; Pool with only release should be removed25define void @test_autorelease_to_release() {26; CHECK-LABEL: define void @test_autorelease_to_release() {27; CHECK-NEXT: [[OBJ:%.*]] = call ptr @create_object()28; CHECK-NEXT: call void @llvm.objc.release(ptr [[OBJ]]) #[[ATTR0:[0-9]+]], !clang.imprecise_release [[META0:![0-9]+]]29; CHECK-NEXT: ret void30;31 %obj = call ptr @create_object()32 %pool = call ptr @llvm.objc.autoreleasePoolPush()33 call ptr @llvm.objc.autorelease(ptr %obj)34 call void @llvm.objc.autoreleasePoolPop(ptr %pool)35 ret void36}37 38; Pool with autoreleases should not be optimized39define void @test_multiple_autoreleases() {40; CHECK-LABEL: define void @test_multiple_autoreleases() {41; CHECK-NEXT: [[OBJ1:%.*]] = call ptr @create_object()42; CHECK-NEXT: [[OBJ2:%.*]] = call ptr @create_object()43; CHECK-NEXT: [[POOL:%.*]] = call ptr @llvm.objc.autoreleasePoolPush() #[[ATTR0]]44; CHECK-NEXT: call void @use_object(ptr [[OBJ1]])45; CHECK-NEXT: [[TMP1:%.*]] = call ptr @llvm.objc.autorelease(ptr [[OBJ1]]) #[[ATTR0]]46; CHECK-NEXT: call void @use_object(ptr [[OBJ2]])47; CHECK-NEXT: [[TMP2:%.*]] = call ptr @llvm.objc.autorelease(ptr [[OBJ2]]) #[[ATTR0]]48; CHECK-NEXT: call void @llvm.objc.autoreleasePoolPop(ptr [[POOL]]) #[[ATTR0]]49; CHECK-NEXT: ret void50;51 %obj1 = call ptr @create_object()52 %obj2 = call ptr @create_object()53 %pool = call ptr @llvm.objc.autoreleasePoolPush()54 call void @use_object(ptr %obj1)55 call ptr @llvm.objc.autorelease(ptr %obj1)56 call void @use_object(ptr %obj2)57 call ptr @llvm.objc.autorelease(ptr %obj2)58 call void @llvm.objc.autoreleasePoolPop(ptr %pool)59 ret void60}61 62; Pool with calls should not be optimized63define void @test_calls() {64; CHECK-LABEL: define void @test_calls() {65; CHECK-NEXT: [[POOL:%.*]] = call ptr @llvm.objc.autoreleasePoolPush() #[[ATTR0]]66; CHECK-NEXT: [[OBJ1:%.*]] = call ptr @object_with_thing()67; CHECK-NEXT: call void @use_object(ptr [[OBJ1]])68; CHECK-NEXT: call void @llvm.objc.autoreleasePoolPop(ptr [[POOL]]) #[[ATTR0]]69; CHECK-NEXT: ret void70;71 %pool = call ptr @llvm.objc.autoreleasePoolPush()72 %obj1 = call ptr @object_with_thing()73 call void @use_object(ptr %obj1)74 call void @llvm.objc.autoreleasePoolPop(ptr %pool)75 ret void76}77 78; Pool with opaque call should not be optimized79define void @test_opaque_call() {80; CHECK-LABEL: define void @test_opaque_call() {81; CHECK-NEXT: [[POOL:%.*]] = call ptr @llvm.objc.autoreleasePoolPush() #[[ATTR0]]82; CHECK-NEXT: call void @opaque_callee()83; CHECK-NEXT: call void @llvm.objc.autoreleasePoolPop(ptr [[POOL]]) #[[ATTR0]]84; CHECK-NEXT: ret void85;86 %pool = call ptr @llvm.objc.autoreleasePoolPush()87 call void @opaque_callee()88 call void @llvm.objc.autoreleasePoolPop(ptr %pool)89 ret void90}91 92; Nested empty pools should be eliminated93define void @test_nested_empty_pools() {94; CHECK-LABEL: define void @test_nested_empty_pools() {95; CHECK-NEXT: ret void96;97 %pool1 = call ptr @llvm.objc.autoreleasePoolPush()98 %pool2 = call ptr @llvm.objc.autoreleasePoolPush()99 call void @llvm.objc.autoreleasePoolPop(ptr %pool2)100 call void @llvm.objc.autoreleasePoolPop(ptr %pool1)101 ret void102}103 104; Empty pool with cast should be eliminated105define void @test_empty_pool_with_cast() {106; CHECK-LABEL: define void @test_empty_pool_with_cast() {107; CHECK-NEXT: [[CAST:%.*]] = bitcast ptr poison to ptr108; CHECK-NEXT: ret void109;110 %pool = call ptr @llvm.objc.autoreleasePoolPush()111 %cast = bitcast ptr %pool to ptr112 call void @llvm.objc.autoreleasePoolPop(ptr %cast)113 ret void114}115 116; Autorelease shadowing - autorelease in inner pool doesn't prevent outer optimization117define void @test_autorelease_shadowing_basic() {118; CHECK-LABEL: define void @test_autorelease_shadowing_basic() {119; CHECK-NEXT: [[OBJ:%.*]] = call ptr @create_object()120; CHECK-NEXT: call void @llvm.objc.release(ptr [[OBJ]]) #[[ATTR0]], !clang.imprecise_release [[META0]]121; CHECK-NEXT: ret void122;123 %obj = call ptr @create_object()124 %outer_pool = call ptr @llvm.objc.autoreleasePoolPush()125 126 ; Inner pool with autorelease - this should be shadowed127 %inner_pool = call ptr @llvm.objc.autoreleasePoolPush()128 call ptr @llvm.objc.autorelease(ptr %obj)129 call void @llvm.objc.autoreleasePoolPop(ptr %inner_pool)130 131 call void @llvm.objc.autoreleasePoolPop(ptr %outer_pool)132 ret void133}134 135; Multiple nested levels with shadowing136define void @test_multiple_nested_shadowing() {137; CHECK-LABEL: define void @test_multiple_nested_shadowing() {138; CHECK-NEXT: [[OBJ1:%.*]] = call ptr @create_object()139; CHECK-NEXT: [[OBJ2:%.*]] = call ptr @create_object()140; CHECK-NEXT: call void @llvm.objc.release(ptr [[OBJ1]]) #[[ATTR0]], !clang.imprecise_release [[META0]]141; CHECK-NEXT: call void @llvm.objc.release(ptr [[OBJ2]]) #[[ATTR0]], !clang.imprecise_release [[META0]]142; CHECK-NEXT: ret void143;144 %obj1 = call ptr @create_object()145 %obj2 = call ptr @create_object()146 %outer_pool = call ptr @llvm.objc.autoreleasePoolPush()147 148 ; First inner pool149 %inner1_pool = call ptr @llvm.objc.autoreleasePoolPush()150 call ptr @llvm.objc.autorelease(ptr %obj1)151 call void @llvm.objc.autoreleasePoolPop(ptr %inner1_pool)152 153 ; Second inner pool with nested level154 %inner2_pool = call ptr @llvm.objc.autoreleasePoolPush()155 %inner3_pool = call ptr @llvm.objc.autoreleasePoolPush()156 call ptr @llvm.objc.autorelease(ptr %obj2)157 call void @llvm.objc.autoreleasePoolPop(ptr %inner3_pool)158 call void @llvm.objc.autoreleasePoolPop(ptr %inner2_pool)159 160 call void @llvm.objc.autoreleasePoolPop(ptr %outer_pool)161 ret void162}163 164; Autorelease outside inner pool prevents optimization165define void @test_autorelease_outside_inner_pool() {166; CHECK-LABEL: define void @test_autorelease_outside_inner_pool() {167; CHECK-NEXT: [[OBJ1:%.*]] = call ptr @create_object()168; CHECK-NEXT: [[OBJ2:%.*]] = call ptr @create_object()169; CHECK-NEXT: call void @llvm.objc.release(ptr [[OBJ1]]) #[[ATTR0]], !clang.imprecise_release [[META0]]170; CHECK-NEXT: call void @llvm.objc.release(ptr [[OBJ2]]) #[[ATTR0]], !clang.imprecise_release [[META0]]171; CHECK-NEXT: ret void172;173 %obj1 = call ptr @create_object()174 %obj2 = call ptr @create_object()175 %outer_pool = call ptr @llvm.objc.autoreleasePoolPush()176 177 ; This autorelease is NOT in an inner pool, so outer pool can't be optimized178 call ptr @llvm.objc.autorelease(ptr %obj1)179 180 ; Inner pool with autorelease (shadowed)181 %inner_pool = call ptr @llvm.objc.autoreleasePoolPush()182 call ptr @llvm.objc.autorelease(ptr %obj2)183 call void @llvm.objc.autoreleasePoolPop(ptr %inner_pool)184 185 call void @llvm.objc.autoreleasePoolPop(ptr %outer_pool)186 ret void187}188 189; Known ObjC functions don't prevent optimization190define void @test_known_objc_functions() {191; CHECK-LABEL: define void @test_known_objc_functions() {192; CHECK-NEXT: [[OBJ:%.*]] = call ptr @create_object()193; CHECK-NEXT: ret void194;195 %obj = call ptr @create_object()196 %pool = call ptr @llvm.objc.autoreleasePoolPush()197 198 ; These are all known ObjC runtime functions that don't produce autoreleases199 %retained = call ptr @llvm.objc.retain(ptr %obj)200 call void @llvm.objc.release(ptr %obj)201 202 call void @llvm.objc.autoreleasePoolPop(ptr %pool)203 ret void204}205 206; Complex shadowing with mixed autoreleases207define void @test_complex_shadowing() {208; CHECK-LABEL: define void @test_complex_shadowing() {209; CHECK-NEXT: [[OBJ1:%.*]] = call ptr @create_object()210; CHECK-NEXT: [[OBJ2:%.*]] = call ptr @create_object()211; CHECK-NEXT: [[OBJ3:%.*]] = call ptr @create_object()212; CHECK-NEXT: call void @llvm.objc.release(ptr [[OBJ1]]) #[[ATTR0]], !clang.imprecise_release [[META0]]213; CHECK-NEXT: call void @llvm.objc.release(ptr [[OBJ2]]) #[[ATTR0]], !clang.imprecise_release [[META0]]214; CHECK-NEXT: [[INNER2_POOL:%.*]] = call ptr @llvm.objc.autoreleasePoolPush() #[[ATTR0]]215; CHECK-NEXT: [[TMP1:%.*]] = call ptr @llvm.objc.autorelease(ptr [[OBJ3]]) #[[ATTR0]]216; CHECK-NEXT: call void @llvm.objc.autoreleasePoolPop(ptr [[INNER2_POOL]]) #[[ATTR0]]217; CHECK-NEXT: ret void218;219 %obj1 = call ptr @create_object()220 %obj2 = call ptr @create_object()221 %obj3 = call ptr @create_object()222 %outer_pool = call ptr @llvm.objc.autoreleasePoolPush()223 224 ; This autorelease is outside inner pools - prevents optimization225 call ptr @llvm.objc.autorelease(ptr %obj1)226 227 ; Inner pool 1 with shadowed autorelease228 %inner1_pool = call ptr @llvm.objc.autoreleasePoolPush()229 call ptr @llvm.objc.autorelease(ptr %obj2)230 call void @llvm.objc.autoreleasePoolPop(ptr %inner1_pool)231 232 ; Some safe ObjC operations233 %retained = call ptr @llvm.objc.retain(ptr %obj3)234 call void @llvm.objc.release(ptr %retained)235 236 ; Inner pool 2 with shadowed autorelease237 %inner2_pool = call ptr @llvm.objc.autoreleasePoolPush()238 call ptr @llvm.objc.autorelease(ptr %obj3)239 call void @llvm.objc.autoreleasePoolPop(ptr %inner2_pool)240 241 call void @llvm.objc.autoreleasePoolPop(ptr %outer_pool)242 ret void243}244 245; Non-ObjC function that may autorelease prevents optimization246define void @test_non_objc_may_autorelease() {247; CHECK-LABEL: define void @test_non_objc_may_autorelease() {248; CHECK-NEXT: [[POOL:%.*]] = call ptr @llvm.objc.autoreleasePoolPush() #[[ATTR0]]249; CHECK-NEXT: [[TMP1:%.*]] = call ptr @function_that_might_autorelease()250; CHECK-NEXT: call void @llvm.objc.autoreleasePoolPop(ptr [[POOL]]) #[[ATTR0]]251; CHECK-NEXT: ret void252;253 %pool = call ptr @llvm.objc.autoreleasePoolPush()254 call ptr @function_that_might_autorelease()255 call void @llvm.objc.autoreleasePoolPop(ptr %pool)256 ret void257}258 259; Non-ObjC function that doesn't autorelease allows optimization260define void @test_non_objc_no_autorelease() {261; CHECK-LABEL: define void @test_non_objc_no_autorelease() {262; CHECK-NEXT: call void @safe_function()263; CHECK-NEXT: ret void264;265 %pool = call ptr @llvm.objc.autoreleasePoolPush()266 call void @safe_function()267 call void @llvm.objc.autoreleasePoolPop(ptr %pool)268 ret void269}270 271; Incomplete push/pop pairs across blocks - only inner pairs count272define void @test_incomplete_pairs_inner_shadowing() {273; CHECK-LABEL: define void @test_incomplete_pairs_inner_shadowing() {274; CHECK-NEXT: [[OBJ:%.*]] = call ptr @create_object()275; CHECK-NEXT: [[OUTER_POOL:%.*]] = call ptr @llvm.objc.autoreleasePoolPush() #[[ATTR0]]276; CHECK-NEXT: call void @llvm.objc.release(ptr [[OBJ]]) #[[ATTR0]], !clang.imprecise_release [[META0]]277; CHECK-NEXT: ret void278;279 %obj = call ptr @create_object()280 %outer_pool = call ptr @llvm.objc.autoreleasePoolPush()281 282 ; Inner complete pair - autorelease should be shadowed by this283 %inner_pool = call ptr @llvm.objc.autoreleasePoolPush()284 call ptr @llvm.objc.autorelease(ptr %obj) ; This SHOULD be shadowed by inner pair285 call void @llvm.objc.autoreleasePoolPop(ptr %inner_pool) ; Completes the inner pair286 287 ; Note: %outer_pool pop is in a different block (common pattern)288 ; But the autorelease was shadowed by the complete inner pair289 ret void290}291 292; Helper functions for testing interprocedural analysis293 294; Safe function that doesn't call autorelease295define void @safe_function() {296 ; Just some computation, no autoreleases297; CHECK-LABEL: define void @safe_function() {298; CHECK-NEXT: [[X:%.*]] = add i32 1, 2299; CHECK-NEXT: ret void300;301 %x = add i32 1, 2302 ret void303}304 305; Function that may produce autoreleases (simulated by calling autorelease)306define ptr @function_that_might_autorelease() {307; CHECK-LABEL: define ptr @function_that_might_autorelease() {308; CHECK-NEXT: [[OBJ:%.*]] = call ptr @create_object()309; CHECK-NEXT: [[AUTORELEASED:%.*]] = call ptr @llvm.objc.autorelease(ptr [[OBJ]]) #[[ATTR0]]310; CHECK-NEXT: ret ptr [[AUTORELEASED]]311;312 %obj = call ptr @create_object()313 %autoreleased = call ptr @llvm.objc.autorelease(ptr %obj)314 ret ptr %autoreleased315}316 317;.318; CHECK: [[META0]] = !{}319;.320