diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 64285702a357c4ad972e0caaea94ed924fc2ec15..7f039b16c289d067870b7e84c2d4be91f829f8dd 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,9 @@
+2010-12-08  Richard Earnshaw  <rearnsha@arm.com>
+
+	PR target/46631
+	* arm.c (thumb2_reorg): Also try to reduce <commutative_op> Rd, Rn, Rd
+	into a 16-bit instruction.
+
 2010-12-08  Michael Meissner  <meissner@linux.vnet.ibm.com>
 
 	PR middle-end/42694
diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
index 88c43e3c3a9cc59992d4125556a00711d38ae91f..9d2c6dd1273b1ef8e6ceb691702e68910127fb58 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -12183,6 +12183,7 @@ thumb2_reorg (void)
   FOR_EACH_BB (bb)
     {
       rtx insn;
+
       COPY_REG_SET (&live, DF_LR_OUT (bb));
       df_simulate_initialize_backwards (bb, &live);
       FOR_BB_INSNS_REVERSE (bb, insn)
@@ -12200,21 +12201,43 @@ thumb2_reorg (void)
 		  rtx dst = XEXP (pat, 0);
 		  rtx src = XEXP (pat, 1);
 		  rtx op0 = XEXP (src, 0);
+		  rtx op1 = (GET_RTX_CLASS (GET_CODE (src)) == RTX_COMM_ARITH
+			     ? XEXP (src, 1) : NULL);
+
 		  if (rtx_equal_p (dst, op0)
 		      || GET_CODE (src) == PLUS || GET_CODE (src) == MINUS)
 		    {
 		      rtx ccreg = gen_rtx_REG (CCmode, CC_REGNUM);
 		      rtx clobber = gen_rtx_CLOBBER (VOIDmode, ccreg);
 		      rtvec vec = gen_rtvec (2, pat, clobber);
+
+		      PATTERN (insn) = gen_rtx_PARALLEL (VOIDmode, vec);
+		      INSN_CODE (insn) = -1;
+		    }
+		  /* We can also handle a commutative operation where the
+		     second operand matches the destination.  */
+		  else if (op1 && rtx_equal_p (dst, op1))
+		    {
+		      rtx ccreg = gen_rtx_REG (CCmode, CC_REGNUM);
+		      rtx clobber = gen_rtx_CLOBBER (VOIDmode, ccreg);
+		      rtvec vec;
+
+		      src = copy_rtx (src);
+		      XEXP (src, 0) = op1;
+		      XEXP (src, 1) = op0;
+		      pat = gen_rtx_SET (VOIDmode, dst, src);
+		      vec = gen_rtvec (2, pat, clobber);
 		      PATTERN (insn) = gen_rtx_PARALLEL (VOIDmode, vec);
 		      INSN_CODE (insn) = -1;
 		    }
 		}
 	    }
+
 	  if (NONDEBUG_INSN_P (insn))
 	    df_simulate_one_insn_backwards (bb, insn, &live);
 	}
     }
+
   CLEAR_REG_SET (&live);
 }
 
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 7a7f3be27ccef0d5ffba0fe5b3e1fdaa76028f57..313af808f8436de0b807b6bbcf13965fd514281c 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,8 @@
+2010-12-08  Wei Guozhi  <carrot@google.com>
+
+	PR target/46631
+	* gcc.target/arm/pr46631: New testcase.
+
 2010-12-08  Michael Meissner  <meissner@linux.vnet.ibm.com>
 
 	PR middle-end/42694
diff --git a/gcc/testsuite/gcc.target/arm/pr46631.c b/gcc/testsuite/gcc.target/arm/pr46631.c
new file mode 100644
index 0000000000000000000000000000000000000000..6f6dc4e85de03c7e418173f359d89c0a34f7dafa
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/pr46631.c
@@ -0,0 +1,16 @@
+/* { dg-options "-mthumb -Os" } */
+/* { dg-require-effective-target arm_thumb2_ok } */
+/* { dg-final { scan-assembler "ands" } } */
+
+struct S {
+      int bi_buf;
+      int bi_valid;
+};
+
+int tz (struct S* p, int bits, int value)
+{
+     if (p == 0) return 1;
+      p->bi_valid = bits;
+      p->bi_buf = value & ((1 << bits) - 1);
+      return 0;
+}