//===- ObjCARCContract.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 late 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 mainly deals with ``contracting'' multiple lower level /// operations into singular higher level operations through pattern matching. /// /// 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. /// //===----------------------------------------------------------------------===// // TODO: ObjCARCContract could insert PHI nodes when uses aren't // dominated by single calls. #include "ARCRuntimeEntryPoints.h" #include "DependencyAnalysis.h" #include "ObjCARC.h" #include "ProvenanceAnalysis.h" #include "llvm/ADT/Statistic.h" #include "llvm/Analysis/AliasAnalysis.h" #include "llvm/Analysis/BasicAliasAnalysis.h" #include "llvm/Analysis/ObjCARCUtil.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/EHPersonalities.h" #include "llvm/IR/InlineAsm.h" #include "llvm/IR/InstIterator.h" #include "llvm/IR/Operator.h" #include "llvm/IR/PassManager.h" #include "llvm/InitializePasses.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Transforms/ObjCARC.h" usingnamespacellvm; usingnamespacellvm::objcarc; #define DEBUG_TYPE … STATISTIC(NumPeeps, "Number of calls peephole-optimized"); STATISTIC(NumStoreStrongs, "Number objc_storeStrong calls formed"); //===----------------------------------------------------------------------===// // Declarations //===----------------------------------------------------------------------===// namespace { /// Late ARC optimizations /// /// These change the IR in a way that makes it difficult to be analyzed by /// ObjCARCOpt, so it's run late. class ObjCARCContract { … }; class ObjCARCContractLegacyPass : public FunctionPass { … }; } //===----------------------------------------------------------------------===// // Implementation //===----------------------------------------------------------------------===// /// Turn objc_retain into objc_retainAutoreleasedReturnValue if the operand is a /// return value. We do this late so we do not disrupt the dataflow analysis in /// ObjCARCOpt. bool ObjCARCContract::optimizeRetainCall(Function &F, Instruction *Retain) { … } /// Merge an autorelease with a retain into a fused call. bool ObjCARCContract::contractAutorelease(Function &F, Instruction *Autorelease, ARCInstKind Class) { … } static StoreInst *findSafeStoreForStoreStrongContraction(LoadInst *Load, Instruction *Release, ProvenanceAnalysis &PA, AAResults *AA) { … } static Instruction * findRetainForStoreStrongContraction(Value *New, StoreInst *Store, Instruction *Release, ProvenanceAnalysis &PA) { … } /// Attempt to merge an objc_release with a store, load, and objc_retain to form /// an objc_storeStrong. An objc_storeStrong: /// /// objc_storeStrong(i8** %old_ptr, i8* new_value) /// /// is equivalent to the following IR sequence: /// /// ; Load old value. /// %old_value = load i8** %old_ptr (1) /// /// ; Increment the new value and then release the old value. This must occur /// ; in order in case old_value releases new_value in its destructor causing /// ; us to potentially have a dangling ptr. /// tail call i8* @objc_retain(i8* %new_value) (2) /// tail call void @objc_release(i8* %old_value) (3) /// /// ; Store the new_value into old_ptr /// store i8* %new_value, i8** %old_ptr (4) /// /// The safety of this optimization is based around the following /// considerations: /// /// 1. We are forming the store strong at the store. Thus to perform this /// optimization it must be safe to move the retain, load, and release to /// (4). /// 2. We need to make sure that any re-orderings of (1), (2), (3), (4) are /// safe. void ObjCARCContract::tryToContractReleaseIntoStoreStrong( Instruction *Release, inst_iterator &Iter, const DenseMap<BasicBlock *, ColorVector> &BlockColors) { … } bool ObjCARCContract::tryToPeepholeInstruction( Function &F, Instruction *Inst, inst_iterator &Iter, bool &TailOkForStoreStrongs, const DenseMap<BasicBlock *, ColorVector> &BlockColors) { … } //===----------------------------------------------------------------------===// // Top Level Driver //===----------------------------------------------------------------------===// bool ObjCARCContract::init(Module &M) { … } bool ObjCARCContract::run(Function &F, AAResults *A, DominatorTree *D) { … } //===----------------------------------------------------------------------===// // Misc Pass Manager //===----------------------------------------------------------------------===// char ObjCARCContractLegacyPass::ID = …; INITIALIZE_PASS_BEGIN(ObjCARCContractLegacyPass, "objc-arc-contract", "ObjC ARC contraction", false, false) INITIALIZE_PASS_DEPENDENCY(AAResultsWrapperPass) INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass) INITIALIZE_PASS_END(ObjCARCContractLegacyPass, "objc-arc-contract", "ObjC ARC contraction", false, false) void ObjCARCContractLegacyPass::getAnalysisUsage(AnalysisUsage &AU) const { … } Pass *llvm::createObjCARCContractPass() { … } bool ObjCARCContractLegacyPass::runOnFunction(Function &F) { … } PreservedAnalyses ObjCARCContractPass::run(Function &F, FunctionAnalysisManager &AM) { … }