That's not as straightforward on Visium as on other architectures because the
port doesn't (cannot) expose the CC register before reload, so you need to
emit a regular operation as first instruction in the expander and a branch,
which will be split into a compare and a jump after reload; in other words,
the pattern generates 3 instructions instead of 2 and it's up to the post-
reload compare elimination pass to merge the first instruction and the compare
so as to yield the expected overflow arithmetic operation and the branch.
That's easy for the unsigned arithmetic operations because the comparisons are
simple (result with first operand for PLUS, operands directly for MINUS,
result with 0 for NEG, MULT is not supported) so the pattern matching in the
post-reload compare elimination pass is minimal.
That's more difficult for signed arithmetic operations because the comparisons
are complex (see for example the implementation in the ARM back-end) so the
pattern matching in the post-reload compare elimination pass would IMO be too
ad-hoc and involved; that's why the implementation uses UNSPECs for signed
operations, which means that only one case (UNSPEC) needs to be added to the
pattern matching in the post-reload compare elimination pass.
As a side effect, this both removes the ??? and implements the improvement
mentioned in the following comment of the pass:
/* ??? For the moment we don't handle comparisons for which IN_B
is a register. We accepted these during initial comparison
recognition in order to eliminate duplicate compares.
An improvement here would be to handle x = a - b; if (a cmp b). */
This yields optimal code for 8-bit, 16-bit and 32-bit signed and unsigned
overflow arithmetic operations (except for multiplication).
Tested on visium-elf, applied on the mainline.
2016-10-20 Eric Botcazou <ebotca...@adacore.com>
* compare-elim.c (conforming_compare): Accept UNSPECs.
(find_comparison_dom_walker::before_dom_children): Deal with
instructions both using and killing the flags register.
(equivalent_reg_at_start): New function extracted from...
(try_eliminate_compare): ...here. Use it and add support for
registers and UNSPECs as second operand of the compare.
* config/visium/visium-modes.def (CCV): New.
* config/visium/predicates.md (visium_v_comparison_operator): New.
(visium_branch_operator): Deal with CCV mode.
* config/visium/visium.c (visium_select_cc_mode): Likewise.
(output_cbranch): Likewise.
* config/visium/visium.md (UNSPEC_{ADD,SUB,NEG}V): New constants.
(uaddv<mode>4): New expander.
(addv<mode>4): Likewise.
(add<mode>3_insn_set_carry): New instruction.
(add<mode>3_insn_set_overflow): Likewise.
(addsi3_insn_set_overflow): Likewise.
(usubv<mode>4): New expander.
(subv<mode>4): Likewise.
(sub<mode>3_insn_set_carry): New instruction.
(sub<mode>3_insn_set_overflow): Likewise.
(subsi3_insn_set_overflow): Likewise.
(unegv<mode>3): New expander.
(negv<mode>3): Likewise.
(neg<mode>2_insn_set_overflow): New instruction.
(addv_tst<mode>): Likewise.
(subv_tst<mode>): Likewise.
(negv_tst<mode>): Likewise.
(cbranch<mode>4_addv_insn): New splitter and instruction.
(cbranch<mode>4_subv_insn): Likewise.
(cbranch<mode>4_negv_insn): Likewise.
testsuite/
* gcc.target/visium/overflow8.c: New.
* gcc.target/visium/overflow16.c: Likewise.
* gcc.target/visium/overflow32: Likewise.
--
Eric Botcazou
Index: compare-elim.c
===================================================================
--- compare-elim.c (revision 241326)
+++ compare-elim.c (working copy)
@@ -143,10 +143,20 @@ conforming_compare (rtx_insn *insn)
if (!REG_P (dest) || REGNO (dest) != targetm.flags_regnum)
return NULL;
- if (REG_P (XEXP (src, 0))
- && (REG_P (XEXP (src, 1)) || CONSTANT_P (XEXP (src, 1))))
+ if (!REG_P (XEXP (src, 0)))
+ return NULL;
+
+ if (CONSTANT_P (XEXP (src, 1)) || REG_P (XEXP (src, 1)))
return src;
+ if (GET_CODE (XEXP (src, 1)) == UNSPEC)
+ {
+ for (int i = 0; i < XVECLEN (XEXP (src, 1), 0); i++)
+ if (!REG_P (XVECEXP (XEXP (src, 1), 0, i)))
+ return NULL;
+ return src;
+ }
+
return NULL;
}
@@ -370,21 +380,24 @@ find_comparison_dom_walker::before_dom_c
last_cmp_valid = true;
}
- /* Notice if this instruction kills the flags register. */
- else if (bitmap_bit_p (killed, targetm.flags_regnum))
+ else
{
- /* See if this insn could be the "clobber" that eliminates
- a future comparison. */
- last_clobber = (arithmetic_flags_clobber_p (insn) ? insn : NULL);
-
- /* In either case, the previous compare is no longer valid. */
- last_cmp = NULL;
- last_cmp_valid = false;
- }
+ /* Notice if this instruction uses the flags register. */
+ if (last_cmp)
+ find_flags_uses_in_insn (last_cmp, insn);
- /* Notice if this instruction uses the flags register. */
- else if (last_cmp)
- find_flags_uses_in_insn (last_cmp, insn);
+ /* Notice if this instruction kills the flags register. */
+ if (bitmap_bit_p (killed, targetm.flags_regnum))
+ {
+ /* See if this insn could be the "clobber" that eliminates
+ a future comparison. */
+ last_clobber = (arithmetic_flags_clobber_p (insn) ? insn : NULL);
+
+ /* In either case, the previous compare is no longer valid. */
+ last_cmp = NULL;
+ last_cmp_valid = false;
+ }
+ }
/* Notice if any of the inputs to the comparison have changed. */
if (last_cmp_valid
@@ -507,39 +520,16 @@ maybe_select_cc_mode (struct comparison
return flags;
}
-/* Attempt to replace a comparison with a prior arithmetic insn that can
- compute the same flags value as the comparison itself. Return true if
- successful, having made all rtl modifications necessary. */
+/* Return a register RTX holding the same value at START as REG at END, or
+ NULL_RTX if there is none. */
-static bool
-try_eliminate_compare (struct comparison *cmp)
+static rtx
+equivalent_reg_at_start (rtx reg, rtx_insn *end, rtx_insn *start)
{
- rtx_insn *insn, *bb_head;
- rtx x, flags, in_a, cmp_src;
-
- /* We must have found an interesting "clobber" preceding the compare. */
- if (cmp->prev_clobber == NULL)
- return false;
-
- /* ??? For the moment we don't handle comparisons for which IN_B
- is a register. We accepted these during initial comparison
- recognition in order to eliminate duplicate compares.
- An improvement here would be to handle x = a - b; if (a cmp b). */
- if (!CONSTANT_P (cmp->in_b))
- return false;
-
- /* Verify that IN_A is not clobbered in between CMP and PREV_CLOBBER.
- Given that this target requires this pass, we can assume that most
- insns do clobber the flags, and so the distance between the compare
- and the clobber is likely to be small. */
- /* ??? This is one point at which one could argue that DF_REF_CHAIN would
- be useful, but it is thought to be too heavy-weight a solution here. */
+ rtx_insn *bb_head = BB_HEAD (BLOCK_FOR_INSN (end));
- in_a = cmp->in_a;
- insn = cmp->insn;
- bb_head = BB_HEAD (BLOCK_FOR_INSN (insn));
- for (insn = PREV_INSN (insn);
- insn != cmp->prev_clobber;
+ for (rtx_insn *insn = PREV_INSN (end);
+ insn != start;
insn = PREV_INSN (insn))
{
const int abnormal_flags
@@ -552,13 +542,13 @@ try_eliminate_compare (struct comparison
/* Note that the BB_HEAD is always either a note or a label, but in
any case it means that IN_A is defined outside the block. */
if (insn == bb_head)
- return false;
+ return NULL_RTX;
if (NOTE_P (insn) || DEBUG_INSN_P (insn))
continue;
/* Find a possible def of IN_A in INSN. */
FOR_EACH_INSN_DEF (def, insn)
- if (DF_REF_REGNO (def) == REGNO (in_a))
+ if (DF_REF_REGNO (def) == REGNO (reg))
break;
/* No definitions of IN_A; continue searching. */
@@ -567,35 +557,87 @@ try_eliminate_compare (struct comparison
/* Bail if this is not a totally normal set of IN_A. */
if (DF_REF_IS_ARTIFICIAL (def))
- return false;
+ return NULL_RTX;
if (DF_REF_FLAGS (def) & abnormal_flags)
- return false;
+ return NULL_RTX;
/* We've found an insn between the compare and the clobber that sets
IN_A. Given that pass_cprop_hardreg has not yet run, we still find
situations in which we can usefully look through a copy insn. */
- x = single_set (insn);
- if (x == NULL)
- return false;
- in_a = SET_SRC (x);
- if (!REG_P (in_a))
+ rtx x = single_set (insn);
+ if (x == NULL_RTX)
+ return NULL_RTX;
+ reg = SET_SRC (x);
+ if (!REG_P (reg))
+ return NULL_RTX;
+ }
+
+ return reg;
+}
+
+/* Attempt to replace a comparison with a prior arithmetic insn that can
+ compute the same flags value as the comparison itself. Return true if
+ successful, having made all rtl modifications necessary. */
+
+static bool
+try_eliminate_compare (struct comparison *cmp)
+{
+ rtx x, flags, in_a, in_b, cmp_src;
+
+ /* We must have found an interesting "clobber" preceding the compare. */
+ if (cmp->prev_clobber == NULL)
+ return false;
+
+ /* Verify that IN_A is not clobbered in between CMP and PREV_CLOBBER.
+ Given that this target requires this pass, we can assume that most
+ insns do clobber the flags, and so the distance between the compare
+ and the clobber is likely to be small. */
+ /* ??? This is one point at which one could argue that DF_REF_CHAIN would
+ be useful, but it is thought to be too heavy-weight a solution here. */
+ in_a = equivalent_reg_at_start (cmp->in_a, cmp->insn, cmp->prev_clobber);
+ if (!in_a)
+ return false;
+
+ /* Likewise for IN_B if need be. */
+ if (CONSTANT_P (cmp->in_b))
+ in_b = cmp->in_b;
+ else if (REG_P (cmp->in_b))
+ {
+ in_b = equivalent_reg_at_start (cmp->in_b, cmp->insn, cmp->prev_clobber);
+ if (!in_b)
return false;
}
+ else if (GET_CODE (cmp->in_b) == UNSPEC)
+ {
+ const int len = XVECLEN (cmp->in_b, 0);
+ rtvec v = rtvec_alloc (len);
+ for (int i = 0; i < len; i++)
+ {
+ rtx r = equivalent_reg_at_start (XVECEXP (cmp->in_b, 0, i),
+ cmp->insn, cmp->prev_clobber);
+ if (!r)
+ return false;
+ RTVEC_ELT (v, i) = r;
+ }
+ in_b = gen_rtx_UNSPEC (GET_MODE (cmp->in_b), v, XINT (cmp->in_b, 1));
+ }
+ else
+ gcc_unreachable ();
/* We've reached PREV_CLOBBER without finding a modification of IN_A.
Validate that PREV_CLOBBER itself does in fact refer to IN_A. Do
recall that we've already validated the shape of PREV_CLOBBER. */
+ rtx insn = cmp->prev_clobber;
x = XVECEXP (PATTERN (insn), 0, 0);
if (rtx_equal_p (SET_DEST (x), in_a))
cmp_src = SET_SRC (x);
/* Also check operations with implicit extensions, e.g.:
[(set (reg:DI)
- (zero_extend:DI (plus:SI (reg:SI)(reg:SI))))
+ (zero_extend:DI (plus:SI (reg:SI) (reg:SI))))
(set (reg:CCZ flags)
- (compare:CCZ
- (plus:SI (reg:SI)(reg:SI))
- (const_int 0)))] */
+ (compare:CCZ (plus:SI (reg:SI) (reg:SI))
+ (const_int 0)))] */
else if (REG_P (SET_DEST (x))
&& REG_P (in_a)
&& REGNO (SET_DEST (x)) == REGNO (in_a)
@@ -603,17 +645,29 @@ try_eliminate_compare (struct comparison
|| GET_CODE (SET_SRC (x)) == SIGN_EXTEND)
&& GET_MODE (XEXP (SET_SRC (x), 0)) == GET_MODE (in_a))
cmp_src = XEXP (SET_SRC (x), 0);
+
+ /* Also check fully redundant comparisons, e.g.:
+ [(set (reg:SI)
+ (minus:SI (reg:SI) (reg:SI))))
+ (set (reg:CC flags)
+ (compare:CC (reg:SI) (reg:SI)))] */
+ else if (REG_P (in_b)
+ && GET_CODE (SET_SRC (x)) == MINUS
+ && rtx_equal_p (XEXP (SET_SRC (x), 0), in_a)
+ && rtx_equal_p (XEXP (SET_SRC (x), 1), in_b))
+ cmp_src = in_a;
+
else
return false;
/* Determine if we ought to use a different CC_MODE here. */
- flags = maybe_select_cc_mode (cmp, cmp_src, cmp->in_b);
+ flags = maybe_select_cc_mode (cmp, cmp_src, in_b);
if (flags == NULL)
flags = gen_rtx_REG (cmp->orig_mode, targetm.flags_regnum);
/* Generate a new comparison for installation in the setter. */
x = copy_rtx (cmp_src);
- x = gen_rtx_COMPARE (GET_MODE (flags), x, cmp->in_b);
+ x = gen_rtx_COMPARE (GET_MODE (flags), x, in_b);
x = gen_rtx_SET (flags, x);
/* Succeed if the new instruction is valid. Note that we may have started
Index: config/visium/predicates.md
===================================================================
--- config/visium/predicates.md (revision 241326)
+++ config/visium/predicates.md (working copy)
@@ -131,13 +131,17 @@ (define_predicate "visium_equality_compa
(match_code "eq,ne"))
;; Return true if OP is a valid comparison operator for CCNZmode.
-(define_special_predicate "visium_nz_comparison_operator"
+(define_predicate "visium_nz_comparison_operator"
(match_code "eq,ne,lt,ge"))
;; Return true if OP is a valid comparison operator for CCCmode.
-(define_special_predicate "visium_c_comparison_operator"
+(define_predicate "visium_c_comparison_operator"
(match_code "eq,ne,ltu,geu"))
+;; Return true if OP is a valid comparison operator for CCVmode.
+(define_predicate "visium_v_comparison_operator"
+ (match_code "eq,ne"))
+
;; Return true if OP is a valid FP comparison operator.
(define_predicate "visium_fp_comparison_operator"
(match_code "eq,ne,ordered,unordered,unlt,unle,ungt,unge,lt,le,gt,ge"))
@@ -155,6 +159,8 @@ (define_predicate "visium_branch_operato
return visium_nz_comparison_operator (op, mode);
case CCCmode:
return visium_c_comparison_operator (op, mode);
+ case CCVmode:
+ return visium_v_comparison_operator (op, mode);
case CCFPmode:
case CCFPEmode:
return visium_fp_comparison_operator (op, mode);
Index: config/visium/visium-modes.def
===================================================================
--- config/visium/visium-modes.def (revision 241326)
+++ config/visium/visium-modes.def (working copy)
@@ -29,6 +29,10 @@ along with GCC; see the file COPYING3.
instruction. Only the =,!= and 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 condition code is set by the
the floating-point unit. One for comparisons which generate an exception
if the result is unordered (CCFPEmode) and one for comparisons which never
@@ -36,5 +40,6 @@ along with GCC; see the file COPYING3.
CC_MODE (CCNZ);
CC_MODE (CCC);
+CC_MODE (CCV);
CC_MODE (CCFP);
CC_MODE (CCFPE);
Index: config/visium/visium.c
===================================================================
--- config/visium/visium.c (revision 241326)
+++ config/visium/visium.c (working copy)
@@ -2833,6 +2833,14 @@ visium_select_cc_mode (enum rtx_code cod
&& rtx_equal_p (XEXP (op0, 0), op1))
return CCCmode;
+ /* This is for the {add,sub,neg}<mode>3_insn_set_overflow pattern. */
+ if ((code == EQ || code == NE)
+ && GET_CODE (op1) == UNSPEC
+ && (XINT (op1, 1) == UNSPEC_ADDV
+ || XINT (op1, 1) == UNSPEC_SUBV
+ || XINT (op1, 1) == UNSPEC_NEGV))
+ return CCVmode;
+
if (op1 != const0_rtx)
return CCmode;
@@ -3101,6 +3109,8 @@ output_cbranch (rtx label, enum rtx_code
case NE:
if (cc_mode == CCCmode)
cond = "cs";
+ else if (cc_mode == CCVmode)
+ cond = "os";
else
cond = "ne";
break;
@@ -3108,6 +3118,8 @@ output_cbranch (rtx label, enum rtx_code
case EQ:
if (cc_mode == CCCmode)
cond = "cc";
+ else if (cc_mode == CCVmode)
+ cond = "oc";
else
cond = "eq";
break;
Index: config/visium/visium.md
===================================================================
--- config/visium/visium.md (revision 241326)
+++ config/visium/visium.md (working copy)
@@ -81,6 +81,9 @@ (define_c_enum "unspec" [
UNSPEC_ITOF
UNSPEC_FTOI
UNSPEC_NOP
+ UNSPEC_ADDV
+ UNSPEC_SUBV
+ UNSPEC_NEGV
])
;; UNSPEC_VOLATILE usage.
@@ -745,6 +748,27 @@ (define_expand "add<mode>3"
(match_operand:QHI 2 "register_operand" "")))]
"")
+(define_expand "uaddv<mode>4"
+ [(set (match_operand:I 0 "register_operand" "")
+ (plus:I (match_operand:I 1 "register_operand" "")
+ (match_operand:I 2 "register_operand" "")))
+ (set (pc)
+ (if_then_else (ltu (match_dup 0) (match_dup 1))
+ (label_ref (match_operand 3 ""))
+ (pc)))]
+ "")
+
+(define_expand "addv<mode>4"
+ [(set (match_operand:I 0 "register_operand" "")
+ (plus:I (match_operand:I 1 "register_operand" "")
+ (match_operand:I 2 "register_operand" "")))
+ (set (pc)
+ (if_then_else (ne (match_dup 0)
+ (unspec:I [(match_dup 1) (match_dup 2)] UNSPEC_ADDV))
+ (label_ref (match_operand 3 ""))
+ (pc)))]
+ "")
+
(define_insn_and_split "*add<mode>3_insn"
[(set (match_operand:QHI 0 "register_operand" "=r")
(plus:QHI (match_operand:QHI 1 "register_operand" "%r")
@@ -767,6 +791,28 @@ (define_insn "*add<mode>3_insn<subst_ari
"add<s> %0,%1,%2"
[(set_attr "type" "arith")])
+(define_insn "*add<mode>3_insn_set_carry"
+ [(set (match_operand:QHI 0 "register_operand" "=r")
+ (plus:QHI (match_operand:QHI 1 "register_operand" "%r")
+ (match_operand:QHI 2 "register_operand" "r")))
+ (set (reg:CCC R_FLAGS)
+ (compare:CCC (plus:QHI (match_dup 1) (match_dup 2))
+ (match_dup 1)))]
+ "reload_completed"
+ "add<s> %0,%1,%2"
+ [(set_attr "type" "arith")])
+
+(define_insn "*add<mode>3_insn_set_overflow"
+ [(set (match_operand:QHI 0 "register_operand" "=r")
+ (plus:QHI (match_operand:QHI 1 "register_operand" "%r")
+ (match_operand:QHI 2 "register_operand" "r")))
+ (set (reg:CCV R_FLAGS)
+ (compare:CCV (plus:QHI (match_dup 1) (match_dup 2))
+ (unspec:QHI [(match_dup 1) (match_dup 2)] UNSPEC_ADDV)))]
+ "reload_completed"
+ "add<s> %0,%1,%2"
+ [(set_attr "type" "arith")])
+
(define_expand "addsi3"
[(set (match_operand:SI 0 "register_operand" "")
(plus:SI (match_operand:SI 1 "register_operand" "")
@@ -822,6 +868,19 @@ (define_insn "addsi3_insn_set_carry"
addi %0,%2"
[(set_attr "type" "arith")])
+(define_insn "*addsi3_insn_set_overflow"
+ [(set (match_operand:SI 0 "register_operand" "=r,r")
+ (plus:SI (match_operand:SI 1 "register_operand" "%r,0")
+ (match_operand:SI 2 "real_add_operand" " r,J")))
+ (set (reg:CCV R_FLAGS)
+ (compare:CCV (plus:SI (match_dup 1) (match_dup 2))
+ (unspec:SI [(match_dup 1) (match_dup 2)] UNSPEC_ADDV)))]
+ "reload_completed"
+ "@
+ add.l %0,%1,%2
+ addi %0,%2"
+ [(set_attr "type" "arith")])
+
(define_expand "adddi3"
[(set (match_operand:DI 0 "register_operand" "")
(plus:DI (match_operand:DI 1 "register_operand" "")
@@ -897,6 +956,34 @@ (define_expand "sub<mode>3"
(match_operand:QHI 2 "register_operand" "")))]
"")
+(define_expand "usubv<mode>4"
+ [(set (match_operand:I 0 "register_operand" "")
+ (minus:I (match_operand:I 1 "reg_or_0_operand" "")
+ (match_operand:I 2 "register_operand" "")))
+ (set (pc)
+ (if_then_else (ltu (match_dup 1) (match_dup 2))
+ (label_ref (match_operand 3 ""))
+ (pc)))]
+ ""
+{
+ if (operands[1] == const0_rtx)
+ {
+ emit_insn (gen_unegv<mode>3 (operands[0], operands[2], operands[3]));
+ DONE;
+ }
+})
+
+(define_expand "subv<mode>4"
+ [(set (match_operand:I 0 "register_operand" "")
+ (minus:I (match_operand:I 1 "register_operand" "")
+ (match_operand:I 2 "register_operand" "")))
+ (set (pc)
+ (if_then_else (ne (match_dup 0)
+ (unspec:I [(match_dup 1) (match_dup 2)] UNSPEC_SUBV))
+ (label_ref (match_operand 3 ""))
+ (pc)))]
+ "")
+
(define_insn_and_split "*sub<mode>3_insn"
[(set (match_operand:QHI 0 "register_operand" "=r")
(minus:QHI (match_operand:QHI 1 "reg_or_0_operand" "rO")
@@ -919,6 +1006,27 @@ (define_insn "*sub<mode>3_insn<subst_ari
"sub<s> %0,%r1,%2"
[(set_attr "type" "arith")])
+(define_insn "*sub<mode>3_insn_set_carry"
+ [(set (match_operand:QHI 0 "register_operand" "=r")
+ (minus:QHI (match_operand:QHI 1 "reg_or_0_operand" "rO")
+ (match_operand:QHI 2 "register_operand" "r")))
+ (set (reg:CC R_FLAGS)
+ (compare:CC (match_dup 1) (match_dup 2)))]
+ "reload_completed"
+ "sub<s> %0,%r1,%2"
+ [(set_attr "type" "arith")])
+
+(define_insn "*sub<mode>3_insn_set_overflow"
+ [(set (match_operand:QHI 0 "register_operand" "=r")
+ (minus:QHI (match_operand:QHI 1 "reg_or_0_operand" "rO")
+ (match_operand:QHI 2 "register_operand" "r")))
+ (set (reg:CCV R_FLAGS)
+ (compare:CCV (minus:QHI (match_dup 1) (match_dup 2))
+ (unspec:QHI [(match_dup 1) (match_dup 2)] UNSPEC_SUBV)))]
+ "reload_completed"
+ "sub<s> %0,%r1,%2"
+ [(set_attr "type" "arith")])
+
(define_expand "subsi3"
[(set (match_operand:SI 0 "register_operand" "")
(minus:SI (match_operand:SI 1 "reg_or_0_operand" "")
@@ -973,6 +1081,19 @@ (define_insn "subsi3_insn_set_carry"
subi %0,%2"
[(set_attr "type" "arith")])
+(define_insn "*subsi3_insn_set_overflow"
+ [(set (match_operand:SI 0 "register_operand" "=r,r")
+ (minus:SI (match_operand:SI 1 "register_operand" " r,0")
+ (match_operand:SI 2 "real_add_operand" " r,J")))
+ (set (reg:CCV R_FLAGS)
+ (compare:CCV (minus:SI (match_dup 1) (match_dup 2))
+ (unspec:SI [(match_dup 1) (match_dup 2)] UNSPEC_SUBV)))]
+ "reload_completed"
+ "@
+ sub.l %0,%1,%2
+ subi %0,%2"
+ [(set_attr "type" "arith")])
+
(define_expand "subdi3"
[(set (match_operand:DI 0 "register_operand" "")
(minus:DI (match_operand:DI 1 "register_operand" "")
@@ -1047,6 +1168,25 @@ (define_expand "neg<mode>2"
(neg:I (match_operand:I 1 "register_operand" "")))]
"")
+(define_expand "unegv<mode>3"
+ [(set (match_operand:I 0 "register_operand" "")
+ (neg:I (match_operand:I 1 "register_operand" "")))
+ (set (pc)
+ (if_then_else (ne (match_dup 0) (const_int 0))
+ (label_ref (match_operand 2 ""))
+ (pc)))]
+ "")
+
+(define_expand "negv<mode>3"
+ [(set (match_operand:I 0 "register_operand" "")
+ (neg:I (match_operand:I 1 "register_operand" "")))
+ (set (pc)
+ (if_then_else (ne (match_dup 0)
+ (unspec:I [(match_dup 1)] UNSPEC_NEGV))
+ (label_ref (match_operand 2 ""))
+ (pc)))]
+ "")
+
(define_insn_and_split "*neg<mode>2_insn"
[(set (match_operand:I 0 "register_operand" "=r")
(neg:I (match_operand:I 1 "register_operand" "r")))]
@@ -1075,6 +1215,16 @@ (define_insn "negsi2_insn_set_carry"
"sub.l %0,r0,%1"
[(set_attr "type" "arith")])
+(define_insn "*neg<mode>2_insn_set_overflow"
+ [(set (match_operand:I 0 "register_operand" "=r")
+ (neg:I (match_operand:I 1 "register_operand" "r")))
+ (set (reg:CCV R_FLAGS)
+ (compare:CCV (neg:I (match_dup 1))
+ (unspec:I [(match_dup 1)] UNSPEC_NEGV)))]
+ "reload_completed"
+ "sub<s> %0,r0,%1"
+ [(set_attr "type" "arith")])
+
(define_expand "negdi2"
[(set (match_operand:DI 0 "register_operand" "")
(neg:DI (match_operand:DI 1 "register_operand" "")))]
@@ -1850,6 +2000,45 @@ (define_insn "*btst"
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
+;; Integer overflow tests
+;;
+;; Modes QI, HI and SI are supported directly.
+;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+
+(define_insn "*addv_tst<mode>"
+ [(set (reg:CCV R_FLAGS)
+ (compare:CCV (match_operand:I 0 "register_operand" "r")
+ (unspec:I [(match_operand:I 1 "register_operand" "%r")
+ (match_operand:I 2 "register_operand" "r")]
+ UNSPEC_ADDV)))]
+ "reload_completed"
+ "add<s> r0,%1,%2"
+ [(set_attr "type" "arith")])
+
+(define_insn "*subv_tst<mode>"
+ [(set (reg:CCV R_FLAGS)
+ (compare:CCV (match_operand:I 0 "register_operand" "r")
+ (unspec:I [(match_operand:I 1 "reg_or_0_operand" "rO")
+ (match_operand:I 2 "register_operand" "r")]
+ UNSPEC_SUBV)))]
+ "reload_completed"
+ "sub<s> r0,%r1,%2"
+ [(set_attr "type" "arith")])
+
+(define_insn "*negv_tst<mode>"
+ [(set (reg:CCV R_FLAGS)
+ (compare:CCV (match_operand:I 0 "register_operand" "r")
+ (unspec:I [(match_operand:I 1 "register_operand" "r")]
+ UNSPEC_NEGV)))]
+ "reload_completed"
+ "sub<s> r0,r0,%1"
+ [(set_attr "type" "arith")])
+
+;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
;; Integer comparisons
;;
;; Modes QI, HI and SI are supported directly.
@@ -2124,6 +2313,65 @@ (define_insn_and_split "*cbranch<mode>4_
DONE;
}
[(set_attr "type" "cmp")])
+
+(define_insn_and_split "*cbranch<mode>4_addv_insn"
+ [(set (pc)
+ (if_then_else (match_operator 0 "visium_equality_comparison_operator"
+ [(match_operand:I 1 "register_operand" "r")
+ (unspec:I [(match_operand:I 2 "register_operand" "%r")
+ (match_operand:I 3 "register_operand" "r")]
+ UNSPEC_ADDV)])
+ (label_ref (match_operand 4 ""))
+ (pc)))]
+ ""
+ "#"
+ "reload_completed"
+ [(const_int 0)]
+{
+ visium_split_cbranch (GET_CODE (operands[0]), XEXP (operands[0], 0),
+ XEXP (operands[0], 1), operands[4]);
+ DONE;
+}
+ [(set_attr "type" "cmp")])
+
+(define_insn_and_split "*cbranch<mode>4_subv_insn"
+ [(set (pc)
+ (if_then_else (match_operator 0 "visium_equality_comparison_operator"
+ [(match_operand:I 1 "register_operand" "r")
+ (unspec:I [(match_operand:I 2 "reg_or_0_operand" "rO")
+ (match_operand:I 3 "register_operand" "r")]
+ UNSPEC_SUBV)])
+ (label_ref (match_operand 4 ""))
+ (pc)))]
+ ""
+ "#"
+ "reload_completed"
+ [(const_int 0)]
+{
+ visium_split_cbranch (GET_CODE (operands[0]), XEXP (operands[0], 0),
+ XEXP (operands[0], 1), operands[4]);
+ DONE;
+}
+ [(set_attr "type" "cmp")])
+
+(define_insn_and_split "*cbranch<mode>4_negv_insn"
+ [(set (pc)
+ (if_then_else (match_operator 0 "visium_equality_comparison_operator"
+ [(match_operand:I 1 "register_operand" "r")
+ (unspec:I [(match_operand:I 2 "register_operand" "r")]
+ UNSPEC_NEGV)])
+ (label_ref (match_operand 3 ""))
+ (pc)))]
+ ""
+ "#"
+ "reload_completed"
+ [(const_int 0)]
+{
+ visium_split_cbranch (GET_CODE (operands[0]), XEXP (operands[0], 0),
+ XEXP (operands[0], 1), operands[3]);
+ DONE;
+}
+ [(set_attr "type" "cmp")])
(define_insn_and_split "*cbranchsi4_btst_insn"
[(set (pc)
Index: testsuite/gcc.target/visium/overflow16.c
===================================================================
--- testsuite/gcc.target/visium/overflow16.c (revision 0)
+++ testsuite/gcc.target/visium/overflow16.c (working copy)
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+#include <stdbool.h>
+
+bool my_uadd_overflow (unsigned short a, unsigned short b, unsigned short *res)
+{
+ return __builtin_add_overflow (a, b, res);
+}
+
+bool my_usub_overflow (unsigned short a, unsigned short b, unsigned short *res)
+{
+ return __builtin_sub_overflow (a, b, res);
+}
+
+bool my_uneg_overflow (unsigned short a, unsigned short *res)
+{
+ return __builtin_sub_overflow (0, a, res);
+}
+
+bool my_add_overflow (short a, short b, short *res)
+{
+ return __builtin_add_overflow (a, b, res);
+}
+
+bool my_sub_overflow (short a, short b, short *res)
+{
+ return __builtin_sub_overflow (a, b, res);
+}
+
+bool my_neg_overflow (short a, short *res)
+{
+ return __builtin_sub_overflow (0, a, res);
+}
+
+/* { dg-final { scan-assembler-times "add.w" 2 } } */
+/* { dg-final { scan-assembler-times "sub.w" 4 } } */
+/* { dg-final { scan-assembler-not "cmp.w" } } */
+/* { dg-final { scan-assembler-not "mov.w" } } */
Index: testsuite/gcc.target/visium/overflow32.c
===================================================================
--- testsuite/gcc.target/visium/overflow32.c (revision 0)
+++ testsuite/gcc.target/visium/overflow32.c (working copy)
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+#include <stdbool.h>
+
+bool my_uadd_overflow (unsigned int a, unsigned int b, unsigned int *res)
+{
+ return __builtin_add_overflow (a, b, res);
+}
+
+bool my_usub_overflow (unsigned int a, unsigned int b, unsigned int *res)
+{
+ return __builtin_sub_overflow (a, b, res);
+}
+
+bool my_uneg_overflow (unsigned int a, unsigned int *res)
+{
+ return __builtin_sub_overflow (0, a, res);
+}
+
+bool my_add_overflow (int a, int b, int *res)
+{
+ return __builtin_add_overflow (a, b, res);
+}
+
+bool my_sub_overflow (int a, int b, int *res)
+{
+ return __builtin_sub_overflow (a, b, res);
+}
+
+bool my_neg_overflow (int a, int *res)
+{
+ return __builtin_sub_overflow (0, a, res);
+}
+
+/* { dg-final { scan-assembler-times "add.l" 2 } } */
+/* { dg-final { scan-assembler-times "sub.l" 4 } } */
+/* { dg-final { scan-assembler-not "cmp.l" } } */
+/* { dg-final { scan-assembler-not "mov.l" } } */
Index: testsuite/gcc.target/visium/overflow8.c
===================================================================
--- testsuite/gcc.target/visium/overflow8.c (revision 0)
+++ testsuite/gcc.target/visium/overflow8.c (working copy)
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+#include <stdbool.h>
+
+bool my_uadd_overflow (unsigned char a, unsigned char b, unsigned char *res)
+{
+ return __builtin_add_overflow (a, b, res);
+}
+
+bool my_usub_overflow (unsigned char a, unsigned char b, unsigned char *res)
+{
+ return __builtin_sub_overflow (a, b, res);
+}
+
+bool my_uneg_overflow (unsigned char a, unsigned char *res)
+{
+ return __builtin_sub_overflow (0, a, res);
+}
+
+bool my_add_overflow (signed char a, signed char b, signed char *res)
+{
+ return __builtin_add_overflow (a, b, res);
+}
+
+bool my_sub_overflow (signed char a, signed char b, signed char *res)
+{
+ return __builtin_sub_overflow (a, b, res);
+}
+
+bool my_neg_overflow (signed char a, signed char *res)
+{
+ return __builtin_sub_overflow (0, a, res);
+}
+
+/* { dg-final { scan-assembler-times "add.b" 2 } } */
+/* { dg-final { scan-assembler-times "sub.b" 4 } } */
+/* { dg-final { scan-assembler-not "cmp.b" } } */
+/* { dg-final { scan-assembler-not "mov.b" } } */