Skip to content

clang 20.1.2 extern template oddity #5329

Open
@jeremyd2019

Description

@jeremyd2019

https://inbox.sourceware.org/cygwin-apps/[email protected]/T/#me1f8e7940029ad203656f3b22ffa2cc948e9b5e4

/cc @mstorsjo @mati865 @tyan0

#include <stdio.h>
#include <string>

class foo
{
public:
  foo() { m_str.assign("hello"); }

  std::string m_str;

} bar;

int main()
{
  printf("%ld - %s\n", __cplusplus, bar.m_str.c_str());
  return 0;
}

With g++ -O2 -S -o test.gcc.s test.cpp

        .file   "test.cpp"
        .text
        .section        .text$_ZN3fooD1Ev,"x"
        .linkonce discard
        .align 2
        .p2align 4
        .globl  _ZN3fooD1Ev
        .def    _ZN3fooD1Ev;    .scl    2;      .type   32;     .endef
        .seh_proc       _ZN3fooD1Ev
_ZN3fooD1Ev:
.LFB1634:
        subq    $56, %rsp
        .seh_stackalloc 56
        .seh_endprologue
        movq    (%rcx), %rax
        leaq    -24(%rax), %rcx
        cmpq    .refptr._ZNSs4_Rep20_S_empty_rep_storageE(%rip), %rcx
        jne     .L6
.L1:
        addq    $56, %rsp
        ret
        .p2align 4,,10
        .p2align 3
.L6:
        movl    $-1, %edx
        lock xaddl      %edx, -8(%rax)
        testl   %edx, %edx
        jg      .L1
        leaq    47(%rsp), %rdx
        call    _ZNSs4_Rep10_M_destroyERKSaIcE
        jmp     .L1
        .seh_endproc
        .def    __main; .scl    2;      .type   32;     .endef
        .section .rdata,"dr"
.LC0:
        .ascii "%ld - %s\12\0"
        .section        .text.startup,"x"
        .p2align 4
        .globl  main
        .def    main;   .scl    2;      .type   32;     .endef
        .seh_proc       main
main:
.LFB1196:
        subq    $40, %rsp
        .seh_stackalloc 40
        .seh_endprologue
        call    __main
        movq    bar(%rip), %r8
        movl    $201703, %edx
        leaq    .LC0(%rip), %rcx
        call    printf
        xorl    %eax, %eax
        addq    $40, %rsp
        ret
        .seh_endproc
        .section .rdata,"dr"
.LC1:
        .ascii "hello\0"
        .section        .text.startup,"x"
        .p2align 4
        .def    _GLOBAL__sub_I_bar;     .scl    3;      .type   32;     .endef
        .seh_proc       _GLOBAL__sub_I_bar
_GLOBAL__sub_I_bar:
.LFB1635:
        pushq   %rsi
        .seh_pushreg    %rsi
        pushq   %rbx
        .seh_pushreg    %rbx
        subq    $40, %rsp
        .seh_stackalloc 40
        .seh_endprologue
        movq    .refptr._ZNSs4_Rep20_S_empty_rep_storageE(%rip), %rax
        leaq    bar(%rip), %rbx
        movl    $5, %r8d
        leaq    .LC1(%rip), %rdx
        movq    %rbx, %rcx
        addq    $24, %rax
        movq    %rax, bar(%rip)
.LEHB0:
        call    _ZNSs6assignEPKcm
.LEHE0:
        movq    .refptr.__dso_handle(%rip), %r8
        movq    %rbx, %rdx
        leaq    _ZN3fooD1Ev(%rip), %rcx
        addq    $40, %rsp
        popq    %rbx
        popq    %rsi
        jmp     __cxa_atexit
.L11:
        movq    %rax, %rsi
        movq    %rbx, %rcx
        call    _ZNSsD1Ev
        movq    %rsi, %rcx
.LEHB1:
        call    _Unwind_Resume
        nop
.LEHE1:
        .def    __gxx_personality_seh0; .scl    2;      .type   32;     .endef
        .seh_handler    __gxx_personality_seh0, @unwind, @except
        .seh_handlerdata
.LLSDA1635:
        .byte   0xff
        .byte   0xff
        .byte   0x1
        .uleb128 .LLSDACSE1635-.LLSDACSB1635
