#include "ObjCARC.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/IR/Constants.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
using namespace llvm::objcarc;
#define DEBUG_TYPE "objc-arc-ap-elim"
namespace {
class ObjCARCAPElim : public ModulePass {
void getAnalysisUsage(AnalysisUsage &AU) const override;
bool runOnModule(Module &M) override;
static bool MayAutorelease(ImmutableCallSite CS, unsigned Depth = 0);
static bool OptimizeBB(BasicBlock *BB);
public:
static char ID;
ObjCARCAPElim() : ModulePass(ID) {
initializeObjCARCAPElimPass(*PassRegistry::getPassRegistry());
}
};
}
char ObjCARCAPElim::ID = 0;
INITIALIZE_PASS(ObjCARCAPElim,
"objc-arc-apelim",
"ObjC ARC autorelease pool elimination",
false, false)
Pass *llvm::createObjCARCAPElimPass() {
return new ObjCARCAPElim();
}
void ObjCARCAPElim::getAnalysisUsage(AnalysisUsage &AU) const {
AU.setPreservesCFG();
}
bool ObjCARCAPElim::MayAutorelease(ImmutableCallSite CS, unsigned Depth) {
if (const Function *Callee = CS.getCalledFunction()) {
if (Callee->isDeclaration() || Callee->mayBeOverridden())
return true;
for (const BasicBlock &BB : *Callee) {
for (const Instruction &I : BB)
if (ImmutableCallSite JCS = ImmutableCallSite(&I))
if (Depth < 3 &&
!JCS.onlyReadsMemory() &&
MayAutorelease(JCS, Depth + 1))
return true;
}
return false;
}
return true;
}
bool ObjCARCAPElim::OptimizeBB(BasicBlock *BB) {
bool Changed = false;
Instruction *Push = nullptr;
for (BasicBlock::iterator I = BB->begin(), E = BB->end(); I != E; ) {
Instruction *Inst = &*I++;
switch (GetBasicARCInstKind(Inst)) {
case ARCInstKind::AutoreleasepoolPush:
Push = Inst;
break;
case ARCInstKind::AutoreleasepoolPop:
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();
}
Push = nullptr;
break;
case ARCInstKind::CallOrUser:
if (MayAutorelease(ImmutableCallSite(Inst)))
Push = nullptr;
break;
default:
break;
}
}
return Changed;
}
bool ObjCARCAPElim::runOnModule(Module &M) {
if (!EnableARCOpts)
return false;
if (!ModuleHasARC(M))
return false;
GlobalVariable *GV = M.getGlobalVariable("llvm.global_ctors");
if (!GV)
return false;
assert(GV->hasDefinitiveInitializer() &&
"llvm.global_ctors is uncooperative!");
bool Changed = false;
ConstantArray *Init = cast<ConstantArray>(GV->getInitializer());
for (User::op_iterator OI = Init->op_begin(), OE = Init->op_end();
OI != OE; ++OI) {
Value *Op = *OI;
Function *F = dyn_cast<Function>(cast<ConstantStruct>(Op)->getOperand(1));
if (!F)
continue;
if (F->isDeclaration())
continue;
if (std::next(F->begin()) != F->end())
continue;
Changed |= OptimizeBB(&F->front());
}
return Changed;
}