aboutsummaryrefslogtreecommitdiff
path: root/libgcc/config/pa/sync-libfuncs.c
diff options
context:
space:
mode:
Diffstat (limited to 'libgcc/config/pa/sync-libfuncs.c')
-rw-r--r--libgcc/config/pa/sync-libfuncs.c324
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)