On Fri, 4 Feb 2022, Richard Sandiford wrote:

> Richard Biener via Gcc-patches <gcc-patches@gcc.gnu.org> writes:
> > niter analysis uses multiple_of_p which currently assumes
> > operations like MULT_EXPR do not wrap.  We've got to rely on this
> > for optimizing size expressions like those in DECL_SIZE and those
> > generally use unsigned arithmetic with no indication that they
> > are not expected to wrap.  To preserve that the following adds
> > a parameter to multiple_of_p, defaulted to true, indicating that
> > the TOP expression is not expected to wrap for outer computations
> > in TYPE.  This mostly follows a patch proposed by Bin last year
> > with the conversion behavior added.
> >
> > Applying to all users the new effect is that upon type conversions
> > in the TOP expression the behavior will switch to honor
> > TYPE_OVERFLOW_UNDEFINED for the converted sub-expressions.
> >
> > The patch also changes the occurance in niter analysis that we
> > know is problematic and we have testcases for to pass false
> > to multiple_of_p.  The patch also contains a change to the
> > PR72817 fix from Bin to avoid regressing gcc.dg/tree-ssa/loop-42.c.
> >
> > The intent for stage1 is to introduce a size_multiple_of_p and
> > internalize the added parameter so all multiple_of_p users will
> > honor TYPE_OVERFLOW_UNDEFINED and users dealing with size expressions
> > need to be switched to size_multiple_of_p.
> >
> > Bootstrapped and tested on x86_64-unknown-linux-gnu with all languages
> > and {,-m32} testing.
> >
> > The patch applies ontop of the three earlier posted ones that touch
> > multiple_of_p but have not yet been reviewed/pushed.
> >
> > OK?
> >
> > Thanks,
> > Richard.
> >
> > 2022-01-26  Richard Biener  <rguent...@suse.de>
> >
> >     PR tree-optimization/100499
> >     * fold-const.h (multiple_of_p): Add nowrap parameter, defaulted
> >     to true.
> >     * fold-const.cc (multiple_of_p): Likewise.  Honor it for
> >     MULT_EXPR, PLUS_EXPR and MINUS_EXPR and pass it along,
> >     switching to false for conversions.
> >     * tree-ssa-loop-niter.cc (number_of_iterations_ne): Do not
> >     claim the outermost expression does not wrap when calling
> >     multiple_of_p.  Refactor the check done to check the
> >     original IV, avoiding a bias that might wrap.
> >
> >     * gcc.dg/torture/pr100499-1.c: New testcase.
> >     * gcc.dg/torture/pr100499-2.c: Likewise.
> >     * gcc.dg/torture/pr100499-3.c: Likewise.
> >
> > Co-authored-by: Bin Cheng  <bin.ch...@linux.alibaba.com>
> > ---
> >  gcc/fold-const.cc                         | 81 +++++++++++++++--------
> >  gcc/fold-const.h                          |  2 +-
> >  gcc/testsuite/gcc.dg/torture/pr100499-1.c | 27 ++++++++
> >  gcc/testsuite/gcc.dg/torture/pr100499-2.c | 16 +++++
> >  gcc/testsuite/gcc.dg/torture/pr100499-3.c | 14 ++++
> >  gcc/tree-ssa-loop-niter.cc                | 52 ++++++---------
> >  6 files changed, 131 insertions(+), 61 deletions(-)
> >  create mode 100644 gcc/testsuite/gcc.dg/torture/pr100499-1.c
> >  create mode 100644 gcc/testsuite/gcc.dg/torture/pr100499-2.c
> >  create mode 100644 gcc/testsuite/gcc.dg/torture/pr100499-3.c
> >
> > diff --git a/gcc/fold-const.cc b/gcc/fold-const.cc
> > index a0a4913c45e..7c204fb6265 100644
> > --- a/gcc/fold-const.cc
> > +++ b/gcc/fold-const.cc
> > @@ -14062,10 +14062,16 @@ fold_binary_initializer_loc (location_t loc, 
> > tree_code code, tree type,
> >       SAVE_EXPR (I) * SAVE_EXPR (J)
> >  
> >     (where the same SAVE_EXPR (J) is used in the original and the
> > -   transformed version).  */
> > +   transformed version).
> > +
> > +   NOWRAP specifies whether all outer operations in TYPE should
> > +   be considered not wrapping.  Any type conversion within TOP acts
> > +   as a barrier and we will fall back to NOWRAP being false.
> > +   NOWRAP is mostly used to treat expressions in TYPE_SIZE and friends
> > +   as not wrapping even though they are generally using unsigned 
> > arithmetic.  */
> >  
> >  int
> > -multiple_of_p (tree type, const_tree top, const_tree bottom)
> > +multiple_of_p (tree type, const_tree top, const_tree bottom, bool nowrap)
> >  {
> >    gimple *stmt;
> >    tree op1, op2;
> > @@ -14083,10 +14089,17 @@ multiple_of_p (tree type, const_tree top, 
> > const_tree bottom)
> >      a multiple of BOTTOM then TOP is a multiple of BOTTOM.  */
> >        if (!integer_pow2p (bottom))
> >     return 0;
> > -      return (multiple_of_p (type, TREE_OPERAND (top, 1), bottom)
> > -         || multiple_of_p (type, TREE_OPERAND (top, 0), bottom));
> > +      return (multiple_of_p (type, TREE_OPERAND (top, 1), bottom, nowrap)
> > +         || multiple_of_p (type, TREE_OPERAND (top, 0), bottom, nowrap));
> >  
> >      case MULT_EXPR:
> > +      /* If the multiplication can wrap we cannot recurse further unless
> > +    the second operand is a power of two which is where wrapping
> > +    does not matter.  */
> > +      if (!nowrap
> > +     && !TYPE_OVERFLOW_UNDEFINED (type)
> > +     && !integer_pow2p (TREE_OPERAND (top, 1)))
> > +   return 0;
> 
> I think I'm missing something, but isn't the key thing whether bottom
> is a power of 2?  E.g. as it stands it looks like we'd still say that a
> wrapping x * 2 is a multiple of 3 based purely on x being a multiple of 3,
> thanks to…

I had to think about this as well but the logic is that (as you noted),
we below test ...

> >        if (TREE_CODE (bottom) == INTEGER_CST)
> >     {
> >       op1 = TREE_OPERAND (top, 0);
> > @@ -14095,24 +14108,24 @@ multiple_of_p (tree type, const_tree top, 
> > const_tree bottom)
> >         std::swap (op1, op2);
> >       if (TREE_CODE (op2) == INTEGER_CST)
> >         {
> > -         if (multiple_of_p (type, op2, bottom))
> > +         if (multiple_of_p (type, op2, bottom, nowrap))
> >             return 1;
> >           /* Handle multiple_of_p ((x * 2 + 2) * 4, 8).  */
> > -         if (multiple_of_p (type, bottom, op2))
> > +         if (multiple_of_p (type, bottom, op2, nowrap))
> >             {
> >               widest_int w = wi::sdiv_trunc (wi::to_widest (bottom),
> >                                              wi::to_widest (op2));
> >               if (wi::fits_to_tree_p (w, TREE_TYPE (bottom)))
> >                 {
> >                   op2 = wide_int_to_tree (TREE_TYPE (bottom), w);
> > -                 return multiple_of_p (type, op1, op2);
> > +                 return multiple_of_p (type, op1, op2, nowrap);
> >                 }
> >             }
> > -         return multiple_of_p (type, op1, bottom);
> > +         return multiple_of_p (type, op1, bottom, nowrap);
> >         }
> >     }
> > -      return (multiple_of_p (type, TREE_OPERAND (top, 1), bottom)
> > -         || multiple_of_p (type, TREE_OPERAND (top, 0), bottom));
> > +      return (multiple_of_p (type, TREE_OPERAND (top, 1), bottom, nowrap)
> > +         || multiple_of_p (type, TREE_OPERAND (top, 0), bottom, nowrap));
> 
> …the second arm of the || here.

.. oh, we use ||, I thought about this again for the plus case
and there we use && which means we'll have bottom a power of two.

I think back last year when we discussed this Bin said having
bottom a power of two is unnecessarily restrictive.

> On the other hand, a wrapping x * 6 is a multiple of 2 even though 6
> isn't a power of 2.

Yes, for bottom being a power of two the logic would definitely apply.

So, changing it to

      if (!nowrap
          && !TYPE_OVERFLOW_UNDEFINED (type)
          && !integer_pow2p (bottom))
        return 0;

would work.

I suppose for consistency we should change the condition on the
plus/minus case in the same way.

Richard.

> 
> Thanks,
> Richard
> 
> >  
> >      case LSHIFT_EXPR:
> >        /* Handle X << CST as X * (1 << CST) and only process the constant.  
> > */
> > @@ -14124,29 +14137,39 @@ multiple_of_p (tree type, const_tree top, 
> > const_tree bottom)
> >           wide_int mul_op
> >             = wi::one (TYPE_PRECISION (type)) << wi::to_wide (op1);
> >           return multiple_of_p (type,
> > -                               wide_int_to_tree (type, mul_op), bottom);
> > +                               wide_int_to_tree (type, mul_op), bottom,
> > +                               nowrap);
> >         }
> >     }
> >        return 0;
> >  
> >      case MINUS_EXPR:
> > -      /* It is impossible to prove if op0 - op1 is multiple of bottom
> > -    precisely, so be conservative here checking if both op0 and op1
> > -    are multiple of bottom.  Note we check the second operand first
> > -    since it's usually simpler.  */
> > -      return (multiple_of_p (type, TREE_OPERAND (top, 1), bottom)
> > -         && multiple_of_p (type, TREE_OPERAND (top, 0), bottom));
> > -
> >      case PLUS_EXPR:
> > -      /* The same as MINUS_EXPR, but handle cases like op0 + 0xfffffffd
> > -    as op0 - 3 if the expression has unsigned type.  For example,
> > -    (X / 3) + 0xfffffffd is multiple of 3, but 0xfffffffd is not.  */
> >        op1 = TREE_OPERAND (top, 1);
> > -      if (TYPE_UNSIGNED (type)
> > +
> > +      /* If the addition or subtraction can wrap we cannot recurse further
> > +    unless the second operand is a power of two which is where wrapping
> > +    does not matter.  */
> > +      if (!nowrap
> > +     && !TYPE_OVERFLOW_UNDEFINED (type)
> > +     && !integer_pow2p (op1))
> > +   return 0;
> > +
> > +      /* Handle cases like op0 + 0xfffffffd as op0 - 3 if the expression 
> > has
> > +    unsigned type.  For example, (X / 3) + 0xfffffffd is multiple of 3,
> > +    but 0xfffffffd is not.  */
> > +      if (TREE_CODE (top) == PLUS_EXPR
> > +     && nowrap
> > +     && TYPE_UNSIGNED (type)
> >       && TREE_CODE (op1) == INTEGER_CST && tree_int_cst_sign_bit (op1))
> >     op1 = fold_build1 (NEGATE_EXPR, type, op1);
> > -      return (multiple_of_p (type, op1, bottom)
> > -         && multiple_of_p (type, TREE_OPERAND (top, 0), bottom));
> > +
> > +      /* It is impossible to prove if op0 +- op1 is multiple of bottom
> > +    precisely, so be conservative here checking if both op0 and op1
> > +    are multiple of bottom.  Note we check the second operand first
> > +    since it's usually simpler.  */
> > +      return (multiple_of_p (type, op1, bottom, nowrap)
> > +         && multiple_of_p (type, TREE_OPERAND (top, 0), bottom, nowrap));
> >  
> >      CASE_CONVERT:
> >        /* Can't handle conversions from non-integral or wider integral 
> > type.  */
> > @@ -14154,15 +14177,17 @@ multiple_of_p (tree type, const_tree top, 
> > const_tree bottom)
> >       || (TYPE_PRECISION (type)
> >           < TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (top, 0)))))
> >     return 0;
> > +      /* NOWRAP only extends to operations in the outermost type so
> > +    make sure to strip it off here.  */
> >        return multiple_of_p (TREE_TYPE (TREE_OPERAND (top, 0)),
> > -                       TREE_OPERAND (top, 0), bottom);
> > +                       TREE_OPERAND (top, 0), bottom, false);
> >  
> >      case SAVE_EXPR:
> > -      return multiple_of_p (type, TREE_OPERAND (top, 0), bottom);
> > +      return multiple_of_p (type, TREE_OPERAND (top, 0), bottom, nowrap);
> >  
> >      case COND_EXPR:
> > -      return (multiple_of_p (type, TREE_OPERAND (top, 1), bottom)
> > -         && multiple_of_p (type, TREE_OPERAND (top, 2), bottom));
> > +      return (multiple_of_p (type, TREE_OPERAND (top, 1), bottom, nowrap)
> > +         && multiple_of_p (type, TREE_OPERAND (top, 2), bottom, nowrap));
> >  
> >      case INTEGER_CST:
> >        if (TREE_CODE (bottom) != INTEGER_CST
> > diff --git a/gcc/fold-const.h b/gcc/fold-const.h
> > index a9a3062e4f6..03a0de3b4de 100644
> > --- a/gcc/fold-const.h
> > +++ b/gcc/fold-const.h
> > @@ -96,7 +96,7 @@ extern void fold_overflow_warning (const char*, enum 
> > warn_strict_overflow_code);
> >  extern enum tree_code fold_div_compare (enum tree_code, tree, tree,
> >                                     tree *, tree *, bool *);
> >  extern bool operand_equal_p (const_tree, const_tree, unsigned int flags = 
> > 0);
> > -extern int multiple_of_p (tree, const_tree, const_tree);
> > +extern int multiple_of_p (tree, const_tree, const_tree, bool = true);
> >  #define omit_one_operand(T1,T2,T3)\
> >     omit_one_operand_loc (UNKNOWN_LOCATION, T1, T2, T3)
> >  extern tree omit_one_operand_loc (location_t, tree, tree, tree);
> > diff --git a/gcc/testsuite/gcc.dg/torture/pr100499-1.c 
> > b/gcc/testsuite/gcc.dg/torture/pr100499-1.c
> > new file mode 100644
> > index 00000000000..9511c323505
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/torture/pr100499-1.c
> > @@ -0,0 +1,27 @@
> > +/* { dg-do run } */
> > +
> > +typedef __UINT16_TYPE__ uint16_t;
> > +typedef __INT32_TYPE__ int32_t;
> > +static uint16_t g_2823 = 0xEC75L;
> > +static uint16_t g_116 = 0xBC07L;
> > +
> > +static uint16_t
> > +safe_mul_func_uint16_t_u_u(uint16_t ui1, uint16_t ui2)
> > +{
> > +  return ((unsigned int)ui1) * ((unsigned int)ui2);
> > +}
> > +
> > +int main ()
> > +{
> > +  uint16_t l_2815 = 0xffff;
> > +  uint16_t *l_2821 = &g_116;
> > +  uint16_t *l_2822 = &g_2823;
> > +
> > +lbl_2826:
> > +  l_2815 &= 0x1eae;
> > +  if (safe_mul_func_uint16_t_u_u(((*l_2821) = l_2815), (--(*l_2822))))
> > +    goto lbl_2826;
> > +  if (g_2823 != 32768)
> > +    __builtin_abort ();
> > +  return 0;
> > +}
> > diff --git a/gcc/testsuite/gcc.dg/torture/pr100499-2.c 
> > b/gcc/testsuite/gcc.dg/torture/pr100499-2.c
> > new file mode 100644
> > index 00000000000..999f931806a
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/torture/pr100499-2.c
> > @@ -0,0 +1,16 @@
> > +/* { dg-do run } */
> > +
> > +unsigned char ag = 55;
> > +unsigned i;
> > +int main()
> > +{
> > +  unsigned char c;
> > +  unsigned char a = ag;
> > +d:
> > +  c = a-- * 52;
> > +  if (c)
> > +    goto d;
> > +  if (a != 255)
> > +    __builtin_abort ();
> > +  return 0;
> > +}
> > diff --git a/gcc/testsuite/gcc.dg/torture/pr100499-3.c 
> > b/gcc/testsuite/gcc.dg/torture/pr100499-3.c
> > new file mode 100644
> > index 00000000000..01be1e50690
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/torture/pr100499-3.c
> > @@ -0,0 +1,14 @@
> > +/* { dg-do run } */
> > +
> > +int a = 0;
> > +unsigned char b = 0;
> > +
> > +int main() {
> > +  a - 6;
> > +  for (; a >= -13; a = a - 8)
> > +    while((unsigned char)(b-- * 6))
> > +      ;
> > +  if (b != 127)
> > +    __builtin_abort();
> > +  return 0;
> > +}
> > diff --git a/gcc/tree-ssa-loop-niter.cc b/gcc/tree-ssa-loop-niter.cc
> > index d33095b8e03..318d10c8fac 100644
> > --- a/gcc/tree-ssa-loop-niter.cc
> > +++ b/gcc/tree-ssa-loop-niter.cc
> > @@ -1042,17 +1042,21 @@ number_of_iterations_ne (class loop *loop, tree 
> > type, affine_iv *iv,
> >         new_base = base - step > FINAL ; step < 0
> >                                          && base - step doesn't overflow
> >  
> > -       2') |FINAL - new_base| is an exact multiple of step.
> > -
> > -     Please refer to PR34114 as an example of loop-ch's impact, also refer
> > -     to PR72817 as an example why condition 2') is necessary.
> > +     Please refer to PR34114 as an example of loop-ch's impact.
> >  
> >       Note, for NE_EXPR, base equals to FINAL is a special case, in
> > -     which the loop exits immediately, and the iv does not overflow.  */
> > +     which the loop exits immediately, and the iv does not overflow.
> > +
> > +     Also note, we prove condition 2) by checking base and final seperately
> > +     along with condition 1) or 1').  */
> >    if (!niter->control.no_overflow
> > -      && (integer_onep (s) || multiple_of_p (type, c, s)))
> > +      && (integer_onep (s)
> > +     || (multiple_of_p (type, fold_convert (niter_type, iv->base), s,
> > +                        false)
> > +         && multiple_of_p (type, fold_convert (niter_type, final), s,
> > +                           false))))
> >      {
> > -      tree t, cond, new_c, relaxed_cond = boolean_false_node;
> > +      tree t, cond, relaxed_cond = boolean_false_node;
> >  
> >        if (tree_int_cst_sign_bit (iv->step))
> >     {
> > @@ -1066,12 +1070,8 @@ number_of_iterations_ne (class loop *loop, tree 
> > type, affine_iv *iv,
> >           if (integer_nonzerop (t))
> >             {
> >               t = fold_build2 (MINUS_EXPR, type, iv->base, iv->step);
> > -             new_c = fold_build2 (MINUS_EXPR, niter_type,
> > -                                  fold_convert (niter_type, t),
> > -                                  fold_convert (niter_type, final));
> > -             if (multiple_of_p (type, new_c, s))
> > -               relaxed_cond = fold_build2 (GT_EXPR, boolean_type_node,
> > -                                           t, final);
> > +             relaxed_cond = fold_build2 (GT_EXPR, boolean_type_node, t,
> > +                                         final);
> >             }
> >         }
> >     }
> > @@ -1087,12 +1087,8 @@ number_of_iterations_ne (class loop *loop, tree 
> > type, affine_iv *iv,
> >           if (integer_nonzerop (t))
> >             {
> >               t = fold_build2 (MINUS_EXPR, type, iv->base, iv->step);
> > -             new_c = fold_build2 (MINUS_EXPR, niter_type,
> > -                                  fold_convert (niter_type, final),
> > -                                  fold_convert (niter_type, t));
> > -             if (multiple_of_p (type, new_c, s))
> > -               relaxed_cond = fold_build2 (LT_EXPR, boolean_type_node,
> > -                                           t, final);
> > +             relaxed_cond = fold_build2 (LT_EXPR, boolean_type_node, t,
> > +                                         final);
> >             }
> >         }
> >     }
> > @@ -1102,19 +1098,11 @@ number_of_iterations_ne (class loop *loop, tree 
> > type, affine_iv *iv,
> >     t = simplify_using_initial_conditions (loop, relaxed_cond);
> >  
> >        if (t && integer_onep (t))
> > -   niter->control.no_overflow = true;
> > -    }
> > -
> > -  /* First the trivial cases -- when the step is 1.  */
> > -  if (integer_onep (s))
> > -    {
> > -      niter->niter = c;
> > -      return true;
> > -    }
> > -  if (niter->control.no_overflow && multiple_of_p (type, c, s))
> > -    {
> > -      niter->niter = fold_build2 (FLOOR_DIV_EXPR, niter_type, c, s);
> > -      return true;
> > +   {
> > +     niter->control.no_overflow = true;
> > +     niter->niter = fold_build2 (EXACT_DIV_EXPR, niter_type, c, s);
> > +     return true;
> > +   }
> >      }
> >  
> >    /* Let nsd (step, size of mode) = d.  If d does not divide c, the loop
> 

-- 
Richard Biener <rguent...@suse.de>
SUSE Software Solutions Germany GmbH, Maxfeldstrasse 5, 90409 Nuernberg,
Germany; GF: Ivo Totev; HRB 36809 (AG Nuernberg)

Reply via email to