diff options
Diffstat (limited to 'libgcc/config/pa/sync-libfuncs.c')
-rw-r--r-- | libgcc/config/pa/sync-libfuncs.c | 324 |
1 files changed, 324 insertions, 0 deletions
diff --git a/libgcc/config/pa/sync-libfuncs.c b/libgcc/config/pa/sync-libfuncs.c new file mode 100644 index 0000000..c70be0f --- /dev/null +++ b/libgcc/config/pa/sync-libfuncs.c @@ -0,0 +1,324 @@ +/* PA-RISC sync libfunc support. + Copyright (C) 2008-2023 Free Software Foundation, Inc. + Based on code contributed by CodeSourcery for ARM EABI Linux. + Modifications for PA Linux by Helge Deller <deller@gmx.de> + Revised for general use by John David Anglin <danglin@gcc.gnu.org> + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC 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 General Public License +for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +<http://www.gnu.org/licenses/>. */ + +typedef unsigned char u8; +typedef short unsigned int u16; +typedef unsigned int u32; +#ifdef __LP64__ +typedef long unsigned int u64; +#else +typedef long long unsigned int u64; +#endif + +/* PA-RISC 2.0 supports out-of-order execution for loads and stores. + Thus, we need to synchonize memory accesses. For more info, see: + "Advanced Performance Features of the 64-bit PA-8000" by Doug Hunt. */ + +typedef volatile int __attribute__((aligned (16))) ldcw_t; +static ldcw_t __atomicity_lock = 1; + +/* We want default visibility for the sync routines. */ +#undef VISIBILITY +#if defined(__hpux__) && !defined(__LP64__) +#define VISIBILITY +#else +#define VISIBILITY __attribute__ ((visibility ("default"))) +#endif + +/* Perform ldcw operation in cache when possible. The ldcw instruction + is a full barrier. */ +#ifndef _PA_LDCW_INSN +# ifdef _PA_RISC2_0 +# define _PA_LDCW_INSN "ldcw,co" +# else +# define _PA_LDCW_INSN "ldcw" +# endif +#endif + +static inline void +__sync_spin_lock (void) +{ + ldcw_t *lock = &__atomicity_lock; + int tmp; + + __asm__ __volatile__ (_PA_LDCW_INSN " 0(%1),%0\n\t" + "cmpib,<>,n 0,%0,.+20\n\t" + "ldw,ma 0(%1),%0\n\t" + "cmpib,<> 0,%0,.-12\n\t" + "nop\n\t" + "b,n .-12" + : "=&r" (tmp) + : "r" (lock) + : "memory"); +} + +static inline void +__sync_spin_unlock (void) +{ + ldcw_t *lock = &__atomicity_lock; + int tmp = 1; + + /* Use ordered store for release. */ + __asm__ __volatile__ ("stw,ma %1,0(%0)" + : : "r" (lock), "r" (tmp) : "memory"); +} + +/* Load value with an atomic processor load if possible. */ +#define ATOMIC_LOAD(TYPE, WIDTH) \ + static inline TYPE \ + atomic_load_##WIDTH (volatile void *ptr) \ + { \ + return *(volatile TYPE *)ptr; \ + } + +#if defined(__LP64__) || defined(__SOFTFP__) +ATOMIC_LOAD (u64, 8) +#else +static inline u64 +atomic_load_8 (volatile void *ptr) +{ + u64 result; + double tmp; + + asm volatile ("{fldds|fldd} 0(%2),%1\n\t" + "{fstds|fstd} %1,-16(%%sp)\n\t" + "{ldws|ldw} -16(%%sp),%0\n\t" + "{ldws|ldw} -12(%%sp),%R0" + : "=r" (result), "=f" (tmp) : "r" (ptr): "memory"); + return result; +} +#endif + +ATOMIC_LOAD (u32, 4) +ATOMIC_LOAD (u16, 2) +ATOMIC_LOAD (u8, 1) + +/* Store value with an atomic processor store if possible. */ +#define ATOMIC_STORE(TYPE, WIDTH) \ + static inline void \ + atomic_store_##WIDTH (volatile void *ptr, TYPE value) \ + { \ + *(volatile TYPE *)ptr = value; \ + } + +#if defined(__LP64__) || defined(__SOFTFP__) +ATOMIC_STORE (u64, 8) +#else +static inline void +atomic_store_8 (volatile void *ptr, u64 value) +{ + double tmp; + + asm volatile ("stws|stw} %2,-16(%%sp)\n\t" + "{stws|stw} %R2,-12(%%sp)\n\t" + "{fldds|fldd} -16(%%sp),%1\n\t" + "{fstds|fstd} %1,0(%0)" + : "=m" (ptr), "=&f" (tmp) : "r" (value): "memory"); +} +#endif + +ATOMIC_STORE (u32, 4) +ATOMIC_STORE (u16, 2) +ATOMIC_STORE (u8, 1) + +#define FETCH_AND_OP(OP, PFX_OP, INF_OP, TYPE, WIDTH) \ + TYPE VISIBILITY \ + __sync_fetch_and_##OP##_##WIDTH (volatile void *ptr, TYPE val) \ + { \ + TYPE tmp, newval; \ + \ + __sync_spin_lock(); \ + tmp = atomic_load_##WIDTH (ptr); \ + newval = PFX_OP (tmp INF_OP val); \ + atomic_store_##WIDTH (ptr, newval); \ + __sync_spin_unlock(); \ + \ + return tmp; \ + } + +FETCH_AND_OP (add, , +, u64, 8) +FETCH_AND_OP (sub, , -, u64, 8) +FETCH_AND_OP (or, , |, u64, 8) +FETCH_AND_OP (and, , &, u64, 8) +FETCH_AND_OP (xor, , ^, u64, 8) +FETCH_AND_OP (nand, ~, &, u64, 8) + +FETCH_AND_OP (add, , +, u32, 4) +FETCH_AND_OP (sub, , -, u32, 4) +FETCH_AND_OP (or, , |, u32, 4) +FETCH_AND_OP (and, , &, u32, 4) +FETCH_AND_OP (xor, , ^, u32, 4) +FETCH_AND_OP (nand, ~, &, u32, 4) + +FETCH_AND_OP (add, , +, u16, 2) +FETCH_AND_OP (sub, , -, u16, 2) +FETCH_AND_OP (or, , |, u16, 2) +FETCH_AND_OP (and, , &, u16, 2) +FETCH_AND_OP (xor, , ^, u16, 2) +FETCH_AND_OP (nand, ~, &, u16, 2) + +FETCH_AND_OP (add, , +, u8, 1) +FETCH_AND_OP (sub, , -, u8, 1) +FETCH_AND_OP (or, , |, u8, 1) +FETCH_AND_OP (and, , &, u8, 1) +FETCH_AND_OP (xor, , ^, u8, 1) +FETCH_AND_OP (nand, ~, &, u8, 1) + +#define OP_AND_FETCH(OP, PFX_OP, INF_OP, TYPE, WIDTH) \ + TYPE VISIBILITY \ + __sync_##OP##_and_fetch_##WIDTH (volatile void *ptr, TYPE val) \ + { \ + TYPE tmp, newval; \ + \ + __sync_spin_lock(); \ + tmp = atomic_load_##WIDTH (ptr); \ + newval = PFX_OP (tmp INF_OP val); \ + atomic_store_##WIDTH (ptr, newval); \ + __sync_spin_unlock(); \ + \ + return newval; \ + } + +OP_AND_FETCH (add, , +, u64, 8) +OP_AND_FETCH (sub, , -, u64, 8) +OP_AND_FETCH (or, , |, u64, 8) +OP_AND_FETCH (and, , &, u64, 8) +OP_AND_FETCH (xor, , ^, u64, 8) +OP_AND_FETCH (nand, ~, &, u64, 8) + +OP_AND_FETCH (add, , +, u32, 4) +OP_AND_FETCH (sub, , -, u32, 4) +OP_AND_FETCH (or, , |, u32, 4) +OP_AND_FETCH (and, , &, u32, 4) +OP_AND_FETCH (xor, , ^, u32, 4) +OP_AND_FETCH (nand, ~, &, u32, 4) + +OP_AND_FETCH (add, , +, u16, 2) +OP_AND_FETCH (sub, , -, u16, 2) +OP_AND_FETCH (or, , |, u16, 2) +OP_AND_FETCH (and, , &, u16, 2) +OP_AND_FETCH (xor, , ^, u16, 2) +OP_AND_FETCH (nand, ~, &, u16, 2) + +OP_AND_FETCH (add, , +, u8, 1) +OP_AND_FETCH (sub, , -, u8, 1) +OP_AND_FETCH (or, , |, u8, 1) +OP_AND_FETCH (and, , &, u8, 1) +OP_AND_FETCH (xor, , ^, u8, 1) +OP_AND_FETCH (nand, ~, &, u8, 1) + +#define COMPARE_AND_SWAP(TYPE, WIDTH) \ + TYPE VISIBILITY \ + __sync_val_compare_and_swap_##WIDTH (volatile void *ptr, TYPE oldval, \ + TYPE newval) \ + { \ + TYPE actual_oldval; \ + \ + __sync_spin_lock(); \ + actual_oldval = atomic_load_##WIDTH (ptr); \ + if (actual_oldval == oldval) \ + atomic_store_##WIDTH (ptr, newval); \ + __sync_spin_unlock(); \ + \ + return actual_oldval; \ + } \ + \ + _Bool VISIBILITY \ + __sync_bool_compare_and_swap_##WIDTH (volatile void *ptr, \ + TYPE oldval, TYPE newval) \ + { \ + TYPE actual_oldval; \ + _Bool result; \ + \ + __sync_spin_lock(); \ + actual_oldval = atomic_load_##WIDTH (ptr); \ + result = (actual_oldval == oldval); \ + if (result) \ + atomic_store_##WIDTH (ptr, newval); \ + __sync_spin_unlock(); \ + \ + return result; \ + } + +COMPARE_AND_SWAP (u64, 8) +COMPARE_AND_SWAP (u32, 4) +COMPARE_AND_SWAP (u16, 2) +COMPARE_AND_SWAP (u8, 1) + +#define SYNC_LOCK_TEST_AND_SET(TYPE, WIDTH) \ +TYPE VISIBILITY \ + __sync_lock_test_and_set_##WIDTH (volatile void *ptr, TYPE val) \ + { \ + TYPE oldval; \ + \ + __sync_spin_lock(); \ + oldval = atomic_load_##WIDTH (ptr); \ + atomic_store_##WIDTH (ptr, val); \ + __sync_spin_unlock(); \ + \ + return oldval; \ + } + +SYNC_LOCK_TEST_AND_SET (u64, 8) +SYNC_LOCK_TEST_AND_SET (u32, 4) +SYNC_LOCK_TEST_AND_SET (u16, 2) +SYNC_LOCK_TEST_AND_SET (u8, 1) + +#define SYNC_LOCK_RELEASE(TYPE, WIDTH) \ + void VISIBILITY \ + __sync_lock_release_##WIDTH (volatile void *ptr) \ + { \ + TYPE val = 0; \ + \ + __sync_spin_lock(); \ + atomic_store_##WIDTH (ptr, val); \ + __sync_spin_unlock(); \ + } + +SYNC_LOCK_RELEASE (u64, 8) +SYNC_LOCK_RELEASE (u32, 4) +SYNC_LOCK_RELEASE (u16, 2) +SYNC_LOCK_RELEASE (u8, 1) + +#define SYNC_LOCK_LOAD(TYPE, WIDTH) \ +TYPE VISIBILITY __sync_lock_load_##WIDTH (volatile void *); \ +TYPE VISIBILITY \ + __sync_lock_load_##WIDTH (volatile void *ptr) \ + { \ + TYPE oldval; \ + \ + __sync_spin_lock(); \ + oldval = atomic_load_##WIDTH (ptr); \ + __sync_spin_unlock(); \ + \ + return oldval; \ + } + +SYNC_LOCK_LOAD (u64, 8) +SYNC_LOCK_LOAD (u32, 4) +SYNC_LOCK_LOAD (u16, 2) +SYNC_LOCK_LOAD (u8, 1) |