Message ID | 01b201d7b1df$8eadd7e0$ac0987a0$@nextmovesoftware.com |
---|---|
State | New |
Headers |
Return-Path: <gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org> X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 0C0DC385840A for <patchwork@sourceware.org>; Sat, 25 Sep 2021 07:33:14 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from server.nextmovesoftware.com (server.nextmovesoftware.com [162.254.253.69]) by sourceware.org (Postfix) with ESMTPS id 7066D3858403 for <gcc-patches@gcc.gnu.org>; Sat, 25 Sep 2021 07:32:56 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 7066D3858403 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=nextmovesoftware.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=nextmovesoftware.com DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=nextmovesoftware.com; s=default; h=Content-Type:MIME-Version:Message-ID: Date:Subject:Cc:To:From:Sender:Reply-To:Content-Transfer-Encoding:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=kZmwBrMqRxy0pTl2MHj7huXl/MqGXMkoDXk+lRfTHxI=; b=hV5Ofx8fSqLqCZdJwWUy+Ambpk wtImBDaL8bdOD+3nLHcyHAoGGsDSpGjtK5rCfPDyZRpJJveWmXJi+wzSussTxPCp6pQtyEIirlPZw Ss2zXkoFrBHOtcWhsXkOd35TBloM/Qs+O8l0O6puBR6vdLgNErH/zRnipGbpYqGzzgEiG7/OET8sQ wUUuxd0vTUvqA3Z409zHlF4AbtggrGeM646+DlXS5acXGwMoW8NJtNlviJwvTZ+51Ray9QWQs6hMN LsY7WxOpIHwZE5zDrS1mrD497BkDEfJeu4htAsshPnQD1QU8sPvQW19EvDXhIPM1EKol3l+0eCFSk msEqA5hw==; Received: from host86-186-213-65.range86-186.btcentralplus.com ([86.186.213.65]:51784 helo=Dell) by server.nextmovesoftware.com with esmtpsa (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from <roger@nextmovesoftware.com>) id 1mU2B5-0000MV-OM; Sat, 25 Sep 2021 03:32:56 -0400 From: "Roger Sayle" <roger@nextmovesoftware.com> To: "'GCC Patches'" <gcc-patches@gcc.gnu.org> Subject: [PATCH] Make flag_trapping_math a non-binary Boolean. Date: Sat, 25 Sep 2021 08:32:54 +0100 Message-ID: <01b201d7b1df$8eadd7e0$ac0987a0$@nextmovesoftware.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="----=_NextPart_000_01B3_01D7B1E7.F0735150" X-Mailer: Microsoft Outlook 16.0 Thread-Index: Adex3sT+8TlYQu6/QuWKuSb7481SWg== Content-Language: en-gb X-AntiAbuse: This header was added to track abuse, please include it with any abuse report X-AntiAbuse: Primary Hostname - server.nextmovesoftware.com X-AntiAbuse: Original Domain - gcc.gnu.org X-AntiAbuse: Originator/Caller UID/GID - [47 12] / [47 12] X-AntiAbuse: Sender Address Domain - nextmovesoftware.com X-Get-Message-Sender-Via: server.nextmovesoftware.com: authenticated_id: roger@nextmovesoftware.com X-Authenticated-Sender: server.nextmovesoftware.com: roger@nextmovesoftware.com X-Source: X-Source-Args: X-Source-Dir: X-Spam-Status: No, score=-12.6 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list <gcc-patches.gcc.gnu.org> List-Unsubscribe: <https://gcc.gnu.org/mailman/options/gcc-patches>, <mailto:gcc-patches-request@gcc.gnu.org?subject=unsubscribe> List-Archive: <https://gcc.gnu.org/pipermail/gcc-patches/> List-Post: <mailto:gcc-patches@gcc.gnu.org> List-Help: <mailto:gcc-patches-request@gcc.gnu.org?subject=help> List-Subscribe: <https://gcc.gnu.org/mailman/listinfo/gcc-patches>, <mailto:gcc-patches-request@gcc.gnu.org?subject=subscribe> Cc: 'Eric Botcazou' <botcazou@adacore.com> Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" <gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org> |
Series |
Make flag_trapping_math a non-binary Boolean.
|
|
Commit Message
Roger Sayle
Sept. 25, 2021, 7:32 a.m. UTC
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. This instalment simply introduces the necessary infrastructure without yet changing any functionality. The test "if (flag_trapping_math)" will remain perfectly valid (but pessimistic). The goal is to allow time for out-of-tree front-ends (modula-2, rust, etc.) to update themselves, if required, and to confirm that this change doesn't introduce problems for LTO, or elsewhere. This patch has been tested on x86_64-pc-linux-gnu with "make bootstrap" and "make -k check", all languages including Ada, with no new failures. Ok for mainline? 2021-09-25 Roger Sayle <roger@nextmovesoftware.com> gcc/ChangeLog * flag-types.h (trapping_math_model): New enumeration (of bits) specifying possible floating-point (and integer) exceptions/traps. * common.opt (ftrapping-math): Specify UInteger and initialize to flag_trapping_math to TRAPPING_MATH_DEFAULT. * toplev.c (process_options): The option -fsignaling-nans should set flag_trapping_math to TRAPPING_MATH_DEFAULT. gcc/ada/ChangeLog * gcc-interface/misc.c (gnat_init_gcc_fp): Set flag_trapping_math to TRAPPING_MATH_DEFAULT (instead of 1) if S'Machine_Overflow. Roger --
Comments
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.)
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 <joseph@codesourcery.com> Sent: 27 September 2021 21:05 To: Roger Sayle <roger@nextmovesoftware.com> Cc: 'GCC Patches' <gcc-patches@gcc.gnu.org>; 'Eric Botcazou' <botcazou@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.)
On Tue, Sep 28, 2021 at 1:34 PM Roger Sayle <roger@nextmovesoftware.com> wrote: > > > 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] Just to throw in a comment without intending to interrupt the fruitful argument... I'd like to keep changes refined to the frontends / middle-ends until we sort out the bigger picture and have an approach that is usable in the actual implementation and also extensible, that is, it doesn't fall apart when considering the related problems Joseph mentioned (-frounding-math, FENV access). And only _then_ think of how to expose this best to the user with new user-visible options and tunables. Because those tend to stick around forever and so mistakes there are much more costly (and it's not that we don't have too many entangled knobs in the area of math semantics...) > 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 <joseph@codesourcery.com> > Sent: 27 September 2021 21:05 > To: Roger Sayle <roger@nextmovesoftware.com> > Cc: 'GCC Patches' <gcc-patches@gcc.gnu.org>; 'Eric Botcazou' > <botcazou@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 > joseph@codesourcery.com >
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.
diff --git a/gcc/ada/gcc-interface/misc.c b/gcc/ada/gcc-interface/misc.c index 96199bd..93cbc71 100644 --- a/gcc/ada/gcc-interface/misc.c +++ b/gcc/ada/gcc-interface/misc.c @@ -451,7 +451,7 @@ gnat_init_gcc_fp (void) /* Assume that FP operations can trap if S'Machine_Overflow is true, but don't override the user if not. */ if (Machine_Overflows_On_Target) - flag_trapping_math = 1; + flag_trapping_math = TRAPPING_MATH_DEFAULT; else if (!global_options_set.x_flag_trapping_math) flag_trapping_math = 0; } diff --git a/gcc/common.opt b/gcc/common.opt index b921f5e..314f9a7 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -2750,7 +2750,7 @@ generate them instead of using descriptors. ; (user-visible) trap. This is the case, for example, in nonstop ; IEEE 754 arithmetic. ftrapping-math -Common Var(flag_trapping_math) Init(1) Optimization SetByCombined +Common Var(flag_trapping_math) Init(TRAPPING_MATH_DEFAULT) Optimization SetByCombined UInteger Assume floating-point operations can trap. ftrapv diff --git a/gcc/flag-types.h b/gcc/flag-types.h index 5bd1f77..98b9ff0 100644 --- a/gcc/flag-types.h +++ b/gcc/flag-types.h @@ -481,6 +481,33 @@ enum openacc_privatization OPENACC_PRIVATIZATION_NOISY }; +/* Trapping math exception classes. */ +enum trapping_math_model +{ + TRAPPING_MATH_NONE = 0, + 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, + + TRAPPING_MATH_DEFAULT = (1UL << 16) - 1, + + TRAPPING_MATH_INEXACT = 1UL << 16, + TRAPPING_MATH_TRAPV = 1UL << 17 +}; + #endif #endif /* ! GCC_FLAG_TYPES_H */ diff --git a/gcc/toplev.c b/gcc/toplev.c index 14d1335..6cd71cc 100644 --- a/gcc/toplev.c +++ b/gcc/toplev.c @@ -1664,7 +1664,7 @@ process_options (void) /* The presence of IEEE signaling NaNs, implies all math can trap. */ if (flag_signaling_nans) - flag_trapping_math = 1; + flag_trapping_math = TRAPPING_MATH_DEFAULT; /* We cannot reassociate if we want traps or signed zeros. */ if (flag_associative_math && (flag_trapping_math || flag_signed_zeros))