[llvm] [LowerGlobalDtors] Skip __cxa_atexit call completely when arg0 is unused (PR #68758)

Sam Clegg via llvm-commits llvm-commits at lists.llvm.org
Thu Oct 19 09:11:03 PDT 2023


https://github.com/sbc100 updated https://github.com/llvm/llvm-project/pull/68758

>From b05a129f2bdb08b35230f3164963168c04da5a00 Mon Sep 17 00:00:00 2001
From: Sam Clegg <sbc at chromium.org>
Date: Tue, 10 Oct 2023 18:53:26 -0700
Subject: [PATCH] [LowerGlobalDtors] Skip __cxa_atexit call completely when
 arg0 is unused

In emscripten we have a build mode (the default actually) where the
runtime never exits and there for `__cxa_atexit` is a dummy/stub
function that does nothing.  In this case we would like to be able
completely DCE any otherwise-unused global dtor functions.
---
 lld/test/wasm/init-fini-no-gc.ll              |  2 +-
 lld/test/wasm/init-fini.ll                    | 35 ++++++++++---------
 .../lib/Transforms/Utils/LowerGlobalDtors.cpp | 11 ++++++
 .../lower-global-dtors-unused.ll              | 17 +++++++++
 4 files changed, 47 insertions(+), 18 deletions(-)
 create mode 100644 llvm/test/Transforms/LowerGlobalDestructors/lower-global-dtors-unused.ll

diff --git a/lld/test/wasm/init-fini-no-gc.ll b/lld/test/wasm/init-fini-no-gc.ll
index 0a22ebfb1454c04..d6ac07f74cb3030 100644
--- a/lld/test/wasm/init-fini-no-gc.ll
+++ b/lld/test/wasm/init-fini-no-gc.ll
@@ -25,7 +25,7 @@ entry:
 }
 
 define hidden i32 @__cxa_atexit(i32 %func, i32 %arg, i32 %dso_handle) {
-  ret i32 0
+  ret i32 %func
 }
 
 @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [
diff --git a/lld/test/wasm/init-fini.ll b/lld/test/wasm/init-fini.ll
index 14385f042efb7c2..3d2e9a78043ee4a 100644
--- a/lld/test/wasm/init-fini.ll
+++ b/lld/test/wasm/init-fini.ll
@@ -26,10 +26,7 @@ entry:
 declare hidden void @externCtor()
 declare hidden void @externDtor()
 declare hidden void @__wasm_call_ctors()
-
-define i32 @__cxa_atexit(i32 %func, i32 %arg, i32 %dso_handle) {
-  ret i32 0
-}
+declare i32 @__cxa_atexit(i32 %func, i32 %arg, i32 %dso_handle)
 
 define hidden void @_start() {
 entry:
@@ -57,13 +54,17 @@ entry:
 ; CHECK:        - Type:            IMPORT
 ; CHECK-NEXT:     Imports:
 ; CHECK-NEXT:       - Module:          env
-; CHECK-NEXT:         Field:           externDtor
+; CHECK-NEXT:         Field:           __cxa_atexit
 ; CHECK-NEXT:         Kind:            FUNCTION
 ; CHECK-NEXT:         SigIndex:        0
 ; CHECK-NEXT:       - Module:          env
+; CHECK-NEXT:         Field:           externDtor
+; CHECK-NEXT:         Kind:            FUNCTION
+; CHECK-NEXT:         SigIndex:        1
+; CHECK-NEXT:       - Module:          env
 ; CHECK-NEXT:         Field:           externCtor
 ; CHECK-NEXT:         Kind:            FUNCTION
-; CHECK-NEXT:         SigIndex:        0
+; CHECK-NEXT:         SigIndex:        1
 ; CHECK:        - Type:            ELEM
 ; CHECK-NEXT:     Segments:
 ; CHECK-NEXT:       - Offset:
@@ -72,31 +73,31 @@ entry:
 ; CHECK-NEXT:         Functions:       [ 9, 11, 13, 17, 19, 21 ]
 ; CHECK-NEXT:   - Type:            CODE
 ; CHECK-NEXT:     Functions:
-; CHECK-NEXT:       - Index:           2
+; CHECK-NEXT:       - Index:           3
 ; CHECK-NEXT:         Locals:
-; CHECK-NEXT:         Body:            10031004100A100F1012100F10141003100C100F10161001100E0B
+; CHECK-NEXT:         Body:            10041005100A100F1012100F10141004100C100F10161002100E0B
 ; CHECK:            - Index:           22
 ; CHECK-NEXT:         Locals:
-; CHECK-NEXT:         Body:            02404186808080004100418088808000108780808000450D0000000B0B
+; CHECK-NEXT:         Body:            02404186808080004100418088808000108080808000450D0000000B0B
 ; CHECK-NEXT:   - Type:            CUSTOM
 ; CHECK-NEXT:     Name:            name
 ; CHECK-NEXT:     FunctionNames:
 ; CHECK-NEXT:       - Index:           0
-; CHECK-NEXT:         Name:            externDtor
+; CHECK-NEXT:         Name:            __cxa_atexit
 ; CHECK-NEXT:       - Index:           1
-; CHECK-NEXT:         Name:            externCtor
+; CHECK-NEXT:         Name:            externDtor
 ; CHECK-NEXT:       - Index:           2
-; CHECK-NEXT:         Name:            __wasm_call_ctors
+; CHECK-NEXT:         Name:            externCtor
 ; CHECK-NEXT:       - Index:           3
-; CHECK-NEXT:         Name:            func1
+; CHECK-NEXT:         Name:            __wasm_call_ctors
 ; CHECK-NEXT:       - Index:           4
-; CHECK-NEXT:         Name:            func2
+; CHECK-NEXT:         Name:            func1
 ; CHECK-NEXT:       - Index:           5
-; CHECK-NEXT:         Name:            func3
+; CHECK-NEXT:         Name:            func2
 ; CHECK-NEXT:       - Index:           6
-; CHECK-NEXT:         Name:            func4
+; CHECK-NEXT:         Name:            func3
 ; CHECK-NEXT:       - Index:           7
-; CHECK-NEXT:         Name:            __cxa_atexit
+; CHECK-NEXT:         Name:            func4
 ; CHECK-NEXT:       - Index:           8
 ; CHECK-NEXT:         Name:            _start
 ; CHECK-NEXT:       - Index:           9
diff --git a/llvm/lib/Transforms/Utils/LowerGlobalDtors.cpp b/llvm/lib/Transforms/Utils/LowerGlobalDtors.cpp
index 195c274ff18e2cd..f67a1eb53b8976a 100644
--- a/llvm/lib/Transforms/Utils/LowerGlobalDtors.cpp
+++ b/llvm/lib/Transforms/Utils/LowerGlobalDtors.cpp
@@ -140,6 +140,17 @@ static bool runImpl(Module &M) {
                         {PointerType::get(AtExitFuncTy, 0), VoidStar, VoidStar},
                         /*isVarArg=*/false));
 
+  // If __cxa_atexit is defined (e.g. in the case of LTO) and arg0 is not
+  // actually used (i.e. it's dummy/stub function as used in emscripten when
+  // the program never exits) we can simply return early and clear out
+  // @llvm.global_dtors.
+  if (auto F = dyn_cast<Function>(AtExit.getCallee())) {
+    if (F && F->hasExactDefinition() && F->getArg(0)->getNumUses() == 0) {
+      GV->eraseFromParent();
+      return true;
+    }
+  }
+
   // Declare __dso_local.
   Type *DsoHandleTy = Type::getInt8Ty(C);
   Constant *DsoHandle = M.getOrInsertGlobal("__dso_handle", DsoHandleTy, [&] {
diff --git a/llvm/test/Transforms/LowerGlobalDestructors/lower-global-dtors-unused.ll b/llvm/test/Transforms/LowerGlobalDestructors/lower-global-dtors-unused.ll
new file mode 100644
index 000000000000000..a6e7133b2947ee4
--- /dev/null
+++ b/llvm/test/Transforms/LowerGlobalDestructors/lower-global-dtors-unused.ll
@@ -0,0 +1,17 @@
+; RUN: opt -passes=lower-global-dtors -S < %s | FileCheck %s --implicit-check-not=llvm.global_dtors
+
+; Test that @llvm.global_dtors is completely removed if __cxa_atexit
+; is a no-op (i.e. doesn't use its first argument).
+
+declare void @orig_dtor()
+
+define i32 @__cxa_atexit(ptr, ptr, ptr) {
+  ret i32 0
+}
+
+ at llvm.global_dtors = appending global [1 x { i32, ptr, ptr }] [
+  { i32, ptr, ptr } { i32 0, ptr @orig_dtor, ptr null }
+]
+
+; CHECK-NOT: @llvm.global_dtors
+; CHECK-NOT: call void @orig_dtor()



More information about the llvm-commits mailing list