<table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Issue</th>
<td>
<a href=https://github.com/llvm/llvm-project/issues/79691>79691</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>
Itanium ABI DSO ctor dtor codegen control
</td>
</tr>
<tr>
<th>Labels</th>
<td>
new issue
</td>
</tr>
<tr>
<th>Assignees</th>
<td>
</td>
</tr>
<tr>
<th>Reporter</th>
<td>
Pawday
</td>
</tr>
</table>
<pre>
# Issue
I have an example of code with static object initialization
```c++
struct Init_t {
Init_t(int val);
~Init_t();
};
Init_t init_0(0);
```
using option ``-fno-register-global-dtors-with-atexit`` i want the clang compiler to not emit any calls to **__cxa_atexit** or **atexit**
I assume this option will disable the calls because compiler manual says:
```sh
clang --help | grep "atexit"
```
```txt
-fno-register-global-dtors-with-atexit
Don't use atexit to register global destructors
-fno-register-global-dtors-with-cxa-atexit
Don't use __cxa_atexit to register global destructors
-fno-use-cxa-atexit
Alias for -fno-register-global-dtors-with-cxa-atexit
-fregister-global-dtors-with-atexit
Use atexit or __cxa_atexit to register global destructors
```
However the command
```sh
clang -S -O3 -fPIC -fno-register-global-dtors-with-atexit DSO_Life.cc
```
where DSO_Life.cc contains:
```c++
struct Init_t {
Init_t(int val);
~Init_t();
};
Init_t init_0(0);
```
will generate x86_64 assembly code with call to **__cxa_atexit** anyway
```asm
_GLOBAL__sub_I_DSO_Life.cc: # @_GLOBAL__sub_I_DSO_Life.cc
.cfi_startproc
# %bb.0:
pushq %rbx
.cfi_def_cfa_offset 16
.cfi_offset %rbx, -16
movq init_0@GOTPCREL(%rip), %rbx
movq %rbx, %rdi
xorl %esi, %esi
callq _ZN6Init_tC1Ei@PLT
movq _ZN6Init_tD1Ev@GOTPCREL(%rip), %rdi
leaq __dso_handle(%rip), %rdx
movq %rbx, %rsi
popq %rbx
.cfi_def_cfa_offset 8
jmp __cxa_atexit@PLT # TAILCALL
```
LLVM IR also contains call to **__cxa_atexit**
```sh
clang -S -emit-llvm -O3 -fPIC -fno-register-global-dtors-with-atexit DSO_Life.cc
```
```llvm
; Function Attrs: nofree nounwind
declare i32 @__cxa_atexit(ptr, ptr, ptr) local_unnamed_addr #2
; Function Attrs: uwtable
define internal void @_GLOBAL__sub_I_DSO_Life.cc() #3 section ".text.startup" {
tail call void @_ZN6Init_tC1Ei(ptr noundef nonnull align 1 dereferenceable(1) @init_0, i32 noundef 0)
%1 = tail call i32 @__cxa_atexit(ptr nonnull @_ZN6Init_tD1Ev, ptr nonnull @init_0, ptr nonnull @__dso_handle) #4
ret void
}
```
# Related behavior
option ``-fno-use-cxa-atexit`` will replace call to **__cxa_atexit** with call to atexit
```sh
clang -S -emit-llvm -O3 -fPIC -fno-register-global-dtors-with-atexit DSO_Life.cc
```
Note: DSO_Life.cc changed to
```c++
struct Init_t {
Init_t(int val);
~Init_t();
};
Init_t init_0(0);
Init_t init_1(0);
Init_t init_2(0);
Init_t init_3(0);
Init_t init_4(0);
Init_t init_5(0);
Init_t init_6(0);
Init_t init_7(0);
```
```llvm
; Function Attrs: uwtable
define internal void @_GLOBAL__sub_I_DSO_Life.cc() #4 section ".text.startup" {
tail call void @Init_t::Init_t(int)(ptr noundef nonnull align 1 dereferenceable(1) @init_0, i32 noundef 0)
%1 = tail call i32 @atexit(ptr nonnull @__dtor_init_0) #3
tail call void @Init_t::Init_t(int)(ptr noundef nonnull align 1 dereferenceable(1) @init_1, i32 noundef 0)
%2 = tail call i32 @atexit(ptr nonnull @__dtor_init_1) #3
tail call void @Init_t::Init_t(int)(ptr noundef nonnull align 1 dereferenceable(1) @init_2, i32 noundef 0)
%3 = tail call i32 @atexit(ptr nonnull @__dtor_init_2) #3
tail call void @Init_t::Init_t(int)(ptr noundef nonnull align 1 dereferenceable(1) @init_3, i32 noundef 0)
%4 = tail call i32 @atexit(ptr nonnull @__dtor_init_3) #3
tail call void @Init_t::Init_t(int)(ptr noundef nonnull align 1 dereferenceable(1) @init_4, i32 noundef 0)
%5 = tail call i32 @atexit(ptr nonnull @__dtor_init_4) #3
tail call void @Init_t::Init_t(int)(ptr noundef nonnull align 1 dereferenceable(1) @init_5, i32 noundef 0)
%6 = tail call i32 @atexit(ptr nonnull @__dtor_init_5) #3
tail call void @Init_t::Init_t(int)(ptr noundef nonnull align 1 dereferenceable(1) @init_6, i32 noundef 0)
%7 = tail call i32 @atexit(ptr nonnull @__dtor_init_6) #3
tail call void @Init_t::Init_t(int)(ptr noundef nonnull align 1 dereferenceable(1) @init_7, i32 noundef 0)
%8 = tail call i32 @atexit(ptr nonnull @__dtor_init_7) #3
ret void
}
```
It also will extract dtor for each global object like that:
```llvm
; Function Attrs: nounwind uwtable
define internal void @__dtor_init_0() #2 section ".text.startup" {
tail call void @Init_t::~Init_t()(ptr @init_0)
ret void
}
; Function Attrs: nounwind uwtable
define internal void @__dtor_init_1() #2 section ".text.startup" {
tail call void @Init_t::~Init_t()(ptr @init_1)
ret void
}
; Function Attrs: nounwind uwtable
define internal void @__dtor_init_2() #2 section ".text.startup" {
tail call void @Init_t::~Init_t()(ptr @init_2)# Proposal
* remove mention of **_cxa_atexit** for ``-fregister-global-dtors-with-atexit`` and ``-fno-register-global-dtors-with-atexit`` options such that
```sh
clang --help | grep "atexit"
```
will produce
<pre>
-fno-register-global-dtors-with-atexit
Don't use atexit <del>or __cxa_atexit</del> to register global destructors
-fno-use-cxa-atexit Don't use __cxa_atexit <del>for calling</del> <ins>to register global</ins> destructors
-fregister-global-dtors-with-atexit
Use atexit <del>or __cxa_atexit</del> to register global destructors <ins>instead of __cxa_atexit</ins>
</pre>
* add ``-fno-register-global-dtors-with-cxa-atexit`` option as alias for existing ``-fno-use-cxa-atexit``
* add ``-fno-use-atexit`` option as alias for existing ``-fno-register-global-dtors-with-atexit``
* make codegen emit separated procedures for construct and destruct global objects in the translation unit if options
``-fno-use-atexit`` and ``-fno-use-cxa-atexit`` are specified
Final output for
```sh
clang --help | grep "atexit"
```
<pre>
-fno-register-global-dtors-with-atexit
Don't use atexit to register global destructors
-fno-register-global-dtors-with-cxa-atexit
Don't use __cxa_atexit to register global destructors
-fno-use-atexit
Alias for -fno-register-global-dtors-with-atexit
-fno-use-cxa-atexit
Alias for -fno-register-global-dtors-with-cxa-atexit
-fregister-global-dtors-with-atexit
Use atexit to register global destructors instead of __cxa_atexit
</pre>
ret void
}
; Function Attrs: nounwind uwtable
define internal void @__dtor_init_3() #2 section ".text.startup" {
tail call void @Init_t::~Init_t()(ptr @init_3)
ret void
}
; Function Attrs: nounwind uwtable
define internal void @__dtor_init_4() #2 section ".text.startup" {
tail call void @Init_t::~Init_t()(ptr @init_4)
ret void
}
; Function Attrs: nounwind uwtable
define internal void @__dtor_init_5() #2 section ".text.startup" {
tail call void @Init_t::~Init_t()(ptr @init_5)
ret void
}
```
(it can be usefull for reflection, i am gonna use that if it's legal)
# GCC behavior
Note: DSO_Life.cc changed to
```c++
struct Init_t {
Init_t(int val);
~Init_t();
};
Init_t init_0(0);
Init_t init_1(1);
```
GCC 13.2.1 also have option ``-fno-use-cxa-atexit``
Command:
```sh
gcc -S -O3 -fPIC -fno-use-cxa-atexit DSO_Life.cc
```
Because GCC does not have -fno-register-global-dtors-with-atexit option
it will produce construction and destruction symbols for all objects in the TU without the calls to runtime procedures
```asm
_GLOBAL__sub_I_DSO_Life.cc:
subq $8, %rsp
movq init_0@GOTPCREL(%rip), %rdi
xorl %esi, %esi
call Init_t::Init_t(int)@PLT ; ctor init_0
movq init_1@GOTPCREL(%rip), %rdi
movl $1, %esi
addq $8, %rsp
jmp Init_t::Init_t(int)@PLT ; ctor init_1
_GLOBAL__sub_D_DSO_Life.cc:
subq $8, %rsp
movq init_1@GOTPCREL(%rip), %rdi
call Init_t::~Init_t()@PLT ; dtor init_1
movq init_0@GOTPCREL(%rip), %rdi
addq $8, %rsp
jmp Init_t::~Init_t()@PLT ; dtor init_0
```
# Proposal
* remove mention of **_cxa_atexit** for ``-fregister-global-dtors-with-atexit`` and ``-fno-register-global-dtors-with-atexit`` options such that
```sh
clang --help | grep "atexit"
```
will produce
<pre>
-fno-register-global-dtors-with-atexit
Don't use atexit <del>or __cxa_atexit</del> to register global destructors
-fno-use-cxa-atexit Don't use __cxa_atexit <del>for calling</del> <ins>to register global</ins> destructors
-fregister-global-dtors-with-atexit
Use atexit <del>or __cxa_atexit</del> to register global destructors <ins>instead of __cxa_atexit</ins>
</pre>
* add ``-fno-register-global-dtors-with-cxa-atexit`` option as alias for existing ``-fno-use-cxa-atexit``
* add ``-fno-use-atexit`` option as alias for existing ``-fno-register-global-dtors-with-atexit``
* make codegen emit separated procedures for construct and destruct global objects in the translation unit if options
``-fno-use-atexit`` and ``-fno-use-cxa-atexit`` are specified
Final output for
```sh
clang --help | grep "atexit"
```
<pre>
-fno-register-global-dtors-with-atexit
Don't use atexit to register global destructors
-fno-register-global-dtors-with-cxa-atexit
Don't use __cxa_atexit to register global destructors
-fno-use-atexit
Alias for -fno-register-global-dtors-with-atexit
-fno-use-cxa-atexit
Alias for -fno-register-global-dtors-with-cxa-atexit
-fregister-global-dtors-with-atexit
Use atexit to register global destructors instead of __cxa_atexit
</pre>
# Thank You
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJzsW1tv2zoS_jXKy8CBRfn64AfHSboBsqdF27PA7otASyObpxSpJSnH2Yf97QuSki05ru24OfHiIEEQuxxe5htyLpxhqdZsIRAnQf8m6N9e0dIspZp8oU8pfb6ay_R5EpAIHrQuMejeBt3pAyzpCoEKwDXNC44gM0hkivDEzBK0oYYlIOd_YGKACWYY5ew_1DAp_ATV30HX_yYBubG_rlUbVSYGHgQzsYFgWDVXDQEZMWFgRXlAxkFUEwHgv5sOTUowvN1-98z7iS1bcTcgo26re81Sc0SpmViALCz_4MmdTMiOwgXTBlVnweWc8k5qpNIdK4IONbhmxvcFBk9UGDBLhIRTsYBE5gXjqMBIENIA5swAFc-QUM61bQ3INCDTOE7WNK7mci0gVUVrtrbgAdW6zBHMkuma6SfGOaRM0zlHz4dbaI4JLTVu-cmpKCkHTZ91EE337pVe-gaPpNNZIi8gGM5gobCAgJCaMXJAoptGszbVDp4o0c127_-5lSIgQwMWlR9hpVlPC35aSNEfMqn0iasna3oGB839O40Pz0ipsbEiVLQpZ1RDJtXrue1kvyzZ37cSler1yPYeg7_JJ1xZNVi6M5hTkR4-bd-g8zmCTvblYXbigYHbb5_jR5bhdZLs5eRpiQqbvSCRwlAmGhrwKjsFcEFT5RR9gQIVNQjr0SAe9KxBwHzOnxsm2ur_QTtDxfMTfd6rs1TnviX-9Pj5ZvoYx7qcxw9xU9LRFKzPCHrdA52qycfXScZibagyhZJ1sx1N-vP5dbdhicZFqZf_DrrjgPTVfN2eIcUsTjIayyzTaCActMlVczWSzKDT6JHLlZ22knSv--nz9y-zr3ePbov6ihVW8mQGO-tWw7Zz2m8p29DXUnFPR80quv1W0-022Anif_028Js9C-9Y0Ot-efy-u8i2z214tzrCY4MHjtQNj1Mt4yUVKce9I46ganBdyOLEPRhtqH_khWOicdAcSndMvk8fHmfTx8cDB_vx8R9_h4evQLmWGw09eoxP8WDfoGP9b4fzVf725mWn0S5SNUU3cF-KxPnnqTHK2hsQMlOIIGQpnlhtDVNMOFUILCJOo1owR4VRdo-aH2PgMqE8LoWgOaYxTVMbNkSkxdNeBsonY4OEeuGMCQQmDCpBOawkS4_otDNpdq0INPq5A0KuDa7NtVPxsggIaZpLQxn3G7mZvq0NDqITSYoZCClEyTlQzhYCQkhRYYYKRYKOcTIKHQe9bm02Z05w9XhnQ6ulA9IPIYhuGzz8VMabhVsMOlX0Ym922C69O7KlhU5QvZobhcaJYOsNDhwoEsFX5NRgCnNc0hWzgWGjw8totR1UVKGp8xcKC04TPO4UWq6jGTZcXsN-kwbt-W058iUVC0zByPNvHe_lzZvE8BCRHCJGh4i9Q8T-IeLgEHH4IjB5Azv4Zmaod74ZqrY1mgbRtHkGnNO8gE36qTWKrdLE9dTe-l4AVngEFjkfVnhBWOQIrOh8WOSCsKIjsHrnw4ouCKt3BFb_fFi9C8LqH4E1OB9W_4KwBkdgDc-HNbggrOERWKPzYQ13Yb0manww_v7koj5cG0UTA3Zul1VCmizr_E2Vu-XsB4JZUuMu9OddaPxN5kSP3nZltRMnb-PE24GaF3PDJ4-PifRPgRm-K8zwUjDJu8IkrimCL0oWUlPevjpNQWEuVwg5CseHzOo7z4srj1WM-hJ1ar6fivScMoG_smnQZbL0SnfC3eq8_LszAIWSaZlgvd-zQmEQ3dXCf1U-fk_aPYhmKfIgutvJFgfRLCD3nvQLOfGDqfbN2nb37AFiYtFaN4hmLsF795IB389T9xYK3jKT_gZC2mJhQhukqT3NL-fyXTZ7HZD75nZvNYOmp57dF7mEKuVAtXWTVaUCcM20YWJxLBPR5mC3d2udvQvtW-dExdsundMf6JLkCxS-KKexoMplWQolE0xLhX69RIoqZWC1vd6PtvvUwISrbRhFheau_gmlYAZYVmt7U0n3w92xJnvTOFQh6AITljFMW1vq_t4za5ZlaYrSWO7_DJOya0A-6nlt23VuLW-njvfT6uBPf_5vyoZH7NjPzNdBk_XOUUz0rlFMdKlgrfeuMHtbmO-Ksv-uKPsnbOaOXSUjZiChAuZozU9mr4NWjxVm3LPr7plAc1hIIaizUTZwtP7F2uuhBo4Ln7Fu-fkIPs1mm8LBXzmPHh4tmVtJhNE1uQ795dg9bTqtfNKcZla9XzjybGaRJHueMeyY9NNKHjfV6x0LIJWo3VMix_2JlRUP0k_GDDQvBdv4xkVajRDH_ls_53PJvVOxurAT7nz_3VWKZGkab42s9S-FYTk2Qqm9ojrtfUE9dKzLua9G90abWnWxob6msP_Kwn3QHR_KIFXl7egGrH-rj-s-vsLT-crlyvPVC_dwRdP0gCh8Cf61LId79uL21_fiFZj3yLptKrZsp7tsn38CXivL4yx1D0XPH8mKj2TFR7LiI1nxkaz4SFZ8JCv-mskK99RxScUP-Kcsm4SrdBKl42hMr3ASDruDqD8cDIdXywkmg0E0wqzbTyjpp8PREIf9PiVR1BuN5sn8ik1Il_S6IRmGJOxFw2vsDaM0ouk4TGk0DknQ62JOGb_mfJVfS7W4YlqXOBmOB-PwitM5cu3-4wchAp_AEa3u9G-v1MSO6czLhQ56Xc600dtZDDMcJw-GClbmML15sJcWHza6iKc2TIkURkl-VSo-WRpTuFfV5D4g9wtmluX8OpF5QO5dGdF_dAolrUGyfsByowNy77j9XwAAAP__H2tX9A">