public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] c++: Disable -frounding-math during manifestly constant evaluation [PR96862]
@ 2020-09-01  7:49 Jakub Jelinek
  2020-09-01 10:13 ` Marc Glisse
  0 siblings, 1 reply; 6+ messages in thread
From: Jakub Jelinek @ 2020-09-01  7:49 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches

Hi!

As discussed in the PR, fold-const.c punts on floating point constant
evaluation if the result is inexact and -frounding-math is turned on.
      /* Don't constant fold this floating point operation if the
         result may dependent upon the run-time rounding mode and
         flag_rounding_math is set, or if GCC's software emulation
         is unable to accurately represent the result.  */
      if ((flag_rounding_math
           || (MODE_COMPOSITE_P (mode) && !flag_unsafe_math_optimizations))
          && (inexact || !real_identical (&result, &value)))
        return NULL_TREE;
Jonathan said that we should be evaluating them anyway, e.g. conceptually
as if they are done with the default rounding mode before user had a chance
to change that, and e.g. in C in initializers it is also ignored.
In fact, fold-const.c for C initializers turns off various other options:

/* Perform constant folding and related simplification of initializer
   expression EXPR.  These behave identically to "fold_buildN" but ignore
   potential run-time traps and exceptions that fold must preserve.  */

#define START_FOLD_INIT \
  int saved_signaling_nans = flag_signaling_nans;\
  int saved_trapping_math = flag_trapping_math;\
  int saved_rounding_math = flag_rounding_math;\
  int saved_trapv = flag_trapv;\
  int saved_folding_initializer = folding_initializer;\
  flag_signaling_nans = 0;\
  flag_trapping_math = 0;\
  flag_rounding_math = 0;\
  flag_trapv = 0;\
  folding_initializer = 1;

#define END_FOLD_INIT \
  flag_signaling_nans = saved_signaling_nans;\
  flag_trapping_math = saved_trapping_math;\
  flag_rounding_math = saved_rounding_math;\
  flag_trapv = saved_trapv;\
  folding_initializer = saved_folding_initializer;

So, shall cxx_eval_outermost_constant_expr instead turn off all those
options (then warning_sentinel wouldn't be the right thing to use, but given
the 8 or how many return stmts in cxx_eval_outermost_constant_expr, we'd
need a RAII class for this.  Not sure about the folding_initializer, that
one is affecting complex multiplication and division constant evaluation
somehow.

The following patch has been bootstrapped/regtested on x86_64-linux and
i686-linux, but see above, maybe we want something else.

2020-09-01  Jakub Jelinek  <jakub@redhat.com>

	PR c++/96862
	* constexpr.c (cxx_eval_outermost_constant_expr): Temporarily disable
	flag_rounding_math during manifestly constant evaluation.

	* g++.dg/cpp1z/constexpr-96862.C: New test.

--- gcc/cp/constexpr.c.jj	2020-08-31 14:10:15.826921458 +0200
+++ gcc/cp/constexpr.c	2020-08-31 15:41:26.429964532 +0200
@@ -6680,6 +6680,8 @@ cxx_eval_outermost_constant_expr (tree t
 			allow_non_constant, strict,
 			manifestly_const_eval || !allow_non_constant };
 
+  /* Turn off -frounding-math for manifestly constant evaluation.  */
+  warning_sentinel rm (flag_rounding_math, ctx.manifestly_const_eval);
   tree type = initialized_type (t);
   tree r = t;
   bool is_consteval = false;
--- gcc/testsuite/g++.dg/cpp1z/constexpr-96862.C.jj	2020-08-31 15:50:07.847473028 +0200
+++ gcc/testsuite/g++.dg/cpp1z/constexpr-96862.C	2020-08-31 15:49:40.829861168 +0200
@@ -0,0 +1,20 @@
+// PR c++/96862
+// { dg-do compile { target c++17 } }
+// { dg-additional-options "-frounding-math" }
+
+constexpr double a = 0x1.0p+100 + 0x1.0p-100;
+const double b = 0x1.0p+100 + 0x1.0p-100;
+const double &&c = 0x1.0p+100 + 0x1.0p-100;
+static_assert (0x1.0p+100 + 0x1.0p-100 == 0x1.0p+100, "");
+
+void
+foo ()
+{
+  constexpr double d = 0x1.0p+100 + 0x1.0p-100;
+  const double e = 0x1.0p+100 + 0x1.0p-100;
+  const double &&f = 0x1.0p+100 + 0x1.0p-100;
+  static_assert (0x1.0p+100 + 0x1.0p-100 == 0x1.0p+100, "");
+}
+
+const double &g = a;
+const double &h = b;

	Jakub


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] c++: Disable -frounding-math during manifestly constant evaluation [PR96862]
  2020-09-01  7:49 [PATCH] c++: Disable -frounding-math during manifestly constant evaluation [PR96862] Jakub Jelinek
@ 2020-09-01 10:13 ` Marc Glisse
  2020-09-02 20:55   ` Jason Merrill
  0 siblings, 1 reply; 6+ messages in thread
From: Marc Glisse @ 2020-09-01 10:13 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Jason Merrill, gcc-patches

On Tue, 1 Sep 2020, Jakub Jelinek via Gcc-patches wrote:

> As discussed in the PR, fold-const.c punts on floating point constant
> evaluation if the result is inexact and -frounding-math is turned on.
>      /* Don't constant fold this floating point operation if the
>         result may dependent upon the run-time rounding mode and
>         flag_rounding_math is set, or if GCC's software emulation
>         is unable to accurately represent the result.  */
>      if ((flag_rounding_math
>           || (MODE_COMPOSITE_P (mode) && !flag_unsafe_math_optimizations))
>          && (inexact || !real_identical (&result, &value)))
>        return NULL_TREE;
> Jonathan said that we should be evaluating them anyway, e.g. conceptually
> as if they are done with the default rounding mode before user had a chance
> to change that, and e.g. in C in initializers it is also ignored.
> In fact, fold-const.c for C initializers turns off various other options:
>
> /* Perform constant folding and related simplification of initializer
>   expression EXPR.  These behave identically to "fold_buildN" but ignore
>   potential run-time traps and exceptions that fold must preserve.  */
>
> #define START_FOLD_INIT \
>  int saved_signaling_nans = flag_signaling_nans;\
>  int saved_trapping_math = flag_trapping_math;\
>  int saved_rounding_math = flag_rounding_math;\
>  int saved_trapv = flag_trapv;\
>  int saved_folding_initializer = folding_initializer;\
>  flag_signaling_nans = 0;\
>  flag_trapping_math = 0;\
>  flag_rounding_math = 0;\
>  flag_trapv = 0;\
>  folding_initializer = 1;
>
> #define END_FOLD_INIT \
>  flag_signaling_nans = saved_signaling_nans;\
>  flag_trapping_math = saved_trapping_math;\
>  flag_rounding_math = saved_rounding_math;\
>  flag_trapv = saved_trapv;\
>  folding_initializer = saved_folding_initializer;
>
> So, shall cxx_eval_outermost_constant_expr instead turn off all those
> options (then warning_sentinel wouldn't be the right thing to use, but given
> the 8 or how many return stmts in cxx_eval_outermost_constant_expr, we'd
> need a RAII class for this.  Not sure about the folding_initializer, that
> one is affecting complex multiplication and division constant evaluation
> somehow.

I don't think we need to turn off flag_signaling_nans or flag_trapv. I 
think we want to turn off flag_trapping_math so we can fold 1./0 to inf 
(still in a context where folding is mandatory). Setting 
folding_initializer seems consistent with that, enabling infinite results 
in complex folding (it also forces folding of __builtin_constant_p, which 
may be redundant with force_folding_builtin_constant_p).

> The following patch has been bootstrapped/regtested on x86_64-linux and
> i686-linux, but see above, maybe we want something else.
>
> 2020-09-01  Jakub Jelinek  <jakub@redhat.com>
>
> 	PR c++/96862
> 	* constexpr.c (cxx_eval_outermost_constant_expr): Temporarily disable
> 	flag_rounding_math during manifestly constant evaluation.
>
> 	* g++.dg/cpp1z/constexpr-96862.C: New test.
>
> --- gcc/cp/constexpr.c.jj	2020-08-31 14:10:15.826921458 +0200
> +++ gcc/cp/constexpr.c	2020-08-31 15:41:26.429964532 +0200
> @@ -6680,6 +6680,8 @@ cxx_eval_outermost_constant_expr (tree t
> 			allow_non_constant, strict,
> 			manifestly_const_eval || !allow_non_constant };
>
> +  /* Turn off -frounding-math for manifestly constant evaluation.  */
> +  warning_sentinel rm (flag_rounding_math, ctx.manifestly_const_eval);
>   tree type = initialized_type (t);
>   tree r = t;
>   bool is_consteval = false;
> --- gcc/testsuite/g++.dg/cpp1z/constexpr-96862.C.jj	2020-08-31 15:50:07.847473028 +0200
> +++ gcc/testsuite/g++.dg/cpp1z/constexpr-96862.C	2020-08-31 15:49:40.829861168 +0200
> @@ -0,0 +1,20 @@
> +// PR c++/96862
> +// { dg-do compile { target c++17 } }
> +// { dg-additional-options "-frounding-math" }
> +
> +constexpr double a = 0x1.0p+100 + 0x1.0p-100;
> +const double b = 0x1.0p+100 + 0x1.0p-100;
> +const double &&c = 0x1.0p+100 + 0x1.0p-100;
> +static_assert (0x1.0p+100 + 0x1.0p-100 == 0x1.0p+100, "");
> +
> +void
> +foo ()
> +{
> +  constexpr double d = 0x1.0p+100 + 0x1.0p-100;
> +  const double e = 0x1.0p+100 + 0x1.0p-100;
> +  const double &&f = 0x1.0p+100 + 0x1.0p-100;
> +  static_assert (0x1.0p+100 + 0x1.0p-100 == 0x1.0p+100, "");
> +}
> +
> +const double &g = a;
> +const double &h = b;
>
> 	Jakub

-- 
Marc Glisse

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] c++: Disable -frounding-math during manifestly constant evaluation [PR96862]
  2020-09-01 10:13 ` Marc Glisse
