Linkage of static local variables in available_externally definitions

In implementing modular codegen, specifically verifying the behavior for static local variables in inline functions in modular headers, I came across a piece of logic in Clang’s linkage calculation that seems off.

To demonstrate a real-world example:

template
inline void f() { static int i; }
extern template void f();

template void f();

In the TU with the extern template declaration, an available_externally definition of f and f::i are provided (when optimizations are enabled).

In the TU with the extern template definition, a weak_odr definition of f is provided but a linkonce_odr definition of f::i is provided.

This surprises me and doesn’t seem correct. What if the TU with the weak_odr definition is optimized to the point of removing the f::i definition - but the available_externally TU is not - it inlines f but leaves a use of f::i available?

What’s the deal here - am I missing something that makes this safe/correct?

  • Dave

Looks buggy to me. Static locals created by explicit template instantiation should probably not be discardable.

I think we’re just lucky so far because either the local has constant initialization and no guard variable, so we never need a real definition, or it has a guard variable which keeps it from being optimized away.

Seems buggy to me, either it should be (decl / def ): available_externally / weak_odr or: linkonce_odr / linkonce_odr.

Indeed based on your example, here are two source files and a header that fail to link on macOS when optimizations are enabled.

a.h (153 Bytes)

b.cpp (49 Bytes)

a.cpp (70 Bytes)

Ah - yep!

Sent out https://reviews.llvm.org/D29233 - turned out linkonce_odr/linkonce_odr is needed for the static local variables to meet the Itanium ABI (well, technically while linkonce_odr must be used in the use - there would be no harm in putting weak_odr in the definition, but no benefit either since the ABI doesn’t guarantee it, so it cannot be depended on)

Richard & I also talked about the ramifications of this for Modular Codegen as well - we came to the conclusion that for now the same behavior can/should be used, but once modular codegen is implemented using a_e/weak could be experimented with and would be a valid implementation based on current ideas around where the ABI and contracts would be between objects in modular codegen scenarios. (essentially the idea was: modular objects will need to be ABI compatible with existing code (eg: maybe some of the code is built with Clang and moduels, some of the code is built with GCC) - but could provide extra ABI surface for objects generated by a compiler using those modules (eg: objects built by the compiler that built the modules used for modular codegen could depend on extra ABI surface, like weak_odr static locals)… that may’ve been a bit rambly, sorry - might be able to explain that better another time)

Thanks, Richard, for walking through all this with me.