From 9b37a5592c5ea6ebd7fd5361fec93bd088a1061f Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 17 Jan 2012 20:52:24 +0000 Subject: [PATCH] Add a new ObjC ARC optimization pass to eliminate unneeded autorelease push+pop pairs. llvm-svn: 148330 --- include/llvm/InitializePasses.h | 1 + include/llvm/LinkAllPasses.h | 1 + include/llvm/Transforms/Scalar.h | 6 ++ lib/Transforms/Scalar/ObjCARC.cpp | 118 +++++++++++++++++++++++++++++- lib/Transforms/Scalar/Scalar.cpp | 1 + test/Transforms/ObjCARC/apelim.ll | 51 +++++++++++++ 6 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 test/Transforms/ObjCARC/apelim.ll diff --git a/include/llvm/InitializePasses.h b/include/llvm/InitializePasses.h index d972eb99013..d70e9ebd570 100644 --- a/include/llvm/InitializePasses.h +++ b/include/llvm/InitializePasses.h @@ -168,6 +168,7 @@ void initializeNoAAPass(PassRegistry&); void initializeNoProfileInfoPass(PassRegistry&); void initializeNoPathProfileInfoPass(PassRegistry&); void initializeObjCARCAliasAnalysisPass(PassRegistry&); +void initializeObjCARCAPElimPass(PassRegistry&); void initializeObjCARCExpandPass(PassRegistry&); void initializeObjCARCContractPass(PassRegistry&); void initializeObjCARCOptPass(PassRegistry&); diff --git a/include/llvm/LinkAllPasses.h b/include/llvm/LinkAllPasses.h index 8b5d0d4700a..537fab757b3 100644 --- a/include/llvm/LinkAllPasses.h +++ b/include/llvm/LinkAllPasses.h @@ -97,6 +97,7 @@ namespace { (void) llvm::createNoAAPass(); (void) llvm::createNoProfileInfoPass(); (void) llvm::createObjCARCAliasAnalysisPass(); + (void) llvm::createObjCARCAPElimPass(); (void) llvm::createObjCARCExpandPass(); (void) llvm::createObjCARCContractPass(); (void) llvm::createObjCARCOptPass(); diff --git a/include/llvm/Transforms/Scalar.h b/include/llvm/Transforms/Scalar.h index 2c4f650a06a..7f055d44617 100644 --- a/include/llvm/Transforms/Scalar.h +++ b/include/llvm/Transforms/Scalar.h @@ -325,6 +325,12 @@ Pass *createLowerAtomicPass(); // Pass *createCorrelatedValuePropagationPass(); +//===----------------------------------------------------------------------===// +// +// ObjCARCAPElim - ObjC ARC autorelease pool elimination. +// +Pass *createObjCARCAPElimPass(); + //===----------------------------------------------------------------------===// // // ObjCARCExpand - ObjC ARC preliminary simplifications. diff --git a/lib/Transforms/Scalar/ObjCARC.cpp b/lib/Transforms/Scalar/ObjCARC.cpp index 03b287f1755..87df83857b1 100644 --- a/lib/Transforms/Scalar/ObjCARC.cpp +++ b/lib/Transforms/Scalar/ObjCARC.cpp @@ -375,7 +375,7 @@ static InstructionClass GetBasicInstructionClass(const Value *V) { } // Otherwise, be conservative. - return IC_User; + return isa(V) ? IC_CallOrUser : IC_User; } /// IsRetain - Test if the the given class is objc_retain or @@ -883,6 +883,122 @@ bool ObjCARCExpand::runOnFunction(Function &F) { return Changed; } +//===----------------------------------------------------------------------===// +// ARC autorelease pool elimination. +//===----------------------------------------------------------------------===// + +namespace { + /// ObjCARCAPElim - Autorelease pool elimination. + class ObjCARCAPElim : public ModulePass { + virtual void getAnalysisUsage(AnalysisUsage &AU) const; + virtual bool runOnModule(Module &M); + + bool MayAutorelease(CallSite CS); + 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(); +} + +/// MayAutorelease - Interprocedurally determine if calls made by the +/// given call site can possibly produce autoreleases. +bool ObjCARCAPElim::MayAutorelease(CallSite CS) { + if (Function *Callee = CS.getCalledFunction()) { + if (Callee->isDeclaration() || Callee->mayBeOverridden()) + return true; + for (Function::iterator I = Callee->begin(), E = Callee->end(); + I != E; ++I) { + BasicBlock *BB = I; + for (BasicBlock::iterator J = BB->begin(), F = BB->end(); J != F; ++J) + if (CallSite JCS = CallSite(J)) + if (!JCS.onlyReadsMemory() && MayAutorelease(JCS)) + return true; + } + return false; + } + + return true; +} + +bool ObjCARCAPElim::OptimizeBB(BasicBlock *BB) { + bool Changed = false; + + Instruction *Push = 0; + for (BasicBlock::iterator I = BB->begin(), E = BB->end(); I != E; ) { + Instruction *Inst = I++; + switch (GetBasicInstructionClass(Inst)) { + case IC_AutoreleasepoolPush: + Push = Inst; + break; + case IC_AutoreleasepoolPop: + // If this pop matches a push and nothing in between can autorelease, + // zap the pair. + if (Push && cast(Inst)->getArgOperand(0) == Push) { + Changed = true; + Inst->eraseFromParent(); + Push->eraseFromParent(); + } + Push = 0; + break; + case IC_CallOrUser: + if (MayAutorelease(CallSite(Inst))) + Push = 0; + break; + default: + break; + } + } + + return Changed; +} + +bool ObjCARCAPElim::runOnModule(Module &M) { + if (!EnableARCOpts) + return false; + + // If nothing in the Module uses ARC, don't do anything. + if (!ModuleHasARC(M)) + return false; + + bool Changed = false; + + for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I) { + Function *F = I; + // Only look at function definitions. + if (F->isDeclaration()) + continue; + // Only look at global constructor functions. Unfortunately, + // the name is the most convenient way to recognize them. + if (!F->getName().startswith("_GLOBAL__I_")) + continue; + // Only look at functions with one basic block. + if (llvm::next(F->begin()) != F->end()) + continue; + // Ok, a single-block constructor function definition. Try to optimize it. + Changed |= OptimizeBB(F->begin()); + } + + return Changed; +} + //===----------------------------------------------------------------------===// // ARC optimization. //===----------------------------------------------------------------------===// diff --git a/lib/Transforms/Scalar/Scalar.cpp b/lib/Transforms/Scalar/Scalar.cpp index f6918deafeb..7d65bcc064e 100644 --- a/lib/Transforms/Scalar/Scalar.cpp +++ b/lib/Transforms/Scalar/Scalar.cpp @@ -51,6 +51,7 @@ void llvm::initializeScalarOpts(PassRegistry &Registry) { initializeLowerExpectIntrinsicPass(Registry); initializeMemCpyOptPass(Registry); initializeObjCARCAliasAnalysisPass(Registry); + initializeObjCARCAPElimPass(Registry); initializeObjCARCExpandPass(Registry); initializeObjCARCContractPass(Registry); initializeObjCARCOptPass(Registry); diff --git a/test/Transforms/ObjCARC/apelim.ll b/test/Transforms/ObjCARC/apelim.ll new file mode 100644 index 00000000000..5fefe53551c --- /dev/null +++ b/test/Transforms/ObjCARC/apelim.ll @@ -0,0 +1,51 @@ +; RUN: opt -S -objc-arc-apelim < %s | FileCheck %s +; rdar://10227311 + +@x = global i32 0 + +declare i32 @bar() nounwind + +define i32 @foo() nounwind { +entry: + ret i32 5 +} + +define internal void @__cxx_global_var_init() { +entry: + %call = call i32 @foo() + store i32 %call, i32* @x, align 4 + ret void +} + +define internal void @__dxx_global_var_init() { +entry: + %call = call i32 @bar() + store i32 %call, i32* @x, align 4 + ret void +} + +; CHECK: define internal void @_GLOBAL__I_x() +; CHECK-NOT: @objc +; CHECK: } +define internal void @_GLOBAL__I_x() { +entry: + %0 = call i8* @objc_autoreleasePoolPush() nounwind + call void @__cxx_global_var_init() + call void @objc_autoreleasePoolPop(i8* %0) nounwind + ret void +} + +; CHECK: define internal void @_GLOBAL__I_y() +; CHECK: %0 = call i8* @objc_autoreleasePoolPush() nounwind +; CHECK: call void @objc_autoreleasePoolPop(i8* %0) nounwind +; CHECK: } +define internal void @_GLOBAL__I_y() { +entry: + %0 = call i8* @objc_autoreleasePoolPush() nounwind + call void @__dxx_global_var_init() + call void @objc_autoreleasePoolPop(i8* %0) nounwind + ret void +} + +declare i8* @objc_autoreleasePoolPush() +declare void @objc_autoreleasePoolPop(i8*)