Hi!

As the PRs show, -fsanitize=thread hasn't been instrumenting __atomic_clear
and __atomic_test_and_set builtins (i.e. those that operate on bool always),
so we reported a data race on the testcase even when there wasn't one.
And, as the other testcase shows, there were various ICEs with
-fnon-call-exceptions -fsanitize=thread.

Fixed thusly, bootstrapped/regtested on x86_64-linux and i686-linux, ok for
trunk?

2016-09-06  Jakub Jelinek  <ja...@redhat.com>

        PR sanitizer/68260
        * tsan.c: Include target.h and tree-eh.h.
        (enum tsan_atomic_action): Add bool_clear and bool_test_and_set.
        (BOOL_CLEAR, BOOL_TEST_AND_SET): Define.
        (tsan_atomic_table): Add BUILT_IN_ATOMIC_CLEAR and
        BUILT_IN_ATOMIC_TEST_AND_SET entries.
        (instrument_builtin_call): Remove EH edges of replaced call stmts.
        Handle bool_clear and bool_test_and_set.
        (instrument_memory_accesses): Add RET argument, purge dead eh edges
        at the end of bbs and set *RET in that case to TODO_cleanup_cfg.
        (tsan_pass): Adjust instrument_memory_accesses caller, return ret.

        * c-c++-common/tsan/pr68260.c: New test.
        * g++.dg/tsan/pr68260.C: New test.

--- gcc/tsan.c.jj       2016-07-11 22:18:08.000000000 +0200
+++ gcc/tsan.c  2016-09-06 14:06:21.193642590 +0200
@@ -40,6 +40,8 @@ along with GCC; see the file COPYING3.
 #include "tsan.h"
 #include "asan.h"
 #include "builtins.h"
+#include "target.h"
+#include "tree-eh.h"
 
 /* Number of instrumented memory accesses in the current function.  */
 
