//===- 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 "ObjCARC.h" #include "llvm/ADT/STLExtras.h" #include "llvm/IR/Constants.h" #include "llvm/IR/PassManager.h" #include "llvm/InitializePasses.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(&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 (BasicBlock::iterator I = BB->begin(), E = BB->end(); I != E; ) { Instruction *Inst = &*I++; 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(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(*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(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(cast(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; } /// Autorelease pool elimination. class ObjCARCAPElim : public ModulePass { void getAnalysisUsage(AnalysisUsage &AU) const override; bool runOnModule(Module &M) override; public: static char ID; ObjCARCAPElim() : ModulePass(ID) { initializeObjCARCAPElimPass(*PassRegistry::getPassRegistry()); } }; } // namespace char ObjCARCAPElim::ID = 0; INITIALIZE_PASS(ObjCARCAPElim, "objc-arc-apelim", "ObjC ARC autorelease pool elimination", false, false) Pass *llvm::createObjCARCAPElimPass() { return new ObjCARCAPElim(); } void ObjCARCAPElim::getAnalysisUsage(AnalysisUsage &AU) const { AU.setPreservesCFG(); } bool ObjCARCAPElim::runOnModule(Module &M) { if (skipModule(M)) return false; return runImpl(M); } PreservedAnalyses ObjCARCAPElimPass::run(Module &M, ModuleAnalysisManager &AM) { if (!runImpl(M)) return PreservedAnalyses::all(); PreservedAnalyses PA; PA.preserveSet(); return PA; }