-//===- ObjCARCOpts.cpp - ObjC ARC Optimization ----------------------------===//
+//===- ObjCARCContract.cpp - ObjC ARC Optimization ------------------------===//
//
// The LLVM Compiler Infrastructure
//
/// 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.
///
#define DEBUG_TYPE "objc-arc-contract"
#include "ObjCARC.h"
-#include "ProvenanceAnalysis.h"
+#include "ARCRuntimeEntryPoints.h"
#include "DependencyAnalysis.h"
-
+#include "ProvenanceAnalysis.h"
#include "llvm/ADT/Statistic.h"
-#include "llvm/Analysis/Dominators.h"
+#include "llvm/IR/Dominators.h"
#include "llvm/IR/InlineAsm.h"
#include "llvm/IR/Operator.h"
+#include "llvm/Support/Debug.h"
using namespace llvm;
using namespace llvm::objcarc;
AliasAnalysis *AA;
DominatorTree *DT;
ProvenanceAnalysis PA;
+ ARCRuntimeEntryPoints EP;
/// A flag indicating whether this optimization pass should run.
bool Run;
- /// Declarations for ObjC runtime functions, for use in creating calls to
- /// them. These are initialized lazily to avoid cluttering up the Module
- /// with unused declarations.
-
- /// Declaration for objc_storeStrong().
- Constant *StoreStrongCallee;
- /// Declaration for objc_retainAutorelease().
- Constant *RetainAutoreleaseCallee;
- /// Declaration for objc_retainAutoreleaseReturnValue().
- Constant *RetainAutoreleaseRVCallee;
-
/// The inline asm string to insert between calls and RetainRV calls to make
/// the optimization work on targets which need it.
const MDString *RetainRVMarker;
/// "tail".
SmallPtrSet<CallInst *, 8> StoreStrongCalls;
- Constant *getStoreStrongCallee(Module *M);
- Constant *getRetainAutoreleaseCallee(Module *M);
- Constant *getRetainAutoreleaseRVCallee(Module *M);
+ bool OptimizeRetainCall(Function &F, Instruction *Retain);
bool ContractAutorelease(Function &F, Instruction *Autorelease,
InstructionClass Class,
INITIALIZE_PASS_BEGIN(ObjCARCContract,
"objc-arc-contract", "ObjC ARC contraction", false, false)
INITIALIZE_AG_DEPENDENCY(AliasAnalysis)
-INITIALIZE_PASS_DEPENDENCY(DominatorTree)
+INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)
INITIALIZE_PASS_END(ObjCARCContract,
"objc-arc-contract", "ObjC ARC contraction", false, false)
void ObjCARCContract::getAnalysisUsage(AnalysisUsage &AU) const {
AU.addRequired<AliasAnalysis>();
- AU.addRequired<DominatorTree>();
+ AU.addRequired<DominatorTreeWrapperPass>();
AU.setPreservesCFG();
}
-Constant *ObjCARCContract::getStoreStrongCallee(Module *M) {
- if (!StoreStrongCallee) {
- LLVMContext &C = M->getContext();
- Type *I8X = PointerType::getUnqual(Type::getInt8Ty(C));
- Type *I8XX = PointerType::getUnqual(I8X);
- Type *Params[] = { I8XX, I8X };
-
- AttributeSet Attr = AttributeSet()
- .addAttribute(M->getContext(), AttributeSet::FunctionIndex,
- Attribute::NoUnwind)
- .addAttribute(M->getContext(), 1, Attribute::NoCapture);
-
- StoreStrongCallee =
- M->getOrInsertFunction(
- "objc_storeStrong",
- FunctionType::get(Type::getVoidTy(C), Params, /*isVarArg=*/false),
- Attr);
- }
- return StoreStrongCallee;
-}
+/// 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) {
+ ImmutableCallSite CS(GetObjCArg(Retain));
+ const Instruction *Call = CS.getInstruction();
+ if (!Call)
+ return false;
+ if (Call->getParent() != Retain->getParent())
+ return false;
-Constant *ObjCARCContract::getRetainAutoreleaseCallee(Module *M) {
- if (!RetainAutoreleaseCallee) {
- LLVMContext &C = M->getContext();
- Type *I8X = PointerType::getUnqual(Type::getInt8Ty(C));
- Type *Params[] = { I8X };
- FunctionType *FTy = FunctionType::get(I8X, Params, /*isVarArg=*/false);
- AttributeSet Attribute =
- AttributeSet().addAttribute(M->getContext(), AttributeSet::FunctionIndex,
- Attribute::NoUnwind);
- RetainAutoreleaseCallee =
- M->getOrInsertFunction("objc_retainAutorelease", FTy, Attribute);
- }
- return RetainAutoreleaseCallee;
-}
+ // Check that the call is next to the retain.
+ BasicBlock::const_iterator I = Call;
+ ++I;
+ while (IsNoopInstruction(I)) ++I;
+ if (&*I != Retain)
+ return false;
-Constant *ObjCARCContract::getRetainAutoreleaseRVCallee(Module *M) {
- if (!RetainAutoreleaseRVCallee) {
- LLVMContext &C = M->getContext();
- Type *I8X = PointerType::getUnqual(Type::getInt8Ty(C));
- Type *Params[] = { I8X };
- FunctionType *FTy = FunctionType::get(I8X, Params, /*isVarArg=*/false);
- AttributeSet Attribute =
- AttributeSet().addAttribute(M->getContext(), AttributeSet::FunctionIndex,
- Attribute::NoUnwind);
- RetainAutoreleaseRVCallee =
- M->getOrInsertFunction("objc_retainAutoreleaseReturnValue", FTy,
- Attribute);
- }
- return RetainAutoreleaseRVCallee;
+ // Turn it to an objc_retainAutoreleasedReturnValue.
+ Changed = true;
+ ++NumPeeps;
+
+ 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(ARCRuntimeEntryPoints::EPT_RetainRV);
+ cast<CallInst>(Retain)->setCalledFunction(Decl);
+
+ DEBUG(dbgs() << "New: " << *Retain << "\n");
+ return true;
}
/// Merge an autorelease with a retain into a fused call.
" Old Retain: "
<< *Retain << "\n");
- if (Class == IC_AutoreleaseRV)
- Retain->setCalledFunction(getRetainAutoreleaseRVCallee(F.getParent()));
- else
- Retain->setCalledFunction(getRetainAutoreleaseCallee(F.getParent()));
+ Constant *Decl = EP.get(Class == IC_AutoreleaseRV ?
+ ARCRuntimeEntryPoints::EPT_RetainAutoreleaseRV :
+ ARCRuntimeEntryPoints::EPT_RetainAutorelease);
+ Retain->setCalledFunction(Decl);
DEBUG(dbgs() << " New Retain: "
<< *Retain << "\n");
Args[0] = new BitCastInst(Args[0], I8XX, "", Store);
if (Args[1]->getType() != I8X)
Args[1] = new BitCastInst(Args[1], I8X, "", Store);
- CallInst *StoreStrong =
- CallInst::Create(getStoreStrongCallee(BB->getParent()->getParent()),
- Args, "", Store);
+ Constant *Decl = EP.get(ARCRuntimeEntryPoints::EPT_StoreStrong);
+ CallInst *StoreStrong = CallInst::Create(Decl, Args, "", Store);
StoreStrong->setDoesNotThrow();
StoreStrong->setDebugLoc(Store->getDebugLoc());
if (!Run)
return false;
- // These are initialized lazily.
- StoreStrongCallee = 0;
- RetainAutoreleaseCallee = 0;
- RetainAutoreleaseRVCallee = 0;
+ EP.Initialize(&M);
// Initialize RetainRVMarker.
RetainRVMarker = 0;
Changed = false;
AA = &getAnalysis<AliasAnalysis>();
- DT = &getAnalysis<DominatorTree>();
+ DT = &getAnalysis<DominatorTreeWrapperPass>().getDomTree();
PA.setAA(&getAnalysis<AliasAnalysis>());
// objc_retainBlock does not necessarily return its argument.
InstructionClass Class = GetBasicInstructionClass(Inst);
switch (Class) {
- case IC_Retain:
case IC_FusedRetainAutorelease:
case IC_FusedRetainAutoreleaseRV:
break;
if (ContractAutorelease(F, Inst, Class, DependingInstructions, Visited))
continue;
break;
+ case IC_Retain:
+ // Attempt to convert retains to retainrvs if they are next to function
+ // calls.
+ if (!OptimizeRetainCall(F, Inst))
+ break;
+ // If we succeed in our optimization, fall through.
+ // FALLTHROUGH
case IC_RetainRV: {
// If we're compiling for a target which needs a special inline-asm
// marker to do the retainAutoreleasedReturnValue optimization,
break;
}
--BBI;
- } while (isNoopInstruction(BBI));
+ } while (IsNoopInstruction(BBI));
if (&*BBI == GetObjCArg(Inst)) {
DEBUG(dbgs() << "ObjCARCContract: Adding inline asm marker for "
case IC_InitWeak: {
// objc_initWeak(p, null) => *p = null
CallInst *CI = cast<CallInst>(Inst);
- if (isNullOrUndef(CI->getArgOperand(1))) {
+ if (IsNullOrUndef(CI->getArgOperand(1))) {
Value *Null =
ConstantPointerNull::get(cast<PointerType>(CI->getType()));
Changed = true;
if (isa<AllocaInst>(Inst))
TailOkForStoreStrongs = false;
continue;
+ case IC_IntrinsicUser:
+ // Remove calls to @clang.arc.use(...).
+ Inst->eraseFromParent();
+ continue;
default:
continue;
}
return Changed;
}
-
-/// @}
-///