Richi has asked the we break the wide-int patch so that the individual port and
front end maintainers can review their parts without have to go through the
entire patch. This patch covers the real.c code.
Ok?
* real.c: Include wide-int.h.
(real_to_integer2): Delete.
(real_to_integer): New function, returning a wide_int.
(real_from_integer): Take a wide_int rather than two HOST_WIDE_INTs.
(ten_to_ptwo): Update call to real_from_integer.
(real_digit): Likewise.
* real.h: Include signop.h, wide-int.h and insn-modes.h.
(real_to_integer2, REAL_VALUE_FROM_INT, REAL_VALUE_FROM_UNSIGNED_INT)
(REAL_VALUE_TO_INT): Delete.
(real_to_integer): Declare a wide-int form.
(real_from_integer): Take a wide_int rather than two HOST_WIDE_INTs.
diff --git a/gcc/real.c b/gcc/real.c
index c1af548..93dfc35 100644
--- a/gcc/real.c
+++ b/gcc/real.c
@@ -29,6 +29,7 @@
#include "realmpfr.h"
#include "tm_p.h"
#include "dfp.h"
+#include "wide-int.h"
/* The floating point model used internally is not exactly IEEE 754
compliant, and close to the description in the ISO C99 standard,
@@ -1370,42 +1371,37 @@ real_to_integer (const REAL_VALUE_TYPE *r)
}
}
-/* Likewise, but to an integer pair, HI+LOW. */
+/* Likewise, but producing a wide-int of PRECISION. If the value cannot
+ be represented in precision, *FAIL is set to TRUE. */
-void
-real_to_integer2 (HOST_WIDE_INT *plow, HOST_WIDE_INT *phigh,
- const REAL_VALUE_TYPE *r)
+wide_int
+real_to_integer (const REAL_VALUE_TYPE *r, bool *fail, int precision)
{
- REAL_VALUE_TYPE t;
- HOST_WIDE_INT low, high;
+ HOST_WIDE_INT val[2 * MAX_BITSIZE_MODE_ANY_INT / HOST_BITS_PER_WIDE_INT];
int exp;
+ int words;
+ wide_int result;
+ int w;
switch (r->cl)
{
case rvc_zero:
underflow:
- low = high = 0;
- break;
+ return wi::zero (precision);
case rvc_inf:
case rvc_nan:
overflow:
- high = (unsigned HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT - 1);
+ *fail = true;
+
if (r->sign)
- low = 0;
+ return wi::set_bit_in_zero (precision - 1, precision);
else
- {
- high--;
- low = -1;
- }
- break;
+ return ~wi::set_bit_in_zero (precision - 1, precision);
case rvc_normal:
if (r->decimal)
- {
- decimal_real_to_integer2 (plow, phigh, r);
- return;
- }
+ return decimal_real_to_integer (r, fail, precision);
exp = REAL_EXP (r);
if (exp <= 0)
@@ -1414,42 +1410,49 @@ real_to_integer2 (HOST_WIDE_INT *plow, HOST_WIDE_INT
*phigh,
undefined, so it doesn't matter what we return, and some callers
expect to be able to use this routine for both signed and
unsigned conversions. */
- if (exp > HOST_BITS_PER_DOUBLE_INT)
+ if (exp > precision)
goto overflow;
- rshift_significand (&t, r, HOST_BITS_PER_DOUBLE_INT - exp);
- if (HOST_BITS_PER_WIDE_INT == HOST_BITS_PER_LONG)
+ words = (precision + HOST_BITS_PER_WIDE_INT - 1) /
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++)
{
- high = t.sig[SIGSZ-1];
- low = t.sig[SIGSZ-2];
+ int j = SIGSZ - words + i;
+ val[i] = (j < 0) ? 0 : r->sig[j];
}
- else
+#else
+ gcc_assert (HOST_BITS_PER_WIDE_INT == 2*HOST_BITS_PER_LONG);
+ for (int i = 0; i < words; i++)
{
- gcc_assert (HOST_BITS_PER_WIDE_INT == 2*HOST_BITS_PER_LONG);
- high = t.sig[SIGSZ-1];
- high = high << (HOST_BITS_PER_LONG - 1) << 1;
- high |= t.sig[SIGSZ-2];
-
- low = t.sig[SIGSZ-3];
- low = low << (HOST_BITS_PER_LONG - 1) << 1;
- low |= t.sig[SIGSZ-4];
+ int j = SIGSZ - (words * 2) + (i + 2) + 1;
+ if (j < 0)
+ val[i] = 0;
+ else
+ {
+ val[i] = r->sig[j];
+ val[i] <<= HOST_BITS_PER_LONG;
+ val[i] |= r->sig[j - 1];
+ }
}
+#endif
+ w = SIGSZ * HOST_BITS_PER_LONG + words * HOST_BITS_PER_WIDE_INT;
+ result = wide_int::from_array
+ (val, (w + HOST_BITS_PER_WIDE_INT - 1) / HOST_BITS_PER_WIDE_INT, w);
+ result = wi::lrshift (result, (words * HOST_BITS_PER_WIDE_INT) - exp);
+ result = wide_int::from (result, precision, UNSIGNED);
if (r->sign)
- {
- if (low == 0)
- high = -high;
- else
- low = -low, high = ~high;
- }
- break;
+ return -result;
+ else
+ return result;
default:
gcc_unreachable ();
}
-
- *plow = low;
- *phigh = high;
}
/* A subroutine of real_to_decimal. Compute the quotient and remainder
@@ -2112,43 +2115,88 @@ real_from_string3 (REAL_VALUE_TYPE *r, const char *s,
enum machine_mode mode)
real_convert (r, mode, r);
}
-/* Initialize R from the integer pair HIGH+LOW. */
+/* Initialize R from the wide_int VAL_IN. The MODE is not VOIDmode,*/
void
real_from_integer (REAL_VALUE_TYPE *r, enum machine_mode mode,
- unsigned HOST_WIDE_INT low, HOST_WIDE_INT high,
- int unsigned_p)
+ const wide_int_ref &val_in, signop sgn)
{
- if (low == 0 && high == 0)
+ if (val_in == 0)
get_zero (r, 0);
else
{
+ unsigned int len = val_in.get_precision ();
+ int i, j, e = 0;
+ int maxbitlen = MAX_BITSIZE_MODE_ANY_INT + HOST_BITS_PER_WIDE_INT;
+ const unsigned int realmax = (SIGNIFICAND_BITS / HOST_BITS_PER_WIDE_INT
+ * HOST_BITS_PER_WIDE_INT);
+
memset (r, 0, sizeof (*r));
r->cl = rvc_normal;
- r->sign = high < 0 && !unsigned_p;
- SET_REAL_EXP (r, HOST_BITS_PER_DOUBLE_INT);
+ r->sign = wi::neg_p (val_in, sgn);
+
+ /* We have to ensure we can negate the largest negative number. */
+ wide_int val = wide_int::from (val_in, maxbitlen, sgn);
if (r->sign)
+ val = -val;
+
+ /* Ensure a multiple of HOST_BITS_PER_WIDE_INT, ceiling, as elt
+ won't work with precisions that are not a multiple of
+ HOST_BITS_PER_WIDE_INT. */
+ len += HOST_BITS_PER_WIDE_INT - 1;
+
+ /* Ensure we can represent the largest negative number. */
+ len += 1;
+
+ len = len/HOST_BITS_PER_WIDE_INT * HOST_BITS_PER_WIDE_INT;
+
+ /* Cap the size to the size allowed by real.h. */
+ if (len > realmax)
{
- high = ~high;
- if (low == 0)
- high += 1;
- else
- low = -low;
+ HOST_WIDE_INT cnt_l_z;
+ cnt_l_z = wi::clz (val);
+
+ if (maxbitlen - cnt_l_z > realmax)
+ {
+ e = maxbitlen - cnt_l_z - realmax;
+
+ /* This value is too large, we must shift it right to
+ preserve all the bits we can, and then bump the
+ exponent up by that amount. */
+ val = wi::lrshift (val, e);
+ }
+ len = realmax;
}
+ /* Clear out top bits so elt will work with precisions that aren't
+ a multiple of HOST_BITS_PER_WIDE_INT. */
+ val = wide_int::from (val, len, sgn);
+ len = len / HOST_BITS_PER_WIDE_INT;
+
+ SET_REAL_EXP (r, len * HOST_BITS_PER_WIDE_INT + e);
+
+ j = SIGSZ - 1;
if (HOST_BITS_PER_LONG == HOST_BITS_PER_WIDE_INT)
- {
- r->sig[SIGSZ-1] = high;
- r->sig[SIGSZ-2] = low;
- }
+ for (i = len - 1; i >= 0; i--)
+ {
+ r->sig[j--] = val.elt (i);
+ if (j < 0)
+ break;
+ }
else
{
gcc_assert (HOST_BITS_PER_LONG*2 == HOST_BITS_PER_WIDE_INT);
- r->sig[SIGSZ-1] = high >> (HOST_BITS_PER_LONG - 1) >> 1;
- r->sig[SIGSZ-2] = high;
- r->sig[SIGSZ-3] = low >> (HOST_BITS_PER_LONG - 1) >> 1;
- r->sig[SIGSZ-4] = low;
+ for (i = len - 1; i >= 0; i--)
+ {
+ HOST_WIDE_INT e = val.elt (i);
+ r->sig[j--] = e >> (HOST_BITS_PER_LONG - 1) >> 1;
+ if (j < 0)
+ break;
+ r->sig[j--] = e;
+ if (j < 0)
+ break;
+ }
}
normalize (r);
@@ -2238,7 +2286,7 @@ ten_to_ptwo (int n)
for (i = 0; i < n; ++i)
t *= t;
- real_from_integer (&tens[n], VOIDmode, t, 0, 1);
+ real_from_integer (&tens[n], VOIDmode, t, UNSIGNED);
}
else
{
@@ -2277,7 +2325,7 @@ real_digit (int n)
gcc_assert (n <= 9);
if (n > 0 && num[n].cl == rvc_zero)
- real_from_integer (&num[n], VOIDmode, n, 0, 1);
+ real_from_integer (&num[n], VOIDmode, n, UNSIGNED);
return &num[n];
}
diff --git a/gcc/real.h b/gcc/real.h
index c4e036e..23908c9 100644
--- a/gcc/real.h
+++ b/gcc/real.h
@@ -21,6 +21,9 @@
#define GCC_REAL_H
#include "machmode.h"
+#include "signop.h"
+#include "wide-int.h"
+#include "insn-modes.h"
/* An expanded form of the represented number. */
@@ -267,8 +270,6 @@ extern void real_to_hexadecimal (char *, const
REAL_VALUE_TYPE *,
/* Render R as an integer. */
extern HOST_WIDE_INT real_to_integer (const REAL_VALUE_TYPE *);
-extern void real_to_integer2 (HOST_WIDE_INT *, HOST_WIDE_INT *,
- const REAL_VALUE_TYPE *);
/* Initialize R from a decimal or hexadecimal string. Return -1 if
the value underflows, +1 if overflows, and 0 otherwise. */
@@ -276,10 +277,6 @@ extern int real_from_string (REAL_VALUE_TYPE *, const char
*);
/* Wrapper to allow different internal representation for decimal floats. */
extern void real_from_string3 (REAL_VALUE_TYPE *, const char *, enum
machine_mode);
-/* Initialize R from an integer pair HIGH/LOW. */
-extern void real_from_integer (REAL_VALUE_TYPE *, enum machine_mode,
- unsigned HOST_WIDE_INT, HOST_WIDE_INT, int);
-
extern long real_to_target_fmt (long *, const REAL_VALUE_TYPE *,
const struct real_format *);
extern long real_to_target (long *, const REAL_VALUE_TYPE *, enum
machine_mode);
@@ -361,12 +358,6 @@ extern const struct real_format arm_half_format;
#define REAL_VALUE_TO_TARGET_SINGLE(IN, OUT) \
((OUT) = real_to_target (NULL, &(IN), mode_for_size (32, MODE_FLOAT, 0)))
-#define REAL_VALUE_FROM_INT(r, lo, hi, mode) \
- real_from_integer (&(r), mode, lo, hi, 0)
-
-#define REAL_VALUE_FROM_UNSIGNED_INT(r, lo, hi, mode) \
- real_from_integer (&(r), mode, lo, hi, 1)
-
/* Real values to IEEE 754 decimal floats. */
/* IN is a REAL_VALUE_TYPE. OUT is an array of longs. */
@@ -383,9 +374,6 @@ extern const struct real_format arm_half_format;
extern REAL_VALUE_TYPE real_value_truncate (enum machine_mode,
REAL_VALUE_TYPE);
-#define REAL_VALUE_TO_INT(plow, phigh, r) \
- real_to_integer2 (plow, phigh, &(r))
-
extern REAL_VALUE_TYPE real_value_negate (const REAL_VALUE_TYPE *);
extern REAL_VALUE_TYPE real_value_abs (const REAL_VALUE_TYPE *);
@@ -485,4 +473,12 @@ extern bool real_isinteger (const REAL_VALUE_TYPE *c, enum
machine_mode mode);
number, (1 - b**-p) * b**emax for a given FP format FMT as a hex
float string. BUF must be large enough to contain the result. */
extern void get_max_float (const struct real_format *, char *, size_t);
+
+#ifndef GENERATOR_FILE
+/* real related routines. */
+extern wide_int real_to_integer (const REAL_VALUE_TYPE *, bool *, int);
+extern void real_from_integer (REAL_VALUE_TYPE *, enum machine_mode,
+ const wide_int_ref &, signop);
+#endif
+
#endif /* ! GCC_REAL_H */