[llvm] [llvm][GlobalOpt] Remove empty atexit destructors/handlers (PR #88836)

Max Winkler via llvm-commits llvm-commits at lists.llvm.org
Thu Apr 25 17:16:33 PDT 2024


https://github.com/MaxEW707 updated https://github.com/llvm/llvm-project/pull/88836

>From cb1616a529e303e05e52b524bbea3eeb7d862b55 Mon Sep 17 00:00:00 2001
From: MaxEW707 <max.enrico.winkler at gmail.com>
Date: Mon, 15 Apr 2024 22:04:18 -0400
Subject: [PATCH 01/11] Remove empty atexit destructors

---
 .../llvm/Analysis/TargetLibraryInfo.def       |  5 ++
 llvm/lib/Transforms/IPO/GlobalOpt.cpp         | 72 +++++++++++--------
 llvm/test/Transforms/GlobalOpt/atexit-dtor.ll | 31 ++++++++
 3 files changed, 80 insertions(+), 28 deletions(-)
 create mode 100644 llvm/test/Transforms/GlobalOpt/atexit-dtor.ll

diff --git a/llvm/include/llvm/Analysis/TargetLibraryInfo.def b/llvm/include/llvm/Analysis/TargetLibraryInfo.def
index 37221eb9e47115..717693a7cf63c1 100644
--- a/llvm/include/llvm/Analysis/TargetLibraryInfo.def
+++ b/llvm/include/llvm/Analysis/TargetLibraryInfo.def
@@ -471,6 +471,11 @@ TLI_DEFINE_ENUM_INTERNAL(cxa_atexit)
 TLI_DEFINE_STRING_INTERNAL("__cxa_atexit")
 TLI_DEFINE_SIG_INTERNAL(Int, Ptr, Ptr, Ptr)
 
+/// int atexit(void (*f)(void));
+TLI_DEFINE_ENUM_INTERNAL(atexit)
+TLI_DEFINE_STRING_INTERNAL("atexit")
+TLI_DEFINE_SIG_INTERNAL(Int, Ptr)
+
 /// void __cxa_guard_abort(guard_t *guard);
 /// guard_t is int64_t in Itanium ABI or int32_t on ARM eabi.
 TLI_DEFINE_ENUM_INTERNAL(cxa_guard_abort)
diff --git a/llvm/lib/Transforms/IPO/GlobalOpt.cpp b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
index da714c9a75701b..d6bc1eda5180fb 100644
--- a/llvm/lib/Transforms/IPO/GlobalOpt.cpp
+++ b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
@@ -87,6 +87,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");
+STATISTIC(NumAtExitRemoved, "Number of atexit handlers removed");
 STATISTIC(NumInternalFunc, "Number of internal functions");
 STATISTIC(NumColdCC, "Number of functions marked coldcc");
 STATISTIC(NumIFuncsResolved, "Number of statically resolved IFuncs");
@@ -2321,36 +2322,46 @@ OptimizeGlobalAliases(Module &M,
 }
 
 static Function *
-FindCXAAtExit(Module &M, function_ref<TargetLibraryInfo &(Function &)> GetTLI) {
-  // Hack to get a default TLI before we have actual Function.
-  auto FuncIter = M.begin();
-  if (FuncIter == M.end())
-    return nullptr;
-  auto *TLI = &GetTLI(*FuncIter);
+FindAtExitLibFunc(Module &M, function_ref<TargetLibraryInfo &(Function &)> GetTLI, LibFunc Func) {
+    // Hack to get a default TLI before we have actual Function.
+    auto FuncIter = M.begin();
+    if (FuncIter == M.end())
+        return nullptr;
+    auto *TLI = &GetTLI(*FuncIter);
 
-  LibFunc F = LibFunc_cxa_atexit;
-  if (!TLI->has(F))
-    return nullptr;
+    LibFunc F = Func;
+    if (!TLI->has(F))
+        return nullptr;
 
-  Function *Fn = M.getFunction(TLI->getName(F));
-  if (!Fn)
-    return nullptr;
+    Function *Fn = M.getFunction(TLI->getName(F));
+    if (!Fn)
+        return nullptr;
 
-  // Now get the actual TLI for Fn.
-  TLI = &GetTLI(*Fn);
+    // Now get the actual TLI for Fn.
+    TLI = &GetTLI(*Fn);
 
-  // Make sure that the function has the correct prototype.
-  if (!TLI->getLibFunc(*Fn, F) || F != LibFunc_cxa_atexit)
-    return nullptr;
+    // Make sure that the function has the correct prototype.
+    if (!TLI->getLibFunc(*Fn, F) || F != Func)
+        return nullptr;
+
+    return Fn;
+}
 
-  return Fn;
+static Function *
+FindCXAAtExit(Module &M, function_ref<TargetLibraryInfo &(Function &)> GetTLI) {
+    return FindAtExitLibFunc(M, GetTLI, LibFunc_cxa_atexit);
 }
 
-/// Returns whether the given function is an empty C++ destructor and can
-/// therefore be eliminated.
+static Function *
+FindAtExit(Module &M, function_ref<TargetLibraryInfo &(Function &)> GetTLI) {
+    return FindAtExitLibFunc(M, GetTLI, LibFunc_atexit);
+}
+
+/// Returns whether the given function is an empty C++ destructor or atexit handler
+/// and can therefore be eliminated.
 /// Note that we assume that other optimization passes have already simplified
 /// the code so we simply check for 'ret'.
-static bool cxxDtorIsEmpty(const Function &Fn) {
+static bool IsEmptyAtExitFunction(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())
@@ -2366,7 +2377,7 @@ static bool cxxDtorIsEmpty(const Function &Fn) {
   return false;
 }
 
-static bool OptimizeEmptyGlobalCXXDtors(Function *CXAAtExitFn) {
+static bool OptimizeEmptyGlobalAtExitDtors(Function *CXAAtExitFn, bool isCXX) {
   /// Itanium C++ ABI p3.3.5:
   ///
   ///   After constructing a global (or local static) object, that will require
@@ -2379,7 +2390,7 @@ static bool OptimizeEmptyGlobalCXXDtors(Function *CXAAtExitFn) {
   ///   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
+  // This pass will look for calls to __cxa_atexit or atexit where the function is trivial
   // and remove them.
   bool Changed = false;
 
@@ -2393,14 +2404,17 @@ static bool OptimizeEmptyGlobalCXXDtors(Function *CXAAtExitFn) {
 
     Function *DtorFn =
       dyn_cast<Function>(CI->getArgOperand(0)->stripPointerCasts());
-    if (!DtorFn || !cxxDtorIsEmpty(*DtorFn))
+    if (!DtorFn || !IsEmptyAtExitFunction(*DtorFn))
       continue;
 
     // Just remove the call.
     CI->replaceAllUsesWith(Constant::getNullValue(CI->getType()));
     CI->eraseFromParent();
 
-    ++NumCXXDtorsRemoved;
+    if (isCXX)
+        ++NumCXXDtorsRemoved;
+    else
+        ++NumAtExitRemoved;
 
     Changed |= true;
   }
@@ -2518,9 +2532,11 @@ optimizeGlobalsInModule(Module &M, const DataLayout &DL,
 
     // Try to remove trivial global destructors if they are not removed
     // already.
-    Function *CXAAtExitFn = FindCXAAtExit(M, GetTLI);
-    if (CXAAtExitFn)
-      LocalChange |= OptimizeEmptyGlobalCXXDtors(CXAAtExitFn);
+    if (Function *CXAAtExitFn = FindCXAAtExit(M, GetTLI))
+        LocalChange |= OptimizeEmptyGlobalAtExitDtors(CXAAtExitFn, true);
+
+    if (Function *AtExitFn = FindAtExit(M, GetTLI))
+        LocalChange |= OptimizeEmptyGlobalAtExitDtors(AtExitFn, false);
 
     // Optimize IFuncs whose callee's are statically known.
     LocalChange |= OptimizeStaticIFuncs(M);
diff --git a/llvm/test/Transforms/GlobalOpt/atexit-dtor.ll b/llvm/test/Transforms/GlobalOpt/atexit-dtor.ll
new file mode 100644
index 00000000000000..b3806af51ab5e7
--- /dev/null
+++ b/llvm/test/Transforms/GlobalOpt/atexit-dtor.ll
@@ -0,0 +1,31 @@
+; RUN: opt < %s -S -passes='cgscc(inline),function(early-cse),globalopt' | FileCheck %s
+
+%struct.A = type { i32 }
+
+$"??1A@@QEAA at XZ" = comdat any
+
+@"?g@@3UA@@A" = dso_local global %struct.A zeroinitializer, align 4
+ at llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @_GLOBAL__sub_I_atexit-dtor, ptr null }]
+
+; CHECK-NOT: call i32 @atexit
+
+define internal void @"??__Eg@@YAXXZ"() #0 {
+  %1 = call i32 @atexit(ptr @"??__Fg@@YAXXZ") #2
+  ret void
+}
+
+define linkonce_odr dso_local void @"??1A@@QEAA at XZ"(ptr noundef nonnull align 4 dereferenceable(4) %0) unnamed_addr #1 comdat align 2 {
+  ret void
+}
+
+define internal void @"??__Fg@@YAXXZ"() #0 {
+  call void @"??1A@@QEAA at XZ"(ptr @"?g@@3UA@@A")
+  ret void
+}
+
+declare dso_local i32 @atexit(ptr) #2
+
+define internal void @_GLOBAL__sub_I_atexit-dtor() #0 {
+  call void @"??__Eg@@YAXXZ"()
+  ret void
+}
\ No newline at end of file

>From d5cd15552526b876fa7d7dedb4353c35a4a2031d Mon Sep 17 00:00:00 2001
From: MaxEW707 <max.enrico.winkler at gmail.com>
Date: Mon, 15 Apr 2024 22:19:21 -0400
Subject: [PATCH 02/11] formatting

---
 llvm/lib/Transforms/IPO/GlobalOpt.cpp | 46 +++++++++++++--------------
 1 file changed, 23 insertions(+), 23 deletions(-)

diff --git a/llvm/lib/Transforms/IPO/GlobalOpt.cpp b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
index d6bc1eda5180fb..bf277efc892bff 100644
--- a/llvm/lib/Transforms/IPO/GlobalOpt.cpp
+++ b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
@@ -2323,38 +2323,38 @@ OptimizeGlobalAliases(Module &M,
 
 static Function *
 FindAtExitLibFunc(Module &M, function_ref<TargetLibraryInfo &(Function &)> GetTLI, LibFunc Func) {
-    // Hack to get a default TLI before we have actual Function.
-    auto FuncIter = M.begin();
-    if (FuncIter == M.end())
-        return nullptr;
-    auto *TLI = &GetTLI(*FuncIter);
+  // Hack to get a default TLI before we have actual Function.
+  auto FuncIter = M.begin();
+  if (FuncIter == M.end())
+    return nullptr;
+  auto *TLI = &GetTLI(*FuncIter);
 
-    LibFunc F = Func;
-    if (!TLI->has(F))
-        return nullptr;
+  LibFunc F = Func;
+  if (!TLI->has(F))
+    return nullptr;
 
-    Function *Fn = M.getFunction(TLI->getName(F));
-    if (!Fn)
-        return nullptr;
+  Function *Fn = M.getFunction(TLI->getName(F));
+  if (!Fn)
+    return nullptr;
 
-    // Now get the actual TLI for Fn.
-    TLI = &GetTLI(*Fn);
+  // Now get the actual TLI for Fn.
+  TLI = &GetTLI(*Fn);
 
-    // Make sure that the function has the correct prototype.
-    if (!TLI->getLibFunc(*Fn, F) || F != Func)
-        return nullptr;
+  // Make sure that the function has the correct prototype.
+  if (!TLI->getLibFunc(*Fn, F) || F != Func)
+    return nullptr;
 
-    return Fn;
+  return Fn;
 }
 
 static Function *
 FindCXAAtExit(Module &M, function_ref<TargetLibraryInfo &(Function &)> GetTLI) {
-    return FindAtExitLibFunc(M, GetTLI, LibFunc_cxa_atexit);
+  return FindAtExitLibFunc(M, GetTLI, LibFunc_cxa_atexit);
 }
 
 static Function *
 FindAtExit(Module &M, function_ref<TargetLibraryInfo &(Function &)> GetTLI) {
-    return FindAtExitLibFunc(M, GetTLI, LibFunc_atexit);
+  return FindAtExitLibFunc(M, GetTLI, LibFunc_atexit);
 }
 
 /// Returns whether the given function is an empty C++ destructor or atexit handler
@@ -2412,9 +2412,9 @@ static bool OptimizeEmptyGlobalAtExitDtors(Function *CXAAtExitFn, bool isCXX) {
     CI->eraseFromParent();
 
     if (isCXX)
-        ++NumCXXDtorsRemoved;
+      ++NumCXXDtorsRemoved;
     else
-        ++NumAtExitRemoved;
+      ++NumAtExitRemoved;
 
     Changed |= true;
   }
@@ -2533,10 +2533,10 @@ optimizeGlobalsInModule(Module &M, const DataLayout &DL,
     // Try to remove trivial global destructors if they are not removed
     // already.
     if (Function *CXAAtExitFn = FindCXAAtExit(M, GetTLI))
-        LocalChange |= OptimizeEmptyGlobalAtExitDtors(CXAAtExitFn, true);
+      LocalChange |= OptimizeEmptyGlobalAtExitDtors(CXAAtExitFn, true);
 
     if (Function *AtExitFn = FindAtExit(M, GetTLI))
-        LocalChange |= OptimizeEmptyGlobalAtExitDtors(AtExitFn, false);
+      LocalChange |= OptimizeEmptyGlobalAtExitDtors(AtExitFn, false);
 
     // Optimize IFuncs whose callee's are statically known.
     LocalChange |= OptimizeStaticIFuncs(M);

>From f5e276d8b3dc7e7dcb96d67a0c3d2b25fc6bcdb0 Mon Sep 17 00:00:00 2001
From: MaxEW707 <max.enrico.winkler at gmail.com>
Date: Mon, 15 Apr 2024 22:23:41 -0400
Subject: [PATCH 03/11] eol

---
 llvm/test/Transforms/GlobalOpt/atexit-dtor.ll | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/test/Transforms/GlobalOpt/atexit-dtor.ll b/llvm/test/Transforms/GlobalOpt/atexit-dtor.ll
index b3806af51ab5e7..83c7482aca1e7b 100644
--- a/llvm/test/Transforms/GlobalOpt/atexit-dtor.ll
+++ b/llvm/test/Transforms/GlobalOpt/atexit-dtor.ll
@@ -28,4 +28,4 @@ declare dso_local i32 @atexit(ptr) #2
 define internal void @_GLOBAL__sub_I_atexit-dtor() #0 {
   call void @"??__Eg@@YAXXZ"()
   ret void
-}
\ No newline at end of file
+}

>From d38ec0cefee847e3d2dc8a157d0a23a118d88fed Mon Sep 17 00:00:00 2001
From: MaxEW707 <max.enrico.winkler at gmail.com>
Date: Mon, 15 Apr 2024 23:05:30 -0400
Subject: [PATCH 04/11] unit test for explicit at exit registration

---
 llvm/test/Transforms/GlobalOpt/atexit-dtor.ll | 25 +++++++++++++++----
 1 file changed, 20 insertions(+), 5 deletions(-)

diff --git a/llvm/test/Transforms/GlobalOpt/atexit-dtor.ll b/llvm/test/Transforms/GlobalOpt/atexit-dtor.ll
index 83c7482aca1e7b..aa1f4998630097 100644
--- a/llvm/test/Transforms/GlobalOpt/atexit-dtor.ll
+++ b/llvm/test/Transforms/GlobalOpt/atexit-dtor.ll
@@ -9,8 +9,8 @@ $"??1A@@QEAA at XZ" = comdat any
 
 ; CHECK-NOT: call i32 @atexit
 
-define internal void @"??__Eg@@YAXXZ"() #0 {
-  %1 = call i32 @atexit(ptr @"??__Fg@@YAXXZ") #2
+define internal void @"??__Eg@@YAXXZ"() {
+  %1 = call i32 @atexit(ptr @"??__Fg@@YAXXZ")
   ret void
 }
 
@@ -18,14 +18,29 @@ define linkonce_odr dso_local void @"??1A@@QEAA at XZ"(ptr noundef nonnull align 4
   ret void
 }
 
-define internal void @"??__Fg@@YAXXZ"() #0 {
+define internal void @"??__Fg@@YAXXZ"() {
   call void @"??1A@@QEAA at XZ"(ptr @"?g@@3UA@@A")
   ret void
 }
 
-declare dso_local i32 @atexit(ptr) #2
+declare dso_local i32 @atexit(ptr)
 
-define internal void @_GLOBAL__sub_I_atexit-dtor() #0 {
+define internal void @_GLOBAL__sub_I_atexit-dtor() {
   call void @"??__Eg@@YAXXZ"()
   ret void
 }
+
+define dso_local void @atexit_handler() {
+  ret void
+}
+
+; CHECK-NOT: call i32 @atexit
+
+; Check that a removed `atexit` call returns `0` which is the value that denotes success.
+define dso_local noundef i32 @register_atexit_handler() {
+  %1 = alloca i32, align 4
+  store i32 0, ptr %1, align 4
+  %2 = call i32 @atexit(ptr @"atexit_handler")
+; CHECK: ret i32 0
+  ret i32 %2
+}
\ No newline at end of file

>From dfd476b7cfb133c744876dddf2fbdccb0696a372 Mon Sep 17 00:00:00 2001
From: MaxEW707 <max.enrico.winkler at gmail.com>
Date: Mon, 15 Apr 2024 23:06:33 -0400
Subject: [PATCH 05/11] eol

---
 llvm/test/Transforms/GlobalOpt/atexit-dtor.ll | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/test/Transforms/GlobalOpt/atexit-dtor.ll b/llvm/test/Transforms/GlobalOpt/atexit-dtor.ll
index aa1f4998630097..f504fcc9ae29d3 100644
--- a/llvm/test/Transforms/GlobalOpt/atexit-dtor.ll
+++ b/llvm/test/Transforms/GlobalOpt/atexit-dtor.ll
@@ -43,4 +43,4 @@ define dso_local noundef i32 @register_atexit_handler() {
   %2 = call i32 @atexit(ptr @"atexit_handler")
 ; CHECK: ret i32 0
   ret i32 %2
-}
\ No newline at end of file
+}

