On 11/06/2011 07:38 PM, Hans-Peter Nilsson wrote:

This (formally a change in the range 181027:181034) got me three
libstdc++ regressions for cris-elf, which has no "atomic"
support whatsoever (well, not the version represented in
"cris-elf"), so something is amiss at the bottom of the default
path:
yes, I have a final pending patch which didn't make it to the branch before the merge. It changes the behaviour of atomic_flag on targets with no compare_and_swap. I *think* it will resolve your problem.

I've attached the early version of the patch which you can try. Its missing a documentation change I was going to add tomorrow before submitting, but we can see if it resolves your problem. Give it a shot and let me know.

Andrew


        gcc
        * expr.h (expand_atomic_exchange): Add extra parameter.
        * builtins.c (expand_builtin_sync_lock_test_and_set): Call
        expand_atomic_exchange with true.
        (expand_builtin_atomic_exchange): Call expand_atomic_exchange with
        false.
        * optabs.c (expand_atomic_exchange): Add use_test_and_set param and
        only fall back to __sync_test_and_set when true.
        (expand_atomic_store): Add false to expand_atomic_exchange call.

        libstdc++-v3
        * include/bits/atomic_base.h (test_and_set): Call __atomic_exchange
        only if it is always lock free, otherwise __sync_lock_test_and_set.


