[llvm-commits] [llvm] r127970 - in /llvm/trunk: lib/Transforms/IPO/GlobalOpt.cpp test/Transforms/GlobalOpt/cxx-dtor.ll

Nick Lewycky nicholas at mxc.ca
Sun Mar 20 19:32:50 PDT 2011


Anders Carlsson wrote:
> Author: andersca
> Date: Sun Mar 20 12:59:11 2011
> New Revision: 127970
>
> URL: http://llvm.org/viewvc/llvm-project?rev=127970&view=rev
> Log:
> Add an optimization to GlobalOpt that eliminates calls to __cxa_atexit, if the function passed is empty.

Very cool!

I think you should also skip debugging intrinsics when determining 
whether the destructor is empty. Those may be left behind after the 
contents of a destructor was optimized away, and in general we want '-g 
-O2' to degrade the debug info without impacting the optimizers.

Nick

>
> Added:
>      llvm/trunk/test/Transforms/GlobalOpt/cxx-dtor.ll
> Modified:
>      llvm/trunk/lib/Transforms/IPO/GlobalOpt.cpp
>
> Modified: llvm/trunk/lib/Transforms/IPO/GlobalOpt.cpp
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/IPO/GlobalOpt.cpp?rev=127970&r1=127969&r2=127970&view=diff
> ==============================================================================
> --- llvm/trunk/lib/Transforms/IPO/GlobalOpt.cpp (original)
> +++ llvm/trunk/lib/Transforms/IPO/GlobalOpt.cpp Sun Mar 20 12:59:11 2011
> @@ -54,6 +54,7 @@
>   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 @@
>       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 @@
>     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 @@
>
>       // Resolve aliases, when possible.
>       LocalChange |= OptimizeGlobalAliases(M);
> +
> +    // Try to remove trivial global destructors.
> +    if (CXAAtExitFn)
> +      LocalChange |= OptimizeEmptyGlobalCXXDtors(CXAAtExitFn);
> +
>       Changed |= LocalChange;
>     }
>
>
> Added: llvm/trunk/test/Transforms/GlobalOpt/cxx-dtor.ll
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/GlobalOpt/cxx-dtor.ll?rev=127970&view=auto
> ==============================================================================
> --- llvm/trunk/test/Transforms/GlobalOpt/cxx-dtor.ll (added)
> +++ llvm/trunk/test/Transforms/GlobalOpt/cxx-dtor.ll Sun Mar 20 12:59:11 2011
> @@ -0,0 +1,31 @@
> +; RUN: opt<  %s -globalopt -S | FileCheck %s
> +
> +%0 = type { i32, void ()* }
> +%struct.A = type { i8 }
> +
> + at a = global %struct.A zeroinitializer, align 1
> + at __dso_handle = external global i8*
> + at 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
> +}
>
>
> _______________________________________________
> llvm-commits mailing list
> llvm-commits at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits
>




More information about the llvm-commits mailing list