Skip to content

Commit e01c7d5

Browse files
authored
[LowerGlobalDtors] Skip __cxa_atexit call completely when arg0 is unused (#68758)
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
1 parent 4554eac commit e01c7d5

File tree

4 files changed

+47
-18
lines changed

4 files changed

+47
-18
lines changed

lld/test/wasm/init-fini-no-gc.ll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ entry:
2525
}
2626

2727
define hidden i32 @__cxa_atexit(i32 %func, i32 %arg, i32 %dso_handle) {
28-
ret i32 0
28+
ret i32 %func
2929
}
3030

3131
@llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [

lld/test/wasm/init-fini.ll

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,7 @@ entry:
2626
declare hidden void @externCtor()
2727
declare hidden void @externDtor()
2828
declare hidden void @__wasm_call_ctors()
29-
30-
define i32 @__cxa_atexit(i32 %func, i32 %arg, i32 %dso_handle) {
31-
ret i32 0
32-
}
29+
declare i32 @__cxa_atexit(i32 %func, i32 %arg, i32 %dso_handle)
3330

3431
define hidden void @_start() {
3532
entry:
@@ -57,13 +54,17 @@ entry:
5754
; CHECK: - Type: IMPORT
5855
; CHECK-NEXT: Imports:
5956
; CHECK-NEXT: - Module: env
60-
; CHECK-NEXT: Field: externDtor
57+
; CHECK-NEXT: Field: __cxa_atexit
6158
; CHECK-NEXT: Kind: FUNCTION
6259
; CHECK-NEXT: SigIndex: 0
6360
; CHECK-NEXT: - Module: env
61+
; CHECK-NEXT: Field: externDtor
62+
; CHECK-NEXT: Kind: FUNCTION
63+
; CHECK-NEXT: SigIndex: 1
64+
; CHECK-NEXT: - Module: env
6465
; CHECK-NEXT: Field: externCtor
6566
; CHECK-NEXT: Kind: FUNCTION
66-
; CHECK-NEXT: SigIndex: 0
67+
; CHECK-NEXT: SigIndex: 1
6768
; CHECK: - Type: ELEM
6869
; CHECK-NEXT: Segments:
6970
; CHECK-NEXT: - Offset:
@@ -72,31 +73,31 @@ entry:
7273
; CHECK-NEXT: Functions: [ 9, 11, 13, 17, 19, 21 ]
7374
; CHECK-NEXT: - Type: CODE
7475
; CHECK-NEXT: Functions:
75-
; CHECK-NEXT: - Index: 2
76+
; CHECK-NEXT: - Index: 3
7677
; CHECK-NEXT: Locals:
77-
; CHECK-NEXT: Body: 10031004100A100F1012100F10141003100C100F10161001100E0B
78+
; CHECK-NEXT: Body: 10041005100A100F1012100F10141004100C100F10161002100E0B
7879
; CHECK: - Index: 22
7980
; CHECK-NEXT: Locals:
80-
; CHECK-NEXT: Body: 02404186808080004100418088808000108780808000450D0000000B0B
81+
; CHECK-NEXT: Body: 02404186808080004100418088808000108080808000450D0000000B0B
8182
; CHECK-NEXT: - Type: CUSTOM
8283
; CHECK-NEXT: Name: name
8384
; CHECK-NEXT: FunctionNames:
8485
; CHECK-NEXT: - Index: 0
85-
; CHECK-NEXT: Name: externDtor
86+
; CHECK-NEXT: Name: __cxa_atexit
8687
; CHECK-NEXT: - Index: 1
87-
; CHECK-NEXT: Name: externCtor
88+
; CHECK-NEXT: Name: externDtor
8889
; CHECK-NEXT: - Index: 2
89-
; CHECK-NEXT: Name: __wasm_call_ctors
90+
; CHECK-NEXT: Name: externCtor
9091
; CHECK-NEXT: - Index: 3
91-
; CHECK-NEXT: Name: func1
92+
; CHECK-NEXT: Name: __wasm_call_ctors
9293
; CHECK-NEXT: - Index: 4
93-
; CHECK-NEXT: Name: func2
94+
; CHECK-NEXT: Name: func1
9495
; CHECK-NEXT: - Index: 5
95-
; CHECK-NEXT: Name: func3
96+
; CHECK-NEXT: Name: func2
9697
; CHECK-NEXT: - Index: 6
97-
; CHECK-NEXT: Name: func4
98+
; CHECK-NEXT: Name: func3
9899
; CHECK-NEXT: - Index: 7
99-
; CHECK-NEXT: Name: __cxa_atexit
100+
; CHECK-NEXT: Name: func4
100101
; CHECK-NEXT: - Index: 8
101102
; CHECK-NEXT: Name: _start
102103
; CHECK-NEXT: - Index: 9

llvm/lib/Transforms/Utils/LowerGlobalDtors.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,17 @@ static bool runImpl(Module &M) {
140140
{PointerType::get(AtExitFuncTy, 0), VoidStar, VoidStar},
141141
/*isVarArg=*/false));
142142

143+
// If __cxa_atexit is defined (e.g. in the case of LTO) and arg0 is not
144+
// actually used (i.e. it's dummy/stub function as used in emscripten when
145+
// the program never exits) we can simply return early and clear out
146+
// @llvm.global_dtors.
147+
if (auto F = dyn_cast<Function>(AtExit.getCallee())) {
148+
if (F && F->hasExactDefinition() && F->getArg(0)->getNumUses() == 0) {
149+
GV->eraseFromParent();
150+
return true;
151+
}
152+
}
153+
143154
// Declare __dso_local.
144155
Type *DsoHandleTy = Type::getInt8Ty(C);
145156
Constant *DsoHandle = M.getOrInsertGlobal("__dso_handle", DsoHandleTy, [&] {
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
; RUN: opt -passes=lower-global-dtors -S < %s | FileCheck %s --implicit-check-not=llvm.global_dtors
2+
3+
; Test that @llvm.global_dtors is completely removed if __cxa_atexit
4+
; is a no-op (i.e. doesn't use its first argument).
5+
6+
declare void @orig_dtor()
7+
8+
define i32 @__cxa_atexit(ptr, ptr, ptr) {
9+
ret i32 0
10+
}
11+
12+
@llvm.global_dtors = appending global [1 x { i32, ptr, ptr }] [
13+
{ i32, ptr, ptr } { i32 0, ptr @orig_dtor, ptr null }
14+
]
15+
16+
; CHECK-NOT: @llvm.global_dtors
17+
; CHECK-NOT: call void @orig_dtor()

0 commit comments

Comments
 (0)