6

Consider the following piece of code:

#include <atomic>

int main(void) {
  std::atomic<double> aDouble;
  aDouble = 6.0;
}

G++ compiles it just fine while clang++ produces the following:

clang++ -std=c++11 Main.cpp 
/tmp/Main-d4f0fc.o: In function `std::atomic<double>::store(double, std::memory_order)':
Main.cpp:(.text._ZNSt6atomicIdE5storeEdSt12memory_order[_ZNSt6atomicIdE5storeEdSt12memory_order]+0x31): undefined reference to `__atomic_store_8'
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Do they not link against the same standard library?

T.C.
  • 133,968
  • 17
  • 288
  • 421
Rovanion
  • 4,382
  • 3
  • 29
  • 49
  • 2
    Mostly clang uses libc++ while gcc uses libstdc++. – Ben Voigt Mar 07 '15 at 22:00
  • 1
    Therefore, if you are compiling with Clang on a system that only has the GCC standard library installed, you probably need to pass the `-lstdc++` flag to Clang. – 5gon12eder Mar 07 '15 at 22:09
  • Yes, `clang++ -std=c++11 -lstdc++ Main.cpp` does indeed compile the program. So it's a matter of `libc++` not having implemented the feature while `libstdc++` has it? – Rovanion Mar 08 '15 at 10:35
  • **use `clang++ -stdlib=libstdc++` or `clang++ -stdlib=libc++` to make sure you get headers compatible with the library you link**. @5gon12eder: don't use `-lstdc++`. You could (at least in theory) get breakage if a `libc++` header file has an inline definition of something that depends on libc++ internals, and it ends up getting called with a pointer to something produced by a non-inlined `libstdc++` function (or vice versa). See https://libcxx.llvm.org/docs/UsingLibcxx.html – Peter Cordes Sep 04 '17 at 04:46
  • BTW, **libc++ supports `std::atomic` now**. For x86, it compiles to the same asm as libstdc++, which unfortunately isn't very efficient https://godbolt.org/g/aCcnG5 (with clang4.0 or gcc7): See https://stackoverflow.com/questions/45055402/atomic-double-floating-point-or-sse-avx-vector-load-store-on-x86-64 for more. – Peter Cordes Sep 04 '17 at 08:21

1 Answers1

3

If clang++ -stdlib=libstdc++ doesn't solve your problem, link with -latomic for the implementation of these functions.

Try to get your compiler to inline 8-byte and narrower atomics, though, because the library functions have potentially large downsides.

Beware that the library functions don't support a memory ordering weaker than memory_order_seq_cst, so they always use mfence on x86, even if the source used relaxed.


The 32-bit x86 version of __atomic_store_8 is even worse: it uses lock cmpxchg8b instead of an SSE or x87 8-byte store. This makes it work even if it's misaligned, but at a massive performance penalty. It also has two redundant lock or [esp], 0 instructions as extra barriers around loading its arguments from the stack. (I'm looking at /usr/lib32/libatomic.so.1.2.0 from gcc7.1.1 on Arch Linux.)

Ironically, current gcc -m32 (in C11 mode, not C++11) under-aligns atomic_llong inside a struct, but inlines movq xmm loads/stores, so it's not actually atomic. (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65146#c4)

Current clang -m32 aligns atomic_llong to 8 bytes even inside structs (unlike regular long long, which the i386 System V ABI only aligns to 4B). The irony is that clang generates calls to the library functions, which uses a lock cmpxchg8b (https://bugs.llvm.org/show_bug.cgi?id=33109) so it actually is atomic even with cache-line splits. (Why is integer assignment on a naturally aligned variable atomic on x86?).

So clang is safe even if some gcc-compiled code passes it a pointer to a misaligned _Atomic long long. But it disagrees with gcc about struct layout, so this can only help if it gets a pointer to the atomic variable directly, rather than the containing struct.


Related: Atomic double floating point or SSE/AVX vector load/store on x86_64

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847