Likely GMP bug

Dennis Clarke dclarke at blastwave.org
Fri May 25 18:01:41 UTC 2018


On 05/25/2018 08:10 AM, Niels Möller wrote:
> Dennis Clarke <dclarke at blastwave.org> writes:
> 
>> gcd_1.c:187:13: runtime error: shift exponent 32 is too large for
>> 32-bit type 'long unsigned int'
>> FAIL t-cmp_ui (exit status: 1)
> 
> And code is essentially
> 
>        count_trailing_zeros (c, t);
>        ulimb >>= (c + 1);
> 
> The intention is to shift right to get rid of both trailing zero bits,
> and the redundant least significant one bit.
> 
> That fails with undefined behavior if by chance t == 2^31, so that c ==
> 31.

I would have thought the deBruijn hash method works for all 32-bit
values to find trailing consecutive zero bits :

http://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightMultLookup

> 
> I don't see how that can happen, though, since ulimb, vlimb < 2^31
> through out the loop, and t = (ulimb - vlimb) mod 2^32.
> 
> And I also wonder why USE_ZEROTAB is set to 0 here.

I have run all the testsuite, both with the assembly and without, on a
pure 32-bit Debian machine and see no errors anywhere.

I'd love to see an isolated code case that creates that runtime error.

Dennis

ps: hours later

phobos$
phobos$ find . -type f -ls | grep "gcd_1" | awk '{ printf("%4i    %s\n", 
$7, $11 )}'
7158    ./mpn/ia64/gcd_1.asm
7192    ./mpn/gcd_1.o
4465    ./mpn/generic/gcd_1.c
7516    ./mpn/.libs/gcd_1.o
4125    ./mpn/x86/k7/gcd_1.asm
6418    ./mpn/x86/k6/gcd_1.asm
3820    ./mpn/x86/p6/gcd_1.asm
1057    ./mpn/x86/fat/gcd_1.c
3323    ./mpn/sparc64/gcd_1.asm
2807    ./mpn/powerpc64/mode64/gcd_1.asm
2520    ./mpn/powerpc64/mode64/p7/gcd_1.asm
2983    ./mpn/arm64/gcd_1.asm
2819    ./mpn/arm/v5/gcd_1.asm
2756    ./mpn/arm/v6t2/gcd_1.asm
4198    ./mpn/alpha/ev67/gcd_1.asm
  267    ./mpn/gcd_1.lo
1255    ./mpn/x86_64/bd1/gcd_1.asm
1255    ./mpn/x86_64/k10/gcd_1.asm
4260    ./mpn/x86_64/gcd_1.asm
4228    ./mpn/x86_64/core2/gcd_1.asm
1255    ./mpn/x86_64/zen/gcd_1.asm
1255    ./mpn/x86_64/nano/gcd_1.asm
phobos$ cat mpn/generic/gcd_1.c
/* mpn_gcd_1 -- mpn and limb greatest common divisor.

Copyright 1994, 1996, 2000, 2001, 2009, 2012 Free Software Foundation, Inc.

This file is part of the GNU MP Library.

The GNU MP Library is free software; you can redistribute it and/or modify
it under the terms of either:

   * the GNU Lesser General Public License as published by the Free
     Software Foundation; either version 3 of the License, or (at your
     option) any later version.

or

   * the GNU General Public License as published by the Free Software
     Foundation; either version 2 of the License, or (at your option) any
     later version.

or both in parallel, as here.

The GNU MP Library is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received copies of the GNU General Public License and the
GNU Lesser General Public License along with the GNU MP Library.  If not,
see https://www.gnu.org/licenses/.  */

#include "gmp-impl.h"
#include "longlong.h"

#ifndef GCD_1_METHOD
#define GCD_1_METHOD 2
#endif

#define USE_ZEROTAB 0

#if USE_ZEROTAB
#define MAXSHIFT 4
#define MASK ((1 << MAXSHIFT) - 1)
static const unsigned char zerotab[1 << MAXSHIFT] =
{
#if MAXSHIFT > 4
   5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
#endif
   4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
};
#endif

