[PR68097] Try to avoid recursing for floats in tree_*_nonnegative_warnv_p.

Message ID 20221112183048.389811-1-aldyh@redhat.com
State New
Headers
Series [PR68097] Try to avoid recursing for floats in tree_*_nonnegative_warnv_p. |

Commit Message

Aldy Hernandez Nov. 12, 2022, 6:30 p.m. UTC
  It irks me that a PR named "we should track ranges for floating-point
hasn't been closed in this release.  This is an attempt to do just
that.

As mentioned in the PR, even though we track ranges for floats, it has
been suggested that avoiding recursing through SSA defs in
gimple_assign_nonnegative_warnv_p is also a goal.  We can do this with
various ranger components without the need for a heavy handed approach
(i.e. a full ranger).

I have implemented two versions of known_float_sign_p() that answer
the question whether we definitely know the sign for an operation or a
tree expression.

Both versions use get_global_range_query, which is a wrapper to query
global ranges.  This means, that no caching or propagation is done.
In the case of an SSA, we just return the global range for it (think
SSA_NAME_RANGE_INFO).  In the case of a tree code with operands, we
also use get_global_range_query to resolve the operands, and then call
into range-ops, which is our lowest level component.  There is no
ranger or gori involved.  All we're doing is resolving the operation
with the ranges passed.

This is enough to avoid recursing in the case where we definitely know
the sign of a range.  Otherwise, we still recurse.

Note that instead of get_global_range_query(), we could use
get_range_query() which uses a ranger (if active in a pass), or
get_global_range_query if not.  This would allow passes that have an
active ranger (with enable_ranger) to use a full ranger.  These passes
are currently, VRP, loop unswitching, DOM, loop versioning, etc.  If
no ranger is active, get_range_query defaults to global ranges, so
there's no additional penalty.

Would this be acceptable, at least enough to close (or rename the PR ;-))?

	PR tree-optimization/68097

gcc/ChangeLog:

	* fold-const.cc (known_float_sign_p): New.
	(tree_unary_nonnegative_warnv_p): Call known_float_sign_p.
	(tree_binary_nonnegative_warnv_p): Same.
	(tree_single_nonnegative_warnv_p): Same.
---
 gcc/fold-const.cc | 51 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 51 insertions(+)
  

Comments

Richard Biener Nov. 14, 2022, 9:12 a.m. UTC | #1
On Sat, Nov 12, 2022 at 7:30 PM Aldy Hernandez <aldyh@redhat.com> wrote:
>
> It irks me that a PR named "we should track ranges for floating-point
> hasn't been closed in this release.  This is an attempt to do just
> that.
>
> As mentioned in the PR, even though we track ranges for floats, it has
> been suggested that avoiding recursing through SSA defs in
> gimple_assign_nonnegative_warnv_p is also a goal.  We can do this with
> various ranger components without the need for a heavy handed approach
> (i.e. a full ranger).
>
> I have implemented two versions of known_float_sign_p() that answer
> the question whether we definitely know the sign for an operation or a
> tree expression.
>
> Both versions use get_global_range_query, which is a wrapper to query
> global ranges.  This means, that no caching or propagation is done.
> In the case of an SSA, we just return the global range for it (think
> SSA_NAME_RANGE_INFO).  In the case of a tree code with operands, we
> also use get_global_range_query to resolve the operands, and then call
> into range-ops, which is our lowest level component.  There is no
> ranger or gori involved.  All we're doing is resolving the operation
> with the ranges passed.
>
> This is enough to avoid recursing in the case where we definitely know
> the sign of a range.  Otherwise, we still recurse.
>
> Note that instead of get_global_range_query(), we could use
> get_range_query() which uses a ranger (if active in a pass), or
> get_global_range_query if not.  This would allow passes that have an
> active ranger (with enable_ranger) to use a full ranger.  These passes
> are currently, VRP, loop unswitching, DOM, loop versioning, etc.  If
> no ranger is active, get_range_query defaults to global ranges, so
> there's no additional penalty.
>
> Would this be acceptable, at least enough to close (or rename the PR ;-))?

