https://github.com/AZero13 updated https://github.com/llvm/llvm-project/pull/144788
>From 32b8b89591596f5ddf467c96311fc431656d6cd1 Mon Sep 17 00:00:00 2001 From: Rose <gfunni...@gmail.com> Date: Wed, 18 Jun 2025 16:05:44 -0400 Subject: [PATCH 1/2] [ObjCARC] Delete empty autoreleasepools with no autoreleases in them Erase empty autorelease pools that have no autorelease in them --- .../ObjCARC/ARCRuntimeEntryPoints.h | 16 +++ llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp | 123 +++++++++++++++++- .../ObjCARC/test_autorelease_pool.ll | 118 +++++++++++++++++ 3 files changed, 252 insertions(+), 5 deletions(-) create mode 100644 llvm/test/Transforms/ObjCARC/test_autorelease_pool.ll diff --git a/llvm/lib/Transforms/ObjCARC/ARCRuntimeEntryPoints.h b/llvm/lib/Transforms/ObjCARC/ARCRuntimeEntryPoints.h index 3fa844eda21cf..6135c7b938a3e 100644 --- a/llvm/lib/Transforms/ObjCARC/ARCRuntimeEntryPoints.h +++ b/llvm/lib/Transforms/ObjCARC/ARCRuntimeEntryPoints.h @@ -46,6 +46,8 @@ enum class ARCRuntimeEntryPointKind { UnsafeClaimRV, RetainAutorelease, RetainAutoreleaseRV, + AutoreleasePoolPush, + AutoreleasePoolPop, }; /// Declarations for ObjC runtime functions and constants. These are initialized @@ -67,6 +69,8 @@ class ARCRuntimeEntryPoints { UnsafeClaimRV = nullptr; RetainAutorelease = nullptr; RetainAutoreleaseRV = nullptr; + AutoreleasePoolPush = nullptr; + AutoreleasePoolPop = nullptr; } Function *get(ARCRuntimeEntryPointKind kind) { @@ -101,6 +105,12 @@ class ARCRuntimeEntryPoints { case ARCRuntimeEntryPointKind::RetainAutoreleaseRV: return getIntrinsicEntryPoint(RetainAutoreleaseRV, Intrinsic::objc_retainAutoreleaseReturnValue); + case ARCRuntimeEntryPointKind::AutoreleasePoolPush: + return getIntrinsicEntryPoint(AutoreleasePoolPush, + Intrinsic::objc_autoreleasePoolPush); + case ARCRuntimeEntryPointKind::AutoreleasePoolPop: + return getIntrinsicEntryPoint(AutoreleasePoolPop, + Intrinsic::objc_autoreleasePoolPop); } llvm_unreachable("Switch should be a covered switch."); @@ -143,6 +153,12 @@ class ARCRuntimeEntryPoints { /// Declaration for objc_retainAutoreleaseReturnValue(). Function *RetainAutoreleaseRV = nullptr; + /// Declaration for objc_autoreleasePoolPush(). + Function *AutoreleasePoolPush = nullptr; + + /// Declaration for objc_autoreleasePoolPop(). + Function *AutoreleasePoolPop = nullptr; + Function *getIntrinsicEntryPoint(Function *&Decl, Intrinsic::ID IntID) { if (Decl) return Decl; diff --git a/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp b/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp index 5eb3f51d38945..deec643532c5d 100644 --- a/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp +++ b/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp @@ -39,6 +39,7 @@ #include "llvm/Analysis/ObjCARCAnalysisUtils.h" #include "llvm/Analysis/ObjCARCInstKind.h" #include "llvm/Analysis/ObjCARCUtil.h" +#include "llvm/Analysis/OptimizationRemarkEmitter.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/CFG.h" #include "llvm/IR/Constant.h" @@ -132,11 +133,8 @@ static const Value *FindSingleUseIdentifiedObject(const Value *Arg) { // // The second retain and autorelease can be deleted. -// TODO: It should be possible to delete -// objc_autoreleasePoolPush and objc_autoreleasePoolPop -// pairs if nothing is actually autoreleased between them. Also, autorelease -// calls followed by objc_autoreleasePoolPop calls (perhaps in ObjC++ code -// after inlining) can be turned into plain release calls. +// TODO: Autorelease calls followed by objc_autoreleasePoolPop calls (perhaps in +// ObjC++ code after inlining) can be turned into plain release calls. // TODO: Critical-edge splitting. If the optimial insertion point is // a critical edge, the current algorithm has to fail, because it doesn't @@ -566,6 +564,8 @@ class ObjCARCOpt { void OptimizeReturns(Function &F); + void OptimizeAutoreleasePools(Function &F); + template <typename PredicateT> static void cloneOpBundlesIf(CallBase *CI, SmallVectorImpl<OperandBundleDef> &OpBundles, @@ -2473,6 +2473,11 @@ bool ObjCARCOpt::run(Function &F, AAResults &AA) { (1 << unsigned(ARCInstKind::AutoreleaseRV)))) OptimizeReturns(F); + // Optimizations for autorelease pools. + if (UsedInThisFunction & ((1 << unsigned(ARCInstKind::AutoreleasepoolPush)) | + (1 << unsigned(ARCInstKind::AutoreleasepoolPop)))) + OptimizeAutoreleasePools(F); + // Gather statistics after optimization. #ifndef NDEBUG if (AreStatisticsEnabled()) { @@ -2485,6 +2490,114 @@ 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"); + + OptimizationRemarkEmitter ORE(&F); + + // 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) { + // Use a stack to track nested autorelease pools + SmallVector<std::pair<CallInst *, bool>, 4> + PoolStack; // {push_inst, has_autorelease_in_scope} + + for (Instruction &Inst : llvm::make_early_inc_range(BB)) { + ARCInstKind Class = GetBasicARCInstKind(&Inst); + + switch (Class) { + case ARCInstKind::AutoreleasepoolPush: { + // Start tracking a new autorelease pool scope + auto *Push = cast<CallInst>(&Inst); + PoolStack.push_back( + {Push, false}); // {push_inst, has_autorelease_in_scope} + LLVM_DEBUG(dbgs() << "Found autorelease pool push: " << *Push << "\n"); + break; + } + + case ARCInstKind::AutoreleasepoolPop: { + auto *Pop = cast<CallInst>(&Inst); + + if (PoolStack.empty()) + break; + + auto &TopPool = PoolStack.back(); + CallInst *PendingPush = TopPool.first; + bool HasAutoreleaseInScope = TopPool.second; + + // Pop the stack - remove this pool scope + PoolStack.pop_back(); + + // Bail if this pop doesn't match the pending push + if (Pop->getArgOperand(0)->stripPointerCasts() != PendingPush) + break; + + // Bail if there were autoreleases in this scope + if (HasAutoreleaseInScope) + break; + + // Optimize: eliminate this empty autorelease pool pair + ORE.emit([&]() { + return OptimizationRemark(DEBUG_TYPE, "AutoreleasePoolElimination", + PendingPush) + << "eliminated empty autorelease pool pair"; + }); + + // Replace all uses of push with poison before deletion + PendingPush->replaceAllUsesWith( + PoisonValue::get(PendingPush->getType())); + + PendingPush->eraseFromParent(); + Pop->eraseFromParent(); + + Changed = true; + ++NumNoops; + break; + } + case ARCInstKind::CallOrUser: + case ARCInstKind::Call: + case ARCInstKind::Autorelease: + case ARCInstKind::AutoreleaseRV: + case ARCInstKind::FusedRetainAutorelease: + case ARCInstKind::FusedRetainAutoreleaseRV: + case ARCInstKind::LoadWeak: { + // Track that we have autorelease calls in the current pool scope + if (!PoolStack.empty()) { + PoolStack.back().second = true; // Set has_autorelease_in_scope = true + LLVM_DEBUG( + dbgs() + << "Found autorelease or potiential autorelease in pool scope: " + << Inst << "\n"); + } + break; + } + + // Enumerate all remaining ARCInstKind cases explicitly + case ARCInstKind::Retain: + case ARCInstKind::RetainRV: + case ARCInstKind::UnsafeClaimRV: + case ARCInstKind::RetainBlock: + case ARCInstKind::Release: + case ARCInstKind::NoopCast: + case ARCInstKind::LoadWeakRetained: + case ARCInstKind::StoreWeak: + case ARCInstKind::InitWeak: + case ARCInstKind::MoveWeak: + case ARCInstKind::CopyWeak: + case ARCInstKind::DestroyWeak: + case ARCInstKind::StoreStrong: + case ARCInstKind::IntrinsicUser: + case ARCInstKind::User: + case ARCInstKind::None: + // These instruction kinds don't affect autorelease pool optimization + break; + } + } + } +} + /// @} /// diff --git a/llvm/test/Transforms/ObjCARC/test_autorelease_pool.ll b/llvm/test/Transforms/ObjCARC/test_autorelease_pool.ll new file mode 100644 index 0000000000000..ac27211a9e60c --- /dev/null +++ b/llvm/test/Transforms/ObjCARC/test_autorelease_pool.ll @@ -0,0 +1,118 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; Test for autorelease pool optimizations +; RUN: opt -passes=objc-arc < %s -S | FileCheck %s + +declare ptr @llvm.objc.autoreleasePoolPush() +declare void @llvm.objc.autoreleasePoolPop(ptr) +declare ptr @llvm.objc.autorelease(ptr) +declare ptr @llvm.objc.retain(ptr) +declare ptr @create_object() +declare void @use_object(ptr) +declare ptr @object_with_thing() +declare void @opaque_callee() + +; Test 1: Empty autorelease pool should be eliminated +define void @test_empty_pool() { +; CHECK-LABEL: define void @test_empty_pool() { +; CHECK-NEXT: ret void +; + %pool = call ptr @llvm.objc.autoreleasePoolPush() + call void @llvm.objc.autoreleasePoolPop(ptr %pool) + ret void +} + +; Test 2: Pool with only release should be removed +define void @test_autorelease_to_release() { +; CHECK-LABEL: define void @test_autorelease_to_release() { +; CHECK-NEXT: [[OBJ:%.*]] = call ptr @create_object() +; CHECK-NEXT: call void @llvm.objc.release(ptr [[OBJ]]) #[[ATTR0:[0-9]+]], !clang.imprecise_release [[META0:![0-9]+]] +; CHECK-NEXT: ret void +; + %obj = call ptr @create_object() + %pool = call ptr @llvm.objc.autoreleasePoolPush() + call ptr @llvm.objc.autorelease(ptr %obj) + call void @llvm.objc.autoreleasePoolPop(ptr %pool) + ret void +} + +; Test 3: Pool with autoreleases should not be optimized +define void @test_multiple_autoreleases() { +; CHECK-LABEL: define void @test_multiple_autoreleases() { +; CHECK-NEXT: [[OBJ1:%.*]] = call ptr @create_object() +; CHECK-NEXT: [[OBJ2:%.*]] = call ptr @create_object() +; CHECK-NEXT: [[POOL:%.*]] = call ptr @llvm.objc.autoreleasePoolPush() #[[ATTR0]] +; 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.autoreleasePoolPop(ptr [[POOL]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; + %obj1 = call ptr @create_object() + %obj2 = call ptr @create_object() + %pool = call ptr @llvm.objc.autoreleasePoolPush() + call void @use_object(ptr %obj1) + call ptr @llvm.objc.autorelease(ptr %obj1) + call void @use_object(ptr %obj2) + call ptr @llvm.objc.autorelease(ptr %obj2) + call void @llvm.objc.autoreleasePoolPop(ptr %pool) + ret void +} + +; Test 4: Pool with calls should not be optimized +define void @test_calls() { +; CHECK-LABEL: define void @test_calls() { +; CHECK-NEXT: [[POOL:%.*]] = call ptr @llvm.objc.autoreleasePoolPush() #[[ATTR0]] +; CHECK-NEXT: [[OBJ1:%.*]] = call ptr @object_with_thing() +; CHECK-NEXT: call void @use_object(ptr [[OBJ1]]) +; CHECK-NEXT: call void @llvm.objc.autoreleasePoolPop(ptr [[POOL]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; + %pool = call ptr @llvm.objc.autoreleasePoolPush() + %obj1 = call ptr @object_with_thing() + call void @use_object(ptr %obj1) + call void @llvm.objc.autoreleasePoolPop(ptr %pool) + ret void +} + +; Test 5: Pool with opaque call should not be optimized +define void @test_opaque_call() { +; CHECK-LABEL: define void @test_opaque_call() { +; CHECK-NEXT: [[POOL:%.*]] = call ptr @llvm.objc.autoreleasePoolPush() #[[ATTR0]] +; CHECK-NEXT: call void @opaque_callee() +; CHECK-NEXT: call void @llvm.objc.autoreleasePoolPop(ptr [[POOL]]) #[[ATTR0]] +; CHECK-NEXT: ret void +; + %pool = call ptr @llvm.objc.autoreleasePoolPush() + call void @opaque_callee() + call void @llvm.objc.autoreleasePoolPop(ptr %pool) + ret void +} + +; Test 6: Nested empty pools should be eliminated +define void @test_nested_empty_pools() { +; CHECK-LABEL: define void @test_nested_empty_pools() { +; CHECK-NEXT: ret void +; + %pool1 = call ptr @llvm.objc.autoreleasePoolPush() + %pool2 = call ptr @llvm.objc.autoreleasePoolPush() + call void @llvm.objc.autoreleasePoolPop(ptr %pool2) + call void @llvm.objc.autoreleasePoolPop(ptr %pool1) + ret void +} + +; Test 7: Empty pool with cast should be eliminated +define void @test_empty_pool_with_cast() { +; CHECK-LABEL: define void @test_empty_pool_with_cast() { +; CHECK-NEXT: [[CAST:%.*]] = bitcast ptr poison to ptr +; CHECK-NEXT: ret void +; + %pool = call ptr @llvm.objc.autoreleasePoolPush() + %cast = bitcast ptr %pool to ptr + call void @llvm.objc.autoreleasePoolPop(ptr %cast) + ret void +} + +;. +; CHECK: [[META0]] = !{} +;. >From 7fc9780811c7302d9bfdbc855453a0ba3938820b Mon Sep 17 00:00:00 2001 From: Rose <gfunni...@gmail.com> Date: Thu, 19 Jun 2025 16:10:14 -0400 Subject: [PATCH 2/2] Delete ObjCARCAPElim, as it is entirely superseded by ObjCARCOpts --- clang/lib/CodeGen/BackendUtil.cpp | 6 - llvm/include/llvm/Transforms/ObjCARC.h | 4 - llvm/lib/Passes/PassRegistry.def | 1 - llvm/lib/Transforms/ObjCARC/CMakeLists.txt | 1 - llvm/lib/Transforms/ObjCARC/ObjCARCAPElim.cpp | 156 ------------------ llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp | 23 +++ llvm/test/Transforms/ObjCARC/apelim.ll | 2 +- llvm/test/Transforms/ObjCARC/comdat-ipo.ll | 2 +- .../llvm/lib/Transforms/ObjCARC/BUILD.gn | 1 - 9 files changed, 25 insertions(+), 171 deletions(-) delete mode 100644 llvm/lib/Transforms/ObjCARC/ObjCARCAPElim.cpp diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp index 7e0a3cf5591ce..ab2707e0d1c05 100644 --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -1022,12 +1022,6 @@ void EmitAssemblyHelper::RunOptimizationPipeline( MPM.addPass( createModuleToFunctionPassAdaptor(ObjCARCExpandPass())); }); - PB.registerPipelineEarlySimplificationEPCallback( - [](ModulePassManager &MPM, OptimizationLevel Level, - ThinOrFullLTOPhase) { - if (Level != OptimizationLevel::O0) - MPM.addPass(ObjCARCAPElimPass()); - }); PB.registerScalarOptimizerLateEPCallback( [](FunctionPassManager &FPM, OptimizationLevel Level) { if (Level != OptimizationLevel::O0) diff --git a/llvm/include/llvm/Transforms/ObjCARC.h b/llvm/include/llvm/Transforms/ObjCARC.h index c927513469a35..c4b4c4f0b09c6 100644 --- a/llvm/include/llvm/Transforms/ObjCARC.h +++ b/llvm/include/llvm/Transforms/ObjCARC.h @@ -35,10 +35,6 @@ struct ObjCARCContractPass : public PassInfoMixin<ObjCARCContractPass> { LLVM_ABI PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); }; -struct ObjCARCAPElimPass : public PassInfoMixin<ObjCARCAPElimPass> { - LLVM_ABI PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); -}; - struct ObjCARCExpandPass : public PassInfoMixin<ObjCARCExpandPass> { LLVM_ABI PreservedAnalyses run(Function &M, FunctionAnalysisManager &AM); }; diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def index ec14c6a9211d9..6d184d71b92cb 100644 --- a/llvm/lib/Passes/PassRegistry.def +++ b/llvm/lib/Passes/PassRegistry.def @@ -113,7 +113,6 @@ MODULE_PASS("module-inline", ModuleInlinerPass()) MODULE_PASS("name-anon-globals", NameAnonGlobalPass()) MODULE_PASS("no-op-module", NoOpModulePass()) MODULE_PASS("nsan", NumericalStabilitySanitizerPass()) -MODULE_PASS("objc-arc-apelim", ObjCARCAPElimPass()) MODULE_PASS("openmp-opt", OpenMPOptPass()) MODULE_PASS("openmp-opt-postlink", OpenMPOptPass(ThinOrFullLTOPhase::FullLTOPostLink)) diff --git a/llvm/lib/Transforms/ObjCARC/CMakeLists.txt b/llvm/lib/Transforms/ObjCARC/CMakeLists.txt index 80867dbc270d7..4274667a2c2b7 100644 --- a/llvm/lib/Transforms/ObjCARC/CMakeLists.txt +++ b/llvm/lib/Transforms/ObjCARC/CMakeLists.txt @@ -2,7 +2,6 @@ add_llvm_component_library(LLVMObjCARCOpts ObjCARC.cpp ObjCARCOpts.cpp ObjCARCExpand.cpp - ObjCARCAPElim.cpp ObjCARCContract.cpp DependencyAnalysis.cpp ProvenanceAnalysis.cpp diff --git a/llvm/lib/Transforms/ObjCARC/ObjCARCAPElim.cpp b/llvm/lib/Transforms/ObjCARC/ObjCARCAPElim.cpp deleted file mode 100644 index dceb2ebb1863e..0000000000000 --- a/llvm/lib/Transforms/ObjCARC/ObjCARCAPElim.cpp +++ /dev/null @@ -1,156 +0,0 @@ -//===- ObjCARCAPElim.cpp - ObjC ARC Optimization --------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// \file -/// -/// This file defines ObjC ARC optimizations. ARC stands for Automatic -/// Reference Counting and is a system for managing reference counts for objects -/// in Objective C. -/// -/// This specific file implements optimizations which remove extraneous -/// autorelease pools. -/// -/// WARNING: This file knows about certain library functions. It recognizes them -/// by name, and hardwires knowledge of their semantics. -/// -/// WARNING: This file knows about how certain Objective-C library functions are -/// used. Naive LLVM IR transformations which would otherwise be -/// behavior-preserving may break these assumptions. -/// -//===----------------------------------------------------------------------===// - -#include "llvm/ADT/STLExtras.h" -#include "llvm/Analysis/ObjCARCAnalysisUtils.h" -#include "llvm/Analysis/ObjCARCInstKind.h" -#include "llvm/IR/Constants.h" -#include "llvm/IR/InstrTypes.h" -#include "llvm/IR/PassManager.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Transforms/ObjCARC.h" - -using namespace llvm; -using namespace llvm::objcarc; - -#define DEBUG_TYPE "objc-arc-ap-elim" - -namespace { - -/// Interprocedurally determine if calls made by the given call site can -/// possibly produce autoreleases. -bool MayAutorelease(const CallBase &CB, unsigned Depth = 0) { - if (const Function *Callee = CB.getCalledFunction()) { - if (!Callee->hasExactDefinition()) - return true; - for (const BasicBlock &BB : *Callee) { - for (const Instruction &I : BB) - if (const CallBase *JCB = dyn_cast<CallBase>(&I)) - // This recursion depth limit is arbitrary. It's just great - // enough to cover known interesting testcases. - if (Depth < 3 && !JCB->onlyReadsMemory() && - MayAutorelease(*JCB, Depth + 1)) - return true; - } - return false; - } - - return true; -} - -bool OptimizeBB(BasicBlock *BB) { - bool Changed = false; - - Instruction *Push = nullptr; - for (Instruction &Inst : llvm::make_early_inc_range(*BB)) { - switch (GetBasicARCInstKind(&Inst)) { - case ARCInstKind::AutoreleasepoolPush: - Push = &Inst; - break; - case ARCInstKind::AutoreleasepoolPop: - // If this pop matches a push and nothing in between can autorelease, - // zap the pair. - if (Push && cast<CallInst>(&Inst)->getArgOperand(0) == Push) { - Changed = true; - LLVM_DEBUG(dbgs() << "ObjCARCAPElim::OptimizeBB: Zapping push pop " - "autorelease pair:\n" - " Pop: " - << Inst << "\n" - << " Push: " << *Push - << "\n"); - Inst.eraseFromParent(); - Push->eraseFromParent(); - } - Push = nullptr; - break; - case ARCInstKind::CallOrUser: - if (MayAutorelease(cast<CallBase>(Inst))) - Push = nullptr; - break; - default: - break; - } - } - - return Changed; -} - -bool runImpl(Module &M) { - if (!EnableARCOpts) - return false; - - // If nothing in the Module uses ARC, don't do anything. - if (!ModuleHasARC(M)) - return false; - // Find the llvm.global_ctors variable, as the first step in - // identifying the global constructors. In theory, unnecessary autorelease - // pools could occur anywhere, but in practice it's pretty rare. Global - // ctors are a place where autorelease pools get inserted automatically, - // so it's pretty common for them to be unnecessary, and it's pretty - // profitable to eliminate them. - GlobalVariable *GV = M.getGlobalVariable("llvm.global_ctors"); - if (!GV) - return false; - - assert(GV->hasDefinitiveInitializer() && - "llvm.global_ctors is uncooperative!"); - - bool Changed = false; - - // Dig the constructor functions out of GV's initializer. - ConstantArray *Init = cast<ConstantArray>(GV->getInitializer()); - for (User::op_iterator OI = Init->op_begin(), OE = Init->op_end(); - OI != OE; ++OI) { - Value *Op = *OI; - // llvm.global_ctors is an array of three-field structs where the second - // members are constructor functions. - Function *F = dyn_cast<Function>(cast<ConstantStruct>(Op)->getOperand(1)); - // If the user used a constructor function with the wrong signature and - // it got bitcasted or whatever, look the other way. - if (!F) - continue; - // Only look at function definitions. - if (F->isDeclaration()) - continue; - // Only look at functions with one basic block. - if (std::next(F->begin()) != F->end()) - continue; - // Ok, a single-block constructor function definition. Try to optimize it. - Changed |= OptimizeBB(&F->front()); - } - - return Changed; -} - -} // namespace - -PreservedAnalyses ObjCARCAPElimPass::run(Module &M, ModuleAnalysisManager &AM) { - if (!runImpl(M)) - return PreservedAnalyses::all(); - PreservedAnalyses PA; - PA.preserveSet<CFGAnalyses>(); - return PA; -} diff --git a/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp b/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp index deec643532c5d..d188e1d539507 100644 --- a/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp +++ b/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp @@ -2490,6 +2490,27 @@ bool ObjCARCOpt::run(Function &F, AAResults &AA) { return Changed; } +/// Interprocedurally determine if calls made by the given call site can +/// possibly produce autoreleases. +bool MayAutorelease(const CallBase &CB, unsigned Depth = 0) { + if (const Function *Callee = CB.getCalledFunction()) { + if (!Callee->hasExactDefinition()) + return true; + for (const BasicBlock &BB : *Callee) { + for (const Instruction &I : BB) + if (const CallBase *JCB = dyn_cast<CallBase>(&I)) + // This recursion depth limit is arbitrary. It's just great + // enough to cover known interesting testcases. + if (Depth < 10 && !JCB->onlyReadsMemory() && + MayAutorelease(*JCB, Depth + 1)) + return true; + } + return false; + } + + return true; +} + /// Optimize autorelease pools by eliminating empty push/pop pairs. void ObjCARCOpt::OptimizeAutoreleasePools(Function &F) { LLVM_DEBUG(dbgs() << "\n== ObjCARCOpt::OptimizeAutoreleasePools ==\n"); @@ -2558,6 +2579,8 @@ void ObjCARCOpt::OptimizeAutoreleasePools(Function &F) { } case ARCInstKind::CallOrUser: case ARCInstKind::Call: + if (!MayAutorelease(cast<CallBase>(Inst))) + break; case ARCInstKind::Autorelease: case ARCInstKind::AutoreleaseRV: case ARCInstKind::FusedRetainAutorelease: diff --git a/llvm/test/Transforms/ObjCARC/apelim.ll b/llvm/test/Transforms/ObjCARC/apelim.ll index 2ac5d15d0df85..01179f3dec048 100644 --- a/llvm/test/Transforms/ObjCARC/apelim.ll +++ b/llvm/test/Transforms/ObjCARC/apelim.ll @@ -1,4 +1,4 @@ -; RUN: opt -S -passes=objc-arc-apelim < %s | FileCheck %s +; RUN: opt -S -passes=objc-arc < %s | FileCheck %s ; rdar://10227311 @llvm.global_ctors = appending global [2 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @_GLOBAL__I_x, ptr null }, { i32, ptr, ptr } { i32 65535, ptr @_GLOBAL__I_y, ptr null }] diff --git a/llvm/test/Transforms/ObjCARC/comdat-ipo.ll b/llvm/test/Transforms/ObjCARC/comdat-ipo.ll index 3f91d3bea9f14..d43804c20d936 100644 --- a/llvm/test/Transforms/ObjCARC/comdat-ipo.ll +++ b/llvm/test/Transforms/ObjCARC/comdat-ipo.ll @@ -1,4 +1,4 @@ -; RUN: opt -S -passes=objc-arc-apelim < %s | FileCheck %s +; RUN: opt -S -passes=objc-arc < %s | FileCheck %s ; See PR26774 diff --git a/llvm/utils/gn/secondary/llvm/lib/Transforms/ObjCARC/BUILD.gn b/llvm/utils/gn/secondary/llvm/lib/Transforms/ObjCARC/BUILD.gn index e7b2084548916..d4ad9154adefe 100644 --- a/llvm/utils/gn/secondary/llvm/lib/Transforms/ObjCARC/BUILD.gn +++ b/llvm/utils/gn/secondary/llvm/lib/Transforms/ObjCARC/BUILD.gn @@ -9,7 +9,6 @@ static_library("ObjCARC") { sources = [ "DependencyAnalysis.cpp", "ObjCARC.cpp", - "ObjCARCAPElim.cpp", "ObjCARCContract.cpp", "ObjCARCExpand.cpp", "ObjCARCOpts.cpp", _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits