public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [SPARC] Add support for overflow arithmetic
@ 2016-10-21  9:18 Eric Botcazou
  2016-10-21  9:48 ` Jakub Jelinek
  0 siblings, 1 reply; 4+ messages in thread
From: Eric Botcazou @ 2016-10-21  9:18 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

[-- Attachment #1: Type: text/plain, Size: 2797 bytes --]

SPARC can expose the CC register before reload so the implementation is direct 
as on x86 and ARM.  Like ARM, there is no support for multiplication at all, 
but support for 64-bit operations in 32-bit mode; the latter would give rise 
to complex patterns for signed arithmetic involving TImode in 32-bit mode 
which is not (supposed to be) supported, so, unlike the ARM implementation, 
the implementation resorts to UNSPECs for signed arithmetic and avoids TImode.

The code is optimal for 32-bit and 64-bit operations in 32-bit mode, but only 
for 64-bit operations in 64-bit mode, although SPARC supports separate 32-bit 
and 64-bit condition codes in 64-bit mode; that's because the port defines the 
WORD_REGISTER_OPERATIONS macro and expand_arith_overflow has:

      /* For sub-word operations, if target doesn't have them, start
	 with precres widening right away, otherwise do it only
	 if the most simple cases can't be used.  */
      if (WORD_REGISTER_OPERATIONS
	  && orig_precres == precres
	  && precres < BITS_PER_WORD)
	;

Jakub, any idea of an elegant way to address this issue?

Tested on SPARC/Solaris, applied on the mainline.


2016-10-21  Eric Botcazou  <ebotcazou@adacore.com>

	* config/sparc/sparc-modes.def (CCV): New.
	(CCXV): Likewise.
	* config/sparc/predicates.md (v_comparison_operator): New.
	(icc_comparison_operator): Add support for CCV/CCXV.
	(xcc_comparison_operator): Likewise.
	* config/sparc/sparc.c (output_cbranch): Likewise.
	(sparc_print_operand): Likewise.
	* config/sparc/sparc.md (UNSPEC_{ADD,SUB,NEG}V): New constants.
	(uaddvdi4): New expander.
	(addvdi4): Likewise.
	(uaddvdi4_sp32): New instruction.
	(addvdi4_sp32): Likewise.
	(uaddvsi4): New expander.
	(addvsi4): Likewise.
	(cmp_ccc_plus_sltu_set): New instruction.
	(cmp_ccv_plus): Likewise.
	(cmp_ccxv_plus): Likewise.
	(cmp_ccv_plus_set): Likewise.
	(cmp_ccxv_plus_set): Likewise.
	(cmp_ccv_plus_sltu_set): Likewise.
	(uaddvdi4): New expander.
	(subvdi4): Likewise.
	(usubdi4_sp32): New instruction.
	(subvdi4_sp32): Likewise.
	(usubvsi4): New expander.
	(subvsi4): Likewise.
	(cmpsi_minus_sltu_set): New instruction.
	(cmp_ccv_minus): Likewise.
	(cmp_ccxv_minus): Likewise.
	(cmp_ccv_minus_set): Likewise.
	(cmp_ccxv_minus_set): Likewise.
	(cmp_ccv_minus_sltu_set): Likewise.
	(unegvdi3): New expander.
	(negvdi3): Likewise.
	(unegdi3_sp32): New instruction.
	(negvdi3_sp32): Likewise.
	(unegvsi3): New expander.
	(negvsi3): Likewise.
	(cmp_ccc_neg_sltu_set): New instruction.
	(cmp_ccv_neg): Likewise.
	(cmp_ccxv_neg): Likewise.
	(cmp_ccv_neg_set): Likewise.
	(cmp_ccxv_neg_set): Likewise.
	(cmp_ccv_neg_sltu_set): Likewise.
testsuite/
	* gcc.target/sparc/overflow-1.c: New test.
	* gcc.target/sparc/overflow-2.c: Likewise.
	* gcc.target/sparc/overflow-3.c: Likewise.

-- 
Eric Botcazou

[-- Attachment #2: sparc_overflow.diff --]
[-- Type: text/x-patch, Size: 34940 bytes --]

Index: config/sparc/predicates.md
===================================================================
--- config/sparc/predicates.md	(revision 241326)
+++ config/sparc/predicates.md	(working copy)
@@ -420,6 +420,10 @@ (define_predicate "nz_comparison_operato
 (define_predicate "c_comparison_operator"
   (match_code "ltu,geu"))
 
+;; Return true if OP is a valid comparison operator for CCVmode.
+(define_predicate "v_comparison_operator"
+  (match_code "eq,ne"))
+
 ;; Return true if OP is an integer comparison operator.  This allows
 ;; the use of MATCH_OPERATOR to recognize all the branch insns.
 (define_predicate "icc_comparison_operator"
@@ -436,6 +440,9 @@ (define_predicate "icc_comparison_operat
     case CCCmode:
     case CCXCmode:
       return c_comparison_operator (op, mode);
+    case CCVmode:
+    case CCXVmode:
+      return v_comparison_operator (op, mode);
     default:
       return false;
     }
Index: config/sparc/sparc-modes.def
===================================================================
--- config/sparc/sparc-modes.def	(revision 241326)
+++ config/sparc/sparc-modes.def	(working copy)
@@ -34,6 +34,10 @@ FLOAT_MODE (TF, 16, ieee_quad_format);
    they explicitly set the C flag (unsigned overflow).  Only the unsigned
    <,>= operators can be used in conjunction with it.
 
+   We also have a CCVmode which is used by the arithmetic instructions when
+   they explicitly set the V flag (signed overflow).  Only the =,!= operators
+   can be used in conjunction with it.
+
    We also have two modes to indicate that the relevant condition code is
    in the floating-point condition code register.  One for comparisons which
    will generate an exception if the result is unordered (CCFPEmode) and
@@ -46,6 +50,8 @@ CC_MODE (CCNZ);
 CC_MODE (CCXNZ);
 CC_MODE (CCC);
 CC_MODE (CCXC);
+CC_MODE (CCV);
+CC_MODE (CCXV);
 CC_MODE (CCFP);
 CC_MODE (CCFPE);
 
Index: config/sparc/sparc.c
===================================================================
--- config/sparc/sparc.c	(revision 241326)
+++ config/sparc/sparc.c	(working copy)
@@ -2784,8 +2784,9 @@ select_cc_mode (enum rtx_code op, rtx x,
 	  gcc_unreachable ();
 	}
     }
-  else if (GET_CODE (x) == PLUS || GET_CODE (x) == MINUS
-	   || GET_CODE (x) == NEG || GET_CODE (x) == ASHIFT)
+  else if ((GET_CODE (x) == PLUS || GET_CODE (x) == MINUS
+	    || GET_CODE (x) == NEG || GET_CODE (x) == ASHIFT)
+	   && y == const0_rtx)
     {
       if (TARGET_ARCH64 && GET_MODE (x) == DImode)
 	return CCXNZmode;
@@ -2803,6 +2804,18 @@ select_cc_mode (enum rtx_code op, rtx x,
 	    return CCCmode;
 	}
 
+      /* This is for the [u]addvdi4_sp32 and [u]subvdi4_sp32 patterns.  */
+      if (!TARGET_ARCH64 && GET_MODE (x) == DImode)
+	{
+	  if (GET_CODE (y) == UNSPEC
+	      && (XINT (y, 1) == UNSPEC_ADDV
+		 || XINT (y, 1) == UNSPEC_SUBV
+	         || XINT (y, 1) == UNSPEC_NEGV))
+	    return CCVmode;
+	  else
+	    return CCCmode;
+	}
+
       if (TARGET_ARCH64 && GET_MODE (x) == DImode)
 	return CCXmode;
       else
@@ -7724,10 +7737,16 @@ output_cbranch (rtx op, rtx dest, int la
       switch (code)
 	{
 	case NE:
-	  branch = "bne";
+	  if (mode == CCVmode || mode == CCXVmode)
+	    branch = "bvs";
+	  else
+	    branch = "bne";
 	  break;
 	case EQ:
-	  branch = "be";
+	  if (mode == CCVmode || mode == CCXVmode)
+	    branch = "bvc";
+	  else
+	    branch = "be";
 	  break;
 	case GE:
 	  if (mode == CCNZmode || mode == CCXNZmode)
@@ -7794,6 +7813,7 @@ output_cbranch (rtx op, rtx dest, int la
 	case CCmode:
 	case CCNZmode:
 	case CCCmode:
+	case CCVmode:
 	  labelno = "%%icc, ";
 	  if (v8)
 	    labelno = "";
@@ -7801,6 +7821,7 @@ output_cbranch (rtx op, rtx dest, int la
 	case CCXmode:
 	case CCXNZmode:
 	case CCXCmode:
+	case CCXVmode:
 	  labelno = "%%xcc, ";
 	  gcc_assert (!v8);
 	  break;
@@ -8804,11 +8825,13 @@ sparc_print_operand (FILE *file, rtx x,
 	    case CCmode:
 	    case CCNZmode:
 	    case CCCmode:
+	    case CCVmode:
 	      s = "%icc";
 	      break;
 	    case CCXmode:
 	    case CCXNZmode:
 	    case CCXCmode:
+	    case CCXVmode:
 	      s = "%xcc";
 	      break;
 	    default:
@@ -8883,10 +8906,16 @@ sparc_print_operand (FILE *file, rtx x,
 	switch (GET_CODE (x))
 	  {
 	  case NE:
-	    s = "ne";
+	    if (mode == CCVmode || mode == CCXVmode)
+	      s = "vs";
+	    else
+	      s = "ne";
 	    break;
 	  case EQ:
-	    s = "e";
+	    if (mode == CCVmode || mode == CCXVmode)
+	      s = "vc";
+	    else
+	      s = "e";
 	    break;
 	  case GE:
 	    if (mode == CCNZmode || mode == CCXNZmode)
Index: config/sparc/sparc.md
===================================================================
--- config/sparc/sparc.md	(revision 241326)
+++ config/sparc/sparc.md	(working copy)
@@ -92,6 +92,10 @@ (define_c_enum "unspec" [
   UNSPEC_MUL8
   UNSPEC_MUL8SU
   UNSPEC_MULDSU
+
+  UNSPEC_ADDV
+  UNSPEC_SUBV
+  UNSPEC_NEGV
 ])
 
 (define_c_enum "unspecv" [
@@ -3714,6 +3718,51 @@ (define_expand "adddi3"
     }
 })
 
+(define_expand "uaddvdi4"
+  [(parallel [(set (reg:CCXC CC_REG)
+		   (compare:CCXC (plus:DI (match_operand:DI 1 "register_operand")
+					  (match_operand:DI 2 "arith_add_operand"))
+			         (match_dup 1)))
+	      (set (match_operand:DI 0 "register_operand")
+		   (plus:DI (match_dup 1) (match_dup 2)))])
+   (set (pc) (if_then_else (ltu (reg:CCXC CC_REG) (const_int 0))
+			   (label_ref (match_operand 3))
+			   (pc)))]
+ ""
+{
+  if (!TARGET_64BIT)
+    {
+      emit_insn (gen_uaddvdi4_sp32 (operands[0], operands[1], operands[2]));
+      rtx x = gen_rtx_LTU (VOIDmode, gen_rtx_REG (CCCmode, SPARC_ICC_REG),
+				     const0_rtx);
+      emit_jump_insn (gen_cbranchcc4 (x, XEXP (x, 0), XEXP (x, 1), operands[3]));
+      DONE;
+    }
+})
+
+(define_expand "addvdi4"
+  [(parallel [(set (reg:CCXV CC_REG)
+		   (compare:CCXV (plus:DI (match_operand:DI 1 "register_operand")
+					  (match_operand:DI 2 "arith_add_operand"))
+			         (unspec:DI [(match_dup 1) (match_dup 2)]
+					    UNSPEC_ADDV)))
+	      (set (match_operand:DI 0 "register_operand")
+		   (plus:DI (match_dup 1) (match_dup 2)))])
+   (set (pc) (if_then_else (ne (reg:CCXV CC_REG) (const_int 0))
+			   (label_ref (match_operand 3))
+			   (pc)))]
+ ""
+{
+  if (!TARGET_64BIT)
+    {
+      emit_insn (gen_addvdi4_sp32 (operands[0], operands[1], operands[2]));
+      rtx x = gen_rtx_NE (VOIDmode, gen_rtx_REG (CCVmode, SPARC_ICC_REG),
+				    const0_rtx);
+      emit_jump_insn (gen_cbranchcc4 (x, XEXP (x, 0), XEXP (x, 1), operands[3]));
+      DONE;
+    }
+})
+
 (define_insn_and_split "adddi3_sp32"
   [(set (match_operand:DI 0 "register_operand" "=&r")
 	(plus:DI (match_operand:DI 1 "register_operand" "%r")
@@ -3740,6 +3789,80 @@ (define_insn_and_split "adddi3_sp32"
 }
   [(set_attr "length" "2")])
 
+(define_insn_and_split "uaddvdi4_sp32"
+  [(set (reg:CCC CC_REG)
+	(compare:CCC (plus:DI (match_operand:DI 1 "register_operand" "%r")
+			      (match_operand:DI 2 "arith_double_operand" "rHI"))
+		     (match_dup 1)))
+   (set (match_operand:DI 0 "register_operand" "=&r")
+	(plus:DI (match_dup 1) (match_dup 2)))]
+  "!TARGET_ARCH64"
+  "#"
+  "&& reload_completed"
+  [(parallel [(set (reg:CCC CC_REG)
+		   (compare:CCC (plus:SI (match_dup 4) (match_dup 5))
+				(match_dup 4)))
+	      (set (match_dup 3)
+		   (plus:SI (match_dup 4) (match_dup 5)))])
+   (parallel [(set (reg:CCC CC_REG)
+		   (compare:CCC (zero_extend:DI
+				  (plus:SI (plus:SI (match_dup 7) (match_dup 8))
+					   (ltu:SI (reg:CCC CC_REG)
+						   (const_int 0))))
+				(plus:DI (plus:DI (zero_extend:DI (match_dup 7))
+						  (zero_extend:DI (match_dup 8)))
+					 (ltu:DI (reg:CCC CC_REG)
+						 (const_int 0)))))
+	      (set (match_dup 6)
+		   (plus:SI (plus:SI (match_dup 7) (match_dup 8))
+			    (ltu:SI (reg:CCC CC_REG)
+				    (const_int 0))))])]
+{
+  operands[3] = gen_lowpart (SImode, operands[0]);
+  operands[4] = gen_lowpart (SImode, operands[1]);
+  operands[5] = gen_lowpart (SImode, operands[2]);
+  operands[6] = gen_highpart (SImode, operands[0]);
+  operands[7] = gen_highpart_mode (SImode, DImode, operands[1]);
+  operands[8] = gen_highpart_mode (SImode, DImode, operands[2]);
+}
+  [(set_attr "length" "2")])
+
+(define_insn_and_split "addvdi4_sp32"
+  [(set (reg:CCV CC_REG)
+	(compare:CCV (plus:DI (match_operand:DI 1 "register_operand" "%r")
+			      (match_operand:DI 2 "arith_double_operand" "rHI"))
+		     (unspec:DI [(match_dup 1) (match_dup 2)] UNSPEC_ADDV)))
+   (set (match_operand:DI 0 "register_operand" "=&r")
+	(plus:DI (match_dup 1) (match_dup 2)))]
+  "!TARGET_ARCH64"
+  "#"
+  "&& reload_completed"
+  [(parallel [(set (reg:CCC CC_REG)
+		   (compare:CCC (plus:SI (match_dup 4) (match_dup 5))
+				(match_dup 4)))
+	      (set (match_dup 3)
+		   (plus:SI (match_dup 4) (match_dup 5)))])
+   (parallel [(set (reg:CCV CC_REG)
+		   (compare:CCV (plus:SI (plus:SI (match_dup 7) (match_dup 8))
+					 (ltu:SI (reg:CCC CC_REG)
+						 (const_int 0)))
+				(unspec:SI [(plus:SI (match_dup 7) (match_dup 8))
+					    (ltu:SI (reg:CCC CC_REG)
+						     (const_int 0))]
+					   UNSPEC_ADDV)))
+	      (set (match_dup 6)
+		   (plus:SI (plus:SI (match_dup 7) (match_dup 8))
+			    (ltu:SI (reg:CCC CC_REG) (const_int 0))))])]
+{
+  operands[3] = gen_lowpart (SImode, operands[0]);
+  operands[4] = gen_lowpart (SImode, operands[1]);
+  operands[5] = gen_lowpart (SImode, operands[2]);
+  operands[6] = gen_highpart (SImode, operands[0]);
+  operands[7] = gen_highpart_mode (SImode, DImode, operands[1]);
+  operands[8] = gen_highpart_mode (SImode, DImode, operands[2]);
+}
+  [(set_attr "length" "2")])
+
 (define_insn_and_split "*addx_extend_sp32"
   [(set (match_operand:DI 0 "register_operand" "=r")
 	(zero_extend:DI (plus:SI (plus:SI
@@ -3797,6 +3920,31 @@ (define_insn "addsi3"
   [(set_attr "type" "*,*")
    (set_attr "fptype" "*,*")])
 
+(define_expand "uaddvsi4"
+  [(parallel [(set (reg:CCC CC_REG)
+		   (compare:CCC (plus:SI (match_operand:SI 1 "register_operand")
+					 (match_operand:SI 2 "arith_operand"))
+			        (match_dup 1)))
+	      (set (match_operand:SI 0 "register_operand")
+		   (plus:SI (match_dup 1) (match_dup 2)))])
+   (set (pc) (if_then_else (ltu (reg:CCC CC_REG) (const_int 0))
+			   (label_ref (match_operand 3))
+			   (pc)))]
+ "")
+
+(define_expand "addvsi4"
+  [(parallel [(set (reg:CCV CC_REG)
+		   (compare:CCV (plus:SI (match_operand:SI 1 "register_operand")
+					 (match_operand:SI 2 "arith_operand"))
+			        (unspec:SI [(match_dup 1) (match_dup 2)]
+					   UNSPEC_ADDV)))
+	      (set (match_operand:SI 0 "register_operand")
+		   (plus:SI (match_dup 1) (match_dup 2)))])
+   (set (pc) (if_then_else (ne (reg:CCV CC_REG) (const_int 0))
+			   (label_ref (match_operand 3))
+			   (pc)))]
+ "")
+
 (define_insn "*cmp_ccnz_plus"
   [(set (reg:CCNZ CC_REG)
 	(compare:CCNZ (plus:SI (match_operand:SI 0 "register_operand" "%r")
@@ -3877,6 +4025,79 @@ (define_insn "*cmp_ccxc_plus_set"
   "addcc\t%1, %2, %0"
   [(set_attr "type" "compare")])
 
+(define_insn "*cmp_ccc_plus_sltu_set"
+  [(set (reg:CCC CC_REG)
+	(compare:CCC (zero_extend:DI
+		       (plus:SI
+			 (plus:SI (match_operand:SI 1 "register_operand" "%r")
+				  (match_operand:SI 2 "arith_operand" "rI"))
+		       (ltu:SI (reg:CCC CC_REG) (const_int 0))))
+		     (plus:DI (plus:DI (zero_extend:DI (match_dup 1))
+				       (zero_extend:DI (match_dup 2)))
+			      (ltu:DI (reg:CCC CC_REG) (const_int 0)))))
+   (set (match_operand:SI 0 "register_operand" "=r")
+	(plus:SI (plus:SI (match_dup 1) (match_dup 2))
+		 (ltu:SI (reg:CCC CC_REG) (const_int 0))))]
+  ""
+  "addxcc\t%1, %2, %0"
+  [(set_attr "type" "compare")])
+
+(define_insn "*cmp_ccv_plus"
+  [(set (reg:CCV CC_REG)
+	(compare:CCV (plus:SI (match_operand:SI 0 "register_operand" "%r")
+			      (match_operand:SI 1 "arith_operand" "rI"))
+		     (unspec:SI [(match_dup 0) (match_dup 1)] UNSPEC_ADDV)))]
+  ""
+  "addcc\t%0, %1, %%g0"
+  [(set_attr "type" "compare")])
+
+(define_insn "*cmp_ccxv_plus"
+  [(set (reg:CCXV CC_REG)
+	(compare:CCXV (plus:DI (match_operand:DI 0 "register_operand" "%r")
+			       (match_operand:DI 1 "arith_operand" "rI"))
+		      (unspec:DI [(match_dup 0) (match_dup 1)] UNSPEC_ADDV)))]
+  "TARGET_ARCH64"
+  "addcc\t%0, %1, %%g0"
+  [(set_attr "type" "compare")])
+
+(define_insn "*cmp_ccv_plus_set"
+  [(set (reg:CCV CC_REG)
+	(compare:CCV (plus:SI (match_operand:SI 1 "register_operand" "%r")
+			      (match_operand:SI 2 "arith_operand" "rI"))
+		     (unspec:SI [(match_dup 1) (match_dup 2)] UNSPEC_ADDV)))
+   (set (match_operand:SI 0 "register_operand" "=r")
+	(plus:SI (match_dup 1) (match_dup 2)))]
+  ""
+  "addcc\t%1, %2, %0"
+  [(set_attr "type" "compare")])
+
+(define_insn "*cmp_ccxv_plus_set"
+  [(set (reg:CCXV CC_REG)
+	(compare:CCXV (plus:DI (match_operand:DI 1 "register_operand" "%r")
+			       (match_operand:DI 2 "arith_operand" "rI"))
+		      (unspec:DI [(match_dup 1) (match_dup 2)] UNSPEC_ADDV)))
+   (set (match_operand:DI 0 "register_operand" "=r")
+	(plus:DI (match_dup 1) (match_dup 2)))]
+  "TARGET_ARCH64"
+  "addcc\t%1, %2, %0"
+  [(set_attr "type" "compare")])
+
+(define_insn "*cmp_ccv_plus_sltu_set"
+  [(set (reg:CCV CC_REG)
+	(compare:CCV (plus:SI (plus:SI (match_operand:SI 1 "register_operand" "%r")
+				       (match_operand:SI 2 "arith_operand" "rI"))
+			      (ltu:SI (reg:CCC CC_REG) (const_int 0)))
+		     (unspec:SI [(plus:SI (match_dup 1) (match_dup 2))
+				 (ltu:SI (reg:CCC CC_REG) (const_int 0))]
+				UNSPEC_ADDV)))
+   (set (match_operand:SI 0 "register_operand" "=r")
+	(plus:SI (plus:SI (match_dup 1) (match_dup 2))
+		 (ltu:SI (reg:CCC CC_REG) (const_int 0))))]
+  ""
+  "addxcc\t%1, %2, %0"
+  [(set_attr "type" "compare")])
+
+
 (define_expand "subdi3"
   [(set (match_operand:DI 0 "register_operand" "")
 	(minus:DI (match_operand:DI 1 "register_operand" "")
@@ -3890,6 +4111,56 @@ (define_expand "subdi3"
     }
 })
 
+(define_expand "usubvdi4"
+  [(parallel [(set (reg:CCX CC_REG)
+		   (compare:CCX (match_operand:DI 1 "register_or_zero_operand")
+				(match_operand:DI 2 "arith_add_operand")))
+	      (set (match_operand:DI 0 "register_operand")
+		   (minus:DI (match_dup 1) (match_dup 2)))])
+   (set (pc) (if_then_else (ltu (reg:CCX CC_REG) (const_int 0))
+			   (label_ref (match_operand 3))
+			   (pc)))]
+ ""
+{
+  if (operands[1] == const0_rtx)
+    {
+      emit_insn (gen_unegvdi3 (operands[0], operands[2], operands[3]));
+      DONE;
+    }
+
+  if (!TARGET_64BIT)
+    {
+      emit_insn (gen_usubvdi4_sp32 (operands[0], operands[1], operands[2]));
+      rtx x = gen_rtx_LTU (VOIDmode, gen_rtx_REG (CCCmode, SPARC_ICC_REG),
+				     const0_rtx);
+      emit_jump_insn (gen_cbranchcc4 (x, XEXP (x, 0), XEXP (x, 1), operands[3]));
+      DONE;
+    }
+})
+
+(define_expand "subvdi4"
+  [(parallel [(set (reg:CCXV CC_REG)
+		   (compare:CCXV (minus:DI (match_operand:DI 1 "register_operand")
+					   (match_operand:DI 2 "arith_add_operand"))
+			         (unspec:DI [(match_dup 1) (match_dup 2)]
+					    UNSPEC_SUBV)))
+	      (set (match_operand:DI 0 "register_operand")
+		   (minus:DI (match_dup 1) (match_dup 2)))])
+   (set (pc) (if_then_else (ne (reg:CCXV CC_REG) (const_int 0))
+			   (label_ref (match_operand 3))
+			   (pc)))]
+ ""
+{
+  if (!TARGET_64BIT)
+    {
+      emit_insn (gen_subvdi4_sp32 (operands[0], operands[1], operands[2]));
+      rtx x = gen_rtx_NE (VOIDmode, gen_rtx_REG (CCVmode, SPARC_ICC_REG),
+				    const0_rtx);
+      emit_jump_insn (gen_cbranchcc4 (x, XEXP (x, 0), XEXP (x, 1), operands[3]));
+      DONE;
+    }
+})
+
 (define_insn_and_split "subdi3_sp32"
   [(set (match_operand:DI 0 "register_operand" "=&r")
 	(minus:DI (match_operand:DI 1 "register_operand" "r")
@@ -3915,6 +4186,80 @@ (define_insn_and_split "subdi3_sp32"
 }
   [(set_attr "length" "2")])
 
+(define_insn_and_split "usubvdi4_sp32"
+  [(set (reg:CCC CC_REG)
+	(compare:CCC (match_operand:DI 1 "register_operand" "r")
+		     (match_operand:DI 2 "arith_double_operand" "rHI")))
+   (set (match_operand:DI 0 "register_operand" "=&r")
+	(minus:DI (match_dup 1) (match_dup 2)))]
+  "!TARGET_ARCH64"
+  "#"
+  "&& reload_completed"
+  [(parallel [(set (reg:CC CC_REG)
+		   (compare:CC (match_dup 4) (match_dup 5)))
+	      (set (match_dup 3)
+		   (minus:SI (match_dup 4) (match_dup 5)))])
+   (parallel [(set (reg:CCC CC_REG)
+		   (compare:CCC (zero_extend:DI
+				  (minus:SI (minus:SI (match_dup 7)
+						      (ltu:SI (reg:CC CC_REG)
+							      (const_int 0)))
+					    (match_dup 8)))
+				(minus:DI
+				  (minus:DI (zero_extend:DI (match_dup 7))
+					    (ltu:DI (reg:CC CC_REG)
+						    (const_int 0)))
+				  (zero_extend:DI (match_dup 8)))))
+	      (set (match_dup 6)
+		   (minus:SI (minus:SI (match_dup 7)
+				       (ltu:SI (reg:CC CC_REG)
+					       (const_int 0)))
+			     (match_dup 8)))])]
+{
+  operands[3] = gen_lowpart (SImode, operands[0]);
+  operands[4] = gen_lowpart (SImode, operands[1]);
+  operands[5] = gen_lowpart (SImode, operands[2]);
+  operands[6] = gen_highpart (SImode, operands[0]);
+  operands[7] = gen_highpart_mode (SImode, DImode, operands[1]);
+  operands[8] = gen_highpart_mode (SImode, DImode, operands[2]);
+}
+  [(set_attr "length" "2")])
+
+(define_insn_and_split "subvdi4_sp32"
+  [(set (reg:CCV CC_REG)
+	(compare:CCV (minus:DI (match_operand:DI 1 "register_operand" "%r")
+			       (match_operand:DI 2 "arith_double_operand" "rHI"))
+		     (unspec:DI [(match_dup 1) (match_dup 2)] UNSPEC_SUBV)))
+   (set (match_operand:DI 0 "register_operand" "=&r")
+	(minus:DI (match_dup 1) (match_dup 2)))]
+  "!TARGET_ARCH64"
+  "#"
+  "&& reload_completed"
+  [(parallel [(set (reg:CC CC_REG)
+		   (compare:CC (match_dup 4) (match_dup 5)))
+	      (set (match_dup 3)
+		   (minus:SI (match_dup 4) (match_dup 5)))])
+   (parallel [(set (reg:CCV CC_REG)
+		   (compare:CCV (minus:SI (minus:SI (match_dup 7) (match_dup 8))
+					  (ltu:SI (reg:CC CC_REG)
+						  (const_int 0)))
+				(unspec:SI [(minus:SI (match_dup 7) (match_dup 8))
+					    (ltu:SI (reg:CC CC_REG)
+						    (const_int 0))]
+					   UNSPEC_SUBV)))
+	      (set (match_dup 6)
+		   (minus:SI (minus:SI (match_dup 7) (match_dup 8))
+			     (ltu:SI (reg:CC CC_REG) (const_int 0))))])]
+{
+  operands[3] = gen_lowpart (SImode, operands[0]);
+  operands[4] = gen_lowpart (SImode, operands[1]);
+  operands[5] = gen_lowpart (SImode, operands[2]);
+  operands[6] = gen_highpart (SImode, operands[0]);
+  operands[7] = gen_highpart_mode (SImode, DImode, operands[1]);
+  operands[8] = gen_highpart_mode (SImode, DImode, operands[2]);
+}
+  [(set_attr "length" "2")])
+
 (define_insn_and_split "*subx_extend_sp32"
   [(set (match_operand:DI 0 "register_operand" "=r")
 	(zero_extend:DI (minus:SI (minus:SI
@@ -3971,6 +4316,37 @@ (define_insn "subsi3"
   [(set_attr "type" "*,*")
    (set_attr "fptype" "*,*")])
 
+(define_expand "usubvsi4"
+  [(parallel [(set (reg:CC CC_REG)
+		   (compare:CC (match_operand:SI 1 "register_or_zero_operand")
+			       (match_operand:SI 2 "arith_operand")))
+	      (set (match_operand:SI 0 "register_operand")
+		   (minus:SI (match_dup 1) (match_dup 2)))])
+   (set (pc) (if_then_else (ltu (reg:CC CC_REG) (const_int 0))
+			   (label_ref (match_operand 3))
+			   (pc)))]
+ ""
+{
+  if (operands[1] == const0_rtx)
+    {
+      emit_insn (gen_unegvsi3 (operands[0], operands[2], operands[3]));
+      DONE;
+    }
+})
+
+(define_expand "subvsi4"
+  [(parallel [(set (reg:CCV CC_REG)
+		   (compare:CCV (minus:SI (match_operand:SI 1 "register_operand")
+					  (match_operand:SI 2 "arith_operand"))
+			        (unspec:SI [(match_dup 1) (match_dup 2)]
+					   UNSPEC_SUBV)))
+	      (set (match_operand:SI 0 "register_operand")
+		   (minus:SI (match_dup 1) (match_dup 2)))])
+   (set (pc) (if_then_else (ne (reg:CCV CC_REG) (const_int 0))
+			   (label_ref (match_operand 3))
+			   (pc)))]
+ "")
+
 (define_insn "*cmp_ccnz_minus"
   [(set (reg:CCNZ CC_REG)
 	(compare:CCNZ (minus:SI (match_operand:SI 0 "register_or_zero_operand" "rJ")
@@ -4031,6 +4407,82 @@ (define_insn "*cmpdi_set"
   "subcc\t%r1, %2, %0"
   [(set_attr "type" "compare")])
 
+(define_insn "*cmp_ccc_minus_sltu_set"
+  [(set (reg:CCC CC_REG)
+	(compare:CCC (zero_extend:DI
+		       (minus:SI
+			 (minus:SI
+			   (match_operand:SI 1 "register_or_zero_operand" "rJ")
+			   (ltu:SI (reg:CC CC_REG) (const_int 0)))
+			 (match_operand:SI 2 "arith_operand" "rI")))
+		     (minus:DI
+		       (minus:DI
+			 (zero_extend:DI (match_dup 1))
+			 (ltu:DI (reg:CC CC_REG) (const_int 0)))
+		       (zero_extend:DI (match_dup 2)))))
+   (set (match_operand:SI 0 "register_operand" "=r")
+	(minus:SI (minus:SI (match_dup 1)
+			    (ltu:SI (reg:CC CC_REG) (const_int 0)))
+		  (match_dup 2)))]
+  ""
+  "subxcc\t%r1, %2, %0"
+  [(set_attr "type" "compare")])
+
+(define_insn "*cmp_ccv_minus"
+  [(set (reg:CCV CC_REG)
+	(compare:CCV (minus:SI (match_operand:SI 0 "register_or_zero_operand" "rJ")
+			       (match_operand:SI 1 "arith_operand" "rI"))
+		     (unspec:SI [(match_dup 0) (match_dup 1)] UNSPEC_SUBV)))]
+  ""
+  "subcc\t%r0, %1, %%g0"
+  [(set_attr "type" "compare")])
+
+(define_insn "*cmp_ccxv_minus"
+  [(set (reg:CCXV CC_REG)
+	(compare:CCXV (minus:DI (match_operand:DI 0 "register_or_zero_operand" "rJ")
+			        (match_operand:DI 1 "arith_operand" "rI"))
+		      (unspec:DI [(match_dup 0) (match_dup 1)] UNSPEC_SUBV)))]
+  "TARGET_ARCH64"
+  "subcc\t%r0, %1, %%g0"
+  [(set_attr "type" "compare")])
+
+(define_insn "*cmp_ccv_minus_set"
+  [(set (reg:CCV CC_REG)
+	(compare:CCV (minus:SI (match_operand:SI 1 "register_or_zero_operand" "rJ")
+			       (match_operand:SI 2 "arith_operand" "rI"))
+		     (unspec:SI [(match_dup 1) (match_dup 2)] UNSPEC_SUBV)))
+   (set (match_operand:SI 0 "register_operand" "=r")
+	(minus:SI (match_dup 1) (match_dup 2)))]
+  ""
+  "subcc\t%r1, %2, %0"
+  [(set_attr "type" "compare")])
+
+(define_insn "*cmp_ccxv_minus_set"
+  [(set (reg:CCXV CC_REG)
+	(compare:CCXV (minus:DI (match_operand:DI 1 "register_or_zero_operand" "rJ")
+			        (match_operand:DI 2 "arith_operand" "rI"))
+		      (unspec:DI [(match_dup 1) (match_dup 2)] UNSPEC_SUBV)))
+   (set (match_operand:DI 0 "register_operand" "=r")
+	(minus:DI (match_dup 1) (match_dup 2)))]
+  "TARGET_ARCH64"
+  "subcc\t%r1, %2, %0"
+  [(set_attr "type" "compare")])
+
+(define_insn "*cmp_ccv_minus_sltu_set"
+  [(set (reg:CCV CC_REG)
+	(compare:CCV (minus:SI (minus:SI (match_operand:SI 1 "register_or_zero_operand" "rJ")
+					 (match_operand:SI 2 "arith_operand" "rI"))
+			       (ltu:SI (reg:CC CC_REG) (const_int 0)))
+		     (unspec:SI [(minus:SI (match_dup 1) (match_dup 2))
+				 (ltu:SI (reg:CC CC_REG) (const_int 0))]
+				UNSPEC_SUBV)))
+   (set (match_operand:SI 0 "register_operand" "=r")
+	(minus:SI (minus:SI (match_dup 1) (match_dup 2))
+		  (ltu:SI (reg:CC CC_REG) (const_int 0))))]
+  ""
+  "subxcc\t%1, %2, %0"
+  [(set_attr "type" "compare")])
+
 
 ;; Integer multiply/divide instructions.
 
@@ -5127,6 +5579,50 @@ (define_expand "negdi2"
     }
 })
 
+(define_expand "unegvdi3"
+  [(parallel [(set (reg:CCXC CC_REG)
+		   (compare:CCXC (not:DI (match_operand:DI 1 "register_operand" ""))
+				 (const_int -1)))
+	      (set (match_operand:DI 0 "register_operand" "")
+		   (neg:DI (match_dup 1)))])
+   (set (pc)
+        (if_then_else (ltu (reg:CCXC CC_REG) (const_int 0))
+		      (label_ref (match_operand 2 ""))
+		      (pc)))]
+  ""
+{
+  if (!TARGET_64BIT)
+    {
+      emit_insn (gen_unegvdi3_sp32 (operands[0], operands[1]));
+      rtx x = gen_rtx_LTU (VOIDmode, gen_rtx_REG (CCCmode, SPARC_ICC_REG),
+				     const0_rtx);
+      emit_jump_insn (gen_cbranchcc4 (x, XEXP (x, 0), XEXP (x, 1), operands[2]));
+      DONE;
+    }
+})
+
+(define_expand "negvdi3"
+  [(parallel [(set (reg:CCXV CC_REG)
+		   (compare:CCXV (neg:DI (match_operand:DI 1 "register_operand" ""))
+				 (unspec:DI [(match_dup 1)] UNSPEC_NEGV)))
+	      (set (match_operand:DI 0 "register_operand" "")
+		   (neg:DI (match_dup 1)))])
+   (set (pc)
+        (if_then_else (ne (reg:CCXV CC_REG) (const_int 0))
+		      (label_ref (match_operand 2 ""))
+		      (pc)))]
+  ""
+{
+  if (!TARGET_64BIT)
+    {
+      emit_insn (gen_negvdi3_sp32 (operands[0], operands[1]));
+      rtx x = gen_rtx_NE (VOIDmode, gen_rtx_REG (CCVmode, SPARC_ICC_REG),
+				    const0_rtx);
+      emit_jump_insn (gen_cbranchcc4 (x, XEXP (x, 0), XEXP (x, 1), operands[2]));
+      DONE;
+    }
+})
+
 (define_insn_and_split "negdi2_sp32"
   [(set (match_operand:DI 0 "register_operand" "=&r")
 	(neg:DI (match_operand:DI 1 "register_operand" "r")))
@@ -5145,6 +5641,64 @@ (define_insn_and_split "negdi2_sp32"
    operands[5] = gen_lowpart (SImode, operands[1]);"
   [(set_attr "length" "2")])
 
+(define_insn_and_split "unegvdi3_sp32"
+  [(set (reg:CCC CC_REG)
+	(compare:CCC (not:DI (match_operand:DI 1 "register_operand" "r"))
+		     (const_int -1)))
+   (set (match_operand:DI 0 "register_operand" "=&r")
+	(neg:DI (match_dup 1)))]
+  "!TARGET_ARCH64"
+  "#"
+  "&& reload_completed"
+  [(parallel [(set (reg:CCC CC_REG)
+                   (compare:CCC (not:SI (match_dup 5)) (const_int -1)))
+              (set (match_dup 4) (neg:SI (match_dup 5)))])
+   (parallel [(set (reg:CCC CC_REG)
+		   (compare:CCC (zero_extend:DI
+				  (neg:SI (plus:SI (match_dup 3)
+						   (ltu:SI (reg:CCC CC_REG)
+							   (const_int 0)))))
+				(neg:DI (plus:DI (zero_extend:DI (match_dup 3))
+						 (ltu:DI (reg:CCC CC_REG)
+							 (const_int 0))))))
+	      (set (match_dup 2) (neg:SI (plus:SI (match_dup 3)
+						  (ltu:SI (reg:CCC CC_REG)
+							  (const_int 0)))))])]
+  "operands[2] = gen_highpart (SImode, operands[0]);
+   operands[3] = gen_highpart (SImode, operands[1]);
+   operands[4] = gen_lowpart (SImode, operands[0]);
+   operands[5] = gen_lowpart (SImode, operands[1]);"
+  [(set_attr "length" "2")])
+
+(define_insn_and_split "negvdi3_sp32"
+  [(set (reg:CCV CC_REG)
+	(compare:CCV (neg:DI (match_operand:DI 1 "register_operand" "r"))
+		     (unspec:DI [(match_dup 1)] UNSPEC_NEGV)))
+   (set (match_operand:DI 0 "register_operand" "=&r")
+	(neg:DI (match_dup 1)))]
+  "!TARGET_ARCH64"
+  "#"
+  "&& reload_completed"
+  [(parallel [(set (reg:CCC CC_REG)
+                   (compare:CCC (not:SI (match_dup 5)) (const_int -1)))
+              (set (match_dup 4) (neg:SI (match_dup 5)))])
+   (parallel [(set (reg:CCV CC_REG)
+		   (compare:CCV (neg:SI (plus:SI (match_dup 3)
+						 (ltu:SI (reg:CCC CC_REG)
+							 (const_int 0))))
+				(unspec:SI [(plus:SI (match_dup 3)
+						     (ltu:SI (reg:CCC CC_REG)
+							     (const_int 0)))]
+					   UNSPEC_NEGV)))
+	      (set (match_dup 2) (neg:SI (plus:SI (match_dup 3)
+						  (ltu:SI (reg:CCC CC_REG)
+							  (const_int 0)))))])]
+  "operands[2] = gen_highpart (SImode, operands[0]);
+   operands[3] = gen_highpart (SImode, operands[1]);
+   operands[4] = gen_lowpart (SImode, operands[0]);
+   operands[5] = gen_lowpart (SImode, operands[1]);"
+  [(set_attr "length" "2")])
+
 (define_insn "*negdi2_sp64"
   [(set (match_operand:DI 0 "register_operand" "=r")
 	(neg:DI (match_operand:DI 1 "register_operand" "r")))]
@@ -5157,6 +5711,30 @@ (define_insn "negsi2"
   ""
   "sub\t%%g0, %1, %0")
 
+(define_expand "unegvsi3"
+  [(parallel [(set (reg:CCC CC_REG)
+		   (compare:CCC (not:SI (match_operand:SI 1 "arith_operand" ""))
+				(const_int -1)))
+	      (set (match_operand:SI 0 "register_operand" "")
+		   (neg:SI (match_dup 1)))])
+   (set (pc)
+        (if_then_else (ltu (reg:CCC CC_REG) (const_int 0))
+		      (label_ref (match_operand 2 ""))
+		      (pc)))]
+  "")
+
+(define_expand "negvsi3"
+  [(parallel [(set (reg:CCV CC_REG)
+		   (compare:CCV (neg:SI (match_operand:SI 1 "arith_operand" ""))
+				(unspec:SI [(match_dup 1)] UNSPEC_NEGV)))
+	      (set (match_operand:SI 0 "register_operand" "")
+		   (neg:SI (match_dup 1)))])
+   (set (pc)
+        (if_then_else (ne (reg:CCV CC_REG) (const_int 0))
+		      (label_ref (match_operand 2 ""))
+		      (pc)))]
+"")
+
 (define_insn "*cmp_ccnz_neg"
   [(set (reg:CCNZ CC_REG)
 	(compare:CCNZ (neg:SI (match_operand:SI 0 "arith_operand" "rI"))
@@ -5213,6 +5791,73 @@ (define_insn "*cmp_ccxc_neg_set"
   "subcc\t%%g0, %1, %0"
   [(set_attr "type" "compare")])
 
+(define_insn "*cmp_ccc_neg_sltu_set"
+  [(set (reg:CCC CC_REG)
+	(compare:CCC (zero_extend:DI
+		       (neg:SI (plus:SI (match_operand:SI 1 "arith_operand" "rI")
+				        (ltu:SI (reg:CCC CC_REG)
+						(const_int 0)))))
+		     (neg:DI (plus:DI (zero_extend:DI (match_dup 1))
+				      (ltu:DI (reg:CCC CC_REG)
+					      (const_int 0))))))
+   (set (match_operand:SI 0 "register_operand" "=r")
+	(neg:SI (plus:SI (match_dup 1)
+			 (ltu:SI (reg:CCC CC_REG) (const_int 0)))))]
+  ""
+  "subxcc\t%%g0, %1, %0"
+  [(set_attr "type" "compare")])
+
+(define_insn "*cmp_ccv_neg"
+  [(set (reg:CCV CC_REG)
+	(compare:CCV (neg:SI (match_operand:SI 0 "arith_operand" "rI"))
+		     (unspec:SI [(match_dup 0)] UNSPEC_NEGV)))]
+  ""
+  "subcc\t%%g0, %0, %%g0"
+  [(set_attr "type" "compare")])
+
+(define_insn "*cmp_ccxv_neg"
+  [(set (reg:CCXV CC_REG)
+	(compare:CCXV (neg:DI (match_operand:DI 0 "arith_operand" "rI"))
+		      (unspec:DI [(match_dup 0)] UNSPEC_NEGV)))]
+  "TARGET_ARCH64"
+  "subcc\t%%g0, %0, %%g0"
+  [(set_attr "type" "compare")])
+
+(define_insn "*cmp_ccv_neg_set"
+  [(set (reg:CCV CC_REG)
+	(compare:CCV (neg:SI (match_operand:SI 1 "arith_operand" "rI"))
+		     (unspec:SI [(match_dup 1)] UNSPEC_NEGV)))
+   (set (match_operand:SI 0 "register_operand" "=r")
+	(neg:SI (match_dup 1)))]
+  ""
+  "subcc\t%%g0, %1, %0"
+  [(set_attr "type" "compare")])
+
+(define_insn "*cmp_ccxv_neg_set"
+  [(set (reg:CCXV CC_REG)
+	(compare:CCXV (neg:DI (match_operand:DI 1 "arith_operand" "rI"))
+		      (unspec:DI [(match_dup 1)] UNSPEC_NEGV)))
+   (set (match_operand:DI 0 "register_operand" "=r")
+	(neg:DI (match_dup 1)))]
+  "TARGET_ARCH64"
+  "subcc\t%%g0, %1, %0"
+  [(set_attr "type" "compare")])
+
+(define_insn "*cmp_ccv_neg_sltu_set"
+  [(set (reg:CCV CC_REG)
+	(compare:CCV (neg:SI (plus:SI (match_operand:SI 1 "arith_operand" "rI")
+				      (ltu:SI (reg:CCC CC_REG) (const_int 0))))
+		     (unspec:SI [(plus:SI (match_dup 1)
+				          (ltu:SI (reg:CCC CC_REG)
+						  (const_int 0)))]
+				UNSPEC_NEGV)))
+   (set (match_operand:SI 0 "register_operand" "=r")
+	(neg:SI (plus:SI (match_dup 1)
+			 (ltu:SI (reg:CCC CC_REG) (const_int 0)))))]
+  ""
+  "subxcc\t%%g0, %1, %0"
+  [(set_attr "type" "compare")])
+
 
 (define_insn "one_cmpldi2"
   [(set (match_operand:DI 0 "register_operand" "=r")
Index: testsuite/gcc.target/sparc/overflow-1.c
===================================================================
--- testsuite/gcc.target/sparc/overflow-1.c	(revision 0)
+++ testsuite/gcc.target/sparc/overflow-1.c	(working copy)
@@ -0,0 +1,43 @@
+/* { dg-do compile } */
+/* { dg-options "-O -mcpu=v8" } */
+/* { dg-require-effective-target ilp32 } */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+bool my_uadd_overflow (uint32_t a, uint32_t b, uint32_t *res)
+{
+  return __builtin_add_overflow (a, b, res);
+}
+
+bool my_usub_overflow (uint32_t a, uint32_t b, uint32_t *res)
+{
+  return __builtin_sub_overflow (a, b, res);
+}
+
+bool my_uneg_overflow (uint32_t a, uint32_t *res)
+{
+  return __builtin_sub_overflow (0, a, res);
+}
+
+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);
+}
+
+/* { dg-final { scan-assembler-times "addcc\t%" 2 } } */
+/* { dg-final { scan-assembler-times "subcc\t%" 4 } } */
+/* { dg-final { scan-assembler-times "addx\t%" 3 } } */
+/* { dg-final { scan-assembler-times "bvs" 3 } } */
+/* { dg-final { scan-assembler-not "cmp\t%" } } */
+/* { dg-final { scan-assembler-not "save\t%" } } */
Index: testsuite/gcc.target/sparc/overflow-2.c
===================================================================
--- testsuite/gcc.target/sparc/overflow-2.c	(revision 0)
+++ testsuite/gcc.target/sparc/overflow-2.c	(working copy)
@@ -0,0 +1,46 @@
+/* { dg-do compile } */
+/* { dg-options "-O -mcpu=v8" } */
+/* { dg-require-effective-target ilp32 } */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+bool my_uadd_overflow (uint64_t a, uint64_t b, uint64_t *res)
+{
+  return __builtin_add_overflow (a, b, res);
+}
+
+bool my_usub_overflow (uint64_t a, uint64_t b, uint64_t *res)
+{
+  return __builtin_sub_overflow (a, b, res);
+}
+
+bool my_uneg_overflow (uint64_t a, uint64_t *res)
+{
+  return __builtin_sub_overflow (0, a, res);
+}
+
+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 "addcc\t%" 2 } } */
+/* { dg-final { scan-assembler-times "addxcc\t%" 2 } } */
+/* { dg-final { scan-assembler-times "subcc\t%" 4 } } */
+/* { dg-final { scan-assembler-times "subxcc\t%" 4 } } */
+/* { dg-final { scan-assembler-times "addx\t%" 2 } } */
+/* { dg-final { scan-assembler-times "blu" 1 } } */
+/* { dg-final { scan-assembler-times "bvs" 3 } } */
+/* { dg-final { scan-assembler-not "cmp\t%" } } */
+/* { dg-final { scan-assembler-not "save\t%" } } */
Index: testsuite/gcc.target/sparc/overflow-3.c
===================================================================
--- testsuite/gcc.target/sparc/overflow-3.c	(revision 0)
+++ testsuite/gcc.target/sparc/overflow-3.c	(working copy)
@@ -0,0 +1,44 @@
+/* { dg-do compile } */
+/* { dg-options "-O" } */
+/* { dg-require-effective-target lp64 } */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+bool my_uadd_overflow (uint64_t a, uint64_t b, uint64_t *res)
+{
+  return __builtin_add_overflow (a, b, res);
+}
+
+bool my_usub_overflow (uint64_t a, uint64_t b, uint64_t *res)
+{
+  return __builtin_sub_overflow (a, b, res);
+}
+
+bool my_uneg_overflow (uint64_t a, uint64_t *res)
+{
+  return __builtin_sub_overflow (0, a, res);
+}
+
+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 "addcc\t%" 2 } } */
+/* { dg-final { scan-assembler-times "subcc\t%" 4 } } */
+/* { dg-final { scan-assembler-times "movlu\t%" 1 } } */
+/* { dg-final { scan-assembler-times "blu" 2 } } */
+/* { dg-final { scan-assembler-times "bvs" 3 } } */
+/* { dg-final { scan-assembler-not "cmp\t%" } } */
+/* { dg-final { scan-assembler-not "save\t%" } } */

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [SPARC] Add support for overflow arithmetic
  2016-10-21  9:18 [SPARC] Add support for overflow arithmetic Eric Botcazou
@ 2016-10-21  9:48 ` Jakub Jelinek
  2016-10-28 17:28   ` Eric Botcazou
  0 siblings, 1 reply; 4+ messages in thread
From: Jakub Jelinek @ 2016-10-21  9:48 UTC (permalink / raw)
  To: Eric Botcazou; +Cc: gcc-patches

On Fri, Oct 21, 2016 at 11:18:31AM +0200, Eric Botcazou wrote:
>       /* For sub-word operations, if target doesn't have them, start
> 	 with precres widening right away, otherwise do it only
> 	 if the most simple cases can't be used.  */
>       if (WORD_REGISTER_OPERATIONS
> 	  && orig_precres == precres
> 	  && precres < BITS_PER_WORD)
> 	;
> 
> Jakub, any idea of an elegant way to address this issue?

Then to some extent defining WORD_REGISTER_OPERATIONS on SPARC is a lie,
it only has "INT_REGISTER_OPERATIONS", i.e. all operations smaller than
int are performed on the whole register, int operations can be really done
in SImode in the IL (no need to sign/zero extend anything to DImode, if you
just ignore the high 32 bits).
Guess easiest would be to add some targetm constant or hook that gives
you bit precision - integral arithmetics smaller than this precision is
performed in precision.  Then define it by default to
#ifdef WORD_REGISTER_OPERATIONS
  BITS_PER_WORD
#else
  BITS_PER_UNIT
#endif
and for sparc set to 32, then use this targetm constant or hook in
internal-fn.c instead of WORD_REGISTER_OPERATIONS and BITS_PER_WORD.

	Jakub

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [SPARC] Add support for overflow arithmetic
  2016-10-21  9:48 ` Jakub Jelinek
