Open Quuxplusone opened 6 years ago
Bugzilla Link | PR38914 |
Status | NEW |
Importance | P normal |
Reported by | Dudu Arbel (duduarbel@gmail.com) |
Reported on | 2018-09-12 07:03:24 -0700 |
Last modified on | 2018-10-11 10:42:05 -0700 |
Version | 6.0 |
Hardware | PC Linux |
CC | cbcode@gmail.com, dblaikie@gmail.com, dehuan@motorola.com, dgregor@apple.com, dimitry@andric.com, llvm-bugs@lists.llvm.org, mclow.lists@gmail.com, oliver@uptheinter.net, richard-llvm@metafoo.co.uk |
Fixed by commit(s) | |
Attachments | |
Blocks | |
Blocked by | |
See also |
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.
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.
(In reply to Dimitry Andric from comment #1)
> 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))
(In reply to Marshall Clow (home) from comment #2)
> (In reply to Dimitry Andric from comment #1)
> > 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).
(In reply to Dimitry Andric from comment #1)
> 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?
(In reply to Marshall Clow (home) from comment #4)
> (In reply to Dimitry Andric from comment #1)
> > 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
(In reply to Dimitry Andric from comment #5)
> (In reply to Marshall Clow (home) from comment #4)
> > (In reply to Dimitry Andric from comment #1)
> > > 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++)
Rebinning to clang.
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.
This program is not guaranteed to work. Your global variable Cls
(In reply to Richard Smith from comment #9)
> 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.
Aha, and that is specifically because Cls is a template class?
(In reply to Dimitry Andric from comment #10)
> 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.
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
(In reply to Oliver from comment #12)
> 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]).
(In reply to Richard Smith from comment #13)
> (In reply to Oliver from comment #12)
> > 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]).
From N4750 - 30.4.2 [iostream.objects.overview] clause 3
> "The results of including <iostream> in a translation unit shall be as if
<iostream> 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?
(In reply to Oliver from comment #14)
> Does "InnerCls Cls<T>::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"
_Bug 39233 has been marked as a duplicate of this bug._
_Bug 39250 has been marked as a duplicate of this bug._