@ 2020-09-02 20:55   ` Jason Merrill
  2020-09-02 22:43     ` Marc Glisse
  2020-09-03 15:03     ` Jakub Jelinek
  0 siblings, 2 replies; 6+ messages in thread
From: Jason Merrill @ 2020-09-02 20:55 UTC (permalink / raw)
  To: gcc-patches, Jakub Jelinek

On 9/1/20 6:13 AM, Marc Glisse wrote:
> On Tue, 1 Sep 2020, Jakub Jelinek via Gcc-patches wrote:
> 
>> As discussed in the PR, fold-const.c punts on floating point constant
>> evaluation if the result is inexact and -frounding-math is turned on.
>>      /* Don't constant fold this floating point operation if the
>>         result may dependent upon the run-time rounding mode and
>>         flag_rounding_math is set, or if GCC's software emulation
>>         is unable to accurately represent the result.  */
>>      if ((flag_rounding_math
>>           || (MODE_COMPOSITE_P (mode) && 
>> !flag_unsafe_math_optimizations))
>>          && (inexact || !real_identical (&result, &value)))
>>        return NULL_TREE;
>> Jonathan said that we should be evaluating them anyway, e.g. conceptually
>> as if they are done with the default rounding mode before user had a 
>> chance
>> to change that, and e.g. in C in initializers it is also ignored.
>> In fact, fold-const.c for C initializers turns off various other options:
>>
>> /* Perform constant folding and related simplification of initializer
>>   expression EXPR.  These behave identically to "fold_buildN" but ignore
>>   potential run-time traps and exceptions that fold must preserve.  */
>>
>> #define START_FOLD_INIT \
>>  int saved_signaling_nans = flag_signaling_nans;\
>>  int saved_trapping_math = flag_trapping_math;\
>>  int saved_rounding_math = flag_rounding_math;\
>>  int saved_trapv = flag_trapv;\
>>  int saved_folding_initializer = folding_initializer;\
>>  flag_signaling_nans = 0;\
>>  flag_trapping_math = 0;\
>>  flag_rounding_math = 0;\
>>  flag_trapv = 0;\
>>  folding_initializer = 1;
>>
>> #define END_FOLD_INIT \
>>  flag_signaling_nans = saved_signaling_nans;\
>>  flag_trapping_math = saved_trapping_math;\
>>  flag_rounding_math = saved_rounding_math;\
>>  flag_trapv = saved_trapv;\
>>  folding_initializer = saved_folding_initializer;
>>
>> So, shall cxx_eval_outermost_constant_expr instead turn off all those
>> options (then warning_sentinel wouldn't be the right thing to use, but 
>> given
>> the 8 or how many return stmts in cxx_eval_outermost_constant_expr, we'd
>> need a RAII class for this.  Not sure about the folding_initializer, that
>> one is affecting complex multiplication and division constant evaluation
>> somehow.
> 
> I don't think we need to turn off flag_signaling_nans or flag_trapv. I 
> think we want to turn off flag_trapping_math so we can fold 1./0 to inf 
> (still in a context where folding is mandatory). Setting 
> folding_initializer seems consistent with that, enabling infinite 
> results in complex folding (it also forces folding of 
> __builtin_constant_p, which may be redundant with 
> force_folding_builtin_constant_p).

C++ says that division by zero has undefined behavior, and that an 
expression with undefined behavior is not constant, so we shouldn't fold 
1./0 to inf anyway.  The same is true of other trapping operations.  So 
clearing flag_signaling_nans, flag_trapping_math, and flag_trapv seems 
wrong for C++.  And folding_initializer seems to be used for the same 
sort of thing.

>> The following patch has been bootstrapped/regtested on x86_64-linux and
>> i686-linux, but see above, maybe we want something else.
>>
>> 2020-09-01  Jakub Jelinek  <jakub@redhat.com>
>>
>>     PR c++/96862
>>     * constexpr.c (cxx_eval_outermost_constant_expr): Temporarily disable
>>     flag_rounding_math during manifestly constant evaluation.

OK.

>>     * g++.dg/cpp1z/constexpr-96862.C: New test.
>>
>> --- gcc/cp/constexpr.c.jj    2020-08-31 14:10:15.826921458 +0200
>> +++ gcc/cp/constexpr.c    2020-08-31 15:41:26.429964532 +0200
>> @@ -6680,6 +6680,8 @@ cxx_eval_outermost_constant_expr (tree t
>>             allow_non_constant, strict,
>>             manifestly_const_eval || !allow_non_constant };
>>
>> +  /* Turn off -frounding-math for manifestly constant evaluation.  */
>> +  warning_sentinel rm (flag_rounding_math, ctx.manifestly_const_eval);
>>   tree type = initialized_type (t);
>>   tree r = t;
>>   bool is_consteval = false;
>> --- gcc/testsuite/g++.dg/cpp1z/constexpr-96862.C.jj    2020-08-31 
>> 15:50:07.847473028 +0200
>> +++ gcc/testsuite/g++.dg/cpp1z/constexpr-96862.C    2020-08-31 
>> 15:49:40.829861168 +0200
>> @@ -0,0 +1,20 @@
>> +// PR c++/96862
>> +// { dg-do compile { target c++17 } }
>> +// { dg-additional-options "-frounding-math" }
>> +
>> +constexpr double a = 0x1.0p+100 + 0x1.0p-100;
>> +const double b = 0x1.0p+100 + 0x1.0p-100;
>> +const double &&c = 0x1.0p+100 + 0x1.0p-100;
>> +static_assert (0x1.0p+100 + 0x1.0p-100 == 0x1.0p+100, "");
>> +
>> +void
>> +foo ()
>> +{
>> +  constexpr double d = 0x1.0p+100 + 0x1.0p-100;
>> +  const double e = 0x1.0p+100 + 0x1.0p-100;
>> +  const double &&f = 0x1.0p+100 + 0x1.0p-100;
>> +  static_assert (0x1.0p+100 + 0x1.0p-100 == 0x1.0p+100, "");
>> +}
>> +
>> +const double &g = a;
>> +const double &h = b;
>>
>>     Jakub
> 


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] c++: Disable -frounding-math during manifestly constant evaluation [PR96862]
  2020-09-02 20:55   ` Jason Merrill
