2012-06-19 Tom de Vries <vries@codesourcery.com>
authorTom de Vries <vries@codesourcery.com>
Wed, 20 Jun 2012 00:57:23 +0000 (00:57 +0000)
committerMaxim Kuvyrkov <mkuvyrkov@gcc.gnu.org>
Wed, 20 Jun 2012 00:57:23 +0000 (00:57 +0000)
    Maxim Kuvyrkov  <maxim@codesourcery.com>

* config/mips/mips.c (mips_emit_pre_atomic_barrier_p,)
(mips_emit_post_atomic_barrier_p): New static functions.
(mips_process_sync_loop): Use them.  Emit sync memory barriers in
accordance with memory model semantics.  Add return of CMP result for
compare_and_swap.
* config/mips/mips.md: Update comment.
(sync_cmp): New attribute.
(sync_memmodel): New attribute replacing sync_release_barrier.
* config/mips/sync.md (UNSPEC_ATOMIC_COMPARE_AND_SWAP,)
(UNSPEC_ATOMIC_EXCHANGE, UNSPEC_ATOMIC_FETCH_OP): New constants.
(sync_lock_test_and_set, test_and_set_12): Update.
(atomic_compare_and_swap, atomic_exchange, atomic_exchange_llsc,)
(atomic_fetch_add, atomic_fetch_add_llsc): New patterns.

Co-Authored-By: Maxim Kuvyrkov <maxim@codesourcery.com>
From-SVN: r188803

gcc/ChangeLog
gcc/config/mips/mips.c
gcc/config/mips/mips.md
gcc/config/mips/sync.md

index 77e029e..069b61c 100644 (file)
@@ -1,3 +1,20 @@
+2012-06-19  Tom de Vries  <vries@codesourcery.com>
+           Maxim Kuvyrkov  <maxim@codesourcery.com>
+
+       * config/mips/mips.c (mips_emit_pre_atomic_barrier_p,)
+       (mips_emit_post_atomic_barrier_p): New static functions.
+       (mips_process_sync_loop): Use them.  Emit sync memory barriers in
+       accordance with memory model semantics.  Add return of CMP result for
+       compare_and_swap.
+       * config/mips/mips.md: Update comment.
+       (sync_cmp): New attribute.
+       (sync_memmodel): New attribute replacing sync_release_barrier.
+       * config/mips/sync.md (UNSPEC_ATOMIC_COMPARE_AND_SWAP,)
+       (UNSPEC_ATOMIC_EXCHANGE, UNSPEC_ATOMIC_FETCH_OP): New constants.
+       (sync_lock_test_and_set, test_and_set_12): Update.
+       (atomic_compare_and_swap, atomic_exchange, atomic_exchange_llsc,)
+       (atomic_fetch_add, atomic_fetch_add_llsc): New patterns.
+
 2012-06-19  Joseph Myers  <joseph@codesourcery.com>
 
        * config/rs6000/spe.md (*mov_si<mode>_e500_subreg0): Rename to
index 6e0f39c..f37c194 100644 (file)
@@ -11976,6 +11976,45 @@ mips_sync_insn2_template (enum attr_sync_insn2 type)
   gcc_unreachable ();
 }
 
+/* Subroutines of the mips_process_sync_loop.
+   Emit barriers as needed for the memory MODEL.  */
+
+static bool
+mips_emit_pre_atomic_barrier_p (enum memmodel model)
+{
+  switch (model)
+    {
+    case MEMMODEL_RELAXED:
+    case MEMMODEL_CONSUME:
+    case MEMMODEL_ACQUIRE:
+      return false;
+    case MEMMODEL_RELEASE:
+    case MEMMODEL_ACQ_REL:
+    case MEMMODEL_SEQ_CST:
+      return true;
+    default:
+      gcc_unreachable ();
+    }
+}
+
+static bool
+mips_emit_post_atomic_barrier_p (enum memmodel model)
+{
+  switch (model)
+    {
+    case MEMMODEL_RELAXED:
+    case MEMMODEL_CONSUME:
+    case MEMMODEL_RELEASE:
+      return false;
+    case MEMMODEL_ACQUIRE:
+    case MEMMODEL_ACQ_REL:
+    case MEMMODEL_SEQ_CST:
+      return true;
+    default:
+      gcc_unreachable ();
+    }
+}
+
 /* OPERANDS are the operands to a sync loop instruction and INDEX is
    the value of the one of the sync_* attributes.  Return the operand
    referred to by the attribute, or DEFAULT_VALUE if the insn doesn't
@@ -11996,11 +12035,13 @@ static void
 mips_process_sync_loop (rtx insn, rtx *operands)
 {
   rtx at, mem, oldval, newval, inclusive_mask, exclusive_mask;
-  rtx required_oldval, insn1_op2, tmp1, tmp2, tmp3;
+  rtx required_oldval, insn1_op2, tmp1, tmp2, tmp3, cmp;
   unsigned int tmp3_insn;
   enum attr_sync_insn1 insn1;
   enum attr_sync_insn2 insn2;
   bool is_64bit_p;
+  int memmodel_attr;
+  enum memmodel model;
 
   /* Read an operand from the sync_WHAT attribute and store it in
      variable WHAT.  DEFAULT is the default value if no attribute
@@ -12017,6 +12058,7 @@ mips_process_sync_loop (rtx insn, rtx *operands)
   /* Read the other attributes.  */
   at = gen_rtx_REG (GET_MODE (mem), AT_REGNUM);
   READ_OPERAND (oldval, at);