>From a3a8419930f5674e5c038574fb27294d04e8e351 Mon Sep 17 00:00:00 2001
From: MaxEW707 <max.enrico.winkler at gmail.com>
Date: Mon, 15 Apr 2024 23:53:40 -0400
Subject: [PATCH 06/11] formatting

---
 llvm/lib/Transforms/IPO/GlobalOpt.cpp | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/llvm/lib/Transforms/IPO/GlobalOpt.cpp b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
index bf277efc892bff..2bb9e032444ff8 100644
--- a/llvm/lib/Transforms/IPO/GlobalOpt.cpp
+++ b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
@@ -2322,7 +2322,9 @@ OptimizeGlobalAliases(Module &M,
 }
 
 static Function *
-FindAtExitLibFunc(Module &M, function_ref<TargetLibraryInfo &(Function &)> GetTLI, LibFunc Func) {
+FindAtExitLibFunc(Module &M,
+                  function_ref<TargetLibraryInfo &(Function &)> GetTLI,
+                  LibFunc Func) {
   // Hack to get a default TLI before we have actual Function.
   auto FuncIter = M.begin();
   if (FuncIter == M.end())
@@ -2357,10 +2359,10 @@ FindAtExit(Module &M, function_ref<TargetLibraryInfo &(Function &)> GetTLI) {
   return FindAtExitLibFunc(M, GetTLI, LibFunc_atexit);
 }
 
-/// Returns whether the given function is an empty C++ destructor or atexit handler
-/// and can therefore be eliminated.
-/// Note that we assume that other optimization passes have already simplified
-/// the code so we simply check for 'ret'.
+/// Returns whether the given function is an empty C++ destructor or atexit
+/// handler and can therefore be eliminated. Note that we assume that other
+/// optimization passes have already simplified the code so we simply check for
+/// 'ret'.
 static bool IsEmptyAtExitFunction(const Function &Fn) {
   // FIXME: We could eliminate C++ destructors if they're readonly/readnone and
   // nounwind, but that doesn't seem worth doing.
@@ -2390,8 +2392,8 @@ static bool OptimizeEmptyGlobalAtExitDtors(Function *CXAAtExitFn, bool isCXX) {
   ///   registered before this one. It returns zero if registration is
   ///   successful, nonzero on failure.
 
-  // This pass will look for calls to __cxa_atexit or atexit where the function is trivial
-  // and remove them.
+  // This pass will look for calls to __cxa_atexit or atexit where the function
+  // is trivial and remove them.
   bool Changed = false;
 
   for (User *U : llvm::make_early_inc_range(CXAAtExitFn->users())) {

>From 8bedd9c275be6dbebe1490fbd55c0074c8c8b2eb Mon Sep 17 00:00:00 2001
From: MaxEW707 <max.enrico.winkler at gmail.com>
Date: Tue, 16 Apr 2024 20:14:33 -0400
Subject: [PATCH 07/11] fix unit test

---
 llvm/unittests/Analysis/TargetLibraryInfoTest.cpp | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/llvm/unittests/Analysis/TargetLibraryInfoTest.cpp b/llvm/unittests/Analysis/TargetLibraryInfoTest.cpp
index 8e3fe3b44a84a9..2539620450f43f 100644
--- a/llvm/unittests/Analysis/TargetLibraryInfoTest.cpp
+++ b/llvm/unittests/Analysis/TargetLibraryInfoTest.cpp
@@ -495,6 +495,8 @@ TEST_F(TargetLibraryInfoTest, ValidProto) {
       "declare i32 @__cxa_guard_acquire(%struct*)\n"
       "declare void @__cxa_guard_release(%struct*)\n"
 
+      "declare i32 @atexit(void ()*)\n"
+
       "declare i32 @__nvvm_reflect(i8*)\n"
 
       "declare i8* @__memcpy_chk(i8*, i8*, i64, i64)\n"
@@ -658,4 +660,4 @@ class TLITestAarch64 : public ::testing::Test {
 TEST_F(TLITestAarch64, TestFrem) {
   EXPECT_EQ(getScalarName(Instruction::FRem, Type::getDoubleTy(Ctx)), "fmod");
   EXPECT_EQ(getScalarName(Instruction::FRem, Type::getFloatTy(Ctx)), "fmodf");
-}
\ No newline at end of file
+}

>From f1b585f579f31e5744e277f9b8a71d16d97589b4 Mon Sep 17 00:00:00 2001
From: MaxEW707 <max.enrico.winkler at gmail.com>
Date: Tue, 16 Apr 2024 21:06:03 -0400
Subject: [PATCH 08/11] Add atexit to ps tli checker

---
 llvm/test/tools/llvm-tli-checker/ps4-tli-check.yaml | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/llvm/test/tools/llvm-tli-checker/ps4-tli-check.yaml b/llvm/test/tools/llvm-tli-checker/ps4-tli-check.yaml
index 46f6a2d0a554ac..95c23007b1a05a 100644
--- a/llvm/test/tools/llvm-tli-checker/ps4-tli-check.yaml
+++ b/llvm/test/tools/llvm-tli-checker/ps4-tli-check.yaml
@@ -34,21 +34,21 @@
 #
 # CHECK: << Total TLI yes SDK no:  8
 # CHECK: >> Total TLI no  SDK yes: 0
-# CHECK: == Total TLI yes SDK yes: 238
+# CHECK: == Total TLI yes SDK yes: 239
 #
 # WRONG_DETAIL: << TLI yes SDK no : '_ZdaPv' aka operator delete[](void*)
 # WRONG_DETAIL: >> TLI no  SDK yes: '_ZdaPvj' aka operator delete[](void*, unsigned int)
 # WRONG_DETAIL-COUNT-8: << TLI yes SDK no : {{.*}}__hot_cold_t
 # WRONG_SUMMARY: << Total TLI yes SDK no:  9{{$}}
 # WRONG_SUMMARY: >> Total TLI no  SDK yes: 1{{$}}
-# WRONG_SUMMARY: == Total TLI yes SDK yes: 237
+# WRONG_SUMMARY: == Total TLI yes SDK yes: 238
 #
 ## The -COUNT suffix doesn't care if there are too many matches, so check
 ## the exact count first; the two directives should add up to that.
 ## Yes, this means additions to TLI will fail this test, but the argument
 ## to -COUNT can't be an expression.
-# AVAIL: TLI knows 479 symbols, 246 available
-# AVAIL-COUNT-246: {{^}} available
+# AVAIL: TLI knows 480 symbols, 247 available
+# AVAIL-COUNT-247: {{^}} available
 # AVAIL-NOT:       {{^}} available
 # UNAVAIL-COUNT-233: not available
 # UNAVAIL-NOT:       not available
@@ -263,6 +263,10 @@ DynamicSymbols:
     Type:            STT_FUNC
     Section:         .text
     Binding:         STB_GLOBAL
+  - Name:            atexit
+    Type:            STT_FUNC
+    Section:         .text
+    Binding:         STB_GLOBAL
   - Name:            atof
     Type:            STT_FUNC
     Section:         .text

>From 08f30d56b9c7a35323411f22a52b571631114925 Mon Sep 17 00:00:00 2001
From: MaxEW707 <max.enrico.winkler at gmail.com>
Date: Tue, 16 Apr 2024 21:19:53 -0400
Subject: [PATCH 09/11] fix formatting

---
 llvm/unittests/Analysis/TargetLibraryInfoTest.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/unittests/Analysis/TargetLibraryInfoTest.cpp b/llvm/unittests/Analysis/TargetLibraryInfoTest.cpp
index 2539620450f43f..1fe94e2aae059e 100644
--- a/llvm/unittests/Analysis/TargetLibraryInfoTest.cpp
+++ b/llvm/unittests/Analysis/TargetLibraryInfoTest.cpp
@@ -660,4 +660,4 @@ class TLITestAarch64 : public ::testing::Test {
 TEST_F(TLITestAarch64, TestFrem) {
   EXPECT_EQ(getScalarName(Instruction::FRem, Type::getDoubleTy(Ctx)), "fmod");
   EXPECT_EQ(getScalarName(Instruction::FRem, Type::getFloatTy(Ctx)), "fmodf");
-}
+}
\ No newline at end of file

>From e9b1eb5cf3c5bbe88010c7b5126aa8c34692166f Mon Sep 17 00:00:00 2001
From: MaxEW707 <max.enrico.winkler at gmail.com>
Date: Wed, 24 Apr 2024 23:42:41 -0400
Subject: [PATCH 10/11] Remove static functions; Feedback variable scoping

---
 llvm/lib/Transforms/IPO/GlobalOpt.cpp | 20 +++++---------------
 1 file changed, 5 insertions(+), 15 deletions(-)

diff --git a/llvm/lib/Transforms/IPO/GlobalOpt.cpp b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
index 2bb9e032444ff8..4fe76b25db7f05 100644
--- a/llvm/lib/Transforms/IPO/GlobalOpt.cpp
+++ b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
@@ -2331,11 +2331,10 @@ FindAtExitLibFunc(Module &M,
     return nullptr;
   auto *TLI = &GetTLI(*FuncIter);
 
-  LibFunc F = Func;
-  if (!TLI->has(F))
+  if (!TLI->has(Func))
     return nullptr;
 
-  Function *Fn = M.getFunction(TLI->getName(F));
+  Function *Fn = M.getFunction(TLI->getName(Func));
   if (!Fn)
     return nullptr;
 
@@ -2343,22 +2342,13 @@ FindAtExitLibFunc(Module &M,
   TLI = &GetTLI(*Fn);
 
   // Make sure that the function has the correct prototype.
+  LibFunc F;
   if (!TLI->getLibFunc(*Fn, F) || F != Func)
     return nullptr;
 
   return Fn;
 }
 
-static Function *
-FindCXAAtExit(Module &M, function_ref<TargetLibraryInfo &(Function &)> GetTLI) {
-  return FindAtExitLibFunc(M, GetTLI, LibFunc_cxa_atexit);
-}
-
-static Function *
-FindAtExit(Module &M, function_ref<TargetLibraryInfo &(Function &)> GetTLI) {
-  return FindAtExitLibFunc(M, GetTLI, LibFunc_atexit);
-}
-
 /// Returns whether the given function is an empty C++ destructor or atexit
 /// handler and can therefore be eliminated. Note that we assume that other
 /// optimization passes have already simplified the code so we simply check for
@@ -2534,10 +2524,10 @@ optimizeGlobalsInModule(Module &M, const DataLayout &DL,
 
     // Try to remove trivial global destructors if they are not removed
     // already.
-    if (Function *CXAAtExitFn = FindCXAAtExit(M, GetTLI))
+    if (Function *CXAAtExitFn = FindAtExitLibFunc(M, GetTLI, LibFunc_cxa_atexit))
       LocalChange |= OptimizeEmptyGlobalAtExitDtors(CXAAtExitFn, true);
 
-    if (Function *AtExitFn = FindAtExit(M, GetTLI))
+    if (Function *AtExitFn = FindAtExitLibFunc(M, GetTLI, LibFunc_atexit))
       LocalChange |= OptimizeEmptyGlobalAtExitDtors(AtExitFn, false);
 
     // Optimize IFuncs whose callee's are statically known.

>From 00c6aed07d839a6ade02ae0ee76a0122c16f3ff9 Mon Sep 17 00:00:00 2001
From: MaxEW707 <max.enrico.winkler at gmail.com>
Date: Thu, 25 Apr 2024 20:16:05 -0400
Subject: [PATCH 11/11] autogenerate check statements for unit test

---
 llvm/test/Transforms/GlobalOpt/atexit-dtor.ll | 43 +++++--------------
 1 file changed, 11 insertions(+), 32 deletions(-)

diff --git a/llvm/test/Transforms/GlobalOpt/atexit-dtor.ll b/llvm/test/Transforms/GlobalOpt/atexit-dtor.ll
index f504fcc9ae29d3..6b9480a3f6a684 100644
--- a/llvm/test/Transforms/GlobalOpt/atexit-dtor.ll
+++ b/llvm/test/Transforms/GlobalOpt/atexit-dtor.ll
@@ -1,46 +1,25 @@
-; RUN: opt < %s -S -passes='cgscc(inline),function(early-cse),globalopt' | FileCheck %s
-
-%struct.A = type { i32 }
-
-$"??1A@@QEAA at XZ" = comdat any
-
-@"?g@@3UA@@A" = dso_local global %struct.A zeroinitializer, align 4
- at llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @_GLOBAL__sub_I_atexit-dtor, ptr null }]
-
-; CHECK-NOT: call i32 @atexit
-
-define internal void @"??__Eg@@YAXXZ"() {
-  %1 = call i32 @atexit(ptr @"??__Fg@@YAXXZ")
-  ret void
-}
-
-define linkonce_odr dso_local void @"??1A@@QEAA at XZ"(ptr noundef nonnull align 4 dereferenceable(4) %0) unnamed_addr #1 comdat align 2 {
-  ret void
-}
-
-define internal void @"??__Fg@@YAXXZ"() {
-  call void @"??1A@@QEAA at XZ"(ptr @"?g@@3UA@@A")
-  ret void
-}
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
+; RUN: opt < %s -S -passes=globalopt | FileCheck %s
 
 declare dso_local i32 @atexit(ptr)
 
-define internal void @_GLOBAL__sub_I_atexit-dtor() {
-  call void @"??__Eg@@YAXXZ"()
-  ret void
-}
-
 define dso_local void @atexit_handler() {
+; CHECK-LABEL: define dso_local void @atexit_handler() local_unnamed_addr {
+; CHECK-NEXT:    ret void
+;
   ret void
 }
 
-; CHECK-NOT: call i32 @atexit
-
+; Check that `atexit` is removed if the handler is empty.
 ; Check that a removed `atexit` call returns `0` which is the value that denotes success.
 define dso_local noundef i32 @register_atexit_handler() {
+; CHECK-LABEL: define dso_local noundef i32 @register_atexit_handler() local_unnamed_addr {
+; CHECK-NEXT:    [[TMP1:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    store i32 0, ptr [[TMP1]], align 4
+; CHECK-NEXT:    ret i32 0
+;
   %1 = alloca i32, align 4
   store i32 0, ptr %1, align 4
   %2 = call i32 @atexit(ptr @"atexit_handler")
-; CHECK: ret i32 0
   ret i32 %2
 }



More information about the llvm-commits mailing list