Index: internal-fn.c
===================================================================
--- internal-fn.c	(revision 241379)
+++ internal-fn.c	(working copy)
@@ -1772,10 +1772,23 @@ expand_arith_overflow (enum tree_code co
   int prec1 = TYPE_PRECISION (TREE_TYPE (arg1));
   int precres = TYPE_PRECISION (type);
   location_t loc = gimple_location (stmt);
-  if (!uns0_p && get_range_pos_neg (arg0) == 1)
-    uns0_p = true;
-  if (!uns1_p && get_range_pos_neg (arg1) == 1)
-    uns1_p = true;
+  /* Try to promote to unsigned since unsigned overflow is easier to open
+     code than signed overflow, but not for multiplication if that would
+     mean not using the hardware because this would very likely result in
+     doing 2 multiplications instead of only 1, e.g. on PowerPC.  */
+  if (code == MULT_EXPR
+      && !unsr_p
+      && precres <= BITS_PER_WORD
+      && optab_handler (mulv4_optab, TYPE_MODE (type)) != CODE_FOR_nothing
+      && optab_handler (umulv4_optab, TYPE_MODE (type)) == CODE_FOR_nothing)
+    ;
+  else
+    {
+      if (!uns0_p && get_range_pos_neg (arg0) == 1)
+	uns0_p = true;
+      if (!uns1_p && get_range_pos_neg (arg1) == 1)
+	uns1_p = true;
+    }
   int pr = get_min_precision (arg0, uns0_p ? UNSIGNED : SIGNED);
   prec0 = MIN (prec0, pr);
   pr = get_min_precision (arg1, uns1_p ? UNSIGNED : SIGNED);
Index: config/rs6000/rs6000-modes.def
===================================================================
--- config/rs6000/rs6000-modes.def	(revision 241313)
+++ config/rs6000/rs6000-modes.def	(working copy)
@@ -32,13 +32,15 @@ FLOAT_MODE (TF, 16, ieee_quad_format);
 /* Add any extra modes needed to represent the condition code.
 
    For the RS/6000, we need separate modes when unsigned (logical) comparisons
-   are being done and we need a separate mode for floating-point.  We also
-   use a mode for the case when we are comparing the results of two
-   comparisons, as then only the EQ bit is valid in the register.  */
+   are being done and we need a separate mode for floating-point.  We also use
+   a mode for the case when we are comparing the results of two comparisons,
+   as then only the EQ bit is valid in the register.  We also use a mode for
+   detecting signed overflow, as only the GT bit is valid in the register.  */
 
 CC_MODE (CCUNS);
 CC_MODE (CCFP);
 CC_MODE (CCEQ);
+CC_MODE (CCV);
 
 /* Vector modes.  */
 VECTOR_MODES (INT, 8);        /*       V8QI  V4HI V2SI */
Index: config/rs6000/rs6000-protos.h
===================================================================
--- config/rs6000/rs6000-protos.h	(revision 241313)
+++ config/rs6000/rs6000-protos.h	(working copy)
@@ -127,8 +127,8 @@ extern int ccr_bit (rtx, int);
 extern void rs6000_output_function_entry (FILE *, const char *);
 extern void print_operand (FILE *, rtx, int);
 extern void print_operand_address (FILE *, rtx);
-extern enum rtx_code rs6000_reverse_condition (machine_mode,
-					       enum rtx_code);
+extern machine_mode rs6000_select_cc_mode (enum rtx_code, rtx, rtx);
+extern enum rtx_code rs6000_reverse_condition (machine_mode, enum rtx_code);
 extern rtx rs6000_emit_eqne (machine_mode, rtx, rtx, rtx);
 extern void rs6000_emit_sISEL (machine_mode, rtx[]);
 extern void rs6000_emit_sCOND (machine_mode, rtx[]);
Index: config/rs6000/rs6000.c
===================================================================
--- config/rs6000/rs6000.c	(revision 241313)
+++ config/rs6000/rs6000.c	(working copy)
@@ -2374,6 +2374,7 @@ rs6000_debug_reg_global (void)
     CCmode,
     CCUNSmode,
     CCEQmode,
+    CCVmode
   };
 
   /* Virtual regs we are interested in.  */
@@ -19334,6 +19335,7 @@ validate_condition_mode (enum rtx_code c
 
   /* These are invalid; the information is not there.  */
   gcc_assert (mode != CCEQmode || code == EQ || code == NE);
+  gcc_assert (mode != CCVmode || code == EQ || code == NE);
 }
 
 