+  READ_OPERAND (cmp, 0);
   READ_OPERAND (newval, at);
   READ_OPERAND (inclusive_mask, 0);
   READ_OPERAND (exclusive_mask, 0);
@@ -12025,10 +12067,23 @@ mips_process_sync_loop (rtx insn, rtx *operands)
   insn1 = get_attr_sync_insn1 (insn);
   insn2 = get_attr_sync_insn2 (insn);
 
+  memmodel_attr = get_attr_sync_memmodel (insn);
+  switch (memmodel_attr)
+    {
+    case 10:
+      model = MEMMODEL_ACQ_REL;
+      break;
+    case 11:
+      model = MEMMODEL_ACQUIRE;
+      break;
+    default:
+      model = INTVAL (operands[memmodel_attr]);
+    }
+
   mips_multi_start ();
 
   /* Output the release side of the memory barrier.  */
-  if (get_attr_sync_release_barrier (insn) == SYNC_RELEASE_BARRIER_YES)
+  if (mips_emit_pre_atomic_barrier_p (model))
     {
       if (required_oldval == 0 && TARGET_OCTEON)
        {
@@ -12066,6 +12121,10 @@ mips_process_sync_loop (rtx insn, rtx *operands)
          tmp1 = at;
        }
       mips_multi_add_insn ("bne\t%0,%z1,2f", tmp1, required_oldval, NULL);
+
+      /* CMP = 0 [delay slot].  */
+      if (cmp)
+        mips_multi_add_insn ("li\t%0,0", cmp, NULL);
     }
 
   /* $TMP1 = OLDVAL & EXCLUSIVE_MASK.  */
@@ -12129,11 +12188,15 @@ mips_process_sync_loop (rtx insn, rtx *operands)
       mips_multi_copy_insn (tmp3_insn);
       mips_multi_set_operand (mips_multi_last_index (), 0, newval);
     }
-  else
+  else if (!(required_oldval && cmp))
     mips_multi_add_insn ("nop", NULL);
 
+  /* CMP = 1 -- either standalone or in a delay slot.  */
+  if (required_oldval && cmp)
+    mips_multi_add_insn ("li\t%0,1", cmp, NULL);
+
   /* Output the acquire side of the memory barrier.  */
-  if (TARGET_SYNC_AFTER_SC)
+  if (TARGET_SYNC_AFTER_SC && mips_emit_post_atomic_barrier_p (model))
     mips_multi_add_insn ("sync", NULL);
 
   /* Output the exit label, if needed.  */
index 0d85340..5b1735f 100644 (file)
 ;;       if (RELEASE_BARRIER == YES) sync
 ;;    1: OLDVAL = *MEM
 ;;       if ((OLDVAL & INCLUSIVE_MASK) != REQUIRED_OLDVAL) goto 2
+;;         CMP  = 0 [delay slot]
 ;;       $TMP1 = OLDVAL & EXCLUSIVE_MASK
 ;;       $TMP2 = INSN1 (OLDVAL, INSN1_OP2)
 ;;       $TMP3 = INSN2 ($TMP2, INCLUSIVE_MASK)
 ;;       $AT |= $TMP1 | $TMP3
 ;;       if (!commit (*MEM = $AT)) goto 1.
 ;;         if (INSN1 != MOVE && INSN1 != LI) NEWVAL = $TMP3 [delay slot]
-;;       sync
+;;       CMP  = 1
+;;       if (ACQUIRE_BARRIER == YES) sync
 ;;    2:
 ;;
 ;; where "$" values are temporaries and where the other values are
 ;; specified, the following values are used instead:
 ;;
 ;;    - OLDVAL: $AT
