[llvm] [ObjCARC] Change autorelease to release when the pool state is not changed between the autorelease and the pool pop (PR #152353)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Aug 6 11:07:50 PDT 2025
https://github.com/AZero13 created https://github.com/llvm/llvm-project/pull/152353
None
>From 6c1b03b7f953c0183282ebc8af27f6ddbe815ce7 Mon Sep 17 00:00:00 2001
From: AZero13 <gfunni234 at gmail.com>
Date: Wed, 6 Aug 2025 14:07:16 -0400
Subject: [PATCH] [ObjCARC] Change autorelease to release when the pool state
is not changed between the autorelease and the pool pop
---
llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp | 56 +++++++++++++++++--
.../ObjCARC/test_autorelease_pool.ll | 2 +-
2 files changed, 53 insertions(+), 5 deletions(-)
diff --git a/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp b/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp
index 66a2c7632aadc..5598d5f221024 100644
--- a/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp
+++ b/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp
@@ -121,6 +121,44 @@ static const Value *FindSingleUseIdentifiedObject(const Value *Arg) {
/// \defgroup ARCOpt ARC Optimization.
/// @{
+/// Check if there is an autoreleasePoolPop after the given autorelease
+/// instruction in the same basic block with no intervening calls that
+/// could affect the autorelease pool.
+static bool HasFollowingAutoreleasePoolPop(Instruction *AutoreleaseInst) {
+ BasicBlock *BB = AutoreleaseInst->getParent();
+
+ // Look forward from the autorelease instruction
+ for (BasicBlock::iterator I = std::next(AutoreleaseInst->getIterator()),
+ E = BB->end();
+ I != E; ++I) {
+ ARCInstKind Class = GetBasicARCInstKind(&*I);
+
+ switch (Class) {
+ case ARCInstKind::AutoreleasepoolPop:
+ // Found a pool pop - the autorelease will be drained
+ return true;
+
+ case ARCInstKind::AutoreleasepoolPush:
+ // A new pool started - this autorelease won't be drained by a later pop
+ return false;
+
+ case ARCInstKind::CallOrUser:
+ case ARCInstKind::Call:
+ // Unknown call could affect autorelease pool state or return autoreleased
+ // objects
+ return false;
+
+ default:
+ // Known ObjC runtime calls and other instructions are safe to continue
+ // through
+ break;
+ }
+ }
+
+ // Reached end of basic block without finding a pool pop
+ return false;
+}
+
// TODO: On code like this:
//
// objc_retain(%x)
@@ -978,12 +1016,22 @@ void ObjCARCOpt::OptimizeIndividualCallImpl(Function &F, Instruction *Inst,
break;
}
- // objc_autorelease(x) -> objc_release(x) if x is otherwise unused.
+ // objc_autorelease(x) -> objc_release(x) if x is otherwise unused
+ // OR if this autorelease is followed by an autoreleasePoolPop.
if (IsAutorelease(Class) && Inst->use_empty()) {
CallInst *Call = cast<CallInst>(Inst);
const Value *Arg = Call->getArgOperand(0);
Arg = FindSingleUseIdentifiedObject(Arg);
- if (Arg) {
+ bool ShouldConvert = (Arg != nullptr);
+ const char *Reason = "since x is otherwise unused";
+
+ // Also convert if this autorelease is followed by a pool pop
+ if (!ShouldConvert && HasFollowingAutoreleasePoolPop(Inst)) {
+ ShouldConvert = true;
+ Reason = "since it's followed by autoreleasePoolPop";
+ }
+
+ if (ShouldConvert) {
Changed = true;
++NumAutoreleases;
@@ -997,8 +1045,8 @@ void ObjCARCOpt::OptimizeIndividualCallImpl(Function &F, Instruction *Inst,
MDNode::get(C, {}));
LLVM_DEBUG(dbgs() << "Replacing autorelease{,RV}(x) with objc_release(x) "
- "since x is otherwise unused.\nOld: "
- << *Call << "\nNew: " << *NewCall << "\n");
+ << Reason << ".\nOld: " << *Call
+ << "\nNew: " << *NewCall << "\n");
EraseInstruction(Call);
Inst = NewCall;
diff --git a/llvm/test/Transforms/ObjCARC/test_autorelease_pool.ll b/llvm/test/Transforms/ObjCARC/test_autorelease_pool.ll
index 896717f92146f..6c7cfeb78dd3a 100644
--- a/llvm/test/Transforms/ObjCARC/test_autorelease_pool.ll
+++ b/llvm/test/Transforms/ObjCARC/test_autorelease_pool.ll
@@ -44,7 +44,7 @@ define void @test_multiple_autoreleases() {
; 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.release(ptr [[OBJ2]]) #[[ATTR0]], !clang.imprecise_release [[META0]]
; CHECK-NEXT: call void @llvm.objc.autoreleasePoolPop(ptr [[POOL]]) #[[ATTR0]]
; CHECK-NEXT: ret void
;
More information about the llvm-commits
mailing list