Hi Joseph,
Firstly very many thanks for taking the time to respond, and especially for
mentioning
the discussion in PR 54192 (and Marc Glisse's -ffenv-access patches, but
they are a
little less relevant).  Indeed the starting point for this patch is Richard
Beiner's proposal
in comment #9 for that PR.  That you've partially misunderstood the goal of
this patch is
encouraging (if it was simple to understand/fix, there wouldn't be so many
open PRs).
Hopefully, I'm bringing some fresh thinking on how to solve/tackle these
long standing
issues.

Next, I'd like to state that your "five restrictions" ontology is an
excellent starting point,
but I'd like to argue that your proposed list of 5 is the wrong shape
(insufficiently refined).
Instead, I'd like to counter-propose that an improvement/refinement of the
Myers model,
is actually "3 primitive restrictions * N trapping conditions * 2 flow
control sensitivity".

For reference, here's your original list:
> [1] Disallowing code transformations that cause some code to raise more 
> exception flags than it would have before.
> [2] Disallowing code transformations that cause some code to raise fewer 
> exception flags than it would have before.
> [3] Ensuring the code generated allows for possible non-local control flow

> from exception traps raised by floating-point operations (this is the part

> where -fnon-call-exceptions might be relevant).
> [4] Disallowing code transformations that might affect whether an exact 
> underflow exception occurs in some code (not observable through exception 
> flags, is observable through trap handlers).
> [5] Ensuring floating-point operations that might raise exception flags
are 
> not removed, or moved past code (asms or function calls) that might read 
> or modify the exception flag state

Firstly your item [3], concerns the relationship between traps and flow
control, such as C++ exception handling, which is as you correctly point out
the role of "-fnon-call-exceptions", which Richard B has recently confirmed
only applies to targets/languages supporting C++ style exceptions, i.e. this
is controlled by -fexceptions.  On targets such as nvptx-none, that don't
support non-local control flow, stack unwinding nor setjmp/longjmp, i.e.
don't support exceptions, this is completely orthogonal to the others.

Next your item [4] highlights what I consider the underlying problem that
until now has been overlooked, that there are different kinds of traps are
observationally/behaviourally different.  Above you describe, "underflow",
but likewise there are traps for inexact result, "2.0 / 3.0", traps for
division
by 0.0, that invokes undefined behaviour in C++ (but sometimes not in C),
and distinctions between quiet and signaling NaNs.  Your primitivie
restrictions,
[1], [2] and [5] may apply differently to these different kinds of
exceptions.

More relevant than Marc Glisse's -fenv-access is actually my
-fpreserve-traps
patch from July:
https://gcc.gnu.org/pipermail/gcc-patches/2021-July/574885.html
which tackles restriction [5] (and perhaps [2]).

Working towards the Myers restriction model, I believe we'd be a significant
step
closer with three (command line) flags (or families of flags):

-ftrapping-math         related to Myers restrictons [1],[2],[5]
-fpreserve-traps        related to Myers restriction [5]
-fcounted-traps         related to Myers restriction [2]

The insight that untangles the Gordian knot, is that these three options are

not simple true/false Binary flags, but actually (bit) sets of exception
types
(hopefully all actually using the same TRAPPING_MATH enumeration).

Consider the following four lines of C++:
constexpr t1 = 2.0 / 3.0;
constexpr t2 = std::numeric_limits<double>::quiet_NaN() == 0.0;
constexpr t2 = std::numeric_limits<double>::quiet_NaN() < 0.0;
constexpr t3 = 1.0 / 0.0;
which by IEEE generate four different types of exception, but as you've
expertly
confirmed have (sometimes) different behaviours under the C++ standard.
Treating all trapping conditions identically is clearly insufficient.

