Skip to content

Commit 1be9eed

Browse files
authored
[ObjCARC] Optimize MayAutorelease by skipping over pools (llvm#188583)
This enables the ARC optimizer to remove autoreleasePoolPush/Pop pairs that were previously retained. By skipping over nested autorelease pools, MayAutorelease now correctly recognizes that autoreleases contained within an inner pool do not escape, allowing the removal of outer pool boundaries.
1 parent cdd2a76 commit 1be9eed

File tree

2 files changed

+54
-7
lines changed

2 files changed

+54
-7
lines changed

llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2502,17 +2502,32 @@ bool MayAutorelease(const CallBase &CB, unsigned Depth = 0) {
25022502
if (!Callee->hasExactDefinition())
25032503
return true;
25042504
for (const BasicBlock &BB : *Callee) {
2505+
// Track nested autorelease pools in a single pass. Autoreleases inside a
2506+
// pool are drained before the pool ends; only effects at function scope
2507+
// (empty stack) or in a pool not closed in this block matter.
2508+
SmallVector<bool, 4> PoolStack;
25052509
for (const Instruction &I : BB) {
2506-
// TODO: Ignore all instructions between autorelease pools
25072510
ARCInstKind InstKind = GetBasicARCInstKind(&I);
25082511
switch (InstKind) {
2512+
case ARCInstKind::AutoreleasepoolPush:
2513+
PoolStack.push_back(false);
2514+
break;
2515+
2516+
case ARCInstKind::AutoreleasepoolPop:
2517+
if (!PoolStack.empty())
2518+
PoolStack.pop_back();
2519+
break;
2520+
25092521
case ARCInstKind::Autorelease:
25102522
case ARCInstKind::AutoreleaseRV:
25112523
case ARCInstKind::FusedRetainAutorelease:
25122524
case ARCInstKind::FusedRetainAutoreleaseRV:
25132525
case ARCInstKind::LoadWeak:
25142526
// These may produce autoreleases
2515-
return true;
2527+
if (PoolStack.empty())
2528+
return true;
2529+
PoolStack.back() = true;
2530+
break;
25162531

25172532
case ARCInstKind::Retain:
25182533
case ARCInstKind::RetainRV:
@@ -2527,16 +2542,17 @@ bool MayAutorelease(const CallBase &CB, unsigned Depth = 0) {
25272542
case ARCInstKind::CopyWeak:
25282543
case ARCInstKind::DestroyWeak:
25292544
case ARCInstKind::StoreStrong:
2530-
case ARCInstKind::AutoreleasepoolPush:
2531-
case ARCInstKind::AutoreleasepoolPop:
25322545
// These ObjC runtime functions don't produce autoreleases
25332546
break;
25342547

25352548
case ARCInstKind::CallOrUser:
25362549
case ARCInstKind::Call:
2537-
// For non-ObjC function calls, recursively analyze
2538-
if (MayAutorelease(cast<CallBase>(I), Depth + 1))
2539-
return true;
2550+
// For non-ObjC function calls, recursively analyze.
2551+
if (MayAutorelease(cast<CallBase>(I), Depth + 1)) {
2552+
if (PoolStack.empty())
2553+
return true;
2554+
PoolStack.back() = true;
2555+
}
25402556
break;
25412557

25422558
case ARCInstKind::IntrinsicUser:
@@ -2546,6 +2562,8 @@ bool MayAutorelease(const CallBase &CB, unsigned Depth = 0) {
25462562
break;
25472563
}
25482564
}
2565+
if (!PoolStack.empty() && llvm::is_contained(PoolStack, true))
2566+
return true;
25492567
}
25502568
return false;
25512569
}

llvm/test/Transforms/ObjCARC/test_autorelease_pool.ll

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,35 @@ define ptr @function_that_might_autorelease() {
314314
ret ptr %autoreleased
315315
}
316316

317+
; Cross-function: callee has its own inner pool containing an autorelease.
318+
; The caller's pool should be optimizable since the callee's autorelease
319+
; is contained within the callee's own pool.
320+
define void @test_cross_function_inner_pool_caller() {
321+
; CHECK-LABEL: define void @test_cross_function_inner_pool_caller() {
322+
; CHECK-NEXT: call void @test_cross_function_inner_pool_callee()
323+
; CHECK-NEXT: ret void
324+
;
325+
%pool = call ptr @llvm.objc.autoreleasePoolPush()
326+
call void @test_cross_function_inner_pool_callee()
327+
call void @llvm.objc.autoreleasePoolPop(ptr %pool)
328+
ret void
329+
}
330+
331+
define void @test_cross_function_inner_pool_callee() {
332+
; CHECK-LABEL: define void @test_cross_function_inner_pool_callee() {
333+
; CHECK-NEXT: [[INNER_POOL:%.*]] = call ptr @llvm.objc.autoreleasePoolPush() #[[ATTR0]]
334+
; CHECK-NEXT: [[OBJ:%.*]] = call ptr @create_object()
335+
; CHECK-NEXT: call void @llvm.objc.release(ptr [[OBJ]]) #[[ATTR0]], !clang.imprecise_release [[META0]]
336+
; CHECK-NEXT: call void @llvm.objc.autoreleasePoolPop(ptr [[INNER_POOL]]) #[[ATTR0]]
337+
; CHECK-NEXT: ret void
338+
;
339+
%inner_pool = call ptr @llvm.objc.autoreleasePoolPush()
340+
%obj = call ptr @create_object()
341+
%ar = call ptr @llvm.objc.autorelease(ptr %obj)
342+
call void @llvm.objc.autoreleasePoolPop(ptr %inner_pool)
343+
ret void
344+
}
345+
317346
;.
318347
; CHECK: [[META0]] = !{}
319348
;.

0 commit comments

Comments
 (0)