Commit 2c35bbe1 by Eric Botcazou Committed by Eric Botcazou

compare-elim.c (conforming_compare): Accept UNSPECs.

	* 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.

From-SVN: r241379
parent 40b8428e
2016-10-20 Eric Botcazou <ebotcazou@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.
2016-10-20 Richard Biener <rguenther@suse.de> 2016-10-20 Richard Biener <rguenther@suse.de>
* tree-ssa-alias.c (ptrs_compare_unequal): Remove code duplication. * tree-ssa-alias.c (ptrs_compare_unequal): Remove code duplication.
......
...@@ -143,10 +143,20 @@ conforming_compare (rtx_insn *insn) ...@@ -143,10 +143,20 @@ conforming_compare (rtx_insn *insn)
if (!REG_P (dest) || REGNO (dest) != targetm.flags_regnum) if (!REG_P (dest) || REGNO (dest) != targetm.flags_regnum)
return NULL; return NULL;
if (REG_P (XEXP (src, 0)) if (!REG_P (XEXP (src, 0)))
&& (REG_P (XEXP (src, 1)) || CONSTANT_P (XEXP (src, 1)))) return NULL;
if (CONSTANT_P (XEXP (src, 1)) || REG_P (XEXP (src, 1)))
return src; 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; return NULL;
} }
...@@ -370,21 +380,24 @@ find_comparison_dom_walker::before_dom_children (basic_block bb) ...@@ -370,21 +380,24 @@ find_comparison_dom_walker::before_dom_children (basic_block bb)
last_cmp_valid = true; last_cmp_valid = true;
} }
/* Notice if this instruction kills the flags register. */ else
else if (bitmap_bit_p (killed, targetm.flags_regnum))
{ {
/* See if this insn could be the "clobber" that eliminates /* Notice if this instruction uses the flags register. */
a future comparison. */ if (last_cmp)
last_clobber = (arithmetic_flags_clobber_p (insn) ? insn : NULL); find_flags_uses_in_insn (last_cmp, insn);
/* In either case, the previous compare is no longer valid. */ /* Notice if this instruction kills the flags register. */
last_cmp = NULL; if (bitmap_bit_p (killed, targetm.flags_regnum))
last_cmp_valid = false; {
} /* See if this insn could be the "clobber" that eliminates
a future comparison. */
last_clobber = (arithmetic_flags_clobber_p (insn) ? insn : NULL);
/* Notice if this instruction uses the flags register. */ /* In either case, the previous compare is no longer valid. */
else if (last_cmp) last_cmp = NULL;
find_flags_uses_in_insn (last_cmp, insn); last_cmp_valid = false;
}
}
/* Notice if any of the inputs to the comparison have changed. */ /* Notice if any of the inputs to the comparison have changed. */
if (last_cmp_valid if (last_cmp_valid
...@@ -507,39 +520,16 @@ maybe_select_cc_mode (struct comparison *cmp, rtx a ATTRIBUTE_UNUSED, ...@@ -507,39 +520,16 @@ maybe_select_cc_mode (struct comparison *cmp, rtx a ATTRIBUTE_UNUSED,
return flags; return flags;
} }
/* Attempt to replace a comparison with a prior arithmetic insn that can /* Return a register RTX holding the same value at START as REG at END, or
compute the same flags value as the comparison itself. Return true if NULL_RTX if there is none. */
successful, having made all rtl modifications necessary. */
static bool static rtx
try_eliminate_compare (struct comparison *cmp) equivalent_reg_at_start (rtx reg, rtx_insn *end, rtx_insn *start)
{ {
rtx_insn *insn, *bb_head; rtx_insn *bb_head = BB_HEAD (BLOCK_FOR_INSN (end));
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. */
in_a = cmp->in_a; for (rtx_insn *insn = PREV_INSN (end);
insn = cmp->insn; insn != start;
bb_head = BB_HEAD (BLOCK_FOR_INSN (insn));
for (insn = PREV_INSN (insn);
insn != cmp->prev_clobber;
insn = PREV_INSN (insn)) insn = PREV_INSN (insn))
{ {
const int abnormal_flags const int abnormal_flags
...@@ -552,13 +542,13 @@ try_eliminate_compare (struct comparison *cmp) ...@@ -552,13 +542,13 @@ try_eliminate_compare (struct comparison *cmp)
/* Note that the BB_HEAD is always either a note or a label, but in /* 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. */ any case it means that IN_A is defined outside the block. */
if (insn == bb_head) if (insn == bb_head)
return false; return NULL_RTX;
if (NOTE_P (insn) || DEBUG_INSN_P (insn)) if (NOTE_P (insn) || DEBUG_INSN_P (insn))
continue; continue;
/* Find a possible def of IN_A in INSN. */ /* Find a possible def of IN_A in INSN. */
FOR_EACH_INSN_DEF (def, insn) FOR_EACH_INSN_DEF (def, insn)
if (DF_REF_REGNO (def) == REGNO (in_a)) if (DF_REF_REGNO (def) == REGNO (reg))
break; break;
/* No definitions of IN_A; continue searching. */ /* No definitions of IN_A; continue searching. */
...@@ -567,35 +557,87 @@ try_eliminate_compare (struct comparison *cmp) ...@@ -567,35 +557,87 @@ try_eliminate_compare (struct comparison *cmp)
/* Bail if this is not a totally normal set of IN_A. */ /* Bail if this is not a totally normal set of IN_A. */
if (DF_REF_IS_ARTIFICIAL (def)) if (DF_REF_IS_ARTIFICIAL (def))
return false; return NULL_RTX;
if (DF_REF_FLAGS (def) & abnormal_flags) 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 /* 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 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. */ situations in which we can usefully look through a copy insn. */
x = single_set (insn); rtx x = single_set (insn);
if (x == NULL) if (x == NULL_RTX)
return false; return NULL_RTX;
in_a = SET_SRC (x); reg = SET_SRC (x);
if (!REG_P (in_a)) 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; 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. /* 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 Validate that PREV_CLOBBER itself does in fact refer to IN_A. Do
recall that we've already validated the shape of PREV_CLOBBER. */ recall that we've already validated the shape of PREV_CLOBBER. */
rtx insn = cmp->prev_clobber;
x = XVECEXP (PATTERN (insn), 0, 0); x = XVECEXP (PATTERN (insn), 0, 0);
if (rtx_equal_p (SET_DEST (x), in_a)) if (rtx_equal_p (SET_DEST (x), in_a))
cmp_src = SET_SRC (x); cmp_src = SET_SRC (x);
/* Also check operations with implicit extensions, e.g.: /* Also check operations with implicit extensions, e.g.:
[(set (reg:DI) [(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) (set (reg:CCZ flags)
(compare:CCZ (compare:CCZ (plus:SI (reg:SI) (reg:SI))
(plus:SI (reg:SI)(reg:SI)) (const_int 0)))] */
(const_int 0)))] */
else if (REG_P (SET_DEST (x)) else if (REG_P (SET_DEST (x))
&& REG_P (in_a) && REG_P (in_a)
&& REGNO (SET_DEST (x)) == REGNO (in_a) && REGNO (SET_DEST (x)) == REGNO (in_a)
...@@ -603,17 +645,29 @@ try_eliminate_compare (struct comparison *cmp) ...@@ -603,17 +645,29 @@ try_eliminate_compare (struct comparison *cmp)
|| GET_CODE (SET_SRC (x)) == SIGN_EXTEND) || GET_CODE (SET_SRC (x)) == SIGN_EXTEND)
&& GET_MODE (XEXP (SET_SRC (x), 0)) == GET_MODE (in_a)) && GET_MODE (XEXP (SET_SRC (x), 0)) == GET_MODE (in_a))
cmp_src = XEXP (SET_SRC (x), 0); 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 else
return false; return false;
/* Determine if we ought to use a different CC_MODE here. */ /* 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) if (flags == NULL)
flags = gen_rtx_REG (cmp->orig_mode, targetm.flags_regnum); flags = gen_rtx_REG (cmp->orig_mode, targetm.flags_regnum);
/* Generate a new comparison for installation in the setter. */ /* Generate a new comparison for installation in the setter. */
x = copy_rtx (cmp_src); 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); x = gen_rtx_SET (flags, x);
/* Succeed if the new instruction is valid. Note that we may have started /* Succeed if the new instruction is valid. Note that we may have started
......
...@@ -131,13 +131,17 @@ ...@@ -131,13 +131,17 @@
(match_code "eq,ne")) (match_code "eq,ne"))
;; Return true if OP is a valid comparison operator for CCNZmode. ;; 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")) (match_code "eq,ne,lt,ge"))
;; Return true if OP is a valid comparison operator for CCCmode. ;; 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")) (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. ;; Return true if OP is a valid FP comparison operator.
(define_predicate "visium_fp_comparison_operator" (define_predicate "visium_fp_comparison_operator"
(match_code "eq,ne,ordered,unordered,unlt,unle,ungt,unge,lt,le,gt,ge")) (match_code "eq,ne,ordered,unordered,unlt,unle,ungt,unge,lt,le,gt,ge"))
...@@ -155,6 +159,8 @@ ...@@ -155,6 +159,8 @@
return visium_nz_comparison_operator (op, mode); return visium_nz_comparison_operator (op, mode);
case CCCmode: case CCCmode:
return visium_c_comparison_operator (op, mode); return visium_c_comparison_operator (op, mode);
case CCVmode:
return visium_v_comparison_operator (op, mode);
case CCFPmode: case CCFPmode:
case CCFPEmode: case CCFPEmode:
return visium_fp_comparison_operator (op, mode); return visium_fp_comparison_operator (op, mode);
......
...@@ -29,6 +29,10 @@ along with GCC; see the file COPYING3. If not see ...@@ -29,6 +29,10 @@ along with GCC; see the file COPYING3. If not see
instruction. Only the =,!= and unsigned <,>= operators can be used in instruction. Only the =,!= and unsigned <,>= operators can be used in
conjunction with it. 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 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 the floating-point unit. One for comparisons which generate an exception
if the result is unordered (CCFPEmode) and one for comparisons which never if the result is unordered (CCFPEmode) and one for comparisons which never
...@@ -36,5 +40,6 @@ along with GCC; see the file COPYING3. If not see ...@@ -36,5 +40,6 @@ along with GCC; see the file COPYING3. If not see
CC_MODE (CCNZ); CC_MODE (CCNZ);
CC_MODE (CCC); CC_MODE (CCC);
CC_MODE (CCV);
CC_MODE (CCFP); CC_MODE (CCFP);
CC_MODE (CCFPE); CC_MODE (CCFPE);
...@@ -2833,6 +2833,14 @@ visium_select_cc_mode (enum rtx_code code, rtx op0, rtx op1) ...@@ -2833,6 +2833,14 @@ visium_select_cc_mode (enum rtx_code code, rtx op0, rtx op1)
&& rtx_equal_p (XEXP (op0, 0), op1)) && rtx_equal_p (XEXP (op0, 0), op1))
return CCCmode; 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) if (op1 != const0_rtx)
return CCmode; return CCmode;
...@@ -3101,6 +3109,8 @@ output_cbranch (rtx label, enum rtx_code code, enum machine_mode cc_mode, ...@@ -3101,6 +3109,8 @@ output_cbranch (rtx label, enum rtx_code code, enum machine_mode cc_mode,
case NE: case NE:
if (cc_mode == CCCmode) if (cc_mode == CCCmode)
cond = "cs"; cond = "cs";
else if (cc_mode == CCVmode)
cond = "os";
else else
cond = "ne"; cond = "ne";
break; break;
...@@ -3108,6 +3118,8 @@ output_cbranch (rtx label, enum rtx_code code, enum machine_mode cc_mode, ...@@ -3108,6 +3118,8 @@ output_cbranch (rtx label, enum rtx_code code, enum machine_mode cc_mode,
case EQ: case EQ:
if (cc_mode == CCCmode) if (cc_mode == CCCmode)
cond = "cc"; cond = "cc";
else if (cc_mode == CCVmode)
cond = "oc";
else else
cond = "eq"; cond = "eq";
break; break;
......
...@@ -81,6 +81,9 @@ ...@@ -81,6 +81,9 @@
UNSPEC_ITOF UNSPEC_ITOF
UNSPEC_FTOI UNSPEC_FTOI
UNSPEC_NOP UNSPEC_NOP
UNSPEC_ADDV
UNSPEC_SUBV
UNSPEC_NEGV
]) ])
;; UNSPEC_VOLATILE usage. ;; UNSPEC_VOLATILE usage.
...@@ -745,6 +748,27 @@ ...@@ -745,6 +748,27 @@
(match_operand:QHI 2 "register_operand" "")))] (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" (define_insn_and_split "*add<mode>3_insn"
[(set (match_operand:QHI 0 "register_operand" "=r") [(set (match_operand:QHI 0 "register_operand" "=r")
(plus:QHI (match_operand:QHI 1 "register_operand" "%r") (plus:QHI (match_operand:QHI 1 "register_operand" "%r")
...@@ -767,6 +791,28 @@ ...@@ -767,6 +791,28 @@
"add<s> %0,%1,%2" "add<s> %0,%1,%2"
[(set_attr "type" "arith")]) [(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" (define_expand "addsi3"
[(set (match_operand:SI 0 "register_operand" "") [(set (match_operand:SI 0 "register_operand" "")
(plus:SI (match_operand:SI 1 "register_operand" "") (plus:SI (match_operand:SI 1 "register_operand" "")
...@@ -822,6 +868,19 @@ ...@@ -822,6 +868,19 @@
addi %0,%2" addi %0,%2"
[(set_attr "type" "arith")]) [(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" (define_expand "adddi3"
[(set (match_operand:DI 0 "register_operand" "") [(set (match_operand:DI 0 "register_operand" "")
(plus:DI (match_operand:DI 1 "register_operand" "") (plus:DI (match_operand:DI 1 "register_operand" "")
...@@ -897,6 +956,34 @@ ...@@ -897,6 +956,34 @@
(match_operand:QHI 2 "register_operand" "")))] (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" (define_insn_and_split "*sub<mode>3_insn"
[(set (match_operand:QHI 0 "register_operand" "=r") [(set (match_operand:QHI 0 "register_operand" "=r")
(minus:QHI (match_operand:QHI 1 "reg_or_0_operand" "rO") (minus:QHI (match_operand:QHI 1 "reg_or_0_operand" "rO")
...@@ -919,6 +1006,27 @@ ...@@ -919,6 +1006,27 @@
"sub<s> %0,%r1,%2" "sub<s> %0,%r1,%2"
[(set_attr "type" "arith")]) [(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" (define_expand "subsi3"
[(set (match_operand:SI 0 "register_operand" "") [(set (match_operand:SI 0 "register_operand" "")
(minus:SI (match_operand:SI 1 "reg_or_0_operand" "") (minus:SI (match_operand:SI 1 "reg_or_0_operand" "")
...@@ -973,6 +1081,19 @@ ...@@ -973,6 +1081,19 @@
subi %0,%2" subi %0,%2"
[(set_attr "type" "arith")]) [(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" (define_expand "subdi3"
[(set (match_operand:DI 0 "register_operand" "") [(set (match_operand:DI 0 "register_operand" "")
(minus:DI (match_operand:DI 1 "register_operand" "") (minus:DI (match_operand:DI 1 "register_operand" "")
...@@ -1047,6 +1168,25 @@ ...@@ -1047,6 +1168,25 @@
(neg:I (match_operand:I 1 "register_operand" "")))] (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" (define_insn_and_split "*neg<mode>2_insn"
[(set (match_operand:I 0 "register_operand" "=r") [(set (match_operand:I 0 "register_operand" "=r")
(neg:I (match_operand:I 1 "register_operand" "r")))] (neg:I (match_operand:I 1 "register_operand" "r")))]
...@@ -1075,6 +1215,16 @@ ...@@ -1075,6 +1215,16 @@
"sub.l %0,r0,%1" "sub.l %0,r0,%1"
[(set_attr "type" "arith")]) [(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" (define_expand "negdi2"
[(set (match_operand:DI 0 "register_operand" "") [(set (match_operand:DI 0 "register_operand" "")
(neg:DI (match_operand:DI 1 "register_operand" "")))] (neg:DI (match_operand:DI 1 "register_operand" "")))]
...@@ -1850,6 +2000,45 @@ ...@@ -1850,6 +2000,45 @@
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; 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 ;; Integer comparisons
;; ;;
;; Modes QI, HI and SI are supported directly. ;; Modes QI, HI and SI are supported directly.
...@@ -2125,6 +2314,65 @@ ...@@ -2125,6 +2314,65 @@
} }
[(set_attr "type" "cmp")]) [(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" (define_insn_and_split "*cbranchsi4_btst_insn"
[(set (pc) [(set (pc)
(if_then_else (match_operator 0 "visium_equality_comparison_operator" (if_then_else (match_operator 0 "visium_equality_comparison_operator"
......
2016-10-20 Eric Botcazou <ebotcazou@adacore.com>
* gcc.target/visium/overflow8.c: New.
* gcc.target/visium/overflow16.c: Likewise.
* gcc.target/visium/overflow32: Likewise.
2016-10-20 Michael Matz <matz@suse.de> 2016-10-20 Michael Matz <matz@suse.de>
* gcc.dg/loop-split.c: New test. * gcc.dg/loop-split.c: New test.
......
/* { 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" } } */
/* { 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" } } */
/* { 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" } } */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment