RISC-V: Provide fmin'/fmax' RTL patterns

As at r2.2 of the RISC-V ISA specification[1] (equivalent to version 2.0
of the "F" and "D" standard architecture extensions for single-precision
and double-precision floating-point respectively) the FMIN and FMAX
machine instructions fully match our requirement for the `fminM3' and
`fmaxM3' standard RTL patterns:

"For FMIN and FMAX, if at least one input is a signaling NaN, or if both
inputs are quiet NaNs, the result is the canonical NaN.  If one operand
is a quiet NaN and the other is not a NaN, the result is the non-NaN
operand."

suitably for the IEEE 754-2008 `minNum' and `maxNum' operations.

However we only define `sminM3' and `smaxM3' standard RTL patterns to
produce the FMIN and FMAX machine instructions, which in turn causes the
`__builtin_fmin' and `__builtin_fmax' family of intrinsics to emit the
corresponding libcalls rather than the relevant machine instructions.
This is according to earlier revisions of the RISC-V ISA specification,
which we however do not support anymore, as from commit 4b81528241
("RISC-V: Support version controling for ISA standard extensions").

As from r20190608 of the RISC-V ISA specification (equivalent to version
2.2 of the "F" and "D" standard ISA extensions for single-precision and
double-precision floating-point respectively) the definition of the FMIN
and FMAX machine instructions has been updated[2]:

"Defined the signed-zero behavior of FMIN.fmt and FMAX.fmt, and changed
their behavior on signaling-NaN inputs to conform to the minimumNumber
and maximumNumber operations in the proposed IEEE 754-201x
specification."

and specifically[3]:

"Floating-point minimum-number and maximum-number instructions FMIN.S
and FMAX.S write, respectively, the smaller or larger of rs1 and rs2 to
rd.  For the purposes of these instructions only, the value -0.0 is
considered to be less than the value +0.0.  If both inputs are NaNs, the
result is the canonical NaN.  If only one operand is a NaN, the result
is the non-NaN operand.  Signaling NaN inputs set the invalid operation
exception flag, even when the result is not NaN."

Consequently for forwards compatibility with r20190608+ hardware we
cannot use the FMIN and FMAX machine instructions unconditionally even
where the ISA level of r2.2 has been specified with the `-misa-spec=2.2'
option where operation would be different between ISA revisions, that
is the handling of signaling NaN inputs.

Therefore provide new `fmin<mode>3' and `fmax<mode>3' patterns removing
the need to emit libcalls with the `__builtin_fmin' and `__builtin_fmax'
family of intrinsics, however limit them to where `-fno-signaling-nans'
is in effect, deferring to other code generation strategies otherwise as
applicable.  Use newly-defined UNSPECs as the operation codes so that
the patterns are only ever used if referred to by their names, as there
is no RTL expression defined for the IEEE 754-2008 `minNum' and `maxNum'
operations.

References:

[1] "The RISC-V Instruction Set Manual, Volume I: User-Level ISA",
    Document Version 2.2, May 7, 2017, Section 8.3 "NaN Generation and
    Propagation", p. 48

[1] "The RISC-V Instruction Set Manual, Volume I: Unprivileged ISA",
    Document Version 20190608-Base-Ratified, June 8, 2019, "Preface",
    p. ii

[2] same, Section 11.6 "Single-Precision Floating-Point Computational
    Instructions", p. 66

	gcc/
	* config/riscv/riscv.md (UNSPEC_FMIN, UNSPEC_FMAX): New
	constants.
	(fmin<mode>3, fmax<mode>3): New insns.

	gcc/testsuite/
	* gcc.target/riscv/fmax-snan.c: New test.
	* gcc.target/riscv/fmax.c: New test.
	* gcc.target/riscv/fmaxf-snan.c: New test.
	* gcc.target/riscv/fmaxf.c: New test.
	* gcc.target/riscv/fmin-snan.c: New test.
	* gcc.target/riscv/fmin.c: New test.
	* gcc.target/riscv/fminf-snan.c: New test.
	* gcc.target/riscv/fminf.c: New test.
	* gcc.target/riscv/smax-ieee.c: New test.
	* gcc.target/riscv/smax.c: New test.
	* gcc.target/riscv/smaxf-ieee.c: New test.
	* gcc.target/riscv/smaxf.c: New test.
	* gcc.target/riscv/smin-ieee.c: New test.
	* gcc.target/riscv/smin.c: New test.
	* gcc.target/riscv/sminf-ieee.c: New test.
	* gcc.target/riscv/sminf.c: New test.
This commit is contained in:
Maciej W. Rozycki 2022-05-10 15:02:31 +01:00
parent 9801ca737b
commit 9ddd44b586
17 changed files with 214 additions and 0 deletions

View File

