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