[llvm] [ObjCARC] Delete empty autoreleasepools with no autoreleases in them (PR #144788)

Jon Roelofs via llvm-commits llvm-commits at lists.llvm.org
Wed Jun 18 15:09:03 PDT 2025


================
@@ -2485,6 +2489,107 @@ bool ObjCARCOpt::run(Function &F, AAResults &AA) {
   return Changed;
 }
 
+/// Optimize autorelease pools by eliminating empty push/pop pairs.
+void ObjCARCOpt::OptimizeAutoreleasePools(Function &F) {
+  LLVM_DEBUG(dbgs() << "\n== ObjCARCOpt::OptimizeAutoreleasePools ==\n");
+
+  // Track empty autorelease pool push/pop pairs
+  SmallVector<std::pair<CallInst *, CallInst *>, 4> EmptyPoolPairs;
+
+  // Process each basic block independently.
+  // TODO: Can we optimize inter-block autorelease pool pairs?
+  // This would involve tracking autorelease pool state across blocks.
+  for (BasicBlock &BB : F) {
+    CallInst *PendingPush = nullptr;
+    bool HasAutoreleaseInScope = false;
+
+    for (Instruction &Inst : BB) {
+      ARCInstKind Class = GetBasicARCInstKind(&Inst);
+
+      switch (Class) {
+      case ARCInstKind::AutoreleasepoolPush: {
+        // Start tracking a new autorelease pool scope
+        PendingPush = cast<CallInst>(&Inst);
+        HasAutoreleaseInScope = false;
+        LLVM_DEBUG(dbgs() << "Found autorelease pool push: " << *PendingPush
+                          << "\n");
+        break;
+      }
+
+      case ARCInstKind::AutoreleasepoolPop: {
+        CallInst *Pop = cast<CallInst>(&Inst);
+
+        if (PendingPush) {
+          // Check if this pop matches the pending push by comparing the token
+          Value *PopArg = Pop->getArgOperand(0);
+          bool IsMatchingPop = (PopArg == PendingPush);
+
+          // Also handle bitcast case
+          if (!IsMatchingPop && isa<BitCastInst>(PopArg)) {
+            Value *BitcastSrc = cast<BitCastInst>(PopArg)->getOperand(0);
+            IsMatchingPop = (BitcastSrc == PendingPush);
+          }
+
+          if (IsMatchingPop && !HasAutoreleaseInScope) {
+            LLVM_DEBUG(dbgs() << "Eliminating empty autorelease pool pair: "
+                              << *PendingPush << " and " << *Pop << "\n");
+
+            // Store the pair for careful deletion later
+            EmptyPoolPairs.push_back({PendingPush, Pop});
+
+            Changed = true;
+            ++NumNoops;
+          }
+        }
+
+        PendingPush = nullptr;
+        HasAutoreleaseInScope = false;
+        break;
+      }
+      case ARCInstKind::CallOrUser:
+      case ARCInstKind::Call:
+      case ARCInstKind::Autorelease:
+      case ARCInstKind::AutoreleaseRV: {
+        // Track that we have autorelease calls in the current pool scope
+        if (PendingPush) {
+          HasAutoreleaseInScope = true;
+          LLVM_DEBUG(
+              dbgs()
+              << "Found autorelease or potiential autorelease in pool scope: "
+              << Inst << "\n");
+        }
+        break;
+      }
+
+      default:
+        break;
+      }
+    }
+  }
+
+  // Handle empty pool pairs carefully to avoid use-after-delete
+  SmallVector<CallInst *, 8> DeadInsts;
+  for (auto &Pair : EmptyPoolPairs) {
+    CallInst *Push = Pair.first;
+    CallInst *Pop = Pair.second;
+
+    // Replace the pop's argument with poison to break dependencies
+    Value *PoisonToken = PoisonValue::get(Push->getType());
+    Pop->setArgOperand(0, PoisonToken);
+
+    LLVM_DEBUG(dbgs() << "Erasing empty pool pair: " << *Push << " and " << *Pop
----------------
jroelofs wrote:

It would be great to have a remark for this.

https://github.com/llvm/llvm-project/pull/144788


More information about the llvm-commits mailing list