<table border="1" cellspacing="0" cellpadding="8">
    <tr>
        <th>Issue</th>
        <td>
            <a href=https://github.com/llvm/llvm-project/issues/67582>67582</a>
        </td>
    </tr>

    <tr>
        <th>Summary</th>
        <td>
            [C++20][Modules] Missed optimization: Unnecessary code emitted for empty module initializers
        </td>
    </tr>

    <tr>
      <th>Labels</th>
      <td>
            new issue
      </td>
    </tr>

    <tr>
      <th>Assignees</th>
      <td>
      </td>
    </tr>

    <tr>
      <th>Reporter</th>
      <td>
          davidstone
      </td>
    </tr>
</table>

<pre>
    Given

```cpp
export module a;
```

and

```cpp
import a;
```

Then after compiling with

```cmd
clang++ -std=c++20 -O3 -x c++-module -fmodule-output=a.pcm --precompile -c a.cpp
clang++ -std=c++20 -O3 -fmodule-file=a=a.pcm -S b.cpp -emit-llvm
```

We end up with the generated code for `b` as:

```
; ModuleID = '/home/david/test/b.cpp'
source_filename = "/home/david/test/b.cpp"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

@llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @_GLOBAL__sub_I_b.cpp, ptr null }]

declare void @_ZGIW1a() local_unnamed_addr

; Function Attrs: uwtable
define internal void @_GLOBAL__sub_I_b.cpp() #0 section ".text.startup" {
entry:
  tail call void @_ZGIW1a()
  ret void
}

attributes #0 = { uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }

!llvm.linker.options = !{}
!llvm.module.flags = !{!0, !1, !2, !3}
!llvm.ident = !{!4}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 8, !"PIC Level", i32 2}
!2 = !{i32 7, !"PIE Level", i32 2}
!3 = !{i32 7, !"uwtable", i32 2}
!4 = !{!"clang version 18.0.0 (https://github.com/llvm/llvm-project.git cd7f1714dea16261e0362b80958296f9782d625e)"}
```

The module initializer for `a` is empty, yet we still do a tail call to it. I would expect there to be no reference to the initializer of module `a` anywhere in the generated code for `b`.

It actually seems to be significantly worse than this. For instance, in my local compiler explorer instance I have an umbrella module `bounded` that imports various sub-modules. I see an unnecessary initializer of the umbrella module (which internally has unnecessary initializers for all its imports), and then I see unnecessary initializers of all of those sub-modules:

```cpp
import bounded;
```

In my compiler explorer output I see

