Open llvmbot opened 6 years ago
mentioned in issue llvm/llvm-bugzilla-archive#39250
mentioned in issue llvm/llvm-bugzilla-archive#39233
Bug llvm/llvm-bugzilla-archive#39250 has been marked as a duplicate of this bug.
Bug llvm/llvm-bugzilla-archive#39233 has been marked as a duplicate of this bug.
Does "InnerCls Cls
::innerCls" not fall into the first category?
Initialization of innerCls is unordered:
Dynamic initialization of a non-local variable with static storage duration is unordered if the variable is an implicitly or explicitly instantiated specialization
I think Clang fails to be standards compliant - I believe since C++11, it should be guaranteed to work.
Please cite the part of the standard that you think justifies your claim.
To my reading, the standard says that you can use
once an ios_base::Init object is constructed (see [iostream.objects.overview]p3) and there's no guarantee you can do so earlier than that. In this case there is no guarantee that an object of type ios_base::Init is constructed before Cls ::innerCls runs, because Cls ::innerCls has unordered initialization (see [basic.start.dynamic]).
From N4750 - 30.4.2 [iostream.objects.overview] clause 3
"The results of including
in a translation unit shall be as if defined an instance of ios_base::Init with static storage duration."
...Of course you mention [basic.start.dynamic] and perhaps I'm misunderstanding what category OP's code falls into, but it states:
"An explicitly specialized non-inline static data member or variable template specialization has ordered initialization."
Does InnerCls Cls<T>::innerCls
not fall into the first category?
I think Clang fails to be standards compliant - I believe since C++11, it should be guaranteed to work.
Please cite the part of the standard that you think justifies your claim.
To my reading, the standard says that you can use <iostream>
once an ios_base::Init
object is constructed (see [iostream.objects.overview]p3) and there's no guarantee you can do so earlier than that. In this case there is no guarantee that an object of type ios_base::Init
is constructed before Cls<int>::innerCls
runs, because Cls<int>::innerCls
has unordered initialization (see [basic.start.dynamic]).
I think Clang fails to be standards compliant - I believe since C++11, it should be guaranteed to work.
From my own testing of Clang 6.0.1 I found that if you pass -std=c++11
AND -stdlib=libc++
then it will work without crashing. If you allow it to link to libstdc++ then it fails.
I believe there is something GCC defines in order for the <iostream>
header to initialise correctly.
Aha, and that is specifically because Cls is a template class?
Yes, exactly so. Compare: https://godbolt.org/z/kSd14o
struct A { A(); } a;
struct B { B(); };
template<typename T> B b;
void *p = &b<int>;
struct C { C(); } c;
GCC generates a single global initializer function to initialize ::a
, then ::c
, then to conditionally initialize ::b<int>
if no other TU got there first.
Clang generates two initializer functions: one to initialize ::a
and ::c
, and another to initialize ::b<int>
. (We generate two separate functions so that we can put the ::b<int>
initializer in a comdat with ::b
, which is a neat optimization that GCC lacks.)
And Clang happens to register the initializer function for ::a
and ::c
after that for ::b<int>
. We could change that, so the initializers for unordered variables happen after the initializers for ordered variables in the same TU. That's probably a good idea, even if for no other reason than to support code such as that in comment#0.
This program is not guaranteed to work. Your global variable Cls
::innerCls has unordered initialization, and so is permitted to be initialized before the ios_base::Init object in the same TU is constructed.
Aha, and that is specifically because Cls is a template class?
This program is not guaranteed to work. Your global variable Cls<int>::innerCls
has unordered initialization, and so is permitted to be initialized before the ios_base::Init
object in the same TU is constructed. Therefore there is no guarantee that std::cout
is initialized prior to its use in the constructor for that variable.
Just tried with clang 8.0.0 r342058 on Fedora 28, same result:
.section .init_array,"aGw",@init_array,Cls<int>::innerCls,comdat
.p2align 3
.quad __cxx_global_var_init.1
.section .init_array,"aw",@init_array
.p2align 3
.quad _GLOBAL__sub_I_pr38914_1.cpp
.ident "clang version 8.0.0 (trunk 342058)"
E.g. _GLOBAL__sub_I_pr38914_1.cpp
is emitted after __cxx_global_var_init.1
, while it should be the other way around.
Rebinning to clang.
I can reproduce this on both Fedora 28 and Ubuntu 18.04, but not on FreeBSD. It seems to be because clang emits the initialization of InnerCls into the .init_array segment before the GLOBAL iostream initializer, like gcc does.
I just downloaded the 6.0.1 release onto Ubuntu 18.04, and tried it there. Works fine for me; I guess I'm just lucky.
Dimitry, and you using any particular compiler flags?
No, just:
clang++ -O2 pr38914-1.cpp -o pr38914-1
This is with libstdc++-7-dev 7.3.0-16ubuntu3 installed, btw, so clang picks that by default:
Interesting. That explains why I'm not seeing it. I was using libc++ (-stdlib=libc++)
I can reproduce this on both Fedora 28 and Ubuntu 18.04, but not on FreeBSD. It seems to be because clang emits the initialization of InnerCls into the .init_array segment before the GLOBAL iostream initializer, like gcc does.
I just downloaded the 6.0.1 release onto Ubuntu 18.04, and tried it there. Works fine for me; I guess I'm just lucky.
Dimitry, and you using any particular compiler flags?
No, just:
clang++ -O2 pr38914-1.cpp -o pr38914-1
This is with libstdc++-7-dev 7.3.0-16ubuntu3 installed, btw, so clang picks that by default:
$ clang++ -v
clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
Found candidate GCC installation: /usr/bin/../lib/gcc/i686-linux-gnu/8
Found candidate GCC installation: /usr/bin/../lib/gcc/i686-linux-gnu/8.0.1
Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/7
Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/7.3.0
Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/8
Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/8.0.1
Found candidate GCC installation: /usr/lib/gcc/i686-linux-gnu/8
Found candidate GCC installation: /usr/lib/gcc/i686-linux-gnu/8.0.1
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/7
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/7.3.0
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/8
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/8.0.1
Selected GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/7.3.0
Candidate multilib: .;@m64
Candidate multilib: 32;@m32
Candidate multilib: x32;@mx32
Selected multilib: .;@m64
I can reproduce this on both Fedora 28 and Ubuntu 18.04, but not on FreeBSD. It seems to be because clang emits the initialization of InnerCls into the .init_array segment before the GLOBAL iostream initializer, like gcc does.
I just downloaded the 6.0.1 release onto Ubuntu 18.04, and tried it there. Works fine for me; I guess I'm just lucky.
Dimitry, and you using any particular compiler flags?
I can reproduce this on both Fedora 28 and Ubuntu 18.04, but not on FreeBSD. It seems to be because clang emits the initialization of InnerCls into the .init_array segment before the GLOBAL iostream initializer, like gcc does.
I can NOT reproduce this on Mac OS X 10.11 with either "Apple LLVM version 8.0.0" or clang ToT (clang version 8.0.0 (trunk 341841))
Nor can I reproduce it with LLVM 5.0.0, 5.0.1, 5.0.2, 6.0.0 (all on my Mac).
I can reproduce this on both Fedora 28 and Ubuntu 18.04, but not on FreeBSD. It seems to be because clang emits the initialization of InnerCls into the .init_array segment before the GLOBAL iostream initializer, like gcc does.
I can NOT reproduce this on Mac OS X 10.11 with either "Apple LLVM version 8.0.0" or clang ToT (clang version 8.0.0 (trunk 341841))
I can reproduce this on both Fedora 28 and Ubuntu 18.04, but not on FreeBSD. It seems to be because clang emits the initialization of InnerCl
s into the .init_array
segment before the GLOBAL iostream
initializer, like gcc does.
E.g. clang 6.0.1 gives:
.type __cxx_global_var_init.1,@function
__cxx_global_var_init.1: # @​__cxx_global_var_init.1
.cfi_startproc
# %bb.0:
pushq %rbx
.cfi_def_cfa_offset 16
.cfi_offset %rbx, -16
cmpb $0, guard variable for Cls<int>::innerCls(%rip)
je .LBB1_1
# %bb.6:
popq %rbx
retq
.LBB1_1:
movl std::cout, %edi
movl $.L__FUNCTION__._ZN8InnerClsC2Ev, %esi
movl $8, %edx
callq std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::
char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
[...]
.section .text.startup,"ax",@progbits
.p2align 4, 0x90 # -- Begin function _GLOBAL__sub_I_pr38914_1.cpp
.type _GLOBAL__sub_I_pr38914_1.cpp,@function
_GLOBAL__sub_I_pr38914_1.cpp: # @​_GLOBAL__sub_I_pr38914_1.cpp
.cfi_startproc
# %bb.0:
pushq %rax
.cfi_def_cfa_offset 16
movl std::__ioinit, %edi
callq std::ios_base::Init::Init()
movl std::ios_base::Init::~Init(), %edi
movl std::__ioinit, %esi
movl $__dso_handle, %edx
popq %rax
jmp __cxa_atexit # TAILCALL
[...]
.section .init_array,"aGw",@init_array,Cls<int>::innerCls,comdat
.p2align 3
.quad __cxx_global_var_init.1
.section .init_array,"aw",@init_array
.p2align 3
.quad _GLOBAL__sub_I_pr38914_1.cpp
while gcc 8.1.1 gives:
.type _GLOBAL__sub_I_main, @​function
_GLOBAL__sub_I_main:
.LFB2042:
.cfi_startproc
subq $8, %rsp
.cfi_def_cfa_offset 16
movl std::__ioinit, %edi
call std::ios_base::Init::Init()
movl $__dso_handle, %edx
movl std::__ioinit, %esi
movl std::ios_base::Init::~Init(), %edi
call __cxa_atexit
cmpb $0, guard variable for Cls<int>::innerCls(%rip)
je .L16
addq $8, %rsp
.cfi_remember_state
.cfi_def_cfa_offset 8
ret
.L16:
.cfi_restore_state
movl $8, %edx
movl InnerCls::InnerCls()::__FUNCTION__, %esi
movl std::cout, %edi
movb $1, guard variable for Cls<int>::innerCls(%rip)
call std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
movl std::cout, %edi
call std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)
movl $__dso_handle, %edx
movl Cls<int>::innerCls, %esi
popq %rax
.cfi_def_cfa_offset 8
movl InnerCls::~InnerCls(), %edi
jmp __cxa_atexit
.cfi_endproc
[...]
.size _GLOBAL__sub_I_main, .-_GLOBAL__sub_I_main
.section .init_array,"aw"
.align 8
.quad _GLOBAL__sub_I_main
Clearly an issue with clang, since <iostream>
was included properly, and thus std::ios_base::Init::Init()
should be called before any static constructors in the main .cpp
file.
Extended Description
This program crashes before starting. on g++ - it runs as expected. it seems like a bug: