Add an optimization to GlobalOpt that eliminates calls to __cxa_atexit, if the function passed is empty.

llvm-svn: 127970
This commit is contained in:
Anders Carlsson 2011-03-20 17:59:11 +00:00
parent dce83c522c
commit ee6bc70d2f
2 changed files with 132 additions and 0 deletions

View File

@ -54,6 +54,7 @@ STATISTIC(NumCtorsEvaluated, "Number of static ctors evaluated");
STATISTIC(NumNestRemoved , "Number of nest attributes removed");
STATISTIC(NumAliasesResolved, "Number of global aliases resolved");
STATISTIC(NumAliasesRemoved, "Number of global aliases eliminated");
STATISTIC(NumCXXDtorsRemoved, "Number of global C++ destructors removed");
namespace {
struct GlobalStatus;
@ -77,6 +78,7 @@ namespace {
bool ProcessInternalGlobal(GlobalVariable *GV,Module::global_iterator &GVI,
const SmallPtrSet<const PHINode*, 16> &PHIUsers,
const GlobalStatus &GS);
bool OptimizeEmptyGlobalCXXDtors(Function *CXAAtExitFn);
};
}
@ -2696,12 +2698,106 @@ bool GlobalOpt::OptimizeGlobalAliases(Module &M) {
return Changed;
}
static Function *FindCXAAtExit(Module &M) {
Function *Fn = M.getFunction("__cxa_atexit");
if (!Fn)
return 0;
const FunctionType *FTy = Fn->getFunctionType();
// Checking that the function has the right number of parameters and that they
// all have pointer types should be enough.
if (FTy->getNumParams() != 3 ||
!FTy->getParamType(0)->isPointerTy() ||
!FTy->getParamType(1)->isPointerTy() ||
!FTy->getParamType(2)->isPointerTy())
return 0;
return Fn;
}
/// cxxDtorIsEmpty - Returns whether the given function is an empty C++
/// destructor and can therefore be eliminated.
/// Note that we assume that other optimization passes have already simplified
/// the code so we only look for a function with a single basic block, where
/// the only allowed instructions are 'ret' or 'call' to empty C++ dtor.
static bool cxxDtorIsEmpty(const Function& Fn) {
if (Fn.empty())
return true;
if (++Fn.begin() != Fn.end())
return false;
const BasicBlock &EntryBlock = Fn.getEntryBlock();
for (BasicBlock::const_iterator I = EntryBlock.begin(), E = EntryBlock.end();
I != E; ++I) {
if (const CallInst *CI = dyn_cast<CallInst>(I)) {
const Function *CalledFn = CI->getCalledFunction();
if (!CalledFn)
return false;
if (!cxxDtorIsEmpty(*CalledFn))
return false;
} else if (isa<ReturnInst>(*I))
return true;
else
return false;
}
return false;
}
bool GlobalOpt::OptimizeEmptyGlobalCXXDtors(Function *CXAAtExitFn) {
/// Itanium C++ ABI p3.3.5:
///
/// After constructing a global (or local static) object, that will require
/// destruction on exit, a termination function is registered as follows:
///
/// extern "C" int __cxa_atexit ( void (*f)(void *), void *p, void *d );
///
/// This registration, e.g. __cxa_atexit(f,p,d), is intended to cause the
/// call f(p) when DSO d is unloaded, before all such termination calls
/// registered before this one. It returns zero if registration is
/// successful, nonzero on failure.
// This pass will look for calls to __cxa_atexit where the function is trivial
// and remove them.
bool Changed = false;
for (Function::use_iterator I = CXAAtExitFn->use_begin(),
E = CXAAtExitFn->use_end(); I != E;) {
CallSite CS(*I++);
if (!CS.getInstruction())
continue;
Function *DtorFn =
dyn_cast<Function>(CS.getArgument(0)->stripPointerCasts());
if (!DtorFn)
continue;
if (!cxxDtorIsEmpty(*DtorFn))
continue;
// Just remove the call.
CS.getInstruction()->eraseFromParent();
++NumCXXDtorsRemoved;
Changed |= true;
}
return Changed;
}
bool GlobalOpt::runOnModule(Module &M) {
bool Changed = false;
// Try to find the llvm.globalctors list.
GlobalVariable *GlobalCtors = FindGlobalCtors(M);
Function *CXAAtExitFn = FindCXAAtExit(M);
bool LocalChange = true;
while (LocalChange) {
LocalChange = false;
@ -2718,6 +2814,11 @@ bool GlobalOpt::runOnModule(Module &M) {
// Resolve aliases, when possible.
LocalChange |= OptimizeGlobalAliases(M);
// Try to remove trivial global destructors.
if (CXAAtExitFn)
LocalChange |= OptimizeEmptyGlobalCXXDtors(CXAAtExitFn);
Changed |= LocalChange;
}

View File

@ -0,0 +1,31 @@
; RUN: opt < %s -globalopt -S | FileCheck %s
%0 = type { i32, void ()* }
%struct.A = type { i8 }
@a = global %struct.A zeroinitializer, align 1
@__dso_handle = external global i8*
@llvm.global_ctors = appending global [1 x %0] [%0 { i32 65535, void ()* @_GLOBAL__I_a }]
; CHECK-NOT: call i32 @__cxa_atexit
define internal void @__cxx_global_var_init() nounwind section "__TEXT,__StaticInit,regular,pure_instructions" {
%1 = call i32 @__cxa_atexit(void (i8*)* bitcast (void (%struct.A*)* @_ZN1AD1Ev to void (i8*)*), i8* getelementptr inbounds (%struct.A* @a, i32 0, i32 0), i8* bitcast (i8** @__dso_handle to i8*))
ret void
}
define linkonce_odr void @_ZN1AD1Ev(%struct.A* %this) nounwind align 2 {
call void @_ZN1AD2Ev(%struct.A* %this)
ret void
}
declare i32 @__cxa_atexit(void (i8*)*, i8*, i8*)
define linkonce_odr void @_ZN1AD2Ev(%struct.A* %this) nounwind align 2 {
ret void
}
define internal void @_GLOBAL__I_a() nounwind section "__TEXT,__StaticInit,regular,pure_instructions" {
call void @__cxx_global_var_init()
ret void
}