aboutsummaryrefslogtreecommitdiff
path: root/gcc/config
diff options
context:
space:
mode:
authorJohn David Anglin <danglin@gcc.gnu.org>2023-01-13 19:22:49 +0000
committerJohn David Anglin <danglin@gcc.gnu.org>2023-01-13 19:24:15 +0000
commitcf467fb93b7b92330ddcb9c8fe7c93df45ce8e40 (patch)
tree821c49d285046168caf441f057bb2ab1e499b87c /gcc/config
parent733a1b777f16cd397b43a242d9c31761f66d3da8 (diff)
downloadgcc-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.h3
-rw-r--r--gcc/config/pa/pa.cc4
-rw-r--r--gcc/config/pa/pa.h12
-rw-r--r--gcc/config/pa/pa.md124
-rw-r--r--gcc/config/pa/pa.opt4
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.