Skip to content

[LowerGlobalDtors] Skip __cxa_atexit call completely when arg0 is unused #68758

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 23, 2023

Conversation

sbc100
Copy link
Collaborator

@sbc100 sbc100 commented Oct 11, 2023

In emscripten we have a build mode (the default actually) where the runtime never exits and therefore __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.

Fixes: emscripten-core/emscripten#19993

@llvmbot
Copy link
Member

llvmbot commented Oct 11, 2023

@llvm/pr-subscribers-lld-wasm
@llvm/pr-subscribers-lld

@llvm/pr-subscribers-llvm-transforms

Author: Sam Clegg (sbc100)

Changes

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.

Fixes: emscripten-core/emscripten#19993


Full diff: https://github.com/llvm/llvm-project/pull/68758.diff

2 Files Affected:

  • (modified) llvm/lib/Transforms/Utils/LowerGlobalDtors.cpp (+11)
  • (added) llvm/test/Transforms/LowerGlobalDestructors/lower-global-dtors-unused.ll (+17)
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
+}
+
+@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()

@sbc100
Copy link
Collaborator Author

sbc100 commented Oct 11, 2023

I think another approach would be to somehow run GlobalDCE (or something like that) after we generate all the __cxa_atexit calls.. I couldn't find a way to do that from WebAssemblyPassConfig::addIRPasses though, and this seems like a more direct appoach.

@sbc100
Copy link
Collaborator Author

sbc100 commented Oct 11, 2023

@kripken

Copy link
Collaborator

@tlively tlively left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, but I wonder if anyone else has any opinions, since I'm not too familiar with this part of the code base.

@tlively
Copy link
Collaborator

tlively commented Oct 16, 2023

Those test failures on CI look real, though.

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.
@sbc100
Copy link
Collaborator Author

sbc100 commented Oct 19, 2023

Adding @sunfishcode who originally wrote this pass and @yln who made it into a generic pass

@sunfishcode
Copy link
Member

The change here looks good to me. I like how it does automatic detection so it avoids needing a flag.

@sbc100 sbc100 merged commit e01c7d5 into llvm:main Oct 23, 2023
@sbc100 sbc100 deleted the skip_atexit branch October 23, 2023 17:08
sbc100 added a commit to sbc100/emscripten that referenced this pull request Oct 23, 2023
sbc100 added a commit to sbc100/emscripten that referenced this pull request Oct 23, 2023
sbc100 added a commit to sbc100/emscripten that referenced this pull request Oct 24, 2023
sbc100 added a commit to emscripten-core/emscripten that referenced this pull request Oct 24, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Global destructors not optimized away even with LTO
4 participants