@ 2020-09-02 22:43     ` Marc Glisse
  2020-09-03 18:25       ` Jason Merrill
  2020-09-03 15:03     ` Jakub Jelinek
  1 sibling, 1 reply; 6+ messages in thread
From: Marc Glisse @ 2020-09-02 22:43 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches, Jakub Jelinek

On Wed, 2 Sep 2020, Jason Merrill via Gcc-patches wrote:

> On 9/1/20 6:13 AM, Marc Glisse wrote:
>> On Tue, 1 Sep 2020, Jakub Jelinek via Gcc-patches wrote:
>> 
>>> As discussed in the PR, fold-const.c punts on floating point constant
>>> evaluation if the result is inexact and -frounding-math is turned on.
>>>      /* Don't constant fold this floating point operation if the
>>>         result may dependent upon the run-time rounding mode and
>>>         flag_rounding_math is set, or if GCC's software emulation
>>>         is unable to accurately represent the result.  */
>>>      if ((flag_rounding_math
>>>           || (MODE_COMPOSITE_P (mode) && !flag_unsafe_math_optimizations))
>>>          && (inexact || !real_identical (&result, &value)))
>>>        return NULL_TREE;
>>> Jonathan said that we should be evaluating them anyway, e.g. conceptually
>>> as if they are done with the default rounding mode before user had a 
>>> chance
>>> to change that, and e.g. in C in initializers it is also ignored.
>>> In fact, fold-const.c for C initializers turns off various other options:
>>> 
>>> /* Perform constant folding and related simplification of initializer
>>>   expression EXPR.  These behave identically to "fold_buildN" but ignore
>>>   potential run-time traps and exceptions that fold must preserve.  */
>>> 
>>> #define START_FOLD_INIT \
>>>  int saved_signaling_nans = flag_signaling_nans;\
>>>  int saved_trapping_math = flag_trapping_math;\
>>>  int saved_rounding_math = flag_rounding_math;\
>>>  int saved_trapv = flag_trapv;\
>>>  int saved_folding_initializer = folding_initializer;\
>>>  flag_signaling_nans = 0;\
>>>  flag_trapping_math = 0;\
>>>  flag_rounding_math = 0;\
>>>  flag_trapv = 0;\
>>>  folding_initializer = 1;
>>> 
>>> #define END_FOLD_INIT \
>>>  flag_signaling_nans = saved_signaling_nans;\
>>>  flag_trapping_math = saved_trapping_math;\
>>>  flag_rounding_math = saved_rounding_math;\
>>>  flag_trapv = saved_trapv;\
>>>  folding_initializer = saved_folding_initializer;
>>> 
>>> So, shall cxx_eval_outermost_constant_expr instead turn off all those
>>> options (then warning_sentinel wouldn't be the right thing to use, but 
>>> given
>>> the 8 or how many return stmts in cxx_eval_outermost_constant_expr, we'd
>>> need a RAII class for this.  Not sure about the folding_initializer, that
>>> one is affecting complex multiplication and division constant evaluation
>>> somehow.
>> 
>> I don't think we need to turn off flag_signaling_nans or flag_trapv. I 
>> think we want to turn off flag_trapping_math so we can fold 1./0 to inf 
>> (still in a context where folding is mandatory). Setting 
>> folding_initializer seems consistent with that, enabling infinite results 
>> in complex folding (it also forces folding of __builtin_constant_p, which 
>> may be redundant with force_folding_builtin_constant_p).
>
> C++ says that division by zero has undefined behavior, and that an expression 
> with undefined behavior is not constant, so we shouldn't fold 1./0 to inf 
> anyway.  The same is true of other trapping operations.  So clearing 
> flag_signaling_nans, flag_trapping_math, and flag_trapv seems wrong for C++. 
> And folding_initializer seems to be used for the same sort of thing.

So we should actually force flag_trapping_math to true during constexpr
evaluation? And folding_initializer to false, and never mind trapv but
maybe disable wrapv?

#include <limits>
constexpr double a = std::numeric_limits<double>::infinity();
constexpr double b = a + a;
constexpr double c = a - a;
constexpr double d = 1. / a;
constexpr double e = 1. / d;

clang rejects c and e. MSVC rejects e. Intel warns on c.

Gcc rejects only e, and accepts the whole thing if I pass
-fno-trapping-math.

Almost any FP operation is possibly trapping, 1./3. sets FE_INEXACT just 
as 1./0. sets FE_DIVBYZERO. But the standard says

char array[1 + int(1 + 0.2 - 0.1 - 0.1)]; // Must be evaluated during translation

So it doesn't seem like it cares about that? Division by zero is the only 
one that gets weirdly special-cased...

-- 
Marc Glisse

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] c++: Disable -frounding-math during manifestly constant evaluation [PR96862]
  2020-09-02 20:55   ` Jason Merrill
  2020-09-02 22:43     ` Marc Glisse
@ 2020-09-03 15:03     ` Jakub Jelinek
  1 sibling, 0 replies; 6+ messages in thread
