From patchwork Sat Aug 15 11:10:42 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roger Sayle X-Patchwork-Id: 1345245 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=8.43.85.97; helo=sourceware.org; envelope-from=gcc-patches-bounces@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=nextmovesoftware.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=nextmovesoftware.com header.i=@nextmovesoftware.com header.a=rsa-sha256 header.s=default header.b=NJoswVOp; dkim-atps=neutral Received: from sourceware.org (server2.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4BTHfj5vy0z9sRK for ; Sat, 15 Aug 2020 21:10:52 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 08F85385E020; Sat, 15 Aug 2020 11:10:48 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org 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 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: , Errors-To: gcc-patches-bounces@gcc.gnu.org Sender: "Gcc-patches" 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 diff --git a/gcc/testsuite/gcc.dg/pr85811.c b/gcc/testsuite/gcc.dg/pr85811.c new file mode 100644 index 0000000..868f66c --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr85811.c @@ -0,0 +1,15 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-optimized" } */ +#include + +int main() { + const double negval = -1.0; + const double nanval = 0.0 / 0.0; + const double val = __builtin_fmax(negval, nanval); + const double absval = __builtin_fabs(val); + printf("fabs(%.16e) = %.16e\n", val, absval); + return absval >= 0 ? 0 : 1; +} + +/* We hope not to see: printf ("fabs(%.16e) = %.16e\n", val_4, val_4); */ +/* { dg-final { scan-tree-dump-not "val_\[0-9\]*, val_\[0-9\]*" "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/fold-isfinite-1.c b/gcc/testsuite/gcc.dg/fold-isfinite-1.c new file mode 100644 index 0000000..2ea0192 --- /dev/null +++ b/gcc/testsuite/gcc.dg/fold-isfinite-1.c @@ -0,0 +1,21 @@ +/* { dg-do compile } */ +/* { dg-require-effective-target inf } */ +/* { dg-options "-O2 -fdump-tree-optimized" } */ + +int foo(int x) +{ + return __builtin_finite((double)x); +} + +int foof(int x) +{ + return __builtin_finitef((float)x); +} + +int fool(int x) +{ + return __builtin_finitel((long double)x); +} + +/* { dg-final { scan-tree-dump-times "_finite" 0 "optimized" } } */ +/* { dg-final { scan-tree-dump-times " u> " 0 "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/fold-isfinite-2.c b/gcc/testsuite/gcc.dg/fold-isfinite-2.c new file mode 100644 index 0000000..ff70d8d --- /dev/null +++ b/gcc/testsuite/gcc.dg/fold-isfinite-2.c @@ -0,0 +1,21 @@ +/* { dg-do compile } */ +/* { dg-require-effective-target inf } */ +/* { dg-options "-O2 -fdump-tree-optimized" } */ + +int foo(unsigned int x) +{ + return __builtin_finite((double)x); +} + +int foof(unsigned int x) +{ + return __builtin_finitef((float)x); +} + +int fool(unsigned int x) +{ + return __builtin_finitel((long double)x); +} + +/* { dg-final { scan-tree-dump-times "_finite" 0 "optimized" } } */ +/* { dg-final { scan-tree-dump-times " u> " 0 "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/fold-isinf-1.c b/gcc/testsuite/gcc.dg/fold-isinf-1.c new file mode 100644 index 0000000..485816e --- /dev/null +++ b/gcc/testsuite/gcc.dg/fold-isinf-1.c @@ -0,0 +1,21 @@ +/* { dg-do compile } */ +/* { dg-require-effective-target inf } */ +/* { dg-options "-O2 -fdump-tree-optimized" } */ + +int foo(int x) +{ + return __builtin_isinf((double)x); +} + +int foof(int x) +{ + return __builtin_isinff((float)x); +} + +int fool(int x) +{ + return __builtin_isinfl((long double)x); +} + +/* { dg-final { scan-tree-dump-times "_isinf" 0 "optimized" } } */ +/* { dg-final { scan-tree-dump-times " u<= " 0 "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/fold-isinf-2.c b/gcc/testsuite/gcc.dg/fold-isinf-2.c new file mode 100644 index 0000000..a236ca1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/fold-isinf-2.c @@ -0,0 +1,21 @@ +/* { dg-do compile } */ +/* { dg-require-effective-target inf } */ +/* { dg-options "-O2 -fdump-tree-optimized" } */ + +int foo(unsigned int x) +{ + return __builtin_isinf((double)x); +} + +int foof(unsigned int x) +{ + return __builtin_isinff((float)x); +} + +int fool(unsigned int x) +{ + return __builtin_isinfl((long double)x); +} + +/* { dg-final { scan-tree-dump-times "_isinf" 0 "optimized" } } */ +/* { dg-final { scan-tree-dump-times " u<= " 0 "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/fold-isnan-1.c b/gcc/testsuite/gcc.dg/fold-isnan-1.c new file mode 100644 index 0000000..05ee930 --- /dev/null +++ b/gcc/testsuite/gcc.dg/fold-isnan-1.c @@ -0,0 +1,21 @@ +/* { dg-do compile } */ +/* { dg-require-effective-target inf } */ +/* { dg-options "-O2 -fdump-tree-optimized" } */ + +int foo(int x) +{ + return __builtin_isnan((double)x); +} + +int foof(int x) +{ + return __builtin_isnanf((float)x); +} + +int fool(int x) +{ + return __builtin_isnanl((long double)x); +} + +/* { dg-final { scan-tree-dump-times "_isnan" 0 "optimized" } } */ +/* { dg-final { scan-tree-dump-times " unord " 0 "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/fold-isnan-2.c b/gcc/testsuite/gcc.dg/fold-isnan-2.c new file mode 100644 index 0000000..32b8833 --- /dev/null +++ b/gcc/testsuite/gcc.dg/fold-isnan-2.c @@ -0,0 +1,21 @@ +/* { dg-do compile } */ +/* { dg-require-effective-target inf } */ +/* { dg-options "-O2 -fdump-tree-optimized" } */ + +int foo(unsigned int x) +{ + return __builtin_isnan((double)x); +} + +int foof(unsigned int x) +{ + return __builtin_isnanf((float)x); +} + +int fool(unsigned int x) +{ + return __builtin_isnanl((long double)x); +} + +/* { dg-final { scan-tree-dump-times "_isnan" 0 "optimized" } } */ +/* { dg-final { scan-tree-dump-times " unord " 0 "optimized" } } */ diff --git a/gcc/builtins.c b/gcc/builtins.c index beb56e0..682e61b 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -9857,9 +9857,10 @@ fold_builtin_classify (location_t loc, tree fndecl, tree arg, int builtin_index) switch (builtin_index) { case BUILT_IN_ISINF: - if (!HONOR_INFINITIES (arg)) + if (tree_expr_infinite_p (arg)) + return omit_one_operand_loc (loc, type, integer_one_node, arg); + if (!tree_expr_maybe_infinite_p (arg)) return omit_one_operand_loc (loc, type, integer_zero_node, arg); - return NULL_TREE; case BUILT_IN_ISINF_SIGN: @@ -9895,14 +9896,16 @@ fold_builtin_classify (location_t loc, tree fndecl, tree arg, int builtin_index) } case BUILT_IN_ISFINITE: - if (!HONOR_NANS (arg) - && !HONOR_INFINITIES (arg)) + if (tree_expr_finite_p (arg)) return omit_one_operand_loc (loc, type, integer_one_node, arg); - + if (tree_expr_nan_p (arg) || tree_expr_infinite_p (arg)) + return omit_one_operand_loc (loc, type, integer_zero_node, arg); return NULL_TREE; case BUILT_IN_ISNAN: - if (!HONOR_NANS (arg)) + if (tree_expr_nan_p (arg)) + return omit_one_operand_loc (loc, type, integer_one_node, arg); + if (!tree_expr_maybe_nan_p (arg)) return omit_one_operand_loc (loc, type, integer_zero_node, arg); { @@ -9976,7 +9979,7 @@ fold_builtin_fpclassify (location_t loc, tree *args, int nargs) arg, build_real (type, r)); res = fold_build3_loc (loc, COND_EXPR, integer_type_node, tmp, fp_normal, res); - if (HONOR_INFINITIES (mode)) + if (tree_expr_maybe_infinite_p (arg)) { real_inf (&r); tmp = fold_build2_loc (loc, EQ_EXPR, integer_type_node, arg, @@ -9985,7 +9988,7 @@ fold_builtin_fpclassify (location_t loc, tree *args, int nargs) fp_infinite, res); } - if (HONOR_NANS (mode)) + if (tree_expr_maybe_nan_p (arg)) { tmp = fold_build2_loc (loc, ORDERED_EXPR, integer_type_node, arg, arg); res = fold_build3_loc (loc, COND_EXPR, integer_type_node, tmp, res, fp_nan); @@ -10033,12 +10036,15 @@ fold_builtin_unordered_cmp (location_t loc, tree fndecl, tree arg0, tree arg1, if (unordered_code == UNORDERED_EXPR) { - if (!HONOR_NANS (arg0)) + if (tree_expr_nan_p (arg0) || tree_expr_nan_p (arg1)) + return omit_two_operands_loc (loc, type, integer_one_node, arg0, arg1); + if (!tree_expr_maybe_nan_p (arg0) && !tree_expr_maybe_nan_p (arg1)) return omit_two_operands_loc (loc, type, integer_zero_node, arg0, arg1); return fold_build2_loc (loc, UNORDERED_EXPR, type, arg0, arg1); } - code = HONOR_NANS (arg0) ? unordered_code : ordered_code; + code = (tree_expr_maybe_nan_p (arg0) || tree_expr_maybe_nan_p (arg1)) + ? unordered_code : ordered_code; return fold_build1_loc (loc, TRUTH_NOT_EXPR, type, fold_build2_loc (loc, code, type, arg0, arg1)); } diff --git a/gcc/fold-const.c b/gcc/fold-const.c index 5d27927..34167f2 100644 --- a/gcc/fold-const.c +++ b/gcc/fold-const.c @@ -13639,6 +13639,248 @@ multiple_of_p (tree type, const_tree top, const_tree bottom) } } +/* Return true if expression X cannot be (or contain) a NaN or infinity. + This function returns true for integer expressions, and returns + false if uncertain. */ + +bool +tree_expr_finite_p (const_tree x) +{ + machine_mode mode = element_mode (x); + if (!HONOR_NANS (mode) && !HONOR_INFINITIES (mode)) + return true; + switch (TREE_CODE (x)) + { + case REAL_CST: + return real_isfinite (TREE_REAL_CST_PTR (x)); + case COMPLEX_CST: + return tree_expr_finite_p (TREE_REALPART (x)) + && tree_expr_finite_p (TREE_IMAGPART (x)); + case FLOAT_EXPR: + return true; + case ABS_EXPR: + case CONVERT_EXPR: + case NON_LVALUE_EXPR: + case NEGATE_EXPR: + case SAVE_EXPR: + return tree_expr_finite_p (TREE_OPERAND (x, 0)); + case MIN_EXPR: + case MAX_EXPR: + return tree_expr_finite_p (TREE_OPERAND (x, 0)) + && tree_expr_finite_p (TREE_OPERAND (x, 1)); + case COND_EXPR: + return tree_expr_finite_p (TREE_OPERAND (x, 1)) + && tree_expr_finite_p (TREE_OPERAND (x, 2)); + case CALL_EXPR: + switch (get_call_combined_fn (x)) + { + CASE_CFN_FABS: + return tree_expr_finite_p (CALL_EXPR_ARG (x, 0)); + CASE_CFN_FMAX: + CASE_CFN_FMIN: + return tree_expr_finite_p (CALL_EXPR_ARG (x, 0)) + && tree_expr_finite_p (CALL_EXPR_ARG (x, 1)); + default: + return false; + } + + default: + return false; + } +} + +/* Return true if expression X evaluates to an infinity. + This function returns false for integer expressions. */ + +bool +tree_expr_infinite_p (const_tree x) +{ + if (!HONOR_INFINITIES (x)) + return false; + switch (TREE_CODE (x)) + { + case REAL_CST: + return real_isinf (TREE_REAL_CST_PTR (x)); + case ABS_EXPR: + case NEGATE_EXPR: + case NON_LVALUE_EXPR: + case SAVE_EXPR: + return tree_expr_infinite_p (TREE_OPERAND (x, 0)); + case COND_EXPR: + return tree_expr_infinite_p (TREE_OPERAND (x, 1)) + && tree_expr_infinite_p (TREE_OPERAND (x, 2)); + default: + return false; + } +} + +/* Return true if expression X could evaluate to an infinity. + This function returns false for integer expressions, and returns + true if uncertain. */ + +bool +tree_expr_maybe_infinite_p (const_tree x) +{ + if (!HONOR_INFINITIES (x)) + return false; + switch (TREE_CODE (x)) + { + case REAL_CST: + return real_isinf (TREE_REAL_CST_PTR (x)); + case FLOAT_EXPR: + return false; + case ABS_EXPR: + case NEGATE_EXPR: + return tree_expr_maybe_infinite_p (TREE_OPERAND (x, 0)); + case COND_EXPR: + return tree_expr_maybe_infinite_p (TREE_OPERAND (x, 1)) + || tree_expr_maybe_infinite_p (TREE_OPERAND (x, 2)); + default: + return true; + } +} + +/* Return true if expression X evaluates to a signaling NaN. + This function returns false for integer expressions. */ + +bool +tree_expr_signaling_nan_p (const_tree x) +{ + if (!HONOR_SNANS (x)) + return false; + switch (TREE_CODE (x)) + { + case REAL_CST: + return real_issignaling_nan (TREE_REAL_CST_PTR (x)); + case NON_LVALUE_EXPR: + case SAVE_EXPR: + return tree_expr_signaling_nan_p (TREE_OPERAND (x, 0)); + case COND_EXPR: + return tree_expr_signaling_nan_p (TREE_OPERAND (x, 1)) + && tree_expr_signaling_nan_p (TREE_OPERAND (x, 2)); + default: + return false; + } +} + +/* Return true if expression X could evaluate to a signaling NaN. + This function returns false for integer expressions, and returns + true if uncertain. */ + +bool +tree_expr_maybe_signaling_nan_p (const_tree x) +{ + if (!HONOR_SNANS (x)) + return false; + switch (TREE_CODE (x)) + { + case REAL_CST: + return real_issignaling_nan (TREE_REAL_CST_PTR (x)); + case FLOAT_EXPR: + return false; + case ABS_EXPR: + case CONVERT_EXPR: + case NEGATE_EXPR: + case NON_LVALUE_EXPR: + case SAVE_EXPR: + return tree_expr_maybe_signaling_nan_p (TREE_OPERAND (x, 0)); + case MIN_EXPR: + case MAX_EXPR: + return tree_expr_maybe_signaling_nan_p (TREE_OPERAND (x, 0)) + || tree_expr_maybe_signaling_nan_p (TREE_OPERAND (x, 1)); + case COND_EXPR: + return tree_expr_maybe_signaling_nan_p (TREE_OPERAND (x, 1)) + || tree_expr_maybe_signaling_nan_p (TREE_OPERAND (x, 2)); + case CALL_EXPR: + switch (get_call_combined_fn (x)) + { + CASE_CFN_FABS: + return tree_expr_maybe_signaling_nan_p (CALL_EXPR_ARG (x, 0)); + CASE_CFN_FMAX: + CASE_CFN_FMIN: + return tree_expr_maybe_signaling_nan_p (CALL_EXPR_ARG (x, 0)) + || tree_expr_maybe_signaling_nan_p (CALL_EXPR_ARG (x, 1)); + default: + return true; + } + default: + return true; + } +} + +/* Return true if expression X evaluates to a NaN. + This function returns false for integer expressions. */ + +bool +tree_expr_nan_p (const_tree x) +{ + if (!HONOR_NANS (x)) + return false; + switch (TREE_CODE (x)) + { + case REAL_CST: + return real_isnan (TREE_REAL_CST_PTR (x)); + case NON_LVALUE_EXPR: + case SAVE_EXPR: + return tree_expr_nan_p (TREE_OPERAND (x, 0)); + case COND_EXPR: + return tree_expr_nan_p (TREE_OPERAND (x, 1)) + && tree_expr_nan_p (TREE_OPERAND (x, 2)); + default: + return false; + } +} + +/* Return true if expression X could evaluate to a NaN. + This function returns false for integer expressions, and returns + true if uncertain. */ + +bool +tree_expr_maybe_nan_p (const_tree x) +{ + if (!HONOR_NANS (x)) + return false; + switch (TREE_CODE (x)) + { + case REAL_CST: + return real_isnan (TREE_REAL_CST_PTR (x)); + case FLOAT_EXPR: + return false; + case PLUS_EXPR: + case MINUS_EXPR: + case MULT_EXPR: + return !tree_expr_finite_p (TREE_OPERAND (x, 0)) + || !tree_expr_finite_p (TREE_OPERAND (x, 1)); + case ABS_EXPR: + case CONVERT_EXPR: + case NEGATE_EXPR: + case NON_LVALUE_EXPR: + case SAVE_EXPR: + return tree_expr_maybe_nan_p (TREE_OPERAND (x, 0)); + case MIN_EXPR: + case MAX_EXPR: + return tree_expr_maybe_nan_p (TREE_OPERAND (x, 0)) + || tree_expr_maybe_nan_p (TREE_OPERAND (x, 1)); + case COND_EXPR: + return tree_expr_maybe_nan_p (TREE_OPERAND (x, 1)) + || tree_expr_maybe_nan_p (TREE_OPERAND (x, 2)); + case CALL_EXPR: + switch (get_call_combined_fn (x)) + { + CASE_CFN_FABS: + return tree_expr_maybe_nan_p (CALL_EXPR_ARG (x, 0)); + CASE_CFN_FMAX: + CASE_CFN_FMIN: + return tree_expr_maybe_nan_p (CALL_EXPR_ARG (x, 0)) + || tree_expr_maybe_nan_p (CALL_EXPR_ARG (x, 1)); + default: + return true; + } + default: + return true; + } +} + #define tree_expr_nonnegative_warnv_p(X, Y) \ _Pragma ("GCC error \"Use RECURSE for recursive calls\"") 0 @@ -13816,7 +14058,13 @@ tree_binary_nonnegative_warnv_p (enum tree_code code, tree type, tree op0, return false; case BIT_AND_EXPR: + return RECURSE (op0) || RECURSE (op1); + case MAX_EXPR: + /* Usually RECURSE (op0) || RECURSE (op1) but NaNs complicate + things. */ + if (tree_expr_maybe_nan_p (op0) || tree_expr_maybe_nan_p (op1)) + return RECURSE (op0) && RECURSE (op1); return RECURSE (op0) || RECURSE (op1); case BIT_IOR_EXPR: @@ -13976,8 +14224,18 @@ tree_call_nonnegative_warnv_p (tree type, combined_fn fn, tree arg0, tree arg1, CASE_CFN_FMAX: CASE_CFN_FMAX_FN: - /* True if the 1st OR 2nd arguments are nonnegative. */ - return RECURSE (arg0) || RECURSE (arg1); + /* 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)); CASE_CFN_FMIN: CASE_CFN_FMIN_FN: diff --git a/gcc/fold-const.h b/gcc/fold-const.h index 0f788a4..dfb98b1 100644 --- a/gcc/fold-const.h +++ b/gcc/fold-const.h @@ -186,6 +186,13 @@ extern tree non_lvalue_loc (location_t, tree); extern bool tree_expr_nonzero_p (tree); extern bool tree_expr_nonnegative_p (tree); extern bool tree_expr_nonnegative_warnv_p (tree, bool *, int = 0); +extern bool tree_expr_finite_p (const_tree); +extern bool tree_expr_infinite_p (const_tree); +extern bool tree_expr_maybe_infinite_p (const_tree); +extern bool tree_expr_signaling_nan_p (const_tree); +extern bool tree_expr_maybe_signaling_nan_p (const_tree); +extern bool tree_expr_nan_p (const_tree); +extern bool tree_expr_maybe_nan_p (const_tree); extern tree make_range (tree, int *, tree *, tree *, bool *); extern tree make_range_step (location_t, enum tree_code, tree, tree, tree, tree *, tree *, int *, bool *); diff --git a/gcc/match.pd b/gcc/match.pd index c3b8816..48e266e 100644 --- a/gcc/match.pd +++ b/gcc/match.pd @@ -4874,6 +4874,24 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) { constant_boolean_node (cmp == ORDERED_EXPR || cmp == LTGT_EXPR ? false : true, type); }))) +/* Fold UNORDERED if either operand must be NaN, or neither can be. */ +(simplify + (unordered @0 @1) + (switch + (if (tree_expr_nan_p (@0) || tree_expr_nan_p (@1)) + { constant_boolean_node (true, type); }) + (if (!tree_expr_maybe_nan_p (@0) && !tree_expr_maybe_nan_p (@1)) + { constant_boolean_node (false, type); }))) + +/* Fold ORDERED if either operand must be NaN, or neither can be. */ +(simplify + (ordered @0 @1) + (switch + (if (tree_expr_nan_p (@0) || tree_expr_nan_p (@1)) + { constant_boolean_node (false, type); }) + (if (!tree_expr_maybe_nan_p (@0) && !tree_expr_maybe_nan_p (@1)) + { constant_boolean_node (true, type); }))) + /* bool_var != 0 becomes bool_var. */ (simplify (ne @0 integer_zerop) @@ -5001,7 +5019,7 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) /* Simplify sqrt(x) * sqrt(x) -> x. */ (simplify (mult (SQRT_ALL@1 @0) @1) - (if (!HONOR_SNANS (type)) + (if (!tree_expr_maybe_signaling_nan_p (@0)) @0)) (for op (plus minus)