On Thu, Aug 3, 2023 at 2:46 PM Richard Sandiford via Gcc-patches
<gcc-patches@gcc.gnu.org> wrote:
>
> can_div_trunc_p (a, b, &Q, &r) tries to compute a Q and r that
> satisfy the usual conditions for truncating division:
>
>      (1) a = b * Q + r
>      (2) |b * Q| <= |a|
>      (3) |r| < |b|
>
> We can compute Q using the constant component (the case when
> all indeterminates are zero).  Since |r| < |b| for the constant
> case, the requirements for indeterminate xi with coefficients
> ai (for a) and bi (for b) are:
>
>      (2') |bi * Q| <= |ai|
>      (3') |ai - bi * Q| <= |bi|
>
> (See the big comment for more details, restrictions, and reasoning).
>
> However, the function works on abstract arithmetic types, and so
> it has to be careful not to introduce new overflow.  The code
> therefore only handled the extreme for (3'), that is:
>
>      |ai - bi * Q| = |bi|
>
> for the case where Q is zero.
>
> Looking at it again, the overflow issue is a bit easier to handle than
> I'd originally thought (or so I hope).  This patch therefore extends the
> code to handle |ai - bi * Q| = |bi| for all Q, with Q = 0 no longer
> being a separate case.
>
> The net effect is to allow the function to succeed for things like:
>
>      (a0 + b1 (Q+1) x) / (b0 + b1 x)
>
> where Q = a0 / b0, with various sign conditions.  E.g. we now handle:
>
>      (7 + 8x) / (4 + 4x)
>
> with Q = 1 and r = 3 + 4x,
>
> Tested on aarch64-linux-gnu.  OK to install?

OK.

Richard.

> Richard
>
>
> gcc/
>         * poly-int.h (can_div_trunc_p): Succeed for more boundary conditions.
>
> gcc/testsuite/
>         * gcc.dg/plugin/poly-int-tests.h (test_can_div_trunc_p_const)
>         (test_can_div_trunc_p_const): Add more tests.
> ---
>  gcc/poly-int.h                               | 45 ++++++-----
>  gcc/testsuite/gcc.dg/plugin/poly-int-tests.h | 85 +++++++++++++++++---
>  2 files changed, 98 insertions(+), 32 deletions(-)
>
> diff --git a/gcc/poly-int.h b/gcc/poly-int.h
> index 12571455081..7bff5e5ad26 100644
> --- a/gcc/poly-int.h
> +++ b/gcc/poly-int.h
> @@ -2355,28 +2355,31 @@ can_div_trunc_p (const poly_int_pod<N, Ca> &a,
>         }
>        else
>         {
> -         if (q == 0)
> -           {
> -             /* For Q == 0 we simply need: (3') |ai| <= |bi|.  */
> -             if (a.coeffs[i] != ICa (0))
> -               {
> -                 /* Use negative absolute to avoid overflow, i.e.
> -                    -|ai| >= -|bi|.  */
> -                 C neg_abs_a = (a.coeffs[i] < 0 ? a.coeffs[i] : 
> -a.coeffs[i]);
> -                 C neg_abs_b = (b.coeffs[i] < 0 ? b.coeffs[i] : 
> -b.coeffs[i]);
> -                 if (neg_abs_a < neg_abs_b)
> -                   return false;
> -                 rem_p = true;
> -               }
> -           }
> +         /* The only unconditional arithmetic that we can do on ai,
> +            bi and Q is ai / bi and ai % bi.  (ai == minimum int and
> +            bi == -1 would be UB in the caller.)  Anything else runs
> +            the risk of overflow.  */
> +         auto qi = NCa (a.coeffs[i]) / NCb (b.coeffs[i]);
> +         auto ri = NCa (a.coeffs[i]) % NCb (b.coeffs[i]);
> +         /* (2') and (3') are satisfied when ai /[trunc] bi == q.
> +            So is the stricter condition |ai - bi * Q| < |bi|.  */
> +         if (qi == q)
> +           rem_p |= (ri != 0);
> +         /* The only other case is when:
> +
> +                |bi * Q| + |bi| = |ai| (for (2'))
> +            and |ai - bi * Q|   = |bi| (for (3'))
> +
> +            The first is equivalent to |bi|(|Q| + 1) == |ai|.
> +            The second requires ai == bi * (Q + 1) or ai == bi * (Q - 1).  */
> +         else if (ri != 0)
> +           return false;
> +         else if (q <= 0 && qi < q && qi + 1 == q)
> +           ;
> +         else if (q >= 0 && qi > q && qi - 1 == q)
> +           ;
>           else
> -           {
> -             /* Otherwise just check for the case in which ai / bi == Q.  */
> -             if (NCa (a.coeffs[i]) / NCb (b.coeffs[i]) != q)
> -               return false;
> -             if (NCa (a.coeffs[i]) % NCb (b.coeffs[i]) != 0)
> -               rem_p = true;
> -           }
> +           return false;
>         }
>      }
>
> diff --git a/gcc/testsuite/gcc.dg/plugin/poly-int-tests.h 
> b/gcc/testsuite/gcc.dg/plugin/poly-int-tests.h
> index 0b89acd91cd..7af98595a5e 100644
> --- a/gcc/testsuite/gcc.dg/plugin/poly-int-tests.h
> +++ b/gcc/testsuite/gcc.dg/plugin/poly-int-tests.h
> @@ -1899,14 +1899,19 @@ test_can_div_trunc_p_const ()
>                                 ph::make (4, 8, 12),
>                                 &const_quot));
>    ASSERT_EQ (const_quot, C (2));
> -  ASSERT_EQ (can_div_trunc_p (ph::make (15, 25, 40),
> +  ASSERT_TRUE (can_div_trunc_p (ph::make (15, 25, 40),
> +                               ph::make (4, 8, 10),
> +                               &const_quot));
> +  ASSERT_EQ (const_quot, C (3));
> +  const_quot = 0;
> +  ASSERT_EQ (can_div_trunc_p (ph::make (15, 25, 41),
>                               ph::make (4, 8, 10),
>                               &const_quot), N <= 2);
> -  ASSERT_EQ (const_quot, C (N <= 2 ? 3 : 2));
> +  ASSERT_EQ (const_quot, C (N <= 2 ? 3 : 0));
>    ASSERT_EQ (can_div_trunc_p (ph::make (43, 79, 80),
>                               ph::make (4, 8, 10),
>                               &const_quot), N == 1);
> -  ASSERT_EQ (const_quot, C (N == 1 ? 10 : N == 2 ? 3 : 2));
> +  ASSERT_EQ (const_quot, C (N == 1 ? 10 : N == 2 ? 3 : 0));
>    ASSERT_TRUE (can_div_trunc_p (ph::make (3, 4, 5),
>                                 ph::make (4, 5, 6),
>                                 &const_quot));
> @@ -1964,16 +1969,22 @@ test_can_div_trunc_p_const ()
>                                 &const_quot, &rem));
>    ASSERT_EQ (const_quot, C (2));
>    ASSERT_KNOWN_EQ (rem, ph::make (1, 7, 6));
> -  ASSERT_EQ (can_div_trunc_p (ph::make (15, 25, 40),
> +  ASSERT_TRUE (can_div_trunc_p (ph::make (15, 25, 40),
> +                               ph::make (4, 8, 10),
> +                               &const_quot, &rem));
> +  ASSERT_EQ (const_quot, C (3));
> +  ASSERT_KNOWN_EQ (rem, ph::make (3, 1, 10));
> +  const_quot = 0, rem = 0;
> +  ASSERT_EQ (can_div_trunc_p (ph::make (15, 25, 41),
>                               ph::make (4, 8, 10),
>                               &const_quot, &rem), N <= 2);
> -  ASSERT_EQ (const_quot, C (N <= 2 ? 3 : 2));
> +  ASSERT_EQ (const_quot, C (N <= 2 ? 3 : 0));
>    if (N <= 2)
>      ASSERT_KNOWN_EQ (rem, ph::make (3, 1, 0));
>    ASSERT_EQ (can_div_trunc_p (ph::make (43, 79, 80),
>                               ph::make (4, 8, 10),
>                               &const_quot, &rem), N == 1);
> -  ASSERT_EQ (const_quot, C (N == 1 ? 10 : N == 2 ? 3 : 2));
> +  ASSERT_EQ (const_quot, C (N == 1 ? 10 : N == 2 ? 3 : 0));
>    if (N == 1)
>      ASSERT_KNOWN_EQ (rem, ch::make (3));
>    ASSERT_TRUE (can_div_trunc_p (ph::make (3, 4, 5),
> @@ -2024,6 +2035,19 @@ test_can_div_trunc_p_const ()
>                                 &const_quot, &rem));
>    ASSERT_EQ (const_quot, C (0));
>    ASSERT_KNOWN_EQ (rem, ch::make (0));
> +  ASSERT_TRUE (can_div_trunc_p (ph::make (9, 10, 20),
> +                               ph::make (5, 5, 20),
> +                               &const_quot, &rem));
> +  ASSERT_EQ (const_quot, C (1));
> +  ASSERT_KNOWN_EQ (rem, ph::make (4, 5, 0));
> +  ASSERT_EQ (can_div_trunc_p (ph::make (9, 11, 20),
> +                             ph::make (5, 5, 20),
> +                             &const_quot, &rem), N == 1);
> +  if (N == 1)
> +    {
> +      ASSERT_EQ (const_quot, C (1));
> +      ASSERT_KNOWN_EQ (rem, C (4));
> +    }
>  }
>
>  /* Test the form of can_div_trunc_p that returns a polynomail quotient,
> @@ -2093,14 +2117,14 @@ test_can_div_away_from_zero_p ()
>                                          ph::make (4, 8, 12),
>                                          &const_quot));
>    ASSERT_EQ (const_quot, C (3));
> -  ASSERT_EQ (can_div_away_from_zero_p (ph::make (15, 25, 40),
> -                                      ph::make (4, 8, 10),
> -                                      &const_quot), N <= 2);
> -  ASSERT_EQ (const_quot, C (N <= 2 ? 4 : 3));
> +  ASSERT_TRUE (can_div_away_from_zero_p (ph::make (15, 25, 40),
> +                                        ph::make (4, 8, 10),
> +                                        &const_quot));
> +  ASSERT_EQ (const_quot, C (4));
>    ASSERT_EQ (can_div_away_from_zero_p (ph::make (43, 79, 80),
>                                        ph::make (4, 8, 10),
>                                        &const_quot), N == 1);
> -  ASSERT_EQ (const_quot, C (N == 1 ? 11 : N == 2 ? 4 : 3));
> +  ASSERT_EQ (const_quot, C (N == 1 ? 11 : 4));
>    ASSERT_TRUE (can_div_away_from_zero_p (ph::make (3, 4, 5),
>                                          ph::make (4, 5, 6),
>                                          &const_quot));
> @@ -3232,6 +3256,45 @@ test_signed_can_div_trunc_p_const ()
>                                 &const_quot, &rem));
>    ASSERT_EQ (const_quot, -2);
>    ASSERT_KNOWN_EQ (rem, ph::make (2, 1, 3));
> +  ASSERT_TRUE (can_div_trunc_p (ph::make (-9, -10, -20),
> +                               ph::make (-5, -5, -20),
> +                               &const_quot, &rem));
> +  ASSERT_EQ (const_quot, C (1));
> +  ASSERT_KNOWN_EQ (rem, ph::make (-4, -5, 0));
> +  ASSERT_EQ (can_div_trunc_p (ph::make (-9, -11, -20),
> +                             ph::make (-5, -5, -20),
> +                             &const_quot, &rem), N == 1);
> +  if (N == 1)
> +    {
> +      ASSERT_EQ (const_quot, C (1));
> +      ASSERT_KNOWN_EQ (rem, C (-4));
> +    }
> +  ASSERT_TRUE (can_div_trunc_p (ph::make (9, 10, 20),
> +                               ph::make (-5, -5, -20),
> +                               &const_quot, &rem));
> +  ASSERT_EQ (const_quot, C (-1));
> +  ASSERT_KNOWN_EQ (rem, ph::make (4, 5, 0));
> +  ASSERT_EQ (can_div_trunc_p (ph::make (9, 11, 20),
> +                             ph::make (-5, -5, -20),
> +                             &const_quot, &rem), N == 1);
> +  if (N == 1)
> +    {
> +      ASSERT_EQ (const_quot, C (-1));
> +      ASSERT_KNOWN_EQ (rem, C (4));
> +    }
> +  ASSERT_TRUE (can_div_trunc_p (ph::make (-9, -10, -20),
> +                               ph::make (5, 5, 20),
> +                               &const_quot, &rem));
> +  ASSERT_EQ (const_quot, C (-1));
> +  ASSERT_KNOWN_EQ (rem, ph::make (-4, -5, 0));
> +  ASSERT_EQ (can_div_trunc_p (ph::make (-9, -11, -20),
> +                             ph::make (5, 5, 20),
> +                             &const_quot, &rem), N == 1);
> +  if (N == 1)
> +    {
> +      ASSERT_EQ (const_quot, C (-1));
> +      ASSERT_KNOWN_EQ (rem, C (-4));
> +    }
>  }
>
>  /* Test the form of can_div_trunc_p that returns a poly_int, for signed C.  
> */
> --
> 2.25.1
>

Reply via email to