+2016-11-23 James Greenhalgh <james.greenhalgh@arm.com>
+
+ * toplev.c (init_excess_precision): Delete most logic.
+ * tree.c (excess_precision_type): Rewrite to use
+ TARGET_EXCESS_PRECISION.
+ * doc/invoke.texi (-fexcess-precision): Document behaviour in a
+ more generic fashion.
+ * ginclude/float.h: Wrap definition of FLT_EVAL_METHOD in
+ __STDC_WANT_IEC_60559_TYPES_EXT__.
+
2016-11-23 James Greenhalgh <james.greenhalgh@arm.com>
* common.opt (fpermitted-flt-eval-methods): New.
+2016-11-23 James Greenhalgh <james.greenhalgh@arm.com>
+
+ * c-common.c (excess_precision_mode_join): New.
+ (c_ts18661_flt_eval_method): New.
+ (c_c11_flt_eval_method): Likewise.
+ (c_flt_eval_method): Likewise.
+ * c-common.h (excess_precision_mode_join): New.
+ (c_flt_eval_method): Likewise.
+ * c-cppbuiltin.c (c_cpp_flt_eval_method_iec_559): New.
+ (cpp_iec_559_value): Call it.
+ (c_cpp_builtins): Modify logic for __LIBGCC_*_EXCESS_PRECISION__,
+ call c_flt_eval_method to set __FLT_EVAL_METHOD__ and
+ __FLT_EVAL_METHOD_TS_18661_3__.
+
2016-11-23 James Greenhalgh <james.greenhalgh@arm.com>
* c-opts.c (c_common_post_options): Add logic to handle the default
return bm.get_best_meaningful_candidate ();
}
+/* Return the latice point which is the wider of the two FLT_EVAL_METHOD
+ modes X, Y. This isn't just >, as the FLT_EVAL_METHOD values added
+ by C TS 18661-3 for interchange types that are computed in their
+ native precision are larger than the C11 values for evaluating in the
+ precision of float/double/long double. If either mode is
+ FLT_EVAL_METHOD_UNPREDICTABLE, return that. */
+
+enum flt_eval_method
+excess_precision_mode_join (enum flt_eval_method x,
+ enum flt_eval_method y)
+{
+ if (x == FLT_EVAL_METHOD_UNPREDICTABLE
+ || y == FLT_EVAL_METHOD_UNPREDICTABLE)
+ return FLT_EVAL_METHOD_UNPREDICTABLE;
+
+ /* GCC only supports one interchange type right now, _Float16. If
+ we're evaluating _Float16 in 16-bit precision, then flt_eval_method
+ will be FLT_EVAL_METHOD_PROMOTE_TO_FLOAT16. */
+ if (x == FLT_EVAL_METHOD_PROMOTE_TO_FLOAT16)
+ return y;
+ if (y == FLT_EVAL_METHOD_PROMOTE_TO_FLOAT16)
+ return x;
+
+ /* Other values for flt_eval_method are directly comparable, and we want
+ the maximum. */
+ return MAX (x, y);
+}
+
+/* Return the value that should be set for FLT_EVAL_METHOD in the
+ context of ISO/IEC TS 18861-3.
+
+ This relates to the effective excess precision seen by the user,
+ which is the join point of the precision the target requests for
+ -fexcess-precision={standard,fast} and the implicit excess precision
+ the target uses. */
+
+static enum flt_eval_method
+c_ts18661_flt_eval_method (void)
+{
+ enum flt_eval_method implicit
+ = targetm.c.excess_precision (EXCESS_PRECISION_TYPE_IMPLICIT);
+
+ enum excess_precision_type flag_type
+ = (flag_excess_precision_cmdline == EXCESS_PRECISION_STANDARD
+ ? EXCESS_PRECISION_TYPE_STANDARD
+ : EXCESS_PRECISION_TYPE_FAST);
+
+ enum flt_eval_method requested
+ = targetm.c.excess_precision (flag_type);
+
+ return excess_precision_mode_join (implicit, requested);
+}
+
+/* As c_cpp_ts18661_flt_eval_method, but clamps the expected values to
+ those that were permitted by C11. That is to say, eliminates
+ FLT_EVAL_METHOD_PROMOTE_TO_FLOAT16. */
+
+static enum flt_eval_method
+c_c11_flt_eval_method (void)
+{
+ return excess_precision_mode_join (c_ts18661_flt_eval_method (),
+ FLT_EVAL_METHOD_PROMOTE_TO_FLOAT);
+}
+
+/* Return the value that should be set for FLT_EVAL_METHOD.
+ MAYBE_C11_ONLY_P is TRUE if we should check
+ FLAG_PERMITTED_EVAL_METHODS as to whether we should limit the possible
+ values we can return to those from C99/C11, and FALSE otherwise.
+ See the comments on c_ts18661_flt_eval_method for what value we choose
+ to set here. */
+
+int
+c_flt_eval_method (bool maybe_c11_only_p)
+{
+ if (maybe_c11_only_p
+ && flag_permitted_flt_eval_methods
+ == PERMITTED_FLT_EVAL_METHODS_C11)
+ return c_c11_flt_eval_method ();
+ else
+ return c_ts18661_flt_eval_method ();
+}
+
#include "gt-c-family-c-common.h"
extern tree tm_mask_to_attr (int);
extern tree find_tm_attribute (tree);
+extern enum flt_eval_method
+excess_precision_mode_join (enum flt_eval_method, enum flt_eval_method);
+
+extern int c_flt_eval_method (bool ts18661_p);
+
#if CHECKING_P
namespace selftest {
extern void c_format_c_tests (void);
(have_swap[psize]? 2 : 1));
}
+/* Return TRUE if the implicit excess precision in which the back-end will
+ compute floating-point calculations is not more than the explicit
+ excess precision that the front-end will apply under
+ -fexcess-precision=[standard|fast].
+
+ More intuitively, return TRUE if the excess precision proposed by the
+ front-end is the excess precision that will actually be used. */
+
+static bool
+c_cpp_flt_eval_method_iec_559 (void)
+{
+ enum excess_precision_type front_end_ept
+ = (flag_excess_precision_cmdline == EXCESS_PRECISION_STANDARD
+ ? EXCESS_PRECISION_TYPE_STANDARD
+ : EXCESS_PRECISION_TYPE_FAST);
+
+ enum flt_eval_method back_end
+ = targetm.c.excess_precision (EXCESS_PRECISION_TYPE_IMPLICIT);
+
+ enum flt_eval_method front_end
+ = targetm.c.excess_precision (front_end_ept);
+
+ return excess_precision_mode_join (front_end, back_end) == front_end;
+}
+
/* Return the value for __GCC_IEC_559. */
static int
cpp_iec_559_value (void)
|| !dfmt->has_signed_zero)
ret = 0;
- /* In strict C standards conformance mode, consider unpredictable
- excess precision to mean lack of IEEE 754 support. The same
- applies to unpredictable contraction. For C++, and outside
- strict conformance mode, do not consider these options to mean
- lack of IEEE 754 support. */
+ /* In strict C standards conformance mode, consider a back-end providing
+ more implicit excess precision than the explicit excess precision
+ the front-end options would require to mean a lack of IEEE 754
+ support. For C++, and outside strict conformance mode, do not consider
+ this to mean a lack of IEEE 754 support. */
+
if (flag_iso
&& !c_dialect_cxx ()
- && TARGET_FLT_EVAL_METHOD != 0
- && flag_excess_precision_cmdline != EXCESS_PRECISION_STANDARD)
+ && !c_cpp_flt_eval_method_iec_559 ())
ret = 0;
+
if (flag_iso
&& !c_dialect_cxx ()
&& flag_fp_contract_mode == FP_CONTRACT_FAST)
builtin_define_with_int_value ("__GCC_IEC_559_COMPLEX",
cpp_iec_559_complex_value ());
- /* float.h needs to know this. */
+ /* float.h needs these to correctly set FLT_EVAL_METHOD
+
+ We define two values:
+
+ __FLT_EVAL_METHOD__
+ Which, depending on the value given for
+ -fpermitted-flt-eval-methods, may be limited to only those values
+ for FLT_EVAL_METHOD defined in C99/C11.
+
+ __FLT_EVAL_METHOD_TS_18661_3__
+ Which always permits the values for FLT_EVAL_METHOD defined in
+ ISO/IEC TS 18661-3. */
builtin_define_with_int_value ("__FLT_EVAL_METHOD__",
- TARGET_FLT_EVAL_METHOD);
+ c_flt_eval_method (true));
+ builtin_define_with_int_value ("__FLT_EVAL_METHOD_TS_18661_3__",
+ c_flt_eval_method (false));
/* And decfloat.h needs this. */
builtin_define_with_int_value ("__DEC_EVAL_METHOD__",
gcc_assert (found_suffix);
}
builtin_define_with_value (macro_name, suffix, 0);
+
+ /* The way __LIBGCC_*_EXCESS_PRECISION__ is used is about
+ eliminating excess precision from results assigned to
+ variables - meaning it should be about the implicit excess
+ precision only. */
bool excess_precision = false;
- if (TARGET_FLT_EVAL_METHOD != 0
- && mode != TYPE_MODE (long_double_type_node)
- && (mode == TYPE_MODE (float_type_node)
- || mode == TYPE_MODE (double_type_node)))
- switch (TARGET_FLT_EVAL_METHOD)
- {
- case -1:
- case 2:
- excess_precision = true;
- break;
-
- case 1:
- excess_precision = mode == TYPE_MODE (float_type_node);
- break;
-
- default:
- gcc_unreachable ();
- }
+ machine_mode float16_type_mode = (float16_type_node
+ ? TYPE_MODE (float16_type_node)
+ : VOIDmode);
+ switch (targetm.c.excess_precision
+ (EXCESS_PRECISION_TYPE_IMPLICIT))
+ {
+ case FLT_EVAL_METHOD_UNPREDICTABLE:
+ case FLT_EVAL_METHOD_PROMOTE_TO_LONG_DOUBLE:
+ excess_precision = (mode == float16_type_mode
+ || mode == TYPE_MODE (float_type_node)
+ || mode == TYPE_MODE (double_type_node));
+ break;
+
+ case FLT_EVAL_METHOD_PROMOTE_TO_DOUBLE:
+ excess_precision = (mode == float16_type_mode
+ || mode == TYPE_MODE (float_type_node));
+ break;
+ case FLT_EVAL_METHOD_PROMOTE_TO_FLOAT:
+ excess_precision = mode == float16_type_mode;
+ break;
+ case FLT_EVAL_METHOD_PROMOTE_TO_FLOAT16:
+ excess_precision = false;
+ break;
+ default:
+ gcc_unreachable ();
+ }
macro_name = (char *) alloca (strlen (name)
+ sizeof ("__LIBGCC__EXCESS_"
"PRECISION__"));
@item -fexcess-precision=@var{style}
@opindex fexcess-precision
This option allows further control over excess precision on machines
-where floating-point registers have more precision than the IEEE
-@code{float} and @code{double} types and the processor does not
-support operations rounding to those types. By default,
-@option{-fexcess-precision=fast} is in effect; this means that
-operations are carried out in the precision of the registers and that
-it is unpredictable when rounding to the types specified in the source
-code takes place. When compiling C, if
-@option{-fexcess-precision=standard} is specified then excess
-precision follows the rules specified in ISO C99; in particular,
+where floating-point operations occur in a format with more precision or
+range than the IEEE standard and interchange floating-point types. By
+default, @option{-fexcess-precision=fast} is in effect; this means that
+operations may be carried out in a wider precision than the types specified
+in the source if that would result in faster code, and it is unpredictable
+when rounding to the types specified in the source code takes place.
+When compiling C, if @option{-fexcess-precision=standard} is specified then
+excess precision follows the rules specified in ISO C99; in particular,
both casts and assignments cause values to be rounded to their
semantic types (whereas @option{-ffloat-store} only affects
assignments). This option is enabled by default for C if a strict
#if (defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) \
|| (defined (__cplusplus) && __cplusplus >= 201103L)
-/* The floating-point expression evaluation method.
- -1 indeterminate
- 0 evaluate all operations and constants just to the range and
- precision of the type
- 1 evaluate operations and constants of type float and double
- to the range and precision of the double type, evaluate
- long double operations and constants to the range and
- precision of the long double type
- 2 evaluate all operations and constants to the range and
- precision of the long double type
+/* The floating-point expression evaluation method. The precise
+ definitions of these values are generalised to include support for
+ the interchange and extended types defined in ISO/IEC TS 18661-3.
+ Prior to this (for C99/C11) the definitions were:
+
+ -1 indeterminate
+ 0 evaluate all operations and constants just to the range and
+ precision of the type
+ 1 evaluate operations and constants of type float and double
+ to the range and precision of the double type, evaluate
+ long double operations and constants to the range and
+ precision of the long double type
+ 2 evaluate all operations and constants to the range and
+ precision of the long double type
+
+ The TS 18661-3 definitions are:
+
+ -1 indeterminate
+ 0 evaluate all operations and constants, whose semantic type has
+ at most the range and precision of float, to the range and
+ precision of float; evaluate all other operations and constants
+ to the range and precision of the semantic type.
+ 1 evaluate all operations and constants, whose semantic type has
+ at most the range and precision of double, to the range and
+ precision of double; evaluate all other operations and constants
+ to the range and precision of the semantic type.
+ 2 evaluate all operations and constants, whose semantic type has
+ at most the range and precision of long double, to the range and
+ precision of long double; evaluate all other operations and
+ constants to the range and precision of the semantic type.
+ N where _FloatN is a supported interchange floating type
+ evaluate all operations and constants, whose semantic type has
+ at most the range and precision of the _FloatN type, to the
+ range and precision of the _FloatN type; evaluate all other
+ operations and constants to the range and precision of the
+ semantic type.
+ N + 1, where _FloatNx is a supported extended floating type
+ evaluate operations and constants, whose semantic type has at
+ most the range and precision of the _FloatNx type, to the range
+ and precision of the _FloatNx type; evaluate all other
+ operations and constants to the range and precision of the
+ semantic type.
+
+ The compiler predefines two macros:
+
+ __FLT_EVAL_METHOD__
+ Which, depending on the value given for
+ -fpermitted-flt-eval-methods, may be limited to only those values
+ for FLT_EVAL_METHOD defined in C99/C11.
+
+ __FLT_EVAL_METHOD_TS_18661_3__
+ Which always permits the values for FLT_EVAL_METHOD defined in
+ ISO/IEC TS 18661-3.
+
+ Here we want to use __FLT_EVAL_METHOD__, unless
+ __STDC_WANT_IEC_60559_TYPES_EXT__ is defined, in which case the user
+ is specifically asking for the ISO/IEC TS 18661-3 types, so we use
+ __FLT_EVAL_METHOD_TS_18661_3__.
??? This ought to change with the setting of the fp control word;
the value provided by the compiler assumes the widest setting. */
#undef FLT_EVAL_METHOD
+#ifdef __STDC_WANT_IEC_60559_TYPES_EXT__
+#define FLT_EVAL_METHOD __FLT_EVAL_METHOD_TS_18661_3__
+#else
#define FLT_EVAL_METHOD __FLT_EVAL_METHOD__
+#endif
/* Number of decimal digits, n, such that any floating-point number in the
widest supported floating type with pmax radix b digits can be rounded
+2016-11-23 James Greenhalgh <james.greenhalgh@arm.com>
+
+ * gcc.dg/fpermitted-flt-eval-methods_3.c: New.
+ * gcc.dg/fpermitted-flt-eval-methods_4.c: Likewise.
+
2016-11-23 James Greenhalgh <james.greenhalgh@arm.com>
* gcc.dg/fpermitted-flt-eval-methods_1.c: New.
--- /dev/null
+/* { dg-do run } */
+/* { dg-options "-std=c11" } */
+
+/* Test that when compiling with -std=c11, we only see the C99/C11 values
+ for FLT_EVAL_METHOD. */
+
+#include <float.h>
+
+int main (int argc, char** argv)
+{
+ switch (FLT_EVAL_METHOD)
+ {
+ case 0:
+ case 1:
+ case 2:
+ case -1:
+ return 0;
+ default:
+ return 1;
+ }
+}
--- /dev/null
+/* { dg-do run } */
+/* { dg-options "-std=c11" } */
+
+/* Test that when compiling with -std=c11 and defining
+ __STDC_WANT_IEC_60559_TYPES_EXT__, we only see the ISO/IEC TS
+ 18661-3 values for FLT_EVAL_METHOD. */
+
+#define __STDC_WANT_IEC_60559_TYPES_EXT__
+
+#include <float.h>
+
+int main (int argc, char** argv)
+{
+ switch (__FLT_EVAL_METHOD__)
+ {
+ case 0:
+ case 1:
+ case 2:
+ case 16:
+ case -1:
+ return 0;
+ default:
+ return 1;
+ }
+}
init_regs ();
}
-/* Initialize excess precision settings. */
+/* Initialize excess precision settings.
+
+ We have no need to modify anything here, just keep track of what the
+ user requested. We'll figure out any appropriate relaxations
+ later. */
+
static void
init_excess_precision (void)
{
- /* Adjust excess precision handling based on the target options. If
- the front end cannot handle it, flag_excess_precision_cmdline
- will already have been set accordingly in the post_options
- hook. */
gcc_assert (flag_excess_precision_cmdline != EXCESS_PRECISION_DEFAULT);
flag_excess_precision = flag_excess_precision_cmdline;
- if (flag_unsafe_math_optimizations)
- flag_excess_precision = EXCESS_PRECISION_FAST;
- if (flag_excess_precision == EXCESS_PRECISION_STANDARD)
- {
- int flt_eval_method = TARGET_FLT_EVAL_METHOD;
- switch (flt_eval_method)
- {
- case -1:
- case 0:
- /* Either the target acts unpredictably (-1) or has all the
- operations required not to have excess precision (0). */
- flag_excess_precision = EXCESS_PRECISION_FAST;
- break;
- case 1:
- case 2:
- /* In these cases, predictable excess precision makes
- sense. */
- break;
- default:
- /* Any other implementation-defined FLT_EVAL_METHOD values
- require the compiler to handle the associated excess
- precision rules in excess_precision_type. */
- gcc_unreachable ();
- }
- }
}
/* Initialize things that are both lang-dependent and target-dependent.
tree
excess_precision_type (tree type)
{
- if (flag_excess_precision != EXCESS_PRECISION_FAST)
+ /* The target can give two different responses to the question of
+ which excess precision mode it would like depending on whether we
+ are in -fexcess-precision=standard or -fexcess-precision=fast. */
+
+ enum excess_precision_type requested_type
+ = (flag_excess_precision == EXCESS_PRECISION_FAST
+ ? EXCESS_PRECISION_TYPE_FAST
+ : EXCESS_PRECISION_TYPE_STANDARD);
+
+ enum flt_eval_method target_flt_eval_method
+ = targetm.c.excess_precision (requested_type);
+
+ /* The target should not ask for unpredictable float evaluation (though
+ it might advertise that implicitly the evaluation is unpredictable,
+ but we don't care about that here, it will have been reported
+ elsewhere). If it does ask for unpredictable evaluation, we have
+ nothing to do here. */
+ gcc_assert (target_flt_eval_method != FLT_EVAL_METHOD_UNPREDICTABLE);
+
+ /* Nothing to do. The target has asked for all types we know about
+ to be computed with their native precision and range. */
+ if (target_flt_eval_method == FLT_EVAL_METHOD_PROMOTE_TO_FLOAT16)
+ return NULL_TREE;
+
+ /* The target will promote this type in a target-dependent way, so excess
+ precision ought to leave it alone. */
+ if (targetm.promoted_type (type) != NULL_TREE)
+ return NULL_TREE;
+
+ machine_mode float16_type_mode = (float16_type_node
+ ? TYPE_MODE (float16_type_node)
+ : VOIDmode);
+ machine_mode float_type_mode = TYPE_MODE (float_type_node);
+ machine_mode double_type_mode = TYPE_MODE (double_type_node);
+
+ switch (TREE_CODE (type))
{
- int flt_eval_method = TARGET_FLT_EVAL_METHOD;
- switch (TREE_CODE (type))
- {
- case REAL_TYPE:
- switch (flt_eval_method)
- {
- case 1:
- if (TYPE_MODE (type) == TYPE_MODE (float_type_node))
- return double_type_node;
- break;
- case 2:
- if (TYPE_MODE (type) == TYPE_MODE (float_type_node)
- || TYPE_MODE (type) == TYPE_MODE (double_type_node))
- return long_double_type_node;
- break;
- default:
- gcc_unreachable ();
- }
- break;
- case COMPLEX_TYPE:
- if (TREE_CODE (TREE_TYPE (type)) != REAL_TYPE)
- return NULL_TREE;
- switch (flt_eval_method)
- {
- case 1:
- if (TYPE_MODE (TREE_TYPE (type)) == TYPE_MODE (float_type_node))
- return complex_double_type_node;
- break;
- case 2:
- if (TYPE_MODE (TREE_TYPE (type)) == TYPE_MODE (float_type_node)
- || (TYPE_MODE (TREE_TYPE (type))
- == TYPE_MODE (double_type_node)))
- return complex_long_double_type_node;
- break;
- default:
- gcc_unreachable ();
- }
- break;
- default:
- break;
- }
+ case REAL_TYPE:
+ {
+ machine_mode type_mode = TYPE_MODE (type);
+ switch (target_flt_eval_method)
+ {
+ case FLT_EVAL_METHOD_PROMOTE_TO_FLOAT:
+ if (type_mode == float16_type_mode)
+ return float_type_node;
+ break;
+ case FLT_EVAL_METHOD_PROMOTE_TO_DOUBLE:
+ if (type_mode == float16_type_mode
+ || type_mode == float_type_mode)
+ return double_type_node;
+ break;
+ case FLT_EVAL_METHOD_PROMOTE_TO_LONG_DOUBLE:
+ if (type_mode == float16_type_mode
+ || type_mode == float_type_mode
+ || type_mode == double_type_mode)
+ return long_double_type_node;
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ break;
+ }
+ case COMPLEX_TYPE:
+ {
+ if (TREE_CODE (TREE_TYPE (type)) != REAL_TYPE)
+ return NULL_TREE;
+ machine_mode type_mode = TYPE_MODE (TREE_TYPE (type));
+ switch (target_flt_eval_method)
+ {
+ case FLT_EVAL_METHOD_PROMOTE_TO_FLOAT:
+ if (type_mode == float16_type_mode)
+ return complex_float_type_node;
+ break;
+ case FLT_EVAL_METHOD_PROMOTE_TO_DOUBLE:
+ if (type_mode == float16_type_mode
+ || type_mode == float_type_mode)
+ return complex_double_type_node;
+ break;
+ case FLT_EVAL_METHOD_PROMOTE_TO_LONG_DOUBLE:
+ if (type_mode == float16_type_mode
+ || type_mode == float_type_mode
+ || type_mode == double_type_mode)
+ return complex_long_double_type_node;
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ break;
+ }
+ default:
+ break;
}
+
return NULL_TREE;
}
\f