[llvm] [ObjCARC] Optimize MayAutorelease by skipping over pools (PR #188583)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Apr 8 12:08:47 PDT 2026
https://github.com/SiliconA-Z updated https://github.com/llvm/llvm-project/pull/188583
>From e7a327d7cff26fb6aee979b819b5d6b05e5613c6 Mon Sep 17 00:00:00 2001
From: AZero13 <gfunni234 at gmail.com>
Date: Wed, 25 Mar 2026 20:42:12 -0400
Subject: [PATCH 1/2] Pre-commit test (NFC)
---
.../ObjCARC/test_autorelease_pool.ll | 31 +++++++++++++++++++
1 file changed, 31 insertions(+)
diff --git a/llvm/test/Transforms/ObjCARC/test_autorelease_pool.ll b/llvm/test/Transforms/ObjCARC/test_autorelease_pool.ll
index 896717f92146f..714c46f4b15df 100644
--- a/llvm/test/Transforms/ObjCARC/test_autorelease_pool.ll
+++ b/llvm/test/Transforms/ObjCARC/test_autorelease_pool.ll
@@ -314,6 +314,37 @@ define ptr @function_that_might_autorelease() {
ret ptr %autoreleased
}
+; Cross-function: callee has its own inner pool containing an autorelease.
+; The caller's pool should be optimizable since the callee's autorelease
+; is contained within the callee's own pool.
+define void @test_cross_function_inner_pool_caller() {
+; CHECK-LABEL: define void @test_cross_function_inner_pool_caller() {
+; CHECK-NEXT: [[POOL:%.*]] = call ptr @llvm.objc.autoreleasePoolPush() #[[ATTR0]]
+; CHECK-NEXT: call void @test_cross_function_inner_pool_callee()
+; CHECK-NEXT: call void @llvm.objc.autoreleasePoolPop(ptr [[POOL]]) #[[ATTR0]]
+; CHECK-NEXT: ret void
+;
+ %pool = call ptr @llvm.objc.autoreleasePoolPush()
+ call void @test_cross_function_inner_pool_callee()
+ call void @llvm.objc.autoreleasePoolPop(ptr %pool)
+ ret void
+}
+
+define void @test_cross_function_inner_pool_callee() {
+; CHECK-LABEL: define void @test_cross_function_inner_pool_callee() {
+; CHECK-NEXT: [[INNER_POOL:%.*]] = call ptr @llvm.objc.autoreleasePoolPush() #[[ATTR0]]
+; CHECK-NEXT: [[OBJ:%.*]] = call ptr @create_object()
+; CHECK-NEXT: call void @llvm.objc.release(ptr [[OBJ]]) #[[ATTR0]], !clang.imprecise_release [[META0]]
+; CHECK-NEXT: call void @llvm.objc.autoreleasePoolPop(ptr [[INNER_POOL]]) #[[ATTR0]]
+; CHECK-NEXT: ret void
+;
+ %inner_pool = call ptr @llvm.objc.autoreleasePoolPush()
+ %obj = call ptr @create_object()
+ %ar = call ptr @llvm.objc.autorelease(ptr %obj)
+ call void @llvm.objc.autoreleasePoolPop(ptr %inner_pool)
+ ret void
+}
+
;.
; CHECK: [[META0]] = !{}
;.
>From 1446c4f8e62dfc35160a0b0bdfb597f262be7552 Mon Sep 17 00:00:00 2001
From: AZero13 <gfunni234 at gmail.com>
Date: Fri, 27 Mar 2026 22:03:27 -0400
Subject: [PATCH 2/2] [ObjCARC] Optimize MayAutorelease by skipping over pools
Just a lot less scanning.
---
llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp | 32 +++++++++++++++----
.../ObjCARC/test_autorelease_pool.ll | 2 --
2 files changed, 25 insertions(+), 9 deletions(-)
diff --git a/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp b/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp
index 69c91b4327e5b..f796266f0cf3c 100644
--- a/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp
+++ b/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp
@@ -2502,17 +2502,32 @@ bool MayAutorelease(const CallBase &CB, unsigned Depth = 0) {
if (!Callee->hasExactDefinition())
return true;
for (const BasicBlock &BB : *Callee) {
+ // Track nested autorelease pools in a single pass. Autoreleases inside a
+ // pool are drained before the pool ends; only effects at function scope
+ // (empty stack) or in a pool not closed in this block matter.
+ SmallVector<bool, 4> PoolStack;
for (const Instruction &I : BB) {
- // TODO: Ignore all instructions between autorelease pools
ARCInstKind InstKind = GetBasicARCInstKind(&I);
switch (InstKind) {
+ case ARCInstKind::AutoreleasepoolPush:
+ PoolStack.push_back(false);
+ break;
+
+ case ARCInstKind::AutoreleasepoolPop:
+ if (!PoolStack.empty())
+ PoolStack.pop_back();
+ break;
+
case ARCInstKind::Autorelease:
case ARCInstKind::AutoreleaseRV:
case ARCInstKind::FusedRetainAutorelease:
case ARCInstKind::FusedRetainAutoreleaseRV:
case ARCInstKind::LoadWeak:
// These may produce autoreleases
- return true;
+ if (PoolStack.empty())
+ return true;
+ PoolStack.back() = true;
+ break;
case ARCInstKind::Retain:
case ARCInstKind::RetainRV:
@@ -2527,16 +2542,17 @@ bool MayAutorelease(const CallBase &CB, unsigned Depth = 0) {
case ARCInstKind::CopyWeak:
case ARCInstKind::DestroyWeak:
case ARCInstKind::StoreStrong:
- case ARCInstKind::AutoreleasepoolPush:
- case ARCInstKind::AutoreleasepoolPop:
// These ObjC runtime functions don't produce autoreleases
break;
case ARCInstKind::CallOrUser:
case ARCInstKind::Call:
- // For non-ObjC function calls, recursively analyze
- if (MayAutorelease(cast<CallBase>(I), Depth + 1))
- return true;
+ // For non-ObjC function calls, recursively analyze.
+ if (MayAutorelease(cast<CallBase>(I), Depth + 1)) {
+ if (PoolStack.empty())
+ return true;
+ PoolStack.back() = true;
+ }
break;
case ARCInstKind::IntrinsicUser:
@@ -2546,6 +2562,8 @@ bool MayAutorelease(const CallBase &CB, unsigned Depth = 0) {
break;
}
}
+ if (!PoolStack.empty() && llvm::is_contained(PoolStack, true))
+ return true;
}
return false;
}
diff --git a/llvm/test/Transforms/ObjCARC/test_autorelease_pool.ll b/llvm/test/Transforms/ObjCARC/test_autorelease_pool.ll
index 714c46f4b15df..d94ad884b60c4 100644
--- a/llvm/test/Transforms/ObjCARC/test_autorelease_pool.ll
+++ b/llvm/test/Transforms/ObjCARC/test_autorelease_pool.ll
@@ -319,9 +319,7 @@ define ptr @function_that_might_autorelease() {
; is contained within the callee's own pool.
define void @test_cross_function_inner_pool_caller() {
; CHECK-LABEL: define void @test_cross_function_inner_pool_caller() {
-; CHECK-NEXT: [[POOL:%.*]] = call ptr @llvm.objc.autoreleasePoolPush() #[[ATTR0]]
; CHECK-NEXT: call void @test_cross_function_inner_pool_callee()
-; CHECK-NEXT: call void @llvm.objc.autoreleasePoolPop(ptr [[POOL]]) #[[ATTR0]]
; CHECK-NEXT: ret void
;
%pool = call ptr @llvm.objc.autoreleasePoolPush()
More information about the llvm-commits
mailing list