@ 2016-10-28 17:28   ` Eric Botcazou
  2016-10-28 18:51     ` Jakub Jelinek
  0 siblings, 1 reply; 4+ messages in thread
From: Eric Botcazou @ 2016-10-28 17:28 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 2360 bytes --]

> Then to some extent defining WORD_REGISTER_OPERATIONS on SPARC is a lie,
> it only has "INT_REGISTER_OPERATIONS", i.e. all operations smaller than
> int are performed on the whole register, int operations can be really done
> in SImode in the IL (no need to sign/zero extend anything to DImode, if you
> just ignore the high 32 bits).

On the other hand SPARC perfectly matches the documentation:

 -- Macro: WORD_REGISTER_OPERATIONS
     Define this macro to 1 if operations between registers with
     integral mode smaller than a word are always performed on the
     entire register.  Most RISC machines have this property and most
     CISC machines do not.

If you don't define it for SPARC, then you'll never define it!  The macro 
makes it possible to do some optimizations in combine.c and rtlanal.c so it 
looks quite useful.  Note that SPARC is one of the very few RISC targets that 
don't define PROMOTE_MODE for variables since a patch of yours from 1999:
  https://gcc.gnu.org/ml/gcc-patches/1999-12n/msg00202.html
so it's already parameterized to avoid sign/zero-extending to DImode.