Index: gcc/expr.h
===================================================================
*** gcc/expr.h  (revision 180770)
--- gcc/expr.h  (working copy)
*************** rtx emit_conditional_add (rtx, enum rtx_
*** 215,221 ****
  rtx expand_sync_operation (rtx, rtx, enum rtx_code);
  rtx expand_sync_fetch_operation (rtx, rtx, enum rtx_code, bool, rtx);
  
! rtx expand_atomic_exchange (rtx, rtx, rtx, enum memmodel);
  rtx expand_atomic_load (rtx, rtx, enum memmodel);
  rtx expand_atomic_store (rtx, rtx, enum memmodel);
  rtx expand_atomic_fetch_op (rtx, rtx, rtx, enum rtx_code, enum memmodel, 
--- 215,221 ----
  rtx expand_sync_operation (rtx, rtx, enum rtx_code);
  rtx expand_sync_fetch_operation (rtx, rtx, enum rtx_code, bool, rtx);
  
! rtx expand_atomic_exchange (rtx, rtx, rtx, enum memmodel, bool);
  rtx expand_atomic_load (rtx, rtx, enum memmodel);
  rtx expand_atomic_store (rtx, rtx, enum memmodel);
  rtx expand_atomic_fetch_op (rtx, rtx, rtx, enum rtx_code, enum memmodel, 
Index: gcc/builtins.c
===================================================================
*** gcc/builtins.c      (revision 180789)
--- gcc/builtins.c      (working copy)
*************** expand_builtin_sync_lock_test_and_set (e
*** 5221,5227 ****
    mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
    val = expand_expr_force_mode (CALL_EXPR_ARG (exp, 1), mode);
  
!   return expand_atomic_exchange (target, mem, val, MEMMODEL_ACQUIRE);
  }
  
  /* Expand the __sync_lock_release intrinsic.  EXP is the CALL_EXPR.  */
--- 5221,5227 ----
    mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
    val = expand_expr_force_mode (CALL_EXPR_ARG (exp, 1), mode);
  
!   return expand_atomic_exchange (target, mem, val, MEMMODEL_ACQUIRE, true);
  }
  
  /* Expand the __sync_lock_release intrinsic.  EXP is the CALL_EXPR.  */
*************** expand_builtin_atomic_exchange (enum mac
*** 5284,5290 ****
    mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
    val = expand_expr_force_mode (CALL_EXPR_ARG (exp, 1), mode);
  
!   return expand_atomic_exchange (target, mem, val, model);
  }
  
  /* Expand the __atomic_compare_exchange intrinsic:
--- 5284,5290 ----
    mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
    val = expand_expr_force_mode (CALL_EXPR_ARG (exp, 1), mode);
  
!   return expand_atomic_exchange (target, mem, val, model, false);
  }
  
  /* Expand the __atomic_compare_exchange intrinsic:
Index: gcc/optabs.c
===================================================================
*** gcc/optabs.c        (revision 180770)
--- gcc/optabs.c        (working copy)
*************** expand_compare_and_swap_loop (rtx mem, r
*** 6872,6881 ****
     atomically store VAL in MEM and return the previous value in MEM.
  
     MEMMODEL is the memory model variant to use.
!    TARGET is an option place to stick the return value.  */
  
  rtx
! expand_atomic_exchange (rtx target, rtx mem, rtx val, enum memmodel model)
  {
    enum machine_mode mode = GET_MODE (mem);
    enum insn_code icode;
--- 6872,6884 ----
     atomically store VAL in MEM and return the previous value in MEM.
  
     MEMMODEL is the memory model variant to use.
!    TARGET is an optional place to stick the return value.  
!    USE_TEST_AND_SET indicates whether __sync_lock_test_and_set should be used
!    as a fall back if the atomic_exchange pattern does not exist.  */
  
  rtx
! expand_atomic_exchange (rtx target, rtx mem, rtx val, enum memmodel model,
!                       bool use_test_and_set)                  
  {
    enum machine_mode mode = GET_MODE (mem);
    enum insn_code icode;
*************** expand_atomic_exchange (rtx target, rtx 
*** 6900,6930 ****
       acquire barrier.  If the pattern exists, and the memory model is stronger
       than acquire, add a release barrier before the instruction.
       The barrier is not needed if sync_lock_test_and_set doesn't exist since
!      it will expand into a compare-and-swap loop.  */
  
!   icode = direct_optab_handler (sync_lock_test_and_set_optab, mode);
!   last_insn = get_last_insn ();
!   if ((icode != CODE_FOR_nothing) && (model == MEMMODEL_SEQ_CST || 
!                                     model == MEMMODEL_RELEASE ||
!                                     model == MEMMODEL_ACQ_REL))
!     expand_builtin_mem_thread_fence (model);
  
!   if (icode != CODE_FOR_nothing)
!     {
!       struct expand_operand ops[3];
  
!       create_output_operand (&ops[0], target, mode);
!       create_fixed_operand (&ops[1], mem);
!       /* VAL may have been promoted to a wider mode.  Shrink it if so.  */
!       create_convert_operand_to (&ops[2], val, mode, true);
!       if (maybe_expand_insn (icode, 3, ops))
!       return ops[0].value;
!     }
  
!   /* Remove any fence we may have inserted since a compare and swap loop is a
!      full memory barrier.  */
!   if (last_insn != get_last_insn ())
!     delete_insns_since (last_insn);
  
    /* Otherwise, use a compare-and-swap loop for the exchange.  */
    if (can_compare_and_swap_p (mode))
--- 6903,6941 ----
       acquire barrier.  If the pattern exists, and the memory model is stronger
       than acquire, add a release barrier before the instruction.
       The barrier is not needed if sync_lock_test_and_set doesn't exist since
!      it will expand into a compare-and-swap loop.
  
!      Some targets have non-compliant test_and_sets, so it would be incorrect
!      to emit a test_and_set in place of an __atomic_exchange.  The 
test_and_set
!      builtin shares this expander since exchange can always replace the
!      test_and_set.  */
! 
!   if (use_test_and_set)
!     {
!       icode = direct_optab_handler (sync_lock_test_and_set_optab, mode);
!       last_insn = get_last_insn ();
!       if ((icode != CODE_FOR_nothing) && (model == MEMMODEL_SEQ_CST || 
!                                         model == MEMMODEL_RELEASE ||
!                                         model == MEMMODEL_ACQ_REL))
!       expand_builtin_mem_thread_fence (model);
  
!       if (icode != CODE_FOR_nothing)
!       {
!         struct expand_operand ops[3];
  
!         create_output_operand (&ops[0], target, mode);
!         create_fixed_operand (&ops[1], mem);
!         /* VAL may have been promoted to a wider mode.  Shrink it if so.  */
!         create_convert_operand_to (&ops[2], val, mode, true);
!         if (maybe_expand_insn (icode, 3, ops))
!           return ops[0].value;
!       }
  
!       /* Remove any fence that was inserted since a compare and swap loop is
!        already a full memory barrier.  */
!       if (last_insn != get_last_insn ())
!       delete_insns_since (last_insn);
!     }
  
    /* Otherwise, use a compare-and-swap loop for the exchange.  */
    if (can_compare_and_swap_p (mode))
*************** expand_atomic_store (rtx mem, rtx val, e
*** 7130,7136 ****
       the result.  If that doesn't work, don't do anything.  */
    if (GET_MODE_PRECISION(mode) > BITS_PER_WORD)
      {
!       rtx target = expand_atomic_exchange (NULL_RTX, mem, val, model);
        if (target)
          return const0_rtx;
        else
--- 7141,7147 ----
       the result.  If that doesn't work, don't do anything.  */
    if (GET_MODE_PRECISION(mode) > BITS_PER_WORD)
      {
!       rtx target = expand_atomic_exchange (NULL_RTX, mem, val, model, false);
        if (target)
          return const0_rtx;
        else
Index: libstdc++-v3/include/bits/atomic_base.h
===================================================================
*** libstdc++-v3/include/bits/atomic_base.h     (revision 180789)
--- libstdc++-v3/include/bits/atomic_base.h     (working copy)
*************** _GLIBCXX_BEGIN_NAMESPACE_VERSION
*** 261,273 ****
      bool
      test_and_set(memory_order __m = memory_order_seq_cst) noexcept
      {
!       return __atomic_exchange_n(&_M_i, 1, __m);
      }
  
      bool
      test_and_set(memory_order __m = memory_order_seq_cst) volatile noexcept
      {
!       return __atomic_exchange_n(&_M_i, 1, __m);
      }
  
      void
--- 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

Reply via email to