@@ -21863,6 +21865,14 @@ print_operand (FILE *file, rtx x, int co
       /* %c is output_addr_const if a CONSTANT_ADDRESS_P, otherwise
 	 output_operand.  */
 
+    case 'C':
+      /* X is a CR register.  Print the index number of the CR.  */
+      if (GET_CODE (x) != REG || ! CR_REGNO_P (REGNO (x)))
+	output_operand_lossage ("invalid %%E value");
+      else
+	fputs (reg_names[REGNO (x)], file);
+      return;
+
     case 'D':
       /* Like 'J' but get to the GT bit only.  */
       gcc_assert (REG_P (x));
@@ -22638,6 +22648,34 @@ rs6000_assemble_visibility (tree decl, i
 }
 #endif
 
+machine_mode
+rs6000_select_cc_mode (enum rtx_code op, rtx x, rtx y)
+{
+  /* For floating-point, CCFPmode should be used.  CCUNSmode should be used
+     for unsigned comparisons.  CCEQmode should be used when we are doing an
+     inequality comparison on the result of a comparison.  CCVmode should be
+     used for the special overflow comparisons.  CCmode should be used in all
+     the other cases.  */
+
+  if (SCALAR_FLOAT_MODE_P (GET_MODE (x)))
+    return CCFPmode;
+
+  if ((op == GTU || op == LTU || op == GEU || op == LEU))
+    return CCUNSmode;
+
+  if ((op == EQ || op == NE) && COMPARISON_P (x))
+    return CCEQmode;
+
+  if (GET_CODE (y) == UNSPEC
+      && (XINT (y, 1) == UNSPEC_ADDV
+	 || XINT (y, 1) == UNSPEC_SUBV
+	 || XINT (y, 1) == UNSPEC_NEGV
+	 || XINT (y, 1) == UNSPEC_MULV))
+    return CCVmode;
+
+  return CCmode;
+}
+
 enum rtx_code
 rs6000_reverse_condition (machine_mode mode, enum rtx_code code)
 {
@@ -23558,7 +23596,6 @@ output_cbranch (rtx op, const char *labe
   enum rtx_code code = GET_CODE (op);
   rtx cc_reg = XEXP (op, 0);
   machine_mode mode = GET_MODE (cc_reg);
-  int cc_regno = REGNO (cc_reg) - CR0_REGNO;
   int need_longbranch = label != NULL && get_attr_length (insn) == 8;
   int really_reversed = reversed ^ need_longbranch;
   char *s = string;
@@ -23601,6 +23638,24 @@ output_cbranch (rtx op, const char *labe
 	}
     }
 
+  if (mode == CCVmode)
+    {
+      switch (code)
+	{
+	case EQ:
+	  /* Opposite of GT.  */
+	  code = LE;
+	  break;
+
+	case NE:
+	  code = GT;
+	  break;
+
+	default:
+	  gcc_unreachable ();
+	}
+    }
+
   switch (code)
     {
       /* Not all of these are actually distinct opcodes, but
@@ -23659,9 +23714,9 @@ output_cbranch (rtx op, const char *labe
 
   /* We need to escape any '%' characters in the reg_names string.
      Assume they'd only be the first character....  */
-  if (reg_names[cc_regno + CR0_REGNO][0] == '%')
+  if (reg_names[REGNO (cc_reg)][0] == '%')
     *s++ = '%';
-  s += sprintf (s, "%s", reg_names[cc_regno + CR0_REGNO]);
+  s += sprintf (s, "%s", reg_names[REGNO (cc_reg)]);
 
   if (label != NULL)
     {
Index: config/rs6000/rs6000.h
===================================================================
--- config/rs6000/rs6000.h	(revision 241313)
+++ config/rs6000/rs6000.h	(working copy)
@@ -2225,17 +2225,8 @@ extern unsigned rs6000_pmode;
 /* #define ADJUST_INSN_LENGTH(X,LENGTH) */
 
 /* Given a comparison code (EQ, NE, etc.) and the first operand of a
-   COMPARE, return the mode to be used for the comparison.  For
-   floating-point, CCFPmode should be used.  CCUNSmode should be used
-   for unsigned comparisons.  CCEQmode should be used when we are
-   doing an inequality comparison on the result of a
-   comparison.  CCmode should be used in all other cases.  */
-
-#define SELECT_CC_MODE(OP,X,Y) \
-  (SCALAR_FLOAT_MODE_P (GET_MODE (X)) ? CCFPmode	\
-   : (OP) == GTU || (OP) == LTU || (OP) == GEU || (OP) == LEU ? CCUNSmode \
-   : (((OP) == EQ || (OP) == NE) && COMPARISON_P (X)			  \
-      ? CCEQmode : CCmode))
+   COMPARE, return the mode to be used for the comparison.  */
+#define SELECT_CC_MODE(OP,X,Y) rs6000_select_cc_mode (OP, X, Y)
 
 /* Can the condition code MODE be safely reversed?  This is safe in
    all cases on this port, because at present it doesn't use the
Index: config/rs6000/rs6000.md
===================================================================
--- config/rs6000/rs6000.md	(revision 241313)
+++ config/rs6000/rs6000.md	(working copy)
@@ -149,6 +149,10 @@ (define_c_enum "unspec"
    UNSPEC_IEEE128_CONVERT
    UNSPEC_SIGNBIT
    UNSPEC_DOLOOP
+   UNSPEC_ADDV
+   UNSPEC_SUBV
+   UNSPEC_NEGV
+   UNSPEC_MULV
   ])
 
 ;;
@@ -1636,6 +1640,39 @@ (define_expand "add<mode>3"
     }
 })
 
+(define_expand "addv<mode>4"
+  [(match_operand:SDI 0 "register_operand")
+   (match_operand:SDI 1 "register_operand")
+   (match_operand:SDI 2 "register_operand")
+   (match_operand 3 "")]
+  "!(<MODE>mode == SImode && TARGET_POWERPC64)"
+{
+  rtx cc_reg = gen_reg_rtx (CCVmode);
+
+  if (<MODE>mode == DImode && !TARGET_POWERPC64)
+    {
+      rtx lo0 = gen_lowpart (SImode, operands[0]);
+      rtx lo1 = gen_lowpart (SImode, operands[1]);
+      rtx lo2 = gen_lowpart (SImode, operands[2]);
+      rtx hi0 = gen_highpart (SImode, operands[0]);
+      rtx hi1 = gen_highpart (SImode, operands[1]);
+      rtx hi2 = gen_highpart (SImode, operands[2]);
+
+      emit_insn (gen_addsi3_carry (lo0, lo1, lo2));
+      emit_insn (gen_addsi3_overflow_carry_in (hi0, hi1, hi2, cc_reg));
+    }
+  else
+    emit_insn (gen_add<mode>3_overflow (operands[0], operands[1], operands[2],
+					cc_reg));
+
+  rtx cond = gen_rtx_NE (VOIDmode, cc_reg, const0_rtx);
+  rtx loc_ref = gen_rtx_LABEL_REF (VOIDmode, operands[3]);
+  emit_jump_insn (gen_rtx_SET (pc_rtx,
+			       gen_rtx_IF_THEN_ELSE (VOIDmode, cond,
+						    loc_ref, pc_rtx)));
+  DONE;
+})
+
 (define_insn "*add<mode>3"
   [(set (match_operand:GPR 0 "gpc_reg_operand" "=r,r,r")
 	(plus:GPR (match_operand:GPR 1 "gpc_reg_operand" "%r,b,b")
@@ -1837,6 +1874,21 @@ (define_insn "*add<mode>3_imm_carry_neg"
   [(set_attr "type" "add")])
 
 
+;; The OV flag is set on overflow in Pmode only.
+(define_insn "add<mode>3_overflow"
+  [(set (match_operand:CCV 3 "cc_reg_operand" "=y")
+	(compare:CCV (plus:P (match_operand:P 1 "gpc_reg_operand" "r")
+			     (match_operand:P 2 "gpc_reg_operand" "r"))
+		     (unspec:P [(match_dup 1) (match_dup 2)]
+			       UNSPEC_ADDV)))
+   (set (match_operand:P 0 "gpc_reg_operand" "=r")
+	(plus:P (match_dup 1) (match_dup 2)))]
+  ""
+  "addo %0,%1,%2\;mcrxr %C3"
+  [(set_attr "type" "add")
+   (set_attr "length" "8")])
+
+
 (define_expand "add<mode>3_carry_in"
   [(parallel [
      (set (match_operand:GPR 0 "gpc_reg_operand")
@@ -1888,6 +1940,83 @@ (define_insn "add<mode>3_carry_in_m1"
   [(set_attr "type" "add")])
 
 
+(define_expand "add<mode>3_overflow_carry_in"
+  [(parallel [
+     (set (match_operand:CCV 3 "cc_reg_operand")
+	  (compare:CCV (plus:P (plus:P (match_operand:P 1 "gpc_reg_operand")
+				       (match_operand:P 2 "adde_operand"))
+				(reg:P CA_REGNO))
+		       (unspec:P [(plus:P (match_dup 1) (match_dup 2))
+				  (reg:P CA_REGNO)] UNSPEC_ADDV)))
+     (set (match_operand:P 0 "gpc_reg_operand")
+	  (plus:P (plus:P (match_dup 1) (match_dup 2))
+		  (reg:P CA_REGNO)))
+     (clobber (reg:P CA_REGNO))])]
+  ""
+{
+  if (operands[2] == const0_rtx)
+    {
+      emit_insn (gen_add<mode>3_overflow_carry_in_0 (operands[0],
+						     operands[1],
+						     operands[3]));
+      DONE;
+    }
+  if (operands[2] == constm1_rtx)
+    {
+      emit_insn (gen_add<mode>3_overflow_carry_in_m1 (operands[0],
+						      operands[1],
+						      operands[3]));
+      DONE;
+    }
+})
+
+(define_insn "*add<mode>3_overflow_carry_in_internal"
+  [(set (match_operand:CCV 3 "cc_reg_operand" "=y")
+	(compare:CCV (plus:P (plus:P (match_operand:P 1 "gpc_reg_operand" "r")
+				     (match_operand:P 2 "gpc_reg_operand" "r"))
+			     (reg:P CA_REGNO))
+		     (unspec:P [(plus:P (match_dup 1) (match_dup 2))
+				(reg:P CA_REGNO)] UNSPEC_ADDV)))
+   (set (match_operand:P 0 "gpc_reg_operand" "=r")
+	(plus:P (plus:P (match_dup 1) (match_dup 2))
+		(reg:P CA_REGNO)))
+   (clobber (reg:GPR CA_REGNO))]
+  ""
+  "addeo %0,%1,%2\;mcrxr %C3"
+  [(set_attr "type" "add")
+   (set_attr "length" "8")])
+
+(define_insn "add<mode>3_overflow_carry_in_0"
+  [(set (match_operand:CCV 2 "cc_reg_operand" "=y")
+	(compare:CCV (plus:P (match_operand:P 1 "gpc_reg_operand" "r")
+			     (reg:P CA_REGNO))
+		     (unspec:P [(plus:P (match_dup 1) (reg:P CA_REGNO))]
+			       UNSPEC_ADDV)))
+   (set (match_operand:P 0 "gpc_reg_operand" "=r")
+	(plus:P (match_dup 1) (reg:P CA_REGNO)))
+   (clobber (reg:P CA_REGNO))]
+  ""
+  "addzeo %0,%1\;mcrxr %C2"
+  [(set_attr "type" "add")
+   (set_attr "length" "8")])
+
+(define_insn "add<mode>3_overflow_carry_in_m1"
+  [(set (match_operand:CCV 2 "cc_reg_operand" "=y")
+	(compare:CCV (plus:P (plus:P (match_operand:P 1 "gpc_reg_operand" "r")
+				     (reg:P CA_REGNO))
+			     (const_int -1))
+		     (unspec:P [(plus:P (match_dup 1) (reg:P CA_REGNO))
+				(const_int -1)] UNSPEC_ADDV)))
+   (set (match_operand:P 0 "gpc_reg_operand" "=r")
+	(plus:P (plus:P (match_dup 1) (reg:P CA_REGNO))
+		(const_int -1)))
+   (clobber (reg:P CA_REGNO))]
+  ""
+  "addmeo %0,%1\;mcrxr %C2"
+  [(set_attr "type" "add")
+   (set_attr "length" "8")])
+
+
 (define_expand "one_cmpl<mode>2"
   [(set (match_operand:SDI 0 "gpc_reg_operand" "")
 	(not:SDI (match_operand:SDI 1 "gpc_reg_operand" "")))]
@@ -1980,6 +2109,39 @@ (define_expand "sub<mode>3"
     }
 })
 
+(define_expand "subv<mode>4"
+  [(match_operand:SDI 0 "register_operand")
+   (match_operand:SDI 1 "register_operand")
+   (match_operand:SDI 2 "register_operand")
+   (match_operand 3 "")]
+  "!(<MODE>mode == SImode && TARGET_POWERPC64)"
+{
+  rtx cc_reg = gen_reg_rtx (CCVmode);
+
+  if (<MODE>mode == DImode && !TARGET_POWERPC64)
+    {
+      rtx lo0 = gen_lowpart (SImode, operands[0]);
+      rtx lo1 = gen_lowpart (SImode, operands[1]);
+      rtx lo2 = gen_lowpart (SImode, operands[2]);
+      rtx hi0 = gen_highpart (SImode, operands[0]);
+      rtx hi1 = gen_highpart (SImode, operands[1]);
+      rtx hi2 = gen_highpart (SImode, operands[2]);
+
+      emit_insn (gen_subfsi3_carry (lo0, lo2, lo1));
+      emit_insn (gen_subfsi3_overflow_carry_in (hi0, hi2, hi1, cc_reg));
+    }
+  else
+    emit_insn (gen_subf<mode>3_overflow (operands[0], operands[2], operands[1],
+				         cc_reg));
+
+  rtx cond = gen_rtx_NE (VOIDmode, cc_reg, const0_rtx);
+  rtx loc_ref = gen_rtx_LABEL_REF (VOIDmode, operands[3]);
+  emit_jump_insn (gen_rtx_SET (pc_rtx,
+			       gen_rtx_IF_THEN_ELSE (VOIDmode, cond,
+						     loc_ref, pc_rtx)));
+  DONE;
+})
+
 (define_insn "*subf<mode>3"
   [(set (match_operand:GPR 0 "gpc_reg_operand" "=r")
 	(minus:GPR (match_operand:GPR 2 "gpc_reg_operand" "r")
@@ -2075,6 +2237,21 @@ (define_insn "*subf<mode>3_imm_carry_m1"
   [(set_attr "type" "add")])
 
 
+;; The OV flag is set on overflow in Pmode only.
+(define_insn "subf<mode>3_overflow"
+  [(set (match_operand:CCV 3 "cc_reg_operand" "=y")
+	(compare:CCV (minus:P (match_operand:P 2 "gpc_reg_operand" "r")
+			      (match_operand:P 1 "gpc_reg_operand" "r"))
+		     (unspec:P [(match_dup 2) (match_dup 1)]
+			       UNSPEC_SUBV)))
+   (set (match_operand:P 0 "gpc_reg_operand" "=r")
+	(minus:P (match_dup 2) (match_dup 1)))]
+  ""
+  "subfo %0,%1,%2\;mcrxr %C3"
+  [(set_attr "type" "add")
+   (set_attr "length" "8")])
+
+
 (define_expand "subf<mode>3_carry_in"
   [(parallel [
      (set (match_operand:GPR 0 "gpc_reg_operand")
@@ -2135,6 +2312,87 @@ (define_insn "subf<mode>3_carry_in_xx"
   [(set_attr "type" "add")])
 
 
+(define_expand "subf<mode>3_overflow_carry_in"
+  [(parallel [
+     (set (match_operand:CCV 3 "cc_reg_operand")
+	  (compare:CCV
+	    (plus:P (plus:P (not:P (match_operand:P 1 "gpc_reg_operand"))
+			    (reg:P CA_REGNO))
+		    (match_operand:P 2 "adde_operand"))
+	    (unspec:P [(plus:P (not:P (match_dup 1)) (reg:P CA_REGNO))
+		       (match_dup 2)] UNSPEC_ADDV)))
+     (set (match_operand:P 0 "gpc_reg_operand")
+	  (plus:P (plus:P (not:P (match_dup 1)) (reg:P CA_REGNO))
+		  (match_dup 2)))
+     (clobber (reg:P CA_REGNO))])]
+  ""
+{
+  if (operands[2] == const0_rtx)
+    {
+      emit_insn (gen_subf<mode>3_overflow_carry_in_0 (operands[0],
+						      operands[1],
+						      operands[3]));
+      DONE;
+    }
+  if (operands[2] == constm1_rtx)
+    {
+      emit_insn (gen_subf<mode>3_overflow_carry_in_m1 (operands[0],
+						       operands[1],
+						       operands[3]));
+      DONE;
+    }
+})
+
+(define_insn "*sub<mode>3_overflow_carry_in_internal"
+  [(set (match_operand:CCV 3 "cc_reg_operand" "=y")
+	(compare:CCV
+	  (plus:P (plus:P (not:P (match_operand:P 1 "gpc_reg_operand" "r"))
+			  (reg:P CA_REGNO))
+		  (match_operand:P 2 "gpc_reg_operand" "r"))
+	  (unspec:P [(plus:P (not:P (match_dup 1)) (reg:P CA_REGNO))
+		     (match_dup 2)] UNSPEC_ADDV)))
+   (set (match_operand:P 0 "gpc_reg_operand" "=r")
+	(plus:P (plus:P (not:P (match_dup 1)) (reg:P CA_REGNO))
+	        (match_dup 2)))
+   (clobber (reg:P CA_REGNO))]
+  ""
+  "subfeo %0,%1,%2\;mcrxr %C3"
+  [(set_attr "type" "add")
+   (set_attr "length" "8")])
+
+(define_insn "subf<mode>3_overflow_carry_in_0"
+  [(set (match_operand:CCV 2 "cc_reg_operand" "=y")
+	(compare:CCV
+	  (plus:P (not:P (match_operand:P 1 "gpc_reg_operand" "r"))
+		  (reg:P CA_REGNO))
+	  (unspec:P [(not:P (match_dup 1)) (reg:P CA_REGNO)]
+		    UNSPEC_ADDV)))
+   (set (match_operand:P 0 "gpc_reg_operand" "=r")
+	(plus:P (not:P (match_dup 1)) (reg:P CA_REGNO)))
+   (clobber (reg:P CA_REGNO))]
+  ""
+  "subfzeo %0,%1\;mcrxr %C2"
+  [(set_attr "type" "add")
+   (set_attr "length" "8")])
+
+(define_insn "subf<mode>3_overflow_carry_in_m1"
+  [(set (match_operand:CCV 2 "cc_reg_operand" "=y")
+	(compare:CCV
+	  (plus:P (minus:P (reg:P CA_REGNO)
+			   (match_operand:P 1 "gpc_reg_operand" "r"))
+		  (const_int -2))
+	  (unspec:P [(minus:P (reg:P CA_REGNO) (match_dup 1))
+		     (const_int -2)] UNSPEC_ADDV)))
+   (set (match_operand:P 0 "gpc_reg_operand" "=r")
+	(plus:P (minus:P (reg:P CA_REGNO) (match_dup 1))
+		(const_int -2)))
+   (clobber (reg:P CA_REGNO))]
+  ""
+  "subfmeo %0,%1\;mcrxr %C2"
+  [(set_attr "type" "add")
+   (set_attr "length" "8")])
+
+
 (define_insn "neg<mode>2"
   [(set (match_operand:GPR 0 "gpc_reg_operand" "=r")
 	(neg:GPR (match_operand:GPR 1 "gpc_reg_operand" "r")))]
@@ -2142,6 +2400,35 @@ (define_insn "neg<mode>2"
   "neg %0,%1"
   [(set_attr "type" "add")])
 
+(define_expand "negv<mode>3"
+  [(match_operand:SDI 0 "register_operand")
+   (match_operand:SDI 1 "register_operand")
+   (match_operand 2 "")]
+  "!(<MODE>mode == SImode && TARGET_POWERPC64)"
+{
+  rtx cc_reg = gen_reg_rtx (CCVmode);
+
+  if (<MODE>mode == DImode && !TARGET_POWERPC64)
+    {
+      rtx lo0 = gen_lowpart (SImode, operands[0]);
+      rtx lo1 = gen_lowpart (SImode, operands[1]);
+      rtx hi0 = gen_highpart (SImode, operands[0]);
+      rtx hi1 = gen_highpart (SImode, operands[1]);
+
+      emit_insn (gen_subfsi3_carry (lo0, lo1, const0_rtx));
+      emit_insn (gen_subfsi3_overflow_carry_in_0 (hi0, hi1, cc_reg));
+    }
+  else
+    emit_insn (gen_neg<mode>2_overflow (operands[0], operands[1], cc_reg));
+
+  rtx cond = gen_rtx_NE (VOIDmode, cc_reg, const0_rtx);
+  rtx loc_ref = gen_rtx_LABEL_REF (VOIDmode, operands[2]);
+  emit_jump_insn (gen_rtx_SET (pc_rtx,
+			       gen_rtx_IF_THEN_ELSE (VOIDmode, cond,
+						     loc_ref, pc_rtx)));
+  DONE;
+})
+
 (define_insn_and_split "*neg<mode>2_dot"
   [(set (match_operand:CC 2 "cc_reg_operand" "=x,?y")
 	(compare:CC (neg:GPR (match_operand:GPR 1 "gpc_reg_operand" "r,r"))
@@ -2184,6 +2471,18 @@ (define_insn_and_split "*neg<mode>2_dot2
    (set_attr "length" "4,8")])
 
 
+(define_insn "neg<mode>2_overflow"
+  [(set (match_operand 2 "cc_reg_operand" "=y")
+	(compare:CCV (neg:P (match_operand:P 1 "gpc_reg_operand" "r"))
+		     (unspec:P [(match_dup 1)] UNSPEC_NEGV)))
+   (set (match_operand:P 0 "gpc_reg_operand" "=r")
+	(neg:P (match_dup 1)))]
+  ""
+  "nego %0,%1\;mcrxr %C2"
+  [(set_attr "type" "add")
+   (set_attr "length" "8")])
+
+
 (define_insn "clz<mode>2"
   [(set (match_operand:GPR 0 "gpc_reg_operand" "=r")
 	(clz:GPR (match_operand:GPR 1 "gpc_reg_operand" "r")))]
@@ -2759,6 +3058,24 @@ (define_insn "mul<mode>3"
 		(const_string "16")]
 	(const_string "<bits>")))])
 
+(define_expand "mulv<mode>4"
+  [(match_operand:SDI 0 "register_operand")
+   (match_operand:SDI 1 "register_operand")
+   (match_operand:SDI 2 "register_operand")
+   (match_operand 3 "")]
+  "<MODE>mode == SImode || TARGET_POWERPC64"
+{
+  rtx cc_reg = gen_reg_rtx (CCVmode);
+  emit_insn (gen_mul<mode>3_overflow (operands[0], operands[1], operands[2],
+				      cc_reg));
+  rtx cond = gen_rtx_NE (VOIDmode, cc_reg, const0_rtx);
+  rtx loc_ref = gen_rtx_LABEL_REF (VOIDmode, operands[3]);
+  emit_jump_insn (gen_rtx_SET (pc_rtx,
+			       gen_rtx_IF_THEN_ELSE (VOIDmode, cond,
+						     loc_ref, pc_rtx)));
+  DONE;
+})
+
 (define_insn_and_split "*mul<mode>3_dot"
   [(set (match_operand:CC 3 "cc_reg_operand" "=x,?y")
 	(compare:CC (mult:GPR (match_operand:GPR 1 "gpc_reg_operand" "r,r")
@@ -2807,6 +3124,20 @@ (define_insn_and_split "*mul<mode>3_dot2
    (set_attr "dot" "yes")
    (set_attr "length" "4,8")])
 
+;; The OV flag is set on overflow in SImode for mullw and DImode for mulld.
+(define_insn "mul<mode>3_overflow"
+  [(set (match_operand:CCV 3 "cc_reg_operand" "=y")
+	(compare:CCV (mult:GPR (match_operand:GPR 1 "gpc_reg_operand" "r")
+			       (match_operand:GPR 2 "gpc_reg_operand" "r"))
+		     (unspec:GPR [(match_dup 1) (match_dup 2)]
+			         UNSPEC_MULV)))
+   (set (match_operand:GPR 0 "gpc_reg_operand" "=r")
+	(mult:GPR (match_dup 1) (match_dup 2)))]
+  ""
+  "mull<wd>o %0,%1,%2\;mcrxr %C3"
+  [(set_attr "type" "mul")
+   (set_attr "length" "8")])
+
 
 (define_expand "<su>mul<mode>3_highpart"
   [(set (match_operand:GPR 0 "gpc_reg_operand")
Index: testsuite/gcc.target/powerpc/overflow-1.c
===================================================================
--- testsuite/gcc.target/powerpc/overflow-1.c	(revision 0)
+++ testsuite/gcc.target/powerpc/overflow-1.c	(working copy)
@@ -0,0 +1,33 @@
+/* { dg-do compile } */
+/* { dg-options "-O" } */
+/* { dg-require-effective-target ilp32 } */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+bool my_add_overflow (int32_t a, int32_t b, int32_t *res)
+{
+  return __builtin_add_overflow (a, b, res);
+}
+
+bool my_sub_overflow (int32_t a, int32_t b, int32_t *res)
+{
+  return __builtin_sub_overflow (a, b, res);
+}
+
+bool my_neg_overflow (int32_t a, int32_t *res)
+{
+  return __builtin_sub_overflow (0, a, res);
+}
+
+bool my_mul_overflow (int32_t a, int32_t b, int32_t *res)
+{
+  return __builtin_mul_overflow (a, b, res);
+}
+
+/* { dg-final { scan-assembler-times "addo" 1 } } */
+/* { dg-final { scan-assembler-times "subfo" 1 } } */
+/* { dg-final { scan-assembler-times "nego" 1 } } */
+/* { dg-final { scan-assembler-times "mullwo" 1 } } */
+/* { dg-final { scan-assembler-times "mcrxr" 4 } } */
+/* { dg-final { scan-assembler-not "cmp" } } */
Index: testsuite/gcc.target/powerpc/overflow-2.c
===================================================================
--- testsuite/gcc.target/powerpc/overflow-2.c	(revision 0)
+++ testsuite/gcc.target/powerpc/overflow-2.c	(working copy)
@@ -0,0 +1,32 @@
+/* { dg-do compile } */
+/* { dg-options "-O" } */
+/* { dg-require-effective-target lp64 } */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+bool my_add_overflow (int32_t a, int32_t b, int32_t *res)
+{
+  return __builtin_add_overflow (a, b, res);
+}
+
+bool my_sub_overflow (int32_t a, int32_t b, int32_t *res)
+{
+  return __builtin_sub_overflow (a, b, res);
+}
+
+bool my_neg_overflow (int32_t a, int32_t *res)
+{
+  return __builtin_sub_overflow (0, a, res);
+}
+
+bool my_mul_overflow (int32_t a, int32_t b, int32_t *res)
+{
+  return __builtin_mul_overflow (a, b, res);
+}
+
+/* { dg-final { scan-assembler-not "addo" } } */
+/* { dg-final { scan-assembler-not "subfo" } } */
+/* { dg-final { scan-assembler-not "nego" } } */
+/* { dg-final { scan-assembler-times "mullwo" 1 } } */
+/* { dg-final { scan-assembler-times "mcrxr" 1 } } */
Index: testsuite/gcc.target/powerpc/overflow-3.c
===================================================================
--- testsuite/gcc.target/powerpc/overflow-3.c	(revision 0)
+++ testsuite/gcc.target/powerpc/overflow-3.c	(working copy)
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-options "-O" } */
+/* { dg-require-effective-target ilp32 } */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+bool my_add_overflow (int64_t a, int64_t b, int64_t *res)
+{
+  return __builtin_add_overflow (a, b, res);
+}
+
+bool my_sub_overflow (int64_t a, int64_t b, int64_t *res)
+{
+  return __builtin_sub_overflow (a, b, res);
+}
+
+bool my_neg_overflow (int64_t a, int64_t *res)
+{
+  return __builtin_sub_overflow (0, a, res);
+}
+
+/* { dg-final { scan-assembler-times "addeo" 1 } } */
+/* { dg-final { scan-assembler-times "subfeo" 1 } } */
+/* { dg-final { scan-assembler-times "subfzeo" 1 } } */
+/* { dg-final { scan-assembler-times "mcrxr" 3 } } */
+/* { dg-final { scan-assembler-not "cmp" } } */
Index: testsuite/gcc.target/powerpc/overflow-4.c
===================================================================
--- testsuite/gcc.target/powerpc/overflow-4.c	(revision 0)
+++ testsuite/gcc.target/powerpc/overflow-4.c	(working copy)
@@ -0,0 +1,33 @@
+/* { dg-do compile } */
+/* { dg-options "-O" } */
+/* { dg-require-effective-target lp64 } */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+bool my_add_overflow (int64_t a, int64_t b, int64_t *res)
+{
+  return __builtin_add_overflow (a, b, res);
+}
+
+bool my_sub_overflow (int64_t a, int64_t b, int64_t *res)
+{
+  return __builtin_sub_overflow (a, b, res);
+}
+
+bool my_neg_overflow (int64_t a, int64_t *res)
+{
+  return __builtin_sub_overflow (0, a, res);
+}
+
+bool my_mul_overflow (int64_t a, int64_t b, int64_t *res)
+{
+  return __builtin_mul_overflow (a, b, res);
+}
+
+/* { dg-final { scan-assembler-times "addo" 1 } } */
+/* { dg-final { scan-assembler-times "subfo" 1 } } */
+/* { dg-final { scan-assembler-times "nego" 1 } } */
+/* { dg-final { scan-assembler-times "mulldo" 1 } } */
+/* { dg-final { scan-assembler-times "mcrxr" 4 } } */
+/* { dg-final { scan-assembler-not "cmp" } } */