> Guess easiest would be to add some targetm constant or hook that gives
> you bit precision - integral arithmetics smaller than this precision is
> performed in precision.  Then define it by default to
> #ifdef WORD_REGISTER_OPERATIONS
>   BITS_PER_WORD
> #else
>   BITS_PER_UNIT
> #endif
> and for sparc set to 32, then use this targetm constant or hook in
> internal-fn.c instead of WORD_REGISTER_OPERATIONS and BITS_PER_WORD.

Thanks for the hint.  The hook is the way to go I think because BITS_PER_WORD 
is not a constant, so the default would not be properly initialized.  Here's a 
tentative patch, I'll add a couple of SPARC-specific testcases if accepted.

Tested on SPARC/Solaris, OK for the mainline?


	* doc/tm.texi.in (Target Macros) Add TARGET_MIN_ARITHMETIC_PRECISION.
	* doc/tm.texi: Regenerate.
	* internal-fn.c (expand_arith_overflow): Rewrite handling of target
	dependent support by means of TARGET_MIN_ARITHMETIC_PRECISION.
	* target.def (min_arithmetic_precision): New hook.
	* targhooks.c (default_min_arithmetic_precision): New function.
	* targhooks.h (default_min_arithmetic_precision): Declare.
	* config/sparc/sparc.c (TARGET_MIN_ARITHMETIC_PRECISION): Define.
	(sparc_min_arithmetic_precision): New function.

