This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] Detect whether target can use -fprofile-update=atomic


Hello.

As reported in [1], m68k has been broken since I installed the patch. Reason is that the target
does not support atomic operations (add, or) for a mode of gcov_type. Because of that, we see
an undefined symbols.

Proper fix contains of 2 parts:
a) compiler emission must verify that -fprofile-update=atomic is doable for a given target; it's done
via a new function can_generate_atomic_builtin
b) libgcc must detect whether __atomic_fetch_add_x can be expanded on the target; that requires configure
support and if the target is not capable to expand these, we must conditionally remove all gcov_.*profiler_atomic
functions from libgcov.a.

Andreas reported that it fixes the test-case mentioned in the PR and I tested that on -march=i386.
Apart from that I've been doing bootstrap on x86_64-linux-gnu.

Ready after it finishes?
Martin

[1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58306#c30
>From a5c6dbdbabc36193f7becce78af58b276b0d3660 Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Tue, 16 Aug 2016 10:13:13 +0200
Subject: [PATCH 2/2] Detect whether target can use -fprofile-update=atomic

gcc/ChangeLog:

2016-08-12  Martin Liska  <mliska@suse.cz>

	* optabs.c (can_generate_atomic_builtin): New function.
	* optabs.h (can_generate_atomic_builtin): Declare the function.
	* tree-profile.c (tree_profiling):  Detect whether target can use
	-fprofile-update=atomic.

gcc/testsuite/ChangeLog:

2016-08-12  Martin Liska  <mliska@suse.cz>

	* gcc.dg/profile-update-warning.c: New test.

libgcc/ChangeLog:

2016-08-16  Martin Liska  <mliska@suse.cz>

	* acinclude.m4: New file.
	* config.in: New macro defines.
	* configure: Regenerated.
	* configure.ac: Detect atomic operations.
	* libgcov-profiler.c: Detect GCOV_SUPPORTS_ATOMIC and
	conditionaly enable/disable *_atomic functions.
---
 gcc/optabs.c                                  |  13 ++
 gcc/optabs.h                                  |   5 +
 gcc/testsuite/gcc.dg/profile-update-warning.c |   7 +
 gcc/tree-profile.c                            |  18 +++
 libgcc/acinclude.m4                           |  22 ++++
 libgcc/config.in                              |  15 ++-
 libgcc/configure                              | 179 +++++++++++++++++++++++++-
 libgcc/configure.ac                           |   8 ++
 libgcc/libgcov-profiler.c                     |  24 +++-
 9 files changed, 279 insertions(+), 12 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/profile-update-warning.c
 create mode 100644 libgcc/acinclude.m4

diff --git a/gcc/optabs.c b/gcc/optabs.c
index 87b4f97..3be0dfe 100644
--- a/gcc/optabs.c
+++ b/gcc/optabs.c
@@ -6476,6 +6476,19 @@ get_atomic_op_for_code (struct atomic_op_functions *op, enum rtx_code code)
     }
 }
 
+bool
+can_generate_atomic_builtin (enum rtx_code code, machine_mode mode)
+{
+  struct atomic_op_functions optab;
+  get_atomic_op_for_code (&optab, code);
+  enum insn_code icode = direct_optab_handler (optab.mem_no_result, mode);
+  if (icode != CODE_FOR_nothing)
+    return true;
+
+  return can_compare_and_swap_p (mode, false)
+    || can_compare_and_swap_p (mode, true);
+}
+
 /* See if there is a more optimal way to implement the operation "*MEM CODE VAL"
    using memory order MODEL.  If AFTER is true the operation needs to return
    the value of *MEM after the operation, otherwise the previous value.  
diff --git a/gcc/optabs.h b/gcc/optabs.h
index 03fd94d..769685a 100644
--- a/gcc/optabs.h
+++ b/gcc/optabs.h
@@ -348,4 +348,9 @@ extern void expand_jump_insn (enum insn_code icode, unsigned int nops,
 
 extern enum rtx_code get_rtx_code (enum tree_code tcode, bool unsignedp);
 
+/* Return true when a target is capable of expansion of an atomic builtin
+   with CODE of a specified machine MODE.  */
+
+extern bool can_generate_atomic_builtin (enum rtx_code code, machine_mode mode);
+
 #endif /* GCC_OPTABS_H */
