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,183 @@ bool ObjCARCOpt::run(Function &F, AAResults &AA) {
2485
2490
return Changed;
2486
2491
}
2487
2492
2493
+ // / Interprocedurally determine if calls made by the given call site can
2494
+ // / possibly produce autoreleases.
2495
+ bool MayAutorelease (const CallBase &CB, unsigned Depth = 0 ) {
2496
+ if (CB.onlyReadsMemory ())
2497
+ return false ;
2498
+
2499
+ // This recursion depth limit is arbitrary. It's just great
2500
+ // enough to cover known interesting testcases.
2501
+ if (Depth > 5 )
2502
+ return true ;
2503
+
2504
+ if (const Function *Callee = CB.getCalledFunction ()) {
2505
+ if (!Callee->hasExactDefinition ())
2506
+ return true ;
2507
+ for (const BasicBlock &BB : *Callee) {
2508
+ for (const Instruction &I : BB) {
2509
+ // TODO: Ignore all instructions between autorelease pools
2510
+ ARCInstKind InstKind = GetBasicARCInstKind (&I);
2511
+ switch (InstKind) {
2512
+ case ARCInstKind::Autorelease:
2513
+ case ARCInstKind::AutoreleaseRV:
2514
+ case ARCInstKind::FusedRetainAutorelease:
2515
+ case ARCInstKind::FusedRetainAutoreleaseRV:
2516
+ case ARCInstKind::LoadWeak:
2517
+ // These may produce autoreleases
2518
+ return true ;
2519
+
2520
+ case ARCInstKind::Retain:
2521
+ case ARCInstKind::RetainRV:
2522
+ case ARCInstKind::UnsafeClaimRV:
2523
+ case ARCInstKind::RetainBlock:
2524
+ case ARCInstKind::Release:
2525
+ case ARCInstKind::NoopCast:
2526
+ case ARCInstKind::LoadWeakRetained:
2527
+ case ARCInstKind::StoreWeak:
2528
+ case ARCInstKind::InitWeak:
2529
+ case ARCInstKind::MoveWeak:
2530
+ case ARCInstKind::CopyWeak:
2531
+ case ARCInstKind::DestroyWeak:
2532
+ case ARCInstKind::StoreStrong:
2533
+ case ARCInstKind::AutoreleasepoolPush:
2534
+ case ARCInstKind::AutoreleasepoolPop:
2535
+ // These ObjC runtime functions don't produce autoreleases
2536
+ break ;
2537
+
2538
+ case ARCInstKind::CallOrUser:
2539
+ case ARCInstKind::Call:
2540
+ // For non-ObjC function calls, recursively analyze
2541
+ if (MayAutorelease (cast<CallBase>(I), Depth + 1 ))
2542
+ return true ;
2543
+ break ;
2544
+
2545
+ case ARCInstKind::IntrinsicUser:
2546
+ case ARCInstKind::User:
2547
+ case ARCInstKind::None:
2548
+ // These are not relevant for autorelease analysis
2549
+ break ;
2550
+ }
2551
+ }
2552
+ }
2553
+ return false ;
2554
+ }
2555
+
2556
+ return true ;
2557
+ }
2558
+
2559
+ // / Optimize autorelease pools by eliminating empty push/pop pairs.
2560
+ void ObjCARCOpt::OptimizeAutoreleasePools (Function &F) {
2561
+ LLVM_DEBUG (dbgs () << " \n == ObjCARCOpt::OptimizeAutoreleasePools ==\n " );
2562
+
2563
+ OptimizationRemarkEmitter ORE (&F);
2564
+
2565
+ // Process each basic block independently.
2566
+ // TODO: Can we optimize inter-block autorelease pool pairs?
2567
+ // This would involve tracking autorelease pool state across blocks.
2568
+ for (BasicBlock &BB : F) {
2569
+ // Use a stack to track nested autorelease pools
2570
+ SmallVector<std::pair<CallInst *, bool >, 4 >
2571
+ PoolStack; // {push_inst, has_autorelease_in_scope}
2572
+
2573
+ for (Instruction &Inst : llvm::make_early_inc_range (BB)) {
2574
+ ARCInstKind Class = GetBasicARCInstKind (&Inst);
2575
+
2576
+ switch (Class) {
2577
+ case ARCInstKind::AutoreleasepoolPush: {
2578
+ // Start tracking a new autorelease pool scope
2579
+ auto *Push = cast<CallInst>(&Inst);
2580
+ PoolStack.push_back (
2581
+ {Push, false }); // {push_inst, has_autorelease_in_scope}
2582
+ LLVM_DEBUG (dbgs () << " Found autorelease pool push: " << *Push << " \n " );
2583
+ break ;
2584
+ }
2585
+
2586
+ case ARCInstKind::AutoreleasepoolPop: {
2587
+ auto *Pop = cast<CallInst>(&Inst);
2588
+
2589
+ if (PoolStack.empty ())
2590
+ break ;
2591
+
2592
+ auto &TopPool = PoolStack.back ();
2593
+ CallInst *PendingPush = TopPool.first ;
2594
+ bool HasAutoreleaseInScope = TopPool.second ;
2595
+
2596
+ // Pop the stack - remove this pool scope
2597
+ PoolStack.pop_back ();
2598
+
2599
+ // Bail if this pop doesn't match the pending push
2600
+ if (Pop->getArgOperand (0 )->stripPointerCasts () != PendingPush)
2601
+ break ;
2602
+
2603
+ // Bail if there were autoreleases in this scope
2604
+ if (HasAutoreleaseInScope)
2605
+ break ;
2606
+
2607
+ // Optimize: eliminate this empty autorelease pool pair
2608
+ ORE.emit ([&]() {
2609
+ return OptimizationRemark (DEBUG_TYPE, " AutoreleasePoolElimination" ,
2610
+ PendingPush)
2611
+ << " eliminated empty autorelease pool pair" ;
2612
+ });
2613
+
2614
+ // Replace all uses of push with poison before deletion
2615
+ PendingPush->replaceAllUsesWith (
2616
+ PoisonValue::get (PendingPush->getType ()));
2617
+
2618
+ Pop->eraseFromParent ();
2619
+ PendingPush->eraseFromParent ();
2620
+
2621
+ Changed = true ;
2622
+ ++NumNoops;
2623
+ break ;
2624
+ }
2625
+ case ARCInstKind::CallOrUser:
2626
+ case ARCInstKind::Call:
2627
+ if (!MayAutorelease (cast<CallBase>(Inst)))
2628
+ break ;
2629
+ LLVM_FALLTHROUGH;
2630
+ case ARCInstKind::Autorelease:
2631
+ case ARCInstKind::AutoreleaseRV:
2632
+ case ARCInstKind::FusedRetainAutorelease:
2633
+ case ARCInstKind::FusedRetainAutoreleaseRV:
2634
+ case ARCInstKind::LoadWeak: {
2635
+ // Track that we have autorelease calls in the current pool scope
2636
+ if (!PoolStack.empty ()) {
2637
+ PoolStack.back ().second = true ; // Set has_autorelease_in_scope = true
2638
+ LLVM_DEBUG (
2639
+ dbgs ()
2640
+ << " Found autorelease or potential autorelease in pool scope: "
2641
+ << Inst << " \n " );
2642
+ }
2643
+ break ;
2644
+ }
2645
+
2646
+ // Enumerate all remaining ARCInstKind cases explicitly
2647
+ case ARCInstKind::Retain:
2648
+ case ARCInstKind::RetainRV:
2649
+ case ARCInstKind::UnsafeClaimRV:
2650
+ case ARCInstKind::RetainBlock:
2651
+ case ARCInstKind::Release:
2652
+ case ARCInstKind::NoopCast:
2653
+ case ARCInstKind::LoadWeakRetained:
2654
+ case ARCInstKind::StoreWeak:
2655
+ case ARCInstKind::InitWeak:
2656
+ case ARCInstKind::MoveWeak:
2657
+ case ARCInstKind::CopyWeak:
2658
+ case ARCInstKind::DestroyWeak:
2659
+ case ARCInstKind::StoreStrong:
2660
+ case ARCInstKind::IntrinsicUser:
2661
+ case ARCInstKind::User:
2662
+ case ARCInstKind::None:
2663
+ // These instruction kinds don't affect autorelease pool optimization
2664
+ break ;
2665
+ }
2666
+ }
2667
+ }
2668
+ }
2669
+
2488
2670
// / @}
2489
2671
// /
2490
2672
0 commit comments