diff --git a/lld/test/wasm/init-fini-no-gc.ll b/lld/test/wasm/init-fini-no-gc.ll index 0a22ebfb1454c0..d6ac07f74cb303 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 14385f042efb7c..3d2e9a78043ee4 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 195c274ff18e2c..f67a1eb53b8976 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(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 00000000000000..a6e7133b2947ee --- /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()