summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/ChangeLog20
-rw-r--r--gcc/config/mips/mips.h144
-rw-r--r--gcc/config/mips/mips.md247
-rw-r--r--gcc/testsuite/ChangeLog7
-rw-r--r--gcc/testsuite/gcc.target/mips/atomic-memory-1.c41
-rw-r--r--gcc/testsuite/gcc.target/mips/atomic-memory-2.c10
-rw-r--r--gcc/testsuite/gcc.target/mips/gcc-have-sync-compare-and-swap-1.c22
-rw-r--r--gcc/testsuite/gcc.target/mips/gcc-have-sync-compare-and-swap-2.c22
8 files changed, 508 insertions, 5 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index df1be60ae9d..6b10f52b9f8 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,23 @@
+2007-09-02 David Daney <ddaney@avtrex.com>
+
+ * config/mips/mips.md (UNSPEC_COMPARE_AND_SWAP, UNSPEC_SYNC_OLD_OP,
+ UNSPEC_SYNC_NEW_OP, UNSPEC_SYNC_EXCHANGE): New define_constants.
+ (optab, insn): Add more attributes.
+ (fetchop_bit): New code macro.
+ (immediate_insn): New code macro attribute.
+ (sync): Change condition to ISA_HAS_SYNC.
+ (rdhwr): Change predicate for operand 0 to register_operand.
+ (memory_barrier): New expand.
+ (sync_compare_and_swap<mode>, sync_add<mode>, sync_sub<mode>,
+ sync_old_add<mode>, sync_old_sub<mode>, sync_new_add<mode>,
+ sync_new_sub<mode>, sync_<optab><mode>, sync_old_<optab><mode>,
+ sync_new_<optab><mode>, sync_nand<mode>, sync_old_nand<mode>,
+ sync_new_nand<mode>, sync_lock_test_and_set<mode>): New insns.
+ * config/mips/mips.h (ISA_HAS_SYNC, ISA_HAS_LL_SC): New ISA predicates.
+ (MIPS_COMPARE_AND_SWAP, MIPS_SYNC_OP, MIPS_SYNC_OLD_OP,
+ MIPS_SYNC_NEW_OP, MIPS_SYNC_NAND, MIPS_SYNC_OLD_NAND,
+ MIPS_SYNC_NEW_NAND, MIPS_SYNC_EXCHANGE): New Macros.
+
2007-09-03 Jesper Nilsson <jesper.nilsson@axis.com>
Hans-Peter Nilsson <hp@axis.com>
diff --git a/gcc/config/mips/mips.h b/gcc/config/mips/mips.h
index c3797e530dc..a44460b0853 100644
--- a/gcc/config/mips/mips.h
+++ b/gcc/config/mips/mips.h
@@ -881,6 +881,13 @@ extern enum mips_code_readable_setting mips_code_readable;
/* ISA includes synci, jr.hb and jalr.hb. */
#define ISA_HAS_SYNCI (ISA_MIPS32R2 && !TARGET_MIPS16)
+/* ISA includes sync. */
+#define ISA_HAS_SYNC ((mips_isa >= 2 || TARGET_MIPS3900) && !TARGET_MIPS16)
+
+/* ISA includes ll and sc. Note that this implies ISA_HAS_SYNC
+ because the expanders use both ISA_HAS_SYNC and ISA_HAS_LL_SC
+ instructions. */
+#define ISA_HAS_LL_SC (mips_isa >= 2 && !TARGET_MIPS16)
/* Add -G xx support. */
@@ -2871,3 +2878,140 @@ while (0)
#ifndef HAVE_AS_TLS
#define HAVE_AS_TLS 0
#endif
+
+/* Return an asm string that atomically:
+
+ - Compares memory reference %1 to register %2 and, if they are
+ equal, changes %1 to %3.
+
+ - Sets register %0 to the old value of memory reference %1.
+
+ SUFFIX is the suffix that should be added to "ll" and "sc" instructions
+ and OP is the instruction that should be used to load %3 into a
+ register. */
+#define MIPS_COMPARE_AND_SWAP(SUFFIX, OP) \
+ "%(%<%[sync\n" \
+ "1:\tll" SUFFIX "\t%0,%1\n" \
+ "\tbne\t%0,%2,2f\n" \
+ "\t" OP "\t%@,%3\n" \
+ "\tsc" SUFFIX "\t%@,%1\n" \
+ "\tbeq\t%@,%.,1b\n" \
+ "\tnop\n" \
+ "2:%]%>%)"
+
+/* Return an asm string that atomically:
+
+ - Sets memory reference %0 to %0 INSN %1.
+
+ SUFFIX is the suffix that should be added to "ll" and "sc"
+ instructions. */
+#define MIPS_SYNC_OP(SUFFIX, INSN) \
+ "%(%<%[sync\n" \
+ "1:\tll" SUFFIX "\t%@,%0\n" \
+ "\t" INSN "\t%@,%@,%1\n" \
+ "\tsc" SUFFIX "\t%@,%0\n" \
+ "\tbeq\t%@,%.,1b\n" \
+ "\tnop%]%>%)"
+
+/* Return an asm string that atomically:
+
+ - Sets memory reference %1 to %1 INSN %2.
+
+ - Sets register %0 to the old value of memory reference %1.
+
+ SUFFIX is the suffix that should be added to "ll" and "sc"
+ instructions. */
+#define MIPS_SYNC_OLD_OP(SUFFIX, INSN) \
+ "%(%<%[sync\n" \
+ "1:\tll" SUFFIX "\t%0,%1\n" \
+ "\t" INSN "\t%@,%0,%2\n" \
+ "\tsc" SUFFIX "\t%@,%1\n" \
+ "\tbeq\t%@,%.,1b\n" \
+ "\tnop%]%>%)"
+
+/* Return an asm string that atomically:
+
+ - Sets memory reference %1 to %1 INSN %2.
+
+ - Sets register %0 to the new value of memory reference %1.
+
+ SUFFIX is the suffix that should be added to "ll" and "sc"
+ instructions. */
+#define MIPS_SYNC_NEW_OP(SUFFIX, INSN) \
+ "%(%<%[sync\n" \
+ "1:\tll" SUFFIX "\t%0,%1\n" \
+ "\t" INSN "\t%@,%0,%2\n" \
+ "\tsc" SUFFIX "\t%@,%1\n" \
+ "\tbeq\t%@,%.,1b\n" \
+ "\t" INSN "\t%0,%0,%2%]%>%)"
+
+/* Return an asm string that atomically:
+
+ - Sets memory reference %0 to ~%0 AND %1.
+
+ SUFFIX is the suffix that should be added to "ll" and "sc"
+ instructions. INSN is the and instruction needed to and a register
+ with %2. */
+#define MIPS_SYNC_NAND(SUFFIX, INSN) \
+ "%(%<%[sync\n" \
+ "1:\tll" SUFFIX "\t%@,%0\n" \
+ "\tnor\t%@,%@,%.\n" \
+ "\t" INSN "\t%@,%@,%1\n" \
+ "\tsc" SUFFIX "\t%@,%0\n" \
+ "\tbeq\t%@,%.,1b\n" \
+ "\tnop%]%>%)"
+
+/* Return an asm string that atomically:
+
+ - Sets memory reference %1 to ~%1 AND %2.
+
+ - Sets register %0 to the old value of memory reference %1.
+
+ SUFFIX is the suffix that should be added to "ll" and "sc"
+ instructions. INSN is the and instruction needed to and a register
+ with %2. */
+#define MIPS_SYNC_OLD_NAND(SUFFIX, INSN) \
+ "%(%<%[sync\n" \
+ "1:\tll" SUFFIX "\t%0,%1\n" \
+ "\tnor\t%@,%0,%.\n" \
+ "\t" INSN "\t%@,%@,%2\n" \
+ "\tsc" SUFFIX "\t%@,%1\n" \
+ "\tbeq\t%@,%.,1b\n" \
+ "\tnop%]%>%)"
+
+/* Return an asm string that atomically:
+
+ - Sets memory reference %1 to ~%1 AND %2.
+
+ - Sets register %0 to the new value of memory reference %1.
+
+ SUFFIX is the suffix that should be added to "ll" and "sc"
+ instructions. INSN is the and instruction needed to and a register
+ with %2. */
+#define MIPS_SYNC_NEW_NAND(SUFFIX, INSN) \
+ "%(%<%[sync\n" \
+ "1:\tll" SUFFIX "\t%0,%1\n" \
+ "\tnor\t%0,%0,%.\n" \
+ "\t" INSN "\t%@,%0,%2\n" \
+ "\tsc" SUFFIX "\t%@,%1\n" \
+ "\tbeq\t%@,%.,1b\n" \
+ "\t" INSN "\t%0,%0,%2%]%>%)"
+
+/* Return an asm string that atomically:
+
+ - Sets memory reference %1 to %2.
+
+ - Sets register %0 to the old value of memory reference %1.
+
+ SUFFIX is the suffix that should be added to "ll" and "sc"
+ instructions. OP is the and instruction that should be used to
+ load %2 into a register. */
+#define MIPS_SYNC_EXCHANGE(SUFFIX, OP) \
+ "%(%<%[\n" \
+ "1:\tll" SUFFIX "\t%0,%1\n" \
+ "\t" OP "\t%@,%2\n" \
+ "\tsc" SUFFIX "\t%@,%1\n" \
+ "\tbeq\t%@,%.,1b\n" \
+ "\tnop\n" \
+ "\tsync%]%>%)"
+
diff --git a/gcc/config/mips/mips.md b/gcc/config/mips/mips.md
index 44c3c9c135f..368da18077c 100644
--- a/gcc/config/mips/mips.md
+++ b/gcc/config/mips/mips.md
@@ -53,7 +53,11 @@
(UNSPEC_RDHWR 34)
(UNSPEC_SYNCI 35)
(UNSPEC_SYNC 36)
-
+ (UNSPEC_COMPARE_AND_SWAP 37)
+ (UNSPEC_SYNC_OLD_OP 38)
+ (UNSPEC_SYNC_NEW_OP 39)
+ (UNSPEC_SYNC_EXCHANGE 40)
+
(UNSPEC_ADDRESS_FIRST 100)
(FAKE_CALL_REGNO 79)
@@ -576,12 +580,18 @@
;; <optab> expands to the name of the optab for a particular code.
(define_code_attr optab [(ashift "ashl")
(ashiftrt "ashr")
- (lshiftrt "lshr")])
+ (lshiftrt "lshr")
+ (ior "ior")
+ (xor "xor")
+ (and "and")])
;; <insn> expands to the name of the insn that implements a particular code.
(define_code_attr insn [(ashift "sll")
(ashiftrt "sra")
- (lshiftrt "srl")])
+ (lshiftrt "srl")
+ (ior "or")
+ (xor "xor")
+ (and "and")])
;; <fcond> is the c.cond.fmt condition associated with a particular code.
(define_code_attr fcond [(unordered "un")
@@ -597,6 +607,14 @@
(gt "lt")
(unge "ule")
(ungt "ult")])
+
+;; Atomic fetch bitwise operations.
+(define_code_macro fetchop_bit [ior xor and])
+
+;; <immediate_insn> expands to the name of the insn that implements
+;; a particular code to operate in immediate values.
+(define_code_attr immediate_insn [(ior "ori") (xor "xori") (and "andi")])
+
;; .........................
;;
@@ -4251,7 +4269,7 @@
(define_insn "sync"
[(unspec_volatile [(const_int 0)] UNSPEC_SYNC)]
- "ISA_HAS_SYNCI"
+ "ISA_HAS_SYNC"
"sync")
(define_insn "synci"
@@ -4261,7 +4279,7 @@
"synci\t0(%0)")
(define_insn "rdhwr"
- [(set (match_operand:SI 0 "general_operand" "=d")
+ [(set (match_operand:SI 0 "register_operand" "=d")
(unspec_volatile [(match_operand:SI 1 "const_int_operand" "n")]
UNSPEC_RDHWR))]
"ISA_HAS_SYNCI"
@@ -4283,6 +4301,225 @@
"\t.set\tpop";
}
[(set_attr "length" "20")])
+
+;; Atomic memory operations.
+
+(define_expand "memory_barrier"
+ [(unspec_volatile [(const_int 0)] UNSPEC_SYNC)]
+ "ISA_HAS_SYNC"
+ "")
+
+(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 (match_dup 1)
+ (unspec_volatile:GPR [(match_operand:GPR 2 "register_operand" "d,d")
+ (match_operand:GPR 3 "arith_operand" "I,d")]
+ UNSPEC_COMPARE_AND_SWAP))]
+ "ISA_HAS_LL_SC"
+{
+ if (which_alternative == 0)
+ return MIPS_COMPARE_AND_SWAP ("<d>", "li");
+ else
+ return MIPS_COMPARE_AND_SWAP ("<d>", "move");
+}
+ [(set_attr "length" "28")])
+
+(define_insn "sync_add<mode>"
+ [(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))]
+ "ISA_HAS_LL_SC"
+{
+ if (which_alternative == 0)
+ return MIPS_SYNC_OP ("<d>", "<d>addiu");
+ else
+ return MIPS_SYNC_OP ("<d>", "<d>addu");
+}
+ [(set_attr "length" "24")])
+
+(define_insn "sync_sub<mode>"
+ [(set (match_operand:GPR 0 "memory_operand" "+R")
+ (unspec_volatile:GPR
+ [(minus:GPR (match_dup 0)
+ (match_operand:GPR 1 "register_operand" "d"))]
+ UNSPEC_SYNC_OLD_OP))]
+ "ISA_HAS_LL_SC"
+{
+ return MIPS_SYNC_OP ("<d>", "<d>subu");
+}
+ [(set_attr "length" "24")])
+
+(define_insn "sync_old_add<mode>"
+ [(set (match_operand:GPR 0 "register_operand" "=&d,d")
+ (match_operand:GPR 1 "memory_operand" "+R,R"))
+ (set (match_dup 1)
+ (unspec_volatile:GPR
+ [(plus:GPR (match_dup 1)
+ (match_operand:GPR 2 "arith_operand" "I,d"))]
+ UNSPEC_SYNC_OLD_OP))]
+ "ISA_HAS_LL_SC"
+{
+ if (which_alternative == 0)
+ return MIPS_SYNC_OLD_OP ("<d>", "<d>addiu");
+ else
+ return MIPS_SYNC_OLD_OP ("<d>", "<d>addu");
+}
+ [(set_attr "length" "24")])
+
+(define_insn "sync_old_sub<mode>"
+ [(set (match_operand:GPR 0 "register_operand" "=&d")
+ (match_operand:GPR 1 "memory_operand" "+R"))
+ (set (match_dup 1)
+ (unspec_volatile:GPR
+ [(minus:GPR (match_dup 1)
+ (match_operand:GPR 2 "register_operand" "d"))]
+ UNSPEC_SYNC_OLD_OP))]
+ "ISA_HAS_LL_SC"
+{
+ return MIPS_SYNC_OLD_OP ("<d>", "<d>subu");
+}
+ [(set_attr "length" "24")])
+
+(define_insn "sync_new_add<mode>"
+ [(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)
+ (unspec_volatile:GPR
+ [(plus:GPR (match_dup 1) (match_dup 2))]
+ UNSPEC_SYNC_NEW_OP))]
+ "ISA_HAS_LL_SC"
+{
+ if (which_alternative == 0)
+ return MIPS_SYNC_NEW_OP ("<d>", "<d>addiu");
+ else
+ return MIPS_SYNC_NEW_OP ("<d>", "<d>addu");
+}
+ [(set_attr "length" "24")])
+
+(define_insn "sync_new_sub<mode>"
+ [(set (match_operand:GPR 0 "register_operand" "=&d")
+ (minus:GPR (match_operand:GPR 1 "memory_operand" "+R")
+ (match_operand:GPR 2 "register_operand" "d")))
+ (set (match_dup 1)
+ (unspec_volatile:GPR
+ [(minus:GPR (match_dup 1) (match_dup 2))]
+ UNSPEC_SYNC_NEW_OP))]
+ "ISA_HAS_LL_SC"
+{
+ return MIPS_SYNC_NEW_OP ("<d>", "<d>subu");
+}
+ [(set_attr "length" "24")])
+
+(define_insn "sync_<optab><mode>"
+ [(set (match_operand:GPR 0 "memory_operand" "+R,R")
+ (unspec_volatile:GPR
+ [(fetchop_bit:GPR (match_operand:GPR 1 "uns_arith_operand" "K,d")
+ (match_dup 0))]
+ UNSPEC_SYNC_OLD_OP))]
+ "ISA_HAS_LL_SC"
+{
+ if (which_alternative == 0)
+ return MIPS_SYNC_OP ("<d>", "<immediate_insn>");
+ else
+ return MIPS_SYNC_OP ("<d>", "<insn>");
+}
+ [(set_attr "length" "24")])
+
+(define_insn "sync_old_<optab><mode>"
+ [(set (match_operand:GPR 0 "register_operand" "=&d,d")
+ (match_operand:GPR 1 "memory_operand" "+R,R"))
+ (set (match_dup 1)
+ (unspec_volatile:GPR
+ [(fetchop_bit:GPR (match_operand:GPR 2 "uns_arith_operand" "K,d")
+ (match_dup 1))]
+ UNSPEC_SYNC_OLD_OP))]
+ "ISA_HAS_LL_SC"
+{
+ if (which_alternative == 0)
+ return MIPS_SYNC_OLD_OP ("<d>", "<immediate_insn>");
+ else
+ return MIPS_SYNC_OLD_OP ("<d>", "<insn>");
+}
+ [(set_attr "length" "24")])
+
+(define_insn "sync_new_<optab><mode>"
+ [(set (match_operand:GPR 0 "register_operand" "=&d,d")
+ (match_operand:GPR 1 "memory_operand" "+R,R"))
+ (set (match_dup 1)
+ (unspec_volatile:GPR
+ [(fetchop_bit:GPR (match_operand:GPR 2 "uns_arith_operand" "K,d")
+ (match_dup 1))]
+ UNSPEC_SYNC_NEW_OP))]
+ "ISA_HAS_LL_SC"
+{
+ if (which_alternative == 0)
+ return MIPS_SYNC_NEW_OP ("<d>", "<immediate_insn>");
+ else
+ return MIPS_SYNC_NEW_OP ("<d>", "<insn>");
+}
+ [(set_attr "length" "24")])
+
+(define_insn "sync_nand<mode>"
+ [(set (match_operand:GPR 0 "memory_operand" "+R,R")
+ (unspec_volatile:GPR [(match_operand:GPR 1 "uns_arith_operand" "K,d")]
+ UNSPEC_SYNC_OLD_OP))]
+ "ISA_HAS_LL_SC"
+{
+ if (which_alternative == 0)
+ return MIPS_SYNC_NAND ("<d>", "andi");
+ else
+ return MIPS_SYNC_NAND ("<d>", "and");
+}
+ [(set_attr "length" "28")])
+
+(define_insn "sync_old_nand<mode>"
+ [(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")]
+ UNSPEC_SYNC_OLD_OP))]
+ "ISA_HAS_LL_SC"
+{
+ if (which_alternative == 0)
+ return MIPS_SYNC_OLD_NAND ("<d>", "andi");
+ else
+ return MIPS_SYNC_OLD_NAND ("<d>", "and");
+}
+ [(set_attr "length" "28")])
+
+(define_insn "sync_new_nand<mode>"
+ [(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")]
+ UNSPEC_SYNC_NEW_OP))]
+ "ISA_HAS_LL_SC"
+{
+ if (which_alternative == 0)
+ return MIPS_SYNC_NEW_NAND ("<d>", "andi");
+ else
+ return MIPS_SYNC_NEW_NAND ("<d>", "and");
+}
+ [(set_attr "length" "28")])
+
+(define_insn "sync_lock_test_and_set<mode>"
+ [(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")]
+ UNSPEC_SYNC_EXCHANGE))]
+ "ISA_HAS_LL_SC"
+{
+ if (which_alternative == 0)
+ return MIPS_SYNC_EXCHANGE ("<d>", "li");
+ else
+ return MIPS_SYNC_EXCHANGE ("<d>", "move");
+}
+ [(set_attr "length" "24")])
;; Block moves, see mips.c for more details.
;; Argument 0 is the destination
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index eb93d33e5ac..620c7a7202e 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,10 @@
+2007-09-02 David Daney <ddaney@avtrex.com>
+
+ * gcc.target/mips/gcc-have-sync-compare-and-swap-1.c: New test.
+ * gcc.target/mips/gcc-have-sync-compare-and-swap-2.c: Ditto.
+ * gcc.target/mips/atomic-memory-1.c: Ditto.
+ * testsuite/gcc.target/mips/atomic-memory-2.c: Ditto.
+
2007-09-03 Jesper Nilsson <jesper.nilsson@axis.com>
* gcc.target/cris/builtin_clz_v0.c: New testcase.
diff --git a/gcc/testsuite/gcc.target/mips/atomic-memory-1.c b/gcc/testsuite/gcc.target/mips/atomic-memory-1.c
new file mode 100644
index 00000000000..1664daa3d71
--- /dev/null
+++ b/gcc/testsuite/gcc.target/mips/atomic-memory-1.c
@@ -0,0 +1,41 @@
+/* { dg-do run } */
+extern void abort (void);
+extern void exit (int);
+
+int main ()
+{
+#ifdef __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4
+ unsigned v = 0;
+ __sync_synchronize ();
+
+ if (!__sync_bool_compare_and_swap (&v, 0, 30000))
+ abort();
+ if (30000 != __sync_val_compare_and_swap (&v, 30000, 100001))
+ abort();
+ __sync_sub_and_fetch (&v, 0x8001);
+ __sync_sub_and_fetch (&v, 0x7fff);
+ if (v != 34465)
+ abort();
+ if (__sync_nand_and_fetch (&v, 0xff) != 94)
+ abort();
+ if (__sync_fetch_and_add (&v, 6) != 94)
+ abort();
+ if (v != 100)
+ abort();
+ if (__sync_or_and_fetch (&v, 0xf001) != 0xf065)
+ abort();
+ if (__sync_and_and_fetch (&v, 0x1000) != 0x1000)
+ abort();
+ if (__sync_xor_and_fetch (&v, 0xa51040) != 0xa50040)
+ abort();
+ __sync_and_and_fetch (&v, 7);
+ if (__sync_lock_test_and_set(&v, 1) != 0)
+ abort();
+ if (v != 1)
+ abort();
+ __sync_lock_release (&v);
+ if (v != 0)
+ abort();
+#endif
+ exit(0);
+}
diff --git a/gcc/testsuite/gcc.target/mips/atomic-memory-2.c b/gcc/testsuite/gcc.target/mips/atomic-memory-2.c
new file mode 100644
index 00000000000..b0492be84a0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/mips/atomic-memory-2.c
@@ -0,0 +1,10 @@
+/* { dg-do compile } */
+/* { dg-mips-options "-O2 -mips32 -mabi=32" } */
+/* { dg-final { scan-assembler "addiu" } } */
+/* { dg-final { scan-assembler-not "subu" } } */
+
+unsigned long
+f(unsigned long *p)
+{
+ return __sync_fetch_and_sub (p, 5);
+}
diff --git a/gcc/testsuite/gcc.target/mips/gcc-have-sync-compare-and-swap-1.c b/gcc/testsuite/gcc.target/mips/gcc-have-sync-compare-and-swap-1.c
new file mode 100644
index 00000000000..315aa464e6e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/mips/gcc-have-sync-compare-and-swap-1.c
@@ -0,0 +1,22 @@
+/* { dg-do preprocess } */
+/* { dg-options "-mips32 -mabi=32" } */
+
+#ifdef __GCC_HAVE_SYNC_COMPARE_AND_SWAP_1
+#error nonono
+#endif
+
+#ifdef __GCC_HAVE_SYNC_COMPARE_AND_SWAP_2
+#error nonono
+#endif
+
+#ifndef __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4
+#error nonono
+#endif
+
+#ifdef __GCC_HAVE_SYNC_COMPARE_AND_SWAP_8
+#error nonono
+#endif
+
+#ifdef __GCC_HAVE_SYNC_COMPARE_AND_SWAP_16
+#error nonono
+#endif
diff --git a/gcc/testsuite/gcc.target/mips/gcc-have-sync-compare-and-swap-2.c b/gcc/testsuite/gcc.target/mips/gcc-have-sync-compare-and-swap-2.c
new file mode 100644
index 00000000000..f07ebe7ebbd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/mips/gcc-have-sync-compare-and-swap-2.c
@@ -0,0 +1,22 @@
+/* { dg-do preprocess { target { mips64*-*-* } } } */
+/* { dg-options "-mips64 -mabi=64" } */
+
+#ifdef __GCC_HAVE_SYNC_COMPARE_AND_SWAP_1
+#error nonono
+#endif
+
+#ifdef __GCC_HAVE_SYNC_COMPARE_AND_SWAP_2
+#error nonono
+#endif
+
+#ifndef __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4
+#error nonono
+#endif
+
+#ifndef __GCC_HAVE_SYNC_COMPARE_AND_SWAP_8
+#error nonono
+#endif
+
+#ifdef __GCC_HAVE_SYNC_COMPARE_AND_SWAP_16
+#error nonono
+#endif