mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2024-12-29 10:32:47 +00:00
Add an optimization to GlobalOpt that eliminates calls to __cxa_atexit, if the function passed is empty.
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@127970 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
ed8f9589a8
commit
a201c4c2e6
@ -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;
|
||||
}
|
||||
|
||||
|
31
test/Transforms/GlobalOpt/cxx-dtor.ll
Normal file
31
test/Transforms/GlobalOpt/cxx-dtor.ll
Normal 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
|
||||
}
|
Loading…
Reference in New Issue
Block a user