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