/* real.c - software floating point emulation.
- Copyright (C) 1993-2014 Free Software Foundation, Inc.
+ Copyright (C) 1993-2015 Free Software Foundation, Inc.
Contributed by Stephen L. Moshier (moshier@world.std.com).
Re-written by Richard Henderson <rth@redhat.com>
#include "system.h"
#include "coretypes.h"
#include "tm.h"
+#include "alias.h"
+#include "symtab.h"
#include "tree.h"
#include "diagnostic-core.h"
-#include "real.h"
#include "realmpfr.h"
#include "tm_p.h"
#include "dfp.h"
+#include "rtl.h"
+#include "options.h"
/* The floating point model used internally is not exactly IEEE 754
compliant, and close to the description in the ISO C99 standard,
mode MODE. Return true if successful. */
bool
-exact_real_inverse (enum machine_mode mode, REAL_VALUE_TYPE *r)
+exact_real_inverse (machine_mode mode, REAL_VALUE_TYPE *r)
{
const REAL_VALUE_TYPE *one = real_digit (1);
REAL_VALUE_TYPE u;
in TMODE. */
bool
-real_can_shorten_arithmetic (enum machine_mode imode, enum machine_mode tmode)
+real_can_shorten_arithmetic (machine_mode imode, machine_mode tmode)
{
const struct real_format *tfmt, *ifmt;
tfmt = REAL_MODE_FORMAT (tmode);
}
}
-/* 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 * WIDE_INT_MAX_ELTS];
int exp;
+ int words, w;
+ wide_int result;
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)
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)
+ /* 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;
+
+#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);
+ if (j < 0)
+ val[i] = 0;
+ else
+ val[i] = r->sig[j];
+ j += 1;
+ if (j >= 0)
+ val[i] |= (unsigned HOST_WIDE_INT) r->sig[j] << HOST_BITS_PER_LONG;
}
+#endif
+ /* 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)
- {
- 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
void
real_to_decimal_for_mode (char *str, const REAL_VALUE_TYPE *r_orig,
size_t buf_size, size_t digits,
- int crop_trailing_zeros, enum machine_mode mode)
+ int crop_trailing_zeros, machine_mode mode)
{
const struct real_format *fmt = NULL;
const REAL_VALUE_TYPE *one, *ten;
because the hex digits used in real_from_mpfr did not
start with a digit 8 to f, but the exponent bounds above
should have avoided underflow or overflow. */
- gcc_assert (r->cl = rvc_normal);
+ gcc_assert (r->cl == rvc_normal);
/* Set a sticky bit if mpfr_strtofr was inexact. */
r->sig[0] |= inexact;
+ mpfr_clear (m);
}
}
/* Legacy. Similar, but return the result directly. */
REAL_VALUE_TYPE
-real_from_string2 (const char *s, enum machine_mode mode)
+real_from_string2 (const char *s, machine_mode mode)
{
REAL_VALUE_TYPE r;
/* Initialize R from string S and desired MODE. */
void
-real_from_string3 (REAL_VALUE_TYPE *r, const char *s, enum machine_mode mode)
+real_from_string3 (REAL_VALUE_TYPE *r, const char *s, machine_mode mode)
{
if (DECIMAL_FLOAT_MODE_P (mode))
decimal_real_from_string (r, s);
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)
+real_from_integer (REAL_VALUE_TYPE *r, machine_mode mode,
+ 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);
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
{
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];
}
bool
real_nan (REAL_VALUE_TYPE *r, const char *str, int quiet,
- enum machine_mode mode)
+ machine_mode mode)
{
const struct real_format *fmt;
If SIGN is nonzero, R is set to the most negative finite value. */
void
-real_maxval (REAL_VALUE_TYPE *r, int sign, enum machine_mode mode)
+real_maxval (REAL_VALUE_TYPE *r, int sign, machine_mode mode)
{
const struct real_format *fmt;
int np2;
/* Fills R with 2**N. */
void
-real_2expN (REAL_VALUE_TYPE *r, int n, enum machine_mode fmode)
+real_2expN (REAL_VALUE_TYPE *r, int n, machine_mode fmode)
{
memset (r, 0, sizeof (*r));
/* Extend or truncate to a new mode. */
void
-real_convert (REAL_VALUE_TYPE *r, enum machine_mode mode,
+real_convert (REAL_VALUE_TYPE *r, machine_mode mode,
const REAL_VALUE_TYPE *a)
{
const struct real_format *fmt;
/* Legacy. Likewise, except return the struct directly. */
REAL_VALUE_TYPE
-real_value_truncate (enum machine_mode mode, REAL_VALUE_TYPE a)
+real_value_truncate (machine_mode mode, REAL_VALUE_TYPE a)
{
REAL_VALUE_TYPE r;
real_convert (&r, mode, &a);
/* Return true if truncating to MODE is exact. */
bool
-exact_real_truncate (enum machine_mode mode, const REAL_VALUE_TYPE *a)
+exact_real_truncate (machine_mode mode, const REAL_VALUE_TYPE *a)
{
const struct real_format *fmt;
REAL_VALUE_TYPE t;
/* Similar, but look up the format from MODE. */
long
-real_to_target (long *buf, const REAL_VALUE_TYPE *r, enum machine_mode mode)
+real_to_target (long *buf, const REAL_VALUE_TYPE *r, machine_mode mode)
{
const struct real_format *fmt;
/* Similar, but look up the format from MODE. */
void
-real_from_target (REAL_VALUE_TYPE *r, const long *buf, enum machine_mode mode)
+real_from_target (REAL_VALUE_TYPE *r, const long *buf, machine_mode mode)
{
const struct real_format *fmt;
/* ??? Legacy. Should get access to real_format directly. */
int
-significand_size (enum machine_mode mode)
+significand_size (machine_mode mode)
{
const struct real_format *fmt;
true,
true,
true,
- false
+ false,
+ "ieee_single"
};
const struct real_format mips_single_format =
true,
true,
false,
- true
+ true,
+ "mips_single"
};
const struct real_format motorola_single_format =
true,
true,
true,
- true
+ true,
+ "motorola_single"
};
/* SPU Single Precision (Extended-Range Mode) format is the same as IEEE
true,
true,
false,
- false
+ false,
+ "spu_single"
};
\f
/* IEEE double-precision format. */
true,
true,
true,
- false
+ false,
+ "ieee_double"
};
const struct real_format mips_double_format =
true,
true,
false,
- true
+ true,
+ "mips_double"
};
const struct real_format motorola_double_format =
true,
true,
true,
- true
+ true,
+ "motorola_double"
};
\f
/* IEEE extended real format. This comes in three flavors: Intel's as
long intermed[3];
encode_ieee_extended (fmt, intermed, r);
+ if (r->cl == rvc_inf)
+ /* For infinity clear the explicit integer bit again, so that the
+ format matches the canonical infinity generated by the FPU. */
+ intermed[1] = 0;
+
/* Motorola chips are assumed always to be big-endian. Also, the
padding in a Motorola extended real goes between the exponent and
the mantissa. At this point the mantissa is entirely within
true,
true,
true,
- true
+ true,
+ "ieee_extended_motorola"
};
const struct real_format ieee_extended_intel_96_format =
true,
true,
true,
- false
+ false,
+ "ieee_extended_intel_96"
};
const struct real_format ieee_extended_intel_128_format =
true,
true,
true,
- false
+ false,
+ "ieee_extended_intel_128"
};
/* The following caters to i386 systems that set the rounding precision
true,
true,
true,
- false
+ false,
+ "ieee_extended_intel_96_round_53"
};
\f
/* IBM 128-bit extended precision format: a pair of IEEE double precision
true,
true,
true,
- false
+ false,
+ "ibm_extended"
};
const struct real_format mips_extended_format =
true,
true,
false,
- true
+ true,
+ "mips_extended"
};
\f
true,
true,
true,
- false
+ false,
+ "ieee_quad"
};
const struct real_format mips_quad_format =
true,
true,
false,
- true
+ true,
+ "mips_quad"
};
\f
/* Descriptions of VAX floating point formats can be found beginning at
false,
false,
false,
- false
+ false,
+ "vax_f"
};
const struct real_format vax_d_format =
false,
false,
false,
- false
+ false,
+ "vax_d"
};
const struct real_format vax_g_format =
false,
false,
false,
- false
+ false,
+ "vax_g"
};
\f
/* Encode real R into a single precision DFP value in BUF. */
true,
true,
true,
- false
+ false,
+ "decimal_single"
};
/* Double precision decimal floating point (IEEE 754). */
true,
true,
true,
- false
+ false,
+ "decimal_double"
};
/* Quad precision decimal floating point (IEEE 754). */
true,
true,
true,
- false
+ false,
+ "decimal_quad"
};
\f
/* Encode half-precision floats. This routine is used both for the IEEE
true,
true,
true,
- false
+ false,
+ "ieee_half"
};
/* ARM's alternative half-precision format, similar to IEEE but with
true,
true,
false,
- false
+ false,
+ "arm_half"
};
\f
/* A synthetic "format" for internal arithmetic. It's the size of the
false,
true,
true,
- false
+ false,
+ "real_internal"
};
\f
/* Calculate X raised to the integer exponent N in mode MODE and store
Algorithms", "The Art of Computer Programming", Volume 2. */
bool
-real_powi (REAL_VALUE_TYPE *r, enum machine_mode mode,
+real_powi (REAL_VALUE_TYPE *r, machine_mode mode,
const REAL_VALUE_TYPE *x, HOST_WIDE_INT n)
{
unsigned HOST_WIDE_INT bit;
towards zero, placing the result in R in mode MODE. */
void
-real_trunc (REAL_VALUE_TYPE *r, enum machine_mode mode,
+real_trunc (REAL_VALUE_TYPE *r, machine_mode mode,
const REAL_VALUE_TYPE *x)
{
do_fix_trunc (r, x);
down, placing the result in R in mode MODE. */
void
-real_floor (REAL_VALUE_TYPE *r, enum machine_mode mode,
+real_floor (REAL_VALUE_TYPE *r, machine_mode mode,
const REAL_VALUE_TYPE *x)
{
REAL_VALUE_TYPE t;
up, placing the result in R in mode MODE. */
void
-real_ceil (REAL_VALUE_TYPE *r, enum machine_mode mode,
+real_ceil (REAL_VALUE_TYPE *r, machine_mode mode,
const REAL_VALUE_TYPE *x)
{
REAL_VALUE_TYPE t;
zero. */
void
-real_round (REAL_VALUE_TYPE *r, enum machine_mode mode,
+real_round (REAL_VALUE_TYPE *r, machine_mode mode,
const REAL_VALUE_TYPE *x)
{
do_add (r, x, &dconsthalf, x->sign);
/* Check whether the real constant value given is an integer. */
bool
-real_isinteger (const REAL_VALUE_TYPE *c, enum machine_mode mode)
+real_isinteger (const REAL_VALUE_TYPE *c, machine_mode mode)
{
REAL_VALUE_TYPE cint;
gcc_assert (strlen (buf) < len);
}
+
+/* True if mode M has a NaN representation and
+ the treatment of NaN operands is important. */
+
+bool
+HONOR_NANS (machine_mode m)
+{
+ return MODE_HAS_NANS (m) && !flag_finite_math_only;
+}
+
+bool
+HONOR_NANS (const_tree t)
+{
+ return HONOR_NANS (element_mode (t));
+}
+
+bool
+HONOR_NANS (const_rtx x)
+{
+ return HONOR_NANS (GET_MODE (x));
+}
+
+/* Like HONOR_NANs, but true if we honor signaling NaNs (or sNaNs). */
+
+bool
+HONOR_SNANS (machine_mode m)
+{
+ return flag_signaling_nans && HONOR_NANS (m);
+}
+
+bool
+HONOR_SNANS (const_tree t)
+{
+ return HONOR_SNANS (element_mode (t));
+}
+
+bool
+HONOR_SNANS (const_rtx x)
+{
+ return HONOR_SNANS (GET_MODE (x));
+}
+
+/* As for HONOR_NANS, but true if the mode can represent infinity and
+ the treatment of infinite values is important. */
+
+bool
+HONOR_INFINITIES (machine_mode m)
+{
+ return MODE_HAS_INFINITIES (m) && !flag_finite_math_only;
+}
+
+bool
+HONOR_INFINITIES (const_tree t)
+{
+ return HONOR_INFINITIES (element_mode (t));
+}
+
+bool
+HONOR_INFINITIES (const_rtx x)
+{
+ return HONOR_INFINITIES (GET_MODE (x));
+}
+
+/* Like HONOR_NANS, but true if the given mode distinguishes between
+ positive and negative zero, and the sign of zero is important. */
+
+bool
+HONOR_SIGNED_ZEROS (machine_mode m)
+{
+ return MODE_HAS_SIGNED_ZEROS (m) && flag_signed_zeros;
+}
+
+bool
+HONOR_SIGNED_ZEROS (const_tree t)
+{
+ return HONOR_SIGNED_ZEROS (element_mode (t));
+}
+
+bool
+HONOR_SIGNED_ZEROS (const_rtx x)
+{
+ return HONOR_SIGNED_ZEROS (GET_MODE (x));
+}
+
+/* Like HONOR_NANS, but true if given mode supports sign-dependent rounding,
+ and the rounding mode is important. */
+
+bool
+HONOR_SIGN_DEPENDENT_ROUNDING (machine_mode m)
+{
+ return MODE_HAS_SIGN_DEPENDENT_ROUNDING (m) && flag_rounding_math;
+}
+
+bool
+HONOR_SIGN_DEPENDENT_ROUNDING (const_tree t)
+{
+ return HONOR_SIGN_DEPENDENT_ROUNDING (element_mode (t));
+}
+
+bool
+HONOR_SIGN_DEPENDENT_ROUNDING (const_rtx x)
+{
+ return HONOR_SIGN_DEPENDENT_ROUNDING (GET_MODE (x));
+}