[compiler-rt] [tsan][go] use __builtin_memcpy for 128-bit Go atomic args buffer access (PR #213123)
Mauri de Souza Meneguzzo via llvm-commits
llvm-commits at lists.llvm.org
Thu Sep 17 05:53:45 PDT 2026
https://github.com/mauri870 updated https://github.com/llvm/llvm-project/pull/213123
>From 5b7b04f3e80967a8da97934c98a8f0ad641f8fcd Mon Sep 17 00:00:00 2001
From: Mauri de Souza Meneguzzo <mauri870 at gmail.com>
Date: Thu, 30 Jul 2026 16:23:47 -0300
Subject: [PATCH 1/4] [tsan][go] use __builtin_memcpy for 128-bit Go atomic
args buffer access
The Go 128-bit atomic helpers read and write the 128-bit value
through an a128* cast of the u8* args buffer (e.g. *(a128*)(a + 8)).
Clang treats a128* as requiring 16-byte alignment and emits MOVAPS or
MOVDQA on x86_64. The Go runtime only guarantees 8-byte alignment of the args
buffer, so a+8 may not be 16-byte aligned. The misaligned MOVAPS raises
GP and causes a SIGSEGV.
Replace the direct a128* casts with __builtin_memcpy. Clang emits
MOVDQU (unaligned 128-bit move), which handles any 8-byte-aligned
address correctly. The target address of the atomic operation itself
(*(a128**)a) is always 16-byte aligned from the Go side.
---
compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cpp | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cpp b/compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cpp
index 6e6774eacb645..27071cbea9278 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cpp
@@ -933,7 +933,8 @@ void __tsan_go_atomic64_load(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
# if __TSAN_HAS_INT128
SANITIZER_INTERFACE_ATTRIBUTE
void __tsan_go_atomic128_load(ThreadState* thr, uptr cpc, uptr pc, u8* a) {
- *(a128*)(a + 8) = AtomicGoRet<OpLoad>(thr, cpc, pc, mo_acquire, *(a128**)a);
+ a128 ret = AtomicGoRet<OpLoad>(thr, cpc, pc, mo_acquire, *(a128**)a);
+ __builtin_memcpy(a + 8, &ret, sizeof(ret));
}
# endif
@@ -950,7 +951,9 @@ void __tsan_go_atomic64_store(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
# if __TSAN_HAS_INT128
SANITIZER_INTERFACE_ATTRIBUTE
void __tsan_go_atomic128_store(ThreadState* thr, uptr cpc, uptr pc, u8* a) {
- AtomicGo<OpStore>(thr, cpc, pc, mo_release, *(a128**)a, *(a128*)(a + 8));
+ a128 val;
+ __builtin_memcpy(&val, a + 8, sizeof(val));
+ AtomicGo<OpStore>(thr, cpc, pc, mo_release, *(a128**)a, val);
}
# endif
@@ -1024,9 +1027,11 @@ void __tsan_go_atomic64_compare_exchange(ThreadState *thr, uptr cpc, uptr pc,
SANITIZER_INTERFACE_ATTRIBUTE
void __tsan_go_atomic128_compare_exchange(ThreadState* thr, uptr cpc, uptr pc,
u8* a) {
- a128 cmp = *(a128*)(a + 8);
+ a128 cmp, xch;
+ __builtin_memcpy(&cmp, a + 8, sizeof(cmp));
+ __builtin_memcpy(&xch, a + 24, sizeof(xch));
a128 cur = AtomicGoRet<OpCAS>(thr, cpc, pc, mo_acq_rel, mo_acquire,
- *(a128**)a, cmp, *(a128*)(a + 24));
+ *(a128**)a, cmp, xch);
*(bool*)(a + 40) = (cur == cmp);
}
# endif
>From 8fee1c4e07204b7fa05cb85facc3a2013b7020f1 Mon Sep 17 00:00:00 2001
From: Mauri de Souza Meneguzzo <mauri870 at gmail.com>
Date: Thu, 17 Sep 2026 09:42:10 -0300
Subject: [PATCH 2/4] feedback from code review
---
compiler-rt/lib/tsan/go/test.c | 18 ++++++++++++++++++
.../lib/tsan/rtl/tsan_interface_atomic.cpp | 8 ++++----
2 files changed, 22 insertions(+), 4 deletions(-)
diff --git a/compiler-rt/lib/tsan/go/test.c b/compiler-rt/lib/tsan/go/test.c
index fcd396227a4ab..dfefe5f5cf68e 100644
--- a/compiler-rt/lib/tsan/go/test.c
+++ b/compiler-rt/lib/tsan/go/test.c
@@ -34,6 +34,11 @@ void __tsan_acquire(void *thr, void *addr);
void __tsan_release(void *thr, void *addr);
void __tsan_release_acquire(void *thr, void *addr);
void __tsan_release_merge(void *thr, void *addr);
+#if defined(__SIZEOF_INT128__)
+void __tsan_go_atomic128_load(void *thr, void *cpc, void *pc, char *a);
+void __tsan_go_atomic128_store(void *thr, void *cpc, void *pc, char *a);
+void __tsan_go_atomic128_compare_exchange(void *thr, void *cpc, void *pc, char *a);
+#endif
void *current_proc;
@@ -113,6 +118,19 @@ int main(void) {
__tsan_go_end(thr2);
__tsan_proc_destroy(proc1);
current_proc = proc0;
+#if defined(__SIZEOF_INT128__)
+ {
+ // Align `a` to 16 bytes so `a + 8` and `a + 24` are 8-byte aligned (8 mod 16).
+ __attribute__((aligned(16))) char a[64];
+ *(void **)(a + 0) = buf;
+ __builtin_memset(a + 8, 0x11, 16);
+ __tsan_go_atomic128_store(thr0, (char *)&barfoo + 1, (char *)&barfoo + 1, a);
+ __builtin_memset(a + 8, 0, 16);
+ __tsan_go_atomic128_load(thr0, (char *)&barfoo + 1, (char *)&barfoo + 1, a);
+ __builtin_memset(a + 24, 0x22, 16);
+ __tsan_go_atomic128_compare_exchange(thr0, (char *)&barfoo + 1, (char *)&barfoo + 1, a);
+ }
+#endif
__tsan_fini();
return 0;
}
diff --git a/compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cpp b/compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cpp
index 27071cbea9278..f16336deade7d 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cpp
@@ -934,7 +934,7 @@ void __tsan_go_atomic64_load(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
SANITIZER_INTERFACE_ATTRIBUTE
void __tsan_go_atomic128_load(ThreadState* thr, uptr cpc, uptr pc, u8* a) {
a128 ret = AtomicGoRet<OpLoad>(thr, cpc, pc, mo_acquire, *(a128**)a);
- __builtin_memcpy(a + 8, &ret, sizeof(ret));
+ internal_memcpy(a + 8, &ret, sizeof(ret));
}
# endif
@@ -952,7 +952,7 @@ void __tsan_go_atomic64_store(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
SANITIZER_INTERFACE_ATTRIBUTE
void __tsan_go_atomic128_store(ThreadState* thr, uptr cpc, uptr pc, u8* a) {
a128 val;
- __builtin_memcpy(&val, a + 8, sizeof(val));
+ internal_memcpy(&val, a + 8, sizeof(val));
AtomicGo<OpStore>(thr, cpc, pc, mo_release, *(a128**)a, val);
}
# endif
@@ -1028,8 +1028,8 @@ SANITIZER_INTERFACE_ATTRIBUTE
void __tsan_go_atomic128_compare_exchange(ThreadState* thr, uptr cpc, uptr pc,
u8* a) {
a128 cmp, xch;
- __builtin_memcpy(&cmp, a + 8, sizeof(cmp));
- __builtin_memcpy(&xch, a + 24, sizeof(xch));
+ internal_memcpy(&cmp, a + 8, sizeof(cmp));
+ internal_memcpy(&xch, a + 24, sizeof(xch));
a128 cur = AtomicGoRet<OpCAS>(thr, cpc, pc, mo_acq_rel, mo_acquire,
*(a128**)a, cmp, xch);
*(bool*)(a + 40) = (cur == cmp);
>From ab7d2fac1bf496b1bed0ef20e75caf4849f02431 Mon Sep 17 00:00:00 2001
From: Mauri de Souza Meneguzzo <mauri870 at gmail.com>
Date: Thu, 17 Sep 2026 09:46:38 -0300
Subject: [PATCH 3/4] clarify comment
---
compiler-rt/lib/tsan/go/test.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/compiler-rt/lib/tsan/go/test.c b/compiler-rt/lib/tsan/go/test.c
index dfefe5f5cf68e..7db59c28cd6cc 100644
--- a/compiler-rt/lib/tsan/go/test.c
+++ b/compiler-rt/lib/tsan/go/test.c
@@ -120,7 +120,8 @@ int main(void) {
current_proc = proc0;
#if defined(__SIZEOF_INT128__)
{
- // Align `a` to 16 bytes so `a + 8` and `a + 24` are 8-byte aligned (8 mod 16).
+ // Align `a` to 16 bytes, matching the alignment Go's runtime guarantees for
+ // the buffer it passes to these functions.
__attribute__((aligned(16))) char a[64];
*(void **)(a + 0) = buf;
__builtin_memset(a + 8, 0x11, 16);
>From 33dad6f3ffb2e0d39b8c183f436ca1f344240ffc Mon Sep 17 00:00:00 2001
From: Mauri de Souza Meneguzzo <mauri870 at gmail.com>
Date: Thu, 17 Sep 2026 09:53:29 -0300
Subject: [PATCH 4/4] track memory of buf using tsan_malloc and free
---
compiler-rt/lib/tsan/go/test.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/compiler-rt/lib/tsan/go/test.c b/compiler-rt/lib/tsan/go/test.c
index 7db59c28cd6cc..1ca4cdeb6e0a3 100644
--- a/compiler-rt/lib/tsan/go/test.c
+++ b/compiler-rt/lib/tsan/go/test.c
@@ -123,6 +123,7 @@ int main(void) {
// Align `a` to 16 bytes, matching the alignment Go's runtime guarantees for
// the buffer it passes to these functions.
__attribute__((aligned(16))) char a[64];
+ __tsan_malloc(thr0, (char *)&barfoo + 1, buf, 16);
*(void **)(a + 0) = buf;
__builtin_memset(a + 8, 0x11, 16);
__tsan_go_atomic128_store(thr0, (char *)&barfoo + 1, (char *)&barfoo + 1, a);
@@ -130,6 +131,7 @@ int main(void) {
__tsan_go_atomic128_load(thr0, (char *)&barfoo + 1, (char *)&barfoo + 1, a);
__builtin_memset(a + 24, 0x22, 16);
__tsan_go_atomic128_compare_exchange(thr0, (char *)&barfoo + 1, (char *)&barfoo + 1, a);
+ __tsan_free(buf, 16);
}
#endif
__tsan_fini();
More information about the llvm-commits
mailing list