[1/8] Document signaling for min, max and ltgt operations
diff mbox series

Message ID 20190820091845.80750-2-iii@linux.ibm.com
State New
Headers show
Series
  • Use signaling FP comparison instructions
Related show

Commit Message

Ilya Leoshkevich Aug. 20, 2019, 9:18 a.m. UTC
Currently it's not clear whether or not min, max and ltgt should raise
floating point exceptions when dealing with qNaNs.

Right now a lot of code assumes that LTGT is signaling: in particular,
it's generated for ((x < y) || (x > y)), which is signaling. The
behavior of MIN/MAX is (intentionally?) left unspecified, according to
commit 64dd117734d0 ("Unconditionally use MAX_EXPR/MIN_EXPR for MAX/MIN
intrinsics").

So document the status quo.

gcc/ChangeLog:

2019-08-09  Ilya Leoshkevich  <iii@linux.ibm.com>

	PR target/91323
	* doc/generic.texi (LTGT_EXPR): Restore the original wording
	regarding floating point exceptions.
	(MIN_EXPR, MAX_EXPR): Document.
	* doc/md.texi (smin, smax): Add a clause regarding floating
	point exceptions.
	* doc/rtl.texi (smin, smax): Add a clause regarding floating
	point exceptions.
---
 gcc/doc/generic.texi | 16 +++++++++++++---
 gcc/doc/md.texi      |  3 ++-
 gcc/doc/rtl.texi     |  3 ++-
 3 files changed, 17 insertions(+), 5 deletions(-)

Comments

Segher Boessenkool Aug. 20, 2019, 3:04 p.m. UTC | #1
On Tue, Aug 20, 2019 at 11:18:38AM +0200, Ilya Leoshkevich wrote:
> Currently it's not clear whether or not min, max and ltgt should raise
> floating point exceptions when dealing with qNaNs.
> 
> Right now a lot of code assumes that LTGT is signaling: in particular,
> it's generated for ((x < y) || (x > y)), which is signaling. The
> behavior of MIN/MAX is (intentionally?) left unspecified, according to
> commit 64dd117734d0 ("Unconditionally use MAX_EXPR/MIN_EXPR for MAX/MIN
> intrinsics").

The < and > operators separately already can cause exceptions, unless
you use -fno-trapping-math, in which case you cannot have LTGT at all.


Segher
Ilya Leoshkevich Aug. 20, 2019, 3:19 p.m. UTC | #2
> Am 20.08.2019 um 17:04 schrieb Segher Boessenkool <segher@kernel.crashing.org>:
> 
> On Tue, Aug 20, 2019 at 11:18:38AM +0200, Ilya Leoshkevich wrote:
>> Currently it's not clear whether or not min, max and ltgt should raise
>> floating point exceptions when dealing with qNaNs.
>> 
>> Right now a lot of code assumes that LTGT is signaling: in particular,
>> it's generated for ((x < y) || (x > y)), which is signaling. The
>> behavior of MIN/MAX is (intentionally?) left unspecified, according to
>> commit 64dd117734d0 ("Unconditionally use MAX_EXPR/MIN_EXPR for MAX/MIN
>> intrinsics").
> 
> The < and > operators separately already can cause exceptions, unless
> you use -fno-trapping-math, in which case you cannot have LTGT at all.

Hmm, I've just tried compiling:

    int foo(float a, float b) { return ((a < b) || (a > b)); }

with -ftrapping-math and -fno-trapping-math with gcc 8.0.1, and got LTGT
on tree and rtl levels in both cases:

    232t.optimized: _1 = a_2(D) <> b_3(D);
    312r.final:        (if_then_else:SI (ltgt (reg:CCS 33 %cc)
Segher Boessenkool Aug. 20, 2019, 3:32 p.m. UTC | #3
On Tue, Aug 20, 2019 at 05:19:46PM +0200, Ilya Leoshkevich wrote:
> > Am 20.08.2019 um 17:04 schrieb Segher Boessenkool <segher@kernel.crashing.org>:
> > The < and > operators separately already can cause exceptions, unless
> > you use -fno-trapping-math, in which case you cannot have LTGT at all.
> 
> Hmm, I've just tried compiling:
> 
>     int foo(float a, float b) { return ((a < b) || (a > b)); }
> 
> with -ftrapping-math and -fno-trapping-math with gcc 8.0.1, and got LTGT
> on tree and rtl levels in both cases:
> 
>     232t.optimized: _1 = a_2(D) <> b_3(D);
>     312r.final:        (if_then_else:SI (ltgt (reg:CCS 33 %cc)

Hrm, it seems to need -ffinite-math-only, even?  Or just -ffast-math
of course.  (That'll teach me to post without testing for another year
or two, sorry!)


Segher
Segher Boessenkool Aug. 20, 2019, 3:50 p.m. UTC | #4
On Tue, Aug 20, 2019 at 11:18:38AM +0200, Ilya Leoshkevich wrote:
> Currently it's not clear whether or not min, max and ltgt should raise
> floating point exceptions when dealing with qNaNs.
> 
> Right now a lot of code assumes that LTGT is signaling: in particular,
> it's generated for ((x < y) || (x > y)), which is signaling. The
> behavior of MIN/MAX is (intentionally?) left unspecified, according to
> commit 64dd117734d0 ("Unconditionally use MAX_EXPR/MIN_EXPR for MAX/MIN
> intrinsics").

Btw, this is not the difference between LTGT and NE, which is exactly
the same difference as that between LT and UNLT: if NaNs are allowed,
the first is false for unordered, while the second is true.  If NaNs
are not allowed, only one of the two is generated.

(0    UNORD)
LT    UNLT
EQ    UNEQ
LE    UNLE
GT    UNGT
LTGT  NE
GE    UNGE
(ORD  1)

There is currently no way to say (in trees or gimple or rtl) whether
comparisons are signaling ("ordered", generate a trap on an unordered
result).  I am working on this, but :-)


Segher
Ilya Leoshkevich Aug. 20, 2019, 4:01 p.m. UTC | #5
> Am 20.08.2019 um 17:32 schrieb Segher Boessenkool <segher@kernel.crashing.org>:
> 
> On Tue, Aug 20, 2019 at 05:19:46PM +0200, Ilya Leoshkevich wrote:
>>> Am 20.08.2019 um 17:04 schrieb Segher Boessenkool <segher@kernel.crashing.org>:
>>> The < and > operators separately already can cause exceptions, unless
>>> you use -fno-trapping-math, in which case you cannot have LTGT at all.
>> 
>> Hmm, I've just tried compiling:
>> 
>>    int foo(float a, float b) { return ((a < b) || (a > b)); }
>> 
>> with -ftrapping-math and -fno-trapping-math with gcc 8.0.1, and got LTGT
>> on tree and rtl levels in both cases:
>> 
>>    232t.optimized: _1 = a_2(D) <> b_3(D);
>>    312r.final:        (if_then_else:SI (ltgt (reg:CCS 33 %cc)
> 
> Hrm, it seems to need -ffinite-math-only, even?  Or just -ffast-math
> of course.  (That'll teach me to post without testing for another year
> or two, sorry!)

Ah, that's correct, and I have even added s390 tests for this: with
-ffinite-math-only the generated code is more "straightforward".  I
should update the commit message, but this isn't affecting the manual
update itself, right?
Ilya Leoshkevich Aug. 20, 2019, 4:13 p.m. UTC | #6
> Am 20.08.2019 um 17:50 schrieb Segher Boessenkool <segher@kernel.crashing.org>:
> 
> On Tue, Aug 20, 2019 at 11:18:38AM +0200, Ilya Leoshkevich wrote:
>> Currently it's not clear whether or not min, max and ltgt should raise
>> floating point exceptions when dealing with qNaNs.
>> 
>> Right now a lot of code assumes that LTGT is signaling: in particular,
>> it's generated for ((x < y) || (x > y)), which is signaling. The
>> behavior of MIN/MAX is (intentionally?) left unspecified, according to
>> commit 64dd117734d0 ("Unconditionally use MAX_EXPR/MIN_EXPR for MAX/MIN
>> intrinsics").
> 
> Btw, this is not the difference between LTGT and NE, which is exactly
> the same difference as that between LT and UNLT: if NaNs are allowed,
> the first is false for unordered, while the second is true.  If NaNs
> are not allowed, only one of the two is generated.
> 
> (0    UNORD)
> LT    UNLT
> EQ    UNEQ
> LE    UNLE
> GT    UNGT
> LTGT  NE
> GE    UNGE
> (ORD  1)

This matches my understanding (modulo signaling). cThis also doesn't
contradict the proposed manual update, right?

> There is currently no way to say (in trees or gimple or rtl) whether
> comparisons are signaling ("ordered", generate a trap on an unordered
> result).  I am working on this, but :-)

Isn't there?  This whole series is based on the following assumption:
LT, LE, GT, GE are definitely signaling; LTGT is most likely signaling
as well; the rest are not signaling.  This is based on gccint 11.6.3:
Unary and Binary Expressions.
Segher Boessenkool Aug. 20, 2019, 5:13 p.m. UTC | #7
On Tue, Aug 20, 2019 at 06:13:00PM +0200, Ilya Leoshkevich wrote:
> > Am 20.08.2019 um 17:50 schrieb Segher Boessenkool <segher@kernel.crashing.org>:
> > There is currently no way to say (in trees or gimple or rtl) whether
> > comparisons are signaling ("ordered", generate a trap on an unordered
> > result).  I am working on this, but :-)
> 
> Isn't there?  This whole series is based on the following assumption:
> LT, LE, GT, GE are definitely signaling; LTGT is most likely signaling
> as well; the rest are not signaling.  This is based on gccint 11.6.3:
> Unary and Binary Expressions.

There is currently no way to implement, say, iseqsig.  And whether an
operation is signaling should be determined by the language frontend,
not separately by each backend!

(There should be a signaling and a non-signaling version of every float
comparison that can be unordered).


Segher
Ilya Leoshkevich Aug. 21, 2019, noon UTC | #8
> Am 20.08.2019 um 19:13 schrieb Segher Boessenkool <segher@kernel.crashing.org>:
> 
> On Tue, Aug 20, 2019 at 06:13:00PM +0200, Ilya Leoshkevich wrote:
>>> Am 20.08.2019 um 17:50 schrieb Segher Boessenkool <segher@kernel.crashing.org>:
>>> There is currently no way to say (in trees or gimple or rtl) whether
>>> comparisons are signaling ("ordered", generate a trap on an unordered
>>> result).  I am working on this, but :-)
>> 
>> Isn't there?  This whole series is based on the following assumption:
>> LT, LE, GT, GE are definitely signaling; LTGT is most likely signaling
>> as well; the rest are not signaling.  This is based on gccint 11.6.3:
>> Unary and Binary Expressions.
> 
> There is currently no way to implement, say, iseqsig.  And whether an
> operation is signaling should be determined by the language frontend,
> not separately by each backend!

Wouldn't expressing it as ((x <= y) && (x >= y)) work?

In any case, I'll need to check whether my patch series handles iseqsig
in at least remotely sane way..

> 
> (There should be a signaling and a non-signaling version of every float
> comparison that can be unordered).

I wholeheartedly agree.  I had to write quite a few ugly patterns to
work around the lack of e.g. non-signaling GT.
Segher Boessenkool Aug. 21, 2019, 12:24 p.m. UTC | #9
On Wed, Aug 21, 2019 at 02:00:10PM +0200, Ilya Leoshkevich wrote:
> > Am 20.08.2019 um 19:13 schrieb Segher Boessenkool <segher@kernel.crashing.org>:
> > On Tue, Aug 20, 2019 at 06:13:00PM +0200, Ilya Leoshkevich wrote:
> >>> Am 20.08.2019 um 17:50 schrieb Segher Boessenkool <segher@kernel.crashing.org>:
> >>> There is currently no way to say (in trees or gimple or rtl) whether
> >>> comparisons are signaling ("ordered", generate a trap on an unordered
> >>> result).  I am working on this, but :-)
> >> 
> >> Isn't there?  This whole series is based on the following assumption:
> >> LT, LE, GT, GE are definitely signaling; LTGT is most likely signaling
> >> as well; the rest are not signaling.  This is based on gccint 11.6.3:
> >> Unary and Binary Expressions.
> > 
> > There is currently no way to implement, say, iseqsig.  And whether an
> > operation is signaling should be determined by the language frontend,
> > not separately by each backend!
> 
> Wouldn't expressing it as ((x <= y) && (x >= y)) work?

That is optimised only partially (on gimple) (first a compare for <=,
then a branch if false, then return "==").

I meant it cannot be implemented directly as one RTL (or gimple) insn,
although it can be done as one machine insn on many architectures.

> > (There should be a signaling and a non-signaling version of every float
> > comparison that can be unordered).
> 
> I wholeheartedly agree.  I had to write quite a few ugly patterns to
> work around the lack of e.g. non-signaling GT.

Ooh, buy in, I like it.  Thanks for the encouragement :-)


Segher

Patch
diff mbox series

diff --git a/gcc/doc/generic.texi b/gcc/doc/generic.texi
index 8901d5f357e..d5ae20bd461 100644
--- a/gcc/doc/generic.texi
+++ b/gcc/doc/generic.texi
@@ -1331,6 +1331,8 @@  the byte offset of the field, but should not be used directly; call
 @tindex UNGE_EXPR
 @tindex UNEQ_EXPR
 @tindex LTGT_EXPR
+@tindex MIN_EXPR
+@tindex MAX_EXPR
 @tindex MODIFY_EXPR
 @tindex INIT_EXPR
 @tindex COMPOUND_EXPR
@@ -1602,13 +1604,21 @@  These operations take two floating point operands and determine whether
 the operands are unordered or are less than, less than or equal to,
 greater than, greater than or equal to, or equal respectively.  For
 example, @code{UNLT_EXPR} returns true if either operand is an IEEE
-NaN or the first operand is less than the second.  With the possible
-exception of @code{LTGT_EXPR}, all of these operations are guaranteed
-not to generate a floating point exception.  The result
+NaN or the first operand is less than the second.  Only @code{LTGT_EXPR}
+is expected to raise an invalid floating-point-operation trap when the
+outcome is unordered.  All other operations are guaranteed not to raise
+a floating point exception.  The result
 type of these expressions will always be of integral or boolean type.
 These operations return the result type's zero value for false,
 and the result type's one value for true.
 
+@item MIN_EXPR
+@itemx MAX_EXPR
+These nodes represent minimum and maximum operations.  When used with
+floating point, if both operands are zeros, or if either operand is
+@code{NaN}, then it is unspecified which of the two operands is returned
+as the result and whether or not a floating point exception is raised.
+
 @item MODIFY_EXPR
 These nodes represent assignment.  The left-hand side is the first
 operand; the right-hand side is the second operand.  The left-hand side
diff --git a/gcc/doc/md.texi b/gcc/doc/md.texi
index 7751984bf5f..74f8ec84974 100644
--- a/gcc/doc/md.texi
+++ b/gcc/doc/md.texi
@@ -5353,7 +5353,8 @@  in the rtl as
 @item @samp{smin@var{m}3}, @samp{smax@var{m}3}
 Signed minimum and maximum operations.  When used with floating point,
 if both operands are zeros, or if either operand is @code{NaN}, then
-it is unspecified which of the two operands is returned as the result.
+it is unspecified which of the two operands is returned as the result
+and whether or not a floating point exception is raised.
 
 @cindex @code{fmin@var{m}3} instruction pattern
 @cindex @code{fmax@var{m}3} instruction pattern
diff --git a/gcc/doc/rtl.texi b/gcc/doc/rtl.texi
index 0814b66a486..e0628da893d 100644
--- a/gcc/doc/rtl.texi
+++ b/gcc/doc/rtl.texi
@@ -2596,7 +2596,8 @@  Represents the smaller (for @code{smin}) or larger (for @code{smax}) of
 @var{x} and @var{y}, interpreted as signed values in mode @var{m}.
 When used with floating point, if both operands are zeros, or if either
 operand is @code{NaN}, then it is unspecified which of the two operands
-is returned as the result.
+is returned as the result and whether or not a floating point exception
+is raised.
 
 @findex umin
 @findex umax