Fix middle-end/85811: Introduce tree_expr_maybe_non_p et al.
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 <roger@nextmovesoftware.com> 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.
This commit is contained in:
parent
579d235ddc
commit
1be4878116
@ -10691,9 +10691,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:
|
||||
@ -10729,14 +10730,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);
|
||||
|
||||
{
|
||||
@ -10810,7 +10813,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,
|
||||
@ -10819,7 +10822,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);
|
||||
@ -10867,12 +10870,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));
|
||||
}
|
||||
|
262
gcc/fold-const.c
262
gcc/fold-const.c
@ -13701,6 +13701,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
|
||||
|
||||
@ -13878,7 +14120,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:
|
||||
@ -14038,8 +14286,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:
|
||||
|
@ -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 *);
|
||||
|
20
gcc/match.pd
20
gcc/match.pd
@ -4932,6 +4932,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)
|
||||
@ -5063,7 +5081,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)
|
||||
|
21
gcc/testsuite/gcc.dg/fold-isfinite-1.c
Normal file
21
gcc/testsuite/gcc.dg/fold-isfinite-1.c
Normal file
@ -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" } } */
|
21
gcc/testsuite/gcc.dg/fold-isfinite-2.c
Normal file
21
gcc/testsuite/gcc.dg/fold-isfinite-2.c
Normal file
@ -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" } } */
|
21
gcc/testsuite/gcc.dg/fold-isinf-1.c
Normal file
21
gcc/testsuite/gcc.dg/fold-isinf-1.c
Normal file
@ -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" } } */
|
21
gcc/testsuite/gcc.dg/fold-isinf-2.c
Normal file
21
gcc/testsuite/gcc.dg/fold-isinf-2.c
Normal file
@ -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" } } */
|
21
gcc/testsuite/gcc.dg/fold-isnan-1.c
Normal file
21
gcc/testsuite/gcc.dg/fold-isnan-1.c
Normal file
@ -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" } } */
|
21
gcc/testsuite/gcc.dg/fold-isnan-2.c
Normal file
21
gcc/testsuite/gcc.dg/fold-isnan-2.c
Normal file
@ -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" } } */
|
15
gcc/testsuite/gcc.dg/pr85811.c
Normal file
15
gcc/testsuite/gcc.dg/pr85811.c
Normal file
@ -0,0 +1,15 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2 -fdump-tree-optimized" } */
|
||||
#include <stdio.h>
|
||||
|
||||
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" } } */
|
Loading…
x
Reference in New Issue
Block a user