39
39
#include " llvm/Analysis/ObjCARCAnalysisUtils.h"
40
40
#include " llvm/Analysis/ObjCARCInstKind.h"
41
41
#include " llvm/Analysis/ObjCARCUtil.h"
42
+ #include " llvm/Analysis/OptimizationRemarkEmitter.h"
42
43
#include " llvm/IR/BasicBlock.h"
43
44
#include " llvm/IR/CFG.h"
44
45
#include " llvm/IR/Constant.h"
@@ -132,11 +133,8 @@ static const Value *FindSingleUseIdentifiedObject(const Value *Arg) {
132
133
//
133
134
// The second retain and autorelease can be deleted.
134
135
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.
136
+ // TODO: Autorelease calls followed by objc_autoreleasePoolPop calls (perhaps in
137
+ // ObjC++ code after inlining) can be turned into plain release calls.
140
138
141
139
// TODO: Critical-edge splitting. If the optimial insertion point is
142
140
// a critical edge, the current algorithm has to fail, because it doesn't
@@ -566,6 +564,8 @@ class ObjCARCOpt {
566
564
567
565
void OptimizeReturns (Function &F);
568
566
567
+ void OptimizeAutoreleasePools (Function &F);
568
+
569
569
template <typename PredicateT>
570
570
static void cloneOpBundlesIf (CallBase *CI,
571
571
SmallVectorImpl<OperandBundleDef> &OpBundles,
@@ -2473,6 +2473,11 @@ bool ObjCARCOpt::run(Function &F, AAResults &AA) {
2473
2473
(1 << unsigned (ARCInstKind::AutoreleaseRV))))
2474
2474
OptimizeReturns (F);
2475
2475
2476
+ // Optimizations for autorelease pools.
2477
+ if (UsedInThisFunction & ((1 << unsigned (ARCInstKind::AutoreleasepoolPush)) |
2478
+ (1 << unsigned (ARCInstKind::AutoreleasepoolPop))))
2479
+ OptimizeAutoreleasePools (F);
2480
+
2476
2481
// Gather statistics after optimization.
2477
2482
#ifndef NDEBUG
2478
2483
if (AreStatisticsEnabled ()) {
@@ -2485,6 +2490,115 @@ bool ObjCARCOpt::run(Function &F, AAResults &AA) {
2485
2490
return Changed;
2486
2491
}
2487
2492
2493
+ // / Optimize autorelease pools by eliminating empty push/pop pairs.
2494
+ void ObjCARCOpt::OptimizeAutoreleasePools (Function &F) {
2495
+ LLVM_DEBUG (dbgs () << " \n == ObjCARCOpt::OptimizeAutoreleasePools ==\n " );
2496
+
2497
+ OptimizationRemarkEmitter ORE (&F);
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
+ // Use a stack to track nested autorelease pools
2504
+ SmallVector<std::pair<CallInst *, bool >, 4 >
2505
+ PoolStack; // {push_inst, has_autorelease_in_scope}
2506
+
2507
+ // Use early_inc_range to safely iterate while potentially erasing instructions
2508
+ for (Instruction &Inst : llvm::make_early_inc_range (BB)) {
2509
+ ARCInstKind Class = GetBasicARCInstKind (&Inst);
2510
+
2511
+ switch (Class) {
2512
+ case ARCInstKind::AutoreleasepoolPush: {
2513
+ // Start tracking a new autorelease pool scope
2514
+ auto *Push = cast<CallInst>(&Inst);
2515
+ PoolStack.push_back (
2516
+ {Push, false }); // {push_inst, has_autorelease_in_scope}
2517
+ LLVM_DEBUG (dbgs () << " Found autorelease pool push: " << *Push << " \n " );
2518
+ break ;
2519
+ }
2520
+
2521
+ case ARCInstKind::AutoreleasepoolPop: {
2522
+ auto *Pop = cast<CallInst>(&Inst);
2523
+
2524
+ if (PoolStack.empty ())
2525
+ break ;
2526
+
2527
+ auto &TopPool = PoolStack.back ();
2528
+ CallInst *PendingPush = TopPool.first ;
2529
+ bool HasAutoreleaseInScope = TopPool.second ;
2530
+
2531
+ // Pop the stack - remove this pool scope
2532
+ PoolStack.pop_back ();
2533
+
2534
+ // Early exit if this pop doesn't match the pending push
2535
+ if (Pop->getArgOperand (0 )->stripPointerCasts () != PendingPush)
2536
+ break ;
2537
+
2538
+ // Early exit if there were autoreleases in this scope
2539
+ if (HasAutoreleaseInScope)
2540
+ break ;
2541
+
2542
+ // Optimize: eliminate this empty autorelease pool pair
2543
+ ORE.emit ([&]() {
2544
+ return OptimizationRemark (DEBUG_TYPE, " AutoreleasePoolElimination" ,
2545
+ PendingPush)
2546
+ << " eliminated empty autorelease pool pair" ;
2547
+ });
2548
+
2549
+ // Replace all uses of push with poison before deletion
2550
+ PendingPush->replaceAllUsesWith (PoisonValue::get (PendingPush->getType ()));
2551
+
2552
+ // Delete the instructions immediately
2553
+ PendingPush->eraseFromParent ();
2554
+ Pop->eraseFromParent ();
2555
+
2556
+ Changed = true ;
2557
+ ++NumNoops;
2558
+ break ;
2559
+ }
2560
+ case ARCInstKind::CallOrUser:
2561
+ case ARCInstKind::Call:
2562
+ case ARCInstKind::Autorelease:
2563
+ case ARCInstKind::AutoreleaseRV:
2564
+ case ARCInstKind::FusedRetainAutorelease:
2565
+ case ARCInstKind::FusedRetainAutoreleaseRV:
2566
+ case ARCInstKind::LoadWeak: {
2567
+ // Track that we have autorelease calls in the current pool scope
2568
+ if (!PoolStack.empty ()) {
2569
+ PoolStack.back ().second = true ; // Set has_autorelease_in_scope = true
2570
+ LLVM_DEBUG (
2571
+ dbgs ()
2572
+ << " Found autorelease or potiential autorelease in pool scope: "
2573
+ << Inst << " \n " );
2574
+ }
2575
+ break ;
2576
+ }
2577
+
2578
+ // Enumerate all remaining ARCInstKind cases explicitly
2579
+ case ARCInstKind::Retain:
2580
+ case ARCInstKind::RetainRV:
2581
+ case ARCInstKind::UnsafeClaimRV:
2582
+ case ARCInstKind::RetainBlock:
2583
+ case ARCInstKind::Release:
2584
+ case ARCInstKind::NoopCast:
2585
+ case ARCInstKind::LoadWeakRetained:
2586
+ case ARCInstKind::StoreWeak:
2587
+ case ARCInstKind::InitWeak:
2588
+ case ARCInstKind::MoveWeak:
2589
+ case ARCInstKind::CopyWeak:
2590
+ case ARCInstKind::DestroyWeak:
2591
+ case ARCInstKind::StoreStrong:
2592
+ case ARCInstKind::IntrinsicUser:
2593
+ case ARCInstKind::User:
2594
+ case ARCInstKind::None:
2595
+ // These instruction kinds don't affect autorelease pool optimization
2596
+ break ;
2597
+ }
2598
+ }
2599
+ }
2600
+ }
2601
+
2488
2602
// / @}
2489
2603
// /
2490
2604
0 commit comments