[compiler-rt] [llvm] [tsan] add fetch_max, fetch_min, fetch_umax, fetch_umin (PR #211388)
Mauri de Souza Meneguzzo via llvm-commits
llvm-commits at lists.llvm.org
Fri Sep 11 04:11:03 PDT 2026
https://github.com/mauri870 updated https://github.com/llvm/llvm-project/pull/211388
>From 44f3d1ccc01b1a0c8216df20e4a923dc3bd8e69a Mon Sep 17 00:00:00 2001
From: Mauri de Souza Meneguzzo <mauri870 at gmail.com>
Date: Wed, 22 Jul 2026 18:23:17 -0300
Subject: [PATCH 1/2] [tsan] add fetch_max, fetch_min, fetch_umax, fetch_umin
The TSan IR instrumentation pass had no handling for AtomicRMWInst::Max,
Min, UMax, and UMin opcodes. These are emitted by Clang for
std::atomic::fetch_max and fetch_min (introduced in C++26 by P0493R5, and
implemented in libc++ by #180333).
Currently, programs using these operations compiled with -fsanitize=thread
have those atomic accesses left uninstrumented, causing the race detector
to silently miss races involving them.
These max/min sanitizer functions are also needed for instrumentation of new Go
sync/atomic apis in golang/go#63999.
---
.../include/sanitizer/tsan_interface_atomic.h | 56 +++
.../lib/tsan/rtl/tsan_interface_atomic.cpp | 323 ++++++++++++++++++
.../Instrumentation/ThreadSanitizer.cpp | 8 +
.../Instrumentation/ThreadSanitizer/atomic.ll | 166 +++++++++
4 files changed, 553 insertions(+)
diff --git a/compiler-rt/include/sanitizer/tsan_interface_atomic.h b/compiler-rt/include/sanitizer/tsan_interface_atomic.h
index 74ed91efade04..8f1770dd4d815 100644
--- a/compiler-rt/include/sanitizer/tsan_interface_atomic.h
+++ b/compiler-rt/include/sanitizer/tsan_interface_atomic.h
@@ -159,6 +159,62 @@ __tsan_atomic128 SANITIZER_CDECL __tsan_atomic128_fetch_nand(
volatile __tsan_atomic128 *a, __tsan_atomic128 v, int mo);
#endif
+__tsan_atomic8 SANITIZER_CDECL
+__tsan_atomic8_fetch_max(volatile __tsan_atomic8 *a, __tsan_atomic8 v, int mo);
+__tsan_atomic16 SANITIZER_CDECL __tsan_atomic16_fetch_max(
+ volatile __tsan_atomic16 *a, __tsan_atomic16 v, int mo);
+__tsan_atomic32 SANITIZER_CDECL __tsan_atomic32_fetch_max(
+ volatile __tsan_atomic32 *a, __tsan_atomic32 v, int mo);
+__tsan_atomic64 SANITIZER_CDECL __tsan_atomic64_fetch_max(
+ volatile __tsan_atomic64 *a, __tsan_atomic64 v, int mo);
+#if __TSAN_HAS_INT128
+__tsan_atomic128
+ SANITIZER_CDECL __tsan_atomic128_fetch_max(volatile __tsan_atomic128 *a,
+ __tsan_atomic128 v, int mo);
+#endif
+
+__tsan_atomic8 SANITIZER_CDECL
+__tsan_atomic8_fetch_min(volatile __tsan_atomic8 *a, __tsan_atomic8 v, int mo);
+__tsan_atomic16 SANITIZER_CDECL __tsan_atomic16_fetch_min(
+ volatile __tsan_atomic16 *a, __tsan_atomic16 v, int mo);
+__tsan_atomic32 SANITIZER_CDECL __tsan_atomic32_fetch_min(
+ volatile __tsan_atomic32 *a, __tsan_atomic32 v, int mo);
+__tsan_atomic64 SANITIZER_CDECL __tsan_atomic64_fetch_min(
+ volatile __tsan_atomic64 *a, __tsan_atomic64 v, int mo);
+#if __TSAN_HAS_INT128
+__tsan_atomic128
+ SANITIZER_CDECL __tsan_atomic128_fetch_min(volatile __tsan_atomic128 *a,
+ __tsan_atomic128 v, int mo);
+#endif
+
+__tsan_atomic8 SANITIZER_CDECL
+__tsan_atomic8_fetch_umax(volatile __tsan_atomic8 *a, __tsan_atomic8 v, int mo);
+__tsan_atomic16 SANITIZER_CDECL __tsan_atomic16_fetch_umax(
+ volatile __tsan_atomic16 *a, __tsan_atomic16 v, int mo);
+__tsan_atomic32 SANITIZER_CDECL __tsan_atomic32_fetch_umax(
+ volatile __tsan_atomic32 *a, __tsan_atomic32 v, int mo);
+__tsan_atomic64 SANITIZER_CDECL __tsan_atomic64_fetch_umax(
+ volatile __tsan_atomic64 *a, __tsan_atomic64 v, int mo);
+#if __TSAN_HAS_INT128
+__tsan_atomic128
+ SANITIZER_CDECL __tsan_atomic128_fetch_umax(volatile __tsan_atomic128 *a,
+ __tsan_atomic128 v, int mo);
+#endif
+
+__tsan_atomic8 SANITIZER_CDECL
+__tsan_atomic8_fetch_umin(volatile __tsan_atomic8 *a, __tsan_atomic8 v, int mo);
+__tsan_atomic16 SANITIZER_CDECL __tsan_atomic16_fetch_umin(
+ volatile __tsan_atomic16 *a, __tsan_atomic16 v, int mo);
+__tsan_atomic32 SANITIZER_CDECL __tsan_atomic32_fetch_umin(
+ volatile __tsan_atomic32 *a, __tsan_atomic32 v, int mo);
+__tsan_atomic64 SANITIZER_CDECL __tsan_atomic64_fetch_umin(
+ volatile __tsan_atomic64 *a, __tsan_atomic64 v, int mo);
+#if __TSAN_HAS_INT128
+__tsan_atomic128
+ SANITIZER_CDECL __tsan_atomic128_fetch_umin(volatile __tsan_atomic128 *a,
+ __tsan_atomic128 v, int mo);
+#endif
+
int SANITIZER_CDECL __tsan_atomic8_compare_exchange_weak(
volatile __tsan_atomic8 *a, __tsan_atomic8 *c, __tsan_atomic8 v, int mo,
int fail_mo);
diff --git a/compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cpp b/compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cpp
index 6e6774eacb645..734dcff4c6b16 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cpp
@@ -162,6 +162,40 @@ a128 func_nand(volatile a128 *v, a128 op) {
return cmp;
}
+a128 func_max(volatile a128* v, a128 op) {
+ SpinMutexLock lock(&mutex128);
+ a128 cmp = *v;
+ if (op > cmp)
+ *v = op;
+ return cmp;
+}
+
+a128 func_min(volatile a128* v, a128 op) {
+ SpinMutexLock lock(&mutex128);
+ a128 cmp = *v;
+ if (op < cmp)
+ *v = op;
+ return cmp;
+}
+
+a128 func_umax(volatile a128* v, a128 op) {
+ __extension__ typedef unsigned __int128 U;
+ SpinMutexLock lock(&mutex128);
+ a128 cmp = *v;
+ if ((U)op > (U)cmp)
+ *v = op;
+ return cmp;
+}
+
+a128 func_umin(volatile a128* v, a128 op) {
+ __extension__ typedef unsigned __int128 U;
+ SpinMutexLock lock(&mutex128);
+ a128 cmp = *v;
+ if ((U)op < (U)cmp)
+ *v = op;
+ return cmp;
+}
+
a128 func_cas(volatile a128 *v, a128 cmp, a128 xch) {
SpinMutexLock lock(&mutex128);
a128 cur = *v;
@@ -171,6 +205,87 @@ a128 func_cas(volatile a128 *v, a128 cmp, a128 xch) {
}
#endif
+template <typename T>
+T func_max(volatile T* v, T op) {
+ T cmp = *v;
+ for (;;) {
+ if (cmp >= op)
+ return cmp;
+ T cur = __sync_val_compare_and_swap(v, cmp, op);
+ if (cmp == cur)
+ return cmp;
+ cmp = cur;
+ }
+}
+
+template <typename T>
+T func_min(volatile T* v, T op) {
+ T cmp = *v;
+ for (;;) {
+ if (cmp <= op)
+ return cmp;
+ T cur = __sync_val_compare_and_swap(v, cmp, op);
+ if (cmp == cur)
+ return cmp;
+ cmp = cur;
+ }
+}
+
+// Maps each signed interface type to its unsigned counterpart for use in
+// unsigned max/min comparisons.
+template <typename T>
+struct UnsignedFor;
+template <>
+struct UnsignedFor<a8> {
+ typedef unsigned char type;
+};
+template <>
+struct UnsignedFor<a16> {
+ typedef unsigned short type;
+};
+template <>
+struct UnsignedFor<a32> {
+ typedef unsigned int type;
+};
+template <>
+struct UnsignedFor<a64> {
+ typedef unsigned long type;
+};
+#if __TSAN_HAS_INT128
+template <>
+struct UnsignedFor<a128> {
+ __extension__ typedef unsigned __int128 type;
+};
+#endif
+
+template <typename T>
+T func_umax(volatile T* v, T op) {
+ typedef typename UnsignedFor<T>::type U;
+ T cmp = *v;
+ for (;;) {
+ if ((U)cmp >= (U)op)
+ return cmp;
+ T cur = __sync_val_compare_and_swap(v, cmp, op);
+ if (cmp == cur)
+ return cmp;
+ cmp = cur;
+ }
+}
+
+template <typename T>
+T func_umin(volatile T* v, T op) {
+ typedef typename UnsignedFor<T>::type U;
+ T cmp = *v;
+ for (;;) {
+ if ((U)cmp <= (U)op)
+ return cmp;
+ T cur = __sync_val_compare_and_swap(v, cmp, op);
+ if (cmp == cur)
+ return cmp;
+ cmp = cur;
+ }
+}
+
template <typename T>
static int AccessSize() {
if (sizeof(T) <= 1)
@@ -410,6 +525,58 @@ struct OpFetchNand {
}
};
+struct OpFetchMax {
+ template <typename T>
+ [[maybe_unused]] static T NoTsanAtomic(morder mo, volatile T* a, T v) {
+ return func_max(a, v);
+ }
+
+ template <typename T>
+ [[maybe_unused]] static T Atomic(ThreadState* thr, uptr pc, morder mo,
+ volatile T* a, T v) {
+ return AtomicRMW<T, func_max>(thr, pc, a, v, mo);
+ }
+};
+
+struct OpFetchMin {
+ template <typename T>
+ [[maybe_unused]] static T NoTsanAtomic(morder mo, volatile T* a, T v) {
+ return func_min(a, v);
+ }
+
+ template <typename T>
+ [[maybe_unused]] static T Atomic(ThreadState* thr, uptr pc, morder mo,
+ volatile T* a, T v) {
+ return AtomicRMW<T, func_min>(thr, pc, a, v, mo);
+ }
+};
+
+struct OpFetchUMax {
+ template <typename T>
+ [[maybe_unused]] static T NoTsanAtomic(morder mo, volatile T* a, T v) {
+ return func_umax(a, v);
+ }
+
+ template <typename T>
+ [[maybe_unused]] static T Atomic(ThreadState* thr, uptr pc, morder mo,
+ volatile T* a, T v) {
+ return AtomicRMW<T, func_umax>(thr, pc, a, v, mo);
+ }
+};
+
+struct OpFetchUMin {
+ template <typename T>
+ [[maybe_unused]] static T NoTsanAtomic(morder mo, volatile T* a, T v) {
+ return func_umin(a, v);
+ }
+
+ template <typename T>
+ [[maybe_unused]] static T Atomic(ThreadState* thr, uptr pc, morder mo,
+ volatile T* a, T v) {
+ return AtomicRMW<T, func_umin>(thr, pc, a, v, mo);
+ }
+};
+
struct OpCAS {
template <typename T>
static bool NoTsanAtomic(morder mo, morder fmo, volatile T *a, T *c, T v) {
@@ -787,6 +954,114 @@ a128 __tsan_atomic128_fetch_nand(volatile a128 *a, a128 v, int mo) {
}
# endif
+SANITIZER_INTERFACE_ATTRIBUTE
+a8 __tsan_atomic8_fetch_max(volatile a8* a, a8 v, int mo) {
+ return AtomicImpl<OpFetchMax>(to_morder(mo), a, v);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a16 __tsan_atomic16_fetch_max(volatile a16* a, a16 v, int mo) {
+ return AtomicImpl<OpFetchMax>(to_morder(mo), a, v);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a32 __tsan_atomic32_fetch_max(volatile a32* a, a32 v, int mo) {
+ return AtomicImpl<OpFetchMax>(to_morder(mo), a, v);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a64 __tsan_atomic64_fetch_max(volatile a64* a, a64 v, int mo) {
+ return AtomicImpl<OpFetchMax>(to_morder(mo), a, v);
+}
+
+# if __TSAN_HAS_INT128
+SANITIZER_INTERFACE_ATTRIBUTE
+a128 __tsan_atomic128_fetch_max(volatile a128* a, a128 v, int mo) {
+ return AtomicImpl<OpFetchMax>(to_morder(mo), a, v);
+}
+# endif
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a8 __tsan_atomic8_fetch_min(volatile a8* a, a8 v, int mo) {
+ return AtomicImpl<OpFetchMin>(to_morder(mo), a, v);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a16 __tsan_atomic16_fetch_min(volatile a16* a, a16 v, int mo) {
+ return AtomicImpl<OpFetchMin>(to_morder(mo), a, v);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a32 __tsan_atomic32_fetch_min(volatile a32* a, a32 v, int mo) {
+ return AtomicImpl<OpFetchMin>(to_morder(mo), a, v);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a64 __tsan_atomic64_fetch_min(volatile a64* a, a64 v, int mo) {
+ return AtomicImpl<OpFetchMin>(to_morder(mo), a, v);
+}
+
+# if __TSAN_HAS_INT128
+SANITIZER_INTERFACE_ATTRIBUTE
+a128 __tsan_atomic128_fetch_min(volatile a128* a, a128 v, int mo) {
+ return AtomicImpl<OpFetchMin>(to_morder(mo), a, v);
+}
+# endif
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a8 __tsan_atomic8_fetch_umax(volatile a8* a, a8 v, int mo) {
+ return AtomicImpl<OpFetchUMax>(to_morder(mo), a, v);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a16 __tsan_atomic16_fetch_umax(volatile a16* a, a16 v, int mo) {
+ return AtomicImpl<OpFetchUMax>(to_morder(mo), a, v);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a32 __tsan_atomic32_fetch_umax(volatile a32* a, a32 v, int mo) {
+ return AtomicImpl<OpFetchUMax>(to_morder(mo), a, v);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a64 __tsan_atomic64_fetch_umax(volatile a64* a, a64 v, int mo) {
+ return AtomicImpl<OpFetchUMax>(to_morder(mo), a, v);
+}
+
+# if __TSAN_HAS_INT128
+SANITIZER_INTERFACE_ATTRIBUTE
+a128 __tsan_atomic128_fetch_umax(volatile a128* a, a128 v, int mo) {
+ return AtomicImpl<OpFetchUMax>(to_morder(mo), a, v);
+}
+# endif
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a8 __tsan_atomic8_fetch_umin(volatile a8* a, a8 v, int mo) {
+ return AtomicImpl<OpFetchUMin>(to_morder(mo), a, v);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a16 __tsan_atomic16_fetch_umin(volatile a16* a, a16 v, int mo) {
+ return AtomicImpl<OpFetchUMin>(to_morder(mo), a, v);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a32 __tsan_atomic32_fetch_umin(volatile a32* a, a32 v, int mo) {
+ return AtomicImpl<OpFetchUMin>(to_morder(mo), a, v);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a64 __tsan_atomic64_fetch_umin(volatile a64* a, a64 v, int mo) {
+ return AtomicImpl<OpFetchUMin>(to_morder(mo), a, v);
+}
+
+# if __TSAN_HAS_INT128
+SANITIZER_INTERFACE_ATTRIBUTE
+a128 __tsan_atomic128_fetch_umin(volatile a128* a, a128 v, int mo) {
+ return AtomicImpl<OpFetchUMin>(to_morder(mo), a, v);
+}
+# endif
+
SANITIZER_INTERFACE_ATTRIBUTE
int __tsan_atomic8_compare_exchange_strong(volatile a8 *a, a8 *c, a8 v, int mo,
int fmo) {
@@ -1030,5 +1305,53 @@ void __tsan_go_atomic128_compare_exchange(ThreadState* thr, uptr cpc, uptr pc,
*(bool*)(a + 40) = (cur == cmp);
}
# endif
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_go_atomic32_fetch_max(ThreadState* thr, uptr cpc, uptr pc, u8* a) {
+ *(a32*)(a + 16) = AtomicGoRet<OpFetchMax>(thr, cpc, pc, mo_acq_rel, *(a32**)a,
+ *(a32*)(a + 8));
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_go_atomic64_fetch_max(ThreadState* thr, uptr cpc, uptr pc, u8* a) {
+ *(a64*)(a + 16) = AtomicGoRet<OpFetchMax>(thr, cpc, pc, mo_acq_rel, *(a64**)a,
+ *(a64*)(a + 8));
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_go_atomic32_fetch_min(ThreadState* thr, uptr cpc, uptr pc, u8* a) {
+ *(a32*)(a + 16) = AtomicGoRet<OpFetchMin>(thr, cpc, pc, mo_acq_rel, *(a32**)a,
+ *(a32*)(a + 8));
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_go_atomic64_fetch_min(ThreadState* thr, uptr cpc, uptr pc, u8* a) {
+ *(a64*)(a + 16) = AtomicGoRet<OpFetchMin>(thr, cpc, pc, mo_acq_rel, *(a64**)a,
+ *(a64*)(a + 8));
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_go_atomic32_fetch_umax(ThreadState* thr, uptr cpc, uptr pc, u8* a) {
+ *(a32*)(a + 16) = AtomicGoRet<OpFetchUMax>(thr, cpc, pc, mo_acq_rel,
+ *(a32**)a, *(a32*)(a + 8));
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_go_atomic64_fetch_umax(ThreadState* thr, uptr cpc, uptr pc, u8* a) {
+ *(a64*)(a + 16) = AtomicGoRet<OpFetchUMax>(thr, cpc, pc, mo_acq_rel,
+ *(a64**)a, *(a64*)(a + 8));
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_go_atomic32_fetch_umin(ThreadState* thr, uptr cpc, uptr pc, u8* a) {
+ *(a32*)(a + 16) = AtomicGoRet<OpFetchUMin>(thr, cpc, pc, mo_acq_rel,
+ *(a32**)a, *(a32*)(a + 8));
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_go_atomic64_fetch_umin(ThreadState* thr, uptr cpc, uptr pc, u8* a) {
+ *(a64*)(a + 16) = AtomicGoRet<OpFetchUMin>(thr, cpc, pc, mo_acq_rel,
+ *(a64**)a, *(a64*)(a + 8));
+}
} // extern "C"
#endif // #if !SANITIZER_GO
diff --git a/llvm/lib/Transforms/Instrumentation/ThreadSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/ThreadSanitizer.cpp
index f05efd863fb74..cd0c36b863d43 100644
--- a/llvm/lib/Transforms/Instrumentation/ThreadSanitizer.cpp
+++ b/llvm/lib/Transforms/Instrumentation/ThreadSanitizer.cpp
@@ -303,6 +303,14 @@ void ThreadSanitizer::initialize(Module &M, const TargetLibraryInfo &TLI) {
NamePart = "_fetch_xor";
else if (Op == AtomicRMWInst::Nand)
NamePart = "_fetch_nand";
+ else if (Op == AtomicRMWInst::Max)
+ NamePart = "_fetch_max";
+ else if (Op == AtomicRMWInst::Min)
+ NamePart = "_fetch_min";
+ else if (Op == AtomicRMWInst::UMax)
+ NamePart = "_fetch_umax";
+ else if (Op == AtomicRMWInst::UMin)
+ NamePart = "_fetch_umin";
else
continue;
SmallString<32> RMWName("__tsan_atomic" + itostr(BitSize) + NamePart);
diff --git a/llvm/test/Instrumentation/ThreadSanitizer/atomic.ll b/llvm/test/Instrumentation/ThreadSanitizer/atomic.ll
index 8b387cd496297..73c74e4f1d406 100644
--- a/llvm/test/Instrumentation/ThreadSanitizer/atomic.ll
+++ b/llvm/test/Instrumentation/ThreadSanitizer/atomic.ll
@@ -2016,6 +2016,102 @@ entry:
; CHECK-LABEL: atomic128_nand_seq_cst
; CHECK: call i128 @__tsan_atomic128_fetch_nand(ptr %a, i128 0, i32 5), !dbg
+define void @atomic8_max_monotonic(ptr %a) nounwind uwtable {
+entry:
+ atomicrmw max ptr %a, i8 0 monotonic, !dbg !7
+ ret void, !dbg !7
+}
+; CHECK-LABEL: atomic8_max_monotonic
+; CHECK: call i8 @__tsan_atomic8_fetch_max(ptr %a, i8 0, i32 0), !dbg
+
+define void @atomic16_max_monotonic(ptr %a) nounwind uwtable {
+entry:
+ atomicrmw max ptr %a, i16 0 monotonic, !dbg !7
+ ret void, !dbg !7
+}
+; CHECK-LABEL: atomic16_max_monotonic
+; CHECK: call i16 @__tsan_atomic16_fetch_max(ptr %a, i16 0, i32 0), !dbg
+
+define void @atomic32_max_monotonic(ptr %a) nounwind uwtable {
+entry:
+ atomicrmw max ptr %a, i32 0 monotonic, !dbg !7
+ ret void, !dbg !7
+}
+; CHECK-LABEL: atomic32_max_monotonic
+; CHECK: call i32 @__tsan_atomic32_fetch_max(ptr %a, i32 0, i32 0), !dbg
+
+define void @atomic64_max_monotonic(ptr %a) nounwind uwtable {
+entry:
+ atomicrmw max ptr %a, i64 0 monotonic, !dbg !7
+ ret void, !dbg !7
+}
+; CHECK-LABEL: atomic64_max_monotonic
+; CHECK: call i64 @__tsan_atomic64_fetch_max(ptr %a, i64 0, i32 0), !dbg
+
+define void @atomic32_max_seq_cst(ptr %a) nounwind uwtable {
+entry:
+ atomicrmw max ptr %a, i32 0 seq_cst, !dbg !7
+ ret void, !dbg !7
+}
+; CHECK-LABEL: atomic32_max_seq_cst
+; CHECK: call i32 @__tsan_atomic32_fetch_max(ptr %a, i32 0, i32 5), !dbg
+
+define void @atomic64_max_seq_cst(ptr %a) nounwind uwtable {
+entry:
+ atomicrmw max ptr %a, i64 0 seq_cst, !dbg !7
+ ret void, !dbg !7
+}
+; CHECK-LABEL: atomic64_max_seq_cst
+; CHECK: call i64 @__tsan_atomic64_fetch_max(ptr %a, i64 0, i32 5), !dbg
+
+define void @atomic32_min_monotonic(ptr %a) nounwind uwtable {
+entry:
+ atomicrmw min ptr %a, i32 0 monotonic, !dbg !7
+ ret void, !dbg !7
+}
+; CHECK-LABEL: atomic32_min_monotonic
+; CHECK: call i32 @__tsan_atomic32_fetch_min(ptr %a, i32 0, i32 0), !dbg
+
+define void @atomic64_min_monotonic(ptr %a) nounwind uwtable {
+entry:
+ atomicrmw min ptr %a, i64 0 monotonic, !dbg !7
+ ret void, !dbg !7
+}
+; CHECK-LABEL: atomic64_min_monotonic
+; CHECK: call i64 @__tsan_atomic64_fetch_min(ptr %a, i64 0, i32 0), !dbg
+
+define void @atomic32_umax_monotonic(ptr %a) nounwind uwtable {
+entry:
+ atomicrmw umax ptr %a, i32 0 monotonic, !dbg !7
+ ret void, !dbg !7
+}
+; CHECK-LABEL: atomic32_umax_monotonic
+; CHECK: call i32 @__tsan_atomic32_fetch_umax(ptr %a, i32 0, i32 0), !dbg
+
+define void @atomic64_umax_monotonic(ptr %a) nounwind uwtable {
+entry:
+ atomicrmw umax ptr %a, i64 0 monotonic, !dbg !7
+ ret void, !dbg !7
+}
+; CHECK-LABEL: atomic64_umax_monotonic
+; CHECK: call i64 @__tsan_atomic64_fetch_umax(ptr %a, i64 0, i32 0), !dbg
+
+define void @atomic32_umin_monotonic(ptr %a) nounwind uwtable {
+entry:
+ atomicrmw umin ptr %a, i32 0 monotonic, !dbg !7
+ ret void, !dbg !7
+}
+; CHECK-LABEL: atomic32_umin_monotonic
+; CHECK: call i32 @__tsan_atomic32_fetch_umin(ptr %a, i32 0, i32 0), !dbg
+
+define void @atomic64_umin_monotonic(ptr %a) nounwind uwtable {
+entry:
+ atomicrmw umin ptr %a, i64 0 monotonic, !dbg !7
+ ret void, !dbg !7
+}
+; CHECK-LABEL: atomic64_umin_monotonic
+; CHECK: call i64 @__tsan_atomic64_fetch_umin(ptr %a, i64 0, i32 0), !dbg
+
define void @atomic128_cas_monotonic(ptr %a) nounwind uwtable {
entry:
cmpxchg ptr %a, i128 0, i128 1 monotonic monotonic, !dbg !7
@@ -2120,11 +2216,41 @@ entry:
; CHECK-LABEL: atomic_thread_fence_seq_cst
; CHECK: call void @__tsan_atomic_thread_fence(i32 5), !dbg
+; CHECK: declare i8 @__tsan_atomic8_fetch_max(ptr, i8, i32)
+; EXT: declare signext i8 @__tsan_atomic8_fetch_max(ptr, i8 signext, i32 signext)
+; MIPS_EXT: declare i8 @__tsan_atomic8_fetch_max(ptr, i8 signext, i32 signext)
+; LA_EXT: declare signext i8 @__tsan_atomic8_fetch_max(ptr, i8 signext, i32 signext)
+
+; CHECK: declare i16 @__tsan_atomic16_fetch_max(ptr, i16, i32)
+; EXT: declare signext i16 @__tsan_atomic16_fetch_max(ptr, i16 signext, i32 signext)
+; MIPS_EXT: declare i16 @__tsan_atomic16_fetch_max(ptr, i16 signext, i32 signext)
+; LA_EXT: declare signext i16 @__tsan_atomic16_fetch_max(ptr, i16 signext, i32 signext)
+
; CHECK: declare void @__tsan_atomic32_store(ptr, i32, i32)
; EXT: declare void @__tsan_atomic32_store(ptr, i32 signext, i32 signext)
; MIPS_EXT: declare void @__tsan_atomic32_store(ptr, i32 signext, i32 signext)
; LA_EXT: declare void @__tsan_atomic32_store(ptr, i32 signext, i32 signext)
+; CHECK: declare i32 @__tsan_atomic32_fetch_max(ptr, i32, i32)
+; EXT: declare signext i32 @__tsan_atomic32_fetch_max(ptr, i32 signext, i32 signext)
+; MIPS_EXT: declare i32 @__tsan_atomic32_fetch_max(ptr, i32 signext, i32 signext)
+; LA_EXT: declare signext i32 @__tsan_atomic32_fetch_max(ptr, i32 signext, i32 signext)
+
+; CHECK: declare i32 @__tsan_atomic32_fetch_min(ptr, i32, i32)
+; EXT: declare signext i32 @__tsan_atomic32_fetch_min(ptr, i32 signext, i32 signext)
+; MIPS_EXT: declare i32 @__tsan_atomic32_fetch_min(ptr, i32 signext, i32 signext)
+; LA_EXT: declare signext i32 @__tsan_atomic32_fetch_min(ptr, i32 signext, i32 signext)
+
+; CHECK: declare i32 @__tsan_atomic32_fetch_umax(ptr, i32, i32)
+; EXT: declare signext i32 @__tsan_atomic32_fetch_umax(ptr, i32 signext, i32 signext)
+; MIPS_EXT: declare i32 @__tsan_atomic32_fetch_umax(ptr, i32 signext, i32 signext)
+; LA_EXT: declare signext i32 @__tsan_atomic32_fetch_umax(ptr, i32 signext, i32 signext)
+
+; CHECK: declare i32 @__tsan_atomic32_fetch_umin(ptr, i32, i32)
+; EXT: declare signext i32 @__tsan_atomic32_fetch_umin(ptr, i32 signext, i32 signext)
+; MIPS_EXT: declare i32 @__tsan_atomic32_fetch_umin(ptr, i32 signext, i32 signext)
+; LA_EXT: declare signext i32 @__tsan_atomic32_fetch_umin(ptr, i32 signext, i32 signext)
+
; CHECK: declare i32 @__tsan_atomic32_compare_exchange_val(ptr, i32, i32, i32, i32)
; EXT: declare signext i32 @__tsan_atomic32_compare_exchange_val(ptr, i32 signext, i32 signext, i32 signext, i32 signext)
; MIPS_EXT: declare i32 @__tsan_atomic32_compare_exchange_val(ptr, i32 signext, i32 signext, i32 signext, i32 signext)
@@ -2145,11 +2271,51 @@ entry:
; MIPS_EXT: declare i64 @__tsan_atomic64_fetch_add(ptr, i64, i32 signext)
; LA_EXT: declare i64 @__tsan_atomic64_fetch_add(ptr, i64, i32 signext)
+; CHECK: declare i64 @__tsan_atomic64_fetch_max(ptr, i64, i32)
+; EXT: declare i64 @__tsan_atomic64_fetch_max(ptr, i64, i32 signext)
+; MIPS_EXT: declare i64 @__tsan_atomic64_fetch_max(ptr, i64, i32 signext)
+; LA_EXT: declare i64 @__tsan_atomic64_fetch_max(ptr, i64, i32 signext)
+
+; CHECK: declare i64 @__tsan_atomic64_fetch_min(ptr, i64, i32)
+; EXT: declare i64 @__tsan_atomic64_fetch_min(ptr, i64, i32 signext)
+; MIPS_EXT: declare i64 @__tsan_atomic64_fetch_min(ptr, i64, i32 signext)
+; LA_EXT: declare i64 @__tsan_atomic64_fetch_min(ptr, i64, i32 signext)
+
+; CHECK: declare i64 @__tsan_atomic64_fetch_umax(ptr, i64, i32)
+; EXT: declare i64 @__tsan_atomic64_fetch_umax(ptr, i64, i32 signext)
+; MIPS_EXT: declare i64 @__tsan_atomic64_fetch_umax(ptr, i64, i32 signext)
+; LA_EXT: declare i64 @__tsan_atomic64_fetch_umax(ptr, i64, i32 signext)
+
+; CHECK: declare i64 @__tsan_atomic64_fetch_umin(ptr, i64, i32)
+; EXT: declare i64 @__tsan_atomic64_fetch_umin(ptr, i64, i32 signext)
+; MIPS_EXT: declare i64 @__tsan_atomic64_fetch_umin(ptr, i64, i32 signext)
+; LA_EXT: declare i64 @__tsan_atomic64_fetch_umin(ptr, i64, i32 signext)
+
; CHECK: declare i64 @__tsan_atomic64_compare_exchange_val(ptr, i64, i64, i32, i32)
; EXT: declare i64 @__tsan_atomic64_compare_exchange_val(ptr, i64, i64, i32 signext, i32 signext)
; MIPS_EXT: declare i64 @__tsan_atomic64_compare_exchange_val(ptr, i64, i64, i32 signext, i32 signext)
; LA_EXT: declare i64 @__tsan_atomic64_compare_exchange_val(ptr, i64, i64, i32 signext, i32 signext)
+; CHECK: declare i128 @__tsan_atomic128_fetch_max(ptr, i128, i32)
+; EXT: declare i128 @__tsan_atomic128_fetch_max(ptr, i128, i32 signext)
+; MIPS_EXT: declare i128 @__tsan_atomic128_fetch_max(ptr, i128, i32 signext)
+; LA_EXT: declare i128 @__tsan_atomic128_fetch_max(ptr, i128, i32 signext)
+
+; CHECK: declare i128 @__tsan_atomic128_fetch_min(ptr, i128, i32)
+; EXT: declare i128 @__tsan_atomic128_fetch_min(ptr, i128, i32 signext)
+; MIPS_EXT: declare i128 @__tsan_atomic128_fetch_min(ptr, i128, i32 signext)
+; LA_EXT: declare i128 @__tsan_atomic128_fetch_min(ptr, i128, i32 signext)
+
+; CHECK: declare i128 @__tsan_atomic128_fetch_umax(ptr, i128, i32)
+; EXT: declare i128 @__tsan_atomic128_fetch_umax(ptr, i128, i32 signext)
+; MIPS_EXT: declare i128 @__tsan_atomic128_fetch_umax(ptr, i128, i32 signext)
+; LA_EXT: declare i128 @__tsan_atomic128_fetch_umax(ptr, i128, i32 signext)
+
+; CHECK: declare i128 @__tsan_atomic128_fetch_umin(ptr, i128, i32)
+; EXT: declare i128 @__tsan_atomic128_fetch_umin(ptr, i128, i32 signext)
+; MIPS_EXT: declare i128 @__tsan_atomic128_fetch_umin(ptr, i128, i32 signext)
+; LA_EXT: declare i128 @__tsan_atomic128_fetch_umin(ptr, i128, i32 signext)
+
; CHECK: declare void @__tsan_atomic_thread_fence(i32)
; EXT: declare void @__tsan_atomic_thread_fence(i32 signext)
; MIPS_EXT: declare void @__tsan_atomic_thread_fence(i32 signext)
>From b4c5b3ec564528c814b16f283ea1865c47b0de98 Mon Sep 17 00:00:00 2001
From: Mauri de Souza Meneguzzo <mauri870 at gmail.com>
Date: Fri, 24 Jul 2026 16:55:21 -0300
Subject: [PATCH 2/2] add missing atomic128 tests
---
.../Instrumentation/ThreadSanitizer/atomic.ll | 32 +++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/llvm/test/Instrumentation/ThreadSanitizer/atomic.ll b/llvm/test/Instrumentation/ThreadSanitizer/atomic.ll
index 73c74e4f1d406..d3b258e55c48f 100644
--- a/llvm/test/Instrumentation/ThreadSanitizer/atomic.ll
+++ b/llvm/test/Instrumentation/ThreadSanitizer/atomic.ll
@@ -2112,6 +2112,38 @@ entry:
; CHECK-LABEL: atomic64_umin_monotonic
; CHECK: call i64 @__tsan_atomic64_fetch_umin(ptr %a, i64 0, i32 0), !dbg
+define void @atomic128_max_monotonic(ptr %a) nounwind uwtable {
+entry:
+ atomicrmw max ptr %a, i128 0 monotonic, !dbg !7
+ ret void, !dbg !7
+}
+; CHECK-LABEL: atomic128_max_monotonic
+; CHECK: call i128 @__tsan_atomic128_fetch_max(ptr %a, i128 0, i32 0), !dbg
+
+define void @atomic128_min_monotonic(ptr %a) nounwind uwtable {
+entry:
+ atomicrmw min ptr %a, i128 0 monotonic, !dbg !7
+ ret void, !dbg !7
+}
+; CHECK-LABEL: atomic128_min_monotonic
+; CHECK: call i128 @__tsan_atomic128_fetch_min(ptr %a, i128 0, i32 0), !dbg
+
+define void @atomic128_umax_monotonic(ptr %a) nounwind uwtable {
+entry:
+ atomicrmw umax ptr %a, i128 0 monotonic, !dbg !7
+ ret void, !dbg !7
+}
+; CHECK-LABEL: atomic128_umax_monotonic
+; CHECK: call i128 @__tsan_atomic128_fetch_umax(ptr %a, i128 0, i32 0), !dbg
+
+define void @atomic128_umin_monotonic(ptr %a) nounwind uwtable {
+entry:
+ atomicrmw umin ptr %a, i128 0 monotonic, !dbg !7
+ ret void, !dbg !7
+}
+; CHECK-LABEL: atomic128_umin_monotonic
+; CHECK: call i128 @__tsan_atomic128_fetch_umin(ptr %a, i128 0, i32 0), !dbg
+
define void @atomic128_cas_monotonic(ptr %a) nounwind uwtable {
entry:
cmpxchg ptr %a, i128 0, i128 1 monotonic monotonic, !dbg !7
More information about the llvm-commits
mailing list