diff --git a/compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cpp b/compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cpp --- a/compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cpp +++ b/compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cpp @@ -402,34 +402,45 @@ template static bool AtomicCAS(ThreadState *thr, uptr pc, volatile T *a, T *c, T v, morder mo, morder fmo) { - (void)fmo; // Unused because llvm does not pass it yet. + // 31.7.2.18: "The failure argument shall not be memory_order_release + // nor memory_order_acq_rel". LLVM (2021-05) fallbacks to Monotonic + // (mo_relaxed) when those are used. + CHECK(IsLoadOrder(fmo)); + MemoryWriteAtomic(thr, pc, (uptr)a, SizeLog()); SyncVar *s = 0; - bool write_lock = mo != mo_acquire && mo != mo_consume; - if (mo != mo_relaxed) { + bool write_lock = IsReleaseOrder(mo); + + if (mo != mo_relaxed || fmo != mo_relaxed) s = ctx->metamap.GetOrCreateAndLock(thr, pc, (uptr)a, write_lock); + + T cc = *c; + T pr = func_cas(a, cc, v); + bool success = pr == cc; + if (!success) { + *c = pr; + mo = fmo; + } + + if (s) { thr->fast_state.IncrementEpoch(); // Can't increment epoch w/o writing to the trace as well. TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0); - if (IsAcqRelOrder(mo)) + + if (success && IsAcqRelOrder(mo)) AcquireReleaseImpl(thr, pc, &s->clock); - else if (IsReleaseOrder(mo)) + else if (success && IsReleaseOrder(mo)) ReleaseImpl(thr, pc, &s->clock); else if (IsAcquireOrder(mo)) AcquireImpl(thr, pc, &s->clock); - } - T cc = *c; - T pr = func_cas(a, cc, v); - if (s) { + if (write_lock) s->mtx.Unlock(); else s->mtx.ReadUnlock(); } - if (pr == cc) - return true; - *c = pr; - return false; + + return success; } template diff --git a/compiler-rt/test/tsan/compare_exchange.cpp b/compiler-rt/test/tsan/compare_exchange.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/tsan/compare_exchange.cpp @@ -0,0 +1,106 @@ +// RUN: %clangxx_tsan -O1 %s %link_libcxx_tsan -o %t && %deflake %env_tsan_opts=atexit_sleep_ms=50 %run %t 2>&1 | FileCheck --check-prefix=CHECK-REPORT %s + +#include +#include +#include +#include + +#define NUM_ORDS 16 +#define NUM_THREADS NUM_ORDS * 2 +struct node { + int val; +}; +std::atomic _nodes[NUM_THREADS] = {}; + +void f1(int i) { + auto n = new node(); + n->val = 42; + _nodes[i].store(n, std::memory_order_release); +} + +template +void f2(int i, std::memory_order mo, std::memory_order fmo) { + node *expected = nullptr; + while (expected == nullptr) { + _nodes[i].compare_exchange_weak(expected, nullptr, mo, fmo); + }; + + ++expected->val; + assert(expected->val == 43); +} + +struct MemOrdSuccFail { + std::memory_order mo; + std::memory_order fmo; +}; + +MemOrdSuccFail OrdList[NUM_ORDS] = { + {std::memory_order_release, std::memory_order_relaxed}, + {std::memory_order_release, std::memory_order_acquire}, + {std::memory_order_release, std::memory_order_consume}, + {std::memory_order_release, std::memory_order_seq_cst}, + + {std::memory_order_acq_rel, std::memory_order_relaxed}, + {std::memory_order_acq_rel, std::memory_order_acquire}, + {std::memory_order_acq_rel, std::memory_order_consume}, + {std::memory_order_acq_rel, std::memory_order_seq_cst}, + + {std::memory_order_seq_cst, std::memory_order_relaxed}, + {std::memory_order_seq_cst, std::memory_order_acquire}, + {std::memory_order_seq_cst, std::memory_order_consume}, + {std::memory_order_seq_cst, std::memory_order_seq_cst}, + + {std::memory_order_relaxed, std::memory_order_relaxed}, + {std::memory_order_relaxed, std::memory_order_acquire}, + {std::memory_order_relaxed, std::memory_order_consume}, + {std::memory_order_relaxed, std::memory_order_seq_cst}, +}; + +int main() { + std::thread threads[NUM_THREADS]; + int ords = 0; + + // Instantiate a new f2 for each MO so we can dedup reports and actually + // make sure relaxed FMO triggers a warning for every different MO. + for (unsigned t = 0; t < 8; t += 2) { + threads[t] = std::thread(f1, t); + threads[t + 1] = std::thread(f2<0>, t, OrdList[ords].mo, OrdList[ords].fmo); + threads[t].join(); + threads[t + 1].join(); + ords++; + } + + for (unsigned t = 8; t < 16; t += 2) { + threads[t] = std::thread(f1, t); + threads[t + 1] = std::thread(f2<1>, t, OrdList[ords].mo, OrdList[ords].fmo); + threads[t].join(); + threads[t + 1].join(); + ords++; + } + + for (unsigned t = 16; t < 24; t += 2) { + threads[t] = std::thread(f1, t); + threads[t + 1] = std::thread(f2<2>, t, OrdList[ords].mo, OrdList[ords].fmo); + threads[t].join(); + threads[t + 1].join(); + ords++; + } + + for (unsigned t = 24; t < 32; t += 2) { + threads[t] = std::thread(f1, t); + threads[t + 1] = std::thread(f2<3>, t, OrdList[ords].mo, OrdList[ords].fmo); + threads[t].join(); + threads[t + 1].join(); + ords++; + } + + fprintf(stderr, "DONE\n"); + return 0; +} + +// CHECK-REPORT: WARNING: ThreadSanitizer: data race +// CHECK-REPORT: WARNING: ThreadSanitizer: data race +// CHECK-REPORT: WARNING: ThreadSanitizer: data race +// CHECK-REPORT: WARNING: ThreadSanitizer: data race +// CHECK-REPORT: DONE +// CHECK-REPORT: ThreadSanitizer: reported 4 warnings \ No newline at end of file diff --git a/llvm/test/Instrumentation/ThreadSanitizer/atomic.ll b/llvm/test/Instrumentation/ThreadSanitizer/atomic.ll --- a/llvm/test/Instrumentation/ThreadSanitizer/atomic.ll +++ b/llvm/test/Instrumentation/ThreadSanitizer/atomic.ll @@ -349,41 +349,61 @@ define void @atomic8_cas_monotonic(i8* %a) nounwind uwtable { entry: cmpxchg i8* %a, i8 0, i8 1 monotonic monotonic, !dbg !7 + cmpxchg i8* %a, i8 0, i8 1 monotonic acquire, !dbg !7 + cmpxchg i8* %a, i8 0, i8 1 monotonic seq_cst, !dbg !7 ret void, !dbg !7 } ; CHECK-LABEL: atomic8_cas_monotonic ; CHECK: call i8 @__tsan_atomic8_compare_exchange_val(i8* %a, i8 0, i8 1, i32 0, i32 0), !dbg +; CHECK: call i8 @__tsan_atomic8_compare_exchange_val(i8* %a, i8 0, i8 1, i32 0, i32 2), !dbg +; CHECK: call i8 @__tsan_atomic8_compare_exchange_val(i8* %a, i8 0, i8 1, i32 0, i32 5), !dbg define void @atomic8_cas_acquire(i8* %a) nounwind uwtable { entry: + cmpxchg i8* %a, i8 0, i8 1 acquire monotonic, !dbg !7 cmpxchg i8* %a, i8 0, i8 1 acquire acquire, !dbg !7 + cmpxchg i8* %a, i8 0, i8 1 acquire seq_cst, !dbg !7 ret void, !dbg !7 } ; CHECK-LABEL: atomic8_cas_acquire +; CHECK: call i8 @__tsan_atomic8_compare_exchange_val(i8* %a, i8 0, i8 1, i32 2, i32 0), !dbg ; CHECK: call i8 @__tsan_atomic8_compare_exchange_val(i8* %a, i8 0, i8 1, i32 2, i32 2), !dbg +; CHECK: call i8 @__tsan_atomic8_compare_exchange_val(i8* %a, i8 0, i8 1, i32 2, i32 5), !dbg define void @atomic8_cas_release(i8* %a) nounwind uwtable { entry: cmpxchg i8* %a, i8 0, i8 1 release monotonic, !dbg !7 + cmpxchg i8* %a, i8 0, i8 1 release acquire, !dbg !7 + cmpxchg i8* %a, i8 0, i8 1 release seq_cst, !dbg !7 ret void, !dbg !7 } ; CHECK-LABEL: atomic8_cas_release ; CHECK: call i8 @__tsan_atomic8_compare_exchange_val(i8* %a, i8 0, i8 1, i32 3, i32 0), !dbg +; CHECK: call i8 @__tsan_atomic8_compare_exchange_val(i8* %a, i8 0, i8 1, i32 3, i32 2), !dbg +; CHECK: call i8 @__tsan_atomic8_compare_exchange_val(i8* %a, i8 0, i8 1, i32 3, i32 5), !dbg define void @atomic8_cas_acq_rel(i8* %a) nounwind uwtable { entry: + cmpxchg i8* %a, i8 0, i8 1 acq_rel monotonic, !dbg !7 cmpxchg i8* %a, i8 0, i8 1 acq_rel acquire, !dbg !7 + cmpxchg i8* %a, i8 0, i8 1 acq_rel seq_cst, !dbg !7 ret void, !dbg !7 } ; CHECK-LABEL: atomic8_cas_acq_rel +; CHECK: call i8 @__tsan_atomic8_compare_exchange_val(i8* %a, i8 0, i8 1, i32 4, i32 0), !dbg ; CHECK: call i8 @__tsan_atomic8_compare_exchange_val(i8* %a, i8 0, i8 1, i32 4, i32 2), !dbg +; CHECK: call i8 @__tsan_atomic8_compare_exchange_val(i8* %a, i8 0, i8 1, i32 4, i32 5), !dbg define void @atomic8_cas_seq_cst(i8* %a) nounwind uwtable { entry: + cmpxchg i8* %a, i8 0, i8 1 seq_cst monotonic, !dbg !7 + cmpxchg i8* %a, i8 0, i8 1 seq_cst acquire, !dbg !7 cmpxchg i8* %a, i8 0, i8 1 seq_cst seq_cst, !dbg !7 ret void, !dbg !7 } ; CHECK-LABEL: atomic8_cas_seq_cst +; CHECK: call i8 @__tsan_atomic8_compare_exchange_val(i8* %a, i8 0, i8 1, i32 5, i32 0), !dbg +; CHECK: call i8 @__tsan_atomic8_compare_exchange_val(i8* %a, i8 0, i8 1, i32 5, i32 2), !dbg ; CHECK: call i8 @__tsan_atomic8_compare_exchange_val(i8* %a, i8 0, i8 1, i32 5, i32 5), !dbg define i16 @atomic16_load_unordered(i16* %a) nounwind uwtable { @@ -733,41 +753,61 @@ define void @atomic16_cas_monotonic(i16* %a) nounwind uwtable { entry: cmpxchg i16* %a, i16 0, i16 1 monotonic monotonic, !dbg !7 + cmpxchg i16* %a, i16 0, i16 1 monotonic acquire, !dbg !7 + cmpxchg i16* %a, i16 0, i16 1 monotonic seq_cst, !dbg !7 ret void, !dbg !7 } ; CHECK-LABEL: atomic16_cas_monotonic ; CHECK: call i16 @__tsan_atomic16_compare_exchange_val(i16* %a, i16 0, i16 1, i32 0, i32 0), !dbg +; CHECK: call i16 @__tsan_atomic16_compare_exchange_val(i16* %a, i16 0, i16 1, i32 0, i32 2), !dbg +; CHECK: call i16 @__tsan_atomic16_compare_exchange_val(i16* %a, i16 0, i16 1, i32 0, i32 5), !dbg define void @atomic16_cas_acquire(i16* %a) nounwind uwtable { entry: + cmpxchg i16* %a, i16 0, i16 1 acquire monotonic, !dbg !7 cmpxchg i16* %a, i16 0, i16 1 acquire acquire, !dbg !7 + cmpxchg i16* %a, i16 0, i16 1 acquire seq_cst, !dbg !7 ret void, !dbg !7 } ; CHECK-LABEL: atomic16_cas_acquire +; CHECK: call i16 @__tsan_atomic16_compare_exchange_val(i16* %a, i16 0, i16 1, i32 2, i32 0), !dbg ; CHECK: call i16 @__tsan_atomic16_compare_exchange_val(i16* %a, i16 0, i16 1, i32 2, i32 2), !dbg +; CHECK: call i16 @__tsan_atomic16_compare_exchange_val(i16* %a, i16 0, i16 1, i32 2, i32 5), !dbg define void @atomic16_cas_release(i16* %a) nounwind uwtable { entry: cmpxchg i16* %a, i16 0, i16 1 release monotonic, !dbg !7 + cmpxchg i16* %a, i16 0, i16 1 release acquire, !dbg !7 + cmpxchg i16* %a, i16 0, i16 1 release seq_cst, !dbg !7 ret void, !dbg !7 } ; CHECK-LABEL: atomic16_cas_release ; CHECK: call i16 @__tsan_atomic16_compare_exchange_val(i16* %a, i16 0, i16 1, i32 3, i32 0), !dbg +; CHECK: call i16 @__tsan_atomic16_compare_exchange_val(i16* %a, i16 0, i16 1, i32 3, i32 2), !dbg +; CHECK: call i16 @__tsan_atomic16_compare_exchange_val(i16* %a, i16 0, i16 1, i32 3, i32 5), !dbg define void @atomic16_cas_acq_rel(i16* %a) nounwind uwtable { entry: + cmpxchg i16* %a, i16 0, i16 1 acq_rel monotonic, !dbg !7 cmpxchg i16* %a, i16 0, i16 1 acq_rel acquire, !dbg !7 + cmpxchg i16* %a, i16 0, i16 1 acq_rel seq_cst, !dbg !7 ret void, !dbg !7 } ; CHECK-LABEL: atomic16_cas_acq_rel +; CHECK: call i16 @__tsan_atomic16_compare_exchange_val(i16* %a, i16 0, i16 1, i32 4, i32 0), !dbg ; CHECK: call i16 @__tsan_atomic16_compare_exchange_val(i16* %a, i16 0, i16 1, i32 4, i32 2), !dbg +; CHECK: call i16 @__tsan_atomic16_compare_exchange_val(i16* %a, i16 0, i16 1, i32 4, i32 5), !dbg define void @atomic16_cas_seq_cst(i16* %a) nounwind uwtable { entry: + cmpxchg i16* %a, i16 0, i16 1 seq_cst monotonic, !dbg !7 + cmpxchg i16* %a, i16 0, i16 1 seq_cst acquire, !dbg !7 cmpxchg i16* %a, i16 0, i16 1 seq_cst seq_cst, !dbg !7 ret void, !dbg !7 } ; CHECK-LABEL: atomic16_cas_seq_cst +; CHECK: call i16 @__tsan_atomic16_compare_exchange_val(i16* %a, i16 0, i16 1, i32 5, i32 0), !dbg +; CHECK: call i16 @__tsan_atomic16_compare_exchange_val(i16* %a, i16 0, i16 1, i32 5, i32 2), !dbg ; CHECK: call i16 @__tsan_atomic16_compare_exchange_val(i16* %a, i16 0, i16 1, i32 5, i32 5), !dbg define i32 @atomic32_load_unordered(i32* %a) nounwind uwtable { @@ -1117,41 +1157,61 @@ define void @atomic32_cas_monotonic(i32* %a) nounwind uwtable { entry: cmpxchg i32* %a, i32 0, i32 1 monotonic monotonic, !dbg !7 + cmpxchg i32* %a, i32 0, i32 1 monotonic acquire, !dbg !7 + cmpxchg i32* %a, i32 0, i32 1 monotonic seq_cst, !dbg !7 ret void, !dbg !7 } ; CHECK-LABEL: atomic32_cas_monotonic ; CHECK: call i32 @__tsan_atomic32_compare_exchange_val(i32* %a, i32 0, i32 1, i32 0, i32 0), !dbg +; CHECK: call i32 @__tsan_atomic32_compare_exchange_val(i32* %a, i32 0, i32 1, i32 0, i32 2), !dbg +; CHECK: call i32 @__tsan_atomic32_compare_exchange_val(i32* %a, i32 0, i32 1, i32 0, i32 5), !dbg define void @atomic32_cas_acquire(i32* %a) nounwind uwtable { entry: + cmpxchg i32* %a, i32 0, i32 1 acquire monotonic, !dbg !7 cmpxchg i32* %a, i32 0, i32 1 acquire acquire, !dbg !7 + cmpxchg i32* %a, i32 0, i32 1 acquire seq_cst, !dbg !7 ret void, !dbg !7 } ; CHECK-LABEL: atomic32_cas_acquire +; CHECK: call i32 @__tsan_atomic32_compare_exchange_val(i32* %a, i32 0, i32 1, i32 2, i32 0), !dbg ; CHECK: call i32 @__tsan_atomic32_compare_exchange_val(i32* %a, i32 0, i32 1, i32 2, i32 2), !dbg +; CHECK: call i32 @__tsan_atomic32_compare_exchange_val(i32* %a, i32 0, i32 1, i32 2, i32 5), !dbg define void @atomic32_cas_release(i32* %a) nounwind uwtable { entry: cmpxchg i32* %a, i32 0, i32 1 release monotonic, !dbg !7 + cmpxchg i32* %a, i32 0, i32 1 release acquire, !dbg !7 + cmpxchg i32* %a, i32 0, i32 1 release seq_cst, !dbg !7 ret void, !dbg !7 } ; CHECK-LABEL: atomic32_cas_release ; CHECK: call i32 @__tsan_atomic32_compare_exchange_val(i32* %a, i32 0, i32 1, i32 3, i32 0), !dbg +; CHECK: call i32 @__tsan_atomic32_compare_exchange_val(i32* %a, i32 0, i32 1, i32 3, i32 2), !dbg +; CHECK: call i32 @__tsan_atomic32_compare_exchange_val(i32* %a, i32 0, i32 1, i32 3, i32 5), !dbg define void @atomic32_cas_acq_rel(i32* %a) nounwind uwtable { entry: + cmpxchg i32* %a, i32 0, i32 1 acq_rel monotonic, !dbg !7 cmpxchg i32* %a, i32 0, i32 1 acq_rel acquire, !dbg !7 + cmpxchg i32* %a, i32 0, i32 1 acq_rel seq_cst, !dbg !7 ret void, !dbg !7 } ; CHECK-LABEL: atomic32_cas_acq_rel +; CHECK: call i32 @__tsan_atomic32_compare_exchange_val(i32* %a, i32 0, i32 1, i32 4, i32 0), !dbg ; CHECK: call i32 @__tsan_atomic32_compare_exchange_val(i32* %a, i32 0, i32 1, i32 4, i32 2), !dbg +; CHECK: call i32 @__tsan_atomic32_compare_exchange_val(i32* %a, i32 0, i32 1, i32 4, i32 5), !dbg define void @atomic32_cas_seq_cst(i32* %a) nounwind uwtable { entry: + cmpxchg i32* %a, i32 0, i32 1 seq_cst monotonic, !dbg !7 + cmpxchg i32* %a, i32 0, i32 1 seq_cst acquire, !dbg !7 cmpxchg i32* %a, i32 0, i32 1 seq_cst seq_cst, !dbg !7 ret void, !dbg !7 } ; CHECK-LABEL: atomic32_cas_seq_cst +; CHECK: call i32 @__tsan_atomic32_compare_exchange_val(i32* %a, i32 0, i32 1, i32 5, i32 0), !dbg +; CHECK: call i32 @__tsan_atomic32_compare_exchange_val(i32* %a, i32 0, i32 1, i32 5, i32 2), !dbg ; CHECK: call i32 @__tsan_atomic32_compare_exchange_val(i32* %a, i32 0, i32 1, i32 5, i32 5), !dbg define i64 @atomic64_load_unordered(i64* %a) nounwind uwtable { @@ -1521,41 +1581,61 @@ define void @atomic64_cas_monotonic(i64* %a) nounwind uwtable { entry: cmpxchg i64* %a, i64 0, i64 1 monotonic monotonic, !dbg !7 + cmpxchg i64* %a, i64 0, i64 1 monotonic acquire, !dbg !7 + cmpxchg i64* %a, i64 0, i64 1 monotonic seq_cst, !dbg !7 ret void, !dbg !7 } ; CHECK-LABEL: atomic64_cas_monotonic ; CHECK: call i64 @__tsan_atomic64_compare_exchange_val(i64* %a, i64 0, i64 1, i32 0, i32 0), !dbg +; CHECK: call i64 @__tsan_atomic64_compare_exchange_val(i64* %a, i64 0, i64 1, i32 0, i32 2), !dbg +; CHECK: call i64 @__tsan_atomic64_compare_exchange_val(i64* %a, i64 0, i64 1, i32 0, i32 5), !dbg define void @atomic64_cas_acquire(i64* %a) nounwind uwtable { entry: + cmpxchg i64* %a, i64 0, i64 1 acquire monotonic, !dbg !7 cmpxchg i64* %a, i64 0, i64 1 acquire acquire, !dbg !7 + cmpxchg i64* %a, i64 0, i64 1 acquire seq_cst, !dbg !7 ret void, !dbg !7 } ; CHECK-LABEL: atomic64_cas_acquire +; CHECK: call i64 @__tsan_atomic64_compare_exchange_val(i64* %a, i64 0, i64 1, i32 2, i32 0), !dbg ; CHECK: call i64 @__tsan_atomic64_compare_exchange_val(i64* %a, i64 0, i64 1, i32 2, i32 2), !dbg +; CHECK: call i64 @__tsan_atomic64_compare_exchange_val(i64* %a, i64 0, i64 1, i32 2, i32 5), !dbg define void @atomic64_cas_release(i64* %a) nounwind uwtable { entry: cmpxchg i64* %a, i64 0, i64 1 release monotonic, !dbg !7 + cmpxchg i64* %a, i64 0, i64 1 release acquire, !dbg !7 + cmpxchg i64* %a, i64 0, i64 1 release seq_cst, !dbg !7 ret void, !dbg !7 } ; CHECK-LABEL: atomic64_cas_release ; CHECK: call i64 @__tsan_atomic64_compare_exchange_val(i64* %a, i64 0, i64 1, i32 3, i32 0), !dbg +; CHECK: call i64 @__tsan_atomic64_compare_exchange_val(i64* %a, i64 0, i64 1, i32 3, i32 2), !dbg +; CHECK: call i64 @__tsan_atomic64_compare_exchange_val(i64* %a, i64 0, i64 1, i32 3, i32 5), !dbg define void @atomic64_cas_acq_rel(i64* %a) nounwind uwtable { entry: + cmpxchg i64* %a, i64 0, i64 1 acq_rel monotonic, !dbg !7 cmpxchg i64* %a, i64 0, i64 1 acq_rel acquire, !dbg !7 + cmpxchg i64* %a, i64 0, i64 1 acq_rel seq_cst, !dbg !7 ret void, !dbg !7 } ; CHECK-LABEL: atomic64_cas_acq_rel +; CHECK: call i64 @__tsan_atomic64_compare_exchange_val(i64* %a, i64 0, i64 1, i32 4, i32 0), !dbg ; CHECK: call i64 @__tsan_atomic64_compare_exchange_val(i64* %a, i64 0, i64 1, i32 4, i32 2), !dbg +; CHECK: call i64 @__tsan_atomic64_compare_exchange_val(i64* %a, i64 0, i64 1, i32 4, i32 5), !dbg define void @atomic64_cas_seq_cst(i64* %a) nounwind uwtable { entry: + cmpxchg i64* %a, i64 0, i64 1 seq_cst monotonic, !dbg !7 + cmpxchg i64* %a, i64 0, i64 1 seq_cst acquire, !dbg !7 cmpxchg i64* %a, i64 0, i64 1 seq_cst seq_cst, !dbg !7 ret void, !dbg !7 } ; CHECK-LABEL: atomic64_cas_seq_cst +; CHECK: call i64 @__tsan_atomic64_compare_exchange_val(i64* %a, i64 0, i64 1, i32 5, i32 0), !dbg +; CHECK: call i64 @__tsan_atomic64_compare_exchange_val(i64* %a, i64 0, i64 1, i32 5, i32 2), !dbg ; CHECK: call i64 @__tsan_atomic64_compare_exchange_val(i64* %a, i64 0, i64 1, i32 5, i32 5), !dbg define void @atomic64_cas_seq_cst_ptr_ty(i8** %a, i8* %v1, i8* %v2) nounwind uwtable {