diff --git a/gcc/testsuite/gcc.dg/profile-update-warning.c b/gcc/testsuite/gcc.dg/profile-update-warning.c
new file mode 100644
index 0000000..0614fad
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/profile-update-warning.c
@@ -0,0 +1,7 @@
+/* { dg-do compile { target { i?86-*-* x86_64-*-* } } } */
+/* { dg-options "-fprofile-update=atomic -fprofile-generate -march=i386 -m32" } */
+
+int main(int argc, char *argv[])
+{
+  return 0;
+} /* { dg-warning "target does not support atomic profile update, single mode is selected" } */
diff --git a/gcc/tree-profile.c b/gcc/tree-profile.c
index 622869e..799de84 100644
--- a/gcc/tree-profile.c
+++ b/gcc/tree-profile.c
@@ -49,6 +49,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "profile.h"
 #include "tree-cfgcleanup.h"
 #include "params.h"
+#include "rtl.h"
+#include "optabs.h"
 
 static GTY(()) tree gcov_type_node;
 static GTY(()) tree tree_interval_profiler_fn;
@@ -535,6 +537,22 @@ tree_profiling (void)
 {
   struct cgraph_node *node;
 
+  /* Verify whether we can utilize atomic update operations.  */
+  if (flag_profile_update == PROFILE_UPDATE_ATOMIC)
+    {
+      machine_mode mode = mode_for_size (LONG_LONG_TYPE_SIZE > 32 ? 64: 32,
+					 MODE_INT, 1);
+      bool r = can_generate_atomic_builtin (PLUS, mode)
+	&& can_generate_atomic_builtin (IOR, mode);
+
+      if (!r)
+	{
+	  warning (0, "target does not support atomic profile update, "
+		   "single mode is selected");
+	  flag_profile_update = PROFILE_UPDATE_SINGLE;
+	}
+    }
+
   /* This is a small-ipa pass that gets called only once, from
      cgraphunit.c:ipa_passes().  */
   gcc_assert (symtab->state == IPA_SSA);
diff --git a/libgcc/acinclude.m4 b/libgcc/acinclude.m4
new file mode 100644
index 0000000..87ac32b
--- /dev/null
+++ b/libgcc/acinclude.m4
@@ -0,0 +1,22 @@
+dnl Check whether the target supports __atomic_operations.
+AC_DEFUN([LIBGCC_CHECK_ATOMIC_OPERATION], [
+  AC_CACHE_CHECK([whether the target supports atomic operations for $1B],
+		 libgcc_cv_have_atomic_operations_$1, [
+  libgcc_cv_have_atomic_operations_$1=no
+
+  AC_LANG_CONFTEST(
+  [AC_LANG_PROGRAM([[int foovar = 0;]], [[__atomic_fetch_add_$1 (&foovar, 1, 0);
+  __atomic_fetch_or_$1 (&foovar, 1, 0)]])])
+  if AC_TRY_COMMAND(${CC-cc} -Werror -S -o conftest.s conftest.c 1>&AS_MESSAGE_LOG_FD); then
+      if grep __atomic_fetch_add_$1 conftest.s > /dev/null; then
+	:
+      else
+	libgcc_cv_have_atomic_operations_$1=yes
+      fi
+    fi
+    rm -f conftest.*
+    ])
+  if test $libgcc_cv_have_atomic_operations_$1 = yes; then
+    AC_DEFINE(HAVE_ATOMIC_OPERATIONS_$1, 1,
+	      [Define to 1 if the target supports atomic operations for $1B])
+  fi])
diff --git a/libgcc/config.in b/libgcc/config.in
index 4d33411..03e848c 100644
--- a/libgcc/config.in
+++ b/libgcc/config.in
@@ -1,5 +1,17 @@
 /* config.in.  Generated from configure.ac by autoheader.  */
 
+/* Define to 1 if the target supports atomic operations for 16B */
+#undef HAVE_ATOMIC_OPERATIONS_16
+
+/* Define to 1 if the target supports atomic operations for 32B */
+#undef HAVE_ATOMIC_OPERATIONS_32
+
+/* Define to 1 if the target supports atomic operations for 4B */
+#undef HAVE_ATOMIC_OPERATIONS_4
+
+/* Define to 1 if the target supports atomic operations for 8B */
+#undef HAVE_ATOMIC_OPERATIONS_8
+
 /* Define to 1 if the target assembler supports thread-local storage. */
 #undef HAVE_CC_TLS
 
@@ -21,9 +33,6 @@
 /* Define if the system-provided CRTs are present on Solaris. */
 #undef HAVE_SOLARIS_CRTS
 
-/* Define if the system-provided CRTs are present on Solaris. */
-#undef HAVE_SOLARIS_CRTS
-
 /* Define to 1 if you have the <stdint.h> header file. */
 #undef HAVE_STDINT_H
 
diff --git a/libgcc/configure b/libgcc/configure
index bf96aec..0058467 100644
--- a/libgcc/configure
+++ b/libgcc/configure
@@ -2321,10 +2321,6 @@ case "${host}" in
 	;;
     i[34567]86-*-mingw* | x86_64-*-mingw*)
 	;;
