Skip to content

[C++20][Modules] Missed optimization: Unnecessary code emitted for empty module initializers #67582

Closed as not planned
@davidstone

Description

@davidstone

Given

a.cpp:

export module a;

and

b.cpp:

import a;

Then after compiling with

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:

import bounded;

In my compiler explorer output I see

_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

Metadata

Metadata

Assignees

No one assigned

    Labels

    clang:modulesC++20 modules and Clang Header ModulesduplicateResolved as duplicate

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions