| //===----------------------------------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| /// |
| /// \file |
| /// Test handling of overly aligned thread local data. |
| /// |
| //===----------------------------------------------------------------------===// |
| |
| #include "hdr/stdint_proxy.h" |
| #include "src/pthread/pthread_create.h" |
| #include "src/pthread/pthread_join.h" |
| #include "test/IntegrationTest/test.h" |
| |
| #include <pthread.h> |
| |
| constexpr size_t ALIGN1 = 64; |
| constexpr size_t ALIGN2 = 128; |
| constexpr size_t ALIGN3 = 256; |
| |
| alignas(ALIGN1) static thread_local int aligned_var1 = 123; |
| alignas(ALIGN2) static thread_local int aligned_var2 = 456; |
| alignas(ALIGN3) static thread_local int aligned_bss_var; |
| |
| struct ThreadTlsPointers { |
| void *ptr1 = nullptr; |
| void *ptr2 = nullptr; |
| void *ptr3 = nullptr; |
| }; |
| |
| static void *thread_func(void *arg) { |
| auto *ptrs = static_cast<ThreadTlsPointers *>(arg); |
| |
| ASSERT_EQ(reinterpret_cast<uintptr_t>(&aligned_var1) % ALIGN1, |
| static_cast<uintptr_t>(0)); |
| ASSERT_EQ(reinterpret_cast<uintptr_t>(&aligned_var2) % ALIGN2, |
| static_cast<uintptr_t>(0)); |
| ASSERT_EQ(reinterpret_cast<uintptr_t>(&aligned_bss_var) % ALIGN3, |
| static_cast<uintptr_t>(0)); |
| ASSERT_EQ(aligned_var1, 123); |
| ASSERT_EQ(aligned_var2, 456); |
| ASSERT_EQ(aligned_bss_var, 0); |
| |
| ptrs->ptr1 = &aligned_var1; |
| ptrs->ptr2 = &aligned_var2; |
| ptrs->ptr3 = &aligned_bss_var; |
| |
| aligned_var1 = 789; |
| aligned_var2 = 101112; |
| aligned_bss_var = 131415; |
| |
| ASSERT_EQ(aligned_var1, 789); |
| ASSERT_EQ(aligned_var2, 101112); |
| ASSERT_EQ(aligned_bss_var, 131415); |
| |
| return nullptr; |
| } |
| |
| TEST_MAIN() { |
| ASSERT_EQ(reinterpret_cast<uintptr_t>(&aligned_var1) % ALIGN1, |
| static_cast<uintptr_t>(0)); |
| ASSERT_EQ(reinterpret_cast<uintptr_t>(&aligned_var2) % ALIGN2, |
| static_cast<uintptr_t>(0)); |
| ASSERT_EQ(reinterpret_cast<uintptr_t>(&aligned_bss_var) % ALIGN3, |
| static_cast<uintptr_t>(0)); |
| ASSERT_EQ(aligned_var1, 123); |
| ASSERT_EQ(aligned_var2, 456); |
| ASSERT_EQ(aligned_bss_var, 0); |
| |
| pthread_t th1; |
| pthread_t th2; |
| ThreadTlsPointers th1_ptrs; |
| ThreadTlsPointers th2_ptrs; |
| void *retval = nullptr; |
| |
| ASSERT_EQ( |
| LIBC_NAMESPACE::pthread_create(&th1, nullptr, thread_func, &th1_ptrs), 0); |
| ASSERT_EQ( |
| LIBC_NAMESPACE::pthread_create(&th2, nullptr, thread_func, &th2_ptrs), 0); |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_join(th1, &retval), 0); |
| ASSERT_EQ(retval, nullptr); |
| ASSERT_EQ(LIBC_NAMESPACE::pthread_join(th2, &retval), 0); |
| ASSERT_EQ(retval, nullptr); |
| |
| // Child thread pointers must not match main thread. |
| ASSERT_NE(th1_ptrs.ptr1, static_cast<void *>(&aligned_var1)); |
| ASSERT_NE(th1_ptrs.ptr2, static_cast<void *>(&aligned_var2)); |
| ASSERT_NE(th1_ptrs.ptr3, static_cast<void *>(&aligned_bss_var)); |
| |
| ASSERT_NE(th2_ptrs.ptr1, static_cast<void *>(&aligned_var1)); |
| ASSERT_NE(th2_ptrs.ptr2, static_cast<void *>(&aligned_var2)); |
| ASSERT_NE(th2_ptrs.ptr3, static_cast<void *>(&aligned_bss_var)); |
| |
| // Child thread pointers must not match each other. |
| ASSERT_NE(th1_ptrs.ptr1, th2_ptrs.ptr1); |
| ASSERT_NE(th1_ptrs.ptr2, th2_ptrs.ptr2); |
| ASSERT_NE(th1_ptrs.ptr3, th2_ptrs.ptr3); |
| |
| // Child thread modifications must not affect main thread. |
| ASSERT_EQ(aligned_var1, 123); |
| ASSERT_EQ(aligned_var2, 456); |
| ASSERT_EQ(aligned_bss_var, 0); |
| |
| return 0; |
| } |