https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78158
--- Comment #2 from Dmitry Vyukov <dvyukov at google dot com> --- With -O0 there is huge potential for ODR violations. E.g. code contains: 401774: e8 f3 06 00 00 callq 401e6c <std::atomic<bool>::operator bool() const> If some other non-instrumented library contains a non-instrumented copy of the function, we can get false positives. However, the problem seems to be different. With -O1 I see that compare_exchange is compiled correctly (redirected into tsan runtime): if(not readers.compare_exchange_weak(local_readers, local_readers + 1)) 401721: 8d 53 01 lea 0x1(%rbx),%edx memory_order __b1 = __m1 & __memory_order_mask; __glibcxx_assert(__b2 != memory_order_release); __glibcxx_assert(__b2 != memory_order_acq_rel); __glibcxx_assert(__b2 <= __b1); return __atomic_compare_exchange_n(&_M_i, &__i1, __i2, 1, __m1, __m2); 401724: 41 b8 05 00 00 00 mov $0x5,%r8d 40172a: b9 05 00 00 00 mov $0x5,%ecx 40172f: 48 8d 74 24 3c lea 0x3c(%rsp),%rsi 401734: bf 68 26 60 00 mov $0x602668,%edi 401739: e8 82 fd ff ff callq 4014c0 <__tsan_atomic32_compare_exchange_weak@plt> 40173e: 84 c0 test %al,%al 401740: 74 a5 je 4016e7 <fun()+0x71> 401742: bb e8 03 00 00 mov $0x3e8,%ebx } However with -O0, there is plain inlined LOCK CMPXCHG instruction without any tsan instrumentation: if(not readers.compare_exchange_weak(local_readers, local_readers + 1)) 4017e4: 48 8d 85 60 ff ff ff lea -0xa0(%rbp),%rax 4017eb: 48 89 c7 mov %rax,%rdi 4017ee: e8 1d fb ff ff callq 401310 <__tsan_read4@plt> 4017f3: 8b 85 60 ff ff ff mov -0xa0(%rbp),%eax 4017f9: 83 c0 01 add $0x1,%eax 4017fc: 89 45 c0 mov %eax,-0x40(%rbp) 4017ff: c7 45 bc 05 00 00 00 movl $0x5,-0x44(%rbp) _GLIBCXX_ALWAYS_INLINE bool compare_exchange_weak(__int_type& __i1, __int_type __i2, memory_order __m = memory_order_seq_cst) noexcept { return compare_exchange_weak(__i1, __i2, __m, 401806: 8b 45 bc mov -0x44(%rbp),%eax 401809: 89 c7 mov %eax,%edi 40180b: e8 cb 05 00 00 callq 401ddb <std::__cmpexch_failure_order(std::memory_order)> 401810: 89 c2 mov %eax,%edx 401812: 48 c7 45 b0 e4 55 60 movq $0x6055e4,-0x50(%rbp) 401819: 00 40181a: 48 8d 85 60 ff ff ff lea -0xa0(%rbp),%rax 401821: 48 89 45 a8 mov %rax,-0x58(%rbp) 401825: 8b 45 c0 mov -0x40(%rbp),%eax 401828: 89 45 a4 mov %eax,-0x5c(%rbp) 40182b: 8b 45 bc mov -0x44(%rbp),%eax 40182e: 89 45 a0 mov %eax,-0x60(%rbp) 401831: 89 55 9c mov %edx,-0x64(%rbp) _GLIBCXX_ALWAYS_INLINE bool compare_exchange_weak(__int_type& __i1, __int_type __i2, memory_order __m1, memory_order __m2) noexcept { memory_order __b2 = __m2 & __memory_order_mask; 401834: 8b 45 9c mov -0x64(%rbp),%eax 401837: be ff ff 00 00 mov $0xffff,%esi 40183c: 89 c7 mov %eax,%edi 40183e: e8 5e 05 00 00 callq 401da1 <std::operator&(std::memory_order, std::__memory_order_modifier)> 401843: 89 45 98 mov %eax,-0x68(%rbp) memory_order __b1 = __m1 & __memory_order_mask; 401846: 8b 45 a0 mov -0x60(%rbp),%eax 401849: be ff ff 00 00 mov $0xffff,%esi 40184e: 89 c7 mov %eax,%edi 401850: e8 4c 05 00 00 callq 401da1 <std::operator&(std::memory_order, std::__memory_order_modifier)> 401855: 89 45 94 mov %eax,-0x6c(%rbp) __glibcxx_assert(__b2 != memory_order_release); __glibcxx_assert(__b2 != memory_order_acq_rel); __glibcxx_assert(__b2 <= __b1); return __atomic_compare_exchange_n(&_M_i, &__i1, __i2, 1, __m1, __m2); 401858: 8b 4d a4 mov -0x5c(%rbp),%ecx 40185b: 48 8b 75 b0 mov -0x50(%rbp),%rsi 40185f: 48 8b 55 a8 mov -0x58(%rbp),%rdx 401863: 8b 02 mov (%rdx),%eax 401865: f0 0f b1 0e lock cmpxchg %ecx,(%rsi) 401869: 89 c1 mov %eax,%ecx 40186b: 0f 94 c0 sete %al 40186e: 84 c0 test %al,%al 401870: 75 02 jne 401874 <fun()+0x12d> 401872: 89 0a mov %ecx,(%rdx) 401874: 83 f0 01 xor $0x1,%eax 401877: 84 c0 test %al,%al 401879: 74 2e je 4018a9 <fun()+0x162> continue; Jakub, do you see how -O0 can lead to inlined but non-instrumented compare_exchange?