Hopefully, the argument/proposal above is sufficient to convince the list
that
we need some form of enumeration (following Richard Beiner's proposal).
Perhaps the devil is in the details, as to what the final form of this
enumeration
should look like [even though at this stage there are no functional changes
yet].

Two very useful references I've been following are:
https://docs.oracle.com/cd/E19957-01/806-3568/ncg_handle.html
https://docs.oracle.com/cd/E88353_01/html/E37846/fex-getexcepthandler-3m.htm
l

Ultimately, the fields and naming of this enumeration are a middle-end
detail,
and reflect constant folding transformations that the middle-end may or may
not perform on either trees or RTL.  In theory, the could be named after
line
numbers in match.pd, fold-const.c and simplify-rtx.c.  For example, what
IEEE calls
"FPE_INTOVF" is more commonly known as TRAPV inside GCC.  Likewise, IEEE
concepts such as FE_INVALID are really just groups of bits in our
enumeration,
but we allow much finer control, for example, whether sqrt of a negative
number
(other than -0.0) is considered a trap.

I'd prefer that GCC maintainers retain control/definition over the semantics
of
this enumeration rather the a standards committee.  It is for front-ends and
libraries to map from their semantics, to the handles provided by the
middle-end.

Finally, I'll correct you that the reason why TRAPPING_MATH_INEXACT is not
included in TRAPPING_MATH_DEFAULT is precisely to preserve the current 
behaviour.  The expressions 2.0/3.0 and (float)1.12345678 are both folded by
the middle-end and considered constexpr by g++; changing this would inhibit
optimization and affect what C++ considers valid code.  However, with full
-ffenv-access we may wish (elect) to perform these operations at run-time.

And with -fcounted-traps, i.e. (flag_counted_traps & TRAPPING_MATH_INEXACT)
        double x = 2.0/3.0;
        double y = 2.0/3.0;
we may even wish to perfom the division twice (c.f. your restriction [2]).


I hope this is useful/insightful.  Please let me know if you strongly feel
all FP traps
must be treated the same by the middle-end.  Indeed, if flag_trapping_math
is
restricted to only be FLAG_TRAPPING_DEFAULT in a front-end(s), they will be.

Best regards,
Roger
--

-----Original Message-----
From: Joseph Myers <jos...@codesourcery.com> 
Sent: 27 September 2021 21:05
To: Roger Sayle <ro...@nextmovesoftware.com>
Cc: 'GCC Patches' <gcc-patches@gcc.gnu.org>; 'Eric Botcazou'
<botca...@adacore.com>
Subject: Re: [PATCH] Make flag_trapping_math a non-binary Boolean.

On Sat, 25 Sep 2021, Roger Sayle wrote:

> Normally Boolean options/flags in GCC take the values zero or one.
> This patch tweaks flag_trapping_math to take the values 0 or 65535.
> More accurately it introduces a new trapping_math_model enumeration in 
> flag-types.h, and uses this to allow front-ends to (potentially) 
> control which expressions may be constant folded at compile-time by the
middle-end.
> Floating point/language experts may recognize these flags (bits) as 
> being modelled upon (extended) FENV_ACCESS.

I'm not sure exactly what those bits are modelled on (they are similar to,
but not exactly the same as, IEEE 754 sub-exceptions), but a lot more
explanation is needed - explanation of the rationale for the particular
model chosen, explanation for what is or is not considered a default there,
comments on each bit documenting its semantics in detail, and explanation of
how this relates to other relevant discussions and patch proposals.

I think the following are the three key things this should be related to,
none of which are mentioned in this patch submission:


(a) The various possible effects -ftrapping-math might have on allowed 
transformations, as discussed in bug 54192, where in comment #8 I 
identified five different kinds of restriction that -ftrapping-math might 
imply (only the first of which, and maybe to some extent the second, is 
handled much in GCC at present - and even there, there are various open 
bugs about cases where e.g. the expanders generate code not raising the 
right exceptions, or libgcc functions don't raise the right exceptions, 
especially when conversions between floating-point and integer are 
involved).

I actually think this sort of classification of effects of -ftrapping-math 
is probably more useful to provide control over (both internally in GCC 
and externally via more fine-grained command-line options) than the 
details of which individual exceptions are involved as suggested in your 
flags values.  However, the two are largely orthogonal (other than the 
point about exact underflows only relating to one of the exceptions).

I don't make any assertion here of which of these effects (if any) ought 
to be the default - making -ftrapping-math actually implement all five 
restrictions fully while keeping it the default might significantly impair 
optimization.  Also as mentioned in bug 54192, even if some such 
restrictions are applied by default, for soft-float systems with no 
support for exceptions it would make sense to apply more transformations 
unconditionally.


