[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