diff options
author | Ben Gamari <bgamari.foss@gmail.com> | 2018-02-03 11:37:01 -0500 |
---|---|---|
committer | Ben Gamari <ben@smart-cactus.org> | 2018-02-03 11:37:14 -0500 |
commit | 217e4170bdce3df28a667803ce5e619553bfecdd (patch) | |
tree | 1e548bae29aecc72ce257dc962025c1388a7a7b5 | |
parent | fdf518c708dc5a34ae810c5d5f3a4db812d226f0 (diff) | |
download | haskell-217e4170bdce3df28a667803ce5e619553bfecdd.tar.gz |
ghc-prim: Emulate C11 atomics when not available
GCC's __sync primitives apparently "usually" imply a full barrier,
meaning they can be used to emulate the more precise C11 atomics albeit
with a loss of efficiency. This restores compatibility with GCC 4.4.
This partially reverts commit 59de290928e6903337f31c1f8107ac8a98ea145d.
Test Plan: Validate on Centos
Reviewers: hvr, simonmar, trommler
Subscribers: rwbarton, thomie, erikd, carter
GHC Trac Issues: #14244
Differential Revision: https://phabricator.haskell.org/D4364
-rw-r--r-- | aclocal.m4 | 25 | ||||
-rw-r--r-- | configure.ac | 5 | ||||
-rw-r--r-- | libraries/ghc-prim/cbits/atomic.c | 36 | ||||
-rw-r--r-- | mk/config.mk.in | 1 | ||||
-rw-r--r-- | mk/warnings.mk | 4 | ||||
-rw-r--r-- | rts/ghc.mk | 2 |
6 files changed, 70 insertions, 3 deletions
diff --git a/aclocal.m4 b/aclocal.m4 index 5989a13ce6..2ed2c08327 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -1237,15 +1237,18 @@ if test -z "$CC" then AC_MSG_ERROR([gcc is required]) fi +GccLT46=NO AC_CACHE_CHECK([version of gcc], [fp_cv_gcc_version], [ # Be sure only to look at the first occurrence of the "version " string; # Some Apple compilers emit multiple messages containing this string. fp_cv_gcc_version="`$CC -v 2>&1 | sed -n -e '1,/version /s/.*version [[^0-9]]*\([[0-9.]]*\).*/\1/p'`" - FP_COMPARE_VERSIONS([$fp_cv_gcc_version], [-lt], [4.7], - [AC_MSG_ERROR([Need at least gcc version 4.7])]) + FP_COMPARE_VERSIONS([$fp_cv_gcc_version], [-lt], [4.4], + [AC_MSG_ERROR([Need at least gcc version 4.4 (4.7+ recommended)])]) + FP_COMPARE_VERSIONS([$fp_cv_gcc_version], [-lt], [4.6], GccLT46=YES) ]) AC_SUBST([GccVersion], [$fp_cv_gcc_version]) +AC_SUBST(GccLT46) ])# FP_GCC_VERSION dnl Check to see if the C compiler is clang or llvm-gcc @@ -1278,6 +1281,24 @@ AC_SUBST(GccIsClang) rm -f conftest.txt ]) +# FP_GCC_SUPPORTS__ATOMICS +# ------------------------ +# Does gcc support the __atomic_* family of builtins? +AC_DEFUN([FP_GCC_SUPPORTS__ATOMICS], +[ + AC_REQUIRE([AC_PROG_CC]) + AC_MSG_CHECKING([whether GCC supports __atomic_ builtins]) + echo 'int test(int *x) { int y; __atomic_load(&x, &y, __ATOMIC_SEQ_CST); return x; }' > conftest.c + if $CC -c conftest.c > /dev/null 2>&1; then + CONF_GCC_SUPPORTS__ATOMICS=YES + AC_MSG_RESULT([yes]) + else + CONF_GCC_SUPPORTS__ATOMICS=NO + AC_MSG_RESULT([no]) + fi + rm -f conftest.c conftest.o +]) + # FP_GCC_SUPPORTS_NO_PIE # ---------------------- # Does gcc support the -no-pie option? If so we should pass it to gcc when diff --git a/configure.ac b/configure.ac index 216a97fb49..5bf096b3d0 100644 --- a/configure.ac +++ b/configure.ac @@ -712,6 +712,11 @@ FP_GCC_VERSION dnl ** See whether gcc supports -no-pie FP_GCC_SUPPORTS_NO_PIE +dnl ** Used to determine how to compile ghc-prim's atomics.c, used by +dnl unregisterised, Sparc, and PPC backends. +FP_GCC_SUPPORTS__ATOMICS +AC_DEFINE([HAVE_C11_ATOMICS], [$CONF_GCC_SUPPORTS__ATOMICS], [Does GCC support __atomic primitives?]) + FP_GCC_EXTRA_FLAGS dnl ** look to see if we have a C compiler using an llvm back end. diff --git a/libraries/ghc-prim/cbits/atomic.c b/libraries/ghc-prim/cbits/atomic.c index b091d22b58..2ded465fcd 100644 --- a/libraries/ghc-prim/cbits/atomic.c +++ b/libraries/ghc-prim/cbits/atomic.c @@ -264,33 +264,53 @@ hs_cmpxchg64(StgWord x, StgWord64 old, StgWord64 new) // __ATOMIC_SEQ_CST: Full barrier in both directions (hoisting and sinking // of code) and synchronizes with acquire loads and release stores in // all threads. +// +// When we lack C11 atomics support we emulate these using the old GCC __sync +// primitives which the GCC documentation claims "usually" implies a full +// barrier. extern StgWord hs_atomicread8(StgWord x); StgWord hs_atomicread8(StgWord x) { +#if HAVE_C11_ATOMICS return __atomic_load_n((StgWord8 *) x, __ATOMIC_SEQ_CST); +#else + return __sync_add_and_fetch((StgWord8 *) x, 0); +#endif } extern StgWord hs_atomicread16(StgWord x); StgWord hs_atomicread16(StgWord x) { +#if HAVE_C11_ATOMICS return __atomic_load_n((StgWord16 *) x, __ATOMIC_SEQ_CST); +#else + return __sync_add_and_fetch((StgWord16 *) x, 0); +#endif } extern StgWord hs_atomicread32(StgWord x); StgWord hs_atomicread32(StgWord x) { +#if HAVE_C11_ATOMICS return __atomic_load_n((StgWord32 *) x, __ATOMIC_SEQ_CST); +#else + return __sync_add_and_fetch((StgWord32 *) x, 0); +#endif } extern StgWord64 hs_atomicread64(StgWord x); StgWord64 hs_atomicread64(StgWord x) { +#if HAVE_C11_ATOMICS return __atomic_load_n((StgWord64 *) x, __ATOMIC_SEQ_CST); +#else + return __sync_add_and_fetch((StgWord64 *) x, 0); +#endif } // AtomicWriteByteArrayOp_Int @@ -301,26 +321,42 @@ extern void hs_atomicwrite8(StgWord x, StgWord val); void hs_atomicwrite8(StgWord x, StgWord val) { +#if HAVE_C11_ATOMICS __atomic_store_n((StgWord8 *) x, (StgWord8) val, __ATOMIC_SEQ_CST); +#else + while (!__sync_bool_compare_and_swap((StgWord8 *) x, *(StgWord8 *) x, (StgWord8) val)); +#endif } extern void hs_atomicwrite16(StgWord x, StgWord val); void hs_atomicwrite16(StgWord x, StgWord val) { +#if HAVE_C11_ATOMICS __atomic_store_n((StgWord16 *) x, (StgWord16) val, __ATOMIC_SEQ_CST); +#else + while (!__sync_bool_compare_and_swap((StgWord16 *) x, *(StgWord16 *) x, (StgWord16) val)); +#endif } extern void hs_atomicwrite32(StgWord x, StgWord val); void hs_atomicwrite32(StgWord x, StgWord val) { +#if HAVE_C11_ATOMICS __atomic_store_n((StgWord32 *) x, (StgWord32) val, __ATOMIC_SEQ_CST); +#else + while (!__sync_bool_compare_and_swap((StgWord32 *) x, *(StgWord32 *) x, (StgWord32) val)); +#endif } extern void hs_atomicwrite64(StgWord x, StgWord64 val); void hs_atomicwrite64(StgWord x, StgWord64 val) { +#if HAVE_C11_ATOMICS __atomic_store_n((StgWord64 *) x, (StgWord64) val, __ATOMIC_SEQ_CST); +#else + while (!__sync_bool_compare_and_swap((StgWord64 *) x, *(StgWord64 *) x, (StgWord64) val)); +#endif } diff --git a/mk/config.mk.in b/mk/config.mk.in index b046abe112..86c626dccd 100644 --- a/mk/config.mk.in +++ b/mk/config.mk.in @@ -522,6 +522,7 @@ GccVersion = @GccVersion@ # TargetPlatformFull retains the string passed to configure so we have it in # the necessary format to pass to libffi's configure. TargetPlatformFull = @TargetPlatformFull@ +GccLT46 = @GccLT46@ GccIsClang = @GccIsClang@ CC = @CC@ diff --git a/mk/warnings.mk b/mk/warnings.mk index 9426db2a1d..0ae81bfcac 100644 --- a/mk/warnings.mk +++ b/mk/warnings.mk @@ -20,11 +20,13 @@ GhcStage2HcOpts += -Wcpp-undef ifneq "$(GccIsClang)" "YES" # Debian doesn't turn -Werror=unused-but-set-variable on by default, so -# we turn it on explicitly for consistency with other users. +# we turn it on explicitly for consistency with other users +ifeq "$(GccLT46)" "NO" # Never set the flag on Windows as the host gcc may be too old. ifneq "$(HostOS_CPP)" "mingw32" SRC_CC_WARNING_OPTS += -Werror=unused-but-set-variable endif +endif # Suppress the warning about __sync_fetch_and_nand (#9678). libraries/ghc-prim/cbits/atomic_CC_OPTS += -Wno-sync-nand diff --git a/rts/ghc.mk b/rts/ghc.mk index 690a883588..761cc43b8f 100644 --- a/rts/ghc.mk +++ b/rts/ghc.mk @@ -311,7 +311,9 @@ WARNING_OPTS += -Wpointer-arith WARNING_OPTS += -Wmissing-noreturn WARNING_OPTS += -Wnested-externs WARNING_OPTS += -Wredundant-decls +ifeq "$(GccLT46)" "NO" WARNING_OPTS += -Wundef +endif # These ones are hard to avoid: #WARNING_OPTS += -Wconversion |