mips.md (UNSPEC_SYNC_NEW_OP_12, [...]): New define_constants.
authorDavid Daney <ddaney@avtrex.com>
Tue, 20 May 2008 23:13:13 +0000 (23:13 +0000)
committerDavid Daney <daney@gcc.gnu.org>
Tue, 20 May 2008 23:13:13 +0000 (23:13 +0000)
2008-05-20  David Daney  <ddaney@avtrex.com>

* config/mips/mips.md (UNSPEC_SYNC_NEW_OP_12,
UNSPEC_SYNC_OLD_OP_12,
UNSPEC_SYNC_EXCHANGE_12): New define_constants.
(UNSPEC_SYNC_EXCHANGE, UNSPEC_MEMORY_BARRIER,
UNSPEC_SET_GOT_VERSION,
UNSPEC_UPDATE_GOT_VERSION): Renumber.
(optab, insn): Add 'plus' and 'minus' to define_code_attr.
(atomic_hiqi_op): New define_code_iterator.
(sync_compare_and_swap<mode>): Call
mips_expand_atomic_qihi instead of
mips_expand_compare_and_swap_12.
(compare_and_swap_12): Use MIPS_COMPARE_AND_SWAP_12 instead of
MIPS_COMPARE_AND_SWAP_12_0.  Pass argument to
MIPS_COMPARE_AND_SWAP_12.
(sync_<optab><mode>, sync_old_<optab><mode>,
sync_new_<optab><mode>, sync_nand<mode>, sync_old_nand<mode>,
sync_new_nand<mode>): New define_expands for HI and QI mode
operands.
(sync_<optab>_12, sync_old_<optab>_12, sync_new_<optab>_12,
sync_nand_12, sync_old_nand_12, sync_new_nand_12): New insns.
(sync_lock_test_and_set<mode>): New define_expand for HI and QI
modes.
(test_and_set_12): New insn.
(sync_old_add<mode>, sync_new_add<mode>, sync_old_<optab><mode>,
sync_new_<optab><mode>, sync_old_nand<mode>,
sync_new_nand<mode>, sync_lock_test_and_set<mode>):  Add early
clobber to operand 0 for SI and DI mode insns.
* config/mips/mips-protos.h (mips_gen_fn_6, mips_gen_fn_5,
mips_gen_fn_4): New typedefs.
(mips_gen_fn_ptrs): Define new union type.
(mips_expand_compare_and_swap_12): Remove declaration.
(mips_expand_atomic_qihi): Declare function.
* config/mips/mips.c (mips_expand_compare_and_swap_12): Rename to...
(mips_expand_atomic_qihi): ... this.  Use new generator function
parameter.
* config/mips/mips.h (MIPS_COMPARE_AND_SWAP_12): Add OPS parameter.
(MIPS_COMPARE_AND_SWAP_12_0): Delete macro.
(MIPS_COMPARE_AND_SWAP_12_ZERO_OP,
MIPS_COMPARE_AND_SWAP_12_NONZERO_OP,
MIPS_SYNC_OP_12, MIPS_SYNC_OP_12_NOT_NOP,
MIPS_SYNC_OP_12_NOT_NOT, MIPS_SYNC_OLD_OP_12,
MIPS_SYNC_OLD_OP_12_NOT_NOP, MIPS_SYNC_OLD_OP_12_NOT_NOP_REG,
MIPS_SYNC_OLD_OP_12_NOT_NOT, MIPS_SYNC_OLD_OP_12_NOT_NOT_REG,
MIPS_SYNC_NEW_OP_12, MIPS_SYNC_NEW_OP_12_NOT_NOP,
MIPS_SYNC_NEW_OP_12_NOT_NOT, MIPS_SYNC_EXCHANGE_12,
MIPS_SYNC_EXCHANGE_12_ZERO_OP,
MIPS_SYNC_EXCHANGE_12_NONZERO_OP): New macros.

From-SVN: r135684

gcc/ChangeLog
gcc/config/mips/mips-protos.h
gcc/config/mips/mips.c
gcc/config/mips/mips.h
gcc/config/mips/mips.md

