Commit 217e4170 authored by Ben Gamari's avatar Ben Gamari Committed by Ben Gamari

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 59de2909.

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
parent fdf518c7
...@@ -1237,15 +1237,18 @@ if test -z "$CC" ...@@ -1237,15 +1237,18 @@ if test -z "$CC"
then then
AC_MSG_ERROR([gcc is required]) AC_MSG_ERROR([gcc is required])
fi fi
GccLT46=NO
AC_CACHE_CHECK([version of gcc], [fp_cv_gcc_version], AC_CACHE_CHECK([version of gcc], [fp_cv_gcc_version],
[ [
# Be sure only to look at the first occurrence of the "version " string; # Be sure only to look at the first occurrence of the "version " string;
# Some Apple compilers emit multiple messages containing this 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_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], FP_COMPARE_VERSIONS([$fp_cv_gcc_version], [-lt], [4.4],
[AC_MSG_ERROR([Need at least gcc version 4.7])]) [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([GccVersion], [$fp_cv_gcc_version])
AC_SUBST(GccLT46)
])# FP_GCC_VERSION ])# FP_GCC_VERSION
dnl Check to see if the C compiler is clang or llvm-gcc dnl Check to see if the C compiler is clang or llvm-gcc
...@@ -1278,6 +1281,24 @@ AC_SUBST(GccIsClang) ...@@ -1278,6 +1281,24 @@ AC_SUBST(GccIsClang)
rm -f conftest.txt 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 # FP_GCC_SUPPORTS_NO_PIE
# ---------------------- # ----------------------
# Does gcc support the -no-pie option? If so we should pass it to gcc when # Does gcc support the -no-pie option? If so we should pass it to gcc when
......
...@@ -712,6 +712,11 @@ FP_GCC_VERSION ...@@ -712,6 +712,11 @@ FP_GCC_VERSION
dnl ** See whether gcc supports -no-pie dnl ** See whether gcc supports -no-pie
FP_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 FP_GCC_EXTRA_FLAGS
dnl ** look to see if we have a C compiler using an llvm back end. dnl ** look to see if we have a C compiler using an llvm back end.
......
...@@ -264,33 +264,53 @@ hs_cmpxchg64(StgWord x, StgWord64 old, StgWord64 new) ...@@ -264,33 +264,53 @@ hs_cmpxchg64(StgWord x, StgWord64 old, StgWord64 new)
// __ATOMIC_SEQ_CST: Full barrier in both directions (hoisting and sinking // __ATOMIC_SEQ_CST: Full barrier in both directions (hoisting and sinking
// of code) and synchronizes with acquire loads and release stores in // of code) and synchronizes with acquire loads and release stores in
// all threads. // 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); extern StgWord hs_atomicread8(StgWord x);
StgWord StgWord
hs_atomicread8(StgWord x) hs_atomicread8(StgWord x)
{ {
#if HAVE_C11_ATOMICS
return __atomic_load_n((StgWord8 *) x, __ATOMIC_SEQ_CST); 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); extern StgWord hs_atomicread16(StgWord x);
StgWord StgWord
hs_atomicread16(StgWord x) hs_atomicread16(StgWord x)
{ {
#if HAVE_C11_ATOMICS
return __atomic_load_n((StgWord16 *) x, __ATOMIC_SEQ_CST); 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); extern StgWord hs_atomicread32(StgWord x);
StgWord StgWord
hs_atomicread32(StgWord x) hs_atomicread32(StgWord x)
{ {
#if HAVE_C11_ATOMICS
return __atomic_load_n((StgWord32 *) x, __ATOMIC_SEQ_CST); 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); extern StgWord64 hs_atomicread64(StgWord x);
StgWord64 StgWord64
hs_atomicread64(StgWord x) hs_atomicread64(StgWord x)
{ {
#if HAVE_C11_ATOMICS
return __atomic_load_n((StgWord64 *) x, __ATOMIC_SEQ_CST); return __atomic_load_n((StgWord64 *) x, __ATOMIC_SEQ_CST);
#else
return __sync_add_and_fetch((StgWord64 *) x, 0);
#endif
} }
// AtomicWriteByteArrayOp_Int // AtomicWriteByteArrayOp_Int
...@@ -301,26 +321,42 @@ extern void hs_atomicwrite8(StgWord x, StgWord val); ...@@ -301,26 +321,42 @@ extern void hs_atomicwrite8(StgWord x, StgWord val);
void void
hs_atomicwrite8(StgWord x, StgWord val) hs_atomicwrite8(StgWord x, StgWord val)
{ {
#if HAVE_C11_ATOMICS
__atomic_store_n((StgWord8 *) x, (StgWord8) val, __ATOMIC_SEQ_CST); __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); extern void hs_atomicwrite16(StgWord x, StgWord val);
void void
hs_atomicwrite16(StgWord x, StgWord val) hs_atomicwrite16(StgWord x, StgWord val)
{ {
#if HAVE_C11_ATOMICS
__atomic_store_n((StgWord16 *) x, (StgWord16) val, __ATOMIC_SEQ_CST); __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); extern void hs_atomicwrite32(StgWord x, StgWord val);
void void
hs_atomicwrite32(StgWord x, StgWord val) hs_atomicwrite32(StgWord x, StgWord val)
{ {
#if HAVE_C11_ATOMICS
__atomic_store_n((StgWord32 *) x, (StgWord32) val, __ATOMIC_SEQ_CST); __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); extern void hs_atomicwrite64(StgWord x, StgWord64 val);
void void
hs_atomicwrite64(StgWord x, StgWord64 val) hs_atomicwrite64(StgWord x, StgWord64 val)
{ {
#if HAVE_C11_ATOMICS
__atomic_store_n((StgWord64 *) x, (StgWord64) val, __ATOMIC_SEQ_CST); __atomic_store_n((StgWord64 *) x, (StgWord64) val, __ATOMIC_SEQ_CST);
#else
while (!__sync_bool_compare_and_swap((StgWord64 *) x, *(StgWord64 *) x, (StgWord64) val));
#endif
} }
...@@ -522,6 +522,7 @@ GccVersion = @GccVersion@ ...@@ -522,6 +522,7 @@ GccVersion = @GccVersion@
# TargetPlatformFull retains the string passed to configure so we have it in # TargetPlatformFull retains the string passed to configure so we have it in
# the necessary format to pass to libffi's configure. # the necessary format to pass to libffi's configure.
TargetPlatformFull = @TargetPlatformFull@ TargetPlatformFull = @TargetPlatformFull@
GccLT46 = @GccLT46@
GccIsClang = @GccIsClang@ GccIsClang = @GccIsClang@
CC = @CC@ CC = @CC@
......
...@@ -20,11 +20,13 @@ GhcStage2HcOpts += -Wcpp-undef ...@@ -20,11 +20,13 @@ GhcStage2HcOpts += -Wcpp-undef
ifneq "$(GccIsClang)" "YES" ifneq "$(GccIsClang)" "YES"
# Debian doesn't turn -Werror=unused-but-set-variable on by default, so # 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. # Never set the flag on Windows as the host gcc may be too old.
ifneq "$(HostOS_CPP)" "mingw32" ifneq "$(HostOS_CPP)" "mingw32"
SRC_CC_WARNING_OPTS += -Werror=unused-but-set-variable SRC_CC_WARNING_OPTS += -Werror=unused-but-set-variable
endif endif
endif
# Suppress the warning about __sync_fetch_and_nand (#9678). # Suppress the warning about __sync_fetch_and_nand (#9678).
libraries/ghc-prim/cbits/atomic_CC_OPTS += -Wno-sync-nand libraries/ghc-prim/cbits/atomic_CC_OPTS += -Wno-sync-nand
......
...@@ -311,7 +311,9 @@ WARNING_OPTS += -Wpointer-arith ...@@ -311,7 +311,9 @@ WARNING_OPTS += -Wpointer-arith
WARNING_OPTS += -Wmissing-noreturn WARNING_OPTS += -Wmissing-noreturn
WARNING_OPTS += -Wnested-externs WARNING_OPTS += -Wnested-externs
WARNING_OPTS += -Wredundant-decls WARNING_OPTS += -Wredundant-decls
ifeq "$(GccLT46)" "NO"
WARNING_OPTS += -Wundef WARNING_OPTS += -Wundef
endif
# These ones are hard to avoid: # These ones are hard to avoid:
#WARNING_OPTS += -Wconversion #WARNING_OPTS += -Wconversion
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment