Proposal: Adding an optional association field to llvm.global_ctors

To implement http://llvm.org/PR16959, I need to add a new field to global_ctors.

Static data members of class template instantiations can have initializers that must only run once on program startup. Itanium solves this with guard variables and the emission of multiple initializers, only one of which actually initializes the data at runtime.

Microsoft solves this by using a COFF comdat feature called IMAGE_COMDAT_SELECT_ASSOCIATIVE. The semantics are that the section with this attribute is only linked if the associated section is chosen for the final link. Otherwise it is discarded.

In this way, only one initializer is linked and only one entry is produced in the initializer array (.CRT$XCU). Everyone I’ve spoken with so far thinks this is basically awesome and is much cleaner than using guard variables. :slight_smile:

I propose changing LangRef to make llvm.global_ctors and llvm.global_dtors be arrays of { i32, void(), i8 }, where the last field is an optional pointer to a GlobalValue. Old modules containing { i32, void()*} elements will also be accepted, and the missing third field will be assumed to be null.

When performing LTO, if there are multiple entries with the same associated global, only one of them will be kept in the linked module. Similarly, on targets supporting this feature (COFF for the moment, but maybe ELF one day as an optimization), LLVM will emit the appropriate comdat bits so that the system linker behaves similarly. On platforms lacking this support, the old behavior will be used, in which case guard variables are still necessary.

This seems fairly uncontroversial, but let me know if anyone objects. I’ll try to send patches later this week.

Hi Reid,

To implement http://llvm.org/PR16959
<http://llvm.org/bugs/show_bug.cgi?id=16959>, I need to add a new field to
global_ctors.

Static data members of class template instantiations can have initializers that
must only run once on program startup. Itanium solves this with guard variables
and the emission of multiple initializers, only one of which actually
initializes the data at runtime.

Microsoft solves this by using a COFF comdat feature called
IMAGE_COMDAT_SELECT_ASSOCIATIVE. The semantics are that the section with this
attribute is only linked if the associated section is chosen for the final link.
Otherwise it is discarded.

In this way, only one initializer is linked and only one entry is produced in
the initializer array (.CRT$XCU). Everyone I've spoken with so far thinks this
is basically awesome and is much cleaner than using guard variables. :slight_smile:

I propose changing LangRef to make llvm.global_ctors and llvm.global_dtors be
arrays of { i32, void()*, i8* }, where the last field is an optional pointer to
a GlobalValue. Old modules containing { i32, void()*} elements will also be
accepted, and the missing third field will be assumed to be null.

When performing LTO, if there are multiple entries with the same associated
global, only one of them will be kept in the linked module.

will two different globals have different initialization functions too, or will
the same function be reused for many different globals?

Thanks, Duncan.

  Similarly, on

Hi Reid,

To implement http://llvm.org/PR16959
<http://llvm.org/bugs/show_**bug.cgi?id=16959<http://llvm.org/bugs/show_bug.cgi?id=16959>>,
I need to add a new field to

global_ctors.

Static data members of class template instantiations can have
initializers that
must only run once on program startup. Itanium solves this with guard
variables
and the emission of multiple initializers, only one of which actually
initializes the data at runtime.

Microsoft solves this by using a COFF comdat feature called
IMAGE_COMDAT_SELECT_**ASSOCIATIVE. The semantics are that the section
with this
attribute is only linked if the associated section is chosen for the
final link.
Otherwise it is discarded.

In this way, only one initializer is linked and only one entry is
produced in
the initializer array (.CRT$XCU). Everyone I've spoken with so far thinks
this
is basically awesome and is much cleaner than using guard variables. :slight_smile:

I propose changing LangRef to make llvm.global_ctors and
llvm.global_dtors be
arrays of { i32, void()*, i8* }, where the last field is an optional
pointer to
a GlobalValue. Old modules containing { i32, void()*} elements will also
be
accepted, and the missing third field will be assumed to be null.

When performing LTO, if there are multiple entries with the same
associated
global, only one of them will be kept in the linked module.