@@ -240,7 +242,8 @@ instrument_expr (gimple_stmt_iterator gs
 enum tsan_atomic_action
 {
   check_last, add_seq_cst, add_acquire, weak_cas, strong_cas,
-  bool_cas, val_cas, lock_release, fetch_op, fetch_op_seq_cst
+  bool_cas, val_cas, lock_release, fetch_op, fetch_op_seq_cst,
+  bool_clear, bool_test_and_set
 };
 
 /* Table how to map sync/atomic builtins to their corresponding
@@ -274,6 +277,10 @@ static const struct tsan_map_atomic
   TRANSFORM (fcode, tsan_fcode, fetch_op, code)
 #define FETCH_OPS(fcode, tsan_fcode, code) \
   TRANSFORM (fcode, tsan_fcode, fetch_op_seq_cst, code)
+#define BOOL_CLEAR(fcode, tsan_fcode) \
+  TRANSFORM (fcode, tsan_fcode, bool_clear, ERROR_MARK)
+#define BOOL_TEST_AND_SET(fcode, tsan_fcode) \
+  TRANSFORM (fcode, tsan_fcode, bool_test_and_set, ERROR_MARK)
 
   CHECK_LAST (ATOMIC_LOAD_1, TSAN_ATOMIC8_LOAD),
   CHECK_LAST (ATOMIC_LOAD_2, TSAN_ATOMIC16_LOAD),
@@ -463,7 +470,11 @@ static const struct tsan_map_atomic
   LOCK_RELEASE (SYNC_LOCK_RELEASE_2, TSAN_ATOMIC16_STORE),
   LOCK_RELEASE (SYNC_LOCK_RELEASE_4, TSAN_ATOMIC32_STORE),
   LOCK_RELEASE (SYNC_LOCK_RELEASE_8, TSAN_ATOMIC64_STORE),
-  LOCK_RELEASE (SYNC_LOCK_RELEASE_16, TSAN_ATOMIC128_STORE)
+  LOCK_RELEASE (SYNC_LOCK_RELEASE_16, TSAN_ATOMIC128_STORE),
+
+  BOOL_CLEAR (ATOMIC_CLEAR, TSAN_ATOMIC8_STORE),
+
+  BOOL_TEST_AND_SET (ATOMIC_TEST_AND_SET, TSAN_ATOMIC8_EXCHANGE)
 };
 
 /* Instrument an atomic builtin.  */
@@ -493,6 +504,8 @@ instrument_builtin_call (gimple_stmt_ite
            if (!tree_fits_uhwi_p (last_arg)
                || memmodel_base (tree_to_uhwi (last_arg)) >= MEMMODEL_LAST)
              return;
+           if (lookup_stmt_eh_lp (stmt))
+             remove_stmt_from_eh_lp (stmt);
            gimple_call_set_fndecl (stmt, decl);
            update_stmt (stmt);
            if (tsan_atomic_table[i].action == fetch_op)
@@ -514,6 +527,8 @@ instrument_builtin_call (gimple_stmt_ite
                                       != add_acquire
                                       ? MEMMODEL_SEQ_CST
                                       : MEMMODEL_ACQUIRE);
+           if (lookup_stmt_eh_lp (stmt))
+             remove_stmt_from_eh_lp (stmt);
            update_gimple_call (gsi, decl, num + 1, args[0], args[1], args[2]);
            stmt = gsi_stmt (*gsi);
            if (tsan_atomic_table[i].action == fetch_op_seq_cst)
@@ -561,6 +576,8 @@ instrument_builtin_call (gimple_stmt_ite
            if (!tree_fits_uhwi_p (args[5])
                || memmodel_base (tree_to_uhwi (args[5])) >= MEMMODEL_LAST)
              return;
+           if (lookup_stmt_eh_lp (stmt))
+             remove_stmt_from_eh_lp (stmt);
            update_gimple_call (gsi, decl, 5, args[0], args[1], args[2],
                                args[4], args[5]);
            return;
@@ -584,6 +601,8 @@ instrument_builtin_call (gimple_stmt_ite
            g = gimple_build_assign (t, args[1]);
            gsi_insert_before (gsi, g, GSI_SAME_STMT);
            lhs = gimple_call_lhs (stmt);
+           if (lookup_stmt_eh_lp (stmt))
+             remove_stmt_from_eh_lp (stmt);
            update_gimple_call (gsi, decl, 5, args[0],
                                build_fold_addr_expr (t), args[2],
                                build_int_cst (NULL_TREE,
@@ -610,11 +629,66 @@ instrument_builtin_call (gimple_stmt_ite
            gcc_assert (num == 1);
            t = TYPE_ARG_TYPES (TREE_TYPE (decl));
            t = TREE_VALUE (TREE_CHAIN (t));
+           if (lookup_stmt_eh_lp (stmt))
+             remove_stmt_from_eh_lp (stmt);
            update_gimple_call (gsi, decl, 3, gimple_call_arg (stmt, 0),
                                build_int_cst (t, 0),
                                build_int_cst (NULL_TREE,
                                               MEMMODEL_RELEASE));
            return;
+         case bool_clear:
+         case bool_test_and_set:
+           if (BOOL_TYPE_SIZE != 8)
+             {
+               decl = NULL_TREE;
+               for (j = 1; j < 5; j++)
+                 if (BOOL_TYPE_SIZE == (8 << j))
+                   {
+                     enum built_in_function tsan_fcode
+                       = (enum built_in_function)
+                         (tsan_atomic_table[i].tsan_fcode + j);
+                     decl = builtin_decl_implicit (tsan_fcode);
+                     break;
+                   }
+               if (decl == NULL_TREE)
+                 return;
+             }
+           last_arg = gimple_call_arg (stmt, num - 1);
+           if (!tree_fits_uhwi_p (last_arg)
+               || memmodel_base (tree_to_uhwi (last_arg)) >= MEMMODEL_LAST)
+             return;
+           t = TYPE_ARG_TYPES (TREE_TYPE (decl));
+           t = TREE_VALUE (TREE_CHAIN (t));
+           if (lookup_stmt_eh_lp (stmt))
+             remove_stmt_from_eh_lp (stmt);
+           if (tsan_atomic_table[i].action == bool_clear)
+             {
+               update_gimple_call (gsi, decl, 3, gimple_call_arg (stmt, 0),
+                                   build_int_cst (t, 0), last_arg);
+               return;
+             }
+           t = build_int_cst (t, targetm.atomic_test_and_set_trueval);
+           update_gimple_call (gsi, decl, 3, gimple_call_arg (stmt, 0),
+                               t, last_arg);
+           stmt = gsi_stmt (*gsi);
+           lhs = gimple_call_lhs (stmt);
+           if (lhs == NULL_TREE)
+             return;
+           if (targetm.atomic_test_and_set_trueval != 1
+               || !useless_type_conversion_p (TREE_TYPE (lhs),
+                                              TREE_TYPE (t)))
+             {
+               tree new_lhs = make_ssa_name (TREE_TYPE (t));
+               gimple_call_set_lhs (stmt, new_lhs);
+               if (targetm.atomic_test_and_set_trueval != 1)
+                 g = gimple_build_assign (lhs, NE_EXPR, new_lhs,
+                                          build_int_cst (TREE_TYPE (t), 0));
+               else
+                 g = gimple_build_assign (lhs, NOP_EXPR, new_lhs);
+               gsi_insert_after (gsi, g, GSI_NEW_STMT);
+               update_stmt (stmt);
+             }
+           return;
          default:
            continue;
          }
@@ -706,7 +780,7 @@ instrument_func_exit (void)
    Return true if func entry/exit should be instrumented.  */
 
 static bool
-instrument_memory_accesses (void)
+instrument_memory_accesses (unsigned int *ret)
 {
   basic_block bb;
   gimple_stmt_iterator gsi;
@@ -715,22 +789,26 @@ instrument_memory_accesses (void)
   auto_vec<gimple *> tsan_func_exits;
 
   FOR_EACH_BB_FN (bb, cfun)
-    for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
-      {
-       gimple *stmt = gsi_stmt (gsi);
-       if (is_gimple_call (stmt)
-           && gimple_call_internal_p (stmt)
-           && gimple_call_internal_fn (stmt) == IFN_TSAN_FUNC_EXIT)
-         {
-           if (fentry_exit_instrument)
-             replace_func_exit (stmt);
-           else
-             tsan_func_exits.safe_push (stmt);
-           func_exit_seen = true;
-         }
-       else
-         fentry_exit_instrument |= instrument_gimple (&gsi);
-      }
+    {
+      for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+       {
+         gimple *stmt = gsi_stmt (gsi);
+         if (is_gimple_call (stmt)
+             && gimple_call_internal_p (stmt)
+             && gimple_call_internal_fn (stmt) == IFN_TSAN_FUNC_EXIT)
+           {
+             if (fentry_exit_instrument)
+               replace_func_exit (stmt);
+             else
+               tsan_func_exits.safe_push (stmt);
+             func_exit_seen = true;
+           }
+         else
+           fentry_exit_instrument |= instrument_gimple (&gsi);
+       }
+      if (gimple_purge_dead_eh_edges (bb))
+       *ret = TODO_cleanup_cfg;
+    }
   unsigned int i;
   gimple *stmt;
   FOR_EACH_VEC_ELT (tsan_func_exits, i, stmt)
@@ -776,10 +854,11 @@ instrument_func_entry (void)
 static unsigned
 tsan_pass (void)
 {
+  unsigned int ret = 0;
   initialize_sanitizer_builtins ();
-  if (instrument_memory_accesses ())
+  if (instrument_memory_accesses (&ret))
     instrument_func_entry ();
-  return 0;
+  return ret;
 }
 
 /* Inserts __tsan_init () into the list of CTORs.  */
--- gcc/testsuite/c-c++-common/tsan/pr68260.c.jj        2016-09-06 
15:56:27.772209548 +0200
+++ gcc/testsuite/c-c++-common/tsan/pr68260.c   2016-09-06 16:08:23.424203133 
+0200
@@ -0,0 +1,28 @@
+/* PR sanitizer/68260 */
+
+#include <pthread.h>
+#include <stdbool.h>
+
+bool lock;
+int counter;
+
+void *
+tf (void *arg)
+{
+  (void) arg;
+  while (__atomic_test_and_set (&lock, __ATOMIC_ACQUIRE))
+    ;
+  ++counter;
+  __atomic_clear (&lock, __ATOMIC_RELEASE);
+  return (void *) 0;
+}
+
+int
+main ()
+{
+  pthread_t thr;
+  pthread_create (&thr, 0, tf, 0);
+  tf ((void *) 0);
+  pthread_join (thr, 0);
+  return 0;
+}
--- gcc/testsuite/g++.dg/tsan/pr68260.C.jj      2016-09-06 15:58:41.118530940 
+0200
+++ gcc/testsuite/g++.dg/tsan/pr68260.C 2016-09-06 16:03:11.988121144 +0200
@@ -0,0 +1,19 @@
+/* PR sanitizer/68260 */
+/* { dg-do compile } */
+/* { dg-additional-options "-fnon-call-exceptions" } */
+
+int
+foo (bool *p, int *q)
+{
+  try
+    {
+      while (__atomic_test_and_set (p, __ATOMIC_ACQUIRE))
+       ;
+      __atomic_clear (p, __ATOMIC_RELEASE);
+      return __sync_fetch_and_add_4 (q, 4);
+    }
+  catch (...)
+    {
+      return -1;
+    }
+}

        Jakub

Reply via email to