From: Fangrui Song Date: Sat, 9 Feb 2019 09:18:37 +0000 (+0000) Subject: [GlobalOpt] Simplify __cxa_atexit elimination X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=a7866b1030537ff197099251d5ee6b46b35c13e2;p=llvm [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). git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@353603 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Transforms/IPO/GlobalOpt.cpp b/lib/Transforms/IPO/GlobalOpt.cpp index 7f3644b1bff..c89541dc732 100644 --- a/lib/Transforms/IPO/GlobalOpt.cpp +++ b/lib/Transforms/IPO/GlobalOpt.cpp @@ -2814,46 +2814,20 @@ static Function *FindCXAAtExit(Module &M, TargetLibraryInfo *TLI) { /// 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', 'call' to an empty C++ dtor and -/// other side-effect free instructions. -static bool cxxDtorIsEmpty(const Function &Fn, - SmallPtrSet &CalledFunctions) { +/// the code so we simply check for 'ret'. +static bool cxxDtorIsEmpty(const Function &Fn) { // FIXME: We could eliminate C++ destructors if they're readonly/readnone and // nounwind, but that doesn't seem worth doing. if (Fn.isDeclaration()) return false; - 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(I)) { - // Ignore debug intrinsics. - if (isa(CI)) - continue; - - const Function *CalledFn = CI->getCalledFunction(); - - if (!CalledFn) - return false; - - SmallPtrSet 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(*I)) - return true; // We're done. - else if (I->mayHaveSideEffects()) - return false; // Destructor with side effects, bail. + for (auto &I : Fn.getEntryBlock()) { + if (isa(I)) + continue; + if (isa(I)) + return true; + break; } - return false; } @@ -2885,11 +2859,7 @@ static bool OptimizeEmptyGlobalCXXDtors(Function *CXAAtExitFn) { Function *DtorFn = dyn_cast(CI->getArgOperand(0)->stripPointerCasts()); - if (!DtorFn) - continue; - - SmallPtrSet CalledFunctions; - if (!cxxDtorIsEmpty(*DtorFn, CalledFunctions)) + if (!DtorFn || !cxxDtorIsEmpty(*DtorFn)) continue; // Just remove the call. diff --git a/test/Transforms/GlobalOpt/cxx-dtor.ll b/test/Transforms/GlobalOpt/cxx-dtor.ll index c94c26b5348..c43a8e2be2e 100644 --- a/test/Transforms/GlobalOpt/cxx-dtor.ll +++ b/test/Transforms/GlobalOpt/cxx-dtor.ll @@ -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 ()* } %struct.A = type { i8 }