will two different globals have different initialization functions too, or
will
the same function be reused for many different globals?

I'm not sure I follow. Do you mean, are initialization functions reused
for two globals in the same TU?

Even today, every global with a dynamic initializer gets its own void()
initialization function. We don't use a single function to initialize
everything.

We need to enforce an ordering on initializers in a single TU, so we have a
function (usually _GLOBAL_I_a) which calls each stub in turn. After
inlining, most stubs are probably eliminated.

Hi Reid,

    Hi Reid,

        To implement http://llvm.org/PR16959
        <http://llvm.org/bugs/show___bug.cgi?id=16959
        <http://llvm.org/bugs/show_bug.cgi?id=16959>>, I need to add a new field to

        global_ctors.

        Static data members of class template instantiations can have
        initializers that
        must only run once on program startup. Itanium solves this with guard
        variables
        and the emission of multiple initializers, only one of which actually
        initializes the data at runtime.

        Microsoft solves this by using a COFF comdat feature called
        IMAGE_COMDAT_SELECT___ASSOCIATIVE. The semantics are that the section
        with this
        attribute is only linked if the associated section is chosen for the
        final link.
        Otherwise it is discarded.

        In this way, only one initializer is linked and only one entry is
        produced in
        the initializer array (.CRT$XCU). Everyone I've spoken with so far
        thinks this
        is basically awesome and is much cleaner than using guard variables. :slight_smile:

        I propose changing LangRef to make llvm.global_ctors and
        llvm.global_dtors be
        arrays of { i32, void()*, i8* }, where the last field is an optional
        pointer to
        a GlobalValue. Old modules containing { i32, void()*} elements will also be
        accepted, and the missing third field will be assumed to be null.

        When performing LTO, if there are multiple entries with the same associated
        global, only one of them will be kept in the linked module.

    will two different globals have different initialization functions too, or will
    the same function be reused for many different globals?

I'm not sure I follow. Do you mean, are initialization functions reused for two
globals in the same TU?

yes, or even in different TU (eg by using linkonce linkage).

Even today, every global with a dynamic initializer gets its own void()
initialization function. We don't use a single function to initialize everything.

OK, thanks for explaining. In that case, will multiple instances of the same
global (the ones you want to eliminate) have the same initialization function?
If so, then I guess right now, without your changes, llvm.global_ctors will
have multiple copies of the same function. Then instead of introducing a third
"global variable" i8* field in llvm.global_ctors, there could instead by a
third boolean field meaning: collapse multiple instances of this initialization
function. In fact, maybe it is always OK to remove duplicate initialization
functions in llvm.global_ctors?

Ciao, Duncan.

Even today, every global with a dynamic initializer gets its own void()

initialization function. We don't use a single function to initialize
everything.

OK, thanks for explaining. In that case, will multiple instances of the
same
global (the ones you want to eliminate) have the same initialization
function?

For the Microsoft C++ ABI, yes, they use the same mangling (?__E*) and are
linkonce_odr.

For the Itanium C++ ABI, no, they are all internal and hence different. It
uses a guard variable to avoid double initialization.

If so, then I guess right now, without your changes, llvm.global_ctors will
have multiple copies of the same function. Then instead of introducing a
third
"global variable" i8* field in llvm.global_ctors, there could instead by a
third boolean field meaning: collapse multiple instances of this
initialization
function. In fact, maybe it is always OK to remove duplicate
initialization
functions in llvm.global_ctors?

Richard and I considered this, but cl.exe associates the .CRT$XCU function
pointer with the global, and not the initializer function. I could try to
recover the name of the global from the name of the initializer, but this
seems fairly terrible, and possibly invalid in the face of GlobalOpt.

Consider a constructor which does nothing but tail call to 'void foo()'.
It should be valid to make the .CRT$XCU function pointer point directly to
foo and delete the initializer. We could outlaw this optimization when the
unique bit is set, but then I still have to recover the global's symbol to
get the MS C++ ABI right.

That said, if I only needed one bit, I could steal it from the i32 priority
field which only uses 16 bits. =D