index 41e28d4981da884ede20423e8ff0e034e15fd066..f354031add9a59641775cc3aa7a83fe5d9abbddf 100644 (file)
@@ -1,3 +1,53 @@
+2008-05-20  David Daney  <ddaney@avtrex.com>
+
+       * config/mips/mips.md (UNSPEC_SYNC_NEW_OP_12,
+       UNSPEC_SYNC_OLD_OP_12,
+       UNSPEC_SYNC_EXCHANGE_12): New define_constants.
+       (UNSPEC_SYNC_EXCHANGE, UNSPEC_MEMORY_BARRIER,
+       UNSPEC_SET_GOT_VERSION,
+       UNSPEC_UPDATE_GOT_VERSION): Renumber.
+       (optab, insn): Add 'plus' and 'minus' to define_code_attr.
+       (atomic_hiqi_op): New define_code_iterator.
+       (sync_compare_and_swap<mode>): Call
+       mips_expand_atomic_qihi instead of
+       mips_expand_compare_and_swap_12.
+       (compare_and_swap_12): Use MIPS_COMPARE_AND_SWAP_12 instead of
+       MIPS_COMPARE_AND_SWAP_12_0.  Pass argument to
+       MIPS_COMPARE_AND_SWAP_12.
+       (sync_<optab><mode>, sync_old_<optab><mode>,
+       sync_new_<optab><mode>, sync_nand<mode>, sync_old_nand<mode>,
+       sync_new_nand<mode>): New define_expands for HI and QI mode
+       operands.
+       (sync_<optab>_12, sync_old_<optab>_12, sync_new_<optab>_12,
+       sync_nand_12, sync_old_nand_12, sync_new_nand_12): New insns.
+       (sync_lock_test_and_set<mode>): New define_expand for HI and QI
+       modes.
+       (test_and_set_12): New insn.
+       (sync_old_add<mode>, sync_new_add<mode>, sync_old_<optab><mode>,
+       sync_new_<optab><mode>, sync_old_nand<mode>,
+       sync_new_nand<mode>, sync_lock_test_and_set<mode>):  Add early
+       clobber to operand 0 for SI and DI mode insns.
+       * config/mips/mips-protos.h (mips_gen_fn_6, mips_gen_fn_5,
+       mips_gen_fn_4): New typedefs.
+       (mips_gen_fn_ptrs): Define new union type.
+       (mips_expand_compare_and_swap_12): Remove declaration.
+       (mips_expand_atomic_qihi): Declare function.
+       * config/mips/mips.c (mips_expand_compare_and_swap_12): Rename to...
+       (mips_expand_atomic_qihi): ... this.  Use new generator function
+       parameter.
+       * config/mips/mips.h (MIPS_COMPARE_AND_SWAP_12): Add OPS parameter.
+       (MIPS_COMPARE_AND_SWAP_12_0): Delete macro.
+       (MIPS_COMPARE_AND_SWAP_12_ZERO_OP,
+       MIPS_COMPARE_AND_SWAP_12_NONZERO_OP,
+       MIPS_SYNC_OP_12, MIPS_SYNC_OP_12_NOT_NOP,
+       MIPS_SYNC_OP_12_NOT_NOT, MIPS_SYNC_OLD_OP_12,
+       MIPS_SYNC_OLD_OP_12_NOT_NOP, MIPS_SYNC_OLD_OP_12_NOT_NOP_REG,
+       MIPS_SYNC_OLD_OP_12_NOT_NOT, MIPS_SYNC_OLD_OP_12_NOT_NOT_REG,
+       MIPS_SYNC_NEW_OP_12, MIPS_SYNC_NEW_OP_12_NOT_NOP,
+       MIPS_SYNC_NEW_OP_12_NOT_NOT, MIPS_SYNC_EXCHANGE_12,
+       MIPS_SYNC_EXCHANGE_12_ZERO_OP,
+       MIPS_SYNC_EXCHANGE_12_NONZERO_OP): New macros.
+
 2008-05-20  H.J. Lu  <hongjiu.lu@intel.com>
 
        * config/i386/i386.c (ix86_expand_vector_init_one_nonzero): Add
index fbac8fcfd07a1592e9b4c665f8ef3f609172e443..db65aab93c91d5be8116f44fe043a9989f5fdbb9 100644 (file)
@@ -292,6 +292,14 @@ extern bool mips_use_ins_ext_p (rtx, HOST_WIDE_INT, HOST_WIDE_INT);
 extern const char *mips16e_output_save_restore (rtx, HOST_WIDE_INT);
 extern bool mips16e_save_restore_pattern_p (rtx, HOST_WIDE_INT,
                                            struct mips16e_save_restore_info *);
-extern void mips_expand_compare_and_swap_12 (rtx, rtx, rtx, rtx);
+union mips_gen_fn_ptrs
+{
+  rtx (*fn_6) (rtx, rtx, rtx, rtx, rtx, rtx);
+  rtx (*fn_5) (rtx, rtx, rtx, rtx, rtx);
+  rtx (*fn_4) (rtx, rtx, rtx, rtx);
+};
+
+extern void mips_expand_atomic_qihi (union mips_gen_fn_ptrs,
+                                    rtx, rtx, rtx, rtx);
 
 #endif /* ! GCC_MIPS_PROTOS_H */
index 218953385b3e8f6af01f9bae2a55cf72a4b4f540..6cb0d293ddab2956d3e077795be597d96caa1b4d 100644 (file)
@@ -5873,14 +5873,29 @@ mips_expand_synci_loop (rtx begin, rtx end)
   emit_jump_insn (gen_condjump (cmp_result, label));
 }
 \f
-/* Expand a QI or HI mode compare_and_swap.  The operands are the same
-   as for the generator function.  */
+/* Expand a QI or HI mode atomic memory operation.
+
+   GENERATOR contains a pointer to the gen_* function that generates
+   the SI mode underlying atomic operation using masks that we
+   calculate.
+
+   RESULT is the return register for the operation.  Its value is NULL
+   if unused.
+
+   MEM is the location of the atomic access.
+
+   OLDVAL is the first operand for the operation.
+
+   NEWVAL is the optional second operand for the operation.  Its value
+   is NULL if unused.  */
 
 void
