On Tue, 28 Sep 2021, Roger Sayle wrote: > 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".
It's true you can treat the rules on what code transformations are permitted as orthogonal to which exceptions or sub-exceptions those are applied to. (I'm not sure exact what you are including under "flow control sensitivity".) And also that IEEE 754-2019 subclause 8.1 says that language standards should allow for alternate exception handling attributes to be associated with sets of exceptions or sub-exceptions (as well as being associated to particular blocks in the source code, as represented for C by the pragmas defined in TS 18661-5), which does tend to suggest such a model listing (sub-)exceptions separately for each rule on alternate exception handling. Note that "trapping conditions" is not a good way of expressing things; the right way is much closer to "alternate exception handling" as defined in IEEE 754-2019 (or -2008), even if some of the more permissive modes (e.g. allowing spurious exceptions to be raised) don't actually correspond to any kind of alternate exception handling described in IEEE 754. Trapping, in the sense of transferring control to a trap handler (typically a SIGFPE signal handler) is, at least at the level of APIs for user code, an obsolescent form of exception handling: it was described in IEEE 754-1985 but removed in IEEE 754-2008, replaced by alternate exception handling. Trapping and trap handlers are too machine-specific to form a good API for normal user code. Some architectures may invoke a trap handler some time later than the instruction that signaled the exception. Some may not support trapping on floating-point exceptions at all; support is optional on Arm and many processors don't implement it, trapping on floating-point exceptions isn't supported by RISC-V at all, for example. So I think we should avoid reference to traps, when talking about floating-point exceptions, as much as possible, in the GCC documentation, command-line option names, source code and development discussion, except in limited cases where the specific legacy mechanism described in IEEE 754-1985 is meant. That doesn't make much difference to permitted optimizations; some forms of alternate exception handling would place similar restrictions on permitted code transformations to those restrictions coming from 1985-style trapping. > 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. As per the above, these aren't kinds of traps, but exceptions or sub-exceptions. > 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 t2 does not generate an exception; == is compareQuietEqual not compareSignalingEqual. > 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.html I don't think these are a good starting point; the TS 18661-5 APIs are a more appropriate basis for possible C bindings to alternate exception handling as described in IEEE 754-2008 or -2019, as opposed to 1985-style trapping or anything not based on 754-2008 or newer. > 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 IEEE has no such name as FPE_INTOVF. -ftrapv is itself an obsolescent feature. Not because of any problems with its notion of trap, which is disjoint from that of floating-point exceptions (it's a synchronous call to abort or something equivalent), but because the implementation is problematic and an alias for certain sanitizer options is more maintainable. We should be moving to that alias rather than adding any more internal representation related to -ftrapv. > 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. That's item (g), "squareRoot if the operand is less than zero", in subclause 7.2 of IEEE 754-2019. TS 18661-5 gives it the name FE_INVALID_SQRT. So we don't need to invent our own name for it. > 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. Where languages want to use this granularity at all, I expect it will be for language bindings doing what IEEE 754-2019 specifies regarding alternate exception handling - including supporting lists of sub-exceptions as specified in IEEE 754-2019. That means the classification in IEEE 754-2019 (given names in TS 18661-5) is the appropriate starting point. That enumeration is sufficiently fine-grained it seems very unlikely it will be useful to subdivide it further. It might be relevant to add *one* further enumeration element for those "invalid" exceptions that do not correspond to any of the listed cases (for example, sin(Inf) doesn't seem to fall into any of them, and, as per TS 18661-5, "Sub-exceptions corresponding to defined macros occur as specified below, and not in other cases." so it shouldn't be added to one of them by the implementation). I don't think any enumeration element will need adding to distinguish exact and inexact underflow, simply because that's covered by one of the orthogonal pieces of data (what kind of alternate exception handling applies to the underflow exception - if we only care about flags, exact underflow is OK to ignore and OK to signal spuriously because it's not observable through flags; if we care about anything changing flow of control, or some other kinds of alternate exception handling such as abruptUnderflow, exact underflow does become of significance). > 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 Losing the inexact exception there is a bug, according to documented semantics of -ftrapping-math. It's a specific case of failing to disallow local transformations that might cause code to signal fewer exceptions than it would have before. Documented semantics apply equally to all exceptions. We might choose to change some defaults (possibly just for inexact, possibly more generally allowing by default transformations that lose exceptions, possibly disabling -ftrapping-math by default), or to change the details of what -ftrapping-math does. And we might choose, when fixing a bug about inexact, to decide that we need to change the defaults at that time. But any such change should be separate from internal classification of (existing) transformations in GCC regarding what restrictions they obey for what exceptions or sub-exceptions. -- Joseph S. Myers jos...@codesourcery.com