From: Jakub Jelinek @ 2020-09-03 15:03 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches

On Wed, Sep 02, 2020 at 04:55:14PM -0400, Jason Merrill via Gcc-patches wrote:
> C++ says that division by zero has undefined behavior, and that an
> expression with undefined behavior is not constant, so we shouldn't fold
> 1./0 to inf anyway.  The same is true of other trapping operations.  So
> clearing flag_signaling_nans, flag_trapping_math, and flag_trapv seems wrong
> for C++.  And folding_initializer seems to be used for the same sort of
> thing.

I indeed see:
"If during the evaluation of an expression, the result is not mathematically defined or not in the range of
representable values for its type, the behavior is undefined. [Note: Treatment of division by zero, forming a
remainder using a zero divisor, and all floating-point exceptions vary among machines, and is sometimes
adjustable by a library function. — end note]"

Now, for floating point exceptions, we have several of them.
I'd hope the inexact one shouldn't count, because otherwise we can't
evaluate at compile time pretty much nothing (but we actually seem to ignore
inexact during fold const except for rounding math).
The other exceptions are invalid, which is for the not mathematically
defined cases (0 / 0, inf - inf and the like), yes, they can be expressed as
NaNs, but I guess the above make it undefined.
Then there are overflows into infinities, should that count as not
representable?
Underflows are more like inexact I'd say.
So perhaps we should have a special flag that fold-const.c checks next to
flag_signalling_math and decide what is and is not undefined.

	Jakub


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] c++: Disable -frounding-math during manifestly constant evaluation [PR96862]
  2020-09-02 22:43     ` Marc Glisse
@ 2020-09-03 18:25       ` Jason Merrill
  0 siblings, 0 replies; 6+ messages in thread