-mips_expand_compare_and_swap_12 (rtx result, rtx mem, rtx oldval, rtx newval)
+mips_expand_atomic_qihi (union mips_gen_fn_ptrs generator,
+                         rtx result, rtx mem, rtx oldval, rtx newval)
 {
   rtx orig_addr, memsi_addr, memsi, shift, shiftsi, unshifted_mask;
-  rtx unshifted_mask_reg, mask, inverted_mask, res;
+  rtx unshifted_mask_reg, mask, inverted_mask, si_op;
+  rtx res = NULL;
   enum machine_mode mode;
 
   mode = GET_MODE (mem);
@@ -5927,7 +5942,7 @@ mips_expand_compare_and_swap_12 (rtx result, rtx mem, rtx oldval, rtx newval)
     }
 
   /* Do the same for the new value.  */
-  if (newval != const0_rtx)
+  if (newval && newval != const0_rtx)
     {
       newval = convert_modes (SImode, mode, newval, true);
       newval = force_reg (SImode, newval);
@@ -5935,14 +5950,24 @@ mips_expand_compare_and_swap_12 (rtx result, rtx mem, rtx oldval, rtx newval)
     }
 
   /* Do the SImode atomic access.  */
-  res = gen_reg_rtx (SImode);
-  emit_insn (gen_compare_and_swap_12 (res, memsi, mask, inverted_mask,
-                                     oldval, newval));
-
-  /* Shift and convert the result.  */
-  mips_emit_binary (AND, res, res, mask);
-  mips_emit_binary (LSHIFTRT, res, res, shiftsi);
-  mips_emit_move (result, gen_lowpart (GET_MODE (result), res));
+  if (result)
+    res = gen_reg_rtx (SImode);
+  if (newval)
+    si_op = generator.fn_6 (res, memsi, mask, inverted_mask, oldval, newval);
+  else if (result)
+    si_op = generator.fn_5 (res, memsi, mask, inverted_mask, oldval);
+  else
+    si_op = generator.fn_4 (memsi, mask, inverted_mask, oldval);
+
+  emit_insn (si_op);
+
+  if (result)
+    {
+      /* Shift and convert the result.  */
+      mips_emit_binary (AND, res, res, mask);
+      mips_emit_binary (LSHIFTRT, res, res, shiftsi);
+      mips_emit_move (result, gen_lowpart (GET_MODE (result), res));
+    }
 }
 
 /* Return true if it is possible to use left/right accesses for a
index 9f59f1a2550c1cb6c90a71ec018d1bc658860ce9..765552778b5d4b2586d792eb2568d2bd5799bbeb 100644 (file)
@@ -1,6 +1,6 @@
 /* Definitions of target machine for GNU compiler.  MIPS version.
    Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998
-   1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007
+   1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007, 2008
    Free Software Foundation, Inc.
    Contributed by A. Lichnewsky (lich@inria.inria.fr).
    Changed by Michael Meissner (meissner@osf.org).
@@ -2908,41 +2908,32 @@ while (0)
 /* Return an asm string that atomically:
 
      - Given that %2 contains a bit mask and %3 the inverted mask and
-       that %4 and %5 have already been ANDed with $2.
+       that %4 and %5 have already been ANDed with %2.
 
      - Compares the bits in memory reference %1 selected by mask %2 to
        register %4 and, if they are equal, changes the selected bits
        in memory to %5.
 
      - Sets register %0 to the old value of memory reference %1.
- */
-#define MIPS_COMPARE_AND_SWAP_12               \
-  "%(%<%[%|sync\n"                             \
-  "1:\tll\t%0,%1\n"                            \
-  "\tand\t%@,%0,%2\n"                          \
-  "\tbne\t%@,%z4,2f\n"                         \
-  "\tand\t%@,%0,%3\n"                          \
-  "\tor\t%@,%@,%5\n"                           \
-  "\tsc\t%@,%1\n"                              \
-  "\tbeq\t%@,%.,1b\n"                          \
-  "\tnop\n"                                    \
-  "\tsync%-%]%>%)\n"                           \
-  "2:\n"
 
-/* Like MIPS_COMPARE_AND_SWAP_12, except %5 is a constant zero,
-   so the OR can be omitted.  */
-#define MIPS_COMPARE_AND_SWAP_12_0             \
+    OPS are the instructions needed to OR %5 with %@.  */
+#define MIPS_COMPARE_AND_SWAP_12(OPS)          \
   "%(%<%[%|sync\n"                             \
   "1:\tll\t%0,%1\n"                            \
   "\tand\t%@,%0,%2\n"                          \
   "\tbne\t%@,%z4,2f\n"                         \
   "\tand\t%@,%0,%3\n"                          \
+  OPS                                          \
   "\tsc\t%@,%1\n"                              \
   "\tbeq\t%@,%.,1b\n"                          \
   "\tnop\n"                                    \
   "\tsync%-%]%>%)\n"                           \
   "2:\n"
 
