[llvm-bugs] [Bug 46719] New: C++11 atomic exchange operation compiles into AArch64 store instruction

via llvm-bugs llvm-bugs at lists.llvm.org
Tue Jul 14 22:58:44 PDT 2020


https://bugs.llvm.org/show_bug.cgi?id=46719

            Bug ID: 46719
           Summary: C++11 atomic exchange operation compiles into AArch64
                    store instruction
           Product: libraries
           Version: trunk
          Hardware: PC
                OS: Linux
            Status: NEW
          Severity: enhancement
          Priority: P
         Component: Scalar Optimizations
          Assignee: unassignedbugs at nondot.org
          Reporter: sunghwan.lee at sf.snu.ac.kr
                CC: llvm-bugs at lists.llvm.org

clang++ compiles a C++11 atomic exchange operation into a single AArch64 store
instruction whenever the value read by the exchange is never used.
It is a miscompilation since an acquire-fence may induce synchronization when
it follows a relaxed exchange operation, but not when it follows a store.
Following target codes are obtained by "clang++ -std=c++11 -O1"
source (C++):
================================
void foo(atomic<uint64_t> &X) {
  X.exchange(42, memory_order_relaxed);
}
================================
target (IR):
================================
define dso_local void @_Z3fooRSt6atomicImE(%"struct.std::atomic"* nocapture
nonnull align 8 dereferenceable(8) %X) local_unnamed_addr #0 {
entry:
  %_M_i.i = getelementptr inbounds %"struct.std::atomic",
%"struct.std::atomic"* %X, i64 0, i32 0, i32 0
  store atomic i64 42, i64* %_M_i.i monotonic, align 8
  ret void
}
================================
target (assembly):
================================
_Z3fooRSt6atomicImE:                    // @_Z3fooRSt6atomicImE
// %bb.0:                               // %entry
        mov     w8, #42
        str     x8, [x0]
        ret
================================
The following program demonstrates a new behavior introduced by this
miscompilation.
================================
uint64_t foo(atomic<uint64_t> &X, atomic<uint64_t> &Y) {
  X.exchange(42, memory_order_relaxed);
  atomic_thread_fence(memory_order_acquire);
  return Y.load(memory_order_relaxed);
}
uint64_t bar(atomic<uint64_t> &X, atomic<uint64_t> &Y) {
  Y.store(1, memory_order_relaxed);
  return X.fetch_add(1, memory_order_release);
}
================================
When "foo" and "bar" running in parallel (where both X and Y are initialized to
0), both "foo" and "bar" returning 0 at the same time is not allowed by C++11.
In particular, if "X.fetch_add" by "bar" read 0 from X and updated X to 1,
"X.exchange" by "foo" is forced to read 1 and update to 42 due to the atomicity
of the "fetch_add".
In this case, the acquire fence by "foo" induces a happens-before relation 
between "Y.store" by "bar" and "Y.load" by "foo".
However both functions returning 0 is allowed by AArch64 when the exchange
operation by "foo" is optimized into a store instruction.

-- 
You are receiving this mail because:
You are on the CC list for the bug.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-bugs/attachments/20200715/93eb03a5/attachment.html>


More information about the llvm-bugs mailing list