The issue here is no atomic support whatsoever. The standard now *requires* that atomic_flag be implementable in a lock free manner for compliance. That means they must resolve to something, and not an external library call.

In order to support atomic_flag in a lock free manner on a target, we need at a minimum the legacy __sync_lock_test_and_set and __sync_lock_release to be implemented.

Previous to this release, if atomic_flag couldn't be implemented lock free, it was implemented with locks. libstdc++-v3 no longer supports any locked implementations.

We'll have the same problem with C1x next release as well, so I bit the bullet and added __atomic_test_and_set and __atomic_clear as built-ins. These routines will first try to use lock free exchange and store. Failing that, legacy __sync_lock_test_and_set and __sync_lock_release are tried. If those fail as well, then we simply default to performing a load and/or store as is required.

Currently I don't issue any warnings because we don't have a good way of saying we are running in a single threaded model. When we add the "-fmemory-model=single" flag (probably next release) I think we should issue a warning that atomics with no support are being used in a non-single threaded environment.

this boostraps and no new regressions on x86_64-unknown-linux-gnu. I also built a cris-elf compiler and looked at the output from atomic-flag.c, and there were no external calls in it, so hopefully it resolves the issue there...

care to give it a try and verify?

Andrew
        libstdc++-v3
        * include/bits/atomic_base.h (atomic_thread_fence): Call built-in.
        (atomic_signal_fence): Call built-in.
        (test_and_set, clear): Call new atomic built-ins.

        gcc
        * builtins.c (expand_builtin_atomic_clear): New.  Expand atomic_clear.
        (expand_builtin_atomic_test_and_set): New.  Expand atomic test_and_set.
        (expand_builtin): Add cases for test_and_set and clear.
        * sync-builtins.def (BUILT_IN_ATOMIC_TEST_AND_SET): New.
        (BUILT_IN_ATOMIC_CLEAR): New.

        testsuite
        * gcc.dg/atomic-invalid.c: Add test for invalid __atomic_clear models.
        * gcc.dg/atomic-flag.c: New.  Test __atomic_test_and_set and
        __atomic_clear.

Index: libstdc++-v3/include/bits/atomic_base.h
===================================================================
*** libstdc++-v3/include/bits/atomic_base.h     (revision 181119)
--- libstdc++-v3/include/bits/atomic_base.h     (working copy)
*************** _GLIBCXX_BEGIN_NAMESPACE_VERSION
*** 68,78 ****
      return __mo2;
    }
  
!   void
!   atomic_thread_fence(memory_order __m) noexcept;
  
!   void
!   atomic_signal_fence(memory_order __m) noexcept;
  
    /// kill_dependency
    template<typename _Tp>
--- 68,84 ----
      return __mo2;
    }
  
!   inline void
!   atomic_thread_fence(memory_order __m) noexcept
!   {
!     __atomic_thread_fence (__m);
!   }
  
!   inline void
!   atomic_signal_fence(memory_order __m) noexcept
!   {
!     __atomic_thread_fence (__m);
!   }
  
    /// kill_dependency
    template<typename _Tp>
*************** _GLIBCXX_BEGIN_NAMESPACE_VERSION
*** 261,295 ****
      bool
      test_and_set(memory_order __m = memory_order_seq_cst) noexcept
      {
!       /* The standard *requires* this to be lock free.  If exchange is not
!        always lock free, the resort to the old test_and_set.  */
!       if (__atomic_always_lock_free (sizeof (_M_i), 0))
!       return __atomic_exchange_n(&_M_i, 1, __m);
!       else
!         {
!         /* Sync test and set is only guaranteed to be acquire.  */
!         if (__m == memory_order_seq_cst || __m == memory_order_release
!             || __m == memory_order_acq_rel)
!           atomic_thread_fence (__m);
!         return __sync_lock_test_and_set (&_M_i, 1);
!       }
      }
  
      bool
      test_and_set(memory_order __m = memory_order_seq_cst) volatile noexcept
      {
!       /* The standard *requires* this to be lock free.  If exchange is not
!        always lock free, the resort to the old test_and_set.  */
!       if (__atomic_always_lock_free (sizeof (_M_i), 0))
!       return __atomic_exchange_n(&_M_i, 1, __m);
!       else
!         {
!         /* Sync test and set is only guaranteed to be acquire.  */
!         if (__m == memory_order_seq_cst || __m == memory_order_release
!             || __m == memory_order_acq_rel)
!           atomic_thread_fence (__m);
!         return __sync_lock_test_and_set (&_M_i, 1);
!       }
      }
  
      void
--- 267,279 ----
      bool
      test_and_set(memory_order __m = memory_order_seq_cst) noexcept
      {
!       return __atomic_test_and_set (&_M_i, __m);
      }
  
      bool
      test_and_set(memory_order __m = memory_order_seq_cst) volatile noexcept
      {
!       return __atomic_test_and_set (&_M_i, __m);
      }
  
      void
