ghc-prim: Emulate C11 atomics when not available
authorBen Gamari <bgamari.foss@gmail.com>
Sat, 3 Feb 2018 16:37:01 +0000 (11:37 -0500)
committerBen Gamari <ben@smart-cactus.org>
Sat, 3 Feb 2018 16:37:14 +0000 (11:37 -0500)
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

aclocal.m4
configure.ac
libraries/ghc-prim/cbits/atomic.c
mk/config.mk.in
mk/warnings.mk
rts/ghc.mk

index 5989a13..2ed2c08 100644 (file)
@@ -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
index 216a97f..5bf096b 100644 (file)
@@ -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.
index b091d22..2ded465 100644 (file)
@@ -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
 }
index b046abe..86c626d 100644 (file)
@@ -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@
index 9426db2..0ae81bf 100644 (file)
@@ -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
index 690a883..761cc43 100644 (file)
@@ -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