/* Does not work for U == 0 or V == 0.  It would be tough to make it 
work for
    V == 0 since gcd(x,0) = x, and U does not generally fit in an mp_limb_t.

    The threshold for doing u%v when size==1 will vary by CPU according to
    the speed of a division and the code generated for the main loop.  Any
    tuning for this is left to a CPU specific implementation.  */

mp_limb_t
mpn_gcd_1 (mp_srcptr up, mp_size_t size, mp_limb_t vlimb)
{
   mp_limb_t      ulimb;
   unsigned long  zero_bits, u_low_zero_bits;

   ASSERT (size >= 1);
   ASSERT (vlimb != 0);
   ASSERT_MPN_NONZERO_P (up, size);

   ulimb = up[0];

   /* Need vlimb odd for modexact, want it odd to get common zeros. */
   count_trailing_zeros (zero_bits, vlimb);
   vlimb >>= zero_bits;

   if (size > 1)
     {
       /* Must get common zeros before the mod reduction.  If ulimb==0 then
          vlimb already gives the common zeros.  */
       if (ulimb != 0)
         {
           count_trailing_zeros (u_low_zero_bits, ulimb);
           zero_bits = MIN (zero_bits, u_low_zero_bits);
         }

       ulimb = MPN_MOD_OR_MODEXACT_1_ODD (up, size, vlimb);
       if (ulimb == 0)
         goto done;

       goto strip_u_maybe;
     }

   /* size==1, so up[0]!=0 */
   count_trailing_zeros (u_low_zero_bits, ulimb);
   ulimb >>= u_low_zero_bits;
   zero_bits = MIN (zero_bits, u_low_zero_bits);

   /* make u bigger */
   if (vlimb > ulimb)
     MP_LIMB_T_SWAP (ulimb, vlimb);

   /* if u is much bigger than v, reduce using a division rather than
      chipping away at it bit-by-bit */
   if ((ulimb >> 16) > vlimb)
     {
       ulimb %= vlimb;
       if (ulimb == 0)
         goto done;
       goto strip_u_maybe;
     }

   ASSERT (ulimb & 1);
   ASSERT (vlimb & 1);

#if GCD_1_METHOD == 1
   while (ulimb != vlimb)
     {
       ASSERT (ulimb & 1);
       ASSERT (vlimb & 1);

       if (ulimb > vlimb)
         {
           ulimb -= vlimb;
           do
             {
               ulimb >>= 1;
               ASSERT (ulimb != 0);
             strip_u_maybe:
               ;
             }
           while ((ulimb & 1) == 0);
         }
       else /*  vlimb > ulimb.  */
         {
           vlimb -= ulimb;
           do
             {
               vlimb >>= 1;
               ASSERT (vlimb != 0);
             }
           while ((vlimb & 1) == 0);
         }
     }
#else
# if GCD_1_METHOD  == 2

   ulimb >>= 1;
   vlimb >>= 1;

   while (ulimb != vlimb)
     {
       int c;
       mp_limb_t t;
       mp_limb_t vgtu;

       t = ulimb - vlimb;
       vgtu = LIMB_HIGHBIT_TO_MASK (t);

       /* v <-- min (u, v) */
       vlimb += (vgtu & t);

       /* u <-- |u - v| */
       ulimb = (t ^ vgtu) - vgtu;

#if USE_ZEROTAB
       /* Number of trailing zeros is the same no matter if we look at
        * t or ulimb, but using t gives more parallelism. */
       c = zerotab[t & MASK];

       while (UNLIKELY (c == MAXSHIFT))
         {
           ulimb >>= MAXSHIFT;
           if (0)
           strip_u_maybe:
             vlimb >>= 1;

           c = zerotab[ulimb & MASK];
         }
#else
       if (0)
         {
         strip_u_maybe:
           vlimb >>= 1;
           t = ulimb;
         }
       count_trailing_zeros (c, t);
#endif
       ulimb >>= (c + 1);
     }

   vlimb = (vlimb << 1) | 1;
# else
#  error Unknown GCD_1_METHOD
# endif
#endif

  done:
   return vlimb << zero_bits;
}
phobos$


More information about the gmp-bugs mailing list