+;;    - CMP: NONE
 ;;    - NEWVAL: $AT
 ;;    - INCLUSIVE_MASK: -1
 ;;    - REQUIRED_OLDVAL: OLDVAL & INCLUSIVE_MASK
 ;; but the gen* programs don't yet support that.
 (define_attr "sync_mem" "none,0,1,2,3,4,5" (const_string "none"))
 (define_attr "sync_oldval" "none,0,1,2,3,4,5" (const_string "none"))
+(define_attr "sync_cmp" "none,0,1,2,3,4,5" (const_string "none"))
 (define_attr "sync_newval" "none,0,1,2,3,4,5" (const_string "none"))
 (define_attr "sync_inclusive_mask" "none,0,1,2,3,4,5" (const_string "none"))
 (define_attr "sync_exclusive_mask" "none,0,1,2,3,4,5" (const_string "none"))
   (const_string "move"))
 (define_attr "sync_insn2" "nop,and,xor,not"
   (const_string "nop"))
-(define_attr "sync_release_barrier" "yes,no"
-  (const_string "yes"))
+;; Memory model specifier.
+;; "0"-"9" values specify the operand that stores the memory model value.
+;; "10" specifies MEMMODEL_ACQ_REL,
+;; "11" specifies MEMMODEL_ACQUIRE.
+(define_attr "sync_memmodel" "" (const_int 10))
 
 ;; Length of instruction in bytes.
 (define_attr "length" ""
index 1b4097e..604aefa 100644 (file)
@@ -29,6 +29,9 @@
   UNSPEC_SYNC_EXCHANGE
   UNSPEC_SYNC_EXCHANGE_12
   UNSPEC_MEMORY_BARRIER
+  UNSPEC_ATOMIC_COMPARE_AND_SWAP
+  UNSPEC_ATOMIC_EXCHANGE
+  UNSPEC_ATOMIC_FETCH_OP
 ])
 
 ;; Atomic fetch bitwise operations.
@@ -54,6 +57,7 @@
   "GENERATE_SYNC"
   { return mips_output_sync (); })
 
+;; Can be removed in favor of atomic_compare_and_swap below.
 (define_insn "sync_compare_and_swap<mode>"
   [(set (match_operand:GPR 0 "register_operand" "=&d,&d")
        (match_operand:GPR 1 "memory_operand" "+R,R"))
    (set_attr "sync_mem" "0")
    (set_attr "sync_insn1_op2" "1")])
 
+;; Can be removed in favor of atomic_fetch_add below.
 (define_insn "sync_old_add<mode>"
   [(set (match_operand:GPR 0 "register_operand" "=&d,&d")
        (match_operand:GPR 1 "memory_operand" "+R,R"))
         UNSPEC_SYNC_EXCHANGE))]
   "GENERATE_LL_SC"
   { return mips_output_sync_loop (insn, operands); }
-  [(set_attr "sync_release_barrier" "no")
+  [(set_attr "sync_memmodel" "11")
    (set_attr "sync_insn1" "li,move")
    (set_attr "sync_oldval" "0")
    (set_attr "sync_mem" "1")
          UNSPEC_SYNC_EXCHANGE_12))]
   "GENERATE_LL_SC"
   { return mips_output_sync_loop (insn, operands); }
-  [(set_attr "sync_release_barrier" "no")
+  [(set_attr "sync_memmodel" "11")
    (set_attr "sync_oldval" "0")
    (set_attr "sync_mem" "1")
    ;; Unused, but needed to give the number of operands expected by
    (set_attr "sync_inclusive_mask" "2")
    (set_attr "sync_exclusive_mask" "3")
    (set_attr "sync_insn1_op2" "4")])