.LLSDACSB1635:
        .uleb128 .LEHB0-.LFB1635
        .uleb128 .LEHE0-.LEHB0
        .uleb128 .L11-.LFB1635
        .uleb128 0
        .uleb128 .LEHB1-.LFB1635
        .uleb128 .LEHE1-.LEHB1
        .uleb128 0
        .uleb128 0
.LLSDACSE1635:
        .section        .text.startup,"x"
        .seh_endproc
        .section        .ctors,"w"
        .align 8
        .quad   _GLOBAL__sub_I_bar
        .globl  bar
        .bss
        .align 8
bar:
        .space 8
        .ident  "GCC: (GNU) 13.3.1 20250404"
        .def    _ZNSs4_Rep10_M_destroyERKSaIcE; .scl    2;      .type   32;    .endef
        .def    printf; .scl    2;      .type   32;     .endef
        .def    _ZNSs6assignEPKcm;      .scl    2;      .type   32;     .endef
        .def    __cxa_atexit;   .scl    2;      .type   32;     .endef
        .def    _ZNSsD1Ev;      .scl    2;      .type   32;     .endef
        .def    _Unwind_Resume; .scl    2;      .type   32;     .endef
        .section        .rdata$.refptr.__dso_handle, "dr"
        .globl  .refptr.__dso_handle
        .linkonce       discard
.refptr.__dso_handle:
        .quad   __dso_handle
        .section        .rdata$.refptr._ZNSs4_Rep20_S_empty_rep_storageE, "dr"
        .globl  .refptr._ZNSs4_Rep20_S_empty_rep_storageE
        .linkonce       discard
.refptr._ZNSs4_Rep20_S_empty_rep_storageE:
        .quad   _ZNSs4_Rep20_S_empty_rep_storageE

with clang++ -O2 -S -o test.clang.s test.cpp

        .def    @feat.00;
        .scl    3;
        .type   0;
        .endef
        .globl  @feat.00
.set @feat.00, 0
        .file   "test.cpp"
        .def    __dtor_bar;
        .scl    3;
        .type   32;
        .endef
        .text
        .p2align        4                               # -- Begin function __dtor_bar
__dtor_bar:                             # @__dtor_bar
# %bb.0:
        movq    bar(%rip), %rax
        leaq    -24(%rax), %rcx
        leaq    _ZNSs4_Rep20_S_empty_rep_storageE(%rip), %rdx
        cmpq    %rdx, %rcx
        jne     .LBB0_1
.LBB0_2:
        retq
.LBB0_1:
        movl    $-1, %edx
        lock            xaddl   %edx, -8(%rax)
        testl   %edx, %edx
        jg      .LBB0_2
# %bb.3:
        movq    -16(%rax), %rdx
        addq    $25, %rdx
        jmp     _ZdlPvm                         # TAILCALL
                                        # -- End function
        .def    main;
        .scl    2;
        .type   32;
        .endef
        .globl  main                            # -- Begin function main
        .p2align        4
main:                                   # @main
.seh_proc main
# %bb.0:
        pushq   %rbp
        .seh_pushreg %rbp
        subq    $32, %rsp
        .seh_stackalloc 32
        leaq    32(%rsp), %rbp
        .seh_setframe %rbp, 32
        .seh_endprologue
        callq   __main
        movq    bar(%rip), %r8
        leaq    .L.str(%rip), %rcx
        movl    $201703, %edx                   # imm = 0x313E7
        callq   printf
        xorl    %eax, %eax
        addq    $32, %rsp
        popq    %rbp
        retq
        .seh_endproc
                                        # -- End function
        .def    _GLOBAL__sub_I_test.cpp;
        .scl    3;
        .type   32;
        .endef
        .p2align        4                               # -- Begin function _GLOBAL__sub_I_test.cpp
_GLOBAL__sub_I_test.cpp:                # @_GLOBAL__sub_I_test.cpp
.Lfunc_begin0:
.seh_proc _GLOBAL__sub_I_test.cpp
        .seh_handler __gxx_personality_seh0, @unwind, @except
# %bb.0:
        pushq   %rsi
        .seh_pushreg %rsi
        subq    $32, %rsp
        .seh_stackalloc 32
        .seh_endprologue
        leaq    _ZNSs4_Rep20_S_empty_rep_storageE+24(%rip), %rax
        movq    %rax, bar(%rip)
        leaq    bar(%rip), %rcx
.Ltmp0:
        leaq    .L.str.1(%rip), %rdx
        movl    $5, %r8d
        callq   _ZNSs6assignEPKcm
