diff --git a/gcc/ifcvt.c b/gcc/ifcvt.c
index 4e3d8f3..f2e7ac6 100644
--- a/gcc/ifcvt.c
+++ b/gcc/ifcvt.c
@@ -2068,23 +2068,127 @@ noce_emit_bb (rtx last_insn, basic_block bb, bool simple)
   return true;
 }
 
-/* Try more complex cases involving conditional_move.  */
+/* Helper for noce_try_cmove_arith.  This gets called twice, once for the
+   then branch, once for the else branch X_BB gives the basic block for the
+   branch we are currently interested in.  X is the destination for this
+   branch.  If X is complex, we need to move it in to a register first, by
+   possibly copying from INSN_X such that we preserve clobbers etc from the
+   original instruction.  EMIT_X is the target register for this branch
+   result.  ORIG_OTHER_DEST gives the original destination from the
+   opposite branch.  OTHER_BB_EXISTS_P is true if there was an opposite
+   branch for us to consider.  */
+
+bool
+noce_arith_helper (rtx *x, rtx *emit_x, rtx_insn *insn_x,
+		   basic_block x_bb, rtx orig_other_dest,
+		   bool other_bb_exists_p)
+{
+  rtx set_tmp = NULL_RTX;
+
+  machine_mode x_mode = GET_MODE (*x);
+
+  /* Two cases to catch here.  Either X is not yet a general operand, in
+     which case we need to move it to an appropriate register.  Or, the other
+     block is empty, in which case ORIG_OTHER_DEST came from the test block.
+     The non-empty complex block that we will emit might clobber the register
+     used by ORIG_OTHER_DEST, so move it to a pseudo first.  */
+  if (! general_operand (*x, x_mode)
+      || !other_bb_exists_p)
+    {
+      rtx reg = gen_reg_rtx (x_mode);
+      if (insn_x)
+	{
+	  rtx_insn *copy_of_x = as_a <rtx_insn *> (copy_rtx (insn_x));
+	  rtx set = single_set (copy_of_x);
+	  SET_DEST (set) = reg;
+	  set_tmp = PATTERN (copy_of_x);
+	}
+      else
+	{
+	  set_tmp = gen_rtx_SET (reg, *x);
+	}
+      *x = reg;
+    }
+
+  /* Check that our new insn isn't going to clobber ORIG_OTHER_DEST.  */
+  bool modified_in_x = (set_tmp != NULL_RTX)
+			&& modified_in_p (orig_other_dest, set_tmp);
+
+  /* If we have a X_BB to check, go through it and make sure the insns we'd
+     duplicate don't write ORIG_OTHER_DEST.  */
+  if (x_bb)
+    {
+      rtx_insn *tmp_insn = NULL;
+      FOR_BB_INSNS (x_bb, tmp_insn)
+	/* Don't check inside the destination insn, we will have changed
+	   it to use a register that doesn't conflict.  */
+	if (!(insn_x && tmp_insn == insn_x)
+	    && modified_in_p (orig_other_dest, tmp_insn))
+	  {
+	    modified_in_x = true;
+	    break;
+	  }
+    }
+
+  /* Store the SET back in EMIT_X.  */
+  *emit_x = set_tmp;
+  return modified_in_x;
+}
+
+/* Try more complex cases involving conditional_move.
+
+   We have:
+
+      if (test)
+	x = a + b;
+      else
+	x = c - d;
+
+    Make it:
+
+      t1 = a + b;
+      t2 = c - d;
+      x = (test) ? t1 : t2;
+
+   Alternatively, we have:
+
+      if (test)
+	x = *y;
+      else
+	x = *z;
+
+   Make it:
+
+     p1 = (test) ? y : z;
+     x = *p1;
+*/
 
 static int
 noce_try_cmove_arith (struct noce_if_info *if_info)
 {
+  /* SET_SRC from the two branches.  */
   rtx a = if_info->a;
   rtx b = if_info->b;
+  /* SET_DEST of both branches.  */
   rtx x = if_info->x;
-  rtx orig_a, orig_b;
-  rtx_insn *insn_a, *insn_b;
+  /* Full insns from the two branches.  */
+  rtx_insn *insn_a = if_info->insn_a;
+  rtx_insn *insn_b = if_info->insn_b;
+  /* Whether the branches are single set.  */
   bool a_simple = if_info->then_simple;
   bool b_simple = if_info->else_simple;
+  /* Our two basic blocks.  */
   basic_block then_bb = if_info->then_bb;
   basic_block else_bb = if_info->else_bb;
+  /* Whether we're handling the transformation of a load.  */
+  bool is_mem = false;
+  /* Copies of A and B before we modified them.  */
+  rtx orig_a = a, orig_b = b;
+  /* A new target to be used by the conditional select.  */
   rtx target;
-  int is_mem = 0;
-  enum rtx_code code;
+  /* The RTX code for the condition in the test block.  */
+  enum rtx_code code = GET_CODE (if_info->cond);
+  /* Our generated sequence.  */
   rtx_insn *ifcvt_seq;
 
   /* A conditional move from two memory sources is equivalent to a
@@ -2094,33 +2198,19 @@ noce_try_cmove_arith (struct noce_if_info *if_info)
   if (cse_not_expected
       && MEM_P (a) && MEM_P (b)
       && MEM_ADDR_SPACE (a) == MEM_ADDR_SPACE (b))
-    {
-      machine_mode address_mode = get_address_mode (a);
-
-      a = XEXP (a, 0);
-      b = XEXP (b, 0);
-      x = gen_reg_rtx (address_mode);
-      is_mem = 1;
-    }
-
+    is_mem = true;
   /* ??? We could handle this if we knew that a load from A or B could
      not trap or fault.  This is also true if we've already loaded
      from the address along the path from ENTRY.  */
   else if (may_trap_or_fault_p (a) || may_trap_or_fault_p (b))
     return FALSE;
 
-  /* if (test) x = a + b; else x = c - d;
-     => y = a + b;
-        x = c - d;
-	if (test)
-	  x = y;
-  */
 
   code = GET_CODE (if_info->cond);
   insn_a = if_info->insn_a;
   insn_b = if_info->insn_b;
 
-  machine_mode x_mode = GET_MODE (x);
+  machine_mode x_mode = is_mem ? get_address_mode (a) : GET_MODE (x);
 
   if (!can_conditionally_move_p (x_mode))
     return FALSE;
@@ -2151,117 +2241,27 @@ noce_try_cmove_arith (struct noce_if_info *if_info)
 
   start_sequence ();
 
-  /* If one of the blocks is empty then the corresponding B or A value
-     came from the test block.  The non-empty complex block that we will
-     emit might clobber the register used by B or A, so move it to a pseudo
-     first.  */
-
-  rtx tmp_a = NULL_RTX;
-  rtx tmp_b = NULL_RTX;
-
-  if (b_simple || !else_bb)
-    tmp_b = gen_reg_rtx (x_mode);
-
-  if (a_simple || !then_bb)
-    tmp_a = gen_reg_rtx (x_mode);
-
   orig_a = a;
   orig_b = b;
 
-  rtx emit_a = NULL_RTX;
-  rtx emit_b = NULL_RTX;
-  rtx_insn *tmp_insn = NULL;
-  bool modified_in_a = false;
-  bool  modified_in_b = false;
-  /* If either operand is complex, load it into a register first.
-     The best way to do this is to copy the original insn.  In this
-     way we preserve any clobbers etc that the insn may have had.
-     This is of course not possible in the IS_MEM case.  */
-
-  if (! general_operand (a, GET_MODE (a)) || tmp_a)
-    {
-
-      if (is_mem)
-	{
-	  rtx reg = gen_reg_rtx (GET_MODE (a));
-	  emit_a = gen_rtx_SET (reg, a);
-	}
-      else
-	{
-	  if (insn_a)
-	    {
-	      a = tmp_a ? tmp_a : gen_reg_rtx (GET_MODE (a));
-
-	      rtx_insn *copy_of_a = as_a <rtx_insn *> (copy_rtx (insn_a));
-	      rtx set = single_set (copy_of_a);
-	      SET_DEST (set) = a;
-
-	      emit_a = PATTERN (copy_of_a);
-	    }
-	  else
-	    {
-	      rtx tmp_reg = tmp_a ? tmp_a : gen_reg_rtx (GET_MODE (a));
-	      emit_a = gen_rtx_SET (tmp_reg, a);
-	      a = tmp_reg;
-	    }
-	}
-    }
-
-  if (! general_operand (b, GET_MODE (b)) || tmp_b)
+  /* Get the addresses if this is a MEM.  */
+  if (is_mem)
     {
-      if (is_mem)
-	{
-          rtx reg = gen_reg_rtx (GET_MODE (b));
-	  emit_b = gen_rtx_SET (reg, b);
-	}
-      else
-	{
-	  if (insn_b)
-	    {
-	      b = tmp_b ? tmp_b : gen_reg_rtx (GET_MODE (b));
-	      rtx_insn *copy_of_b = as_a <rtx_insn *> (copy_rtx (insn_b));
-	      rtx set = single_set (copy_of_b);
-
-	      SET_DEST (set) = b;
-	      emit_b = PATTERN (copy_of_b);
-	    }
-	  else
-	    {
-	      rtx tmp_reg = tmp_b ? tmp_b : gen_reg_rtx (GET_MODE (b));
-	      emit_b = gen_rtx_SET (tmp_reg, b);
-	      b = tmp_reg;
-	  }
-	}
+      a = XEXP (a, 0);
+      b = XEXP (b, 0);
     }
 
-  modified_in_a = emit_a != NULL_RTX && modified_in_p (orig_b, emit_a);
-  if (tmp_b && then_bb)
-    {
-      FOR_BB_INSNS (then_bb, tmp_insn)
-	/* Don't check inside insn_a.  We will have changed it to emit_a
-	   with a destination that doesn't conflict.  */
-	if (!(insn_a && tmp_insn == insn_a)
-	    && modified_in_p (orig_b, tmp_insn))
-	  {
-	    modified_in_a = true;
-	    break;
-	  }
-
-    }
+  rtx emit_a = NULL_RTX;
+  rtx emit_b = NULL_RTX;
 
-  modified_in_b = emit_b != NULL_RTX && modified_in_p (orig_a, emit_b);
-  if (tmp_a && else_bb)
-    {
-      FOR_BB_INSNS (else_bb, tmp_insn)
-      /* Don't check inside insn_b.  We will have changed it to emit_b
-	 with a destination that doesn't conflict.  */
-      if (!(insn_b && tmp_insn == insn_b)
-	  && modified_in_p (orig_a, tmp_insn))
-	{
-	  modified_in_b = true;
-	  break;
-	}
-    }
+  /* Sort out temporary registers and figure out whether either branch
+     would clobber the other.  */
+  bool modified_in_a
+    = noce_arith_helper (&a, &emit_a, insn_a, then_bb,
+			 orig_b, (else_bb != NULL));
+  bool modified_in_b
+    = noce_arith_helper (&b, &emit_b, insn_b, else_bb,
+			 orig_a, (then_bb != NULL));
 
   /* If insn to set up A clobbers any registers B depends on, try to
      swap insn that sets up A with the one that sets up B.  If even
@@ -2285,6 +2285,12 @@ noce_try_cmove_arith (struct noce_if_info *if_info)
   else
     goto end_seq_and_fail;
 
+  /* Emit the conditional move.  If our operands were MEMs, we
+     need to generate a temporary to hold the result of conditionally
+     selecting between the two possible addresses.  We'll fix this up
+     immediately afterwards.  */
+  if (is_mem)
+    x = gen_reg_rtx (x_mode);
   target = noce_emit_cmove (if_info, x, code, XEXP (if_info->cond, 0),
 			    XEXP (if_info->cond, 1), a, b);
 
