=== modified file 'gcc/config/arm/arm.c'
--- gcc/config/arm/arm.c	2010-11-11 11:50:33 +0000
+++ gcc/config/arm/arm.c	2010-11-29 12:59:50 +0000
@@ -23084,10 +23084,46 @@
       break;
     }
 
-  arm_output_strex (emit, mode, "", t2, t1, memory);
-  operands[0] = t2;
-  arm_output_asm_insn (emit, 0, operands, "teq\t%%0, #0");
-  arm_output_asm_insn (emit, 0, operands, "bne\t%sLSYT%%=", LOCAL_LABEL_PREFIX);
+  if (t2)
+    {
+       arm_output_strex (emit, mode, "", t2, t1, memory);
+       operands[0] = t2;
+       arm_output_asm_insn (emit, 0, operands, "teq\t%%0, #0");
+       arm_output_asm_insn (emit, 0, operands, "bne\t%sLSYT%%=",
+			    LOCAL_LABEL_PREFIX);
+    }
+  else
+    {
+      /* Use old_value for the return value because for some operations
+	 the old_value can easily be restored.  This saves one register.  */
+      arm_output_strex (emit, mode, "", old_value, t1, memory);
+      operands[0] = old_value;
+      arm_output_asm_insn (emit, 0, operands, "teq\t%%0, #0");
+      arm_output_asm_insn (emit, 0, operands, "bne\t%sLSYT%%=",
+			   LOCAL_LABEL_PREFIX);
+
+      switch (sync_op)
+	{
+	case SYNC_OP_ADD:
+	  arm_output_op3 (emit, "sub", old_value, t1, new_value);
+	  break;
+
+	case SYNC_OP_SUB:
+	  arm_output_op3 (emit, "add", old_value, t1, new_value);
+	  break;
+
+	case SYNC_OP_XOR:
+	  arm_output_op3 (emit, "eor", old_value, t1, new_value);
+	  break;
+
+	case SYNC_OP_NONE:
+	  arm_output_op2 (emit, "mov", old_value, required_value);
+	  break;
+
+	default:
+	  gcc_unreachable ();
+	}
+    }
 
   arm_process_output_memory_barrier (emit, NULL);
   arm_output_asm_insn (emit, 1, operands, "%sLSYB%%=:", LOCAL_LABEL_PREFIX);

=== modified file 'gcc/config/arm/sync.md'
--- gcc/config/arm/sync.md	2010-09-13 15:39:11 +0000
+++ gcc/config/arm/sync.md	2010-11-29 13:54:17 +0000
@@ -103,6 +103,18 @@
 			      (plus "add")
 			      (minus "sub")])
 
+(define_code_attr sync_clobber [(ior "=&r")
+				(and "=&r")
+				(xor "X")
+				(plus "X")
+				(minus "X")])
+
+(define_code_attr sync_t2_reqd [(ior "4")
+				(and "4")
+				(xor "*")
+				(plus "*")
+				(minus "*")])
+
 (define_expand "sync_<sync_optab>si"
   [(match_operand:SI 0 "memory_operand")
    (match_operand:SI 1 "s_register_operand")
@@ -286,7 +298,6 @@
 	  VUNSPEC_SYNC_COMPARE_AND_SWAP))
    (set (match_dup 1) (unspec_volatile:SI [(match_dup 2)]
                                           VUNSPEC_SYNC_COMPARE_AND_SWAP))
-   (clobber:SI (match_scratch:SI 4 "=&r"))
    (set (reg:CC CC_REGNUM) (unspec_volatile:CC [(match_dup 1)]
                                                 VUNSPEC_SYNC_COMPARE_AND_SWAP))
    ]
@@ -299,7 +310,6 @@
    (set_attr "sync_required_value"  "2")
    (set_attr "sync_new_value"       "3")
    (set_attr "sync_t1"              "0")
-   (set_attr "sync_t2"              "4")
    (set_attr "conds" "clob")
    (set_attr "predicable" "no")])
 
@@ -313,7 +323,6 @@
 	    VUNSPEC_SYNC_COMPARE_AND_SWAP)))
    (set (match_dup 1) (unspec_volatile:NARROW [(match_dup 2)]
                                           VUNSPEC_SYNC_COMPARE_AND_SWAP))
-   (clobber:SI (match_scratch:SI 4 "=&r"))
    (set (reg:CC CC_REGNUM) (unspec_volatile:CC [(match_dup 1)]
                                                 VUNSPEC_SYNC_COMPARE_AND_SWAP))
    ]
@@ -326,7 +335,6 @@
    (set_attr "sync_required_value"  "2")
    (set_attr "sync_new_value"       "3")
    (set_attr "sync_t1"              "0")
-   (set_attr "sync_t2"              "4")
    (set_attr "conds" "clob")
    (set_attr "predicable" "no")])
 
@@ -487,7 +495,7 @@
 	                    VUNSPEC_SYNC_OLD_OP))
    (clobber (reg:CC CC_REGNUM))
    (clobber (match_scratch:SI 3 "=&r"))
-   (clobber (match_scratch:SI 4 "=&r"))]
+   (clobber (match_scratch:SI 4 "<sync_clobber>"))]
   "TARGET_HAVE_LDREX && TARGET_HAVE_MEMORY_BARRIER"
   {
     return arm_output_sync_insn (insn, operands);
@@ -496,7 +504,7 @@
    (set_attr "sync_memory"          "1")
    (set_attr "sync_new_value"       "2")
    (set_attr "sync_t1"              "3")
-   (set_attr "sync_t2"              "4")
+   (set_attr "sync_t2"              "<sync_t2_reqd>")
    (set_attr "sync_op"              "<sync_optab>")
    (set_attr "conds" "clob")
    (set_attr "predicable" "no")])
@@ -540,7 +548,7 @@
 	                    VUNSPEC_SYNC_OLD_OP))
    (clobber (reg:CC CC_REGNUM))
    (clobber (match_scratch:SI 3 "=&r"))
-   (clobber (match_scratch:SI 4 "=&r"))]
+   (clobber (match_scratch:SI 4 "<sync_clobber>"))]
   "TARGET_HAVE_LDREXBHD && TARGET_HAVE_MEMORY_BARRIER"
   {
     return arm_output_sync_insn (insn, operands);
@@ -549,7 +557,7 @@
    (set_attr "sync_memory"          "1")
    (set_attr "sync_new_value"       "2")
    (set_attr "sync_t1"              "3")
-   (set_attr "sync_t2"              "4")
+   (set_attr "sync_t2"              "<sync_t2_reqd>")
    (set_attr "sync_op"              "<sync_optab>")
    (set_attr "conds" 		    "clob")
    (set_attr "predicable" "no")])

