Kyrill Tkachov <kyrylo.tkac...@arm.com> writes: > On 28/04/14 18:03, Kenneth Zadeck wrote: >> At this point we have believe that we have addressed all of the concerns >> that the community has made about the wide-int branch. We have also >> had each of the sections of the branch approved by the area maintainers. >> >> We are awaiting a clean build on the arm > > Unfortunately arm bootstrap fails a bit further down the line in stage2 while > building libstdc++-v3/src/c++98/ios.cc: > > xgcc: internal compiler error: Segmentation fault (program cc1plus) > Please submit a full bug report, > with preprocessed source if appropriate. > See <http://gcc.gnu.org/bugs.html> for instructions. > > Running the cc1plus subcommand through gdb gives: > Program received signal SIGSEGV, Segmentation fault. > 0x005c32c8 in real_to_decimal_for_mode(char*, real_value const*, unsigned > int, > unsigned int, int, machine_mode) () > (gdb) bt > #0 0x005c32c8 in real_to_decimal_for_mode(char*, real_value const*, unsigned > int, unsigned int, int, machine_mode) () > #1 0x000033f6 in ?? () > Backtrace stopped: previous frame identical to this frame (corrupt stack?) > (gdb) > > The debug info seems to be scarce here, any hints on where to look?
I saw the same thing on 32-bit s390-linux-gnu. It was due to a bug in the HOST_BITS_PER_WIDE_INT != HOST_BITS_PER_LONG part of real_to_integer, which would read from before the sig[] buffer if the array size is odd. The conversion to real_int also meant that we were passing the bit count "w" to the "need_canon_p" parameter of from_array. "w" looks bogus to me; the integer that we're constructing in val[] has precision "words * HOST_BITS_PER_WIDE_INT", with the top bit being the same as the significand. It seems simpler to do the operation on that precision instead. This also avoids the need for a fixed_wide_int, since "words * HOST_BITS_PER_WIDE_INT" takes the same number of HWIs as the final wide_int result. This patch seems to fix the failure for me and means that the stage2 real.s produced on s390-linux-gnu is the same as that produced by s390x-linux-gnu -m31. I'll test on s390-linux-gnu overnight. Thanks, Richard Index: gcc/real.c =================================================================== --- gcc/real.c (revision 209874) +++ gcc/real.c (working copy) @@ -1377,13 +1377,10 @@ wide_int real_to_integer (const REAL_VALUE_TYPE *r, bool *fail, int precision) { - typedef FIXED_WIDE_INT (WIDE_INT_MAX_PRECISION * 2) real_int; - HOST_WIDE_INT val[2 * MAX_BITSIZE_MODE_ANY_INT / HOST_BITS_PER_WIDE_INT]; + HOST_WIDE_INT val[2 * WIDE_INT_MAX_ELTS]; int exp; - int words; + int words, w; wide_int result; - real_int tmp; - int w; switch (r->cl) { @@ -1415,11 +1412,13 @@ if (exp > precision) goto overflow; + /* Put the significand into a wide_int that has precision W, which + is the smallest HWI-multiple that has at least PRECISION bits. + This ensures that the top bit of the significand is in the + top bit of the wide_int. */ words = (precision + HOST_BITS_PER_WIDE_INT - 1) / HOST_BITS_PER_WIDE_INT; + w = words * HOST_BITS_PER_WIDE_INT; - for (unsigned int i = 0; i < ARRAY_SIZE (val); i++) - val[i] = 0; - #if (HOST_BITS_PER_WIDE_INT == HOST_BITS_PER_LONG) for (int i = 0; i < words; i++) { @@ -1427,27 +1426,23 @@ val[i] = (j < 0) ? 0 : r->sig[j]; } #else - gcc_assert (HOST_BITS_PER_WIDE_INT == 2*HOST_BITS_PER_LONG); + gcc_assert (HOST_BITS_PER_WIDE_INT == 2 * HOST_BITS_PER_LONG); for (int i = 0; i < words; i++) { - int j = SIGSZ - (words * 2) + (i + 2) + 1; + int j = SIGSZ - (words * 2) + (i * 2); if (j < 0) val[i] = 0; else - { - val[i] = r->sig[j]; - unsigned HOST_WIDE_INT v = val[i]; - v <<= HOST_BITS_PER_LONG; - val[i] = v; - val[i] |= r->sig[j - 1]; - } + val[i] = r->sig[j]; + j += 1; + if (j >= 0) + val[i] |= (unsigned HOST_WIDE_INT) r->sig[j] << HOST_BITS_PER_LONG; } #endif - w = SIGSZ * HOST_BITS_PER_LONG + words * HOST_BITS_PER_WIDE_INT; - tmp = real_int::from_array - (val, (w + HOST_BITS_PER_WIDE_INT - 1) / HOST_BITS_PER_WIDE_INT, w); - tmp = wi::lrshift (tmp, (words * HOST_BITS_PER_WIDE_INT) - exp); - result = wide_int::from (tmp, precision, UNSIGNED); + /* Shift the value into place and truncate to the desired precision. */ + result = wide_int::from_array (val, words, w); + result = wi::lrshift (result, w - exp); + result = wide_int::from (result, precision, UNSIGNED); if (r->sign) return -result;