@@ -132,11 +132,8 @@ static const Value *FindSingleUseIdentifiedObject(const Value *Arg) {
132
132
//
133
133
// The second retain and autorelease can be deleted.
134
134
135
- // TODO: It should be possible to delete
136
- // objc_autoreleasePoolPush and objc_autoreleasePoolPop
137
- // pairs if nothing is actually autoreleased between them. Also, autorelease
138
- // calls followed by objc_autoreleasePoolPop calls (perhaps in ObjC++ code
139
- // after inlining) can be turned into plain release calls.
135
+ // TODO: Autorelease calls followed by objc_autoreleasePoolPop calls (perhaps in
136
+ // ObjC++ code after inlining) can be turned into plain release calls.
140
137
141
138
// TODO: Critical-edge splitting. If the optimial insertion point is
142
139
// a critical edge, the current algorithm has to fail, because it doesn't
@@ -566,6 +563,8 @@ class ObjCARCOpt {
566
563
567
564
void OptimizeReturns (Function &F);
568
565
566
+ void OptimizeAutoreleasePools (Function &F);
567
+
569
568
template <typename PredicateT>
570
569
static void cloneOpBundlesIf (CallBase *CI,
571
570
SmallVectorImpl<OperandBundleDef> &OpBundles,
@@ -2473,6 +2472,11 @@ bool ObjCARCOpt::run(Function &F, AAResults &AA) {
2473
2472
(1 << unsigned (ARCInstKind::AutoreleaseRV))))
2474
2473
OptimizeReturns (F);
2475
2474
2475
+ // Optimizations for autorelease pools.
2476
+ if (UsedInThisFunction & ((1 << unsigned (ARCInstKind::AutoreleasepoolPush)) |
2477
+ (1 << unsigned (ARCInstKind::AutoreleasepoolPop))))
2478
+ OptimizeAutoreleasePools (F);
2479
+
2476
2480
// Gather statistics after optimization.
2477
2481
#ifndef NDEBUG
2478
2482
if (AreStatisticsEnabled ()) {
@@ -2485,6 +2489,107 @@ bool ObjCARCOpt::run(Function &F, AAResults &AA) {
2485
2489
return Changed;
2486
2490
}
2487
2491
2492
+ // / Optimize autorelease pools by eliminating empty push/pop pairs.
2493
+ void ObjCARCOpt::OptimizeAutoreleasePools (Function &F) {
2494
+ LLVM_DEBUG (dbgs () << " \n == ObjCARCOpt::OptimizeAutoreleasePools ==\n " );
2495
+
2496
+ // Track empty autorelease pool push/pop pairs
2497
+ SmallVector<std::pair<CallInst *, CallInst *>, 4 > EmptyPoolPairs;
2498
+
2499
+ // Process each basic block independently.
2500
+ // TODO: Can we optimize inter-block autorelease pool pairs?
2501
+ // This would involve tracking autorelease pool state across blocks.
2502
+ for (BasicBlock &BB : F) {
2503
+ CallInst *PendingPush = nullptr ;
2504
+ bool HasAutoreleaseInScope = false ;
2505
+
2506
+ for (Instruction &Inst : BB) {
2507
+ ARCInstKind Class = GetBasicARCInstKind (&Inst);
2508
+
2509
+ switch (Class) {
2510
+ case ARCInstKind::AutoreleasepoolPush: {
2511
+ // Start tracking a new autorelease pool scope
2512
+ PendingPush = cast<CallInst>(&Inst);
2513
+ HasAutoreleaseInScope = false ;
2514
+ LLVM_DEBUG (dbgs () << " Found autorelease pool push: " << *PendingPush
2515
+ << " \n " );
2516
+ break ;
2517
+ }
2518
+
2519
+ case ARCInstKind::AutoreleasepoolPop: {
2520
+ CallInst *Pop = cast<CallInst>(&Inst);
2521
+
2522
+ if (PendingPush) {
2523
+ // Check if this pop matches the pending push by comparing the token
2524
+ Value *PopArg = Pop->getArgOperand (0 );
2525
+ bool IsMatchingPop = (PopArg == PendingPush);
2526
+
2527
+ // Also handle bitcast case
2528
+ if (!IsMatchingPop && isa<BitCastInst>(PopArg)) {
2529
+ Value *BitcastSrc = cast<BitCastInst>(PopArg)->getOperand (0 );
2530
+ IsMatchingPop = (BitcastSrc == PendingPush);
2531
+ }
2532
+
2533
+ if (IsMatchingPop && !HasAutoreleaseInScope) {
2534
+ LLVM_DEBUG (dbgs () << " Eliminating empty autorelease pool pair: "
2535
+ << *PendingPush << " and " << *Pop << " \n " );
2536
+
2537
+ // Store the pair for careful deletion later
2538
+ EmptyPoolPairs.push_back ({PendingPush, Pop});
2539
+
2540
+ Changed = true ;
2541
+ ++NumNoops;
2542
+ }
2543
+ }
2544
+
2545
+ PendingPush = nullptr ;
2546
+ HasAutoreleaseInScope = false ;
2547
+ break ;
2548
+ }
2549
+ case ARCInstKind::CallOrUser:
2550
+ case ARCInstKind::Call:
2551
+ case ARCInstKind::Autorelease:
2552
+ case ARCInstKind::AutoreleaseRV: {
2553
+ // Track that we have autorelease calls in the current pool scope
2554
+ if (PendingPush) {
2555
+ HasAutoreleaseInScope = true ;
2556
+ LLVM_DEBUG (
2557
+ dbgs ()
2558
+ << " Found autorelease or potiential autorelease in pool scope: "
2559
+ << Inst << " \n " );
2560
+ }
2561
+ break ;
2562
+ }
2563
+
2564
+ default :
2565
+ break ;
2566
+ }
2567
+ }
2568
+ }
2569
+
2570
+ // Handle empty pool pairs carefully to avoid use-after-delete
2571
+ SmallVector<CallInst *, 8 > DeadInsts;
2572
+ for (auto &Pair : EmptyPoolPairs) {
2573
+ CallInst *Push = Pair.first ;
2574
+ CallInst *Pop = Pair.second ;
2575
+
2576
+ // Replace the pop's argument with poison to break dependencies
2577
+ Value *PoisonToken = PoisonValue::get (Push->getType ());
2578
+ Pop->setArgOperand (0 , PoisonToken);
2579
+
2580
+ LLVM_DEBUG (dbgs () << " Erasing empty pool pair: " << *Push << " and " << *Pop
2581
+ << " \n " );
2582
+ DeadInsts.push_back (Pop);
2583
+ DeadInsts.push_back (Push);
2584
+ }
2585
+
2586
+ // Remove the pairs
2587
+ for (CallInst *DeadInst : DeadInsts) {
2588
+ LLVM_DEBUG (dbgs () << " Erasing dead instruction: " << *DeadInst << " \n " );
2589
+ DeadInst->eraseFromParent ();
2590
+ }
2591
+ }
2592
+
2488
2593
// / @}
2489
2594
// /
2490
2595
0 commit comments