diff options
| author | Adhemerval Zanella <adhemerval.zanella@linaro.org> | 2019-11-12 19:11:10 +0000 |
|---|---|---|
| committer | Adhemerval Zanella <adhemerval.zanella@linaro.org> | 2019-11-27 10:31:13 -0300 |
| commit | 3b5ebe85aabfa44583a18a7ef51bc4d387e362c1 (patch) | |
| tree | 8853a1bcbe48f93c1e696ae89e503922845c6e8c | |
| parent | 5d9b7b9fa734c5381e0295c85c0e40520d9f6063 (diff) | |
| download | glibc-3b5ebe85aabfa44583a18a7ef51bc4d387e362c1.tar.xz glibc-3b5ebe85aabfa44583a18a7ef51bc4d387e362c1.zip | |
sparc: Use atomic compiler builtins on sparc
This patch removes the arch-specific atomic instruction, relying on
compiler builtins. The __sparc32_atomic_locks support is removed
and a configure check is added to check if compiler uses libatomic
to implement CAS.
It also removes the sparc specific sem_* and pthread_barrier_*
implementations. It in turn allows buidling against a LEON3/LEON4
sparcv8 target, although it will still be incompatible with generic
sparcv9.
Checked on sparcv9-linux-gnu and sparc64-linux-gnu. I also checked
with build against sparcv8-linux-gnu with -mcpu=leon3.
Tested-by: Andreas Larsson <andreas@gaisler.com>
| -rw-r--r-- | sysdeps/sparc/Makefile | 9 | ||||
| -rw-r--r-- | sysdeps/sparc/atomic-machine.h | 95 | ||||
| -rw-r--r-- | sysdeps/sparc/cpu_relax.c (renamed from sysdeps/sparc/sparc64/cpu_relax.c) | 4 | ||||
| -rw-r--r-- | sysdeps/sparc/sparc32/atomic-machine.h | 363 | ||||
| -rw-r--r-- | sysdeps/sparc/sparc32/configure | 35 | ||||
| -rw-r--r-- | sysdeps/sparc/sparc32/configure.ac | 24 | ||||
| -rw-r--r-- | sysdeps/sparc/sparc32/lowlevellock.c | 53 | ||||
| -rw-r--r-- | sysdeps/sparc/sparc32/pthread_barrier_wait.c | 1 | ||||
| -rw-r--r-- | sysdeps/sparc/sparc32/sem_post.c | 82 | ||||
| -rw-r--r-- | sysdeps/sparc/sparc32/sem_waitcommon.c | 146 | ||||
| -rw-r--r-- | sysdeps/sparc/sparc32/sparcv9/Makefile | 9 | ||||
| -rw-r--r-- | sysdeps/sparc/sparc32/sparcv9/atomic-machine.h | 108 | ||||
| -rw-r--r-- | sysdeps/sparc/sparc32/sparcv9/cpu_relax.c | 1 | ||||
| -rw-r--r-- | sysdeps/sparc/sparc64/Makefile | 9 | ||||
| -rw-r--r-- | sysdeps/sparc/sparc64/atomic-machine.h | 129 | ||||
| -rw-r--r-- | sysdeps/unix/sysv/linux/sparc/lowlevellock.h | 130 |
16 files changed, 166 insertions, 1032 deletions
diff --git a/sysdeps/sparc/Makefile b/sysdeps/sparc/Makefile index 3f0c096400..38b33af6e0 100644 --- a/sysdeps/sparc/Makefile +++ b/sysdeps/sparc/Makefile @@ -16,5 +16,14 @@ CPPFLAGS-crti.S += -fPIC CPPFLAGS-crtn.S += -fPIC endif +# nscd uses atomic_spin_nop which in turn requires cpu_relax +ifeq ($(subdir),nscd) +routines += cpu_relax +endif + +ifeq ($(subdir), nptl) +libpthread-routines += cpu_relax +endif + # The assembler on SPARC needs the -fPIC flag even when it's assembler code. ASFLAGS-.os += -fPIC diff --git a/sysdeps/sparc/atomic-machine.h b/sysdeps/sparc/atomic-machine.h new file mode 100644 index 0000000000..4453184c9c --- /dev/null +++ b/sysdeps/sparc/atomic-machine.h @@ -0,0 +1,95 @@ +/* Atomic operations. Sparc version. + Copyright (C) 2019 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <https://www.gnu.org/licenses/>. */ + +#ifndef _ATOMIC_MACHINE_H +#define _ATOMIC_MACHINE_H 1 + +#include <stdint.h> + +typedef int8_t atomic8_t; +typedef uint8_t uatomic8_t; +typedef int_fast8_t atomic_fast8_t; +typedef uint_fast8_t uatomic_fast8_t; + +typedef int16_t atomic16_t; +typedef uint16_t uatomic16_t; +typedef int_fast16_t atomic_fast16_t; +typedef uint_fast16_t uatomic_fast16_t; + +typedef int32_t atomic32_t; +typedef uint32_t uatomic32_t; +typedef int_fast32_t atomic_fast32_t; +typedef uint_fast32_t uatomic_fast32_t; + +typedef int64_t atomic64_t; +typedef uint64_t uatomic64_t; +typedef int_fast64_t atomic_fast64_t; +typedef uint_fast64_t uatomic_fast64_t; + +typedef intptr_t atomicptr_t; +typedef uintptr_t uatomicptr_t; +typedef intmax_t atomic_max_t; +typedef uintmax_t uatomic_max_t; + +#ifdef __arch64__ +# define __HAVE_64B_ATOMICS 1 +#else +# define __HAVE_64B_ATOMICS 0 +#endif +#define USE_ATOMIC_COMPILER_BUILTINS 1 + +/* XXX Is this actually correct? */ +#define ATOMIC_EXCHANGE_USES_CAS __HAVE_64B_ATOMICS + +/* Compare and exchange. + For all "bool" routines, we return FALSE if exchange succesful. */ + +#define __arch_compare_and_exchange_val_int(mem, newval, oldval, model) \ + ({ \ + typeof (*mem) __oldval = (oldval); \ + __atomic_compare_exchange_n (mem, (void *) &__oldval, newval, 0, \ + model, __ATOMIC_RELAXED); \ + __oldval; \ + }) + +#define atomic_compare_and_exchange_val_acq(mem, new, old) \ + ({ \ + __typeof ((__typeof (*(mem))) *(mem)) __result; \ + if (sizeof (*mem) == 4 \ + || (__HAVE_64B_ATOMICS && sizeof (*mem) == 8)) \ + __result = __arch_compare_and_exchange_val_int (mem, new, old, \ + __ATOMIC_ACQUIRE); \ + else \ + abort (); \ + __result; \ + }) + +#ifdef __sparc_v9__ +# define atomic_full_barrier() \ + __asm __volatile ("membar #LoadLoad | #LoadStore" \ + " | #StoreLoad | #StoreStore" : : : "memory") +# define atomic_read_barrier() \ + __asm __volatile ("membar #LoadLoad | #LoadStore" : : : "memory") +# define atomic_write_barrier() \ + __asm __volatile ("membar #LoadStore | #StoreStore" : : : "memory") + +extern void __cpu_relax (void); +# define atomic_spin_nop() __cpu_relax () +#endif + +#endif /* _ATOMIC_MACHINE_H */ diff --git a/sysdeps/sparc/sparc64/cpu_relax.c b/sysdeps/sparc/cpu_relax.c index b86afdec2c..973dfb5935 100644 --- a/sysdeps/sparc/sparc64/cpu_relax.c +++ b/sysdeps/sparc/cpu_relax.c @@ -1,4 +1,4 @@ -/* CPU strand yielding for busy loops. Linux/sparc64 version. +/* CPU strand yielding for busy loops. Linux/sparc version. Copyright (C) 2017-2019 Free Software Foundation, Inc. This file is part of the GNU C Library. @@ -18,6 +18,7 @@ #include <sparc-ifunc.h> +#ifdef __sparc_v9__ static void __cpu_relax_generic (void) { @@ -36,3 +37,4 @@ sparc_libc_ifunc (__cpu_relax, hwcap & HWCAP_SPARC_PAUSE ? __cpu_relax_pause : __cpu_relax_generic) +#endif diff --git a/sysdeps/sparc/sparc32/atomic-machine.h b/sysdeps/sparc/sparc32/atomic-machine.h deleted file mode 100644 index d82a4f4ecc..0000000000 --- a/sysdeps/sparc/sparc32/atomic-machine.h +++ /dev/null @@ -1,363 +0,0 @@ -/* Atomic operations. sparc32 version. - Copyright (C) 2003-2019 Free Software Foundation, Inc. - This file is part of the GNU C Library. - Contributed by Jakub Jelinek <jakub@redhat.com>, 2003. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library; if not, see - <https://www.gnu.org/licenses/>. */ - -#ifndef _ATOMIC_MACHINE_H -#define _ATOMIC_MACHINE_H 1 - -#include <stdint.h> - -typedef int8_t atomic8_t; -typedef uint8_t uatomic8_t; -typedef int_fast8_t atomic_fast8_t; -typedef uint_fast8_t uatomic_fast8_t; - -typedef int16_t atomic16_t; -typedef uint16_t uatomic16_t; -typedef int_fast16_t atomic_fast16_t; -typedef uint_fast16_t uatomic_fast16_t; - -typedef int32_t atomic32_t; -typedef uint32_t uatomic32_t; -typedef int_fast32_t atomic_fast32_t; -typedef uint_fast32_t uatomic_fast32_t; - -typedef int64_t atomic64_t; -typedef uint64_t uatomic64_t; -typedef int_fast64_t atomic_fast64_t; -typedef uint_fast64_t uatomic_fast64_t; - -typedef intptr_t atomicptr_t; -typedef uintptr_t uatomicptr_t; -typedef intmax_t atomic_max_t; -typedef uintmax_t uatomic_max_t; - -#define __HAVE_64B_ATOMICS 0 -#define USE_ATOMIC_COMPILER_BUILTINS 0 - -/* XXX Is this actually correct? */ -#define ATOMIC_EXCHANGE_USES_CAS 1 - - -/* We have no compare and swap, just test and set. - The following implementation contends on 64 global locks - per library and assumes no variable will be accessed using atomic.h - macros from two different libraries. */ - -__make_section_unallocated - (".gnu.linkonce.b.__sparc32_atomic_locks, \"aw\", %nobits"); - -volatile unsigned char __sparc32_atomic_locks[64] - __attribute__ ((nocommon, section (".gnu.linkonce.b.__sparc32_atomic_locks" - __sec_comment), - visibility ("hidden"))); - -#define __sparc32_atomic_do_lock(addr) \ - do \ - { \ - unsigned int __old_lock; \ - unsigned int __idx = (((long) addr >> 2) ^ ((long) addr >> 12)) \ - & 63; \ - do \ - __asm __volatile ("ldstub %1, %0" \ - : "=r" (__old_lock), \ - "=m" (__sparc32_atomic_locks[__idx]) \ - : "m" (__sparc32_atomic_locks[__idx]) \ - : "memory"); \ - while (__old_lock); \ - } \ - while (0) - -#define __sparc32_atomic_do_unlock(addr) \ - do \ - { \ - __sparc32_atomic_locks[(((long) addr >> 2) \ - ^ ((long) addr >> 12)) & 63] = 0; \ - __asm __volatile ("" ::: "memory"); \ - } \ - while (0) - -#define __sparc32_atomic_do_lock24(addr) \ - do \ - { \ - unsigned int __old_lock; \ - do \ - __asm __volatile ("ldstub %1, %0" \ - : "=r" (__old_lock), "=m" (*(addr)) \ - : "m" (*(addr)) \ - : "memory"); \ - while (__old_lock); \ - } \ - while (0) - -#define __sparc32_atomic_do_unlock24(addr) \ - do \ - { \ - __asm __volatile ("" ::: "memory"); \ - *(char *) (addr) = 0; \ - } \ - while (0) - - -#ifndef SHARED -# define __v9_compare_and_exchange_val_32_acq(mem, newval, oldval) \ -({union { __typeof (oldval) a; uint32_t v; } oldval_arg = { .a = (oldval) }; \ - union { __typeof (newval) a; uint32_t v; } newval_arg = { .a = (newval) }; \ - register uint32_t __acev_tmp __asm ("%g6"); \ - register __typeof (mem) __acev_mem __asm ("%g1") = (mem); \ - register uint32_t __acev_oldval __asm ("%g5"); \ - __acev_tmp = newval_arg.v; \ - __acev_oldval = oldval_arg.v; \ - /* .word 0xcde05005 is cas [%g1], %g5, %g6. Can't use cas here though, \ - because as will then mark the object file as V8+ arch. */ \ - __asm __volatile (".word 0xcde05005" \ - : "+r" (__acev_tmp), "=m" (*__acev_mem) \ - : "r" (__acev_oldval), "m" (*__acev_mem), \ - "r" (__acev_mem) : "memory"); \ - (__typeof (oldval)) __acev_tmp; }) -#endif - -/* The only basic operation needed is compare and exchange. */ -#define __v7_compare_and_exchange_val_acq(mem, newval, oldval) \ - ({ __typeof (mem) __acev_memp = (mem); \ - __typeof (*mem) __acev_ret; \ - __typeof (*mem) __acev_newval = (newval); \ - \ - __sparc32_atomic_do_lock (__acev_memp); \ - __acev_ret = *__acev_memp; \ - if (__acev_ret == (oldval)) \ - *__acev_memp = __acev_newval; \ - __sparc32_atomic_do_unlock (__acev_memp); \ - __acev_ret; }) - -#define __v7_compare_and_exchange_bool_acq(mem, newval, oldval) \ - ({ __typeof (mem) __aceb_memp = (mem); \ - int __aceb_ret; \ - __typeof (*mem) __aceb_newval = (newval); \ - \ - __sparc32_atomic_do_lock (__aceb_memp); \ - __aceb_ret = 0; \ - if (*__aceb_memp == (oldval)) \ - *__aceb_memp = __aceb_newval; \ - else \ - __aceb_ret = 1; \ - __sparc32_atomic_do_unlock (__aceb_memp); \ - __aceb_ret; }) - -#define __v7_exchange_acq(mem, newval) \ - ({ __typeof (mem) __acev_memp = (mem); \ - __typeof (*mem) __acev_ret; \ - __typeof (*mem) __acev_newval = (newval); \ - \ - __sparc32_atomic_do_lock (__acev_memp); \ - __acev_ret = *__acev_memp; \ - *__acev_memp = __acev_newval; \ - __sparc32_atomic_do_unlock (__acev_memp); \ - __acev_ret; }) - -#define __v7_exchange_and_add(mem, value) \ - ({ __typeof (mem) __acev_memp = (mem); \ - __typeof (*mem) __acev_ret; \ - \ - __sparc32_atomic_do_lock (__acev_memp); \ - __acev_ret = *__acev_memp; \ - *__acev_memp = __acev_ret + (value); \ - __sparc32_atomic_do_unlock (__acev_memp); \ - __acev_ret; }) - -/* Special versions, which guarantee that top 8 bits of all values - are cleared and use those bits as the ldstub lock. */ -#define __v7_compare_and_exchange_val_24_acq(mem, newval, oldval) \ - ({ __typeof (mem) __acev_memp = (mem); \ - __typeof (*mem) __acev_ret; \ - __typeof (*mem) __acev_newval = (newval); \ - \ - __sparc32_atomic_do_lock24 (__acev_memp); \ - __acev_ret = *__acev_memp & 0xffffff; \ - if (__acev_ret == (oldval)) \ - *__acev_memp = __acev_newval; \ - else \ - __sparc32_atomic_do_unlock24 (__acev_memp); \ - __asm __volatile ("" ::: "memory"); \ - __acev_ret; }) - -#define __v7_exchange_24_rel(mem, newval) \ - ({ __typeof (mem) __acev_memp = (mem); \ - __typeof (*mem) __acev_ret; \ - __typeof (*mem) __acev_newval = (newval); \ - \ - __sparc32_atomic_do_lock24 (__acev_memp); \ - __acev_ret = *__acev_memp & 0xffffff; \ - *__acev_memp = __acev_newval; \ - __asm __volatile ("" ::: "memory"); \ - __acev_ret; }) - -#ifdef SHARED - -/* When dynamically linked, we assume pre-v9 libraries are only ever - used on pre-v9 CPU. */ -# define __atomic_is_v9 0 - -# define atomic_compare_and_exchange_val_acq(mem, newval, oldval) \ - __v7_compare_and_exchange_val_acq (mem, newval, oldval) - -# define atomic_compare_and_exchange_bool_acq(mem, newval, oldval) \ - __v7_compare_and_exchange_bool_acq (mem, newval, oldval) - -# define atomic_exchange_acq(mem, newval) \ - __v7_exchange_acq (mem, newval) - -# define atomic_exchange_and_add(mem, value) \ - __v7_exchange_and_add (mem, value) - -# define atomic_compare_and_exchange_val_24_acq(mem, newval, oldval) \ - ({ \ - if (sizeof (*mem) != 4) \ - abort (); \ - __v7_compare_and_exchange_val_24_acq (mem, newval, oldval); }) - -# define atomic_exchange_24_rel(mem, newval) \ - ({ \ - if (sizeof (*mem) != 4) \ - abort (); \ - __v7_exchange_24_rel (mem, newval); }) - -# define atomic_full_barrier() __asm ("" ::: "memory") -# define atomic_read_barrier() atomic_full_barrier () -# define atomic_write_barrier() atomic_full_barrier () - -#else - -/* In libc.a/libpthread.a etc. we don't know if we'll be run on - pre-v9 or v9 CPU. To be interoperable with dynamically linked - apps on v9 CPUs e.g. with process shared primitives, use cas insn - on v9 CPUs and ldstub on pre-v9. */ - -extern uint64_t _dl_hwcap __attribute__((weak)); -# define __atomic_is_v9 \ - (__builtin_expect (&_dl_hwcap != 0, 1) \ - && __builtin_expect (_dl_hwcap & HWCAP_SPARC_V9, HWCAP_SPARC_V9)) - -# define atomic_compare_and_exchange_val_acq(mem, newval, oldval) \ - ({ \ - __typeof (*mem) __acev_wret; \ - if (sizeof (*mem) != 4) \ - abort (); \ - if (__atomic_is_v9) \ - __acev_wret \ - = __v9_compare_and_exchange_val_32_acq (mem, newval, oldval);\ - else \ - __acev_wret \ - = __v7_compare_and_exchange_val_acq (mem, newval, oldval); \ - __acev_wret; }) - -# define atomic_compare_and_exchange_bool_acq(mem, newval, oldval) \ - ({ \ - int __acev_wret; \ - if (sizeof (*mem) != 4) \ - abort (); \ - if (__atomic_is_v9) \ - { \ - __typeof (oldval) __acev_woldval = (oldval); \ - __acev_wret \ - = __v9_compare_and_exchange_val_32_acq (mem, newval, \ - __acev_woldval) \ - != __acev_woldval; \ - } \ - else \ - __acev_wret \ - = __v7_compare_and_exchange_bool_acq (mem, newval, oldval); \ - __acev_wret; }) - -# define atomic_exchange_rel(mem, newval) \ - ({ \ - __typeof (*mem) __acev_wret; \ - if (sizeof (*mem) != 4) \ - abort (); \ - if (__atomic_is_v9) \ - { \ - __typeof (mem) __acev_wmemp = (mem); \ - __typeof (*(mem)) __acev_wval = (newval); \ - do \ - __acev_wret = *__acev_wmemp; \ - while (__builtin_expect \ - (__v9_compare_and_exchange_val_32_acq (__acev_wmemp,\ - __acev_wval, \ - __acev_wret) \ - != __acev_wret, 0)); \ - } \ - else \ - __acev_wret = __v7_exchange_acq (mem, newval); \ - __acev_wret; }) - -# define atomic_compare_and_exchange_val_24_acq(mem, newval, oldval) \ - ({ \ - __typeof (*mem) __acev_wret; \ - if (sizeof (*mem) != 4) \ - abort (); \ - if (__atomic_is_v9) \ - __acev_wret \ - = __v9_compare_and_exchange_val_32_acq (mem, newval, oldval);\ - else \ - __acev_wret \ - = __v7_compare_and_exchange_val_24_acq (mem, newval, oldval);\ - __acev_wret; }) - -# define atomic_exchange_24_rel(mem, newval) \ - ({ \ - __typeof (*mem) __acev_w24ret; \ - if (sizeof (*mem) != 4) \ - abort (); \ - if (__atomic_is_v9) \ - __acev_w24ret = atomic_exchange_rel (mem, newval); \ - else \ - __acev_w24ret = __v7_exchange_24_rel (mem, newval); \ - __acev_w24ret; }) - -#define atomic_full_barrier() \ - do { \ - if (__atomic_is_v9) \ - /* membar #LoadLoad | #LoadStore | #StoreLoad | #StoreStore */ \ - __asm __volatile (".word 0x8143e00f" : : : "memory"); \ - else \ - __asm __volatile ("" : : : "memory"); \ - } while (0) - -#define atomic_read_barrier() \ - do { \ - if (__atomic_is_v9) \ - /* membar #LoadLoad | #LoadStore */ \ - __asm __volatile (".word 0x8143e005" : : : "memory"); \ - else \ - __asm __volatile ("" : : : "memory"); \ - } while (0) - -#define atomic_write_barrier() \ - do { \ - if (__atomic_is_v9) \ - /* membar #LoadStore | #StoreStore */ \ - __asm __volatile (".word 0x8143e00c" : : : "memory"); \ - else \ - __asm __volatile ("" : : : "memory"); \ - } while (0) - -#endif - -#include <sysdep.h> - -#endif /* atomic-machine.h */ diff --git a/sysdeps/sparc/sparc32/configure b/sysdeps/sparc/sparc32/configure index d7f16b1de0..b6ec0e7d8f 100644 --- a/sysdeps/sparc/sparc32/configure +++ b/sysdeps/sparc/sparc32/configure @@ -160,3 +160,38 @@ $as_echo "$libc_cv_sparcv8" >&6; } if test $libc_cv_sparcv8 = no; then as_fn_error $? "no support for pre-v8 sparc" "$LINENO" 5 fi + +# Test if compiler generates external calls to libatomic for CAS operation. +# It is suffice to check for int only and the test is similar of C11 +# atomic_compare_exchange_strong using GCC builtins. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for external libatomic calls" >&5 +$as_echo_n "checking for external libatomic calls... " >&6; } +if ${libc_ |