*************** _GLIBCXX_BEGIN_NAMESPACE_VERSION
*** 299,315 ****
        __glibcxx_assert(__m != memory_order_acquire);
        __glibcxx_assert(__m != memory_order_acq_rel);
  
!       /* The standard *requires* this to be lock free.  If store is not always
!        lock free, the resort to the old style __sync_lock_release.  */
!       if (__atomic_always_lock_free (sizeof (_M_i), 0))
!       __atomic_store_n(&_M_i, 0, __m);
!       else
!         {
!         __sync_lock_release (&_M_i, 0);
!         /* __sync_lock_release is only guaranteed to be a release barrier.  */
!         if (__m == memory_order_seq_cst)
!           atomic_thread_fence (__m);
!       }
      }
  
      void
--- 283,289 ----
        __glibcxx_assert(__m != memory_order_acquire);
        __glibcxx_assert(__m != memory_order_acq_rel);
  
!       __atomic_clear (&_M_i, __m);
      }
  
      void
*************** _GLIBCXX_BEGIN_NAMESPACE_VERSION
*** 319,335 ****
        __glibcxx_assert(__m != memory_order_acquire);
        __glibcxx_assert(__m != memory_order_acq_rel);
  
!       /* The standard *requires* this to be lock free.  If store is not always
!        lock free, the resort to the old style __sync_lock_release.  */
!       if (__atomic_always_lock_free (sizeof (_M_i), 0))
!       __atomic_store_n(&_M_i, 0, __m);
!       else
!         {
!         __sync_lock_release (&_M_i, 0);
!         /* __sync_lock_release is only guaranteed to be a release barrier.  */
!         if (__m == memory_order_seq_cst)
!           atomic_thread_fence (__m);
!       }
      }
    };
  
--- 293,299 ----
        __glibcxx_assert(__m != memory_order_acquire);
        __glibcxx_assert(__m != memory_order_acq_rel);
  
!       __atomic_clear (&_M_i, __m);
      }
    };
  
Index: gcc/builtins.c
===================================================================
*** gcc/builtins.c      (revision 181111)
--- gcc/builtins.c      (working copy)
*************** expand_builtin_atomic_fetch_op (enum mac
*** 5465,5470 ****
--- 5465,5545 ----
    return ret;
  }
  
+ 
+ /* Expand an atomic clear operation.
+       void _atomic_clear (BOOL *obj, enum memmodel)
+    EXP is the call expression.  */
+ 
+ static rtx
+ expand_builtin_atomic_clear (tree exp) 
+ {
+   enum machine_mode mode;
+   rtx mem, ret;
+   enum memmodel model;
+ 
+   mode = mode_for_size (BOOL_TYPE_SIZE, MODE_INT, 0);
+   mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
+   model = get_memmodel (CALL_EXPR_ARG (exp, 1));
+ 
+   if (model == MEMMODEL_ACQUIRE || model == MEMMODEL_ACQ_REL)
+     {
+       error ("invalid memory model for %<__atomic_store%>");
+       return const0_rtx;
+     }
+ 
+   /* Try issuing an __atomic_store, and allow fallback to __sync_lock_release.
+      Failing that, a store is issued by __atomic_store.  The only way this can
+      fail is if the bool type is larger than a word size.  Unlikely, but
+      handle it anyway for completeness.  */
+   ret = expand_atomic_store (mem, const0_rtx, model, true);
+   if (!ret)
+     {
+       location_t loc = EXPR_LOCATION (exp);
+       /* Otherwise issue the store and a warning.  */
+       warning_at (loc, 0,
+                 "__atomic_clear used on target with no atomic support");
+       emit_move_insn (mem, const0_rtx);
+     }
+   return const0_rtx;
+ }
+ 
+ /* Expand an atomic test_and_set operation.
+       bool _atomic_test_and_set (BOOL *obj, enum memmodel)
+    EXP is the call expression.  */
+ 
+ static rtx
+ expand_builtin_atomic_test_and_set (tree exp)
+ {
+   rtx mem, ret;
+   enum memmodel model;
+   enum machine_mode mode;
+   location_t loc;
+ 
+   mode = mode_for_size (BOOL_TYPE_SIZE, MODE_INT, 0);
+   mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
+   model = get_memmodel (CALL_EXPR_ARG (exp, 1));
+ 
+   /* Try issuing an exchange.  If it is lock free, or if there is a limited
+      functionality __sync_lock_test_and_set, this will utilize it.  */
+   ret = expand_atomic_exchange (NULL_RTX, mem, const1_rtx, model, true);
+   if (ret)
+     return ret;
+ 
+   /* Otherwise, there is no lock free support for test and set.  Issue a
+      warning and simply perform a load and a store.  Since this presumes
+      a non-atomic architecture, also assume single threadedness and don't
+      issue barriers either. */
+ 
+   loc = EXPR_LOCATION (exp);
+   warning_at (loc, 0, 
+             "__atomic_test_and_set used on target with no atomic support");
+   ret = gen_reg_rtx (mode);
+   emit_move_insn (ret, mem);
+   emit_move_insn (mem, const1_rtx);
+   return ret;
+ }
+ 
+ 
  /* Return true if (optional) argument ARG1 of size ARG0 is always lock free on
     this architecture.  If ARG1 is NULL, use typical alignment for size ARG0.  
*/
  
