diff options
author | John David Anglin <danglin@gcc.gnu.org> | 2023-01-13 19:22:49 +0000 |
---|---|---|
committer | John David Anglin <danglin@gcc.gnu.org> | 2023-01-13 19:24:15 +0000 |
commit | cf467fb93b7b92330ddcb9c8fe7c93df45ce8e40 (patch) | |
tree | 821c49d285046168caf441f057bb2ab1e499b87c /gcc/config | |
parent | 733a1b777f16cd397b43a242d9c31761f66d3da8 (diff) | |
download | gcc-cf467fb93b7b92330ddcb9c8fe7c93df45ce8e40.zip gcc-cf467fb93b7b92330ddcb9c8fe7c93df45ce8e40.tar.gz gcc-cf467fb93b7b92330ddcb9c8fe7c93df45ce8e40.tar.bz2 |
Fix support for atomic loads and stores on hppa.
This change updates the atomic libcall support to fix the following
issues:
1) A internal compiler error with -fno-sync-libcalls.
2) When sync libcalls are disabled, we don't generate libcalls for
libatomic.
3) There is no sync libcall support for targets other than linux.
As a result, non-atomic stores are silently emitted for types
smaller or equal to the word size. There are now a few atomic
libcalls in the libgcc code, so we need sync support on all
targets.
2023-01-13 John David Anglin <danglin@gcc.gnu.org>
gcc/ChangeLog:
* config/pa/pa-linux.h (TARGET_SYNC_LIBCALL): Delete define.
* config/pa/pa.cc (pa_init_libfuncs): Use MAX_SYNC_LIBFUNC_SIZE
define.
* config/pa/pa.h (TARGET_SYNC_LIBCALLS): Use flag_sync_libcalls.
(MAX_SYNC_LIBFUNC_SIZE): Define.
(TARGET_CPU_CPP_BUILTINS): Define __SOFTFP__ when soft float is
enabled.
* config/pa/pa.md (atomic_storeqi): Emit __atomic_exchange_1
libcall when sync libcalls are disabled.
(atomic_storehi, atomic_storesi, atomic_storedi): Likewise.
(atomic_loaddi): Emit __atomic_load_8 libcall when sync libcalls
are disabled on 32-bit target.
* config/pa/pa.opt (matomic-libcalls): New option.
* doc/invoke.texi (HPPA Options): Update.
libgcc/ChangeLog:
* config.host (hppa*64*-*-linux*): Adjust tmake_file to use
pa/t-pa64-linux.
(hppa*64*-*-hpux11*): Adjust tmake_file to use pa/t-pa64-hpux
instead of pa/t-hpux and pa/t-pa64.
* config/pa/linux-atomic.c: Define u32 type.
(ATOMIC_LOAD): Define new macro to implement atomic_load_1,
atomic_load_2, atomic_load_4 and atomic_load_8. Update sync
defines to use atomic_load calls for type.
(SYNC_LOCK_LOAD_2): New macro to implement __sync_lock_load_8.
* config/pa/sync-libfuncs.c: New file.
* config/pa/t-netbsd (LIB2ADD_ST): Define.
* config/pa/t-openbsd (LIB2ADD_ST): Define.
* config/pa/t-pa64-hpux: New file.
* config/pa/t-pa64-linux: New file.
Diffstat (limited to 'gcc/config')
-rw-r--r-- | gcc/config/pa/pa-linux.h | 3 | ||||
-rw-r--r-- | gcc/config/pa/pa.cc | 4 | ||||
-rw-r--r-- | gcc/config/pa/pa.h | 12 | ||||
-rw-r--r-- | gcc/config/pa/pa.md | 124 | ||||
-rw-r--r-- | gcc/config/pa/pa.opt | 4 |
5 files changed, 121 insertions, 26 deletions
diff --git a/gcc/config/pa/pa-linux.h b/gcc/config/pa/pa-linux.h index 5af11a1..1073f42 100644 --- a/gcc/config/pa/pa-linux.h +++ b/gcc/config/pa/pa-linux.h @@ -133,9 +133,6 @@ along with GCC; see the file COPYING3. If not see #undef TARGET_GAS #define TARGET_GAS 1 -#undef TARGET_SYNC_LIBCALL -#define TARGET_SYNC_LIBCALL 1 - /* The SYNC operations are implemented as library functions, not INSN patterns. As a result, the HAVE defines for the patterns are not defined. We need to define them to generate the corresponding diff --git a/gcc/config/pa/pa.cc b/gcc/config/pa/pa.cc index 9f43802..b43a91f 100644 --- a/gcc/config/pa/pa.cc +++ b/gcc/config/pa/pa.cc @@ -5940,8 +5940,8 @@ pa_init_libfuncs (void) "_U_Qfcnvxf_udbl_to_quad"); } - if (TARGET_SYNC_LIBCALL) - init_sync_libfuncs (8); + if (TARGET_SYNC_LIBCALLS) + init_sync_libfuncs (MAX_SYNC_LIBFUNC_SIZE); } /* HP's millicode routines mean something special to the assembler. diff --git a/gcc/config/pa/pa.h b/gcc/config/pa/pa.h index bafdf60..93d6f53 100644 --- a/gcc/config/pa/pa.h +++ b/gcc/config/pa/pa.h @@ -72,10 +72,12 @@ extern unsigned long total_code_bytes; #define HPUX_LONG_DOUBLE_LIBRARY 0 #endif -/* Linux kernel atomic operation support. */ -#ifndef TARGET_SYNC_LIBCALL -#define TARGET_SYNC_LIBCALL 0 -#endif +/* Sync libcall support. */ +#define TARGET_SYNC_LIBCALLS (flag_sync_libcalls) + +/* The maximum size of the sync library functions supported. DImode + is supported on 32-bit targets using floating point loads and stores. */ +#define MAX_SYNC_LIBFUNC_SIZE 8 /* The following three defines are potential target switches. The current defines are optimal given the current capabilities of GAS and GNU ld. */ @@ -173,6 +175,8 @@ do { \ builtin_define("_PA_RISC1_0"); \ if (HPUX_LONG_DOUBLE_LIBRARY) \ builtin_define("__SIZEOF_FLOAT128__=16"); \ + if (TARGET_SOFT_FLOAT) \ + builtin_define("__SOFTFP__"); \ } while (0) /* An old set of OS defines for various BSD-like systems. */ diff --git a/gcc/config/pa/pa.md b/gcc/config/pa/pa.md index 4138227..71f391f 100644 --- a/gcc/config/pa/pa.md +++ b/gcc/config/pa/pa.md @@ -10360,7 +10360,23 @@ add,l %2,%3,%3\;bv,n %%r0(%3)" ;; doubleword loads and stores are not guaranteed to be atomic ;; when referencing the I/O address space. -;; These patterns are at the bottom so the non atomic versions are preferred. +;; Atomic and sync libcalls use different lock sets. Great care is +;; needed if both are used in a single application. + +;; Atomic load and store libcalls are enabled by the -matomic-libcalls +;; option. This option is not enabled by default as the generated +;; libcalls depend on libatomic which is not built until the end of +;; the gcc build. For loads, we only need an atomic libcall for DImode. +;; Sync libcalls are not generated when atomic libcalls are enabled. + +;; Sync libcalls are enabled by default when supported. They can be +;; disabled by the -fno-sync-libcalls option. Sync libcalls always +;; use a single memory store in their implementation, even for DImode. +;; DImode stores are done using either std or fstd. Thus, we only +;; need a sync load libcall for DImode when we don't have an atomic +;; processor load available for the mode (TARGET_SOFT_FLOAT). + +;; Implement atomic QImode store using exchange. (define_expand "atomic_storeqi" [(match_operand:QI 0 "memory_operand") ;; memory @@ -10368,19 +10384,30 @@ add,l %2,%3,%3\;bv,n %%r0(%3)" (match_operand:SI 2 "const_int_operand")] ;; model "" { - if (TARGET_SYNC_LIBCALL) + rtx addr, libfunc; + + if (TARGET_SYNC_LIBCALLS) { - rtx libfunc = optab_libfunc (sync_lock_test_and_set_optab, QImode); - rtx addr = convert_memory_address (Pmode, XEXP (operands[0], 0)); + addr = convert_memory_address (Pmode, XEXP (operands[0], 0)); + libfunc = optab_libfunc (sync_lock_test_and_set_optab, QImode); + emit_library_call (libfunc, LCT_NORMAL, VOIDmode, addr, Pmode, + operands[1], QImode); + DONE; + } + if (TARGET_ATOMIC_LIBCALLS) + { + addr = convert_memory_address (Pmode, XEXP (operands[0], 0)); + libfunc = init_one_libfunc ("__atomic_exchange_1"); emit_library_call (libfunc, LCT_NORMAL, VOIDmode, addr, Pmode, operands[1], QImode); DONE; } + FAIL; }) -;; Implement atomic HImode stores using exchange. +;; Implement atomic HImode store using exchange. (define_expand "atomic_storehi" [(match_operand:HI 0 "memory_operand") ;; memory @@ -10388,15 +10415,26 @@ add,l %2,%3,%3\;bv,n %%r0(%3)" (match_operand:SI 2 "const_int_operand")] ;; model "" { - if (TARGET_SYNC_LIBCALL) + rtx addr, libfunc; + + if (TARGET_SYNC_LIBCALLS) { - rtx libfunc = optab_libfunc (sync_lock_test_and_set_optab, HImode); - rtx addr = convert_memory_address (Pmode, XEXP (operands[0], 0)); + addr = convert_memory_address (Pmode, XEXP (operands[0], 0)); + libfunc = optab_libfunc (sync_lock_test_and_set_optab, HImode); + emit_library_call (libfunc, LCT_NORMAL, VOIDmode, addr, Pmode, + operands[1], HImode); + DONE; + } + if (TARGET_ATOMIC_LIBCALLS) + { + addr = convert_memory_address (Pmode, XEXP (operands[0], 0)); + libfunc = init_one_libfunc ("__atomic_exchange_2"); emit_library_call (libfunc, LCT_NORMAL, VOIDmode, addr, Pmode, operands[1], HImode); DONE; } + FAIL; }) @@ -10408,20 +10446,39 @@ add,l %2,%3,%3\;bv,n %%r0(%3)" (match_operand:SI 2 "const_int_operand")] ;; model "" { - if (TARGET_SYNC_LIBCALL) + rtx addr, libfunc; + + if (TARGET_SYNC_LIBCALLS) { - rtx libfunc = optab_libfunc (sync_lock_test_and_set_optab, SImode); - rtx addr = convert_memory_address (Pmode, XEXP (operands[0], 0)); + addr = convert_memory_address (Pmode, XEXP (operands[0], 0)); + libfunc = optab_libfunc (sync_lock_test_and_set_optab, SImode); + emit_library_call (libfunc, LCT_NORMAL, VOIDmode, addr, Pmode, + operands[1], SImode); + DONE; + } + if (TARGET_ATOMIC_LIBCALLS) + { + addr = convert_memory_address (Pmode, XEXP (operands[0], 0)); + libfunc = init_one_libfunc ("__atomic_exchange_4"); emit_library_call (libfunc, LCT_NORMAL, VOIDmode, addr, Pmode, operands[1], SImode); DONE; } + FAIL; }) ;; Implement atomic DImode load. +;; We need an atomic or sync libcall whenever the processor load or +;; store used for DImode is not atomic. The 32-bit libatomic +;; implementation uses a pair of stw instructions. They are not +;; atomic, so we need to call __atomic_load_8. The linux libgcc +;; sync implementation uses a std or fstd instruction. They are +;; atomic, so we only need to call __sync_load_8 when the load +;; operation would not be atomic (e.g., 32-bit TARGET_SOFT_FLOAT). + (define_expand "atomic_loaddi" [(match_operand:DI 0 "register_operand") ;; val out (match_operand:DI 1 "memory_operand") ;; memory @@ -10429,12 +10486,35 @@ add,l %2,%3,%3\;bv,n %%r0(%3)" "" { enum memmodel model; + rtx addr, libfunc; - if (TARGET_64BIT || TARGET_SOFT_FLOAT) + if (TARGET_64BIT) FAIL; + if (TARGET_SYNC_LIBCALLS && MAX_SYNC_LIBFUNC_SIZE >= 8 && TARGET_SOFT_FLOAT) + { + addr = convert_memory_address (Pmode, XEXP (operands[1], 0)); + libfunc = init_one_libfunc ("__sync_load_8"); + emit_library_call_value (libfunc, operands[0], LCT_NORMAL, DImode, + addr, Pmode); + DONE; + } + + if (TARGET_ATOMIC_LIBCALLS && TARGET_SOFT_FLOAT) + { + addr = convert_memory_address (Pmode, XEXP (operands[1], 0)); + libfunc = init_one_libfunc ("__atomic_load_8"); + emit_library_call_value (libfunc, operands[0], LCT_NORMAL, DImode, + addr, Pmode); + DONE; + } + + if (TARGET_SOFT_FLOAT) + FAIL; + + /* Fallback to processor load with barriers. */ model = memmodel_from_int (INTVAL (operands[2])); - operands[1] = force_reg (SImode, XEXP (operands[1], 0)); + operands[1] = force_reg (Pmode, XEXP (operands[1], 0)); if (is_mm_seq_cst (model)) expand_mem_thread_fence (model); emit_insn (gen_atomic_loaddi_1 (operands[0], operands[1])); @@ -10460,12 +10540,21 @@ add,l %2,%3,%3\;bv,n %%r0(%3)" "" { enum memmodel model; + rtx addr, libfunc; - if (TARGET_SYNC_LIBCALL) + if (TARGET_SYNC_LIBCALLS && MAX_SYNC_LIBFUNC_SIZE >= 8) { - rtx libfunc = optab_libfunc (sync_lock_test_and_set_optab, DImode); - rtx addr = convert_memory_address (Pmode, XEXP (operands[0], 0)); + addr = convert_memory_address (Pmode, XEXP (operands[0], 0)); + libfunc = optab_libfunc (sync_lock_test_and_set_optab, DImode); + emit_library_call (libfunc, LCT_NORMAL, VOIDmode, addr, Pmode, + operands[1], DImode); + DONE; + } + if (TARGET_ATOMIC_LIBCALLS) + { + addr = convert_memory_address (Pmode, XEXP (operands[0], 0)); + libfunc = init_one_libfunc ("__atomic_exchange_8"); emit_library_call (libfunc, LCT_NORMAL, VOIDmode, addr, Pmode, operands[1], DImode); DONE; @@ -10474,8 +10563,9 @@ add,l %2,%3,%3\;bv,n %%r0(%3)" if (TARGET_64BIT || TARGET_SOFT_FLOAT) FAIL; + /* Fallback to processor store with barriers. */ model = memmodel_from_int (INTVAL (operands[2])); - operands[0] = force_reg (SImode, XEXP (operands[0], 0)); + operands[0] = force_reg (Pmode, XEXP (operands[0], 0)); if (operands[1] != CONST0_RTX (DImode)) operands[1] = force_reg (DImode, operands[1]); expand_mem_thread_fence (model); diff --git a/gcc/config/pa/pa.opt b/gcc/config/pa/pa.opt index 58b59f8..2d074f5 100644 --- a/gcc/config/pa/pa.opt +++ b/gcc/config/pa/pa.opt @@ -37,6 +37,10 @@ march=2.0 Target RejectNegative Generate PA2.0 code (requires binutils 2.10 or later). +matomic-libcalls +Target Var(TARGET_ATOMIC_LIBCALLS) Init(1) +Generate libcalls for atomic loads and stores when sync libcalls are disabled. + mbig-switch Target Ignore Does nothing. Preserved for backward compatibility. |