On Wed, 19 Jul 2023, Jakub Jelinek wrote: > Hi! > > As the following testcase shows, wi::divmod_internal doesn't handle > correctly signed division with precision > 64 when the dividend (and likely > divisor as well) is the type's minimum and the precision isn't divisible > by 64. > > A few lines above what the patch hunk changes is: > /* Make the divisor and dividend positive and remember what we > did. */ > if (sgn == SIGNED) > { > if (wi::neg_p (dividend)) > { > neg_dividend = -dividend; > dividend = neg_dividend; > dividend_neg = true; > } > if (wi::neg_p (divisor)) > { > neg_divisor = -divisor; > divisor = neg_divisor; > divisor_neg = true; > } > } > i.e. we negate negative dividend or divisor and remember those. > But, after we do that, when unpacking those values into b_dividend and > b_divisor we need to always treat the wide_ints as UNSIGNED, > because divmod_internal_2 performs an unsigned division only. > Now, if precision <= 64, we don't reach here at all, earlier code > handles it. If dividend or divisor aren't the most negative values, > the negation clears their most significant bit, so it doesn't really > matter if we unpack SIGNED or UNSIGNED. And if precision is multiple > of HOST_BITS_PER_WIDE_INT, there is no difference in behavior, while > -0x80000000000000000000000000000000 negates to > -0x80000000000000000000000000000000 the unpacking of it as SIGNED > or UNSIGNED works the same. > In the testcase, we have signed precision 119 and the dividend is > val = { 0, 0xffc0000000000000 }, len = 2, precision = 119 > both before and after negation. > Divisor is > val = { 2 }, len = 1, precision = 119 > But we really want to divide 0x400000000000000000000000000000 by 2 > unsigned and then negate at the end. > If it is unsigned precision 119 division > 0x400000000000000000000000000000 by 2 > dividend is > val = { 0, 0xffc0000000000000 }, len = 2, precision = 119 > but as we unpack it UNSIGNED, it is unpacked into > 0, 0, 0, 0x00400000 > > The following patch fixes it by always using UNSIGNED unpacking > because we've already negated negative values at that point if > sgn == SIGNED and so most negative constants should be treated as > positive. > > Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk, 13.2 > and later for older branches?
OK. Thanks, Richard. > 2023-07-19 Jakub Jelinek <ja...@redhat.com> > > PR tree-optimization/110731 > * wide-int.cc (wi::divmod_internal): Always unpack dividend and > divisor as UNSIGNED regardless of sgn. > > * gcc.dg/pr110731.c: New test. > > --- gcc/wide-int.cc.jj 2023-06-12 15:47:22.461502821 +0200 > +++ gcc/wide-int.cc 2023-07-19 09:52:40.241661869 +0200 > @@ -1911,9 +1911,9 @@ wi::divmod_internal (HOST_WIDE_INT *quot > } > > wi_unpack (b_dividend, dividend.get_val (), dividend.get_len (), > - dividend_blocks_needed, dividend_prec, sgn); > + dividend_blocks_needed, dividend_prec, UNSIGNED); > wi_unpack (b_divisor, divisor.get_val (), divisor.get_len (), > - divisor_blocks_needed, divisor_prec, sgn); > + divisor_blocks_needed, divisor_prec, UNSIGNED); > > m = dividend_blocks_needed; > b_dividend[m] = 0; > --- gcc/testsuite/gcc.dg/pr110731.c.jj 2023-07-19 10:03:03.707986705 > +0200 > +++ gcc/testsuite/gcc.dg/pr110731.c 2023-07-19 10:04:34.857716862 +0200 > @@ -0,0 +1,17 @@ > +/* PR tree-optimization/110731 */ > +/* { dg-do run { target int128 } } */ > +/* { dg-options "-O2" } */ > + > +__int128 > +foo (void) > +{ > + struct S { __int128 f : 119; } s = { ((__int128) -18014398509481984) << 64 > }; > + return s.f / 2; > +} > + > +int > +main () > +{ > + if (foo () != (((__int128) -9007199254740992) << 64)) > + __builtin_abort (); > +} > > Jakub > > -- Richard Biener <rguent...@suse.de> SUSE Software Solutions Germany GmbH, Frankenstrasse 146, 90461 Nuernberg, Germany; GF: Ivo Totev, Andrew Myers, Andrew McDonald, Boudien Moerman; HRB 36809 (AG Nuernberg)