[GlobalOpt] Simplify __cxa_atexit elimination

cxxDtorIsEmpty checks callers recursively to determine if the
__cxa_atexit-registered function is empty, and eliminates the
__cxa_atexit call accordingly.

This recursive check is unnecessary as redundant instructions and
function calls can be removed by early-cse and inliner. In addition,
cxxDtorIsEmpty does not mark visited function and it may visit a
function exponential times (multiplication principle).

llvm-svn: 353603
This commit is contained in:
Fangrui Song 2019-02-09 09:18:37 +00:00
parent c5cb2ce905
commit 6e679f8ba5
2 changed files with 10 additions and 40 deletions

View File

@ -2814,46 +2814,20 @@ static Function *FindCXAAtExit(Module &M, TargetLibraryInfo *TLI) {
/// Returns whether the given function is an empty C++ destructor and can /// Returns whether the given function is an empty C++ destructor and can
/// therefore be eliminated. /// therefore be eliminated.
/// Note that we assume that other optimization passes have already simplified /// 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 code so we simply check for 'ret'.
/// the only allowed instructions are 'ret', 'call' to an empty C++ dtor and static bool cxxDtorIsEmpty(const Function &Fn) {
/// other side-effect free instructions.
static bool cxxDtorIsEmpty(const Function &Fn,
SmallPtrSet<const Function *, 8> &CalledFunctions) {
// FIXME: We could eliminate C++ destructors if they're readonly/readnone and // FIXME: We could eliminate C++ destructors if they're readonly/readnone and
// nounwind, but that doesn't seem worth doing. // nounwind, but that doesn't seem worth doing.
if (Fn.isDeclaration()) if (Fn.isDeclaration())
return false; return false;
if (++Fn.begin() != Fn.end()) for (auto &I : Fn.getEntryBlock()) {
return false; if (isa<DbgInfoIntrinsic>(I))
continue;
const BasicBlock &EntryBlock = Fn.getEntryBlock(); if (isa<ReturnInst>(I))
for (BasicBlock::const_iterator I = EntryBlock.begin(), E = EntryBlock.end(); return true;
I != E; ++I) { break;
if (const CallInst *CI = dyn_cast<CallInst>(I)) {
// Ignore debug intrinsics.
if (isa<DbgInfoIntrinsic>(CI))
continue;
const Function *CalledFn = CI->getCalledFunction();
if (!CalledFn)
return false;
SmallPtrSet<const Function *, 8> NewCalledFunctions(CalledFunctions);
// Don't treat recursive functions as empty.
if (!NewCalledFunctions.insert(CalledFn).second)
return false;
if (!cxxDtorIsEmpty(*CalledFn, NewCalledFunctions))
return false;
} else if (isa<ReturnInst>(*I))
return true; // We're done.
else if (I->mayHaveSideEffects())
return false; // Destructor with side effects, bail.
} }
return false; return false;
} }
@ -2885,11 +2859,7 @@ static bool OptimizeEmptyGlobalCXXDtors(Function *CXAAtExitFn) {
Function *DtorFn = Function *DtorFn =
dyn_cast<Function>(CI->getArgOperand(0)->stripPointerCasts()); dyn_cast<Function>(CI->getArgOperand(0)->stripPointerCasts());
if (!DtorFn) if (!DtorFn || !cxxDtorIsEmpty(*DtorFn))
continue;
SmallPtrSet<const Function *, 8> CalledFunctions;
if (!cxxDtorIsEmpty(*DtorFn, CalledFunctions))
continue; continue;
// Just remove the call. // Just remove the call.

View File

@ -1,4 +1,4 @@
; RUN: opt < %s -globalopt -S | FileCheck %s ; RUN: opt < %s -S -passes='cgscc(inline),function(early-cse),globalopt' | FileCheck %s
%0 = type { i32, void ()* } %0 = type { i32, void ()* }
%struct.A = type { i8 } %struct.A = type { i8 }