*************** expand_builtin (tree exp, rtx target, rt
*** 6693,6698 ****
--- 6768,6779 ----
        if (target)
        return target;
        break;
+ 
+     case BUILT_IN_ATOMIC_TEST_AND_SET:
+       return expand_builtin_atomic_test_and_set (exp);
+ 
+     case BUILT_IN_ATOMIC_CLEAR:
+       return expand_builtin_atomic_clear (exp);
   
      case BUILT_IN_ATOMIC_ALWAYS_LOCK_FREE:
        return expand_builtin_atomic_always_lock_free (exp);
Index: gcc/sync-builtins.def
===================================================================
*** gcc/sync-builtins.def       (revision 181110)
--- gcc/sync-builtins.def       (working copy)
*************** DEF_SYNC_BUILTIN (BUILT_IN_SYNC_SYNCHRON
*** 259,264 ****
--- 259,270 ----
  
  /* __sync* builtins for the C++ memory model.  */
  
+ DEF_SYNC_BUILTIN (BUILT_IN_ATOMIC_TEST_AND_SET, "__atomic_test_and_set",
+                 BT_FN_BOOL_VPTR_INT, ATTR_NOTHROW_LEAF_LIST)
+ 
+ DEF_SYNC_BUILTIN (BUILT_IN_ATOMIC_CLEAR, "__atomic_clear", 
BT_FN_VOID_VPTR_INT,
+                 ATTR_NOTHROW_LEAF_LIST)
+ 
  DEF_SYNC_BUILTIN (BUILT_IN_ATOMIC_EXCHANGE,
                  "__atomic_exchange",
                  BT_FN_VOID_SIZE_VPTR_PTR_PTR_INT, ATTR_NOTHROW_LEAF_LIST)
Index: gcc/testsuite/gcc.dg/atomic-invalid.c
===================================================================
*** gcc/testsuite/gcc.dg/atomic-invalid.c       (revision 181110)
--- gcc/testsuite/gcc.dg/atomic-invalid.c       (working copy)
***************
*** 4,12 ****
--- 4,14 ----
  /* { dg-require-effective-target sync_int_long } */
  
  #include <stddef.h>
+ #include <stdbool.h>
  
  int i, e, b;
  size_t s;
+ bool x;
  
  main ()
  {
*************** main ()
*** 26,29 ****
--- 28,36 ----
    i = __atomic_always_lock_free (s, NULL); /* { dg-error "non-constant 
argument" } */
  
    __atomic_load_n (&i, 44); /* { dg-warning "invalid memory model" } */
+ 
+   __atomic_clear (&x, __ATOMIC_ACQUIRE); /* { dg-error "invalid memory model" 
} */
+ 
+   __atomic_clear (&x, __ATOMIC_ACQ_REL); /* { dg-error "invalid memory model" 
} */
+ 
  }
Index: gcc/testsuite/gcc.dg/atomic-flag.c
===================================================================
*** gcc/testsuite/gcc.dg/atomic-flag.c  (revision 0)
--- gcc/testsuite/gcc.dg/atomic-flag.c  (revision 0)
***************
*** 0 ****
--- 1,33 ----
+ /* Test __atomic routines for existence and execution.  */
+ /* { dg-do run } */
+ 
+ #include <stdbool.h>
+ 
+ /* Test that __atomic_test_and_set and __atomic_clear builtins execute.  */
+ 
+ extern void abort(void);
+ bool a;
+ 
+ main ()
+ {
+   bool b;
+ 
+   __atomic_clear (&a, __ATOMIC_RELAXED);  /* { dg-warning "__atomic_clear 
used on target with no atomic support" "" { target "cris-*-elf" } } */
+   if (a != 0)
+     abort ();
+ 
+   b = __atomic_test_and_set (&a, __ATOMIC_SEQ_CST);  /* { dg-warning 
"__atomic_test_and_set used on target with no atomic support" "" { target 
"cris-*-elf" } } */
+   if (a != 1 || b != 0)
+     abort ();
+ 
+   b = __atomic_test_and_set (&a, __ATOMIC_ACQ_REL);  /* { dg-warning 
"__atomic_test_and_set used on target with no atomic support" "" { target 
"cris-*-elf" } } */
+   if (b != 1 || a != 1)
+     abort ();
+ 
+   __atomic_clear (&a, __ATOMIC_SEQ_CST);  /* { dg-warning "__atomic_clear 
used on target with no atomic support" "" { target "cris-*-elf" } } */
+   if (a != 0)
+     abort ();
+ 
+   return 0;
+ }
+ 

Reply via email to