I think the checks would belong to the gimple_stmt_nonnegative_warnv_p function
only (that's the SSA name entry from the fold-const.cc ones)?

I also notice the use of 'bool' for the "sign".  That's not really
descriptive.  We
have SIGNED and UNSIGNED (aka enum signop), not sure if that's the
perfect match vs. NEGATIVE and NONNEGATIVE.  Maybe the functions
name is just bad and they should be known_float_negative_p?

>         PR tree-optimization/68097
>
> gcc/ChangeLog:
>
>         * fold-const.cc (known_float_sign_p): New.
>         (tree_unary_nonnegative_warnv_p): Call known_float_sign_p.
>         (tree_binary_nonnegative_warnv_p): Same.
>         (tree_single_nonnegative_warnv_p): Same.
> ---
>  gcc/fold-const.cc | 51 +++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 51 insertions(+)
>
> diff --git a/gcc/fold-const.cc b/gcc/fold-const.cc
> index b89cac91cae..bd74cfca996 100644
> --- a/gcc/fold-const.cc
> +++ b/gcc/fold-const.cc
> @@ -14577,6 +14577,44 @@ tree_simple_nonnegative_warnv_p (enum tree_code code, tree type)
>    return false;
>  }
>
> +/* Return true if T is of type floating point and has a known sign.
> +   If so, set the sign in SIGN.  */
> +
> +static bool
> +known_float_sign_p (bool &sign, tree t)
> +{
> +  if (!frange::supports_p (TREE_TYPE (t)))
> +    return false;
> +
> +  frange r;
> +  return (get_global_range_query ()->range_of_expr (r, t)
> +         && r.signbit_p (sign));
> +}
> +
> +/* Return true if TYPE is a floating-point type and (CODE OP0 OP1) has
> +   a known sign.  If so, set the sign in SIGN.  */
> +
> +static bool
> +known_float_sign_p (bool &sign, enum tree_code code, tree type, tree op0,
> +                   tree op1 = NULL_TREE)
> +{
> +  if (!frange::supports_p (type))
> +    return false;
> +
> +  range_op_handler handler (code, type);
> +  if (handler)
> +    {
> +      frange res, r0, r1;
> +      get_global_range_query ()->range_of_expr (r0, op0);
> +      if (op1)
> +       get_global_range_query ()->range_of_expr (r1, op1);
> +      else
> +       r1.set_varying (type);
> +      return handler.fold_range (res, type, r0, r1) && res.signbit_p (sign);
> +    }
> +  return false;
> +}
> +
>  /* Return true if (CODE OP0) is known to be non-negative.  If the return
>     value is based on the assumption that signed overflow is undefined,
>     set *STRICT_OVERFLOW_P to true; otherwise, don't change
> @@ -14589,6 +14627,10 @@ tree_unary_nonnegative_warnv_p (enum tree_code code, tree type, tree op0,
>    if (TYPE_UNSIGNED (type))
>      return true;
>
> +  bool sign;
> +  if (known_float_sign_p (sign, code, type, op0))
> +    return !sign;
> +
>    switch (code)
>      {
>      case ABS_EXPR:
> @@ -14656,6 +14698,10 @@ tree_binary_nonnegative_warnv_p (enum tree_code code, tree type, tree op0,
>    if (TYPE_UNSIGNED (type))
>      return true;
>
> +  bool sign;
> +  if (known_float_sign_p (sign, code, type, op0, op1))
> +    return !sign;
> +
>    switch (code)
>      {
>      case POINTER_PLUS_EXPR:
> @@ -14778,6 +14824,8 @@ tree_binary_nonnegative_warnv_p (enum tree_code code, tree type, tree op0,
>  bool
>  tree_single_nonnegative_warnv_p (tree t, bool *strict_overflow_p, int depth)
>  {
> +  bool sign;
> +
>    if (TYPE_UNSIGNED (TREE_TYPE (t)))
>      return true;
>
> @@ -14796,6 +14844,9 @@ tree_single_nonnegative_warnv_p (tree t, bool *strict_overflow_p, int depth)
>        return RECURSE (TREE_OPERAND (t, 1)) && RECURSE (TREE_OPERAND (t, 2));
>
>      case SSA_NAME:
> +      if (known_float_sign_p (sign, t))
> +       return !sign;
> +
>        /* Limit the depth of recursion to avoid quadratic behavior.
>          This is expected to catch almost all occurrences in practice.
>          If this code misses important cases that unbounded recursion
> --
> 2.38.1
>
  
Aldy Hernandez Nov. 14, 2022, 7:05 p.m. UTC | #2
On 11/14/22 10:12, Richard Biener wrote:
> On Sat, Nov 12, 2022 at 7:30 PM Aldy Hernandez <aldyh@redhat.com> wrote:
>>
>> It irks me that a PR named "we should track ranges for floating-point
>> hasn't been closed in this release.  This is an attempt to do just
>> that.
>>
>> As mentioned in the PR, even though we track ranges for floats, it has
>> been suggested that avoiding recursing through SSA defs in
>> gimple_assign_nonnegative_warnv_p is also a goal.  We can do this with
>> various ranger components without the need for a heavy handed approach
>> (i.e. a full ranger).
>>
>> I have implemented two versions of known_float_sign_p() that answer
>> the question whether we definitely know the sign for an operation or a
>> tree expression.
>>
>> Both versions use get_global_range_query, which is a wrapper to query
>> global ranges.  This means, that no caching or propagation is done.
>> In the case of an SSA, we just return the global range for it (think
>> SSA_NAME_RANGE_INFO).  In the case of a tree code with operands, we
>> also use get_global_range_query to resolve the operands, and then call
>> into range-ops, which is our lowest level component.  There is no
>> ranger or gori involved.  All we're doing is resolving the operation
>> with the ranges passed.
>>
>> This is enough to avoid recursing in the case where we definitely know
>> the sign of a range.  Otherwise, we still recurse.
>>
>> Note that instead of get_global_range_query(), we could use
>> get_range_query() which uses a ranger (if active in a pass), or
>> get_global_range_query if not.  This would allow passes that have an
>> active ranger (with enable_ranger) to use a full ranger.  These passes
>> are currently, VRP, loop unswitching, DOM, loop versioning, etc.  If
>> no ranger is active, get_range_query defaults to global ranges, so
>> there's no additional penalty.
>>
>> Would this be acceptable, at least enough to close (or rename the PR ;-))?
> 
> I think the checks would belong to the gimple_stmt_nonnegative_warnv_p function
> only (that's the SSA name entry from the fold-const.cc ones)?

That was my first approach, but I thought I'd cover the unary and binary 
operators as well, since they had other callers.  But I'm happy with 
just the top-level tweak.  It's a lot less code :).

> 
> I also notice the use of 'bool' for the "sign".  That's not really
> descriptive.  We
> have SIGNED and UNSIGNED (aka enum signop), not sure if that's the
> perfect match vs. NEGATIVE and NONNEGATIVE.  Maybe the functions
> name is just bad and they should be known_float_negative_p?

The bool sign is to keep in line with real.*, and was suggested by Jeff 
(in real.* not here).  I'm happy to change the entire frange API to use 
sgnop.  It is cleaner.  If that's acceptable, I could do that as a 
follow-up.

How's this, pending tests once I figure out why my trees have been 
broken all day :-/.

Aldy

p.s. First it was sphinx failure, now I'm seeing this:
/home/aldyh/src/clean/gcc/match.pd:7935:8 error: return statement not 
allowed in C expression
        return NULL_TREE;
        ^
  
Richard Biener Nov. 15, 2022, 7:15 a.m. UTC | #3
On Mon, Nov 14, 2022 at 8:05 PM Aldy Hernandez <aldyh@redhat.com> wrote:
>
>
>
> On 11/14/22 10:12, Richard Biener wrote:
> > On Sat, Nov 12, 2022 at 7:30 PM Aldy Hernandez <aldyh@redhat.com> wrote:
> >>
> >> It irks me that a PR named "we should track ranges for floating-point
> >> hasn't been closed in this release.  This is an attempt to do just
> >> that.
> >>
> >> As mentioned in the PR, even though we track ranges for floats, it has
> >> been suggested that avoiding recursing through SSA defs in
> >> gimple_assign_nonnegative_warnv_p is also a goal.  We can do this with
> >> various ranger components without the need for a heavy handed approach
> >> (i.e. a full ranger).
> >>
> >> I have implemented two versions of known_float_sign_p() that answer
> >> the question whether we definitely know the sign for an operation or a
> >> tree expression.
> >>
> >> Both versions use get_global_range_query, which is a wrapper to query
> >> global ranges.  This means, that no caching or propagation is done.
> >> In the case of an SSA, we just return the global range for it (think
> >> SSA_NAME_RANGE_INFO).  In the case of a tree code with operands, we
> >> also use get_global_range_query to resolve the operands, and then call
> >> into range-ops, which is our lowest level component.  There is no
> >> ranger or gori involved.  All we're doing is resolving the operation
> >> with the ranges passed.
> >>
> >> This is enough to avoid recursing in the case where we definitely know
> >> the sign of a range.  Otherwise, we still recurse.
> >>
> >> Note that instead of get_global_range_query(), we could use
> >> get_range_query() which uses a ranger (if active in a pass), or
> >> get_global_range_query if not.  This would allow passes that have an
> >> active ranger (with enable_ranger) to use a full ranger.  These passes
> >> are currently, VRP, loop unswitching, DOM, loop versioning, etc.  If
> >> no ranger is active, get_range_query defaults to global ranges, so
> >> there's no additional penalty.
> >>
> >> Would this be acceptable, at least enough to close (or rename the PR ;-))?
> >
> > I think the checks would belong to the gimple_stmt_nonnegative_warnv_p function
> > only (that's the SSA name entry from the fold-const.cc ones)?
>
> That was my first approach, but I thought I'd cover the unary and binary
> operators as well, since they had other callers.  But I'm happy with
> just the top-level tweak.  It's a lot less code :).

@@ -9234,6 +9235,15 @@ bool
 gimple_stmt_nonnegative_warnv_p (gimple *stmt, bool *strict_overflow_p,
                                 int depth)
 {
+  tree type = gimple_range_type (stmt);
+  if (type && frange::supports_p (type))
+    {
+      frange r;
+      bool sign;
+      return (get_global_range_query ()->range_of_stmt (r, stmt)
+             && r.signbit_p (sign)
+             && sign == false);
+    }

the above means we never fall through to the switch below if
frange::supports_p (type) - that's eventually good enough, I
don't think we ever call this very function directly but it gets
invoked via recursion through operands only.  But of course
I wonder what types are not supported by frange and whether
the manual processing we fall through to does anything meaningful
for those?

I won't ask you to thoroughly answer this now but please put in
a comment reflecting the above before the switch stmt.

   switch (gimple_code (stmt))


Otherwise OK, in case you tree gets back to bootstrapping ;)

> >
> > I also notice the use of 'bool' for the "sign".  That's not really
> > descriptive.  We
> > have SIGNED and UNSIGNED (aka enum signop), not sure if that's the
> > perfect match vs. NEGATIVE and NONNEGATIVE.  Maybe the functions
> > name is just bad and they should be known_float_negative_p?
>
> The bool sign is to keep in line with real.*, and was suggested by Jeff
> (in real.* not here).  I'm happy to change the entire frange API to use
> sgnop.  It is cleaner.  If that's acceptable, I could do that as a
> follow-up.
>
> How's this, pending tests once I figure out why my trees have been
> broken all day :-/.
>
> Aldy
>
> p.s. First it was sphinx failure, now I'm seeing this:
> /home/aldyh/src/clean/gcc/match.pd:7935:8 error: return statement not
> allowed in C expression
>         return NULL_TREE;
>         ^

Supposedly somebody pushed and reverted this transient error?  Yep,
Tamar did.

Richard.
  
Aldy Hernandez Nov. 15, 2022, 10:46 a.m. UTC | #4
On 11/15/22 08:15, Richard Biener wrote:
> On Mon, Nov 14, 2022 at 8:05 PM Aldy Hernandez <aldyh@redhat.com> wrote:
>>
>>
>>
>> On 11/14/22 10:12, Richard Biener wrote:
>>> On Sat, Nov 12, 2022 at 7:30 PM Aldy Hernandez <aldyh@redhat.com> wrote:
>>>>
>>>> It irks me that a PR named "we should track ranges for floating-point
>>>> hasn't been closed in this release.  This is an attempt to do just
>>>> that.
>>>>
>>>> As mentioned in the PR, even though we track ranges for floats, it has
>>>> been suggested that avoiding recursing through SSA defs in
>>>> gimple_assign_nonnegative_warnv_p is also a goal.  We can do this with
>>>> various ranger components without the need for a heavy handed approach
>>>> (i.e. a full ranger).
>>>>
>>>> I have implemented two versions of known_float_sign_p() that answer
>>>> the question whether we definitely know the sign for an operation or a
>>>> tree expression.
>>>>
>>>> Both versions use get_global_range_query, which is a wrapper to query
>>>> global ranges.  This means, that no caching or propagation is done.
>>>> In the case of an SSA, we just return the global range for it (think
>>>> SSA_NAME_RANGE_INFO).  In the case of a tree code with operands, we
>>>> also use get_global_range_query to resolve the operands, and then call
>>>> into range-ops, which is our lowest level component.  There is no
>>>> ranger or gori involved.  All we're doing is resolving the operation
>>>> with the ranges passed.
>>>>
>>>> This is enough to avoid recursing in the case where we definitely know
>>>> the sign of a range.  Otherwise, we still recurse.
>>>>
>>>> Note that instead of get_global_range_query(), we could use
>>>> get_range_query() which uses a ranger (if active in a pass), or
>>>> get_global_range_query if not.  This would allow passes that have an
>>>> active ranger (with enable_ranger) to use a full ranger.  These passes
>>>> are currently, VRP, loop unswitching, DOM, loop versioning, etc.  If
>>>> no ranger is active, get_range_query defaults to global ranges, so
>>>> there's no additional penalty.
>>>>
>>>> Would this be acceptable, at least enough to close (or rename the PR ;-))?
>>>
>>> I think the checks would belong to the gimple_stmt_nonnegative_warnv_p function
>>> only (that's the SSA name entry from the fold-const.cc ones)?
>>
>> That was my first approach, but I thought I'd cover the unary and binary
>> operators as well, since they had other callers.  But I'm happy with
>> just the top-level tweak.  It's a lot less code :).
> 
> @@ -9234,6 +9235,15 @@ bool
>   gimple_stmt_nonnegative_warnv_p (gimple *stmt, bool *strict_overflow_p,
>                                   int depth)
>   {
> +  tree type = gimple_range_type (stmt);
> +  if (type && frange::supports_p (type))
> +    {
> +      frange r;
> +      bool sign;
> +      return (get_global_range_query ()->range_of_stmt (r, stmt)
> +             && r.signbit_p (sign)
> +             && sign == false);
> +    }
> 
> the above means we never fall through to the switch below if
> frange::supports_p (type) - that's eventually good enough, I
> don't think we ever call this very function directly but it gets
> invoked via recursion through operands only.  But of course

Woah, sorry.  That was not intended.  For that matter, the patch as 
posted caused:

FAIL: gcc.dg/builtins-10.c (test for excess errors)
FAIL: gcc.dg/builtins-57.c (test for excess errors)
FAIL: gcc.dg/torture/builtin-nonneg-1.c   -O1  (test for excess errors)
FAIL: gcc.dg/torture/builtin-nonneg-1.c   -O2  (test for excess errors)
FAIL: gcc.dg/torture/builtin-nonneg-1.c   -O2 -flto 
-fno-use-linker-plugin -flto-partition=none  (test for excess errors)
FAIL: gcc.dg/torture/builtin-nonneg-1.c   -O3 -g  (test for excess errors)
FAIL: gcc.dg/torture/builtin-nonneg-1.c   -Os  (test for excess errors)
FAIL: gcc.dg/torture/builtin-power-1.c   -O1  (test for excess errors)
FAIL: gcc.dg/torture/builtin-power-1.c   -O2  (test for excess errors)
FAIL: gcc.dg/torture/builtin-power-1.c   -O2 -flto 
-fno-use-linker-plugin -flto-partition=none  (test for excess errors)
FAIL: gcc.dg/torture/builtin-power-1.c   -O3 -g  (test for excess errors)
FAIL: gcc.dg/torture/builtin-power-1.c   -Os  (test for excess errors)

Note that ranger folding calls this function, though it won't run the 
risk of endless recursion because range_of_stmt uses the LHS, and only 
use global ranges to solve the LHS.

Also, frange::supports_p() does not support all floats:

   static bool supports_p (const_tree type)
   {
     // ?? Decimal floats can have multiple representations for the
     // same number.  Supporting them may be as simple as just
     // disabling them in singleton_p.  No clue.
     return SCALAR_FLOAT_TYPE_P (type) && !DECIMAL_FLOAT_TYPE_P (type);
   }

Finally, my patch is more conservative than what the *nonnegative_warn* 
friends do.  We only return true when we're sure about the sign bit and 
it's FALSE.  As I mentioned elsewhere, tree_call_nonnegative_warn_p() 
always returns true for:

     CASE_CFN_ACOS:
     CASE_CFN_ACOS_FN:
     CASE_CFN_ACOSH:
     CASE_CFN_ACOSH_FN:
     CASE_CFN_CABS:
     CASE_CFN_CABS_FN:
...
...
       /* Always true.  */
       return true;

