diff options
Diffstat (limited to 'lib/Transforms/ObjCARC/ObjCARCContract.cpp')
-rw-r--r-- | lib/Transforms/ObjCARC/ObjCARCContract.cpp | 177 |
1 files changed, 117 insertions, 60 deletions
diff --git a/lib/Transforms/ObjCARC/ObjCARCContract.cpp b/lib/Transforms/ObjCARC/ObjCARCContract.cpp index c4e61218f3f3..1f1ea9f58739 100644 --- a/lib/Transforms/ObjCARC/ObjCARCContract.cpp +++ b/lib/Transforms/ObjCARC/ObjCARCContract.cpp @@ -31,6 +31,7 @@ #include "ObjCARC.h" #include "ProvenanceAnalysis.h" #include "llvm/ADT/Statistic.h" +#include "llvm/Analysis/EHPersonalities.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/InlineAsm.h" #include "llvm/IR/Operator.h" @@ -50,7 +51,7 @@ STATISTIC(NumStoreStrongs, "Number objc_storeStrong calls formed"); //===----------------------------------------------------------------------===// namespace { - /// \brief Late ARC optimizations + /// Late ARC optimizations /// /// These change the IR in a way that makes it difficult to be analyzed by /// ObjCARCOpt, so it's run late. @@ -74,11 +75,12 @@ namespace { SmallPtrSet<CallInst *, 8> StoreStrongCalls; /// Returns true if we eliminated Inst. - bool tryToPeepholeInstruction(Function &F, Instruction *Inst, - inst_iterator &Iter, - SmallPtrSetImpl<Instruction *> &DepInsts, - SmallPtrSetImpl<const BasicBlock *> &Visited, - bool &TailOkForStoreStrong); + bool tryToPeepholeInstruction( + Function &F, Instruction *Inst, inst_iterator &Iter, + SmallPtrSetImpl<Instruction *> &DepInsts, + SmallPtrSetImpl<const BasicBlock *> &Visited, + bool &TailOkForStoreStrong, + const DenseMap<BasicBlock *, ColorVector> &BlockColors); bool optimizeRetainCall(Function &F, Instruction *Retain); @@ -88,8 +90,9 @@ namespace { SmallPtrSetImpl<Instruction *> &DependingInstructions, SmallPtrSetImpl<const BasicBlock *> &Visited); - void tryToContractReleaseIntoStoreStrong(Instruction *Release, - inst_iterator &Iter); + void tryToContractReleaseIntoStoreStrong( + Instruction *Release, inst_iterator &Iter, + const DenseMap<BasicBlock *, ColorVector> &BlockColors); void getAnalysisUsage(AnalysisUsage &AU) const override; bool doInitialization(Module &M) override; @@ -129,16 +132,18 @@ bool ObjCARCContract::optimizeRetainCall(Function &F, Instruction *Retain) { Changed = true; ++NumPeeps; - DEBUG(dbgs() << "Transforming objc_retain => " - "objc_retainAutoreleasedReturnValue since the operand is a " - "return value.\nOld: "<< *Retain << "\n"); + LLVM_DEBUG( + dbgs() << "Transforming objc_retain => " + "objc_retainAutoreleasedReturnValue since the operand is a " + "return value.\nOld: " + << *Retain << "\n"); // We do not have to worry about tail calls/does not throw since // retain/retainRV have the same properties. Constant *Decl = EP.get(ARCRuntimeEntryPointKind::RetainRV); cast<CallInst>(Retain)->setCalledFunction(Decl); - DEBUG(dbgs() << "New: " << *Retain << "\n"); + LLVM_DEBUG(dbgs() << "New: " << *Retain << "\n"); return true; } @@ -177,16 +182,19 @@ bool ObjCARCContract::contractAutorelease( Changed = true; ++NumPeeps; - DEBUG(dbgs() << " Fusing retain/autorelease!\n" - " Autorelease:" << *Autorelease << "\n" - " Retain: " << *Retain << "\n"); + LLVM_DEBUG(dbgs() << " Fusing retain/autorelease!\n" + " Autorelease:" + << *Autorelease + << "\n" + " Retain: " + << *Retain << "\n"); Constant *Decl = EP.get(Class == ARCInstKind::AutoreleaseRV ? ARCRuntimeEntryPointKind::RetainAutoreleaseRV : ARCRuntimeEntryPointKind::RetainAutorelease); Retain->setCalledFunction(Decl); - DEBUG(dbgs() << " New RetainAutorelease: " << *Retain << "\n"); + LLVM_DEBUG(dbgs() << " New RetainAutorelease: " << *Retain << "\n"); EraseInstruction(Autorelease); return true; @@ -303,6 +311,24 @@ findRetainForStoreStrongContraction(Value *New, StoreInst *Store, return Retain; } +/// Create a call instruction with the correct funclet token. Should be used +/// instead of calling CallInst::Create directly. +static CallInst * +createCallInst(Value *Func, ArrayRef<Value *> Args, const Twine &NameStr, + Instruction *InsertBefore, + const DenseMap<BasicBlock *, ColorVector> &BlockColors) { + SmallVector<OperandBundleDef, 1> OpBundles; + if (!BlockColors.empty()) { + const ColorVector &CV = BlockColors.find(InsertBefore->getParent())->second; + assert(CV.size() == 1 && "non-unique color for block!"); + Instruction *EHPad = CV.front()->getFirstNonPHI(); + if (EHPad->isEHPad()) + OpBundles.emplace_back("funclet", EHPad); + } + + return CallInst::Create(Func, Args, OpBundles, NameStr, InsertBefore); +} + /// Attempt to merge an objc_release with a store, load, and objc_retain to form /// an objc_storeStrong. An objc_storeStrong: /// @@ -330,8 +356,9 @@ findRetainForStoreStrongContraction(Value *New, StoreInst *Store, /// (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) { +void ObjCARCContract::tryToContractReleaseIntoStoreStrong( + Instruction *Release, inst_iterator &Iter, + const DenseMap<BasicBlock *, ColorVector> &BlockColors) { // See if we are releasing something that we just loaded. auto *Load = dyn_cast<LoadInst>(GetArgRCIdentityRoot(Release)); if (!Load || !Load->isSimple()) @@ -365,7 +392,7 @@ void ObjCARCContract::tryToContractReleaseIntoStoreStrong(Instruction *Release, Changed = true; ++NumStoreStrongs; - DEBUG( + LLVM_DEBUG( llvm::dbgs() << " Contracting retain, release into objc_storeStrong.\n" << " Old:\n" << " Store: " << *Store << "\n" @@ -383,7 +410,7 @@ void ObjCARCContract::tryToContractReleaseIntoStoreStrong(Instruction *Release, if (Args[1]->getType() != I8X) Args[1] = new BitCastInst(Args[1], I8X, "", Store); Constant *Decl = EP.get(ARCRuntimeEntryPointKind::StoreStrong); - CallInst *StoreStrong = CallInst::Create(Decl, Args, "", Store); + CallInst *StoreStrong = createCallInst(Decl, Args, "", Store, BlockColors); StoreStrong->setDoesNotThrow(); StoreStrong->setDebugLoc(Store->getDebugLoc()); @@ -392,7 +419,8 @@ void ObjCARCContract::tryToContractReleaseIntoStoreStrong(Instruction *Release, // we can set the tail flag once we know it's safe. StoreStrongCalls.insert(StoreStrong); - DEBUG(llvm::dbgs() << " New Store Strong: " << *StoreStrong << "\n"); + LLVM_DEBUG(llvm::dbgs() << " New Store Strong: " << *StoreStrong + << "\n"); if (&*Iter == Retain) ++Iter; if (&*Iter == Store) ++Iter; @@ -407,7 +435,8 @@ bool ObjCARCContract::tryToPeepholeInstruction( Function &F, Instruction *Inst, inst_iterator &Iter, SmallPtrSetImpl<Instruction *> &DependingInsts, SmallPtrSetImpl<const BasicBlock *> &Visited, - bool &TailOkForStoreStrongs) { + bool &TailOkForStoreStrongs, + const DenseMap<BasicBlock *, ColorVector> &BlockColors) { // Only these library routines return their argument. In particular, // objc_retainBlock does not necessarily return its argument. ARCInstKind Class = GetBasicARCInstKind(Inst); @@ -449,15 +478,16 @@ bool ObjCARCContract::tryToPeepholeInstruction( } while (IsNoopInstruction(&*BBI)); if (&*BBI == GetArgRCIdentityRoot(Inst)) { - DEBUG(dbgs() << "Adding inline asm marker for the return value " - "optimization.\n"); + LLVM_DEBUG(dbgs() << "Adding inline asm marker for the return value " + "optimization.\n"); Changed = true; InlineAsm *IA = InlineAsm::get( FunctionType::get(Type::getVoidTy(Inst->getContext()), /*isVarArg=*/false), RVInstMarker->getString(), /*Constraints=*/"", /*hasSideEffects=*/true); - CallInst::Create(IA, "", Inst); + + createCallInst(IA, None, "", Inst, BlockColors); } decline_rv_optimization: return false; @@ -471,8 +501,8 @@ bool ObjCARCContract::tryToPeepholeInstruction( Changed = true; new StoreInst(Null, CI->getArgOperand(0), CI); - DEBUG(dbgs() << "OBJCARCContract: Old = " << *CI << "\n" - << " New = " << *Null << "\n"); + LLVM_DEBUG(dbgs() << "OBJCARCContract: Old = " << *CI << "\n" + << " New = " << *Null << "\n"); CI->replaceAllUsesWith(Null); CI->eraseFromParent(); @@ -482,7 +512,7 @@ bool ObjCARCContract::tryToPeepholeInstruction( case ARCInstKind::Release: // Try to form an objc store strong from our release. If we fail, there is // nothing further to do below, so continue. - tryToContractReleaseIntoStoreStrong(Inst, Iter); + tryToContractReleaseIntoStoreStrong(Inst, Iter, BlockColors); return true; case ARCInstKind::User: // Be conservative if the function has any alloca instructions. @@ -518,7 +548,12 @@ bool ObjCARCContract::runOnFunction(Function &F) { PA.setAA(&getAnalysis<AAResultsWrapperPass>().getAAResults()); - DEBUG(llvm::dbgs() << "**** ObjCARC Contract ****\n"); + DenseMap<BasicBlock *, ColorVector> BlockColors; + if (F.hasPersonalityFn() && + isScopedEHPersonality(classifyEHPersonality(F.getPersonalityFn()))) + BlockColors = colorEHFunclets(F); + + LLVM_DEBUG(llvm::dbgs() << "**** ObjCARC Contract ****\n"); // Track whether it's ok to mark objc_storeStrong calls with the "tail" // keyword. Be conservative if the function has variadic arguments. @@ -536,12 +571,12 @@ bool ObjCARCContract::runOnFunction(Function &F) { for (inst_iterator I = inst_begin(&F), E = inst_end(&F); I != E;) { Instruction *Inst = &*I++; - DEBUG(dbgs() << "Visiting: " << *Inst << "\n"); + LLVM_DEBUG(dbgs() << "Visiting: " << *Inst << "\n"); // First try to peephole Inst. If there is nothing further we can do in // terms of undoing objc-arc-expand, process the next inst. if (tryToPeepholeInstruction(F, Inst, I, DependingInstructions, Visited, - TailOkForStoreStrongs)) + TailOkForStoreStrongs, BlockColors)) continue; // Otherwise, try to undo objc-arc-expand. @@ -568,35 +603,48 @@ bool ObjCARCContract::runOnFunction(Function &F) { // trivially dominate itself, which would lead us to rewriting its // argument in terms of its return value, which would lead to // infinite loops in GetArgRCIdentityRoot. - if (DT->isReachableFromEntry(U) && DT->dominates(Inst, U)) { - Changed = true; - Instruction *Replacement = Inst; - Type *UseTy = U.get()->getType(); - if (PHINode *PHI = dyn_cast<PHINode>(U.getUser())) { - // For PHI nodes, insert the bitcast in the predecessor block. - unsigned ValNo = PHINode::getIncomingValueNumForOperand(OperandNo); - BasicBlock *BB = PHI->getIncomingBlock(ValNo); - if (Replacement->getType() != UseTy) - Replacement = new BitCastInst(Replacement, UseTy, "", - &BB->back()); - // While we're here, rewrite all edges for this PHI, rather - // than just one use at a time, to minimize the number of - // bitcasts we emit. - for (unsigned i = 0, e = PHI->getNumIncomingValues(); i != e; ++i) - if (PHI->getIncomingBlock(i) == BB) { - // Keep the UI iterator valid. - if (UI != UE && - &PHI->getOperandUse( - PHINode::getOperandNumForIncomingValue(i)) == &*UI) - ++UI; - PHI->setIncomingValue(i, Replacement); - } - } else { - if (Replacement->getType() != UseTy) - Replacement = new BitCastInst(Replacement, UseTy, "", - cast<Instruction>(U.getUser())); - U.set(Replacement); + if (!DT->isReachableFromEntry(U) || !DT->dominates(Inst, U)) + continue; + + Changed = true; + Instruction *Replacement = Inst; + Type *UseTy = U.get()->getType(); + if (PHINode *PHI = dyn_cast<PHINode>(U.getUser())) { + // For PHI nodes, insert the bitcast in the predecessor block. + unsigned ValNo = PHINode::getIncomingValueNumForOperand(OperandNo); + BasicBlock *IncomingBB = PHI->getIncomingBlock(ValNo); + if (Replacement->getType() != UseTy) { + // A catchswitch is both a pad and a terminator, meaning a basic + // block with a catchswitch has no insertion point. Keep going up + // the dominator tree until we find a non-catchswitch. + BasicBlock *InsertBB = IncomingBB; + while (isa<CatchSwitchInst>(InsertBB->getFirstNonPHI())) { + InsertBB = DT->getNode(InsertBB)->getIDom()->getBlock(); + } + + assert(DT->dominates(Inst, &InsertBB->back()) && + "Invalid insertion point for bitcast"); + Replacement = + new BitCastInst(Replacement, UseTy, "", &InsertBB->back()); } + + // While we're here, rewrite all edges for this PHI, rather + // than just one use at a time, to minimize the number of + // bitcasts we emit. + for (unsigned i = 0, e = PHI->getNumIncomingValues(); i != e; ++i) + if (PHI->getIncomingBlock(i) == IncomingBB) { + // Keep the UI iterator valid. + if (UI != UE && + &PHI->getOperandUse( + PHINode::getOperandNumForIncomingValue(i)) == &*UI) + ++UI; + PHI->setIncomingValue(i, Replacement); + } + } else { + if (Replacement->getType() != UseTy) + Replacement = new BitCastInst(Replacement, UseTy, "", + cast<Instruction>(U.getUser())); + U.set(Replacement); } } }; @@ -618,8 +666,17 @@ bool ObjCARCContract::runOnFunction(Function &F) { else if (isa<GlobalAlias>(Arg) && !cast<GlobalAlias>(Arg)->isInterposable()) Arg = cast<GlobalAlias>(Arg)->getAliasee(); - else + else { + // If Arg is a PHI node, get PHIs that are equivalent to it and replace + // their uses. + if (PHINode *PN = dyn_cast<PHINode>(Arg)) { + SmallVector<Value *, 1> PHIList; + getEquivalentPHIs(*PN, PHIList); + for (Value *PHI : PHIList) + ReplaceArgUses(PHI); + } break; + } } // Replace bitcast users of Arg that are dominated by Inst. |