Message ID | alpine.DEB.2.20.2205101419240.11552@tpp.orcam.me.uk |
---|---|
State | New |
Headers | show |
Series | [RESEND,committed,v4] RISC-V: Provide `fmin'/`fmax' RTL patterns | expand |
Thanks Maciej! On Tue, May 10, 2022 at 10:05 PM Maciej W. Rozycki <macro@embecosm.com> wrote: > > 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 4b81528241ca > ("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. > --- > On Mon, 28 Feb 2022, Jim Wilson wrote: > > > On Tue, Feb 8, 2022 at 4:35 AM Maciej W. Rozycki <macro@embecosm.com> wrote: > > > > > gcc/ > > > * config/riscv/riscv.md (UNSPEC_FMIN, UNSPEC_FMAX): New > > > constants. > > > (fmin<mode>3, fmax<mode>3): New insns. > > > ... > > > > > > I tried testing on some of the hardware I have. Both the HiFive Unleashed > > (2018) and HiFive Unmatched (2021) implement the current definition of > > fmin/fmax. But the Allwinner Nezha (2021) implements the previous > > definition of fmin/fmax. SiFive was involved with the fmin/fmax change, so > > it isn't surprising that they implemented the new semantics before other > > companies. The Nezha board with the T-Head C906 is a popular one, so we do > > need to continue to support the 2017 spec, which your patch does with the > > HONORS_SNAN checks. I agree that we don't need to worry about spec > > versions older than that. > > > > This looks OK to me. > > > > I've got builds running in parallel on the Unleashed and Unmatched to test > > but that will take a couple of days and I don't expect any problems since > > you already tested it. I could do a build on the Nezha if I had to, but > > that would take at least a week as it is a much slower board and I'd rather > > not do that unless I have to. This is hardware implementing the older spec > > that you probably haven't tested though. > > Correct, I have only verified this change with an Unmatched system. > > Since Stage 1 is now back I have rerun regression testing with the change > included, both without and with `-fsignaling-nans' given. There were no > changes in results between the runs except for intermittent failures with > gfortran.dg/ISO_Fortran_binding_17.f90 at various optimisation levels, > clearly unrelated as this test does some DWARF information inspection. > > I have committed this change now, thank you for your review. > > Maciej > > Changes from v3: > > - Quote `\' characters in `scan-assembler' for literal `.' in regexp. > > - Mechanically update ChangeLog to have its entries separately for the > testsuite. > > Changes from v2: > > - Use UNSPECs for the `fmin'/`fmax' patterns, so that they cannot be > matched by the RTL expression. > > - Revert the `HONOR_SNANS (<MODE>mode)' restriction for the `smin'/`smax' > patterns; it was wrong, because HONOR_SNANS implies HONOR_NANS, and that > is not true for `-ffinite-math-only' where `smin'/`smax' still have to > work. > > - Add test cases. > > - Further clarify ISA/extension revisions/versions. > > Changes from v1: > > - Adjust heading from "RISC-V: Replace `smin'/`smax' RTL patterns with > `fmin'/`fmax'". > > - Do not remove `smin'/`smax' patterns; instead make them available if > `HONOR_SNANS (<MODE>mode)'. > > - Make `fmin'/`fmax' patterns available if `!HONOR_SNANS (<MODE>mode)' > only. > > - Update description accordingly; refer r2.2 and r20190608 ISA specs. > --- > gcc/config/riscv/riscv.md | 22 ++++++++++++++++++++++ > gcc/testsuite/gcc.target/riscv/fmax-snan.c | 12 ++++++++++++ > gcc/testsuite/gcc.target/riscv/fmax.c | 12 ++++++++++++ > gcc/testsuite/gcc.target/riscv/fmaxf-snan.c | 12 ++++++++++++ > gcc/testsuite/gcc.target/riscv/fmaxf.c | 12 ++++++++++++ > gcc/testsuite/gcc.target/riscv/fmin-snan.c | 12 ++++++++++++ > gcc/testsuite/gcc.target/riscv/fmin.c | 12 ++++++++++++ > gcc/testsuite/gcc.target/riscv/fminf-snan.c | 12 ++++++++++++ > gcc/testsuite/gcc.target/riscv/fminf.c | 12 ++++++++++++ > gcc/testsuite/gcc.target/riscv/smax-ieee.c | 12 ++++++++++++ > gcc/testsuite/gcc.target/riscv/smax.c | 12 ++++++++++++ > gcc/testsuite/gcc.target/riscv/smaxf-ieee.c | 12 ++++++++++++ > gcc/testsuite/gcc.target/riscv/smaxf.c | 12 ++++++++++++ > gcc/testsuite/gcc.target/riscv/smin-ieee.c | 12 ++++++++++++ > gcc/testsuite/gcc.target/riscv/smin.c | 12 ++++++++++++ > gcc/testsuite/gcc.target/riscv/sminf-ieee.c | 12 ++++++++++++ > gcc/testsuite/gcc.target/riscv/sminf.c | 12 ++++++++++++ > 17 files changed, 214 insertions(+) > > gcc-riscv-fmin-fmax.diff > Index: gcc/gcc/config/riscv/riscv.md > =================================================================== > --- gcc.orig/gcc/config/riscv/riscv.md > +++ gcc/gcc/config/riscv/riscv.md > @@ -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") > Index: gcc/gcc/testsuite/gcc.target/riscv/fmax-snan.c > =================================================================== > --- /dev/null > +++ gcc/gcc/testsuite/gcc.target/riscv/fmax-snan.c > @@ -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" } } */ > Index: gcc/gcc/testsuite/gcc.target/riscv/fmax.c > =================================================================== > --- /dev/null > +++ gcc/gcc/testsuite/gcc.target/riscv/fmax.c > @@ -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" } } */ > Index: gcc/gcc/testsuite/gcc.target/riscv/fmaxf-snan.c > =================================================================== > --- /dev/null > +++ gcc/gcc/testsuite/gcc.target/riscv/fmaxf-snan.c > @@ -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" } } */ > Index: gcc/gcc/testsuite/gcc.target/riscv/fmaxf.c > =================================================================== > --- /dev/null > +++ gcc/gcc/testsuite/gcc.target/riscv/fmaxf.c > @@ -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" } } */ > Index: gcc/gcc/testsuite/gcc.target/riscv/fmin-snan.c > =================================================================== > --- /dev/null > +++ gcc/gcc/testsuite/gcc.target/riscv/fmin-snan.c > @@ -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" } } */ > Index: gcc/gcc/testsuite/gcc.target/riscv/fmin.c > =================================================================== > --- /dev/null > +++ gcc/gcc/testsuite/gcc.target/riscv/fmin.c > @@ -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" } } */ > Index: gcc/gcc/testsuite/gcc.target/riscv/fminf-snan.c > =================================================================== > --- /dev/null > +++ gcc/gcc/testsuite/gcc.target/riscv/fminf-snan.c > @@ -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" } } */ > Index: gcc/gcc/testsuite/gcc.target/riscv/fminf.c > =================================================================== > --- /dev/null > +++ gcc/gcc/testsuite/gcc.target/riscv/fminf.c > @@ -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" } } */ > Index: gcc/gcc/testsuite/gcc.target/riscv/smax-ieee.c > =================================================================== > --- /dev/null > +++ gcc/gcc/testsuite/gcc.target/riscv/smax-ieee.c > @@ -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" } } */ > Index: gcc/gcc/testsuite/gcc.target/riscv/smax.c > =================================================================== > --- /dev/null > +++ gcc/gcc/testsuite/gcc.target/riscv/smax.c > @@ -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" } } */ > Index: gcc/gcc/testsuite/gcc.target/riscv/smaxf-ieee.c > =================================================================== > --- /dev/null > +++ gcc/gcc/testsuite/gcc.target/riscv/smaxf-ieee.c > @@ -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" } } */ > Index: gcc/gcc/testsuite/gcc.target/riscv/smaxf.c > =================================================================== > --- /dev/null > +++ gcc/gcc/testsuite/gcc.target/riscv/smaxf.c > @@ -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" } } */ > Index: gcc/gcc/testsuite/gcc.target/riscv/smin-ieee.c > =================================================================== > --- /dev/null > +++ gcc/gcc/testsuite/gcc.target/riscv/smin-ieee.c > @@ -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" } } */ > Index: gcc/gcc/testsuite/gcc.target/riscv/smin.c > =================================================================== > --- /dev/null > +++ gcc/gcc/testsuite/gcc.target/riscv/smin.c > @@ -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" } } */ > Index: gcc/gcc/testsuite/gcc.target/riscv/sminf-ieee.c > =================================================================== > --- /dev/null > +++ gcc/gcc/testsuite/gcc.target/riscv/sminf-ieee.c > @@ -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" } } */ > Index: gcc/gcc/testsuite/gcc.target/riscv/sminf.c > =================================================================== > --- /dev/null > +++ gcc/gcc/testsuite/gcc.target/riscv/sminf.c > @@ -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" } } */
On Tue, 10 May 2022 08:48:33 PDT (-0700), gcc-patches@gcc.gnu.org wrote: > Thanks Maciej! Yep. We should have a NEWS entry, though, as this one is user-visible and may be tricky to sort out if it turns out there is some HW lurking around that has the old behavior. > On Tue, May 10, 2022 at 10:05 PM Maciej W. Rozycki <macro@embecosm.com> wrote: >> >> 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 4b81528241ca >> ("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. >> --- >> On Mon, 28 Feb 2022, Jim Wilson wrote: >> >> > On Tue, Feb 8, 2022 at 4:35 AM Maciej W. Rozycki <macro@embecosm.com> wrote: >> > >> > > gcc/ >> > > * config/riscv/riscv.md (UNSPEC_FMIN, UNSPEC_FMAX): New >> > > constants. >> > > (fmin<mode>3, fmax<mode>3): New insns. >> > > ... >> > >> > >> > I tried testing on some of the hardware I have. Both the HiFive Unleashed >> > (2018) and HiFive Unmatched (2021) implement the current definition of >> > fmin/fmax. But the Allwinner Nezha (2021) implements the previous >> > definition of fmin/fmax. SiFive was involved with the fmin/fmax change, so >> > it isn't surprising that they implemented the new semantics before other >> > companies. The Nezha board with the T-Head C906 is a popular one, so we do >> > need to continue to support the 2017 spec, which your patch does with the >> > HONORS_SNAN checks. I agree that we don't need to worry about spec >> > versions older than that. >> > >> > This looks OK to me. >> > >> > I've got builds running in parallel on the Unleashed and Unmatched to test >> > but that will take a couple of days and I don't expect any problems since >> > you already tested it. I could do a build on the Nezha if I had to, but >> > that would take at least a week as it is a much slower board and I'd rather >> > not do that unless I have to. This is hardware implementing the older spec >> > that you probably haven't tested though. >> >> Correct, I have only verified this change with an Unmatched system. >> >> Since Stage 1 is now back I have rerun regression testing with the change >> included, both without and with `-fsignaling-nans' given. There were no >> changes in results between the runs except for intermittent failures with >> gfortran.dg/ISO_Fortran_binding_17.f90 at various optimisation levels, >> clearly unrelated as this test does some DWARF information inspection. >> >> I have committed this change now, thank you for your review. >> >> Maciej >> >> Changes from v3: >> >> - Quote `\' characters in `scan-assembler' for literal `.' in regexp. >> >> - Mechanically update ChangeLog to have its entries separately for the >> testsuite. >> >> Changes from v2: >> >> - Use UNSPECs for the `fmin'/`fmax' patterns, so that they cannot be >> matched by the RTL expression. >> >> - Revert the `HONOR_SNANS (<MODE>mode)' restriction for the `smin'/`smax' >> patterns; it was wrong, because HONOR_SNANS implies HONOR_NANS, and that >> is not true for `-ffinite-math-only' where `smin'/`smax' still have to >> work. >> >> - Add test cases. >> >> - Further clarify ISA/extension revisions/versions. >> >> Changes from v1: >> >> - Adjust heading from "RISC-V: Replace `smin'/`smax' RTL patterns with >> `fmin'/`fmax'". >> >> - Do not remove `smin'/`smax' patterns; instead make them available if >> `HONOR_SNANS (<MODE>mode)'. >> >> - Make `fmin'/`fmax' patterns available if `!HONOR_SNANS (<MODE>mode)' >> only. >> >> - Update description accordingly; refer r2.2 and r20190608 ISA specs. >> --- >> gcc/config/riscv/riscv.md | 22 ++++++++++++++++++++++ >> gcc/testsuite/gcc.target/riscv/fmax-snan.c | 12 ++++++++++++ >> gcc/testsuite/gcc.target/riscv/fmax.c | 12 ++++++++++++ >> gcc/testsuite/gcc.target/riscv/fmaxf-snan.c | 12 ++++++++++++ >> gcc/testsuite/gcc.target/riscv/fmaxf.c | 12 ++++++++++++ >> gcc/testsuite/gcc.target/riscv/fmin-snan.c | 12 ++++++++++++ >> gcc/testsuite/gcc.target/riscv/fmin.c | 12 ++++++++++++ >> gcc/testsuite/gcc.target/riscv/fminf-snan.c | 12 ++++++++++++ >> gcc/testsuite/gcc.target/riscv/fminf.c | 12 ++++++++++++ >> gcc/testsuite/gcc.target/riscv/smax-ieee.c | 12 ++++++++++++ >> gcc/testsuite/gcc.target/riscv/smax.c | 12 ++++++++++++ >> gcc/testsuite/gcc.target/riscv/smaxf-ieee.c | 12 ++++++++++++ >> gcc/testsuite/gcc.target/riscv/smaxf.c | 12 ++++++++++++ >> gcc/testsuite/gcc.target/riscv/smin-ieee.c | 12 ++++++++++++ >> gcc/testsuite/gcc.target/riscv/smin.c | 12 ++++++++++++ >> gcc/testsuite/gcc.target/riscv/sminf-ieee.c | 12 ++++++++++++ >> gcc/testsuite/gcc.target/riscv/sminf.c | 12 ++++++++++++ >> 17 files changed, 214 insertions(+) >> >> gcc-riscv-fmin-fmax.diff >> Index: gcc/gcc/config/riscv/riscv.md >> =================================================================== >> --- gcc.orig/gcc/config/riscv/riscv.md >> +++ gcc/gcc/config/riscv/riscv.md >> @@ -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") >> Index: gcc/gcc/testsuite/gcc.target/riscv/fmax-snan.c >> =================================================================== >> --- /dev/null >> +++ gcc/gcc/testsuite/gcc.target/riscv/fmax-snan.c >> @@ -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" } } */ >> Index: gcc/gcc/testsuite/gcc.target/riscv/fmax.c >> =================================================================== >> --- /dev/null >> +++ gcc/gcc/testsuite/gcc.target/riscv/fmax.c >> @@ -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" } } */ >> Index: gcc/gcc/testsuite/gcc.target/riscv/fmaxf-snan.c >> =================================================================== >> --- /dev/null >> +++ gcc/gcc/testsuite/gcc.target/riscv/fmaxf-snan.c >> @@ -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" } } */ >> Index: gcc/gcc/testsuite/gcc.target/riscv/fmaxf.c >> =================================================================== >> --- /dev/null >> +++ gcc/gcc/testsuite/gcc.target/riscv/fmaxf.c >> @@ -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" } } */ >> Index: gcc/gcc/testsuite/gcc.target/riscv/fmin-snan.c >> =================================================================== >> --- /dev/null >> +++ gcc/gcc/testsuite/gcc.target/riscv/fmin-snan.c >> @@ -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" } } */ >> Index: gcc/gcc/testsuite/gcc.target/riscv/fmin.c >> =================================================================== >> --- /dev/null >> +++ gcc/gcc/testsuite/gcc.target/riscv/fmin.c >> @@ -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" } } */ >> Index: gcc/gcc/testsuite/gcc.target/riscv/fminf-snan.c >> =================================================================== >> --- /dev/null >> +++ gcc/gcc/testsuite/gcc.target/riscv/fminf-snan.c >> @@ -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" } } */ >> Index: gcc/gcc/testsuite/gcc.target/riscv/fminf.c >> =================================================================== >> --- /dev/null >> +++ gcc/gcc/testsuite/gcc.target/riscv/fminf.c >> @@ -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" } } */ >> Index: gcc/gcc/testsuite/gcc.target/riscv/smax-ieee.c >> =================================================================== >> --- /dev/null >> +++ gcc/gcc/testsuite/gcc.target/riscv/smax-ieee.c >> @@ -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" } } */ >> Index: gcc/gcc/testsuite/gcc.target/riscv/smax.c >> =================================================================== >> --- /dev/null >> +++ gcc/gcc/testsuite/gcc.target/riscv/smax.c >> @@ -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" } } */ >> Index: gcc/gcc/testsuite/gcc.target/riscv/smaxf-ieee.c >> =================================================================== >> --- /dev/null >> +++ gcc/gcc/testsuite/gcc.target/riscv/smaxf-ieee.c >> @@ -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" } } */ >> Index: gcc/gcc/testsuite/gcc.target/riscv/smaxf.c >> =================================================================== >> --- /dev/null >> +++ gcc/gcc/testsuite/gcc.target/riscv/smaxf.c >> @@ -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" } } */ >> Index: gcc/gcc/testsuite/gcc.target/riscv/smin-ieee.c >> =================================================================== >> --- /dev/null >> +++ gcc/gcc/testsuite/gcc.target/riscv/smin-ieee.c >> @@ -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" } } */ >> Index: gcc/gcc/testsuite/gcc.target/riscv/smin.c >> =================================================================== >> --- /dev/null >> +++ gcc/gcc/testsuite/gcc.target/riscv/smin.c >> @@ -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" } } */ >> Index: gcc/gcc/testsuite/gcc.target/riscv/sminf-ieee.c >> =================================================================== >> --- /dev/null >> +++ gcc/gcc/testsuite/gcc.target/riscv/sminf-ieee.c >> @@ -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" } } */ >> Index: gcc/gcc/testsuite/gcc.target/riscv/sminf.c >> =================================================================== >> --- /dev/null >> +++ gcc/gcc/testsuite/gcc.target/riscv/sminf.c >> @@ -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" } } */
On Fri, 13 May 2022, Palmer Dabbelt wrote: > Yep. We should have a NEWS entry, though, as this one is user-visible and may > be tricky to sort out if it turns out there is some HW lurking around that has > the old behavior. Hmm, should we? We only support `-misa-spec=<2.2|20190608|20191213>' already and this update is fine for r.2.2+. If someone has pre-r.2.2 hw, then it's been already unsupported even before this change (as from GCC 11 AFAICS). Have I missed anything? Maciej
On Fri, 13 May 2022 12:57:35 PDT (-0700), macro@embecosm.com wrote: > On Fri, 13 May 2022, Palmer Dabbelt wrote: > >> Yep. We should have a NEWS entry, though, as this one is user-visible and may >> be tricky to sort out if it turns out there is some HW lurking around that has >> the old behavior. > > Hmm, should we? We only support `-misa-spec=<2.2|20190608|20191213>' > already and this update is fine for r.2.2+. If someone has pre-r.2.2 hw, > then it's been already unsupported even before this change (as from GCC 11 > AFAICS). Have I missed anything? I have no idea, but either you did or I did... IIUC this actually changed for version 2.2 of the F extension, which happened well after version 2.2 of the ISA manual. I see eb78171 ("F/D extensions to v2.2") both changing the version and noting the change, with cd20cee ("FMIN/FMAX now implement minimumNumber/maximumNumber, not minNum/maxNum") actually making the change and also adding some notation about this being a draft of version 2.3 of the ISA manual (which was presumably never released as 2.3 but instead one of those other tags). It also calls this out as F 2.0, but I'm assuming that's non-canonical because this is a draft. Complicating this is that there's some HW that predates that next spec but implements some of the post-2.2 behavior like this -- IIUC that's how the SiFive FE510-G000 (on the HiFive Unleashed) works and that's the only hardware any of us know about from that timeframe, but in practice there's a lot of HW floating around that we don't know of and it's going to be all but impossible to tell what ended up implemented in that two-year gap between releases. I do now remember having some discussions on this and things getting so complicated I gave up, not sure if I just care enough to chase it down this time or if it was something else -- it's first thing on Saturday morning so it could go either way :)
On Sat, 14 May 2022, Palmer Dabbelt wrote: > > Hmm, should we? We only support `-misa-spec=<2.2|20190608|20191213>' > > already and this update is fine for r.2.2+. If someone has pre-r.2.2 hw, > > then it's been already unsupported even before this change (as from GCC 11 > > AFAICS). Have I missed anything? > > I have no idea, but either you did or I did... > > IIUC this actually changed for version 2.2 of the F extension, which happened > well after version 2.2 of the ISA manual. I see eb78171 ("F/D extensions to > v2.2") both changing the version and noting the change, with cd20cee > ("FMIN/FMAX now implement minimumNumber/maximumNumber, not minNum/maxNum") > actually making the change and also adding some notation about this being a > draft of version 2.3 of the ISA manual (which was presumably never released as > 2.3 but instead one of those other tags). It also calls this out as F 2.0, > but I'm assuming that's non-canonical because this is a draft. So the only actual concern with F/D 2.0 vs F/D 2.2 is the change of the treatment of signalling NaNs, which is why the new `fmin'/`fmax' patterns are keyed with !HONOR_SNANS so that code produces the same results from ISA r.2.2 onwards. I am sorry if that got buried in the elaborate change description. Once we've got the generic bits available for minimumNumber/maximumNumber we can add suitable `*min'/`*max' patterns that implement these operations and key them with (riscv_isa_spec >= ISA_SPEC_CLASS_20190608). Does this explanation clear your concern? Maciej
Index: gcc/gcc/config/riscv/riscv.md =================================================================== --- gcc.orig/gcc/config/riscv/riscv.md +++ gcc/gcc/config/riscv/riscv.md @@ -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") Index: gcc/gcc/testsuite/gcc.target/riscv/fmax-snan.c =================================================================== --- /dev/null +++ gcc/gcc/testsuite/gcc.target/riscv/fmax-snan.c @@ -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" } } */ Index: gcc/gcc/testsuite/gcc.target/riscv/fmax.c =================================================================== --- /dev/null +++ gcc/gcc/testsuite/gcc.target/riscv/fmax.c @@ -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" } } */ Index: gcc/gcc/testsuite/gcc.target/riscv/fmaxf-snan.c =================================================================== --- /dev/null +++ gcc/gcc/testsuite/gcc.target/riscv/fmaxf-snan.c @@ -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" } } */ Index: gcc/gcc/testsuite/gcc.target/riscv/fmaxf.c =================================================================== --- /dev/null +++ gcc/gcc/testsuite/gcc.target/riscv/fmaxf.c @@ -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" } } */ Index: gcc/gcc/testsuite/gcc.target/riscv/fmin-snan.c =================================================================== --- /dev/null +++ gcc/gcc/testsuite/gcc.target/riscv/fmin-snan.c @@ -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" } } */ Index: gcc/gcc/testsuite/gcc.target/riscv/fmin.c =================================================================== --- /dev/null +++ gcc/gcc/testsuite/gcc.target/riscv/fmin.c @@ -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" } } */ Index: gcc/gcc/testsuite/gcc.target/riscv/fminf-snan.c =================================================================== --- /dev/null +++ gcc/gcc/testsuite/gcc.target/riscv/fminf-snan.c @@ -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" } } */ Index: gcc/gcc/testsuite/gcc.target/riscv/fminf.c =================================================================== --- /dev/null +++ gcc/gcc/testsuite/gcc.target/riscv/fminf.c @@ -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" } } */ Index: gcc/gcc/testsuite/gcc.target/riscv/smax-ieee.c =================================================================== --- /dev/null +++ gcc/gcc/testsuite/gcc.target/riscv/smax-ieee.c @@ -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" } } */ Index: gcc/gcc/testsuite/gcc.target/riscv/smax.c =================================================================== --- /dev/null +++ gcc/gcc/testsuite/gcc.target/riscv/smax.c @@ -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" } } */ Index: gcc/gcc/testsuite/gcc.target/riscv/smaxf-ieee.c =================================================================== --- /dev/null +++ gcc/gcc/testsuite/gcc.target/riscv/smaxf-ieee.c @@ -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" } } */ Index: gcc/gcc/testsuite/gcc.target/riscv/smaxf.c =================================================================== --- /dev/null +++ gcc/gcc/testsuite/gcc.target/riscv/smaxf.c @@ -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" } } */ Index: gcc/gcc/testsuite/gcc.target/riscv/smin-ieee.c =================================================================== --- /dev/null +++ gcc/gcc/testsuite/gcc.target/riscv/smin-ieee.c @@ -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" } } */ Index: gcc/gcc/testsuite/gcc.target/riscv/smin.c =================================================================== --- /dev/null +++ gcc/gcc/testsuite/gcc.target/riscv/smin.c @@ -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" } } */ Index: gcc/gcc/testsuite/gcc.target/riscv/sminf-ieee.c =================================================================== --- /dev/null +++ gcc/gcc/testsuite/gcc.target/riscv/sminf-ieee.c @@ -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" } } */ Index: gcc/gcc/testsuite/gcc.target/riscv/sminf.c =================================================================== --- /dev/null +++ gcc/gcc/testsuite/gcc.target/riscv/sminf.c @@ -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" } } */