+#define MIPS_COMPARE_AND_SWAP_12_ZERO_OP ""
+#define MIPS_COMPARE_AND_SWAP_12_NONZERO_OP "\tor\t%@,%@,%5\n"
+
+
 /* Return an asm string that atomically:
 
      - Sets memory reference %0 to %0 INSN %1.
@@ -2958,6 +2949,97 @@ while (0)
   "\tnop\n"                                    \
   "\tsync%-%]%>%)"
 
+/* Return an asm string that atomically:
+
+     - Given that %1 contains a bit mask and %2 the inverted mask and
+       that %3 has already been ANDed with %1.
+
+     - Sets the selected bits of memory reference %0 to %0 INSN %3.
+
+     - Uses scratch register %4.
+
+    NOT_OP are the optional instructions to do a bit-wise not
+    operation in conjunction with an AND INSN to generate a sync_nand
+    operation.  */
+#define MIPS_SYNC_OP_12(INSN, NOT_OP)          \
+  "%(%<%[%|sync\n"                             \
+  "1:\tll\t%4,%0\n"                            \
+  "\tand\t%@,%4,%2\n"                          \
+  NOT_OP                                       \
+  "\t" INSN "\t%4,%4,%z3\n"                    \
+  "\tand\t%4,%4,%1\n"                          \
+  "\tor\t%@,%@,%4\n"                           \
+  "\tsc\t%@,%0\n"                              \
+  "\tbeq\t%@,%.,1b\n"                          \
+  "\tnop\n"                                    \
+  "\tsync%-%]%>%)"
+
+#define MIPS_SYNC_OP_12_NOT_NOP ""
+#define MIPS_SYNC_OP_12_NOT_NOT "\tnor\t%4,%4,%.\n"
+
+/* Return an asm string that atomically:
+
+     - Given that %2 contains a bit mask and %3 the inverted mask and
+       that %4 has already been ANDed with %2.
+
+     - Sets the selected bits of memory reference %1 to %1 INSN %4.
+
+     - Sets %0 to the original value of %1.
+
+     - Uses scratch register %5.
+
+    NOT_OP are the optional instructions to do a bit-wise not
+    operation in conjunction with an AND INSN to generate a sync_nand
+    operation.
+
+    REG is used in conjunction with NOT_OP and is used to select the
+    register operated on by the INSN.  */
+#define MIPS_SYNC_OLD_OP_12(INSN, NOT_OP, REG) \
+  "%(%<%[%|sync\n"                             \
+  "1:\tll\t%0,%1\n"                            \
+  "\tand\t%@,%0,%3\n"                          \
+  NOT_OP                                       \
+  "\t" INSN "\t%5," REG ",%z4\n"               \
+  "\tand\t%5,%5,%2\n"                          \
+  "\tor\t%@,%@,%5\n"                           \
+  "\tsc\t%@,%1\n"                              \
+  "\tbeq\t%@,%.,1b\n"                          \
+  "\tnop\n"                                    \
+  "\tsync%-%]%>%)"
+
+#define MIPS_SYNC_OLD_OP_12_NOT_NOP ""
+#define MIPS_SYNC_OLD_OP_12_NOT_NOP_REG "%0"
+#define MIPS_SYNC_OLD_OP_12_NOT_NOT "\tnor\t%5,%0,%.\n"
+#define MIPS_SYNC_OLD_OP_12_NOT_NOT_REG "%5"
+
+/* Return an asm string that atomically:
+
+     - Given that %2 contains a bit mask and %3 the inverted mask and
+       that %4 has already been ANDed with %2.
+
+     - Sets the selected bits of memory reference %1 to %1 INSN %4.
+
+     - Sets %0 to the new value of %1.
+
+    NOT_OP are the optional instructions to do a bit-wise not
+    operation in conjunction with an AND INSN to generate a sync_nand
+    operation.  */
+#define MIPS_SYNC_NEW_OP_12(INSN, NOT_OP)      \
+  "%(%<%[%|sync\n"                             \
+  "1:\tll\t%0,%1\n"                            \
+  "\tand\t%@,%0,%3\n"                          \
+  NOT_OP                                       \
+  "\t" INSN "\t%0,%0,%z4\n"                    \
+  "\tand\t%0,%0,%2\n"                          \
+  "\tor\t%@,%@,%0\n"                           \
+  "\tsc\t%@,%1\n"                              \
+  "\tbeq\t%@,%.,1b\n"                          \
+  "\tnop\n"                                    \
+  "\tsync%-%]%>%)"
+
+#define MIPS_SYNC_NEW_OP_12_NOT_NOP ""
+#define MIPS_SYNC_NEW_OP_12_NOT_NOT "\tnor\t%0,%0,%.\n"
+
 /* Return an asm string that atomically:
 
      - Sets memory reference %1 to %1 INSN %2.
@@ -3065,6 +3147,33 @@ while (0)
   "\tnop\n"                                    \
   "\tsync%-%]%>%)"
 
+/* Return an asm string that atomically:
+
+     - Given that %2 contains an inclusive mask, %3 and exclusive mask
+       and %4 has already been ANDed with the inclusive mask.
+
+     - Sets bits selected by the inclusive mask of memory reference %1
+       to %4.
+
+     - Sets register %0 to the old value of memory reference %1.
+
+    OPS are the instructions needed to OR %4 with %@.
+
+    Operand %2 is unused, but needed as to give the test_and_set_12
+    insn the five operands expected by the expander.  */
+#define MIPS_SYNC_EXCHANGE_12(OPS)              \
+  "%(%<%[%|\n"                                 \
+  "1:\tll\t%0,%1\n"                            \
+  "\tand\t%@,%0,%3\n"                          \
+  OPS                                          \
+  "\tsc\t%@,%1\n"                              \
+  "\tbeq\t%@,%.,1b\n"                          \
+  "\tnop\n"                                    \
+  "\tsync%-%]%>%)"
+
+#define MIPS_SYNC_EXCHANGE_12_ZERO_OP ""
+#define MIPS_SYNC_EXCHANGE_12_NONZERO_OP "\tor\t%@,%@,%4\n"
+
 #ifndef USED_FOR_TARGET
 extern const enum reg_class mips_regno_to_class[];
 extern bool mips_hard_regno_mode_ok[][FIRST_PSEUDO_REGISTER];