```asm
_GLOBAL__sub_I_example.cpp:             # @_GLOBAL__sub_I_example.cpp
        push    rax
        call initializer for module bounded@PLT
        call    initializer for module bounded.arithmetic.operators@PLT
        call    initializer for module bounded.arithmetic.operators_builtin@PLT
        call    initializer for module bounded.arithmetic.round_up_divide@PLT
        call    initializer for module bounded.abs@PLT
        call    initializer for module bounded.builtin_min_max_value@PLT
        call    initializer for module bounded.cast@PLT
        call    initializer for module bounded.check_in_range@PLT
        call    initializer for module bounded.clamp@PLT
        call    initializer for module bounded.comparison@PLT
        call    initializer for module bounded.comparison_builtin@PLT
        call    initializer for module bounded.construct@PLT
        call    initializer for module bounded.construct_at@PLT
        call    initializer for module bounded.copy@PLT
        call    initializer for module bounded.hash@PLT
        call    initializer for module bounded.integer@PLT
        call    initializer for module bounded.integer_tombstone_traits@PLT
        call    initializer for module bounded.is_bounded_integer@PLT
        call    initializer for module bounded.lazy_init@PLT
        call    initializer for module bounded.literal@PLT
        call    initializer for module bounded.log@PLT
        call    initializer for module bounded.minmax@PLT
        call    initializer for module bounded.normalize@PLT
        call    initializer for module bounded.number_of@PLT
        call    initializer for module bounded.pow@PLT
        call    initializer for module bounded.representation_digits@PLT
        call    initializer for module bounded.size_of@PLT
        call    initializer for module bounded.std_iterator@PLT
        call    initializer for module bounded.stream@PLT
        pop     rax
        jmp     initializer for module bounded.to_integer@PLT  # TAILCALL
```
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJysWF9vo7oS_zTui0UEJn_IQx6a5mZVqatzpLNXR7ovyOAh-KyxkT3kz376KxtIk2671REbtWDw_H6eGY89Y7hz8qABNmSxJYvdA--wNnYj-FEKh0bDQ2HEZfNFHkGTeEfix-G6jPu_sm37N3BujUXaGNEpoJyk2zeSt3Cuxa_ZZBPYPqH5VoOmvEKwtDRNK5XUB3qSWL9P3gyDlorrA2FbwrY0cihIuiv7RxbT6I-URmc6vIgGe6Kqb0Smw7ZDku74rC0bGkWthX5soFFJ-exqwuejjJyVVOAZX1n_ooUnohE0EiOljs0vvPA3UNCCdm0wnWIN9AAaLEcQtDQCaGUsJcu4IMuYckfSx3f9MzymW_o16PW8oyTdUcJWhO1r0wBh-xAYhO0RHBK2D1p6gQB1prMl5N4czRsY0OwzNOvRyO0BkAqOXPGL6XDEQ9SQ9BGilq1ikj6mLFz8Y3L_6FvLebhEcmxUmQclLIt05hvLEbOcR38lLHs7PlrZqqvu52yZL-dRp79rc9KRkro7RwfdXVHDdR77SZodlCm4yks01gUK3raghQ_LvouSxTahZ0pWWypTRtgTbdEON0pWO7LYeZkP-0ckXS4W6eLaMY_zLy9_bB9f8tx1Rf6cD77tu3Wn1EB-q7WAUnEL9GikCBT_-_L8d8KJd8qaKlNylXfaT6XIuRD2zuJ0S_edLlEaTR8RrQ8r2p2QFwpG-kpqoFIjWM3V6zDvahrGJCyNqYOelTA2QzjjzCG32PlI8dYP241Ge7lGMqXIpaIlV-p9a0YxCxgEBiNWd_7giFYWHYLrFQkxsNqOVnmFGqkjBQeuoiP4WY5OUmDtoyHdEcbioCNj2kRoedtKfYgafiOAtoNBxiEvv0etNdgzFV1VgY2c_AFX8WyQ7WMzKtvu2nXOltFyft9fAcfOgrsKEbYtG3Mk7Mm3zlnfqM7O9q2mOfcN5-DaYH3rnK1G9k7D3dhhe5FlPyF3LiQsCQtBSf0d7My0fibdsJoSP3tX-UGy3wNnleKHOzmWxD58CUuS4c6Ge_oThxSg8R48_1mx-EbCr6CRlzB2Kmtu89H17CmssPntOMkbcPYK_vP5ib7AEdQNlt1i2Rvs6hb7n19j04-x42L7ADm_dwhhLGQkegTr_PJKslk8iylhWY3YhqzA9oTtDxLrrpiVpiFsH1JPf_Oh-g-UODtIpKVYVckqmQvgyZItE4jTJSuyeL3I2HpZrVcZE0u2AL_02I1aH2TxsWyQWqLkSv4AOyYt7pOWdBSaFi_e0AsgPQF1KJWiwlB-s_bRUIkz-kxPplOCwrmFEn1GtOD7CqDaUAsVWNBleOWz5e2ophp1Gcfm-nIKBFJ_kltnt1Y9I-UldlypC3UAjRsU8PWWrGTJNaoLPRnrgGLNPbd0M7o3lkrtkOsSwrRq2lz6vXioccB6u5Sx8CpJn2nNj0C5pl1TWFCK35hRmE4LEN4YrDnSvrhy9MitNJ2jriuGOsd53znoibSGEpzj9vLWQ94NP43DslMty_q636sLrbn7iMYFx_k5k-hGjUK0PFGuhR9CD7p8yGCqQBAUMg5u7fioxnlbYI6u-WWZ-Rzm4Gfv97Vgr-W7o3E31G1vUh6cedMqCIkvfaS3P8LSd3LkLWDIZP2v7Vzt75af7zvCcni7nIapGo2ex3--fHsHRuknyBm3EusGUJYz0_rVYKz7zXR50UmFUv8eWutf5V2bC3mUAqaQFlMsHYzKG__Pz_mRq26KNiV3OAVeQ_k9lzq3XB8m6aF4007Bm6blVjozZb5fSX5D8JRGO7RdOcm9I0fOp9G0lwnwmrt6Atzv5wew0xlyNE0RPirkaLnEKetIunxo5tP1U_zHJfcyUzgkguVqCoM5TEA3Ujf8PIFAG9uE3ikcXVOAzU01gaM1pwloC60FBxq5P3jkQh6mxZk_Ekyzx6HIQ2ygmRKjDi3w5j2C1rTh_lMJ8E_Td3zCjOZ-CfU1yLfH55enx5eXNxXNg9ikYp2u-QNskuV6kaWrVTZ_qDccVnESz4sVjxdxCmsmIM5W62UFIo7Lgj3IDYtZGq_ZKsniNE5nrBJ8lQghYF1BtkjIPIaGSzULRzpjDw_SuQ42y9UiYw-KF6Bc-EzJmIYTDZ3-bLHYPdhNOKAU3cHPtZIO3SsLSlTh--bT-PWNLHZksf06VIqLHf0qnQNB_Wm1kT9C6Pi67L83hWeo9aGR6At_78VwHnnn3OIeOqs2_-5IRdg-mOMI2wdz_x8AAP__XgSaCQ">