(b) Marc Glisse's -ffenv-access patches from August 2020 (and the 
discussion of them from that time).  Those don't claim to be complete, but 
they are the nearest we have to an attempt at implementing the sort of 
thing that would actually be needed to avoid code movement or removal that 
is invalid in the presence of code using floating-point flags (which 
overlaps a lot with what's needed to get -frounding-math correct under 
similar circumstances - except that a full -ftrapping-math might well 
involve stricter optimization restrictions than full -frounding-math, even 
in the absence of supporting non-local control float for trap handlers, 
because floating-point operations only read the rounding mode, but both 
read and write the exception state).


(c) The alternate exception handling bindings (FENV_EXCEPT pragma) in TS 
18661-5.  I'm not aware of any implementations of those bindings, it's far 
from clear whether they will turn out in the end to be a good way of 
providing C bindings to IEEE 754 alternate exception handling or not, and 
(given those issues) they aren't going to be integrated into C23.  But 
it's at least possible that the OPTIONAL_FLAG action (allowing 
transformations that cause certain exceptions or sub-exceptions not to 
raise the corresponding flag) could sometimes be useful in practice - and 
it's what seems to relate most closely to the sort of classification of 
exceptions in your patch (to implement it, you'd need that classification 
- though you'd also need to fix the other issues under (a) above).


> +  TRAPPING_MATH_QNANOP = 1UL << 0,
> +  TRAPPING_MATH_SNANOP = 1UL << 1,
> +  TRAPPING_MATH_QNANCMP = 1UL << 2,
> +  TRAPPING_MATH_SNANCMP = 1UL << 3,
> +  TRAPPING_MATH_INTCONV = 1UL << 4,
> +  TRAPPING_MATH_SQRTNEG = 1UL << 5,
> +  TRAPPING_MATH_LIBMFUN = 1UL << 6,
> +  TRAPPING_MATH_FDIVZERO = 1UL << 7,
> +  TRAPPING_MATH_IDIVZERO = 1UL << 8,
> +  TRAPPING_MATH_FPDENORM = 1UL << 9,
> +  TRAPPING_MATH_OVERFLOW = 1UL << 10,
> +  TRAPPING_MATH_UNDERFLOW = 1UL << 11,
> +  TRAPPING_MATH_INFDIVINF = 1UL << 12,
> +  TRAPPING_MATH_INFSUBINF = 1UL << 13,
> +  TRAPPING_MATH_INFMULZERO = 1UL << 14,
> +  TRAPPING_MATH_ZERODIVZERO = 1UL << 15,

Many of these are similar to, but not the same as, the sub-exceptions in 
IEEE 754 (enumerated as a more explicit list with names in TS 18661-5).

I think that if you want to handle sub-exceptions at all, it would be much 
better to follow the list in TS 18661-5 exactly (including using the names 
after the FE_ that appear in TS 18661-5, rather than inventing a different 
name for the same thing).  Maybe you then need an additional name to cover 
sub-exception cases not mentioned there (for lots of math.h functions, 
it's not exactly clear which sub-exception an invalid input corresponds 
to), but the TS 18661-5 list would be a good starting point.  (The only 
case I know of hardware that tracks sub-exception information, powerpc, 
has a more limited set of flag bits for sub-exceptions of "invalid".)

> +  TRAPPING_MATH_DEFAULT = (1UL << 16) - 1,
> +
> +  TRAPPING_MATH_INEXACT = 1UL << 16,

I think the existing semantics of -ftrapping-math apply to "inexact" just 
as they do to other exceptions (there may well be bugs there, of course), 
and it should be considered to be included in the default.  Any change to 
the default should be proposed separately from adding more fine-grained 
tracking of parts of -ftrapping-math.

> +  TRAPPING_MATH_TRAPV = 1UL << 17

I'm not sure what the semantics of this one are meant to be.  But since 
-ftrapv is more or less obsolescent, superseded by sanitizers, I don't 
think a new flag should really be named after it.  (And not named after 
the sanitizers either, unless you arrange for the sanitizers to generate 
IR using the new flag in some way.)

-- 
Joseph S. Myers
jos...@codesourcery.com

Reply via email to