+
+(define_insn "atomic_compare_and_swap<mode>"
+  [(set (match_operand:GPR 0 "register_operand" "=&d,&d")
+       ;; Logically this unspec is an "eq" operator, but we need to obscure
+       ;; reads and writes from/to memory with an unspec to prevent
+       ;; optimizations on shared memory locations.  Otherwise, comparison in
+       ;; { mem = 2; if (atomic_cmp_swap(mem,...) == 2) ...; }
+       ;; would be optimized away.  In addition to that we need to use
+       ;; unspec_volatile, not just plain unspec -- for the sake of other
+       ;; threads -- to make sure we don't remove the entirety of the pattern
+       ;; just because current thread doesn't observe any effect from it.
+       ;; TODO: the obscuring unspec can be relaxed for permissive memory
+       ;; models.
+       ;; Same applies to other atomic_* patterns.
+       (unspec_volatile:GPR [(match_operand:GPR 2 "memory_operand" "+R,R")
+                             (match_operand:GPR 3 "reg_or_0_operand" "dJ,dJ")]
+        UNSPEC_ATOMIC_COMPARE_AND_SWAP))
+   (set (match_operand:GPR 1 "register_operand" "=&d,&d")
+       (unspec_volatile:GPR [(match_dup 2)]
+        UNSPEC_ATOMIC_COMPARE_AND_SWAP))
+   (set (match_dup 2)
+       (unspec_volatile:GPR [(match_dup 2)
+                             (match_dup 3)
+                             (match_operand:GPR 4 "arith_operand" "I,d")]
+        UNSPEC_ATOMIC_COMPARE_AND_SWAP))
+   (unspec_volatile:GPR [(match_operand:SI 5 "const_int_operand")
+                        (match_operand:SI 6 "const_int_operand")
+                        (match_operand:SI 7 "const_int_operand")]
+    UNSPEC_ATOMIC_COMPARE_AND_SWAP)]
+  "GENERATE_LL_SC"
+  { return mips_output_sync_loop (insn, operands); }
+  [(set_attr "sync_insn1" "li,move")
+   (set_attr "sync_oldval" "1")
+   (set_attr "sync_cmp" "0")
+   (set_attr "sync_mem" "2")
+   (set_attr "sync_required_oldval" "3")
+   (set_attr "sync_insn1_op2" "4")
+   (set_attr "sync_memmodel" "6")])
+
+(define_expand "atomic_exchange<mode>"
+  [(match_operand:GPR 0 "register_operand")
+   (match_operand:GPR 1 "memory_operand")
+   (match_operand:GPR 2 "arith_operand")
+   (match_operand:SI 3 "const_int_operand")]
+  "GENERATE_LL_SC"
+{
+    emit_insn (gen_atomic_exchange<mode>_llsc (operands[0], operands[1],
+                                              operands[2], operands[3]));
+  DONE;
+})
+
+(define_insn "atomic_exchange<mode>_llsc"
+  [(set (match_operand:GPR 0 "register_operand" "=&d,&d")
+       (unspec_volatile:GPR [(match_operand:GPR 1 "memory_operand" "+R,R")]
+        UNSPEC_ATOMIC_EXCHANGE))
+   (set (match_dup 1)
+       (unspec_volatile:GPR [(match_operand:GPR 2 "arith_operand" "I,d")]
+        UNSPEC_ATOMIC_EXCHANGE))
+   (unspec_volatile:GPR [(match_operand:SI 3 "const_int_operand")]
+    UNSPEC_ATOMIC_EXCHANGE)]
+  "GENERATE_LL_SC"
+  { return mips_output_sync_loop (insn, operands); }
+  [(set_attr "sync_insn1" "li,move")
+   (set_attr "sync_oldval" "0")
+   (set_attr "sync_mem" "1")
+   (set_attr "sync_insn1_op2" "2")
+   (set_attr "sync_memmodel" "3")])
+
+(define_expand "atomic_fetch_add<mode>"
+  [(match_operand:GPR 0 "register_operand")
+   (match_operand:GPR 1 "memory_operand")
+   (match_operand:GPR 2 "arith_operand")
+   (match_operand:SI 3 "const_int_operand")]
+  "GENERATE_LL_SC"
+{
+    emit_insn (gen_atomic_fetch_add<mode>_llsc (operands[0], operands[1],
+                                               operands[2], operands[3]));
+  DONE;
+})
+
+(define_insn "atomic_fetch_add<mode>_llsc"
+  [(set (match_operand:GPR 0 "register_operand" "=&d,&d")
+       (unspec_volatile:GPR [(match_operand:GPR 1 "memory_operand" "+R,R")]
+        UNSPEC_ATOMIC_FETCH_OP))
+   (set (match_dup 1)
+       (unspec_volatile:GPR
+        [(plus:GPR (match_dup 1)
+                   (match_operand:GPR 2 "arith_operand" "I,d"))]
+        UNSPEC_ATOMIC_FETCH_OP))
+   (unspec_volatile:GPR [(match_operand:SI 3 "const_int_operand")]
+    UNSPEC_ATOMIC_FETCH_OP)]
+  "GENERATE_LL_SC"
+  { return mips_output_sync_loop (insn, operands); }
+  [(set_attr "sync_insn1" "addiu,addu")
+   (set_attr "sync_oldval" "0")
+   (set_attr "sync_mem" "1")
+   (set_attr "sync_insn1_op2" "2")
+   (set_attr "sync_memmodel" "3")])