-    i[34567]86-*-interix[3-9]*)
-	# Interix 3.x gcc -fpic/-fPIC options generate broken code.
-	# Instead, we relocate shared libraries at runtime.
-	;;
     i[34567]86-*-nto-qnx*)
 	# QNX uses GNU C++, but need to define -shared option too, otherwise
 	# it will coredump.
@@ -5080,6 +5076,180 @@ esac
 
 
 
+# Check out sync builtins support.
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the target supports atomic operations for 32B" >&5
+$as_echo_n "checking whether the target supports atomic operations for 32B... " >&6; }
+if test "${libgcc_cv_have_atomic_operations_32+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+
+  libgcc_cv_have_atomic_operations_32=no
+
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+int foovar = 0;
+int
+main ()
+{
+__atomic_fetch_add_32 (&foovar, 1, 0);
+  __atomic_fetch_or_32 (&foovar, 1, 0)
+  ;
+  return 0;
+}
+_ACEOF
+  if { ac_try='${CC-cc} -Werror -S -o conftest.s conftest.c 1>&5'
+  { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }; then
+      if grep __atomic_fetch_add_32 conftest.s > /dev/null; then
+	:
+      else
+	libgcc_cv_have_atomic_operations_32=yes
+      fi
+    fi
+    rm -f conftest.*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $libgcc_cv_have_atomic_operations_32" >&5
+$as_echo "$libgcc_cv_have_atomic_operations_32" >&6; }
+  if test $libgcc_cv_have_atomic_operations_32 = yes; then
+
+$as_echo "#define HAVE_ATOMIC_OPERATIONS_32 1" >>confdefs.h
+
+  fi
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the target supports atomic operations for 16B" >&5
+$as_echo_n "checking whether the target supports atomic operations for 16B... " >&6; }
+if test "${libgcc_cv_have_atomic_operations_16+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+
+  libgcc_cv_have_atomic_operations_16=no
+
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+int foovar = 0;
+int
+main ()
+{
+__atomic_fetch_add_16 (&foovar, 1, 0);
+  __atomic_fetch_or_16 (&foovar, 1, 0)
+  ;
+  return 0;
+}
+_ACEOF
+  if { ac_try='${CC-cc} -Werror -S -o conftest.s conftest.c 1>&5'
+  { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }; then
+      if grep __atomic_fetch_add_16 conftest.s > /dev/null; then
+	:
+      else
+	libgcc_cv_have_atomic_operations_16=yes
+      fi
+    fi
+    rm -f conftest.*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $libgcc_cv_have_atomic_operations_16" >&5
+$as_echo "$libgcc_cv_have_atomic_operations_16" >&6; }
+  if test $libgcc_cv_have_atomic_operations_16 = yes; then
+
+$as_echo "#define HAVE_ATOMIC_OPERATIONS_16 1" >>confdefs.h
+
+  fi
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the target supports atomic operations for 8B" >&5
+$as_echo_n "checking whether the target supports atomic operations for 8B... " >&6; }
+if test "${libgcc_cv_have_atomic_operations_8+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+
+  libgcc_cv_have_atomic_operations_8=no
+
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+int foovar = 0;
+int
+main ()
+{
+__atomic_fetch_add_8 (&foovar, 1, 0);
+  __atomic_fetch_or_8 (&foovar, 1, 0)
+  ;
+  return 0;
+}
+_ACEOF
+  if { ac_try='${CC-cc} -Werror -S -o conftest.s conftest.c 1>&5'
+  { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }; then
+      if grep __atomic_fetch_add_8 conftest.s > /dev/null; then
+	:
+      else
+	libgcc_cv_have_atomic_operations_8=yes
+      fi
+    fi
+    rm -f conftest.*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $libgcc_cv_have_atomic_operations_8" >&5
+$as_echo "$libgcc_cv_have_atomic_operations_8" >&6; }
+  if test $libgcc_cv_have_atomic_operations_8 = yes; then
+
+$as_echo "#define HAVE_ATOMIC_OPERATIONS_8 1" >>confdefs.h
+
+  fi
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the target supports atomic operations for 4B" >&5
+$as_echo_n "checking whether the target supports atomic operations for 4B... " >&6; }
+if test "${libgcc_cv_have_atomic_operations_4+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+
+  libgcc_cv_have_atomic_operations_4=no
+
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+int foovar = 0;
+int
+main ()
+{
+__atomic_fetch_add_4 (&foovar, 1, 0);
+  __atomic_fetch_or_4 (&foovar, 1, 0)
+  ;
+  return 0;
+}
+_ACEOF
+  if { ac_try='${CC-cc} -Werror -S -o conftest.s conftest.c 1>&5'
+  { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }; then
+      if grep __atomic_fetch_add_4 conftest.s > /dev/null; then
+	:
+      else
+	libgcc_cv_have_atomic_operations_4=yes
+      fi
+    fi
+    rm -f conftest.*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $libgcc_cv_have_atomic_operations_4" >&5
+$as_echo "$libgcc_cv_have_atomic_operations_4" >&6; }
+  if test $libgcc_cv_have_atomic_operations_4 = yes; then
+
+$as_echo "#define HAVE_ATOMIC_OPERATIONS_4 1" >>confdefs.h
+
+  fi
+
 # Substitute configuration variables
 
 