From: Jason Merrill @ 2020-09-03 18:25 UTC (permalink / raw)
  To: gcc-patches; +Cc: Jakub Jelinek

On 9/2/20 6:43 PM, Marc Glisse wrote:
> On Wed, 2 Sep 2020, Jason Merrill via Gcc-patches wrote:
> 
>> On 9/1/20 6:13 AM, Marc Glisse wrote:
>>> On Tue, 1 Sep 2020, Jakub Jelinek via Gcc-patches wrote:
>>>
>>>> As discussed in the PR, fold-const.c punts on floating point constant
>>>> evaluation if the result is inexact and -frounding-math is turned on.
>>>>      /* Don't constant fold this floating point 
>>>> operation if the
>>>>         result may dependent upon the run-time 
>>>> rounding mode and
>>>>         flag_rounding_math is set, or if GCC's 
>>>> software emulation
>>>>         is unable to accurately represent the 
>>>> result.  */
>>>>      if ((flag_rounding_math
>>>>           || (MODE_COMPOSITE_P (mode) && 
>>>> !flag_unsafe_math_optimizations))
>>>>          && (inexact || !real_identical 
>>>> (&result, &value)))
>>>>        return NULL_TREE;
>>>> Jonathan said that we should be evaluating them anyway, e.g. 
>>>> conceptually
>>>> as if they are done with the default rounding mode before user had a 
>>>> chance
>>>> to change that, and e.g. in C in initializers it is also ignored.
>>>> In fact, fold-const.c for C initializers turns off various other 
>>>> options:
>>>>
>>>> /* Perform constant folding and related simplification of initializer
>>>>   expression EXPR.  These behave identically to "fold_buildN" 
>>>> but ignore
>>>>   potential run-time traps and exceptions that fold must 
>>>> preserve.  */
>>>>
>>>> #define START_FOLD_INIT \
>>>>  int saved_signaling_nans = flag_signaling_nans;\
>>>>  int saved_trapping_math = flag_trapping_math;\
>>>>  int saved_rounding_math = flag_rounding_math;\
>>>>  int saved_trapv = flag_trapv;\
>>>>  int saved_folding_initializer = folding_initializer;\
>>>>  flag_signaling_nans = 0;\
>>>>  flag_trapping_math = 0;\
>>>>  flag_rounding_math = 0;\
>>>>  flag_trapv = 0;\
>>>>  folding_initializer = 1;
>>>>
>>>> #define END_FOLD_INIT \
>>>>  flag_signaling_nans = saved_signaling_nans;\
>>>>  flag_trapping_math = saved_trapping_math;\
>>>>  flag_rounding_math = saved_rounding_math;\
>>>>  flag_trapv = saved_trapv;\
>>>>  folding_initializer = saved_folding_initializer;
>>>>
>>>> So, shall cxx_eval_outermost_constant_expr instead turn off all those
>>>> options (then warning_sentinel wouldn't be the right thing to use, 
>>>> but given
>>>> the 8 or how many return stmts in cxx_eval_outermost_constant_expr, 
>>>> we'd
>>>> need a RAII class for this.  Not sure about the 
>>>> folding_initializer, that
>>>> one is affecting complex multiplication and division constant 
>>>> evaluation
>>>> somehow.
>>>
>>> I don't think we need to turn off flag_signaling_nans or flag_trapv. 
>>> I think we want to turn off flag_trapping_math so we can fold 1./0 to 
>>> inf (still in a context where folding is mandatory). Setting 
>>> folding_initializer seems consistent with that, enabling infinite 
>>> results in complex folding (it also forces folding of 
>>> __builtin_constant_p, which may be redundant with 
>>> force_folding_builtin_constant_p).
>>
>> C++ says that division by zero has undefined behavior, and that an 
>> expression with undefined behavior is not constant, so we shouldn't 
>> fold 1./0 to inf anyway.  The same is true of other trapping 
>> operations.  So clearing flag_signaling_nans, flag_trapping_math, and 
>> flag_trapv seems wrong for C++. And folding_initializer seems to be 
>> used for the same sort of thing.
> 
> So we should actually force flag_trapping_math to true during constexpr
> evaluation? And folding_initializer to false, and never mind trapv but
> maybe disable wrapv?
> 
> #include <limits>
> constexpr double a = std::numeric_limits<double>::infinity();
> constexpr double b = a + a;
> constexpr double c = a - a;
> constexpr double d = 1. / a;
> constexpr double e = 1. / d;
> 
> clang rejects c and e. MSVC rejects e. Intel warns on c.
> 
> Gcc rejects only e, and accepts the whole thing if I pass
> -fno-trapping-math.

At the November 2016 C++ meeting the numerics study group (SG6) said 
about core issue 2168, "arithmetic operations (not conversions) that 
produce infinity are not allowed in a constant expression. Just using 
std::numeric_limits<T>::infinity() is ok, but you can't add 0 to it." 
But the issue is still open.

Some later discussion disputes that this is the right choice, but there 
were voices on both sides and the discussion didn't come to a conclusion.
https://lists.isocpp.org/core/2018/02/3974.php

It seems that clang used to reject expressions that produced infinities 
(as per SG6 above), but that was changed last year to treat the range of 
floating point types as including infinity, so that a floating point 
operation is never considered overflowing.
https://reviews.llvm.org/D63793

C11 says, "The minimum range of representable values for a floating type 
is the most negative finite floating-point number representable in that 
type through the most positive finite floating-point number 
representable in that type. In addition, if negative infinity is 
representable in a type, the range of that type is extended to all 
negative real numbers; likewise, if positive infinity is representable 
in a type, the range of that type is extended to all positive real numbers."

The clang change seems consistent with C11.  And given that C++ inherits 
<float.h> from C11, it would make sense to adopt the associated floating 
point model as well.

> Almost any FP operation is possibly trapping, 1./3. sets FE_INEXACT just 
> as 1./0. sets FE_DIVBYZERO. But the standard says
> 
> char array[1 + int(1 + 0.2 - 0.1 - 0.1)]; // Must be evaluated during 
> translation
> 
> So it doesn't seem like it cares about that? Division by zero is the 
> only one that gets weirdly special-cased...

Yes, division by zero is still explicitly undefined behavior ("If the 
second operand of / or % is zero the behavior is undefined.").  So I 
guess cxx_eval_binary_expression should check this before calling 
fold_binary_loc.

I suspect that the unfortunate thing here is that constexpr generally 
declared undefined behavior to mean non-constant, and we didn't notice 
that this use of "undefined behavior" needed to be changed to 
accommodate implementations that gave it well-defined behavior.

Hopefully CWG/SG6 discussion now will be more fruitful.

Jason


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2020-09-03 18:25 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-09-01  7:49 [PATCH] c++: Disable -frounding-math during manifestly constant evaluation [PR96862] Jakub Jelinek
2020-09-01 10:13 ` Marc Glisse
2020-09-02 20:55   ` Jason Merrill
2020-09-02 22:43     ` Marc Glisse
2020-09-03 18:25       ` Jason Merrill
2020-09-03 15:03     ` Jakub Jelinek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).