From patchwork Thu Oct 20 14:41:16 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Botcazou X-Patchwork-Id: 78519 Delivered-To: patch@linaro.org Received: by 10.140.97.247 with SMTP id m110csp819066qge; Thu, 20 Oct 2016 07:41:48 -0700 (PDT) X-Received: by 10.107.8.139 with SMTP id h11mr1243794ioi.55.1476974508926; Thu, 20 Oct 2016 07:41:48 -0700 (PDT) Return-Path: Received: from sourceware.org (server1.sourceware.org. [209.132.180.131]) by mx.google.com with ESMTPS id wh7si12332293pac.102.2016.10.20.07.41.48 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 20 Oct 2016 07:41:48 -0700 (PDT) Received-SPF: pass (google.com: domain of gcc-patches-return-439135-patch=linaro.org@gcc.gnu.org designates 209.132.180.131 as permitted sender) client-ip=209.132.180.131; Authentication-Results: mx.google.com; dkim=pass header.i=@gcc.gnu.org; spf=pass (google.com: domain of gcc-patches-return-439135-patch=linaro.org@gcc.gnu.org designates 209.132.180.131 as permitted sender) smtp.mailfrom=gcc-patches-return-439135-patch=linaro.org@gcc.gnu.org DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:subject:date:message-id:mime-version:content-type :content-transfer-encoding; q=dns; s=default; b=RCDrImrJHfuaopxN yD5wuWnBO6AXdmXhnEqKJULYU6fm/DJ32WNmwHwEe+9VvpHzEGDJVcjUTItldhHa o/CluulGyN1FXMI4Kzd33ZJgHn0P174YCIyIChGaHbk9NiObCvt/fP4jZ7FQ8wtj osmuL4Hy0pfu6yxeEUsgsmvOHuI= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:subject:date:message-id:mime-version:content-type :content-transfer-encoding; s=default; bh=iGNsUJ+Zdy1hEbMLTpo628 Pudhc=; b=Jkrq9Yq9MzguqX0dGMJSMHztdPub9OofzNQtBWFRHgFLZ1k1J9UVeH 2SW6Vk/olwKs4l1AobzGP3+kuWLdB4DQUGphk9TTW1OU51iPjoYwHbAPmH2LF+mP dPDcImZPHGhYztO2RF8VjUrpv7InVt5DjYngGguUJfspbiBC/V/e8= Received: (qmail 7912 invoked by alias); 20 Oct 2016 14:41:31 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 7894 invoked by uid 89); 20 Oct 2016 14:41:30 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=0.7 required=5.0 tests=AWL, BAYES_20, KAM_ASCII_DIVIDERS, RCVD_IN_DNSWL_NONE, SPF_PASS autolearn=no version=3.3.2 spammy=rr, kills, rj, 1598 X-HELO: smtp.eu.adacore.com Received: from mel.act-europe.fr (HELO smtp.eu.adacore.com) (194.98.77.210) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Thu, 20 Oct 2016 14:41:20 +0000 Received: from localhost (localhost [127.0.0.1]) by filtered-smtp.eu.adacore.com (Postfix) with ESMTP id AE028812FC for ; Thu, 20 Oct 2016 16:41:17 +0200 (CEST) Received: from smtp.eu.adacore.com ([127.0.0.1]) by localhost (smtp.eu.adacore.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id rzfDui5puSOT for ; Thu, 20 Oct 2016 16:41:17 +0200 (CEST) Received: from polaris.localnet (bon31-6-88-161-99-133.fbx.proxad.net [88.161.99.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.eu.adacore.com (Postfix) with ESMTPSA id 51EE0812F9 for ; Thu, 20 Oct 2016 16:41:17 +0200 (CEST) From: Eric Botcazou To: gcc-patches@gcc.gnu.org Subject: [Visium] Add support for overflow arithmetic Date: Thu, 20 Oct 2016 16:41:16 +0200 Message-ID: <1839309.05VOrexfmn@polaris> User-Agent: KMail/4.14.10 (Linux/3.16.7-42-desktop; KDE/4.14.9; x86_64; ; ) MIME-Version: 1.0 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 * 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. 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}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 "add3" (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 @@ (define_insn "*add3_insn %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 @@ (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 "sub3" (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 @@ (define_insn "*sub3_insn %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 @@ (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 "neg2" (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 @@ (define_insn "negsi2_insn_set_carry" "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" "")))] @@ -1850,6 +2000,45 @@ (define_insn "*btst" ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; +;; 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")]) + +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; Integer comparisons ;; ;; Modes QI, HI and SI are supported directly. @@ -2124,6 +2313,65 @@ (define_insn_and_split "*cbranch4_ DONE; } [(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) 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 + +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 + +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 + +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" } } */