From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from server.nextmovesoftware.com (server.nextmovesoftware.com [162.254.253.69]) by sourceware.org (Postfix) with ESMTPS id 13AD53857C4A for ; Sat, 15 Aug 2020 11:10:44 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 13AD53857C4A Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=nextmovesoftware.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=roger@nextmovesoftware.com DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=nextmovesoftware.com; s=default; h=Content-Type:MIME-Version:Message-ID: Date:Subject:To:From:Sender:Reply-To:Cc:Content-Transfer-Encoding:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=38B3F7rZPVQXwts26e8KcBLWx8RGB6HcCCiMj8WO7AI=; b=NJoswVOpUvuHazB1diMPhG/J4f 71GS5eGH+aTq0/sXqWYSPEKFAoV5ldtvhsKFgmSu+wGYAbp5j0YLJulBqCW1RSTpPMZxfHpAC2bt2 LHHfz97psaXpaflEi2B06Ovak+ftNwXjo26uxdqIxLWBgIqOZkuWulZrQ9X1r/KZ8sYY+wEFl7xze ZFf4g7HAQUN4jC4pwm/gFdCCvNUoQPdwIZLwo2/l3PW0V968Cccmy2e45XrErnXx/rrmj5X51+ZbI nziUYabdj9eYEot7oojY8azF2j4kYth1qpJk+lr4hT6Ve+ljYhMPFMIE/ubgxQiEoWi6pCP1j2kKt lIhPmJXQ==; Received: from host86-137-89-56.range86-137.btcentralplus.com ([86.137.89.56]:55475 helo=Dell) by server.nextmovesoftware.com with esmtpsa (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.93) (envelope-from ) id 1k6u5D-0007rA-FG for gcc-patches@gcc.gnu.org; Sat, 15 Aug 2020 07:10:43 -0400 From: "Roger Sayle" To: "'GCC Patches'" Subject: [PATCH] middle-end: Fix PR middle-end/85811: Introduce tree_expr_maybe_nan_p et al. Date: Sat, 15 Aug 2020 12:10:42 +0100 Message-ID: <004901d672f4$b80b66f0$282234d0$@nextmovesoftware.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="----=_NextPart_000_004A_01D672FD.19D218E0" X-Mailer: Microsoft Outlook 16.0 Thread-Index: AdZy8hTxjMPPJ/fVR1eNuXJRcjE0sA== Content-Language: en-gb X-AntiAbuse: This header was added to track abuse, please include it with any abuse report X-AntiAbuse: Primary Hostname - server.nextmovesoftware.com X-AntiAbuse: Original Domain - gcc.gnu.org X-AntiAbuse: Originator/Caller UID/GID - [47 12] / [47 12] X-AntiAbuse: Sender Address Domain - nextmovesoftware.com X-Get-Message-Sender-Via: server.nextmovesoftware.com: authenticated_id: roger@nextmovesoftware.com X-Authenticated-Sender: server.nextmovesoftware.com: roger@nextmovesoftware.com X-Source: X-Source-Args: X-Source-Dir: X-Spam-Status: No, score=-11.0 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 15 Aug 2020 11:10:46 -0000 This is a multipart message in MIME format. ------=_NextPart_000_004A_01D672FD.19D218E0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit The motivation for this patch is PR middle-end/85811, a wrong-code regression entitled "Invalid optimization with fmax, fabs and nan". The optimization involves assuming max(x,y) is non-negative if (say) y is non-negative, i.e. max(x,2.0). Unfortunately, this is an invalid assumption in the presence of NaNs. Hence max(x,+qNaN), with IEEE fmax semantics will always return x even though the qNaN is non-negative. Worse, max(x,2.0) may return a negative value if x is -sNaN. I'll quote Joseph Myers (many thanks) who describes things clearly as: > (a) When both arguments are NaNs, the return value should be a qNaN, > but sometimes it is an sNaN if at least one argument is an sNaN. > (b) Under TS 18661-1 semantics, if either argument is an sNaN then the > result should be a qNaN (whereas if one argument is a qNaN and the > other is not a NaN, the result should be the non-NaN argument). > Various implementations treat sNaNs like qNaNs here. Under this logic, the tree_expr_nonnegative_p for IEEE fmax should be: CASE_CFN_FMAX: CASE_CFN_FMAX_FN: /* Usually RECURSE (arg0) || RECURSE (arg1) but NaNs complicate things. In the presence of sNaNs, we're only guaranteed to be non-negative if both operands are non-negative. In the presence of qNaNs, we're non-negative if either operand is non-negative and can't be a qNaN, or if both operands are non-negative. */ if (tree_expr_maybe_signaling_nan_p (arg0) || tree_expr_maybe_signaling_nan_p (arg1)) return RECURSE (arg0) && RECURSE (arg1); return RECURSE (arg0) ? (!tree_expr_maybe_nan_p (arg0) || RECURSE (arg1)) : (RECURSE (arg1) && !tree_expr_maybe_nan_p (arg1)); Which indeed resolves the wrong code in the PR. The infrastructure that makes this possible are the two new functions tree_expr_maybe_nan_p and tree_expr_maybe_signaling_nan_p which test whether a value may potentially be a NaN or a signaling NaN respectively. In fact, this patch adds seven new predicates to the middle-end: bool tree_expr_finite_p (const_tree); bool tree_expr_infinite_p (const_tree); bool tree_expr_maybe_infinite_p (const_tree); bool tree_expr_signaling_nan_p (const_tree); bool tree_expr_maybe_signaling_nan_p (const_tree); bool tree_expr_nan_p (const_tree); bool tree_expr_maybe_nan_p (const_tree); These functions correspond to the "must" and "may" operators in modal logic, and allow us to triage expressions in the middle-end; definitely a NaN, definitely not a NaN, and unknown at compile-time, etc. A prime example of the utility of these functions is that a IEEE floating point value promoted from an integer type can't be a NaN or infinite. Hence (double)i+0.0 where i is an integer can be simplified to (double)i even with -fsignaling-nans. Currently in GCC optimizations are enabled/disabled based on whether the expression's type supports NaNs or sNaNs; with these new predicates they can be controlled by whether the actual operands may or may not be NaNs. Having added these extremely useful helper functions to the middle-end, I couldn't help by use then in a few places in fold-const.c, builtins.c and match.pd. In the near term, these can/should be used in places where the tree optimizers test for HONOR_NANS, HONOR_INFINITIES or HONOR_SNANS, or explicitly test whether a REAL_CST is a NaN or Inf. In the longer term (I'm not volunteering) these predicates could perhaps be hooked into the middle-end's SSA chaining and/or VRP machinery, allowing finiteness to propagated around the CFG, much like we currently propagate value ranges. This patch has been tested on x86_64-pc-linux-gnu with a "make bootstrap" and "make -k check". Ok for mainline? 2020-08-15 Roger Sayle gcc/ChangeLog PR middle-end/85811 * fold-const.c (tree_expr_finite_p): New function to test whether a tree expression must be finite, i.e. not a FP NaN or infinity. (tree_expr_infinite_p): New function to test whether a tree expression must be infinite, i.e. a FP infinity. (tree_expr_maybe_infinite_p): New function to test whether a tree expression may be infinite, i.e. a FP infinity. (tree_expr_signaling_nan_p): New function to test whether a tree expression must evaluate to a signaling NaN (sNaN). (tree_expr_maybe_signaling_nan_p): New function to test whether a tree expression may be a signaling NaN (sNaN). (tree_expr_nan_p): New function to test whether a tree expression must evaluate to a (quiet or signaling) NaN. (tree_expr_maybe_nan_p): New function to test whether a tree expression me be a (quiet or signaling) NaN. (tree_binary_nonnegative_warnv_p) [MAX_EXPR]: In the presence of NaNs, MAX_EXPR is only guaranteed to be non-negative, if both operands are non-negative. (tree_call_nonnegative_warnv_p) [CASE_CFN_FMAX,CASE_CFN_FMAX_FN]: In the presence of signaling NaNs, fmax is only guaranteed to be non-negative if both operands are negative. In the presence of quiet NaNs, fmax is non-negative if either operand is non-negative and not a qNaN, or both operands are non-negative. * fold-const.h (tree_expr_finite_p, tree_expr_infinite_p, tree_expr_maybe_infinite_p, tree_expr_signaling_nan_p, tree_expr_maybe_signaling_nan_p, tree_expr_nan_p, tree_expr_maybe_nan_p): Prototype new functions here. * builtins.c (fold_builtin_classify) [BUILT_IN_ISINF]: Fold to a constant if argument is known to be (or not to be) an Infinity. [BUILT_IN_ISFINITE]: Fold to a constant if argument is known to be (or not to be) finite. [BUILT_IN_ISNAN]: Fold to a constant if argument is known to be (or not to be) a NaN. (fold_builtin_fpclassify): Check tree_expr_maybe_infinite_p and tree_expr_maybe_nan_p instead of HONOR_INFINITIES and HONOR_NANS respectively. (fold_builtin_unordered_cmp): Fold UNORDERED_EXPR to a constant when its arguments are known to be (or not be) NaNs. Check tree_expr_maybe_nan_p instead of HONOR_NANS when choosing between unordered and regular forms of comparison operators. * match.pd (ordered(x,y)->true/false): Constant fold ORDERED_EXPR if its operands are known to be (or not to be) NaNs. (unordered(x,y)->true/false): Constant fold UNORDERED_EXPR if its operands are known to be (or not to be) NaNs. (sqrt(x)*sqrt(x)->x): Check tree_expr_maybe_signaling_nan_p instead of HONOR_SNANS. gcc/testsuite/ChangeLog PR middle-end/85811 * gcc.dg/pr85811.c: New test. * gcc.dg/fold-isfinite-1.c: New test. * gcc.dg/fold-isfinite-2.c: New test. * gcc.dg/fold-isinf-1.c: New test. * gcc.dg/fold-isinf-2.c: New test. * gcc.dg/fold-isnan-1.c: New test. * gcc.dg/fold-isnan-2.c: New test. Thanks in advance, Roger -- Roger Sayle NextMove Software Cambridge, UK ------=_NextPart_000_004A_01D672FD.19D218E0 Content-Type: text/plain; name="patchn.txt" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="patchn.txt" diff --git a/gcc/builtins.c b/gcc/builtins.c=0A= index beb56e0..682e61b 100644=0A= --- a/gcc/builtins.c=0A= +++ b/gcc/builtins.c=0A= @@ -9857,9 +9857,10 @@ fold_builtin_classify (location_t loc, tree = fndecl, tree arg, int builtin_index)=0A= switch (builtin_index)=0A= {=0A= case BUILT_IN_ISINF:=0A= - if (!HONOR_INFINITIES (arg))=0A= + if (tree_expr_infinite_p (arg))=0A= + return omit_one_operand_loc (loc, type, integer_one_node, arg);=0A= + if (!tree_expr_maybe_infinite_p (arg))=0A= return omit_one_operand_loc (loc, type, integer_zero_node, arg);=0A= -=0A= return NULL_TREE;=0A= =0A= case BUILT_IN_ISINF_SIGN:=0A= @@ -9895,14 +9896,16 @@ fold_builtin_classify (location_t loc, tree = fndecl, tree arg, int builtin_index)=0A= }=0A= =0A= case BUILT_IN_ISFINITE:=0A= - if (!HONOR_NANS (arg)=0A= - && !HONOR_INFINITIES (arg))=0A= + if (tree_expr_finite_p (arg))=0A= return omit_one_operand_loc (loc, type, integer_one_node, arg);=0A= -=0A= + if (tree_expr_nan_p (arg) || tree_expr_infinite_p (arg))=0A= + return omit_one_operand_loc (loc, type, integer_zero_node, arg);=0A= return NULL_TREE;=0A= =0A= case BUILT_IN_ISNAN:=0A= - if (!HONOR_NANS (arg))=0A= + if (tree_expr_nan_p (arg))=0A= + return omit_one_operand_loc (loc, type, integer_one_node, arg);=0A= + if (!tree_expr_maybe_nan_p (arg))=0A= return omit_one_operand_loc (loc, type, integer_zero_node, arg);=0A= =0A= {=0A= @@ -9976,7 +9979,7 @@ fold_builtin_fpclassify (location_t loc, tree = *args, int nargs)=0A= arg, build_real (type, r));=0A= res =3D fold_build3_loc (loc, COND_EXPR, integer_type_node, tmp, = fp_normal, res);=0A= =0A= - if (HONOR_INFINITIES (mode))=0A= + if (tree_expr_maybe_infinite_p (arg))=0A= {=0A= real_inf (&r);=0A= tmp =3D fold_build2_loc (loc, EQ_EXPR, integer_type_node, arg,=0A= @@ -9985,7 +9988,7 @@ fold_builtin_fpclassify (location_t loc, tree = *args, int nargs)=0A= fp_infinite, res);=0A= }=0A= =0A= - if (HONOR_NANS (mode))=0A= + if (tree_expr_maybe_nan_p (arg))=0A= {=0A= tmp =3D fold_build2_loc (loc, ORDERED_EXPR, integer_type_node, = arg, arg);=0A= res =3D fold_build3_loc (loc, COND_EXPR, integer_type_node, tmp, = res, fp_nan);=0A= @@ -10033,12 +10036,15 @@ fold_builtin_unordered_cmp (location_t loc, = tree fndecl, tree arg0, tree arg1,=0A= =0A= if (unordered_code =3D=3D UNORDERED_EXPR)=0A= {=0A= - if (!HONOR_NANS (arg0))=0A= + if (tree_expr_nan_p (arg0) || tree_expr_nan_p (arg1))=0A= + return omit_two_operands_loc (loc, type, integer_one_node, arg0, arg1);=0A= + if (!tree_expr_maybe_nan_p (arg0) && !tree_expr_maybe_nan_p = (arg1))=0A= return omit_two_operands_loc (loc, type, integer_zero_node, arg0, = arg1);=0A= return fold_build2_loc (loc, UNORDERED_EXPR, type, arg0, arg1);=0A= }=0A= =0A= - code =3D HONOR_NANS (arg0) ? unordered_code : ordered_code;=0A= + code =3D (tree_expr_maybe_nan_p (arg0) || tree_expr_maybe_nan_p = (arg1))=0A= + ? unordered_code : ordered_code;=0A= return fold_build1_loc (loc, TRUTH_NOT_EXPR, type,=0A= fold_build2_loc (loc, code, type, arg0, arg1));=0A= }=0A= diff --git a/gcc/fold-const.c b/gcc/fold-const.c=0A= index 5d27927..34167f2 100644=0A= --- a/gcc/fold-const.c=0A= +++ b/gcc/fold-const.c=0A= @@ -13639,6 +13639,248 @@ multiple_of_p (tree type, const_tree top, = const_tree bottom)=0A= }=0A= }=0A= =0A= +/* Return true if expression X cannot be (or contain) a NaN or infinity.=0A= + This function returns true for integer expressions, and returns=0A= + false if uncertain. */=0A= +=0A= +bool=0A= +tree_expr_finite_p (const_tree x)=0A= +{=0A= + machine_mode mode =3D element_mode (x);=0A= + if (!HONOR_NANS (mode) && !HONOR_INFINITIES (mode))=0A= + return true;=0A= + switch (TREE_CODE (x))=0A= + {=0A= + case REAL_CST:=0A= + return real_isfinite (TREE_REAL_CST_PTR (x));=0A= + case COMPLEX_CST:=0A= + return tree_expr_finite_p (TREE_REALPART (x))=0A= + && tree_expr_finite_p (TREE_IMAGPART (x));=0A= + case FLOAT_EXPR:=0A= + return true;=0A= + case ABS_EXPR:=0A= + case CONVERT_EXPR:=0A= + case NON_LVALUE_EXPR:=0A= + case NEGATE_EXPR:=0A= + case SAVE_EXPR:=0A= + return tree_expr_finite_p (TREE_OPERAND (x, 0));=0A= + case MIN_EXPR:=0A= + case MAX_EXPR:=0A= + return tree_expr_finite_p (TREE_OPERAND (x, 0))=0A= + && tree_expr_finite_p (TREE_OPERAND (x, 1));=0A= + case COND_EXPR:=0A= + return tree_expr_finite_p (TREE_OPERAND (x, 1))=0A= + && tree_expr_finite_p (TREE_OPERAND (x, 2));=0A= + case CALL_EXPR:=0A= + switch (get_call_combined_fn (x))=0A= + {=0A= + CASE_CFN_FABS:=0A= + return tree_expr_finite_p (CALL_EXPR_ARG (x, 0));=0A= + CASE_CFN_FMAX:=0A= + CASE_CFN_FMIN:=0A= + return tree_expr_finite_p (CALL_EXPR_ARG (x, 0))=0A= + && tree_expr_finite_p (CALL_EXPR_ARG (x, 1));=0A= + default:=0A= + return false;=0A= + }=0A= +=0A= + default:=0A= + return false;=0A= + }=0A= +}=0A= +=0A= +/* Return true if expression X evaluates to an infinity.=0A= + This function returns false for integer expressions. */=0A= +=0A= +bool=0A= +tree_expr_infinite_p (const_tree x)=0A= +{=0A= + if (!HONOR_INFINITIES (x))=0A= + return false;=0A= + switch (TREE_CODE (x))=0A= + {=0A= + case REAL_CST:=0A= + return real_isinf (TREE_REAL_CST_PTR (x));=0A= + case ABS_EXPR:=0A= + case NEGATE_EXPR:=0A= + case NON_LVALUE_EXPR:=0A= + case SAVE_EXPR:=0A= + return tree_expr_infinite_p (TREE_OPERAND (x, 0));=0A= + case COND_EXPR:=0A= + return tree_expr_infinite_p (TREE_OPERAND (x, 1))=0A= + && tree_expr_infinite_p (TREE_OPERAND (x, 2));=0A= + default:=0A= + return false;=0A= + }=0A= +}=0A= +=0A= +/* Return true if expression X could evaluate to an infinity.=0A= + This function returns false for integer expressions, and returns=0A= + true if uncertain. */=0A= +=0A= +bool=0A= +tree_expr_maybe_infinite_p (const_tree x)=0A= +{=0A= + if (!HONOR_INFINITIES (x))=0A= + return false;=0A= + switch (TREE_CODE (x))=0A= + {=0A= + case REAL_CST:=0A= + return real_isinf (TREE_REAL_CST_PTR (x));=0A= + case FLOAT_EXPR:=0A= + return false;=0A= + case ABS_EXPR:=0A= + case NEGATE_EXPR:=0A= + return tree_expr_maybe_infinite_p (TREE_OPERAND (x, 0));=0A= + case COND_EXPR:=0A= + return tree_expr_maybe_infinite_p (TREE_OPERAND (x, 1))=0A= + || tree_expr_maybe_infinite_p (TREE_OPERAND (x, 2));=0A= + default:=0A= + return true;=0A= + }=0A= +}=0A= +=0A= +/* Return true if expression X evaluates to a signaling NaN.=0A= + This function returns false for integer expressions. */=0A= +=0A= +bool=0A= +tree_expr_signaling_nan_p (const_tree x)=0A= +{=0A= + if (!HONOR_SNANS (x))=0A= + return false;=0A= + switch (TREE_CODE (x))=0A= + {=0A= + case REAL_CST:=0A= + return real_issignaling_nan (TREE_REAL_CST_PTR (x));=0A= + case NON_LVALUE_EXPR:=0A= + case SAVE_EXPR:=0A= + return tree_expr_signaling_nan_p (TREE_OPERAND (x, 0));=0A= + case COND_EXPR:=0A= + return tree_expr_signaling_nan_p (TREE_OPERAND (x, 1))=0A= + && tree_expr_signaling_nan_p (TREE_OPERAND (x, 2));=0A= + default:=0A= + return false;=0A= + }=0A= +}=0A= +=0A= +/* Return true if expression X could evaluate to a signaling NaN.=0A= + This function returns false for integer expressions, and returns=0A= + true if uncertain. */=0A= +=0A= +bool=0A= +tree_expr_maybe_signaling_nan_p (const_tree x)=0A= +{=0A= + if (!HONOR_SNANS (x))=0A= + return false;=0A= + switch (TREE_CODE (x))=0A= + {=0A= + case REAL_CST:=0A= + return real_issignaling_nan (TREE_REAL_CST_PTR (x));=0A= + case FLOAT_EXPR:=0A= + return false;=0A= + case ABS_EXPR:=0A= + case CONVERT_EXPR:=0A= + case NEGATE_EXPR:=0A= + case NON_LVALUE_EXPR:=0A= + case SAVE_EXPR:=0A= + return tree_expr_maybe_signaling_nan_p (TREE_OPERAND (x, 0));=0A= + case MIN_EXPR:=0A= + case MAX_EXPR:=0A= + return tree_expr_maybe_signaling_nan_p (TREE_OPERAND (x, 0))=0A= + || tree_expr_maybe_signaling_nan_p (TREE_OPERAND (x, 1));=0A= + case COND_EXPR:=0A= + return tree_expr_maybe_signaling_nan_p (TREE_OPERAND (x, 1))=0A= + || tree_expr_maybe_signaling_nan_p (TREE_OPERAND (x, 2));=0A= + case CALL_EXPR:=0A= + switch (get_call_combined_fn (x))=0A= + {=0A= + CASE_CFN_FABS:=0A= + return tree_expr_maybe_signaling_nan_p (CALL_EXPR_ARG (x, 0));=0A= + CASE_CFN_FMAX:=0A= + CASE_CFN_FMIN:=0A= + return tree_expr_maybe_signaling_nan_p (CALL_EXPR_ARG (x, 0))=0A= + || tree_expr_maybe_signaling_nan_p (CALL_EXPR_ARG (x, 1));=0A= + default:=0A= + return true;=0A= + }=0A= + default:=0A= + return true;=0A= + }=0A= +}=0A= +=0A= +/* Return true if expression X evaluates to a NaN.=0A= + This function returns false for integer expressions. */=0A= +=0A= +bool=0A= +tree_expr_nan_p (const_tree x)=0A= +{=0A= + if (!HONOR_NANS (x))=0A= + return false;=0A= + switch (TREE_CODE (x))=0A= + {=0A= + case REAL_CST:=0A= + return real_isnan (TREE_REAL_CST_PTR (x));=0A= + case NON_LVALUE_EXPR:=0A= + case SAVE_EXPR:=0A= + return tree_expr_nan_p (TREE_OPERAND (x, 0));=0A= + case COND_EXPR:=0A= + return tree_expr_nan_p (TREE_OPERAND (x, 1))=0A= + && tree_expr_nan_p (TREE_OPERAND (x, 2));=0A= + default:=0A= + return false;=0A= + }=0A= +}=0A= +=0A= +/* Return true if expression X could evaluate to a NaN.=0A= + This function returns false for integer expressions, and returns=0A= + true if uncertain. */=0A= +=0A= +bool=0A= +tree_expr_maybe_nan_p (const_tree x)=0A= +{=0A= + if (!HONOR_NANS (x))=0A= + return false;=0A= + switch (TREE_CODE (x))=0A= + {=0A= + case REAL_CST:=0A= + return real_isnan (TREE_REAL_CST_PTR (x));=0A= + case FLOAT_EXPR:=0A= + return false;=0A= + case PLUS_EXPR:=0A= + case MINUS_EXPR:=0A= + case MULT_EXPR:=0A= + return !tree_expr_finite_p (TREE_OPERAND (x, 0))=0A= + || !tree_expr_finite_p (TREE_OPERAND (x, 1));=0A= + case ABS_EXPR:=0A= + case CONVERT_EXPR:=0A= + case NEGATE_EXPR:=0A= + case NON_LVALUE_EXPR:=0A= + case SAVE_EXPR:=0A= + return tree_expr_maybe_nan_p (TREE_OPERAND (x, 0));=0A= + case MIN_EXPR:=0A= + case MAX_EXPR:=0A= + return tree_expr_maybe_nan_p (TREE_OPERAND (x, 0))=0A= + || tree_expr_maybe_nan_p (TREE_OPERAND (x, 1));=0A= + case COND_EXPR:=0A= + return tree_expr_maybe_nan_p (TREE_OPERAND (x, 1))=0A= + || tree_expr_maybe_nan_p (TREE_OPERAND (x, 2));=0A= + case CALL_EXPR:=0A= + switch (get_call_combined_fn (x))=0A= + {=0A= + CASE_CFN_FABS:=0A= + return tree_expr_maybe_nan_p (CALL_EXPR_ARG (x, 0));=0A= + CASE_CFN_FMAX:=0A= + CASE_CFN_FMIN:=0A= + return tree_expr_maybe_nan_p (CALL_EXPR_ARG (x, 0))=0A= + || tree_expr_maybe_nan_p (CALL_EXPR_ARG (x, 1));=0A= + default:=0A= + return true;=0A= + }=0A= + default:=0A= + return true;=0A= + }=0A= +}=0A= +=0A= #define tree_expr_nonnegative_warnv_p(X, Y) \=0A= _Pragma ("GCC error \"Use RECURSE for recursive calls\"") 0=0A= =0A= @@ -13816,7 +14058,13 @@ tree_binary_nonnegative_warnv_p (enum tree_code = code, tree type, tree op0,=0A= return false;=0A= =0A= case BIT_AND_EXPR:=0A= + return RECURSE (op0) || RECURSE (op1);=0A= +=0A= case MAX_EXPR:=0A= + /* Usually RECURSE (op0) || RECURSE (op1) but NaNs complicate=0A= + things. */=0A= + if (tree_expr_maybe_nan_p (op0) || tree_expr_maybe_nan_p (op1))=0A= + return RECURSE (op0) && RECURSE (op1);=0A= return RECURSE (op0) || RECURSE (op1);=0A= =0A= case BIT_IOR_EXPR:=0A= @@ -13976,8 +14224,18 @@ tree_call_nonnegative_warnv_p (tree type, = combined_fn fn, tree arg0, tree arg1,=0A= =0A= CASE_CFN_FMAX:=0A= CASE_CFN_FMAX_FN:=0A= - /* True if the 1st OR 2nd arguments are nonnegative. */=0A= - return RECURSE (arg0) || RECURSE (arg1);=0A= + /* Usually RECURSE (arg0) || RECURSE (arg1) but NaNs complicate=0A= + things. In the presence of sNaNs, we're only guaranteed to be=0A= + non-negative if both operands are non-negative. In the presence=0A= + of qNaNs, we're non-negative if either operand is non-negative=0A= + and can't be a qNaN, or if both operands are non-negative. */=0A= + if (tree_expr_maybe_signaling_nan_p (arg0) ||=0A= + tree_expr_maybe_signaling_nan_p (arg1))=0A= + return RECURSE (arg0) && RECURSE (arg1);=0A= + return RECURSE (arg0) ? (!tree_expr_maybe_nan_p (arg0)=0A= + || RECURSE (arg1))=0A= + : (RECURSE (arg1)=0A= + && !tree_expr_maybe_nan_p (arg1));=0A= =0A= CASE_CFN_FMIN:=0A= CASE_CFN_FMIN_FN:=0A= diff --git a/gcc/fold-const.h b/gcc/fold-const.h=0A= index 0f788a4..dfb98b1 100644=0A= --- a/gcc/fold-const.h=0A= +++ b/gcc/fold-const.h=0A= @@ -186,6 +186,13 @@ extern tree non_lvalue_loc (location_t, tree);=0A= extern bool tree_expr_nonzero_p (tree);=0A= extern bool tree_expr_nonnegative_p (tree);=0A= extern bool tree_expr_nonnegative_warnv_p (tree, bool *, int =3D 0);=0A= +extern bool tree_expr_finite_p (const_tree);=0A= +extern bool tree_expr_infinite_p (const_tree);=0A= +extern bool tree_expr_maybe_infinite_p (const_tree);=0A= +extern bool tree_expr_signaling_nan_p (const_tree);=0A= +extern bool tree_expr_maybe_signaling_nan_p (const_tree);=0A= +extern bool tree_expr_nan_p (const_tree);=0A= +extern bool tree_expr_maybe_nan_p (const_tree);=0A= extern tree make_range (tree, int *, tree *, tree *, bool *);=0A= extern tree make_range_step (location_t, enum tree_code, tree, tree, = tree,=0A= tree *, tree *, int *, bool *);=0A= diff --git a/gcc/match.pd b/gcc/match.pd=0A= index c3b8816..48e266e 100644=0A= --- a/gcc/match.pd=0A= +++ b/gcc/match.pd=0A= @@ -4874,6 +4874,24 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)=0A= { constant_boolean_node (cmp =3D=3D ORDERED_EXPR || cmp =3D=3D = LTGT_EXPR=0A= ? false : true, type); })))=0A= =0A= +/* Fold UNORDERED if either operand must be NaN, or neither can be. */=0A= +(simplify=0A= + (unordered @0 @1)=0A= + (switch=0A= + (if (tree_expr_nan_p (@0) || tree_expr_nan_p (@1))=0A= + { constant_boolean_node (true, type); })=0A= + (if (!tree_expr_maybe_nan_p (@0) && !tree_expr_maybe_nan_p (@1))=0A= + { constant_boolean_node (false, type); })))=0A= +=0A= +/* Fold ORDERED if either operand must be NaN, or neither can be. */=0A= +(simplify=0A= + (ordered @0 @1)=0A= + (switch=0A= + (if (tree_expr_nan_p (@0) || tree_expr_nan_p (@1))=0A= + { constant_boolean_node (false, type); })=0A= + (if (!tree_expr_maybe_nan_p (@0) && !tree_expr_maybe_nan_p (@1))=0A= + { constant_boolean_node (true, type); })))=0A= +=0A= /* bool_var !=3D 0 becomes bool_var. */=0A= (simplify=0A= (ne @0 integer_zerop)=0A= @@ -5001,7 +5019,7 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)=0A= /* Simplify sqrt(x) * sqrt(x) -> x. */=0A= (simplify=0A= (mult (SQRT_ALL@1 @0) @1)=0A= - (if (!HONOR_SNANS (type))=0A= + (if (!tree_expr_maybe_signaling_nan_p (@0))=0A= @0))=0A= =0A= (for op (plus minus)=0A= ------=_NextPart_000_004A_01D672FD.19D218E0 Content-Type: text/plain; name="patchnb.txt" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="patchnb.txt" diff --git a/gcc/testsuite/gcc.dg/pr85811.c = b/gcc/testsuite/gcc.dg/pr85811.c=0A= new file mode 100644=0A= index 0000000..868f66c=0A= --- /dev/null=0A= +++ b/gcc/testsuite/gcc.dg/pr85811.c=0A= @@ -0,0 +1,15 @@=0A= +/* { dg-do compile } */=0A= +/* { dg-options "-O2 -fdump-tree-optimized" } */=0A= +#include =0A= +=0A= +int main() {=0A= + const double negval =3D -1.0;=0A= + const double nanval =3D 0.0 / 0.0;=0A= + const double val =3D __builtin_fmax(negval, nanval);=0A= + const double absval =3D __builtin_fabs(val);=0A= + printf("fabs(%.16e) =3D %.16e\n", val, absval);=0A= + return absval >=3D 0 ? 0 : 1;=0A= +}=0A= +=0A= +/* We hope not to see: printf ("fabs(%.16e) =3D %.16e\n", val_4, = val_4); */=0A= +/* { dg-final { scan-tree-dump-not "val_\[0-9\]*, val_\[0-9\]*" = "optimized" } } */=0A= diff --git a/gcc/testsuite/gcc.dg/fold-isfinite-1.c = b/gcc/testsuite/gcc.dg/fold-isfinite-1.c=0A= new file mode 100644=0A= index 0000000..2ea0192=0A= --- /dev/null=0A= +++ b/gcc/testsuite/gcc.dg/fold-isfinite-1.c=0A= @@ -0,0 +1,21 @@=0A= +/* { dg-do compile } */=0A= +/* { dg-require-effective-target inf } */=0A= +/* { dg-options "-O2 -fdump-tree-optimized" } */=0A= +=0A= +int foo(int x)=0A= +{=0A= + return __builtin_finite((double)x);=0A= +}=0A= +=0A= +int foof(int x)=0A= +{=0A= + return __builtin_finitef((float)x);=0A= +}=0A= +=0A= +int fool(int x)=0A= +{=0A= + return __builtin_finitel((long double)x);=0A= +}=0A= +=0A= +/* { dg-final { scan-tree-dump-times "_finite" 0 "optimized" } } */=0A= +/* { dg-final { scan-tree-dump-times " u> " 0 "optimized" } } */=0A= diff --git a/gcc/testsuite/gcc.dg/fold-isfinite-2.c = b/gcc/testsuite/gcc.dg/fold-isfinite-2.c=0A= new file mode 100644=0A= index 0000000..ff70d8d=0A= --- /dev/null=0A= +++ b/gcc/testsuite/gcc.dg/fold-isfinite-2.c=0A= @@ -0,0 +1,21 @@=0A= +/* { dg-do compile } */=0A= +/* { dg-require-effective-target inf } */=0A= +/* { dg-options "-O2 -fdump-tree-optimized" } */=0A= +=0A= +int foo(unsigned int x)=0A= +{=0A= + return __builtin_finite((double)x);=0A= +}=0A= +=0A= +int foof(unsigned int x)=0A= +{=0A= + return __builtin_finitef((float)x);=0A= +}=0A= +=0A= +int fool(unsigned int x)=0A= +{=0A= + return __builtin_finitel((long double)x);=0A= +}=0A= +=0A= +/* { dg-final { scan-tree-dump-times "_finite" 0 "optimized" } } */=0A= +/* { dg-final { scan-tree-dump-times " u> " 0 "optimized" } } */=0A= diff --git a/gcc/testsuite/gcc.dg/fold-isinf-1.c = b/gcc/testsuite/gcc.dg/fold-isinf-1.c=0A= new file mode 100644=0A= index 0000000..485816e=0A= --- /dev/null=0A= +++ b/gcc/testsuite/gcc.dg/fold-isinf-1.c=0A= @@ -0,0 +1,21 @@=0A= +/* { dg-do compile } */=0A= +/* { dg-require-effective-target inf } */=0A= +/* { dg-options "-O2 -fdump-tree-optimized" } */=0A= +=0A= +int foo(int x)=0A= +{=0A= + return __builtin_isinf((double)x);=0A= +}=0A= +=0A= +int foof(int x)=0A= +{=0A= + return __builtin_isinff((float)x);=0A= +}=0A= +=0A= +int fool(int x)=0A= +{=0A= + return __builtin_isinfl((long double)x);=0A= +}=0A= +=0A= +/* { dg-final { scan-tree-dump-times "_isinf" 0 "optimized" } } */=0A= +/* { dg-final { scan-tree-dump-times " u<=3D " 0 "optimized" } } */=0A= diff --git a/gcc/testsuite/gcc.dg/fold-isinf-2.c = b/gcc/testsuite/gcc.dg/fold-isinf-2.c=0A= new file mode 100644=0A= index 0000000..a236ca1=0A= --- /dev/null=0A= +++ b/gcc/testsuite/gcc.dg/fold-isinf-2.c=0A= @@ -0,0 +1,21 @@=0A= +/* { dg-do compile } */=0A= +/* { dg-require-effective-target inf } */=0A= +/* { dg-options "-O2 -fdump-tree-optimized" } */=0A= +=0A= +int foo(unsigned int x)=0A= +{=0A= + return __builtin_isinf((double)x);=0A= +}=0A= +=0A= +int foof(unsigned int x)=0A= +{=0A= + return __builtin_isinff((float)x);=0A= +}=0A= +=0A= +int fool(unsigned int x)=0A= +{=0A= + return __builtin_isinfl((long double)x);=0A= +}=0A= +=0A= +/* { dg-final { scan-tree-dump-times "_isinf" 0 "optimized" } } */=0A= +/* { dg-final { scan-tree-dump-times " u<=3D " 0 "optimized" } } */=0A= diff --git a/gcc/testsuite/gcc.dg/fold-isnan-1.c = b/gcc/testsuite/gcc.dg/fold-isnan-1.c=0A= new file mode 100644=0A= index 0000000..05ee930=0A= --- /dev/null=0A= +++ b/gcc/testsuite/gcc.dg/fold-isnan-1.c=0A= @@ -0,0 +1,21 @@=0A= +/* { dg-do compile } */=0A= +/* { dg-require-effective-target inf } */=0A= +/* { dg-options "-O2 -fdump-tree-optimized" } */=0A= +=0A= +int foo(int x)=0A= +{=0A= + return __builtin_isnan((double)x);=0A= +}=0A= +=0A= +int foof(int x)=0A= +{=0A= + return __builtin_isnanf((float)x);=0A= +}=0A= +=0A= +int fool(int x)=0A= +{=0A= + return __builtin_isnanl((long double)x);=0A= +}=0A= +=0A= +/* { dg-final { scan-tree-dump-times "_isnan" 0 "optimized" } } */=0A= +/* { dg-final { scan-tree-dump-times " unord " 0 "optimized" } } */=0A= diff --git a/gcc/testsuite/gcc.dg/fold-isnan-2.c = b/gcc/testsuite/gcc.dg/fold-isnan-2.c=0A= new file mode 100644=0A= index 0000000..32b8833=0A= --- /dev/null=0A= +++ b/gcc/testsuite/gcc.dg/fold-isnan-2.c=0A= @@ -0,0 +1,21 @@=0A= +/* { dg-do compile } */=0A= +/* { dg-require-effective-target inf } */=0A= +/* { dg-options "-O2 -fdump-tree-optimized" } */=0A= +=0A= +int foo(unsigned int x)=0A= +{=0A= + return __builtin_isnan((double)x);=0A= +}=0A= +=0A= +int foof(unsigned int x)=0A= +{=0A= + return __builtin_isnanf((float)x);=0A= +}=0A= +=0A= +int fool(unsigned int x)=0A= +{=0A= + return __builtin_isnanl((long double)x);=0A= +}=0A= +=0A= +/* { dg-final { scan-tree-dump-times "_isnan" 0 "optimized" } } */=0A= +/* { dg-final { scan-tree-dump-times " unord " 0 "optimized" } } */=0A= ------=_NextPart_000_004A_01D672FD.19D218E0--