@ -42,6 +42,8 @@
UNSPEC_COPYSIGN
UNSPEC_LRINT
UNSPEC_LROUND
UNSPEC_FMIN
UNSPEC_FMAX
;; Stack tie
UNSPEC_TIE
@ -1216,6 +1218,26 @@
;;
;; ....................
(define_insn "fmin<mode>3"
[(set (match_operand:ANYF 0 "register_operand" "=f")
(unspec:ANYF [(use (match_operand:ANYF 1 "register_operand" " f"))
(use (match_operand:ANYF 2 "register_operand" " f"))]
UNSPEC_FMIN))]
"TARGET_HARD_FLOAT && !HONOR_SNANS (<MODE>mode)"
"fmin.<fmt>\t%0,%1,%2"
[(set_attr "type" "fmove")
(set_attr "mode" "<UNITMODE>")])
(define_insn "fmax<mode>3"
[(set (match_operand:ANYF 0 "register_operand" "=f")
(unspec:ANYF [(use (match_operand:ANYF 1 "register_operand" " f"))
(use (match_operand:ANYF 2 "register_operand" " f"))]
UNSPEC_FMAX))]
"TARGET_HARD_FLOAT && !HONOR_SNANS (<MODE>mode)"
"fmax.<fmt>\t%0,%1,%2"
[(set_attr "type" "fmove")
(set_attr "mode" "<UNITMODE>")])
(define_insn "smin<mode>3"
[(set (match_operand:ANYF 0 "register_operand" "=f")
(smin:ANYF (match_operand:ANYF 1 "register_operand" " f")

View File

@ -0,0 +1,12 @@
/* { dg-do compile } */
/* { dg-options "-fno-finite-math-only -fsigned-zeros -fsignaling-nans -dp" } */
double
fmax (double x, double y)
{
return __builtin_fmax (x, y);
}
/* { dg-final { scan-assembler-not "\tfmax\\.d\t" } } */
/* { dg-final { scan-assembler-not "\tfge\\.d\t" } } */
/* { dg-final { scan-assembler "\t(call|tail)\tfmax\t" } } */

View File

@ -0,0 +1,12 @@
/* { dg-do compile } */
/* { dg-options "-fno-finite-math-only -fsigned-zeros -fno-signaling-nans -dp" } */
double
fmax (double x, double y)
{
return __builtin_fmax (x, y);
}
/* { dg-final { scan-assembler-not "\ttail\tfmax\t" } } */
/* { dg-final { scan-assembler-not "\tfge\\.d\t" } } */
/* { dg-final { scan-assembler "\tfmax\\.d\t\[^\n\]* fmaxdf3\n" } } */

View File

@ -0,0 +1,12 @@
/* { dg-do compile } */
/* { dg-options "-fno-finite-math-only -fsigned-zeros -fsignaling-nans -dp" } */
float
fmaxf (float x, float y)
{
return __builtin_fmaxf (x, y);
}
/* { dg-final { scan-assembler-not "\tfmax\\.s\t" } } */
/* { dg-final { scan-assembler-not "\tfge\\.s\t" } } */
/* { dg-final { scan-assembler "\t(call|tail)\tfmaxf\t" } } */

View File

@ -0,0 +1,12 @@
/* { dg-do compile } */
/* { dg-options "-fno-finite-math-only -fsigned-zeros -fno-signaling-nans -dp" } */
float
fmaxf (float x, float y)
{
return __builtin_fmaxf (x, y);
}
/* { dg-final { scan-assembler-not "\ttail\tfmaxf\t" } } */
/* { dg-final { scan-assembler-not "\tfge\\.s\t" } } */
/* { dg-final { scan-assembler "\tfmax\\.s\t\[^\n\]* fmaxsf3\n" } } */

View File

@ -0,0 +1,12 @@
/* { dg-do compile } */
/* { dg-options "-fno-finite-math-only -fsigned-zeros -fsignaling-nans -dp" } */
double
fmin (double x, double y)
{
return __builtin_fmin (x, y);
}
/* { dg-final { scan-assembler-not "\tfmin\\.d\t" } } */
/* { dg-final { scan-assembler-not "\tfle\\.d\t" } } */
/* { dg-final { scan-assembler "\t(call|tail)\tfmin\t" } } */

View File

@ -0,0 +1,12 @@
/* { dg-do compile } */
/* { dg-options "-fno-finite-math-only -fsigned-zeros -fno-signaling-nans -dp" } */
double
fmin (double x, double y)
{
return __builtin_fmin (x, y);
}
/* { dg-final { scan-assembler-not "\ttail\tfmin\t" } } */
/* { dg-final { scan-assembler-not "\tfle\\.d\t" } } */
/* { dg-final { scan-assembler "\tfmin\\.d\t\[^\n\]* fmindf3\n" } } */

View File

@ -0,0 +1,12 @@
/* { dg-do compile } */
/* { dg-options "-fno-finite-math-only -fsigned-zeros -fsignaling-nans -dp" } */
float
fminf (float x, float y)
{
return __builtin_fminf (x, y);
}
/* { dg-final { scan-assembler-not "\tfmin\\.s\t" } } */
/* { dg-final { scan-assembler-not "\tfle\\.s\t" } } */
/* { dg-final { scan-assembler "\t(call|tail)\tfminf\t" } } */

View File

@ -0,0 +1,12 @@
/* { dg-do compile } */
/* { dg-options "-fno-finite-math-only -fsigned-zeros -fno-signaling-nans -dp" } */
float
fminf (float x, float y)
{
return __builtin_fminf (x, y);
}
/* { dg-final { scan-assembler-not "\ttail\tfminf\t" } } */
/* { dg-final { scan-assembler-not "\tfle\\.s\t" } } */
/* { dg-final { scan-assembler "\tfmin\\.s\t\[^\n\]* fminsf3\n" } } */

View File

@ -0,0 +1,12 @@
/* { dg-do compile } */
/* { dg-options "-ffinite-math-only -fsigned-zeros -dp" } */
double
smax (double x, double y)
{
return x >= y ? x : y;
}
/* { dg-final { scan-assembler-not "\t(call|tail)\tfmax\t" } } */
/* { dg-final { scan-assembler-not "\tfmax\\.d\t" } } */
/* { dg-final { scan-assembler "\tfge\\.d\t" } } */

View File

@ -0,0 +1,12 @@
/* { dg-do compile } */
/* { dg-options "-ffinite-math-only -fno-signed-zeros -dp" } */
double
smax (double x, double y)
{
return x >= y ? x : y;
}
/* { dg-final { scan-assembler-not "\ttail\tfmax\t" } } */
/* { dg-final { scan-assembler-not "\tfge\\.d\t" } } */
/* { dg-final { scan-assembler "\tfmax\\.d\t\[^\n\]* smaxdf3\n" } } */

View File

@ -0,0 +1,12 @@
/* { dg-do compile } */
/* { dg-options "-ffinite-math-only -fsigned-zeros -dp" } */
float
smaxf (float x, float y)
{
return x >= y ? x : y;
}
/* { dg-final { scan-assembler-not "\t(call|tail)\tfmaxf\t" } } */
/* { dg-final { scan-assembler-not "\tfmax\\.s\t" } } */
/* { dg-final { scan-assembler "\tfge\\.s\t" } } */

View File

@ -0,0 +1,12 @@
/* { dg-do compile } */
/* { dg-options "-ffinite-math-only -fno-signed-zeros -dp" } */
float
smaxf (float x, float y)
{
return x >= y ? x : y;
}
/* { dg-final { scan-assembler-not "\ttail\tfmaxf\t" } } */
/* { dg-final { scan-assembler-not "\tfge\\.s\t" } } */
/* { dg-final { scan-assembler "\tfmax\\.s\t\[^\n\]* smaxsf3\n" } } */

View File

@ -0,0 +1,12 @@
/* { dg-do compile } */
/* { dg-options "-ffinite-math-only -fsigned-zeros -dp" } */
double
smin (double x, double y)
{
return x <= y ? x : y;
}
/* { dg-final { scan-assembler-not "\t(call|tail)\tfmin\t" } } */
/* { dg-final { scan-assembler-not "\tfmin\\.d\t" } } */
/* { dg-final { scan-assembler "\tfle\\.d\t" } } */

View File

@ -0,0 +1,12 @@
/* { dg-do compile } */
/* { dg-options "-ffinite-math-only -fno-signed-zeros -dp" } */
double
smin (double x, double y)
{
return x <= y ? x : y;
}
/* { dg-final { scan-assembler-not "\ttail\tfmin\t" } } */
/* { dg-final { scan-assembler-not "\tfle\\.d\t" } } */
/* { dg-final { scan-assembler "\tfmin\\.d\t\[^\n\]* smindf3\n" } } */

View File

@ -0,0 +1,12 @@
/* { dg-do compile } */
/* { dg-options "-ffinite-math-only -fsigned-zeros -dp" } */
float
sminf (float x, float y)
{
return x <= y ? x : y;
}
/* { dg-final { scan-assembler-not "\t(call|tail)\tfminf\t" } } */
/* { dg-final { scan-assembler-not "\tfmin\\.s\t" } } */
/* { dg-final { scan-assembler "\tfle\\.s\t" } } */

View File

@ -0,0 +1,12 @@
/* { dg-do compile } */
/* { dg-options "-ffinite-math-only -fno-signed-zeros -dp" } */
float
sminf (float x, float y)
{
return x <= y ? x : y;
}
/* { dg-final { scan-assembler-not "\ttail\tfminf\t" } } */
/* { dg-final { scan-assembler-not "\tfle\\.s\t" } } */
/* { dg-final { scan-assembler "\tfmin\\.s\t\[^\n\]* sminsf3\n" } } */