This means that we'll return true for a NAN, but we're incorrectly 
assuming the NAN will be +NAN.  In my proposed patch, we don't make such 
assumptions.  We only return true if the range is non-negative, 
including the NAN.

> I wonder what types are not supported by frange and whether
> the manual processing we fall through to does anything meaningful
> for those?
> 
> I won't ask you to thoroughly answer this now but please put in
> a comment reflecting the above before the switch stmt.
> 
>     switch (gimple_code (stmt))
> 
> 
> Otherwise OK, in case you tree gets back to bootstrapping ;)

Updated patch that passes test.

OK?  And if so, can I close the PR?

Thanks.
Aldy
  
Aldy Hernandez Nov. 15, 2022, 1:52 p.m. UTC | #5
On Mon, Nov 14, 2022 at 10:12 AM Richard Biener
<richard.guenther@gmail.com> wrote:
>
> On Sat, Nov 12, 2022 at 7:30 PM Aldy Hernandez <aldyh@redhat.com> wrote:
> >
> > It irks me that a PR named "we should track ranges for floating-point
> > hasn't been closed in this release.  This is an attempt to do just
> > that.
> >
> > As mentioned in the PR, even though we track ranges for floats, it has
> > been suggested that avoiding recursing through SSA defs in
> > gimple_assign_nonnegative_warnv_p is also a goal.  We can do this with
> > various ranger components without the need for a heavy handed approach
> > (i.e. a full ranger).
> >
> > I have implemented two versions of known_float_sign_p() that answer
> > the question whether we definitely know the sign for an operation or a
> > tree expression.
> >
> > Both versions use get_global_range_query, which is a wrapper to query
> > global ranges.  This means, that no caching or propagation is done.
> > In the case of an SSA, we just return the global range for it (think
> > SSA_NAME_RANGE_INFO).  In the case of a tree code with operands, we
> > also use get_global_range_query to resolve the operands, and then call
> > into range-ops, which is our lowest level component.  There is no
> > ranger or gori involved.  All we're doing is resolving the operation
> > with the ranges passed.
> >
> > This is enough to avoid recursing in the case where we definitely know
> > the sign of a range.  Otherwise, we still recurse.
> >
> > Note that instead of get_global_range_query(), we could use
> > get_range_query() which uses a ranger (if active in a pass), or
> > get_global_range_query if not.  This would allow passes that have an
> > active ranger (with enable_ranger) to use a full ranger.  These passes
> > are currently, VRP, loop unswitching, DOM, loop versioning, etc.  If
> > no ranger is active, get_range_query defaults to global ranges, so
> > there's no additional penalty.
> >
> > Would this be acceptable, at least enough to close (or rename the PR ;-))?
>
> I think the checks would belong to the gimple_stmt_nonnegative_warnv_p function
> only (that's the SSA name entry from the fold-const.cc ones)?
>
> I also notice the use of 'bool' for the "sign".  That's not really
> descriptive.  We
> have SIGNED and UNSIGNED (aka enum signop), not sure if that's the
> perfect match vs. NEGATIVE and NONNEGATIVE.  Maybe the functions
> name is just bad and they should be known_float_negative_p?

Yeah, SIGNED and UNSIGNED doesn't seem to be much clearer than "bool signbit".

For instance, we have the following in frange:

  void set_nan (tree type, bool sign);
  void update_nan (bool sign);
  bool maybe_isnan (bool sign) const;
  bool signbit_p (bool &signbit) const;

I'm OK changing them to enum signop if you prefer.  I'm just not
totally convinced it's more readable.

??

Aldy
  
Richard Biener Nov. 16, 2022, 3:59 p.m. UTC | #6
On Tue, Nov 15, 2022 at 2:52 PM Aldy Hernandez <aldyh@redhat.com> wrote:
>
> On Mon, Nov 14, 2022 at 10:12 AM Richard Biener
> <richard.guenther@gmail.com> wrote:
> >
> > On Sat, Nov 12, 2022 at 7:30 PM Aldy Hernandez <aldyh@redhat.com> wrote:
> > >
> > > It irks me that a PR named "we should track ranges for floating-point
> > > hasn't been closed in this release.  This is an attempt to do just
> > > that.
> > >
> > > As mentioned in the PR, even though we track ranges for floats, it has
> > > been suggested that avoiding recursing through SSA defs in
> > > gimple_assign_nonnegative_warnv_p is also a goal.  We can do this with
> > > various ranger components without the need for a heavy handed approach
> > > (i.e. a full ranger).
> > >
> > > I have implemented two versions of known_float_sign_p() that answer
> > > the question whether we definitely know the sign for an operation or a
> > > tree expression.
> > >
> > > Both versions use get_global_range_query, which is a wrapper to query
> > > global ranges.  This means, that no caching or propagation is done.
> > > In the case of an SSA, we just return the global range for it (think
> > > SSA_NAME_RANGE_INFO).  In the case of a tree code with operands, we
> > > also use get_global_range_query to resolve the operands, and then call
> > > into range-ops, which is our lowest level component.  There is no
> > > ranger or gori involved.  All we're doing is resolving the operation
> > > with the ranges passed.
> > >
> > > This is enough to avoid recursing in the case where we definitely know
> > > the sign of a range.  Otherwise, we still recurse.
> > >
> > > Note that instead of get_global_range_query(), we could use
> > > get_range_query() which uses a ranger (if active in a pass), or
> > > get_global_range_query if not.  This would allow passes that have an
> > > active ranger (with enable_ranger) to use a full ranger.  These passes
> > > are currently, VRP, loop unswitching, DOM, loop versioning, etc.  If
> > > no ranger is active, get_range_query defaults to global ranges, so
> > > there's no additional penalty.
> > >
> > > Would this be acceptable, at least enough to close (or rename the PR ;-))?
> >
> > I think the checks would belong to the gimple_stmt_nonnegative_warnv_p function
> > only (that's the SSA name entry from the fold-const.cc ones)?
> >
> > I also notice the use of 'bool' for the "sign".  That's not really
> > descriptive.  We
> > have SIGNED and UNSIGNED (aka enum signop), not sure if that's the
> > perfect match vs. NEGATIVE and NONNEGATIVE.  Maybe the functions
> > name is just bad and they should be known_float_negative_p?
>
> Yeah, SIGNED and UNSIGNED doesn't seem to be much clearer than "bool signbit".
>
> For instance, we have the following in frange:
>
>   void set_nan (tree type, bool sign);
>   void update_nan (bool sign);
>   bool maybe_isnan (bool sign) const;
>   bool signbit_p (bool &signbit) const;
>
> I'm OK changing them to enum signop if you prefer.  I'm just not
> totally convinced it's more readable.
>
> ??

I think when talking about 'signbit' or 'sign' the old usual 'unsigned' type is
better than bool.    signbit_p feels a bit redundant (the _p).

We could have another enum like signop just I think the obvious
candidates { POSITIVE, NEGATIVE } or { NEGATIVE, NONNEGATIVE }
aren't too great.  The obvious alternative is to follow the existing uns_p
(unsigned?) parameters and not use bool sign but bool unsigned_p,
there 'true' and 'false' are obvious.  For 'signbit_p' it would then be
bool signbit_p (unsigned &signbit) being the true value of the bit
(and the return value indicates the UNKNOWN case).

Richard.

>
> Aldy
>
  
Richard Biener Nov. 16, 2022, 4:04 p.m. UTC | #7
On Tue, Nov 15, 2022 at 11:46 AM Aldy Hernandez <aldyh@redhat.com> wrote:
>
>
>
> On 11/15/22 08:15, Richard Biener wrote:
> > On Mon, Nov 14, 2022 at 8:05 PM Aldy Hernandez <aldyh@redhat.com> wrote:
> >>
> >>
> >>
> >> On 11/14/22 10:12, Richard Biener wrote:
> >>> On Sat, Nov 12, 2022 at 7:30 PM Aldy Hernandez <aldyh@redhat.com> wrote:
> >>>>
> >>>> It irks me that a PR named "we should track ranges for floating-point
> >>>> hasn't been closed in this release.  This is an attempt to do just
> >>>> that.
> >>>>
> >>>> As mentioned in the PR, even though we track ranges for floats, it has
> >>>> been suggested that avoiding recursing through SSA defs in
> >>>> gimple_assign_nonnegative_warnv_p is also a goal.  We can do this with
> >>>> various ranger components without the need for a heavy handed approach
> >>>> (i.e. a full ranger).
> >>>>
> >>>> I have implemented two versions of known_float_sign_p() that answer
> >>>> the question whether we definitely know the sign for an operation or a
> >>>> tree expression.
> >>>>
> >>>> Both versions use get_global_range_query, which is a wrapper to query
> >>>> global ranges.  This means, that no caching or propagation is done.
> >>>> In the case of an SSA, we just return the global range for it (think
> >>>> SSA_NAME_RANGE_INFO).  In the case of a tree code with operands, we
> >>>> also use get_global_range_query to resolve the operands, and then call
> >>>> into range-ops, which is our lowest level component.  There is no
> >>>> ranger or gori involved.  All we're doing is resolving the operation
> >>>> with the ranges passed.
> >>>>
> >>>> This is enough to avoid recursing in the case where we definitely know
> >>>> the sign of a range.  Otherwise, we still recurse.
> >>>>
> >>>> Note that instead of get_global_range_query(), we could use
> >>>> get_range_query() which uses a ranger (if active in a pass), or
> >>>> get_global_range_query if not.  This would allow passes that have an
> >>>> active ranger (with enable_ranger) to use a full ranger.  These passes
> >>>> are currently, VRP, loop unswitching, DOM, loop versioning, etc.  If
> >>>> no ranger is active, get_range_query defaults to global ranges, so
> >>>> there's no additional penalty.
> >>>>
> >>>> Would this be acceptable, at least enough to close (or rename the PR ;-))?
> >>>
> >>> I think the checks would belong to the gimple_stmt_nonnegative_warnv_p function
> >>> only (that's the SSA name entry from the fold-const.cc ones)?
> >>
> >> That was my first approach, but I thought I'd cover the unary and binary
> >> operators as well, since they had other callers.  But I'm happy with
> >> just the top-level tweak.  It's a lot less code :).
> >
> > @@ -9234,6 +9235,15 @@ bool
> >   gimple_stmt_nonnegative_warnv_p (gimple *stmt, bool *strict_overflow_p,
> >                                   int depth)
> >   {
> > +  tree type = gimple_range_type (stmt);
> > +  if (type && frange::supports_p (type))
> > +    {
> > +      frange r;
> > +      bool sign;
> > +      return (get_global_range_query ()->range_of_stmt (r, stmt)
> > +             && r.signbit_p (sign)
> > +             && sign == false);
> > +    }
> >
> > the above means we never fall through to the switch below if
> > frange::supports_p (type) - that's eventually good enough, I
> > don't think we ever call this very function directly but it gets
> > invoked via recursion through operands only.  But of course
>
> Woah, sorry.  That was not intended.  For that matter, the patch as
> posted caused:
>
> FAIL: gcc.dg/builtins-10.c (test for excess errors)
> FAIL: gcc.dg/builtins-57.c (test for excess errors)
> FAIL: gcc.dg/torture/builtin-nonneg-1.c   -O1  (test for excess errors)
> FAIL: gcc.dg/torture/builtin-nonneg-1.c   -O2  (test for excess errors)
> FAIL: gcc.dg/torture/builtin-nonneg-1.c   -O2 -flto
> -fno-use-linker-plugin -flto-partition=none  (test for excess errors)
> FAIL: gcc.dg/torture/builtin-nonneg-1.c   -O3 -g  (test for excess errors)
> FAIL: gcc.dg/torture/builtin-nonneg-1.c   -Os  (test for excess errors)
> FAIL: gcc.dg/torture/builtin-power-1.c   -O1  (test for excess errors)
> FAIL: gcc.dg/torture/builtin-power-1.c   -O2  (test for excess errors)
> FAIL: gcc.dg/torture/builtin-power-1.c   -O2 -flto
> -fno-use-linker-plugin -flto-partition=none  (test for excess errors)
> FAIL: gcc.dg/torture/builtin-power-1.c   -O3 -g  (test for excess errors)
> FAIL: gcc.dg/torture/builtin-power-1.c   -Os  (test for excess errors)

Did you investigate why?  Because the old patch removed the recursion
while the new keeps it in case the global range isn't present which isn't
as nice.

> Note that ranger folding calls this function, though it won't run the
> risk of endless recursion because range_of_stmt uses the LHS, and only
> use global ranges to solve the LHS.
>
> Also, frange::supports_p() does not support all floats:
>
>    static bool supports_p (const_tree type)
>    {
>      // ?? Decimal floats can have multiple representations for the
>      // same number.  Supporting them may be as simple as just
>      // disabling them in singleton_p.  No clue.
>      return SCALAR_FLOAT_TYPE_P (type) && !DECIMAL_FLOAT_TYPE_P (type);
>    }

OK, _Complex types are obviously missing, so are vector ones.

> Finally, my patch is more conservative than what the *nonnegative_warn*
> friends do.  We only return true when we're sure about the sign bit and
> it's FALSE.  As I mentioned elsewhere, tree_call_nonnegative_warn_p()
> always returns true for:
>
>      CASE_CFN_ACOS:
>      CASE_CFN_ACOS_FN:
>      CASE_CFN_ACOSH:
>      CASE_CFN_ACOSH_FN:
>      CASE_CFN_CABS:
>      CASE_CFN_CABS_FN:
> ...
> ...
>        /* Always true.  */
>        return true;
>
> This means that we'll return true for a NAN, but we're incorrectly
> assuming the NAN will be +NAN.  In my proposed patch, we don't make such
> assumptions.  We only return true if the range is non-negative,
> including the NAN.

Yep, the usual issue whether nonnegative means copysign (1, x) produces
1 or whether !(x < 0) is true.

> > I wonder what types are not supported by frange and whether
> > the manual processing we fall through to does anything meaningful
> > for those?
> >
> > I won't ask you to thoroughly answer this now but please put in
> > a comment reflecting the above before the switch stmt.
> >
> >     switch (gimple_code (stmt))
> >
> >
> > Otherwise OK, in case you tree gets back to bootstrapping ;)
>
> Updated patch that passes test.
>
> OK?  And if so, can I close the PR?

Yes, I think we now track float ranges - improvements are of course
always possible.

Richard.

> Thanks.
> Aldy
  
Aldy Hernandez Nov. 16, 2022, 5:38 p.m. UTC | #8
On 11/16/22 17:04, Richard Biener wrote:
> On Tue, Nov 15, 2022 at 11:46 AM Aldy Hernandez <aldyh@redhat.com> wrote:
>>
>>
>>
>> On 11/15/22 08:15, Richard Biener wrote:
>>> On Mon, Nov 14, 2022 at 8:05 PM Aldy Hernandez <aldyh@redhat.com> wrote:
>>>>
>>>>
>>>>
>>>> On 11/14/22 10:12, Richard Biener wrote:
>>>>> On Sat, Nov 12, 2022 at 7:30 PM Aldy Hernandez <aldyh@redhat.com> wrote:
>>>>>>
>>>>>> It irks me that a PR named "we should track ranges for floating-point
>>>>>> hasn't been closed in this release.  This is an attempt to do just
>>>>>> that.
>>>>>>
>>>>>> As mentioned in the PR, even though we track ranges for floats, it has
>>>>>> been suggested that avoiding recursing through SSA defs in
>>>>>> gimple_assign_nonnegative_warnv_p is also a goal.  We can do this with
>>>>>> various ranger components without the need for a heavy handed approach
>>>>>> (i.e. a full ranger).
>>>>>>
>>>>>> I have implemented two versions of known_float_sign_p() that answer
>>>>>> the question whether we definitely know the sign for an operation or a
>>>>>> tree expression.
>>>>>>
>>>>>> Both versions use get_global_range_query, which is a wrapper to query
>>>>>> global ranges.  This means, that no caching or propagation is done.
>>>>>> In the case of an SSA, we just return the global range for it (think
>>>>>> SSA_NAME_RANGE_INFO).  In the case of a tree code with operands, we
>>>>>> also use get_global_range_query to resolve the operands, and then call
>>>>>> into range-ops, which is our lowest level component.  There is no
>>>>>> ranger or gori involved.  All we're doing is resolving the operation
>>>>>> with the ranges passed.
>>>>>>
>>>>>> This is enough to avoid recursing in the case where we definitely know
>>>>>> the sign of a range.  Otherwise, we still recurse.
>>>>>>
>>>>>> Note that instead of get_global_range_query(), we could use
>>>>>> get_range_query() which uses a ranger (if active in a pass), or
>>>>>> get_global_range_query if not.  This would allow passes that have an
>>>>>> active ranger (with enable_ranger) to use a full ranger.  These passes
>>>>>> are currently, VRP, loop unswitching, DOM, loop versioning, etc.  If
>>>>>> no ranger is active, get_range_query defaults to global ranges, so
>>>>>> there's no additional penalty.
>>>>>>
>>>>>> Would this be acceptable, at least enough to close (or rename the PR ;-))?
>>>>>
>>>>> I think the checks would belong to the gimple_stmt_nonnegative_warnv_p function
>>>>> only (that's the SSA name entry from the fold-const.cc ones)?
>>>>
>>>> That was my first approach, but I thought I'd cover the unary and binary
>>>> operators as well, since they had other callers.  But I'm happy with
>>>> just the top-level tweak.  It's a lot less code :).
>>>
>>> @@ -9234,6 +9235,15 @@ bool
>>>    gimple_stmt_nonnegative_warnv_p (gimple *stmt, bool *strict_overflow_p,
>>>                                    int depth)
>>>    {
>>> +  tree type = gimple_range_type (stmt);
>>> +  if (type && frange::supports_p (type))
>>> +    {
>>> +      frange r;
>>> +      bool sign;
>>> +      return (get_global_range_query ()->range_of_stmt (r, stmt)
>>> +             && r.signbit_p (sign)
>>> +             && sign == false);
>>> +    }
>>>
>>> the above means we never fall through to the switch below if
>>> frange::supports_p (type) - that's eventually good enough, I
>>> don't think we ever call this very function directly but it gets
>>> invoked via recursion through operands only.  But of course
>>
>> Woah, sorry.  That was not intended.  For that matter, the patch as
>> posted caused:
>>
>> FAIL: gcc.dg/builtins-10.c (test for excess errors)
>> FAIL: gcc.dg/builtins-57.c (test for excess errors)
>> FAIL: gcc.dg/torture/builtin-nonneg-1.c   -O1  (test for excess errors)
>> FAIL: gcc.dg/torture/builtin-nonneg-1.c   -O2  (test for excess errors)
>> FAIL: gcc.dg/torture/builtin-nonneg-1.c   -O2 -flto
>> -fno-use-linker-plugin -flto-partition=none  (test for excess errors)
>> FAIL: gcc.dg/torture/builtin-nonneg-1.c   -O3 -g  (test for excess errors)
>> FAIL: gcc.dg/torture/builtin-nonneg-1.c   -Os  (test for excess errors)
>> FAIL: gcc.dg/torture/builtin-power-1.c   -O1  (test for excess errors)
>> FAIL: gcc.dg/torture/builtin-power-1.c   -O2  (test for excess errors)
>> FAIL: gcc.dg/torture/builtin-power-1.c   -O2 -flto
>> -fno-use-linker-plugin -flto-partition=none  (test for excess errors)
>> FAIL: gcc.dg/torture/builtin-power-1.c   -O3 -g  (test for excess errors)
>> FAIL: gcc.dg/torture/builtin-power-1.c   -Os  (test for excess errors)
> 
> Did you investigate why?  Because the old patch removed the recursion
> while the new keeps it in case the global range isn't present which isn't
> as nice.

For gcc.dg/builtins-10.c, there are a few calls to 
gimple_stmt_nonnegative* that are being made before we have global 
ranges (ccp1 and forwprop1), so the query returns VARYING (i.e. no known 
sign).  If you're curious, the call to gimple_stmt_nonnegative* comes 
via courtesy of match.pd:

/* Canonicalization of sequences of math builtins.  These rules represent
    IL simplifications but are not necessarily optimizations.

So ISTM, we still need to fall through if we're being called before 
global ranges are available.

After global ranges are available (evrp), we would avoid further lookups 
if it weren't for an unrelated problem I found.

foperator_abs::fold_range() is trying to set a range of [+0.0, +INF], 
but this little snpipet in the frange normalization code adds a -0.0 to 
the range:

   else if (!HONOR_SIGNED_ZEROS (m_type))
     {
       if (real_iszero (&m_max, 1))
	m_max.sign = 0;
       if (real_iszero (&m_min, 0))
	m_min.sign = 1;
     }

We end up with:

[frange] double [-0.0 (-0x0.0p+0), 
1.79769313486231570814527423731704356798070567525844996599e+308 
(0x0.fffffffffffff8p+1024)]

I must say this is beyond my paygrade :).  Jakub, it was your suggestion 
to add the snippet above.  Is this correct?  Note that this test is for 
-ffast-math.

If I comment out the code above, the regressions are fixed, both with my 
current patch or with the original one.  But as I suggested, maybe we 
want the second patch, because we may be called before global ranges are 
available.

IMHO, we could go with the second patch, and fix the ABS problem 
independently.

Yay?  Nay?

Aldy

> 
>> Note that ranger folding calls this function, though it won't run the
>> risk of endless recursion because range_of_stmt uses the LHS, and only
>> use global ranges to solve the LHS.
>>
>> Also, frange::supports_p() does not support all floats:
>>
>>     static bool supports_p (const_tree type)
>>     {
>>       // ?? Decimal floats can have multiple representations for the
>>       // same number.  Supporting them may be as simple as just
>>       // disabling them in singleton_p.  No clue.
>>       return SCALAR_FLOAT_TYPE_P (type) && !DECIMAL_FLOAT_TYPE_P (type);
>>     }
> 
> OK, _Complex types are obviously missing, so are vector ones.
> 
>> Finally, my patch is more conservative than what the *nonnegative_warn*
>> friends do.  We only return true when we're sure about the sign bit and
>> it's FALSE.  As I mentioned elsewhere, tree_call_nonnegative_warn_p()
>> always returns true for:
>>
>>       CASE_CFN_ACOS:
>>       CASE_CFN_ACOS_FN:
>>       CASE_CFN_ACOSH:
>>       CASE_CFN_ACOSH_FN:
>>       CASE_CFN_CABS:
>>       CASE_CFN_CABS_FN:
>> ...
>> ...
>>         /* Always true.  */
>>         return true;
>>
>> This means that we'll return true for a NAN, but we're incorrectly
>> assuming the NAN will be +NAN.  In my proposed patch, we don't make such
>> assumptions.  We only return true if the range is non-negative,
>> including the NAN.
> 
> Yep, the usual issue whether nonnegative means copysign (1, x) produces
> 1 or whether !(x < 0) is true.
> 
>>> I wonder what types are not supported by frange and whether
>>> the manual processing we fall through to does anything meaningful
>>> for those?
>>>
>>> I won't ask you to thoroughly answer this now but please put in
>>> a comment reflecting the above before the switch stmt.
>>>
>>>      switch (gimple_code (stmt))
>>>
>>>
>>> Otherwise OK, in case you tree gets back to bootstrapping ;)
>>
>> Updated patch that passes test.
>>
>> OK?  And if so, can I close the PR?
> 
> Yes, I think we now track float ranges - improvements are of course
> always possible.
> 
> Richard.
> 
>> Thanks.
>> Aldy
>
  
Richard Biener Nov. 17, 2022, 8:01 a.m. UTC | #9
On Wed, Nov 16, 2022 at 6:38 PM Aldy Hernandez <aldyh@redhat.com> wrote:
>
>
>
> On 11/16/22 17:04, Richard Biener wrote:
> > On Tue, Nov 15, 2022 at 11:46 AM Aldy Hernandez <aldyh@redhat.com> wrote:
> >>
> >>
> >>
> >> On 11/15/22 08:15, Richard Biener wrote:
> >>> On Mon, Nov 14, 2022 at 8:05 PM Aldy Hernandez <aldyh@redhat.com> wrote:
> >>>>
> >>>>
> >>>>
> >>>> On 11/14/22 10:12, Richard Biener wrote:
> >>>>> On Sat, Nov 12, 2022 at 7:30 PM Aldy Hernandez <aldyh@redhat.com> wrote:
> >>>>>>
> >>>>>> It irks me that a PR named "we should track ranges for floating-point
> >>>>>> hasn't been closed in this release.  This is an attempt to do just
> >>>>>> that.
> >>>>>>
> >>>>>> As mentioned in the PR, even though we track ranges for floats, it has
> >>>>>> been suggested that avoiding recursing through SSA defs in
> >>>>>> gimple_assign_nonnegative_warnv_p is also a goal.  We can do this with
> >>>>>> various ranger components without the need for a heavy handed approach
> >>>>>> (i.e. a full ranger).
> >>>>>>
> >>>>>> I have implemented two versions of known_float_sign_p() that answer
> >>>>>> the question whether we definitely know the sign for an operation or a
> >>>>>> tree expression.
> >>>>>>
> >>>>>> Both versions use get_global_range_query, which is a wrapper to query
> >>>>>> global ranges.  This means, that no caching or propagation is done.
> >>>>>> In the case of an SSA, we just return the global range for it (think
> >>>>>> SSA_NAME_RANGE_INFO).  In the case of a tree code with operands, we
> >>>>>> also use get_global_range_query to resolve the operands, and then call
> >>>>>> into range-ops, which is our lowest level component.  There is no
> >>>>>> ranger or gori involved.  All we're doing is resolving the operation
> >>>>>> with the ranges passed.
> >>>>>>
> >>>>>> This is enough to avoid recursing in the case where we definitely know
> >>>>>> the sign of a range.  Otherwise, we still recurse.
> >>>>>>
> >>>>>> Note that instead of get_global_range_query(), we could use
> >>>>>> get_range_query() which uses a ranger (if active in a pass), or
> >>>>>> get_global_range_query if not.  This would allow passes that have an
> >>>>>> active ranger (with enable_ranger) to use a full ranger.  These passes
> >>>>>> are currently, VRP, loop unswitching, DOM, loop versioning, etc.  If
> >>>>>> no ranger is active, get_range_query defaults to global ranges, so
> >>>>>> there's no additional penalty.
> >>>>>>
> >>>>>> Would this be acceptable, at least enough to close (or rename the PR ;-))?
> >>>>>
> >>>>> I think the checks would belong to the gimple_stmt_nonnegative_warnv_p function
> >>>>> only (that's the SSA name entry from the fold-const.cc ones)?
> >>>>
> >>>> That was my first approach, but I thought I'd cover the unary and binary
> >>>> operators as well, since they had other callers.  But I'm happy with
> >>>> just the top-level tweak.  It's a lot less code :).
> >>>
> >>> @@ -9234,6 +9235,15 @@ bool
> >>>    gimple_stmt_nonnegative_warnv_p (gimple *stmt, bool *strict_overflow_p,
> >>>                                    int depth)
> >>>    {
> >>> +  tree type = gimple_range_type (stmt);
> >>> +  if (type && frange::supports_p (type))
> >>> +    {
> >>> +      frange r;
> >>> +      bool sign;
> >>> +      return (get_global_range_query ()->range_of_stmt (r, stmt)
> >>> +             && r.signbit_p (sign)
> >>> +             && sign == false);
> >>> +    }
> >>>
> >>> the above means we never fall through to the switch below if
> >>> frange::supports_p (type) - that's eventually good enough, I
> >>> don't think we ever call this very function directly but it gets
> >>> invoked via recursion through operands only.  But of course
> >>
> >> Woah, sorry.  That was not intended.  For that matter, the patch as
> >> posted caused:
> >>
> >> FAIL: gcc.dg/builtins-10.c (test for excess errors)
> >> FAIL: gcc.dg/builtins-57.c (test for excess errors)
> >> FAIL: gcc.dg/torture/builtin-nonneg-1.c   -O1  (test for excess errors)
> >> FAIL: gcc.dg/torture/builtin-nonneg-1.c   -O2  (test for excess errors)
> >> FAIL: gcc.dg/torture/builtin-nonneg-1.c   -O2 -flto
> >> -fno-use-linker-plugin -flto-partition=none  (test for excess errors)
> >> FAIL: gcc.dg/torture/builtin-nonneg-1.c   -O3 -g  (test for excess errors)
> >> FAIL: gcc.dg/torture/builtin-nonneg-1.c   -Os  (test for excess errors)
> >> FAIL: gcc.dg/torture/builtin-power-1.c   -O1  (test for excess errors)
> >> FAIL: gcc.dg/torture/builtin-power-1.c   -O2  (test for excess errors)
> >> FAIL: gcc.dg/torture/builtin-power-1.c   -O2 -flto
> >> -fno-use-linker-plugin -flto-partition=none  (test for excess errors)
> >> FAIL: gcc.dg/torture/builtin-power-1.c   -O3 -g  (test for excess errors)
> >> FAIL: gcc.dg/torture/builtin-power-1.c   -Os  (test for excess errors)
> >
> > Did you investigate why?  Because the old patch removed the recursion
> > while the new keeps it in case the global range isn't present which isn't
> > as nice.
>
> For gcc.dg/builtins-10.c, there are a few calls to
> gimple_stmt_nonnegative* that are being made before we have global
> ranges (ccp1 and forwprop1), so the query returns VARYING (i.e. no known
> sign).  If you're curious, the call to gimple_stmt_nonnegative* comes
> via courtesy of match.pd:
>
> /* Canonicalization of sequences of math builtins.  These rules represent
>     IL simplifications but are not necessarily optimizations.
>
> So ISTM, we still need to fall through if we're being called before
> global ranges are available.
>
> After global ranges are available (evrp), we would avoid further lookups
> if it weren't for an unrelated problem I found.
>
> foperator_abs::fold_range() is trying to set a range of [+0.0, +INF],
> but this little snpipet in the frange normalization code adds a -0.0 to
> the range:
>
>    else if (!HONOR_SIGNED_ZEROS (m_type))
>      {
>        if (real_iszero (&m_max, 1))
>         m_max.sign = 0;
>        if (real_iszero (&m_min, 0))
>         m_min.sign = 1;
>      }
>
> We end up with:
>
> [frange] double [-0.0 (-0x0.0p+0),
> 1.79769313486231570814527423731704356798070567525844996599e+308
> (0x0.fffffffffffff8p+1024)]
>
> I must say this is beyond my paygrade :).  Jakub, it was your suggestion
> to add the snippet above.  Is this correct?  Note that this test is for
> -ffast-math.
>
> If I comment out the code above, the regressions are fixed, both with my
> current patch or with the original one.  But as I suggested, maybe we
> want the second patch, because we may be called before global ranges are
> available.
>
> IMHO, we could go with the second patch, and fix the ABS problem
> independently.
>
> Yay?  Nay?

Yes, the 2nd patch is approved, I was just curious.

Richard.

> Aldy
>
> >
> >> Note that ranger folding calls this function, though it won't run the
> >> risk of endless recursion because range_of_stmt uses the LHS, and only
> >> use global ranges to solve the LHS.
> >>
> >> Also, frange::supports_p() does not support all floats:
> >>
> >>     static bool supports_p (const_tree type)
> >>     {
> >>       // ?? Decimal floats can have multiple representations for the
> >>       // same number.  Supporting them may be as simple as just
> >>       // disabling them in singleton_p.  No clue.
> >>       return SCALAR_FLOAT_TYPE_P (type) && !DECIMAL_FLOAT_TYPE_P (type);
> >>     }
> >
> > OK, _Complex types are obviously missing, so are vector ones.
> >
> >> Finally, my patch is more conservative than what the *nonnegative_warn*
> >> friends do.  We only return true when we're sure about the sign bit and
> >> it's FALSE.  As I mentioned elsewhere, tree_call_nonnegative_warn_p()
> >> always returns true for:
> >>
> >>       CASE_CFN_ACOS:
> >>       CASE_CFN_ACOS_FN:
> >>       CASE_CFN_ACOSH:
> >>       CASE_CFN_ACOSH_FN:
> >>       CASE_CFN_CABS:
> >>       CASE_CFN_CABS_FN:
> >> ...
> >> ...
> >>         /* Always true.  */
> >>         return true;
> >>
> >> This means that we'll return true for a NAN, but we're incorrectly
> >> assuming the NAN will be +NAN.  In my proposed patch, we don't make such
> >> assumptions.  We only return true if the range is non-negative,
> >> including the NAN.
> >
> > Yep, the usual issue whether nonnegative means copysign (1, x) produces
> > 1 or whether !(x < 0) is true.
> >
> >>> I wonder what types are not supported by frange and whether
> >>> the manual processing we fall through to does anything meaningful
> >>> for those?
> >>>
> >>> I won't ask you to thoroughly answer this now but please put in
> >>> a comment reflecting the above before the switch stmt.
> >>>
> >>>      switch (gimple_code (stmt))
> >>>
> >>>
> >>> Otherwise OK, in case you tree gets back to bootstrapping ;)
> >>
> >> Updated patch that passes test.
> >>
> >> OK?  And if so, can I close the PR?
> >
> > Yes, I think we now track float ranges - improvements are of course
> > always possible.
> >
> > Richard.
> >
> >> Thanks.
> >> Aldy
> >
>
  

Patch

diff --git a/gcc/fold-const.cc b/gcc/fold-const.cc
index b89cac91cae..bd74cfca996 100644
--- a/gcc/fold-const.cc
+++ b/gcc/fold-const.cc
@@ -14577,6 +14577,44 @@  tree_simple_nonnegative_warnv_p (enum tree_code code, tree type)
   return false;
 }
 
