Bug 44952

Summary: #include <iostream> implies global constructor.
Product: gcc Reporter: Jan Hubicka <hubicka>
Component: libstdc++Assignee: Patrick Palka <ppalka>
Status: ASSIGNED ---    
Severity: enhancement CC: ccoutant, fw, gcc-bugs, jason, noloader, paolo.carlini, ppalka, redi, rguenth, sjames, webrown.cpp
Priority: P3    
Version: 4.6.0   
Target Milestone: ---   
See Also: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=98108
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94810
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=62200
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108969
Host: Target:
Build: Known to work:
Known to fail: Last reconfirmed: 2022-11-03 00:00:00

Description Jan Hubicka 2010-07-15 15:51:43 UTC
Noticed while reading http://comments.gmane.org/gmane.comp.web.chromium.devel/16789

evans:/abuild/jh/trunk-install/bin/:[0]# more g.C
#include <iostream>
evans:/abuild/jh/trunk-install/bin/:[0]# ./g++  -O2 g.C -S
evans:/abuild/jh/trunk-install/bin/:[0]# more g.s
        .file   "g.C"
        .text
        .p2align 4,,15
        .type   _GLOBAL__I_g.C, @function
_GLOBAL__I_g.C:
.LFB969:
        .cfi_startproc
        subq    $8, %rsp
        .cfi_def_cfa_offset 16
        movl    $_ZStL8__ioinit, %edi
        call    _ZNSt8ios_base4InitC1Ev
        movl    $__dso_handle, %edx
        movl    $_ZStL8__ioinit, %esi
        movl    $_ZNSt8ios_base4InitD1Ev, %edi
        addq    $8, %rsp
        .cfi_def_cfa_offset 8
        jmp     __cxa_atexit
        .cfi_endproc
.LFE969:
Comment 1 pinskia@gmail.com 2010-07-15 16:02:32 UTC
Subject: Re:   New: #include <iostream.h> imply global constructor.

This is expected and iirc required by the c++ standard too.

On Jul 15, 2010, at 8:51 AM, "hubicka at gcc dot gnu dot org" <gcc-bugzilla@gcc.gnu.org 
 > wrote:

