aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp')
-rw-r--r--llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp73
1 files changed, 58 insertions, 15 deletions
diff --git a/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp b/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp
index b80c1675050b..cb1fa804fa11 100644
--- a/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp
+++ b/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp
@@ -43,7 +43,6 @@
#include "llvm/Analysis/ObjCARCInstKind.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/CFG.h"
-#include "llvm/IR/CallSite.h"
#include "llvm/IR/Constant.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DerivedTypes.h"
@@ -610,8 +609,7 @@ bool
ObjCARCOpt::OptimizeRetainRVCall(Function &F, Instruction *RetainRV) {
// Check for the argument being from an immediately preceding call or invoke.
const Value *Arg = GetArgRCIdentityRoot(RetainRV);
- ImmutableCallSite CS(Arg);
- if (const Instruction *Call = CS.getInstruction()) {
+ if (const Instruction *Call = dyn_cast<CallBase>(Arg)) {
if (Call->getParent() == RetainRV->getParent()) {
BasicBlock::const_iterator I(Call);
++I;
@@ -678,6 +676,7 @@ bool ObjCARCOpt::OptimizeInlinedAutoreleaseRVCall(
// Delete the RV pair, starting with the AutoreleaseRV.
AutoreleaseRV->replaceAllUsesWith(
cast<CallInst>(AutoreleaseRV)->getArgOperand(0));
+ Changed = true;
EraseInstruction(AutoreleaseRV);
if (Class == ARCInstKind::RetainRV) {
// AutoreleaseRV and RetainRV cancel out. Delete the RetainRV.
@@ -877,23 +876,49 @@ void ObjCARCOpt::OptimizeIndividualCalls(Function &F) {
optimizeDelayedAutoreleaseRV();
}
+/// This function returns true if the value is inert. An ObjC ARC runtime call
+/// taking an inert operand can be safely deleted.
+static bool isInertARCValue(Value *V, SmallPtrSet<Value *, 1> &VisitedPhis) {
+ V = V->stripPointerCasts();
+
+ if (IsNullOrUndef(V))
+ return true;
+
+ // See if this is a global attribute annotated with an 'objc_arc_inert'.
+ if (auto *GV = dyn_cast<GlobalVariable>(V))
+ if (GV->hasAttribute("objc_arc_inert"))
+ return true;
+
+ if (auto PN = dyn_cast<PHINode>(V)) {
+ // Ignore this phi if it has already been discovered.
+ if (!VisitedPhis.insert(PN).second)
+ return true;
+ // Look through phis's operands.
+ for (Value *Opnd : PN->incoming_values())
+ if (!isInertARCValue(Opnd, VisitedPhis))
+ return false;
+ return true;
+ }
+
+ return false;
+}
+
void ObjCARCOpt::OptimizeIndividualCallImpl(
Function &F, DenseMap<BasicBlock *, ColorVector> &BlockColors,
Instruction *Inst, ARCInstKind Class, const Value *Arg) {
LLVM_DEBUG(dbgs() << "Visiting: Class: " << Class << "; " << *Inst << "\n");
- // Some of the ARC calls can be deleted if their arguments are global
- // variables that are inert in ARC.
- if (IsNoopOnGlobal(Class)) {
- Value *Opnd = Inst->getOperand(0);
- if (auto *GV = dyn_cast<GlobalVariable>(Opnd->stripPointerCasts()))
- if (GV->hasAttribute("objc_arc_inert")) {
- if (!Inst->getType()->isVoidTy())
- Inst->replaceAllUsesWith(Opnd);
- Inst->eraseFromParent();
- return;
- }
- }
+ // We can delete this call if it takes an inert value.
+ SmallPtrSet<Value *, 1> VisitedPhis;
+
+ if (IsNoopOnGlobal(Class))
+ if (isInertARCValue(Inst->getOperand(0), VisitedPhis)) {
+ if (!Inst->getType()->isVoidTy())
+ Inst->replaceAllUsesWith(Inst->getOperand(0));
+ Inst->eraseFromParent();
+ Changed = true;
+ return;
+ }
switch (Class) {
default:
@@ -1544,6 +1569,15 @@ ObjCARCOpt::VisitTopDown(BasicBlock *BB,
}
}
+ // Check that BB and MyStates have the same number of predecessors. This
+ // prevents retain calls that live outside a loop from being moved into the
+ // loop.
+ if (!BB->hasNPredecessors(MyStates.pred_end() - MyStates.pred_begin()))
+ for (auto I = MyStates.top_down_ptr_begin(),
+ E = MyStates.top_down_ptr_end();
+ I != E; ++I)
+ I->second.SetCFGHazardAfflicted(true);
+
LLVM_DEBUG(dbgs() << "Before:\n"
<< BBStates[BB] << "\n"
<< "Performing Dataflow:\n");
@@ -2020,6 +2054,7 @@ void ObjCARCOpt::OptimizeWeakCalls(Function &F) {
// Delete objc_loadWeak calls with no users.
if (Class == ARCInstKind::LoadWeak && Inst->use_empty()) {
Inst->eraseFromParent();
+ Changed = true;
continue;
}
@@ -2310,6 +2345,14 @@ void ObjCARCOpt::OptimizeReturns(Function &F) {
bool HasSafePathToCall = HasSafePathToPredecessorCall(Arg, Retain,
DependingInstructions,
Visited, PA);
+
+ // Don't remove retainRV/autoreleaseRV pairs if the call isn't a tail call.
+ if (HasSafePathToCall &&
+ GetBasicARCInstKind(Retain) == ARCInstKind::RetainRV &&
+ GetBasicARCInstKind(Autorelease) == ARCInstKind::AutoreleaseRV &&
+ !cast<CallInst>(*DependingInstructions.begin())->isTailCall())
+ continue;
+
DependingInstructions.clear();
Visited.clear();