@@ -5100,6 +5270,7 @@ ac_config_files="$ac_config_files Makefile"
 
 ac_config_commands="$ac_config_commands default"
 
+
 cat >confcache <<\_ACEOF
 # This file is a shell script that caches the results of configure
 # tests run on this system so they can be shared between configure
diff --git a/libgcc/configure.ac b/libgcc/configure.ac
index 269997f..f74be17 100644
--- a/libgcc/configure.ac
+++ b/libgcc/configure.ac
@@ -11,6 +11,7 @@ sinclude(../config/dfp.m4)
 sinclude(../config/unwind_ipinfo.m4)
 sinclude(../config/gthr.m4)
 sinclude(../config/sjlj.m4)
+sinclude(acinclude.m4)
 
 AC_PREREQ(2.64)
 AC_INIT([GNU C Runtime Library], 1.0,,[libgcc])
@@ -544,6 +545,12 @@ AC_SUBST(tm_defines)
 # Map from thread model to thread header.
 GCC_AC_THREAD_HEADER([$target_thread_file])
 
+# Check out sync builtins support.
+LIBGCC_CHECK_ATOMIC_OPERATION(32)
+LIBGCC_CHECK_ATOMIC_OPERATION(16)
+LIBGCC_CHECK_ATOMIC_OPERATION(8)
+LIBGCC_CHECK_ATOMIC_OPERATION(4)
+
 # Substitute configuration variables
 AC_SUBST(cpu_type)
 AC_SUBST(extra_parts)
@@ -572,4 +579,5 @@ CONFIG_SHELL=${CONFIG_SHELL-/bin/sh}
 libgcc_topdir=${libgcc_topdir}
 CC="${CC}"
 ]])