.Ltmp1:
# %bb.1:
        leaq    __dtor_bar(%rip), %rcx
        addq    $32, %rsp
        popq    %rsi
        jmp     atexit                          # TAILCALL
.LBB2_2:
.Ltmp2:
        movq    %rax, %rsi
        leaq    bar(%rip), %rcx
        callq   _ZNSsD2Ev
        movq    %rsi, %rcx
        callq   _Unwind_Resume
        int3
.Lfunc_end0:
        .seh_handlerdata
        .text
        .seh_endproc
        .section        .xdata,"dr"
        .p2align        2, 0x0
GCC_except_table2:
.Lexception0:
        .byte   255                             # @LPStart Encoding = omit
        .byte   255                             # @TType Encoding = omit
        .byte   1                               # Call site Encoding = uleb128
        .uleb128 .Lcst_end0-.Lcst_begin0
.Lcst_begin0:
        .uleb128 .Ltmp0-.Lfunc_begin0           # >> Call Site 1 <<
        .uleb128 .Ltmp1-.Ltmp0                  #   Call between .Ltmp0 and .Ltmp1
        .uleb128 .Ltmp2-.Lfunc_begin0           #     jumps to .Ltmp2
        .byte   0                               #   On action: cleanup
        .uleb128 .Ltmp1-.Lfunc_begin0           # >> Call Site 2 <<
        .uleb128 .Lfunc_end0-.Ltmp1             #   Call between .Ltmp1 and .Lfunc_end0
        .byte   0                               #     has no landing pad
        .byte   0                               #   On action: cleanup
.Lcst_end0:
        .p2align        2, 0x0
        .text
                                        # -- End function
        .bss
        .globl  bar                             # @bar
        .p2align        3, 0x0
bar:
        .zero   8

        .section        .rdata,"dr"
.L.str:                                 # @.str
        .asciz  "%ld - %s\n"

.L.str.1:                               # @.str.1
        .asciz  "hello"

        .section        .bss,"bw",discard,_ZNSs4_Rep20_S_empty_rep_storageE
        .globl  _ZNSs4_Rep20_S_empty_rep_storageE # @_ZNSs4_Rep20_S_empty_rep_storageE
        .p2align        4, 0x0
_ZNSs4_Rep20_S_empty_rep_storageE:
        .zero   32

        .section        .ctors,"dw"
        .p2align        3, 0x0
        .quad   _GLOBAL__sub_I_test.cpp
        .addrsig
        .addrsig_sym __dtor_bar
        .addrsig_sym __gxx_personality_seh0
        .addrsig_sym _GLOBAL__sub_I_test.cpp
        .addrsig_sym _Unwind_Resume
        .addrsig_sym bar
        .addrsig_sym _ZNSs4_Rep20_S_empty_rep_storageE

Issue is that clang is not importing _ZNSs4_Rep20_S_empty_rep_storageE from libstdc++ as it should.

relevant bits of headers:

  // Inhibit implicit instantiations for required instantiations,
  // which are defined via explicit instantiations elsewhere.
#if _GLIBCXX_EXTERN_TEMPLATE
  // The explicit instantiation definitions in src/c++11/string-inst.cc and
  // src/c++17/string-inst.cc only instantiate the members required for C++17
  // and earlier standards (so not C++20's starts_with and ends_with).
  // Suppress the explicit instantiation declarations for C++20, so C++20
  // code will implicitly instantiate std::string and std::wstring as needed.
# if __cplusplus <= 201703L && _GLIBCXX_EXTERN_TEMPLATE > 0
  extern template class basic_string<char>;
# elif ! _GLIBCXX_USE_CXX11_ABI
  // Still need to prevent implicit instantiation of the COW empty rep,
  // to ensure the definition in libstdc++.so is unique (PR 86138).
  extern template basic_string<char>::size_type
    basic_string<char>::_Rep::_S_empty_rep_storage[];
# elif _GLIBCXX_EXTERN_TEMPLATE > 0
  // Export _M_replace_cold even for C++20.
  extern template void
    basic_string<char>::_M_replace_cold(char *, size_type, const char*,
					const size_type, const size_type);
# endif

on Cygwin (where this is an issue), _GLIBCXX_USE_CXX11_ABI is 0. on MSYS2 (which doesn't have this issue apparently due to not having inline code trying to reference _S_empty_rep_storage[](?) has _GLIBCXX_USE_CXX11_ABI as 1. Both have _GLIBCXX_EXTERN_TEMPLATE 1.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions