//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "objc-arc"
-#include "llvm/Support/raw_ostream.h"
-#include "llvm/Support/CommandLine.h"
#include "llvm/ADT/DenseMap.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/raw_ostream.h"
using namespace llvm;
// A handy option to enable/disable all optimizations in this file.
// ARC Utilities.
//===----------------------------------------------------------------------===//
-#include "llvm/Intrinsics.h"
-#include "llvm/Module.h"
+#include "llvm/ADT/StringSwitch.h"
#include "llvm/Analysis/ValueTracking.h"
-#include "llvm/Transforms/Utils/Local.h"
+#include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/Module.h"
#include "llvm/Support/CallSite.h"
-#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Transforms/Utils/Local.h"
namespace {
/// InstructionClass - A simple classification for instructions.
// IC_RetainBlock may be given a stack argument.
return Class == IC_Retain ||
Class == IC_RetainRV ||
- Class == IC_Autorelease ||
Class == IC_AutoreleaseRV;
}
+/// \brief Test if the given class represents instructions which are never safe
+/// to mark with the "tail" keyword.
+static bool IsNeverTail(InstructionClass Class) {
+ /// It is never safe to tail call objc_autorelease since by tail calling
+ /// objc_autorelease, we also tail call -[NSObject autorelease] which supports
+ /// fast autoreleasing causing our object to be potentially reclaimed from the
+ /// autorelease pool which violates the semantics of __autoreleasing types in
+ /// ARC.
+ return Class == IC_Autorelease;
+}
+
/// IsNoThrow - Test if the given class represents instructions which are always
/// safe to mark with the nounwind attribute..
static bool IsNoThrow(InstructionClass Class) {
// ARC AliasAnalysis.
//===----------------------------------------------------------------------===//
-#include "llvm/Pass.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/Passes.h"
+#include "llvm/Pass.h"
namespace {
/// ObjCARCAliasAnalysis - This is a simple alias analysis
for (inst_iterator I = inst_begin(&F), E = inst_end(&F); I != E; ++I) {
Instruction *Inst = &*I;
+ DEBUG(dbgs() << "ObjCARCExpand: Visiting: " << *Inst << "\n");
+
switch (GetBasicInstructionClass(Inst)) {
case IC_Retain:
case IC_RetainRV:
case IC_Autorelease:
case IC_AutoreleaseRV:
case IC_FusedRetainAutorelease:
- case IC_FusedRetainAutoreleaseRV:
+ case IC_FusedRetainAutoreleaseRV: {
// These calls return their argument verbatim, as a low-level
// optimization. However, this makes high-level optimizations
// harder. Undo any uses of this optimization that the front-end
// emitted here. We'll redo them in the contract pass.
Changed = true;
- Inst->replaceAllUsesWith(cast<CallInst>(Inst)->getArgOperand(0));
+ Value *Value = cast<CallInst>(Inst)->getArgOperand(0);
+ DEBUG(dbgs() << "ObjCARCExpand: Old = " << *Inst << "\n"
+ " New = " << *Value << "\n");
+ Inst->replaceAllUsesWith(Value);
break;
+ }
default:
break;
}
}
+ DEBUG(dbgs() << "ObjCARCExpand: Finished List.\n\n");
+
return Changed;
}
// ARC autorelease pool elimination.
//===----------------------------------------------------------------------===//
-#include "llvm/Constants.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/IR/Constants.h"
namespace {
/// ObjCARCAPElim - Autorelease pool elimination.
// zap the pair.
if (Push && cast<CallInst>(Inst)->getArgOperand(0) == Push) {
Changed = true;
+ DEBUG(dbgs() << "ObjCARCAPElim::OptimizeBB: Zapping push pop autorelease pair:\n"
+ << " Pop: " << *Inst << "\n"
+ << " Push: " << *Push << "\n");
Inst->eraseFromParent();
Push->eraseFromParent();
}
// TODO: Delete release+retain pairs (rare).
-#include "llvm/LLVMContext.h"
-#include "llvm/Support/CFG.h"
-#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/Support/CFG.h"
STATISTIC(NumNoops, "Number of no-op objc calls eliminated");
STATISTIC(NumPartialNoops, "Number of partially no-op objc calls eliminated");
Type *I8X = PointerType::getUnqual(Type::getInt8Ty(C));
Type *Params[] = { I8X };
FunctionType *FTy = FunctionType::get(I8X, Params, /*isVarArg=*/false);
- AttrBuilder B;
- B.addAttribute(Attributes::NoUnwind);
- AttrListPtr Attributes =
- AttrListPtr().addAttr(M->getContext(), AttrListPtr::FunctionIndex,
- Attributes::get(M->getContext(), B));
+ AttributeSet Attribute =
+ AttributeSet().addAttr(M->getContext(), AttributeSet::FunctionIndex,
+ Attribute::get(C, Attribute::NoUnwind));
RetainRVCallee =
M->getOrInsertFunction("objc_retainAutoreleasedReturnValue", FTy,
- Attributes);
+ Attribute);
}
return RetainRVCallee;
}
Type *I8X = PointerType::getUnqual(Type::getInt8Ty(C));
Type *Params[] = { I8X };
FunctionType *FTy = FunctionType::get(I8X, Params, /*isVarArg=*/false);
- AttrBuilder B;
- B.addAttribute(Attributes::NoUnwind);
- AttrListPtr Attributes =
- AttrListPtr().addAttr(M->getContext(), AttrListPtr::FunctionIndex,
- Attributes::get(C, B));
+ AttributeSet Attribute =
+ AttributeSet().addAttr(M->getContext(), AttributeSet::FunctionIndex,
+ Attribute::get(C, Attribute::NoUnwind));
AutoreleaseRVCallee =
M->getOrInsertFunction("objc_autoreleaseReturnValue", FTy,
- Attributes);
+ Attribute);
}
return AutoreleaseRVCallee;
}
if (!ReleaseCallee) {
LLVMContext &C = M->getContext();
Type *Params[] = { PointerType::getUnqual(Type::getInt8Ty(C)) };
- AttrBuilder B;
- B.addAttribute(Attributes::NoUnwind);
- AttrListPtr Attributes =
- AttrListPtr().addAttr(M->getContext(), AttrListPtr::FunctionIndex,
- Attributes::get(C, B));
+ AttributeSet Attribute =
+ AttributeSet().addAttr(M->getContext(), AttributeSet::FunctionIndex,
+ Attribute::get(C, Attribute::NoUnwind));
ReleaseCallee =
M->getOrInsertFunction(
"objc_release",
FunctionType::get(Type::getVoidTy(C), Params, /*isVarArg=*/false),
- Attributes);
+ Attribute);
}
return ReleaseCallee;
}
if (!RetainCallee) {
LLVMContext &C = M->getContext();
Type *Params[] = { PointerType::getUnqual(Type::getInt8Ty(C)) };
- AttrBuilder B;
- B.addAttribute(Attributes::NoUnwind);
- AttrListPtr Attributes =
- AttrListPtr().addAttr(M->getContext(), AttrListPtr::FunctionIndex,
- Attributes::get(C, B));
+ AttributeSet Attribute =
+ AttributeSet().addAttr(M->getContext(), AttributeSet::FunctionIndex,
+ Attribute::get(C, Attribute::NoUnwind));
RetainCallee =
M->getOrInsertFunction(
"objc_retain",
FunctionType::get(Params[0], Params, /*isVarArg=*/false),
- Attributes);
+ Attribute);
}
return RetainCallee;
}
M->getOrInsertFunction(
"objc_retainBlock",
FunctionType::get(Params[0], Params, /*isVarArg=*/false),
- AttrListPtr());
+ AttributeSet());
}
return RetainBlockCallee;
}
if (!AutoreleaseCallee) {
LLVMContext &C = M->getContext();
Type *Params[] = { PointerType::getUnqual(Type::getInt8Ty(C)) };
- AttrBuilder B;
- B.addAttribute(Attributes::NoUnwind);
- AttrListPtr Attributes =
- AttrListPtr().addAttr(M->getContext(), AttrListPtr::FunctionIndex,
- Attributes::get(C, B));
+ AttributeSet Attribute =
+ AttributeSet().addAttr(M->getContext(), AttributeSet::FunctionIndex,
+ Attribute::get(C, Attribute::NoUnwind));
AutoreleaseCallee =
M->getOrInsertFunction(
"objc_autorelease",
FunctionType::get(Params[0], Params, /*isVarArg=*/false),
- Attributes);
+ Attribute);
}
return AutoreleaseCallee;
}
// Turn it to an objc_retainAutoreleasedReturnValue..
Changed = true;
++NumPeeps;
+
+ DEBUG(dbgs() << "ObjCARCOpt::OptimizeRetainCall: Transforming "
+ "objc_retainAutoreleasedReturnValue => "
+ "objc_retain since the operand is not a return value.\n"
+ " Old: "
+ << *Retain << "\n");
+
cast<CallInst>(Retain)->setCalledFunction(getRetainRVCallee(F.getParent()));
+
+ DEBUG(dbgs() << " New: "
+ << *Retain << "\n");
}
/// OptimizeRetainRVCall - Turn objc_retainAutoreleasedReturnValue into
GetObjCArg(I) == Arg) {
Changed = true;
++NumPeeps;
+
+ DEBUG(dbgs() << "ObjCARCOpt::OptimizeRetainRVCall: Erasing " << *I << "\n"
+ << " Erasing " << *RetainRV
+ << "\n");
+
EraseInstruction(I);
EraseInstruction(RetainRV);
return true;
// Turn it to a plain objc_retain.
Changed = true;
++NumPeeps;
+
+ DEBUG(dbgs() << "ObjCARCOpt::OptimizeRetainRVCall: Transforming "
+ "objc_retainAutoreleasedReturnValue => "
+ "objc_retain since the operand is not a return value.\n"
+ " Old: "
+ << *RetainRV << "\n");
+
cast<CallInst>(RetainRV)->setCalledFunction(getRetainCallee(F.getParent()));
+
+ DEBUG(dbgs() << " New: "
+ << *RetainRV << "\n");
+
return false;
}
Changed = true;
++NumPeeps;
- cast<CallInst>(AutoreleaseRV)->
+
+ DEBUG(dbgs() << "ObjCARCOpt::OptimizeAutoreleaseRVCall: Transforming "
+ "objc_autoreleaseReturnValue => "
+ "objc_autorelease since its operand is not used as a return "
+ "value.\n"
+ " Old: "
+ << *AutoreleaseRV << "\n");
+
+ CallInst *AutoreleaseRVCI = cast<CallInst>(AutoreleaseRV);
+ AutoreleaseRVCI->
setCalledFunction(getAutoreleaseCallee(F.getParent()));
+ AutoreleaseRVCI->setTailCall(false); // Never tail call objc_autorelease.
+
+ DEBUG(dbgs() << " New: "
+ << *AutoreleaseRV << "\n");
+
}
/// OptimizeIndividualCalls - Visit each call, one at a time, and make
// Visit all objc_* calls in F.
for (inst_iterator I = inst_begin(&F), E = inst_end(&F); I != E; ) {
Instruction *Inst = &*I++;
+
+ DEBUG(dbgs() << "ObjCARCOpt::OptimizeIndividualCalls: Visiting: " <<
+ *Inst << "\n");
+
InstructionClass Class = GetBasicInstructionClass(Inst);
switch (Class) {
case IC_NoopCast:
Changed = true;
++NumNoops;
+ DEBUG(dbgs() << "ObjCARCOpt::OptimizeIndividualCalls: Erasing no-op cast:"
+ " " << *Inst << "\n");
EraseInstruction(Inst);
continue;
new StoreInst(UndefValue::get(cast<PointerType>(Ty)->getElementType()),
Constant::getNullValue(Ty),
CI);
- CI->replaceAllUsesWith(UndefValue::get(CI->getType()));
+ llvm::Value *NewValue = UndefValue::get(CI->getType());
+ DEBUG(dbgs() << "ObjCARCOpt::OptimizeIndividualCalls: A null "
+ "pointer-to-weak-pointer is undefined behavior.\n"
+ " Old = " << *CI <<
+ "\n New = " <<
+ *NewValue << "\n");
+ CI->replaceAllUsesWith(NewValue);
CI->eraseFromParent();
continue;
}
new StoreInst(UndefValue::get(cast<PointerType>(Ty)->getElementType()),
Constant::getNullValue(Ty),
CI);
- CI->replaceAllUsesWith(UndefValue::get(CI->getType()));
+
+ llvm::Value *NewValue = UndefValue::get(CI->getType());
+ DEBUG(dbgs() << "ObjCARCOpt::OptimizeIndividualCalls: A null "
+ "pointer-to-weak-pointer is undefined behavior.\n"
+ " Old = " << *CI <<
+ "\n New = " <<
+ *NewValue << "\n");
+
+ CI->replaceAllUsesWith(NewValue);
CI->eraseFromParent();
continue;
}
Call->getArgOperand(0), "", Call);
NewCall->setMetadata(ImpreciseReleaseMDKind,
MDNode::get(C, ArrayRef<Value *>()));
+
+ DEBUG(dbgs() << "ObjCARCOpt::OptimizeIndividualCalls: Replacing "
+ "objc_autorelease(x) with objc_release(x) since x is "
+ "otherwise unused.\n"
+ " Old: " << *Call <<
+ "\n New: " <<
+ *NewCall << "\n");
+
EraseInstruction(Call);
Inst = NewCall;
Class = IC_Release;
// a tail keyword.
if (IsAlwaysTail(Class)) {
Changed = true;
+ DEBUG(dbgs() << "ObjCARCOpt::OptimizeIndividualCalls: Adding tail keyword"
+ " to function since it can never be passed stack args: " << *Inst <<
+ "\n");
cast<CallInst>(Inst)->setTailCall();
}
+ // Ensure that functions that can never have a "tail" keyword due to the
+ // semantics of ARC truly do not do so.
+ if (IsNeverTail(Class)) {
+ Changed = true;
+ DEBUG(dbgs() << "ObjCARCOpt::OptimizeIndividualCalls: Removing tail keyword"
+ " from function: " << *Inst <<
+ "\n");
+ cast<CallInst>(Inst)->setTailCall(false);
+ }
+
// Set nounwind as needed.
if (IsNoThrow(Class)) {
Changed = true;
+ DEBUG(dbgs() << "ObjCARCOpt::OptimizeIndividualCalls: Found no throw"
+ " class. Setting nounwind on: " << *Inst << "\n");
cast<CallInst>(Inst)->setDoesNotThrow();
}
if (isNullOrUndef(Arg)) {
Changed = true;
++NumNoops;
+ DEBUG(dbgs() << "ObjCARCOpt::OptimizeIndividualCalls: ARC calls with "
+ " null are no-ops. Erasing: " << *Inst << "\n");
EraseInstruction(Inst);
continue;
}
Op = new BitCastInst(Op, ParamTy, "", InsertPos);
Clone->setArgOperand(0, Op);
Clone->insertBefore(InsertPos);
+
+ DEBUG(dbgs() << "ObjCARCOpt::OptimizeIndividualCalls: Cloning "
+ << *CInst << "\n"
+ " And inserting "
+ "clone at " << *InsertPos << "\n");
Worklist.push_back(std::make_pair(Clone, Incoming));
}
}
// Erase the original call.
+ DEBUG(dbgs() << "Erasing: " << *CInst << "\n");
EraseInstruction(CInst);
continue;
}
}
} while (!Worklist.empty());
+
+ DEBUG(dbgs() << "ObjCARCOpt::OptimizeIndividualCalls: Finished Queue.\n\n");
+
}
}
MDNode::get(M->getContext(), ArrayRef<Value *>()));
else
Call->setTailCall();
+
+ DEBUG(dbgs() << "ObjCARCOpt::MoveCalls: Inserting new Release: " << *Call
+ << "\n"
+ " At insertion point: " << *InsertPt
+ << "\n");
}
for (SmallPtrSet<Instruction *, 2>::const_iterator
PI = RetainsToMove.ReverseInsertPts.begin(),
Call->setDoesNotThrow();
if (ReleasesToMove.IsTailCallRelease)
Call->setTailCall();
+
+ DEBUG(dbgs() << "ObjCARCOpt::MoveCalls: Inserting new Retain: " << *Call
+ << "\n"
+ " At insertion point: " << *InsertPt
+ << "\n");
}
// Delete the original retain and release calls.
Instruction *OrigRetain = *AI;
Retains.blot(OrigRetain);
DeadInsts.push_back(OrigRetain);
+ DEBUG(dbgs() << "ObjCARCOpt::MoveCalls: Deleting retain: " << *OrigRetain <<
+ "\n");
}
for (SmallPtrSet<Instruction *, 2>::const_iterator
AI = ReleasesToMove.Calls.begin(),
Instruction *OrigRelease = *AI;
Releases.erase(OrigRelease);
DeadInsts.push_back(OrigRelease);
+ DEBUG(dbgs() << "ObjCARCOpt::MoveCalls: Deleting release: " << *OrigRelease
+ << "\n");
}
}
if (!V) continue; // blotted
Instruction *Retain = cast<Instruction>(V);
+
+ DEBUG(dbgs() << "ObjCARCOpt::PerformCodePlacement: Visiting: " << *Retain
+ << "\n");
+
Value *Arg = GetObjCArg(Retain);
// If the object being released is in static or stack storage, we know it's
// queries instead.
for (inst_iterator I = inst_begin(&F), E = inst_end(&F); I != E; ) {
Instruction *Inst = &*I++;
+
+ DEBUG(dbgs() << "ObjCARCOpt::OptimizeWeakCalls: Visiting: " << *Inst <<
+ "\n");
+
InstructionClass Class = GetBasicInstructionClass(Inst);
if (Class != IC_LoadWeak && Class != IC_LoadWeakRetained)
continue;
done:;
}
}
+
+ DEBUG(dbgs() << "ObjCARCOpt::OptimizeWeakCalls: Finished List.\n\n");
+
}
/// OptimizeSequences - Identify program paths which execute sequences of
for (Function::iterator FI = F.begin(), FE = F.end(); FI != FE; ++FI) {
BasicBlock *BB = FI;
ReturnInst *Ret = dyn_cast<ReturnInst>(&BB->back());
+
+ DEBUG(dbgs() << "ObjCARCOpt::OptimizeReturns: Visiting: " << *Ret << "\n");
+
if (!Ret) continue;
const Value *Arg = StripPointerCastsAndObjCCalls(Ret->getOperand(0));
// Convert the autorelease to an autoreleaseRV, since it's
// returning the value.
if (AutoreleaseClass == IC_Autorelease) {
+ DEBUG(dbgs() << "ObjCARCOpt::OptimizeReturns: Converting autorelease "
+ "=> autoreleaseRV since it's returning a value.\n"
+ " In: " << *Autorelease
+ << "\n");
Autorelease->setCalledFunction(getAutoreleaseRVCallee(F.getParent()));
+ DEBUG(dbgs() << " Out: " << *Autorelease
+ << "\n");
+ Autorelease->setTailCall(); // Always tail call autoreleaseRV.
AutoreleaseClass = IC_AutoreleaseRV;
}
// If so, we can zap the retain and autorelease.
Changed = true;
++NumRets;
+ DEBUG(dbgs() << "ObjCARCOpt::OptimizeReturns: Erasing: " << *Retain
+ << "\n Erasing: "
+ << *Autorelease << "\n");
EraseInstruction(Retain);
EraseInstruction(Autorelease);
}
DependingInstructions.clear();
Visited.clear();
}
+
+ DEBUG(dbgs() << "ObjCARCOpt::OptimizeReturns: Finished List.\n\n");
+
}
bool ObjCARCOpt::doInitialization(Module &M) {
// TODO: ObjCARCContract could insert PHI nodes when uses aren't
// dominated by single calls.
-#include "llvm/Operator.h"
-#include "llvm/InlineAsm.h"
#include "llvm/Analysis/Dominators.h"
+#include "llvm/IR/InlineAsm.h"
+#include "llvm/IR/Operator.h"
STATISTIC(NumStoreStrongs, "Number objc_storeStrong calls formed");
Type *I8XX = PointerType::getUnqual(I8X);
Type *Params[] = { I8XX, I8X };
- AttrBuilder BNoUnwind;
- BNoUnwind.addAttribute(Attributes::NoUnwind);
- AttrBuilder BNoCapture;
- BNoCapture.addAttribute(Attributes::NoCapture);
- AttrListPtr Attributes = AttrListPtr()
- .addAttr(M->getContext(), AttrListPtr::FunctionIndex,
- Attributes::get(C, BNoUnwind))
- .addAttr(M->getContext(), 1, Attributes::get(C, BNoCapture));
+ AttributeSet Attribute = AttributeSet()
+ .addAttr(M->getContext(), AttributeSet::FunctionIndex,
+ Attribute::get(C, Attribute::NoUnwind))
+ .addAttr(M->getContext(), 1, Attribute::get(C, Attribute::NoCapture));
StoreStrongCallee =
M->getOrInsertFunction(
"objc_storeStrong",
FunctionType::get(Type::getVoidTy(C), Params, /*isVarArg=*/false),
- Attributes);
+ Attribute);
}
return StoreStrongCallee;
}
Type *I8X = PointerType::getUnqual(Type::getInt8Ty(C));
Type *Params[] = { I8X };
FunctionType *FTy = FunctionType::get(I8X, Params, /*isVarArg=*/false);
- AttrBuilder B;
- B.addAttribute(Attributes::NoUnwind);
- AttrListPtr Attributes =
- AttrListPtr().addAttr(M->getContext(), AttrListPtr::FunctionIndex,
- Attributes::get(C, B));
+ AttributeSet Attribute =
+ AttributeSet().addAttr(M->getContext(), AttributeSet::FunctionIndex,
+ Attribute::get(C, Attribute::NoUnwind));
RetainAutoreleaseCallee =
- M->getOrInsertFunction("objc_retainAutorelease", FTy, Attributes);
+ M->getOrInsertFunction("objc_retainAutorelease", FTy, Attribute);
}
return RetainAutoreleaseCallee;
}
Type *I8X = PointerType::getUnqual(Type::getInt8Ty(C));
Type *Params[] = { I8X };
FunctionType *FTy = FunctionType::get(I8X, Params, /*isVarArg=*/false);
- AttrBuilder B;
- B.addAttribute(Attributes::NoUnwind);
- AttrListPtr Attributes =
- AttrListPtr().addAttr(M->getContext(), AttrListPtr::FunctionIndex,
- Attributes::get(C, B));
+ AttributeSet Attribute =
+ AttributeSet().addAttr(M->getContext(), AttributeSet::FunctionIndex,
+ Attribute::get(C, Attribute::NoUnwind));
RetainAutoreleaseRVCallee =
M->getOrInsertFunction("objc_retainAutoreleaseReturnValue", FTy,
- Attributes);
+ Attribute);
}
return RetainAutoreleaseRVCallee;
}
Changed = true;
++NumPeeps;
+ DEBUG(dbgs() << "ObjCARCContract::ContractAutorelease: Fusing "
+ "retain/autorelease. Erasing: " << *Autorelease << "\n"
+ " Old Retain: "
+ << *Retain << "\n");
+
if (Class == IC_AutoreleaseRV)
Retain->setCalledFunction(getRetainAutoreleaseRVCallee(F.getParent()));
else
Retain->setCalledFunction(getRetainAutoreleaseCallee(F.getParent()));
+ DEBUG(dbgs() << " New Retain: "
+ << *Retain << "\n");
+
EraseInstruction(Autorelease);
return true;
}
for (inst_iterator I = inst_begin(&F), E = inst_end(&F); I != E; ) {
Instruction *Inst = &*I++;
+ DEBUG(dbgs() << "ObjCARCContract: Visiting: " << *Inst << "\n");
+
// Only these library routines return their argument. In particular,
// objc_retainBlock does not necessarily return its argument.
InstructionClass Class = GetBasicInstructionClass(Inst);
} while (isNoopInstruction(BBI));
if (&*BBI == GetObjCArg(Inst)) {
+ DEBUG(dbgs() << "ObjCARCContract: Adding inline asm marker for "
+ "retainAutoreleasedReturnValue optimization.\n");
Changed = true;
InlineAsm *IA =
InlineAsm::get(FunctionType::get(Type::getVoidTy(Inst->getContext()),
ConstantPointerNull::get(cast<PointerType>(CI->getType()));
Changed = true;
new StoreInst(Null, CI->getArgOperand(0), CI);
+
+ DEBUG(dbgs() << "OBJCARCContract: Old = " << *CI << "\n"
+ << " New = " << *Null << "\n");
+
CI->replaceAllUsesWith(Null);
CI->eraseFromParent();
}
continue;
}
+ DEBUG(dbgs() << "ObjCARCContract: Finished List.\n\n");
+
// Don't use GetObjCArg because we don't want to look through bitcasts
// and such; to do the replacement, the argument must have type i8*.
const Value *Arg = cast<CallInst>(Inst)->getArgOperand(0);