[Patch 6/17] Migrate excess precision logic to use TARGET_EXCESS_PRECISION

gcc/

	* 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__.

gcc/c-family/

	* 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__.

gcc/testsuite/

	* gcc.dg/fpermitted-flt-eval-methods_3.c: New.
	* gcc.dg/fpermitted-flt-eval-methods_4.c: Likewise.

From-SVN: r242776
This commit is contained in:
James Greenhalgh 2016-11-23 17:23:12 +00:00 committed by James Greenhalgh
parent 04f0fcf7bd
commit 56d8ffc170
12 changed files with 407 additions and 117 deletions

View File

@ -1,3 +1,13 @@
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.

View File

@ -1,3 +1,17 @@
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

View File

@ -7950,4 +7950,86 @@ cb_get_suggestion (cpp_reader *, const char *goal,
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"

View File

@ -1546,6 +1546,11 @@ extern int tm_attr_to_mask (tree);
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);

View File

@ -728,6 +728,31 @@ cpp_atomic_builtins (cpp_reader *pfile)
(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)
@ -770,16 +795,17 @@ 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)
@ -1045,9 +1071,22 @@ c_cpp_builtins (cpp_reader *pfile)
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__",
@ -1188,25 +1227,38 @@ c_cpp_builtins (cpp_reader *pfile)
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;
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 1:
excess_precision = mode == TYPE_MODE (float_type_node);
break;
default:
gcc_unreachable ();
}
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__"));

View File

@ -8987,15 +8987,14 @@ them to store all pertinent intermediate computations into variables.
@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

View File

@ -129,21 +129,73 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
#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

View File

@ -1,3 +1,8 @@
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.

View File

@ -0,0 +1,21 @@
/* { 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;
}
}

View File

@ -0,0 +1,25 @@
/* { 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;
}
}

View File

@ -1680,41 +1680,17 @@ backend_init (void)
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.

View File

@ -8855,50 +8855,99 @@ build_complex_type (tree component_type, bool named)
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;
}