-- 
Eric Botcazou

[-- Attachment #2: sparc_overflow-2.diff --]
[-- Type: text/x-patch, Size: 6887 bytes --]

Index: doc/tm.texi
===================================================================
--- doc/tm.texi	(revision 241611)
+++ doc/tm.texi	(working copy)
@@ -10618,6 +10618,23 @@ smaller than a word are always performed
 Most RISC machines have this property and most CISC machines do not.
 @end defmac
 
+@deftypefn {Target Hook} {unsigned int} TARGET_MIN_ARITHMETIC_PRECISION (void)
+On some RISC architectures with 64-bit registers, the processor also
+maintains 32-bit condition codes that make it possible to do real 32-bit
+arithmetic, although the operations are performed on the full registers.
+
+On such architectures, defining this hook to 32 tells the compiler to try
+using 32-bit arithmetical operations setting the condition codes instead
+of doing full 64-bit arithmetic.
+
+More generally, define this hook on RISC architectures if you want the
+compiler to try using arithmetical operations setting the condition codes
+with a precision lower than the word precision.
+
+You need not define this hook if @code{WORD_REGISTER_OPERATIONS} is not
+defined to 1.
+@end deftypefn
+
 @defmac LOAD_EXTEND_OP (@var{mem_mode})
 Define this macro to be a C expression indicating when insns that read
 memory in @var{mem_mode}, an integral mode narrower than a word, set the
Index: doc/tm.texi.in
===================================================================
--- doc/tm.texi.in	(revision 241611)
+++ doc/tm.texi.in	(working copy)
@@ -7575,6 +7575,8 @@ smaller than a word are always performed
 Most RISC machines have this property and most CISC machines do not.
 @end defmac
 
+@hook TARGET_MIN_ARITHMETIC_PRECISION
+
 @defmac LOAD_EXTEND_OP (@var{mem_mode})
 Define this macro to be a C expression indicating when insns that read
 memory in @var{mem_mode}, an integral mode narrower than a word, set the
Index: internal-fn.c
===================================================================
--- internal-fn.c	(revision 241611)
+++ internal-fn.c	(working copy)
@@ -1824,12 +1836,11 @@ expand_arith_overflow (enum tree_code co
 	  return;
 	}
 
-      /* For sub-word operations, if target doesn't have them, start
-	 with precres widening right away, otherwise do it only
-	 if the most simple cases can't be used.  */
-      if (WORD_REGISTER_OPERATIONS
-	  && orig_precres == precres
-	  && precres < BITS_PER_WORD)
+      /* For operations with low precision, if target doesn't have them, start
+	 with precres widening right away, otherwise do it only if the most
+	 simple cases can't be used.  */
+      const int min_precision = targetm.min_arithmetic_precision ();
+      if (orig_precres == precres && precres < min_precision)
 	;
       else if ((uns0_p && uns1_p && unsr_p && prec0 <= precres
 		&& prec1 <= precres)
@@ -1864,7 +1875,7 @@ expand_arith_overflow (enum tree_code co
       /* For sub-word operations, retry with a wider type first.  */
       if (orig_precres == precres && precop <= BITS_PER_WORD)
 	{
-	  int p = WORD_REGISTER_OPERATIONS ? BITS_PER_WORD : precop;
+	  int p = MAX (min_precision, precop);
 	  enum machine_mode m = smallest_mode_for_size (p, MODE_INT);
 	  tree optype = build_nonstandard_integer_type (GET_MODE_PRECISION (m),
 							uns0_p && uns1_p
Index: target.def
===================================================================
--- target.def	(revision 241611)
+++ target.def	(working copy)
@@ -5932,6 +5932,24 @@ comparison code or operands.",
  void, (int *code, rtx *op0, rtx *op1, bool op0_preserve_value),
  default_canonicalize_comparison)
 
+DEFHOOK
+(min_arithmetic_precision,
+ "On some RISC architectures with 64-bit registers, the processor also\n\
+maintains 32-bit condition codes that make it possible to do real 32-bit\n\
+arithmetic, although the operations are performed on the full registers.\n\
+\n\
+On such architectures, defining this hook to 32 tells the compiler to try\n\
+using 32-bit arithmetical operations setting the condition codes instead\n\
+of doing full 64-bit arithmetic.\n\
+\n\
+More generally, define this hook on RISC architectures if you want the\n\
+compiler to try using arithmetical operations setting the condition codes\n\
+with a precision lower than the word precision.\n\
+\n\
+You need not define this hook if @code{WORD_REGISTER_OPERATIONS} is not\n\
+defined to 1.",
+ unsigned int, (void), default_min_arithmetic_precision)
+
 DEFHOOKPOD
 (atomic_test_and_set_trueval,
  "This value should be set if the result written by\
Index: targhooks.c
===================================================================
--- targhooks.c	(revision 241611)
+++ targhooks.c	(working copy)
@@ -2127,4 +2127,12 @@ default_max_noce_ifcvt_seq_cost (edge e)
     return BRANCH_COST (true, predictable_p) * COSTS_N_INSNS (3);
 }
 
+/* Default implementation if TARGET_MIN_ARITHMETIC_PRECISION.  */
+
+unsigned int
+default_min_arithmetic_precision (void)
+{
+  return WORD_REGISTER_OPERATIONS ? BITS_PER_WORD : BITS_PER_UNIT;
+}
+
 #include "gt-targhooks.h"
Index: targhooks.h
===================================================================
--- targhooks.h	(revision 241611)
+++ targhooks.h	(working copy)
@@ -260,7 +260,7 @@ extern void default_setup_incoming_varar
 						  int second_time ATTRIBUTE_UNUSED);
 extern bool default_optab_supported_p (int, machine_mode, machine_mode,
 				       optimization_type);
-
 extern unsigned int default_max_noce_ifcvt_seq_cost (edge);
+extern unsigned int default_min_arithmetic_precision (void);
 
 #endif /* GCC_TARGHOOKS_H */
Index: config/sparc/sparc.c
===================================================================
--- config/sparc/sparc.c	(revision 241611)
+++ config/sparc/sparc.c	(working copy)
@@ -648,6 +648,7 @@ static reg_class_t sparc_secondary_reloa
 static machine_mode sparc_cstore_mode (enum insn_code icode);
 static void sparc_atomic_assign_expand_fenv (tree *, tree *, tree *);
 static bool sparc_fixed_condition_code_regs (unsigned int *, unsigned int *);
+static unsigned int sparc_min_arithmetic_precision (void);
 \f
 #ifdef SUBTARGET_ATTRIBUTE_TABLE
 /* Table of valid machine attributes.  */
@@ -866,6 +867,9 @@ char sparc_hard_reg_printed[8];
 #undef TARGET_FIXED_CONDITION_CODE_REGS
 #define TARGET_FIXED_CONDITION_CODE_REGS sparc_fixed_condition_code_regs
 
+#undef TARGET_MIN_ARITHMETIC_PRECISION
+#define TARGET_MIN_ARITHMETIC_PRECISION sparc_min_arithmetic_precision
+
 #undef TARGET_CUSTOM_FUNCTION_DESCRIPTORS
 #define TARGET_CUSTOM_FUNCTION_DESCRIPTORS 1
 
@@ -2749,6 +2753,14 @@ sparc_fixed_condition_code_regs (unsigne
   return true;
 }
 
+/* Implement TARGET_MIN_ARITHMETIC_PRECISION.  */
+
+static unsigned int
+sparc_min_arithmetic_precision (void)
+{
+  return 32;
+}
+
 /* 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,
    CCFP[E]mode is used.  CCNZmode should be used when the first operand

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [SPARC] Add support for overflow arithmetic
  2016-10-28 17:28   ` Eric Botcazou
@ 2016-10-28 18:51     ` Jakub Jelinek
  0 siblings, 0 replies; 4+ messages in thread
From: Jakub Jelinek @ 2016-10-28 18:51 UTC (permalink / raw)
  To: Eric Botcazou; +Cc: gcc-patches

On Fri, Oct 28, 2016 at 07:27:56PM +0200, Eric Botcazou wrote:
> Thanks for the hint.  The hook is the way to go I think because BITS_PER_WORD 
> is not a constant, so the default would not be properly initialized.  Here's a 
> tentative patch, I'll add a couple of SPARC-specific testcases if accepted.
> 
> Tested on SPARC/Solaris, OK for the mainline?
> 
> 
> 	* doc/tm.texi.in (Target Macros) Add TARGET_MIN_ARITHMETIC_PRECISION.
> 	* doc/tm.texi: Regenerate.
> 	* internal-fn.c (expand_arith_overflow): Rewrite handling of target
> 	dependent support by means of TARGET_MIN_ARITHMETIC_PRECISION.
> 	* target.def (min_arithmetic_precision): New hook.
> 	* targhooks.c (default_min_arithmetic_precision): New function.
> 	* targhooks.h (default_min_arithmetic_precision): Declare.
> 	* config/sparc/sparc.c (TARGET_MIN_ARITHMETIC_PRECISION): Define.
> 	(sparc_min_arithmetic_precision): New function.

Ok, thanks.

	Jakub

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2016-10-28 18:51 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-10-21  9:18 [SPARC] Add support for overflow arithmetic Eric Botcazou
2016-10-21  9:48 ` Jakub Jelinek
2016-10-28 17:28   ` Eric Botcazou
2016-10-28 18:51     ` Jakub Jelinek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).