From 2c35bbe14a830f58e121a6bd59c7088de93fc155 Mon Sep 17 00:00:00 2001 From: Eric Botcazou Date: Thu, 20 Oct 2016 14:38:43 +0000 Subject: [PATCH] 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. (uaddv4): New expander. (addv4): Likewise. (add3_insn_set_carry): New instruction. (add3_insn_set_overflow): Likewise. (addsi3_insn_set_overflow): Likewise. (usubv4): New expander. (subv4): Likewise. (sub3_insn_set_carry): New instruction. (sub3_insn_set_overflow): Likewise. (subsi3_insn_set_overflow): Likewise. (unegv3): New expander. (negv3): Likewise. (neg2_insn_set_overflow): New instruction. (addv_tst): Likewise. (subv_tst): Likewise. (negv_tst): Likewise. (cbranch4_addv_insn): New splitter and instruction. (cbranch4_subv_insn): Likewise. (cbranch4_negv_insn): Likewise. From-SVN: r241379 --- gcc/ChangeLog | 34 +++ gcc/compare-elim.c | 172 ++++++++----- gcc/config/visium/predicates.md | 10 +- gcc/config/visium/visium-modes.def | 5 + gcc/config/visium/visium.c | 12 + gcc/config/visium/visium.md | 248 +++++++++++++++++++ gcc/testsuite/ChangeLog | 6 + gcc/testsuite/gcc.target/visium/overflow16.c | 39 +++ gcc/testsuite/gcc.target/visium/overflow32.c | 39 +++ gcc/testsuite/gcc.target/visium/overflow8.c | 39 +++ 10 files changed, 543 insertions(+), 61 deletions(-) create mode 100644 gcc/testsuite/gcc.target/visium/overflow16.c create mode 100644 gcc/testsuite/gcc.target/visium/overflow32.c create mode 100644 gcc/testsuite/gcc.target/visium/overflow8.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 2c770c240ec..7b72536b5e3 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,37 @@ +2016-10-20 Eric Botcazou + + * 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. + (uaddv4): New expander. + (addv4): Likewise. + (add3_insn_set_carry): New instruction. + (add3_insn_set_overflow): Likewise. + (addsi3_insn_set_overflow): Likewise. + (usubv4): New expander. + (subv4): Likewise. + (sub3_insn_set_carry): New instruction. + (sub3_insn_set_overflow): Likewise. + (subsi3_insn_set_overflow): Likewise. + (unegv3): New expander. + (negv3): Likewise. + (neg2_insn_set_overflow): New instruction. + (addv_tst): Likewise. + (subv_tst): Likewise. + (negv_tst): Likewise. + (cbranch4_addv_insn): New splitter and instruction. + (cbranch4_subv_insn): Likewise. + (cbranch4_negv_insn): Likewise. + 2016-10-20 Richard Biener * tree-ssa-alias.c (ptrs_compare_unequal): Remove code duplication. diff --git a/gcc/compare-elim.c b/gcc/compare-elim.c index 329f18f8be7..2820de9ae8d 100644 --- a/gcc/compare-elim.c +++ b/gcc/compare-elim.c @@ -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_children (basic_block bb) 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); + /* Notice if this instruction uses the flags register. */ + if (last_cmp) + find_flags_uses_in_insn (last_cmp, insn); - /* In either case, the previous compare is no longer valid. */ - last_cmp = NULL; - last_cmp_valid = false; - } + /* 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); - /* Notice if this instruction uses the flags register. */ - else if (last_cmp) - find_flags_uses_in_insn (last_cmp, insn); + /* 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 *cmp, rtx a ATTRIBUTE_UNUSED, 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 *cmp) /* 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 *cmp) /* 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 *cmp) || 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 diff --git a/gcc/config/visium/predicates.md b/gcc/config/visium/predicates.md index 5e08768666a..8ebc410b9dd 100644 --- a/gcc/config/visium/predicates.md +++ b/gcc/config/visium/predicates.md @@ -131,13 +131,17 @@ (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 @@ 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); diff --git a/gcc/config/visium/visium-modes.def b/gcc/config/visium/visium-modes.def index 980745ebfe7..558d1f3a35c 100644 --- a/gcc/config/visium/visium-modes.def +++ b/gcc/config/visium/visium-modes.def @@ -29,6 +29,10 @@ along with GCC; see the file COPYING3. If not see 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. If not see CC_MODE (CCNZ); CC_MODE (CCC); +CC_MODE (CCV); CC_MODE (CCFP); CC_MODE (CCFPE); diff --git a/gcc/config/visium/visium.c b/gcc/config/visium/visium.c index c04b345479a..73c345a3f18 100644 --- a/gcc/config/visium/visium.c +++ b/gcc/config/visium/visium.c @@ -2833,6 +2833,14 @@ visium_select_cc_mode (enum rtx_code code, rtx op0, rtx op1) && rtx_equal_p (XEXP (op0, 0), op1)) return CCCmode; + /* This is for the {add,sub,neg}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 code, enum machine_mode cc_mode, 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 code, enum machine_mode cc_mode, case EQ: if (cc_mode == CCCmode) cond = "cc"; + else if (cc_mode == CCVmode) + cond = "oc"; else cond = "eq"; break; diff --git a/gcc/config/visium/visium.md b/gcc/config/visium/visium.md index de1c710f441..5f31f60b779 100644 --- a/gcc/config/visium/visium.md +++ b/gcc/config/visium/visium.md @@ -81,6 +81,9 @@ UNSPEC_ITOF UNSPEC_FTOI UNSPEC_NOP + UNSPEC_ADDV + UNSPEC_SUBV + UNSPEC_NEGV ]) ;; UNSPEC_VOLATILE usage. @@ -745,6 +748,27 @@ (match_operand:QHI 2 "register_operand" "")))] "") +(define_expand "uaddv4" + [(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 "addv4" + [(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 "*add3_insn" [(set (match_operand:QHI 0 "register_operand" "=r") (plus:QHI (match_operand:QHI 1 "register_operand" "%r") @@ -767,6 +791,28 @@ "add %0,%1,%2" [(set_attr "type" "arith")]) +(define_insn "*add3_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 %0,%1,%2" + [(set_attr "type" "arith")]) + +(define_insn "*add3_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 %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 @@ 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 @@ (match_operand:QHI 2 "register_operand" "")))] "") +(define_expand "usubv4" + [(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_unegv3 (operands[0], operands[2], operands[3])); + DONE; + } +}) + +(define_expand "subv4" + [(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 "*sub3_insn" [(set (match_operand:QHI 0 "register_operand" "=r") (minus:QHI (match_operand:QHI 1 "reg_or_0_operand" "rO") @@ -919,6 +1006,27 @@ "sub %0,%r1,%2" [(set_attr "type" "arith")]) +(define_insn "*sub3_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 %0,%r1,%2" + [(set_attr "type" "arith")]) + +(define_insn "*sub3_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 %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 @@ 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 @@ (neg:I (match_operand:I 1 "register_operand" "")))] "") +(define_expand "unegv3" + [(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 "negv3" + [(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 "*neg2_insn" [(set (match_operand:I 0 "register_operand" "=r") (neg:I (match_operand:I 1 "register_operand" "r")))] @@ -1075,6 +1215,16 @@ "sub.l %0,r0,%1" [(set_attr "type" "arith")]) +(define_insn "*neg2_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 %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" "")))] @@ -1847,6 +1997,45 @@ "lsr.l r0,%0,32-%1" [(set_attr "type" "logic")]) +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; Integer overflow tests +;; +;; Modes QI, HI and SI are supported directly. +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; + +(define_insn "*addv_tst" + [(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 r0,%1,%2" + [(set_attr "type" "arith")]) + +(define_insn "*subv_tst" + [(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 r0,%r1,%2" + [(set_attr "type" "arith")]) + +(define_insn "*negv_tst" + [(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 r0,r0,%1" + [(set_attr "type" "arith")]) + ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; @@ -2125,6 +2314,65 @@ } [(set_attr "type" "cmp")]) +(define_insn_and_split "*cbranch4_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 "*cbranch4_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 "*cbranch4_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) (if_then_else (match_operator 0 "visium_equality_comparison_operator" diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 33e86f68cb7..9805c78dfa0 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2016-10-20 Eric Botcazou + + * gcc.target/visium/overflow8.c: New. + * gcc.target/visium/overflow16.c: Likewise. + * gcc.target/visium/overflow32: Likewise. + 2016-10-20 Michael Matz * gcc.dg/loop-split.c: New test. diff --git a/gcc/testsuite/gcc.target/visium/overflow16.c b/gcc/testsuite/gcc.target/visium/overflow16.c new file mode 100644 index 00000000000..9c435775ce0 --- /dev/null +++ b/gcc/testsuite/gcc.target/visium/overflow16.c @@ -0,0 +1,39 @@ +/* { dg-do compile } */ +/* { dg-options "-O2" } */ + +#include + +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" } } */ diff --git a/gcc/testsuite/gcc.target/visium/overflow32.c b/gcc/testsuite/gcc.target/visium/overflow32.c new file mode 100644 index 00000000000..2c489e3a16e --- /dev/null +++ b/gcc/testsuite/gcc.target/visium/overflow32.c @@ -0,0 +1,39 @@ +/* { dg-do compile } */ +/* { dg-options "-O2" } */ + +#include + +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" } } */ diff --git a/gcc/testsuite/gcc.target/visium/overflow8.c b/gcc/testsuite/gcc.target/visium/overflow8.c new file mode 100644 index 00000000000..5761e3ed9e8 --- /dev/null +++ b/gcc/testsuite/gcc.target/visium/overflow8.c @@ -0,0 +1,39 @@ +/* { dg-do compile } */ +/* { dg-options "-O2" } */ + +#include + +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" } } */ -- 2.30.2