+
 AC_OUTPUT
diff --git a/libgcc/libgcov-profiler.c b/libgcc/libgcov-profiler.c
index 70a821d..d07f81a 100644
--- a/libgcc/libgcov-profiler.c
+++ b/libgcc/libgcov-profiler.c
@@ -24,8 +24,20 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 <http://www.gnu.org/licenses/>.  */
 
 #include "libgcov.h"
+#include "auto-target.h"
 #if !defined(inhibit_libc)
 
+/* Detect whether target can support atomic update of profilers.  */
+#if LONG_LONG_TYPE_SIZE <= 32 && HAVE_ATOMIC_OPERATIONS_4
+#define GCOV_SUPPORTS_ATOMIC 1
+#else
+#if LONG_LONG_TYPE_SIZE > 32 && HAVE_ATOMIC_OPERATIONS_8
+#define GCOV_SUPPORTS_ATOMIC 1
+#else
+#define GCOV_SUPPORTS_ATOMIC 0
+#endif
+#endif
+
 #ifdef L_gcov_interval_profiler
 /* If VALUE is in interval <START, START + STEPS - 1>, then increases the
    corresponding counter in COUNTERS.  If the VALUE is above or below
@@ -46,7 +58,7 @@ __gcov_interval_profiler (gcov_type *counters, gcov_type value,
 }
 #endif
 
-#ifdef L_gcov_interval_profiler_atomic
+#if defined(L_gcov_interval_profiler_atomic) && GCOV_SUPPORTS_ATOMIC
 /* If VALUE is in interval <START, START + STEPS - 1>, then increases the
    corresponding counter in COUNTERS.  If the VALUE is above or below
    the interval, COUNTERS[STEPS] or COUNTERS[STEPS + 1] is increased
@@ -80,7 +92,7 @@ __gcov_pow2_profiler (gcov_type *counters, gcov_type value)
 }
 #endif
 
-#ifdef L_gcov_pow2_profiler_atomic
+#if defined(L_gcov_pow2_profiler_atomic) && GCOV_SUPPORTS_ATOMIC
 /* If VALUE is a power of two, COUNTERS[1] is incremented.  Otherwise
    COUNTERS[0] is incremented.  Function is thread-safe.  */
 
@@ -134,7 +146,7 @@ __gcov_one_value_profiler (gcov_type *counters, gcov_type value)
 }
 #endif
 
-#ifdef L_gcov_one_value_profiler_atomic
+#if defined(L_gcov_one_value_profiler_atomic) && GCOV_SUPPORTS_ATOMIC
 
 /* Update one value profilers (COUNTERS) for a given VALUE.
 
@@ -342,6 +354,7 @@ __gcov_time_profiler (gcov_type* counters)
     counters[0] = ++function_counter;
 }
 
+#if GCOV_SUPPORTS_ATOMIC
 /* Sets corresponding COUNTERS if there is no value.
    Function is thread-safe.  */
 
@@ -352,6 +365,7 @@ __gcov_time_profiler_atomic (gcov_type* counters)
     counters[0] = __atomic_add_fetch (&function_counter, 1, MEMMODEL_RELAXED);
 }
 #endif
+#endif
 
 
 #ifdef L_gcov_average_profiler
@@ -366,7 +380,7 @@ __gcov_average_profiler (gcov_type *counters, gcov_type value)
 }
 #endif
 
-#ifdef L_gcov_average_profiler_atomic
+#if defined(L_gcov_average_profiler_atomic) && GCOV_SUPPORTS_ATOMIC
 /* Increase corresponding COUNTER by VALUE.  FIXME: Perhaps we want
    to saturate up.  Function is thread-safe.  */
 
@@ -388,7 +402,7 @@ __gcov_ior_profiler (gcov_type *counters, gcov_type value)
 }
 #endif
 
-#ifdef L_gcov_ior_profiler_atomic
+#if defined(L_gcov_ior_profiler_atomic) && GCOV_SUPPORTS_ATOMIC
 /* Bitwise-OR VALUE into COUNTER.  Function is thread-safe.  */
 
 void
-- 
2.9.2


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]