Linker failures in debug build - compiler/linker poll?

Hi,

since recently I'm getting linker failures in debug builds. The root cause is that -fvisibility-inlines-hidden causes inline functions in explicit template instantiations to become hidden in certain cases.

The following hunk works around this...

--- a/cmake/modules/HandleLLVMOptions.cmake
+++ b/cmake/modules/HandleLLVMOptions.cmake
@@ -159,7 +159,13 @@ endif()
  if(NOT WIN32 AND NOT CYGWIN)
    # MinGW warns if -fvisibility-inlines-hidden is used.
    check_cxx_compiler_flag("-fvisibility-inlines-hidden" SUPPORTS_FVISIBILITY_INLINES_HIDDEN_FLAG)
- append_if(SUPPORTS_FVISIBILITY_INLINES_HIDDEN_FLAG "-fvisibility-inlines-hidden" CMAKE_CXX_FLAGS)

Hi,

This probably just works around a layering violation. Can you provide the cmake line that produces a broken build?

Cheers,
Rafael

This probably just works around a layering violation. Can you provide
the cmake line that produces a broken build?

I can't really trace it down to a single cmake line (the linker failure happens while linking llc, for example).

And yes, I'm starting to think that this may be something like an ODR-rule violation. I've attached a minimal example that has the shape of what happens in LLVM. Build with

c++ -fPIC -fvisibility-inlines-hidden -Wall -c a.cpp
c++ -fPIC -fvisibility-inlines-hidden -Wall -c b.cpp
c++ -fPIC -shared a.o b.o -o test.so

Then nm test.so | grep Example says:

0000000000000640 T _Z7TriggerRK7ExampleIiE
000000000000066a W _ZNK7ExampleIdE3getEv
000000000000065a t _ZNK7ExampleIiE3getEv

In b.cpp, both Example<int> and Example<double> are supposed to be explicitly instantiated and those instantiations are supposed to be visible to users of test.so. However, for Example<int>::get, the fact that a.cpp doesn't see the "extern template class Example<int>" causes it to instantiate Example<int>::get() a second time, with a hidden visibility flag.

The problem isn't visible in an optimized build because the Example<int>::get() is inlined while compiling a.cpp.

To translate that back to LLVM, the problem is that LoopPass.cpp uses AnalysisManager<Loop> without including LoopPassManager.h, where the extern template declaration lives.

I'm not sufficiently expert in the rules of how visibility and explicit template instantiations are supposed to interact to be able to tell whether this is a bug in the build tools or a bug in the LLVM source code.

In any case, I noticed this morning that somebody else fixed it in the other way that I had in mind, which is including LoopPassManager.h in LoopPass.cpp.

This does seem pretty fragile to me, though. Perhaps all the "extern template" declarations should be in the same header file as the template itself?

Cheers,
Nicolai

a.cpp (72 Bytes)

a.h (134 Bytes)

b.cpp (77 Bytes)

b.h (131 Bytes)

In any case, I noticed this morning that somebody else fixed it in the other
way that I had in mind, which is including LoopPassManager.h in
LoopPass.cpp.

Including LoopPassManager.h in LoopPass.cpp fixes the problem for you?
I think that is the correct fix. I am not sure if c++ requires that an
extern template be visible in all translation units if it is visible
in one, but -fvisibility-inline-hidden is know to not be fully
standards compliant.

Cheers,
Rafael