> Noticed while reading
> http://comments.gmane.org/gmane.comp.web.chromium.devel/16789
>
> evans:/abuild/jh/trunk-install/bin/:[0]# more g.C
> #include <iostream>
> evans:/abuild/jh/trunk-install/bin/:[0]# ./g++  -O2 g.C -S
> evans:/abuild/jh/trunk-install/bin/:[0]# more g.s
>        .file   "g.C"
>        .text
>        .p2align 4,,15
>        .type   _GLOBAL__I_g.C, @function
> _GLOBAL__I_g.C:
> .LFB969:
>        .cfi_startproc
>        subq    $8, %rsp
>        .cfi_def_cfa_offset 16
>        movl    $_ZStL8__ioinit, %edi
>        call    _ZNSt8ios_base4InitC1Ev
>        movl    $__dso_handle, %edx
>        movl    $_ZStL8__ioinit, %esi
>        movl    $_ZNSt8ios_base4InitD1Ev, %edi
>        addq    $8, %rsp
>        .cfi_def_cfa_offset 8
>        jmp     __cxa_atexit
>        .cfi_endproc
> .LFE969:
>
>
> -- 
>           Summary: #include <iostream.h> imply global constructor.
>           Product: gcc
>           Version: 4.6.0
>            Status: UNCONFIRMED
>          Severity: normal
>          Priority: P3
>         Component: libstdc++
>        AssignedTo: unassigned at gcc dot gnu dot org
>        ReportedBy: hubicka at gcc dot gnu dot org
>
>
> http://gcc.gnu.org/bugzilla/show_bug.cgi?id=44952
>
Comment 2 Richard Biener 2010-07-15 16:03:55 UTC
Why's this not in libstdc++.so .init?
Comment 3 Jan Hubicka 2010-07-15 16:12:27 UTC
... and are we required to emit the constructor even if we know var is not used?
Comment 4 Andrew Pinski 2010-07-15 16:30:23 UTC
(In reply to comment #2)
> Why's this not in libstdc++.so .init?

because this will not work if libstdc++ is a static library.
Take:

#include <iostream>

namespace {
struct g
{
  g(){ std::cout << "t"; }
};
g one;
}

--- CUT ---
The C++ standard says order of initializers between TUs is unspecified (though the order inside TUs is specified as being the first one will run first).
So with a static version, the above will be included first and that will cause std::cout to be used without being initialized.

>... and are we required to emit the constructor even if we know var is not
used?
It is hard to do that in Standard C++ really or imposable.
Comment 5 Jonathan Wakely 2010-07-15 16:38:52 UTC
This is why you should only include <iostream> if you want actually want std::cin, std::cout or std::cerr (or the wide character equivalents.)
Otherwise you should only include one or more of <iosfwd>, <istream> and <ostream>, as needed.


(In reply to comment #3)
> ... and are we required to emit the constructor even if we know var is not
> used?

27.4 [iostream.objects] paragraph 2
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. Similarly, the entire program shall behave as if there were at least one instance of ios_base::Init with static storage duration.




Comment 6 Jonathan Wakely 2010-07-15 16:45:13 UTC
and please ... it's 2010, <iostream> not <iostream.h>  ;-)

Comment 7 Jan Hubicka 2010-07-15 16:53:15 UTC
Hehe, I am really not C++ guy even in 2010, but I have impression that people are including iostream without really thinking about consequences here.

Well, so what we can do about the startup times then?  I will teach ipa-profile to propagate info if function is only called from constructor and will put them into separate section. This will save the random access at file startup. But we ought to be able to do better.
Comment 8 Jonathan Wakely 2010-07-15 17:49:15 UTC
(In reply to comment #7)
> Hehe, I am really not C++ guy even in 2010, but I have impression that people
> are including iostream without really thinking about consequences here.

Yes, and in many cases that's the simplest thing to do, but it has a cost.
 
> Well, so what we can do about the startup times then?

I would do nothing.  If people care about startup times they should not include facilities they don't use.



Comment 9 Paolo Carlini 2010-07-15 18:26:09 UTC
Let's say we remove that horrible .h from the Summary, since, to be fair, didn't exist in g.C in the first place ;)

That said, I agree with Jon, by and large, with the following minor additional observations: 1- I'm pretty sure the library is correct, but we should double check whether other established and new implementations of the C++ runtime are trying to do something special, performance-wise - low priority I'm afraid; 2- As library maintainers we certainly welcome any improvement to the optimizers improving the code GCC generates for these constructors, because certainly many user applications could benefit, not just because the library would; -3 While we are at it, I think we should make sure not regressing on libstdc++/39796, or even making progress at once. Ideas? (I didn't really manage to study it in any detail)
Comment 10 Andrew Pinski 2010-07-22 00:01:53 UTC
The C++ standard actually requires this as noted in comment #5.
Comment 11 Jakub Jelinek 2010-07-22 11:03:48 UTC
Perhaps with LTO we could special case this (perhaps using some special attribute) and only construct/destruct the first of these
  static ios_base::Init __ioinit;
vars and optimize all the others away together with their construction/destruction, assuming they aren't otherwise referenced.
Comment 12 Paolo Carlini 2010-07-22 11:25:41 UTC
The same idea vaguely occurred to me...
Comment 13 Jakub Jelinek 2010-07-22 11:41:29 UTC
So reopening for this enhancement...

Another alternative would be some .init.first array or something similar which would contain pointers to functions to be run as early constructors and linker would remove duplicates in it and put it at the beginning of .init_array section.
Comment 14 Jan Hubicka 2010-08-19 09:39:14 UTC
Well, it might be easy to modify libstdc++ implementation so the static constructor compiles into single function call.  Then we can teach GCC to optimize constructor containing only a call into entry in ctors table.
Then we will get a lot of duplicate calls, but all at one place and we won't have the locality problem at startup.
Comment 15 Cary Coutant 2010-12-14 18:50:11 UTC
(In reply to comment #13)
> So reopening for this enhancement...
> 
> Another alternative would be some .init.first array or something similar which
> would contain pointers to functions to be run as early constructors and linker
> would remove duplicates in it and put it at the beginning of .init_array
> section.

For ELF targets, this is what DT_PREINIT_ARRAY is for.

http://www.sco.com/developers/gabi/latest/ch5.dynamic.html#init_fini

-cary
Comment 16 Jonathan Wakely 2020-12-10 16:03:44 UTC
(In reply to Jonathan Wakely from comment #5)
> 27.4 [iostream.objects] paragraph 2
> 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. Similarly, the entire program shall behave as if there were at
> least one instance of ios_base::Init with static storage duration.

N.B. https://wg21.link/lwg2765 removed that last sentence, so we are no longer required to run the stream initialization in all programs. Only in programs which include <iostream> in at least one TU.
Comment 17 Jason Merrill 2022-11-03 14:38:27 UTC
The __ioinit hack won't work with the move to modules, and it doesn't seem necessary either:

For programs that use libstdc++ as a shared library, it should be fine for the initialization to run in the library, before the main program is loaded.

For programs that use a static libstdc++, attribute init_priority should be sufficient to make sure it runs first.
Comment 18 GCC Commits 2022-11-06 16:16:18 UTC
The master branch has been updated by Patrick Palka <ppalka@gcc.gnu.org>:

https://gcc.gnu.org/g:4e4e3ffd10f53ef71696bc728ab40258751a2df4

commit r13-3707-g4e4e3ffd10f53ef71696bc728ab40258751a2df4
Author: Patrick Palka <ppalka@redhat.com>
Date:   Sun Nov 6 11:16:00 2022 -0500

    libstdc++: Move stream initialization into compiled library [PR44952]
    
    This patch moves the static object for constructing the standard streams
    out from <iostream> and into the compiled library on systems that support
    init priorities.  This'll mean <iostream> no longer introduces a separate
    global constructor in each TU that includes it.
    
    We can do this only if the init_priority attribute is supported because
    we need a way to ensure the stream initialization runs first before any
    user global initializer, particularly when linking with a static
    libstdc++.a.
    
            PR libstdc++/44952
            PR libstdc++/39796
            PR libstdc++/98108
    
    libstdc++-v3/ChangeLog:
    
            * include/std/iostream (__ioinit): No longer define here if
            the init_priority attribute is usable.
            * src/c++98/ios_init.cc (__ioinit): Define here instead if
            init_priority is usable, via ...
            * src/c++98/ios_base_init.h: ... this new file.