+/* Return true if T is of type floating point and has a known sign.
+   If so, set the sign in SIGN.  */
+
+static bool
+known_float_sign_p (bool &sign, tree t)
+{
+  if (!frange::supports_p (TREE_TYPE (t)))
+    return false;
+
+  frange r;
+  return (get_global_range_query ()->range_of_expr (r, t)
+	  && r.signbit_p (sign));
+}
+
+/* Return true if TYPE is a floating-point type and (CODE OP0 OP1) has
+   a known sign.  If so, set the sign in SIGN.  */
+
+static bool
+known_float_sign_p (bool &sign, enum tree_code code, tree type, tree op0,
+		    tree op1 = NULL_TREE)
+{
+  if (!frange::supports_p (type))
+    return false;
+
+  range_op_handler handler (code, type);
+  if (handler)
+    {
+      frange res, r0, r1;
+      get_global_range_query ()->range_of_expr (r0, op0);
+      if (op1)
+	get_global_range_query ()->range_of_expr (r1, op1);
+      else
+	r1.set_varying (type);
+      return handler.fold_range (res, type, r0, r1) && res.signbit_p (sign);
+    }
+  return false;
+}
+
 /* Return true if (CODE OP0) is known to be non-negative.  If the return
    value is based on the assumption that signed overflow is undefined,
    set *STRICT_OVERFLOW_P to true; otherwise, don't change
@@ -14589,6 +14627,10 @@  tree_unary_nonnegative_warnv_p (enum tree_code code, tree type, tree op0,
   if (TYPE_UNSIGNED (type))
     return true;
 
+  bool sign;
+  if (known_float_sign_p (sign, code, type, op0))
+    return !sign;
+
   switch (code)
     {
     case ABS_EXPR:
@@ -14656,6 +14698,10 @@  tree_binary_nonnegative_warnv_p (enum tree_code code, tree type, tree op0,
   if (TYPE_UNSIGNED (type))
     return true;
 
+  bool sign;
+  if (known_float_sign_p (sign, code, type, op0, op1))
+    return !sign;
+
   switch (code)
     {
     case POINTER_PLUS_EXPR:
@@ -14778,6 +14824,8 @@  tree_binary_nonnegative_warnv_p (enum tree_code code, tree type, tree op0,
 bool
 tree_single_nonnegative_warnv_p (tree t, bool *strict_overflow_p, int depth)
 {
+  bool sign;
+
   if (TYPE_UNSIGNED (TREE_TYPE (t)))
     return true;
 
@@ -14796,6 +14844,9 @@  tree_single_nonnegative_warnv_p (tree t, bool *strict_overflow_p, int depth)
       return RECURSE (TREE_OPERAND (t, 1)) && RECURSE (TREE_OPERAND (t, 2));
 
     case SSA_NAME:
+      if (known_float_sign_p (sign, t))
+	return !sign;
+
       /* Limit the depth of recursion to avoid quadratic behavior.
 	 This is expected to catch almost all occurrences in practice.
 	 If this code misses important cases that unbounded recursion