index a423529b3308c5491e0a97caa3d747ff81ec4349..2b789eef8b602ce068e4edec68249c8d932c3996 100644 (file)
@@ -1,6 +1,6 @@
 ;;  Mips.md         Machine Description for MIPS based processors
 ;;  Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
-;;  1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
+;;  1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
 ;;  Free Software Foundation, Inc.
 ;;  Contributed by   A. Lichnewsky, lich@inria.inria.fr
 ;;  Changes by       Michael Meissner, meissner@osf.org
    (UNSPEC_COMPARE_AND_SWAP_12 38)
    (UNSPEC_SYNC_OLD_OP         39)
    (UNSPEC_SYNC_NEW_OP         40)
-   (UNSPEC_SYNC_EXCHANGE       41)
-   (UNSPEC_MEMORY_BARRIER      42)
-   (UNSPEC_SET_GOT_VERSION     43)
-   (UNSPEC_UPDATE_GOT_VERSION  44)
+   (UNSPEC_SYNC_NEW_OP_12      41)
+   (UNSPEC_SYNC_OLD_OP_12      42)
+   (UNSPEC_SYNC_EXCHANGE       43)
+   (UNSPEC_SYNC_EXCHANGE_12    44)
+   (UNSPEC_MEMORY_BARRIER      45)
+   (UNSPEC_SET_GOT_VERSION     46)
+   (UNSPEC_UPDATE_GOT_VERSION  47)
    
    (UNSPEC_ADDRESS_FIRST       100)
 
                         (lshiftrt "lshr")
                         (ior "ior")
                         (xor "xor")
-                        (and "and")])
+                        (and "and")
+                        (plus "add")
+                        (minus "sub")])
 
 ;; <insn> expands to the name of the insn that implements a particular code.
 (define_code_attr insn [(ashift "sll")
                        (lshiftrt "srl")
                        (ior "or")
                        (xor "xor")
-                       (and "and")])
+                       (and "and")
+                       (plus "addu")
+                       (minus "subu")])
 
 ;; <fcond> is the c.cond.fmt condition associated with a particular code.
 (define_code_attr fcond [(unordered "un")
 ;; a particular code to operate in immediate values.
 (define_code_attr immediate_insn [(ior "ori") (xor "xori") (and "andi")])
 
+;; Atomic HI and QI operations
+(define_code_iterator atomic_hiqi_op [plus minus ior xor and])
 \f
 ;; .........................
 ;;
    (match_operand:SHORT 3 "general_operand")]
   "GENERATE_LL_SC"
 {
-  mips_expand_compare_and_swap_12 (operands[0], operands[1],
-                                  operands[2], operands[3]);
+  union mips_gen_fn_ptrs generator;
+  generator.fn_6 = gen_compare_and_swap_12;
+  mips_expand_atomic_qihi (generator,
+                          operands[0], operands[1], operands[2], operands[3]);
   DONE;
 })
 
-;; Helper insn for mips_expand_compare_and_swap_12.
+;; Helper insn for mips_expand_atomic_qihi.
 (define_insn "compare_and_swap_12"
   [(set (match_operand:SI 0 "register_operand" "=&d,&d")
        (match_operand:SI 1 "memory_operand" "+R,R"))
   "GENERATE_LL_SC"
 {
   if (which_alternative == 0)
-    return MIPS_COMPARE_AND_SWAP_12;
+    return MIPS_COMPARE_AND_SWAP_12 (MIPS_COMPARE_AND_SWAP_12_NONZERO_OP);
   else
-    return MIPS_COMPARE_AND_SWAP_12_0;
+    return MIPS_COMPARE_AND_SWAP_12 (MIPS_COMPARE_AND_SWAP_12_ZERO_OP);
 }
   [(set_attr "length" "40,36")])
 
   [(set (match_operand:GPR 0 "memory_operand" "+R,R")
        (unspec_volatile:GPR
           [(plus:GPR (match_dup 0)
-                             (match_operand:GPR 1 "arith_operand" "I,d"))]
-        UNSPEC_SYNC_OLD_OP))]
+                    (match_operand:GPR 1 "arith_operand" "I,d"))]
+         UNSPEC_SYNC_OLD_OP))]
   "GENERATE_LL_SC"
 {
   if (which_alternative == 0)
 }
   [(set_attr "length" "28")])
 
+(define_expand "sync_<optab><mode>"
+  [(set (match_operand:SHORT 0 "memory_operand")
+       (unspec_volatile:SHORT
+         [(atomic_hiqi_op:SHORT (match_dup 0)
+                                (match_operand:SHORT 1 "general_operand"))]
+         UNSPEC_SYNC_OLD_OP))]
+  "GENERATE_LL_SC"
+{
+  union mips_gen_fn_ptrs generator;
+  generator.fn_4 = gen_sync_<optab>_12;
+  mips_expand_atomic_qihi (generator,
+                          NULL, operands[0], operands[1], NULL);
+  DONE;
+})
+
+;; Helper insn for sync_<optab><mode>
+(define_insn "sync_<optab>_12"
+  [(set (match_operand:SI 0 "memory_operand" "+R")
+       (unspec_volatile:SI
+          [(match_operand:SI 1 "register_operand" "d")
+          (match_operand:SI 2 "register_operand" "d")
+          (atomic_hiqi_op:SI (match_dup 0)
+                             (match_operand:SI 3 "register_operand" "dJ"))]
+         UNSPEC_SYNC_OLD_OP_12))
+   (clobber (match_scratch:SI 4 "=&d"))]
+  "GENERATE_LL_SC"
+{
+    return MIPS_SYNC_OP_12 ("<insn>", MIPS_SYNC_OP_12_NOT_NOP);        
+}
+  [(set_attr "length" "40")])
+
+(define_expand "sync_old_<optab><mode>"
+  [(parallel [
+     (set (match_operand:SHORT 0 "register_operand")
+         (match_operand:SHORT 1 "memory_operand"))
+     (set (match_dup 1)
+         (unspec_volatile:SHORT [(atomic_hiqi_op:SHORT
+                                   (match_dup 1)
+                                   (match_operand:SHORT 2 "general_operand"))]
+           UNSPEC_SYNC_OLD_OP))])]
+  "GENERATE_LL_SC"
+{
+  union mips_gen_fn_ptrs generator;
+  generator.fn_5 = gen_sync_old_<optab>_12;
+  mips_expand_atomic_qihi (generator,
+                          operands[0], operands[1], operands[2], NULL);
+  DONE;
+})
+
+;; Helper insn for sync_old_<optab><mode>
+(define_insn "sync_old_<optab>_12"
+  [(set (match_operand:SI 0 "register_operand" "=&d")
+       (match_operand:SI 1 "memory_operand" "+R"))
+   (set (match_dup 1)
+       (unspec_volatile:SI
+          [(match_operand:SI 2 "register_operand" "d")
+          (match_operand:SI 3 "register_operand" "d")
+          (atomic_hiqi_op:SI (match_dup 0)
+                             (match_operand:SI 4 "register_operand" "dJ"))]
+         UNSPEC_SYNC_OLD_OP_12))
+   (clobber (match_scratch:SI 5 "=&d"))]
+  "GENERATE_LL_SC"
+{
+    return MIPS_SYNC_OLD_OP_12 ("<insn>", MIPS_SYNC_OLD_OP_12_NOT_NOP,
+                               MIPS_SYNC_OLD_OP_12_NOT_NOP_REG);       
+}
+  [(set_attr "length" "40")])
+
+(define_expand "sync_new_<optab><mode>"
+  [(parallel [
+     (set (match_operand:SHORT 0 "register_operand")
+         (unspec_volatile:SHORT [(atomic_hiqi_op:SHORT
+                                   (match_operand:SHORT 1 "memory_operand")
+                                   (match_operand:SHORT 2 "general_operand"))]
+           UNSPEC_SYNC_NEW_OP))
+     (set (match_dup 1)
+         (unspec_volatile:SHORT [(match_dup 1) (match_dup 2)]
+           UNSPEC_SYNC_NEW_OP))])]
+  "GENERATE_LL_SC"
+{
+  union mips_gen_fn_ptrs generator;
+  generator.fn_5 = gen_sync_new_<optab>_12;
+  mips_expand_atomic_qihi (generator,
+                          operands[0], operands[1], operands[2], NULL);
+  DONE;
+})
+
+;; Helper insn for sync_new_<optab><mode>
+(define_insn "sync_new_<optab>_12"
+  [(set (match_operand:SI 0 "register_operand" "=&d")
+       (unspec_volatile:SI
+          [(match_operand:SI 1 "memory_operand" "+R")
+          (match_operand:SI 2 "register_operand" "d")
+          (match_operand:SI 3 "register_operand" "d")
+          (atomic_hiqi_op:SI (match_dup 0)
+                             (match_operand:SI 4 "register_operand" "dJ"))]
+         UNSPEC_SYNC_NEW_OP_12))
+   (set (match_dup 1)
+       (unspec_volatile:SI
+         [(match_dup 1)
+          (match_dup 2)
+          (match_dup 3)
+          (match_dup 4)] UNSPEC_SYNC_NEW_OP_12))]
+  "GENERATE_LL_SC"
+{
+    return MIPS_SYNC_NEW_OP_12 ("<insn>", MIPS_SYNC_NEW_OP_12_NOT_NOP);
+}
+  [(set_attr "length" "40")])
+
+(define_expand "sync_nand<mode>"
+  [(set (match_operand:SHORT 0 "memory_operand")
+       (unspec_volatile:SHORT
+         [(match_dup 0)
+          (match_operand:SHORT 1 "general_operand")]
+         UNSPEC_SYNC_OLD_OP))]
+  "GENERATE_LL_SC"
+{
+  union mips_gen_fn_ptrs generator;
+  generator.fn_4 = gen_sync_nand_12;
+  mips_expand_atomic_qihi (generator,
+                          NULL, operands[0], operands[1], NULL);
+  DONE;
+})
+
+;; Helper insn for sync_nand<mode>
+(define_insn "sync_nand_12"
+  [(set (match_operand:SI 0 "memory_operand" "+R")
+       (unspec_volatile:SI
+          [(match_operand:SI 1 "register_operand" "d")
+          (match_operand:SI 2 "register_operand" "d")
+          (match_dup 0)
+          (match_operand:SI 3 "register_operand" "dJ")]
+         UNSPEC_SYNC_OLD_OP_12))
+   (clobber (match_scratch:SI 4 "=&d"))]
+  "GENERATE_LL_SC"
+{
+    return MIPS_SYNC_OP_12 ("and", MIPS_SYNC_OP_12_NOT_NOT);   
+}
+  [(set_attr "length" "44")])
+
+(define_expand "sync_old_nand<mode>"
+  [(parallel [
+     (set (match_operand:SHORT 0 "register_operand")
+         (match_operand:SHORT 1 "memory_operand"))
+     (set (match_dup 1)
+         (unspec_volatile:SHORT [(match_dup 1)
+                                 (match_operand:SHORT 2 "general_operand")]
+           UNSPEC_SYNC_OLD_OP))])]
+  "GENERATE_LL_SC"
+{
+  union mips_gen_fn_ptrs generator;
+  generator.fn_5 = gen_sync_old_nand_12;
+  mips_expand_atomic_qihi (generator,
+                          operands[0], operands[1], operands[2], NULL);
+  DONE;
+})
+
+;; Helper insn for sync_old_nand<mode>
+(define_insn "sync_old_nand_12"
+  [(set (match_operand:SI 0 "register_operand" "=&d")
+       (match_operand:SI 1 "memory_operand" "+R"))
+   (set (match_dup 1)
+       (unspec_volatile:SI
+          [(match_operand:SI 2 "register_operand" "d")
+          (match_operand:SI 3 "register_operand" "d")
+          (match_operand:SI 4 "register_operand" "dJ")]
+         UNSPEC_SYNC_OLD_OP_12))
+   (clobber (match_scratch:SI 5 "=&d"))]
+  "GENERATE_LL_SC"
+{
+    return MIPS_SYNC_OLD_OP_12 ("and", MIPS_SYNC_OLD_OP_12_NOT_NOT,
+                               MIPS_SYNC_OLD_OP_12_NOT_NOT_REG);       
+}
+  [(set_attr "length" "44")])
+
+(define_expand "sync_new_nand<mode>"
+  [(parallel [
+     (set (match_operand:SHORT 0 "register_operand")
+         (unspec_volatile:SHORT [(match_operand:SHORT 1 "memory_operand")
+                                 (match_operand:SHORT 2 "general_operand")]
+           UNSPEC_SYNC_NEW_OP))
+     (set (match_dup 1)
+         (unspec_volatile:SHORT [(match_dup 1) (match_dup 2)]
+           UNSPEC_SYNC_NEW_OP))])]
+  "GENERATE_LL_SC"
+{
+  union mips_gen_fn_ptrs generator;
+  generator.fn_5 = gen_sync_new_nand_12;
+  mips_expand_atomic_qihi (generator,
+                          operands[0], operands[1], operands[2], NULL);
+  DONE;
+})
+
+;; Helper insn for sync_new_nand<mode>
+(define_insn "sync_new_nand_12"
+  [(set (match_operand:SI 0 "register_operand" "=&d")
+       (unspec_volatile:SI
+          [(match_operand:SI 1 "memory_operand" "+R")
+          (match_operand:SI 2 "register_operand" "d")
+          (match_operand:SI 3 "register_operand" "d")
+          (match_operand:SI 4 "register_operand" "dJ")]
+         UNSPEC_SYNC_NEW_OP_12))
+   (set (match_dup 1)
+       (unspec_volatile:SI
+         [(match_dup 1)
+          (match_dup 2)
+          (match_dup 3)
+          (match_dup 4)] UNSPEC_SYNC_NEW_OP_12))]
+  "GENERATE_LL_SC"
+{
+    return MIPS_SYNC_NEW_OP_12 ("and", MIPS_SYNC_NEW_OP_12_NOT_NOT);
+}
+  [(set_attr "length" "40")])
+
 (define_insn "sync_sub<mode>"
   [(set (match_operand:GPR 0 "memory_operand" "+R")
        (unspec_volatile:GPR
   [(set_attr "length" "28")])
 
 (define_insn "sync_old_add<mode>"
-  [(set (match_operand:GPR 0 "register_operand" "=d,&d")
+  [(set (match_operand:GPR 0 "register_operand" "=&d,&d")
        (match_operand:GPR 1 "memory_operand" "+R,R"))
    (set (match_dup 1)
        (unspec_volatile:GPR
   [(set_attr "length" "28")])
 
 (define_insn "sync_new_add<mode>"
-  [(set (match_operand:GPR 0 "register_operand" "=d,&d")
+  [(set (match_operand:GPR 0 "register_operand" "=&d,&d")
         (plus:GPR (match_operand:GPR 1 "memory_operand" "+R,R")
                  (match_operand:GPR 2 "arith_operand" "I,d")))
    (set (match_dup 1)
   [(set_attr "length" "28")])
 
 (define_insn "sync_old_<optab><mode>"
-  [(set (match_operand:GPR 0 "register_operand" "=d,&d")
+  [(set (match_operand:GPR 0 "register_operand" "=&d,&d")
        (match_operand:GPR 1 "memory_operand" "+R,R"))
    (set (match_dup 1)
        (unspec_volatile:GPR
   [(set_attr "length" "28")])
 
 (define_insn "sync_new_<optab><mode>"
-  [(set (match_operand:GPR 0 "register_operand" "=d,&d")
+  [(set (match_operand:GPR 0 "register_operand" "=&d,&d")
        (match_operand:GPR 1 "memory_operand" "+R,R"))
    (set (match_dup 1)
        (unspec_volatile:GPR
   [(set_attr "length" "32")])
 
 (define_insn "sync_old_nand<mode>"
-  [(set (match_operand:GPR 0 "register_operand" "=d,&d")
+  [(set (match_operand:GPR 0 "register_operand" "=&d,&d")
        (match_operand:GPR 1 "memory_operand" "+R,R"))
    (set (match_dup 1)
         (unspec_volatile:GPR [(match_operand:GPR 2 "uns_arith_operand" "K,d")]
   [(set_attr "length" "32")])
 
 (define_insn "sync_new_nand<mode>"
-  [(set (match_operand:GPR 0 "register_operand" "=d,&d")
+  [(set (match_operand:GPR 0 "register_operand" "=&d,&d")
        (match_operand:GPR 1 "memory_operand" "+R,R"))
    (set (match_dup 1)
        (unspec_volatile:GPR [(match_operand:GPR 2 "uns_arith_operand" "K,d")]
   [(set_attr "length" "32")])
 
 (define_insn "sync_lock_test_and_set<mode>"
-  [(set (match_operand:GPR 0 "register_operand" "=d,&d")
+  [(set (match_operand:GPR 0 "register_operand" "=&d,&d")
        (match_operand:GPR 1 "memory_operand" "+R,R"))
    (set (match_dup 1)
        (unspec_volatile:GPR [(match_operand:GPR 2 "arith_operand" "I,d")]
     return MIPS_SYNC_EXCHANGE ("<d>", "move");
 }
   [(set_attr "length" "24")])
+
+(define_expand "sync_lock_test_and_set<mode>"
+  [(match_operand:SHORT 0 "register_operand")
+   (match_operand:SHORT 1 "memory_operand")
+   (match_operand:SHORT 2 "general_operand")]
+  "GENERATE_LL_SC"
+{
+  union mips_gen_fn_ptrs generator;
+  generator.fn_5 = gen_test_and_set_12;
+  mips_expand_atomic_qihi (generator,
+                          operands[0], operands[1], operands[2], NULL);
+  DONE;
+})
+
+(define_insn "test_and_set_12"
+  [(set (match_operand:SI 0 "register_operand" "=&d,&d")
+       (match_operand:SI 1 "memory_operand" "+R,R"))
+   (set (match_dup 1)
+       (unspec_volatile:SI [(match_operand:SI 2 "register_operand" "d,d")
+                            (match_operand:SI 3 "register_operand" "d,d")
+                            (match_operand:SI 4 "arith_operand" "d,J")]
+         UNSPEC_SYNC_EXCHANGE_12))]
+  "GENERATE_LL_SC"
+{
+  if (which_alternative == 0)
+    return MIPS_SYNC_EXCHANGE_12 (MIPS_SYNC_EXCHANGE_12_NONZERO_OP);
+  else
+    return MIPS_SYNC_EXCHANGE_12 (MIPS_SYNC_EXCHANGE_12_ZERO_OP);
+}
+  [(set_attr "length" "28,24")])
 \f
 ;; Block moves, see mips.c for more details.
 ;; Argument 0 is the destination