]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: emit classify instructions for infinity tests on riscv64
authorMichael Munday <mike.munday@lowrisc.org>
Wed, 8 Sep 2021 13:48:48 +0000 (13:48 +0000)
committerMeng Zhuo <mengzhuo1203@gmail.com>
Thu, 14 Aug 2025 03:33:56 +0000 (20:33 -0700)
The 'classify' instruction on RISC-V sets a bit in a mask to indicate
the class a floating point value belongs to (e.g. whether the value is
an infinity, a normal number, a subnormal number and so on). There are
other places this instruction is useful but for now I've just used it
for infinity tests.

The gains are relatively small (~1-2 instructions per IsInf call) but
using FCLASSD does potentially unlock further optimizations. It also
reduces the number of loads from memory and the number of moves
between general purpose and floating point register files.

goos: linux
goarch: riscv64
pkg: math
cpu: Spacemit(R) X60
                    │        sec/op        │   sec/op     vs base                │
Acos                           159.9n ± 0%   173.7n ± 0%   +8.66% (p=0.000 n=10)
Acosh                          249.8n ± 0%   254.4n ± 0%   +1.86% (p=0.000 n=10)
Asin                           159.9n ± 0%   173.7n ± 0%   +8.66% (p=0.000 n=10)
Asinh                          292.2n ± 0%   283.0n ± 0%   -3.15% (p=0.000 n=10)
Atan                           119.1n ± 0%   119.0n ± 0%   -0.08% (p=0.036 n=10)
Atanh                          265.1n ± 0%   271.6n ± 0%   +2.43% (p=0.000 n=10)
Atan2                          194.9n ± 0%   186.7n ± 0%   -4.23% (p=0.000 n=10)
Cbrt                           216.3n ± 0%   203.1n ± 0%   -6.10% (p=0.000 n=10)
Ceil                           31.82n ± 0%   31.81n ± 0%        ~ (p=0.063 n=10)
Copysign                       4.897n ± 0%   4.893n ± 3%   -0.08% (p=0.038 n=10)
Cos                            123.9n ± 0%   107.7n ± 1%  -13.03% (p=0.000 n=10)
Cosh                           293.0n ± 0%   264.6n ± 0%   -9.68% (p=0.000 n=10)
Erf                            150.0n ± 0%   133.8n ± 0%  -10.80% (p=0.000 n=10)
Erfc                           151.8n ± 0%   137.9n ± 0%   -9.16% (p=0.000 n=10)
Erfinv                         173.8n ± 0%   173.8n ± 0%        ~ (p=0.820 n=10)
Erfcinv                        173.8n ± 0%   173.8n ± 0%        ~ (p=1.000 n=10)
Exp                            247.7n ± 0%   220.4n ± 0%  -11.04% (p=0.000 n=10)
ExpGo                          261.4n ± 0%   232.5n ± 0%  -11.04% (p=0.000 n=10)
Expm1                          176.2n ± 0%   164.9n ± 0%   -6.41% (p=0.000 n=10)
Exp2                           220.4n ± 0%   190.2n ± 0%  -13.70% (p=0.000 n=10)
Exp2Go                         232.5n ± 0%   204.0n ± 0%  -12.22% (p=0.000 n=10)
Abs                            4.897n ± 0%   4.897n ± 0%        ~ (p=0.726 n=10)
Dim                            16.32n ± 0%   16.31n ± 0%        ~ (p=0.770 n=10)
Floor                          31.84n ± 0%   31.83n ± 0%        ~ (p=0.677 n=10)
Max                            26.11n ± 0%   26.13n ± 0%        ~ (p=0.290 n=10)
Min                            26.10n ± 0%   26.11n ± 0%        ~ (p=0.424 n=10)
Mod                            416.2n ± 0%   337.8n ± 0%  -18.83% (p=0.000 n=10)
Frexp                          63.65n ± 0%   50.60n ± 0%  -20.50% (p=0.000 n=10)
Gamma                          218.8n ± 0%   206.4n ± 0%   -5.62% (p=0.000 n=10)
Hypot                          92.20n ± 0%   94.69n ± 0%   +2.70% (p=0.000 n=10)
HypotGo                        107.7n ± 0%   109.3n ± 0%   +1.49% (p=0.000 n=10)
Ilogb                          59.54n ± 0%   44.04n ± 0%  -26.04% (p=0.000 n=10)
J0                             708.9n ± 0%   674.5n ± 0%   -4.86% (p=0.000 n=10)
J1                             707.6n ± 0%   676.1n ± 0%   -4.44% (p=0.000 n=10)
Jn                             1.513µ ± 0%   1.427µ ± 0%   -5.68% (p=0.000 n=10)
Ldexp                          70.20n ± 0%   57.09n ± 0%  -18.68% (p=0.000 n=10)
Lgamma                         201.5n ± 0%   185.3n ± 1%   -8.01% (p=0.000 n=10)
Log                            201.5n ± 0%   182.7n ± 0%   -9.35% (p=0.000 n=10)
Logb                           59.54n ± 0%   46.53n ± 0%  -21.86% (p=0.000 n=10)
Log1p                          178.8n ± 0%   173.9n ± 6%   -2.74% (p=0.021 n=10)
Log10                          201.4n ± 0%   184.3n ± 0%   -8.49% (p=0.000 n=10)
Log2                           79.17n ± 0%   66.07n ± 0%  -16.54% (p=0.000 n=10)
Modf                           34.27n ± 0%   34.25n ± 0%        ~ (p=0.559 n=10)
Nextafter32                    49.34n ± 0%   49.37n ± 0%   +0.05% (p=0.040 n=10)
Nextafter64                    43.66n ± 0%   43.66n ± 0%        ~ (p=0.869 n=10)
PowInt                         309.1n ± 0%   267.4n ± 0%  -13.49% (p=0.000 n=10)
PowFrac                        769.6n ± 0%   677.3n ± 0%  -11.98% (p=0.000 n=10)
Pow10Pos                       13.88n ± 0%   13.88n ± 0%        ~ (p=0.811 n=10)
Pow10Neg                       19.58n ± 0%   19.57n ± 0%        ~ (p=0.993 n=10)
Round                          23.65n ± 0%   23.66n ± 0%        ~ (p=0.354 n=10)
RoundToEven                    27.75n ± 0%   27.75n ± 0%        ~ (p=0.971 n=10)
Remainder                      380.0n ± 0%   309.9n ± 0%  -18.45% (p=0.000 n=10)
Signbit                        13.06n ± 0%   13.06n ± 0%        ~ (p=1.000 n=10)
Sin                            133.8n ± 0%   120.8n ± 0%   -9.75% (p=0.000 n=10)
Sincos                         160.7n ± 0%   147.7n ± 0%   -8.12% (p=0.000 n=10)
Sinh                           305.9n ± 0%   277.9n ± 0%   -9.17% (p=0.000 n=10)
SqrtIndirect                   3.265n ± 0%   3.264n ± 0%        ~ (p=0.546 n=10)
SqrtLatency                    19.58n ± 0%   19.58n ± 0%        ~ (p=0.973 n=10)
SqrtIndirectLatency            19.59n ± 0%   19.58n ± 0%        ~ (p=0.370 n=10)
SqrtGoLatency                  205.7n ± 0%   202.7n ± 0%   -1.46% (p=0.000 n=10)
SqrtPrime                      4.953µ ± 0%   4.954µ ± 0%        ~ (p=0.477 n=10)
Tan                            163.2n ± 0%   150.2n ± 0%   -7.99% (p=0.000 n=10)
Tanh                           312.4n ± 0%   284.2n ± 0%   -9.01% (p=0.000 n=10)
Trunc                          31.83n ± 0%   31.83n ± 0%        ~ (p=0.663 n=10)
Y0                             701.0n ± 0%   669.2n ± 0%   -4.54% (p=0.000 n=10)
Y1                             704.5n ± 0%   672.4n ± 0%   -4.55% (p=0.000 n=10)
Yn                             1.490µ ± 0%   1.422µ ± 0%   -4.60% (p=0.000 n=10)
Float64bits                    5.713n ± 0%   5.710n ± 0%        ~ (p=0.926 n=10)
Float64frombits                4.896n ± 0%   4.896n ± 0%        ~ (p=0.663 n=10)
Float32bits                    12.25n ± 0%   12.25n ± 0%        ~ (p=0.571 n=10)
Float32frombits                4.898n ± 0%   4.896n ± 0%        ~ (p=0.754 n=10)
FMA                            4.895n ± 0%   4.895n ± 0%        ~ (p=0.745 n=10)
geomean                        94.40n        89.43n        -5.27%

Change-Id: I4fe0f2e9f609e38d79463f9ba2519a3f9427432e
Reviewed-on: https://go-review.googlesource.com/c/go/+/348389
Reviewed-by: Keith Randall <khr@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Meng Zhuo <mengzhuo1203@gmail.com>
Reviewed-by: David Chase <drchase@google.com>
Reviewed-by: Keith Randall <khr@google.com>
src/cmd/compile/internal/riscv64/ssa.go
src/cmd/compile/internal/ssa/_gen/RISCV64.rules
src/cmd/compile/internal/ssa/_gen/RISCV64Ops.go
src/cmd/compile/internal/ssa/opGen.go
src/cmd/compile/internal/ssa/rewriteRISCV64.go
src/cmd/compile/internal/test/float_test.go
test/codegen/math.go

index ed20782a29cd91b43a88493fcff69673fe8b0e9c..3d2f65a75e621245fb29686d02a76e4a18968ee5 100644 (file)
@@ -420,6 +420,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
                ssa.OpRISCV64FMVSX, ssa.OpRISCV64FMVXS, ssa.OpRISCV64FMVDX, ssa.OpRISCV64FMVXD,
                ssa.OpRISCV64FCVTSW, ssa.OpRISCV64FCVTSL, ssa.OpRISCV64FCVTWS, ssa.OpRISCV64FCVTLS,
                ssa.OpRISCV64FCVTDW, ssa.OpRISCV64FCVTDL, ssa.OpRISCV64FCVTWD, ssa.OpRISCV64FCVTLD, ssa.OpRISCV64FCVTDS, ssa.OpRISCV64FCVTSD,
+               ssa.OpRISCV64FCLASSS, ssa.OpRISCV64FCLASSD,
                ssa.OpRISCV64NOT, ssa.OpRISCV64NEG, ssa.OpRISCV64NEGW, ssa.OpRISCV64CLZ, ssa.OpRISCV64CLZW, ssa.OpRISCV64CTZ, ssa.OpRISCV64CTZW,
                ssa.OpRISCV64REV8, ssa.OpRISCV64CPOP, ssa.OpRISCV64CPOPW:
                p := s.Prog(v.Op.Asm())
index 69bf1c7c9e4f0e00e7060cfa86f853fa3367d61c..9d79fc34e8732b9a843cc0092649f1defd9a7a7b 100644 (file)
 (F(MADD|NMADD|MSUB|NMSUB)D neg:(FNEGD x) y z) && neg.Uses == 1 => (F(NMSUB|MSUB|NMADD|MADD)D x y z)
 (F(MADD|NMADD|MSUB|NMSUB)D x y neg:(FNEGD z)) && neg.Uses == 1 => (F(MSUB|NMSUB|MADD|NMADD)D x y z)
 
+// Test for -∞ (bit 0) using 64 bit classify instruction.
+(FLTD x (FMVDX (MOVDconst [int64(math.Float64bits(-math.MaxFloat64))]))) => (ANDI [1] (FCLASSD x))
+(FLED (FMVDX (MOVDconst [int64(math.Float64bits(-math.MaxFloat64))])) x) => (SNEZ (ANDI <typ.Int64> [0xff &^ 1] (FCLASSD x)))
+(FEQD x (FMVDX (MOVDconst [int64(math.Float64bits(math.Inf(-1)))])))  => (ANDI [1] (FCLASSD x))
+(FNED x (FMVDX (MOVDconst [int64(math.Float64bits(math.Inf(-1)))])))  => (SEQZ (ANDI <typ.Int64> [1] (FCLASSD x)))
+
+// Test for +∞ (bit 7) using 64 bit classify instruction.
+(FLTD (FMVDX (MOVDconst [int64(math.Float64bits(math.MaxFloat64))])) x) => (SNEZ (ANDI <typ.Int64> [1<<7] (FCLASSD x)))
+(FLED x (FMVDX (MOVDconst [int64(math.Float64bits(math.MaxFloat64))]))) => (SNEZ (ANDI <typ.Int64> [0xff &^ (1<<7)] (FCLASSD x)))
+(FEQD x (FMVDX (MOVDconst [int64(math.Float64bits(math.Inf(1)))]))) => (SNEZ (ANDI <typ.Int64> [1<<7] (FCLASSD x)))
+(FNED x (FMVDX (MOVDconst [int64(math.Float64bits(math.Inf(1)))]))) => (SEQZ (ANDI <typ.Int64> [1<<7] (FCLASSD x)))
+
 //
 // Optimisations for rva22u64 and above.
 //
index d468a00b0f74167feb0639074b8da39b4c85dc11..0bccaf63bc4182cf9dc24d4d5b51c52f80f9f8c4 100644 (file)
@@ -497,6 +497,27 @@ func init() {
                {name: "FLED", argLength: 2, reg: fp2gp, asm: "FLED"},                                                                               // arg0 <= arg1
                {name: "LoweredFMIND", argLength: 2, reg: fp21, resultNotInArgs: true, asm: "FMIND", commutative: true, typ: "Float64"},             // min(arg0, arg1)
                {name: "LoweredFMAXD", argLength: 2, reg: fp21, resultNotInArgs: true, asm: "FMAXD", commutative: true, typ: "Float64"},             // max(arg0, arg1)
+
+               // Floating point classify (in the F and D extensions).
+               //
+               // The FCLASS instructions will always set exactly one bit in the output
+               // register, all other bits will be cleared.
+               //
+               //   Bit | Class
+               //   ====+=============================
+               //     0 | -∞
+               //     1 | a negative normal number
+               //     2 | a negative subnormal number
+               //     3 | -0
+               //     4 | +0
+               //     5 | a positive subnormal number
+               //     6 | a positive normal number
+               //     7 | +∞
+               //     8 | qNaN
+               //     9 | sNaN
+               //   ====+=============================
+               {name: "FCLASSS", argLength: 1, reg: fpgp, asm: "FCLASSS", typ: "Int64"}, // classify float32
+               {name: "FCLASSD", argLength: 1, reg: fpgp, asm: "FCLASSD", typ: "Int64"}, // classify float64
        }
 
        RISCV64blocks := []blockData{
index a62a4b1cf6a2c47bef1ade2f59b155f205ef8d86..215f0ae43aab14032eb1833ab7ce130c6c911fcf 100644 (file)
@@ -2644,6 +2644,8 @@ const (
        OpRISCV64FLED
        OpRISCV64LoweredFMIND
        OpRISCV64LoweredFMAXD
+       OpRISCV64FCLASSS
+       OpRISCV64FCLASSD
 
        OpS390XFADDS
        OpS390XFADD
@@ -35611,6 +35613,32 @@ var opcodeTable = [...]opInfo{
                        },
                },
        },
+       {
+               name:   "FCLASSS",
+               argLen: 1,
+               asm:    riscv.AFCLASSS,
+               reg: regInfo{
+                       inputs: []inputInfo{
+                               {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+                       },
+                       outputs: []outputInfo{
+                               {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+                       },
+               },
+       },
+       {
+               name:   "FCLASSD",
+               argLen: 1,
+               asm:    riscv.AFCLASSD,
+               reg: regInfo{
+                       inputs: []inputInfo{
+                               {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+                       },
+                       outputs: []outputInfo{
+                               {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+                       },
+               },
+       },
 
        {
                name:         "FADDS",
index 0dd952f5120b0d745f2df42f47279f147098781d..c78ae89561910edc032adae4f78575307da776f6 100644 (file)
@@ -513,6 +513,12 @@ func rewriteValueRISCV64(v *Value) bool {
                return rewriteValueRISCV64_OpRISCV64FADDD(v)
        case OpRISCV64FADDS:
                return rewriteValueRISCV64_OpRISCV64FADDS(v)
+       case OpRISCV64FEQD:
+               return rewriteValueRISCV64_OpRISCV64FEQD(v)
+       case OpRISCV64FLED:
+               return rewriteValueRISCV64_OpRISCV64FLED(v)
+       case OpRISCV64FLTD:
+               return rewriteValueRISCV64_OpRISCV64FLTD(v)
        case OpRISCV64FMADDD:
                return rewriteValueRISCV64_OpRISCV64FMADDD(v)
        case OpRISCV64FMADDS:
@@ -529,6 +535,8 @@ func rewriteValueRISCV64(v *Value) bool {
                return rewriteValueRISCV64_OpRISCV64FMSUBD(v)
        case OpRISCV64FMSUBS:
                return rewriteValueRISCV64_OpRISCV64FMSUBS(v)
+       case OpRISCV64FNED:
+               return rewriteValueRISCV64_OpRISCV64FNED(v)
        case OpRISCV64FNMADDD:
                return rewriteValueRISCV64_OpRISCV64FNMADDD(v)
        case OpRISCV64FNMADDS:
@@ -3762,6 +3770,149 @@ func rewriteValueRISCV64_OpRISCV64FADDS(v *Value) bool {
        }
        return false
 }
+func rewriteValueRISCV64_OpRISCV64FEQD(v *Value) bool {
+       v_1 := v.Args[1]
+       v_0 := v.Args[0]
+       b := v.Block
+       typ := &b.Func.Config.Types
+       // match: (FEQD x (FMVDX (MOVDconst [int64(math.Float64bits(math.Inf(-1)))])))
+       // result: (ANDI [1] (FCLASSD x))
+       for {
+               for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+                       x := v_0
+                       if v_1.Op != OpRISCV64FMVDX {
+                               continue
+                       }
+                       v_1_0 := v_1.Args[0]
+                       if v_1_0.Op != OpRISCV64MOVDconst || auxIntToInt64(v_1_0.AuxInt) != int64(math.Float64bits(math.Inf(-1))) {
+                               continue
+                       }
+                       v.reset(OpRISCV64ANDI)
+                       v.AuxInt = int64ToAuxInt(1)
+                       v0 := b.NewValue0(v.Pos, OpRISCV64FCLASSD, typ.Int64)
+                       v0.AddArg(x)
+                       v.AddArg(v0)
+                       return true
+               }
+               break
+       }
+       // match: (FEQD x (FMVDX (MOVDconst [int64(math.Float64bits(math.Inf(1)))])))
+       // result: (SNEZ (ANDI <typ.Int64> [1<<7] (FCLASSD x)))
+       for {
+               for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+                       x := v_0
+                       if v_1.Op != OpRISCV64FMVDX {
+                               continue
+                       }
+                       v_1_0 := v_1.Args[0]
+                       if v_1_0.Op != OpRISCV64MOVDconst || auxIntToInt64(v_1_0.AuxInt) != int64(math.Float64bits(math.Inf(1))) {
+                               continue
+                       }
+                       v.reset(OpRISCV64SNEZ)
+                       v0 := b.NewValue0(v.Pos, OpRISCV64ANDI, typ.Int64)
+                       v0.AuxInt = int64ToAuxInt(1 << 7)
+                       v1 := b.NewValue0(v.Pos, OpRISCV64FCLASSD, typ.Int64)
+                       v1.AddArg(x)
+                       v0.AddArg(v1)
+                       v.AddArg(v0)
+                       return true
+               }
+               break
+       }
+       return false
+}
+func rewriteValueRISCV64_OpRISCV64FLED(v *Value) bool {
+       v_1 := v.Args[1]
+       v_0 := v.Args[0]
+       b := v.Block
+       typ := &b.Func.Config.Types
+       // match: (FLED (FMVDX (MOVDconst [int64(math.Float64bits(-math.MaxFloat64))])) x)
+       // result: (SNEZ (ANDI <typ.Int64> [0xff &^ 1] (FCLASSD x)))
+       for {
+               if v_0.Op != OpRISCV64FMVDX {
+                       break
+               }
+               v_0_0 := v_0.Args[0]
+               if v_0_0.Op != OpRISCV64MOVDconst || auxIntToInt64(v_0_0.AuxInt) != int64(math.Float64bits(-math.MaxFloat64)) {
+                       break
+               }
+               x := v_1
+               v.reset(OpRISCV64SNEZ)
+               v0 := b.NewValue0(v.Pos, OpRISCV64ANDI, typ.Int64)
+               v0.AuxInt = int64ToAuxInt(0xff &^ 1)
+               v1 := b.NewValue0(v.Pos, OpRISCV64FCLASSD, typ.Int64)
+               v1.AddArg(x)
+               v0.AddArg(v1)
+               v.AddArg(v0)
+               return true
+       }
+       // match: (FLED x (FMVDX (MOVDconst [int64(math.Float64bits(math.MaxFloat64))])))
+       // result: (SNEZ (ANDI <typ.Int64> [0xff &^ (1<<7)] (FCLASSD x)))
+       for {
+               x := v_0
+               if v_1.Op != OpRISCV64FMVDX {
+                       break
+               }
+               v_1_0 := v_1.Args[0]
+               if v_1_0.Op != OpRISCV64MOVDconst || auxIntToInt64(v_1_0.AuxInt) != int64(math.Float64bits(math.MaxFloat64)) {
+                       break
+               }
+               v.reset(OpRISCV64SNEZ)
+               v0 := b.NewValue0(v.Pos, OpRISCV64ANDI, typ.Int64)
+               v0.AuxInt = int64ToAuxInt(0xff &^ (1 << 7))
+               v1 := b.NewValue0(v.Pos, OpRISCV64FCLASSD, typ.Int64)
+               v1.AddArg(x)
+               v0.AddArg(v1)
+               v.AddArg(v0)
+               return true
+       }
+       return false
+}
+func rewriteValueRISCV64_OpRISCV64FLTD(v *Value) bool {
+       v_1 := v.Args[1]
+       v_0 := v.Args[0]
+       b := v.Block
+       typ := &b.Func.Config.Types
+       // match: (FLTD x (FMVDX (MOVDconst [int64(math.Float64bits(-math.MaxFloat64))])))
+       // result: (ANDI [1] (FCLASSD x))
+       for {
+               x := v_0
+               if v_1.Op != OpRISCV64FMVDX {
+                       break
+               }
+               v_1_0 := v_1.Args[0]
+               if v_1_0.Op != OpRISCV64MOVDconst || auxIntToInt64(v_1_0.AuxInt) != int64(math.Float64bits(-math.MaxFloat64)) {
+                       break
+               }
+               v.reset(OpRISCV64ANDI)
+               v.AuxInt = int64ToAuxInt(1)
+               v0 := b.NewValue0(v.Pos, OpRISCV64FCLASSD, typ.Int64)
+               v0.AddArg(x)
+               v.AddArg(v0)
+               return true
+       }
+       // match: (FLTD (FMVDX (MOVDconst [int64(math.Float64bits(math.MaxFloat64))])) x)
+       // result: (SNEZ (ANDI <typ.Int64> [1<<7] (FCLASSD x)))
+       for {
+               if v_0.Op != OpRISCV64FMVDX {
+                       break
+               }
+               v_0_0 := v_0.Args[0]
+               if v_0_0.Op != OpRISCV64MOVDconst || auxIntToInt64(v_0_0.AuxInt) != int64(math.Float64bits(math.MaxFloat64)) {
+                       break
+               }
+               x := v_1
+               v.reset(OpRISCV64SNEZ)
+               v0 := b.NewValue0(v.Pos, OpRISCV64ANDI, typ.Int64)
+               v0.AuxInt = int64ToAuxInt(1 << 7)
+               v1 := b.NewValue0(v.Pos, OpRISCV64FCLASSD, typ.Int64)
+               v1.AddArg(x)
+               v0.AddArg(v1)
+               v.AddArg(v0)
+               return true
+       }
+       return false
+}
 func rewriteValueRISCV64_OpRISCV64FMADDD(v *Value) bool {
        v_2 := v.Args[2]
        v_1 := v.Args[1]
@@ -4186,6 +4337,59 @@ func rewriteValueRISCV64_OpRISCV64FMSUBS(v *Value) bool {
        }
        return false
 }
+func rewriteValueRISCV64_OpRISCV64FNED(v *Value) bool {
+       v_1 := v.Args[1]
+       v_0 := v.Args[0]
+       b := v.Block
+       typ := &b.Func.Config.Types
+       // match: (FNED x (FMVDX (MOVDconst [int64(math.Float64bits(math.Inf(-1)))])))
+       // result: (SEQZ (ANDI <typ.Int64> [1] (FCLASSD x)))
+       for {
+               for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+                       x := v_0
+                       if v_1.Op != OpRISCV64FMVDX {
+                               continue
+                       }
+                       v_1_0 := v_1.Args[0]
+                       if v_1_0.Op != OpRISCV64MOVDconst || auxIntToInt64(v_1_0.AuxInt) != int64(math.Float64bits(math.Inf(-1))) {
+                               continue
+                       }
+                       v.reset(OpRISCV64SEQZ)
+                       v0 := b.NewValue0(v.Pos, OpRISCV64ANDI, typ.Int64)
+                       v0.AuxInt = int64ToAuxInt(1)
+                       v1 := b.NewValue0(v.Pos, OpRISCV64FCLASSD, typ.Int64)
+                       v1.AddArg(x)
+                       v0.AddArg(v1)
+                       v.AddArg(v0)
+                       return true
+               }
+               break
+       }
+       // match: (FNED x (FMVDX (MOVDconst [int64(math.Float64bits(math.Inf(1)))])))
+       // result: (SEQZ (ANDI <typ.Int64> [1<<7] (FCLASSD x)))
+       for {
+               for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+                       x := v_0
+                       if v_1.Op != OpRISCV64FMVDX {
+                               continue
+                       }
+                       v_1_0 := v_1.Args[0]
+                       if v_1_0.Op != OpRISCV64MOVDconst || auxIntToInt64(v_1_0.AuxInt) != int64(math.Float64bits(math.Inf(1))) {
+                               continue
+                       }
+                       v.reset(OpRISCV64SEQZ)
+                       v0 := b.NewValue0(v.Pos, OpRISCV64ANDI, typ.Int64)
+                       v0.AuxInt = int64ToAuxInt(1 << 7)
+                       v1 := b.NewValue0(v.Pos, OpRISCV64FCLASSD, typ.Int64)
+                       v1.AddArg(x)
+                       v0.AddArg(v1)
+                       v.AddArg(v0)
+                       return true
+               }
+               break
+       }
+       return false
+}
 func rewriteValueRISCV64_OpRISCV64FNMADDD(v *Value) bool {
        v_2 := v.Args[2]
        v_1 := v.Args[1]
index c736f970f9941705d001d8e9c008b66e13fdec2a..9e61148c5297e58965ab9323b5f0de57da6329f4 100644 (file)
@@ -523,6 +523,106 @@ func TestFloatSignalingNaNConversionConst(t *testing.T) {
        }
 }
 
+//go:noinline
+func isPosInf(x float64) bool {
+       return math.IsInf(x, 1)
+}
+
+//go:noinline
+func isPosInfEq(x float64) bool {
+       return x == math.Inf(1)
+}
+
+//go:noinline
+func isPosInfCmp(x float64) bool {
+       return x > math.MaxFloat64
+}
+
+//go:noinline
+func isNotPosInf(x float64) bool {
+       return !math.IsInf(x, 1)
+}
+
+//go:noinline
+func isNotPosInfEq(x float64) bool {
+       return x != math.Inf(1)
+}
+
+//go:noinline
+func isNotPosInfCmp(x float64) bool {
+       return x <= math.MaxFloat64
+}
+
+//go:noinline
+func isNegInf(x float64) bool {
+       return math.IsInf(x, -1)
+}
+
+//go:noinline
+func isNegInfEq(x float64) bool {
+       return x == math.Inf(-1)
+}
+
+//go:noinline
+func isNegInfCmp(x float64) bool {
+       return x < -math.MaxFloat64
+}
+
+//go:noinline
+func isNotNegInf(x float64) bool {
+       return !math.IsInf(x, -1)
+}
+
+//go:noinline
+func isNotNegInfEq(x float64) bool {
+       return x != math.Inf(-1)
+}
+
+//go:noinline
+func isNotNegInfCmp(x float64) bool {
+       return x >= -math.MaxFloat64
+}
+
+func TestInf(t *testing.T) {
+       tests := []struct {
+               value    float64
+               isPosInf bool
+               isNegInf bool
+               isNaN    bool
+       }{
+               {value: math.Inf(1), isPosInf: true},
+               {value: math.MaxFloat64},
+               {value: math.Inf(-1), isNegInf: true},
+               {value: -math.MaxFloat64},
+               {value: math.NaN(), isNaN: true},
+       }
+
+       check := func(name string, f func(x float64) bool, value float64, want bool) {
+               got := f(value)
+               if got != want {
+                       t.Errorf("%v(%g): want %v, got %v", name, value, want, got)
+               }
+       }
+
+       for _, test := range tests {
+               check("isPosInf", isPosInf, test.value, test.isPosInf)
+               check("isPosInfEq", isPosInfEq, test.value, test.isPosInf)
+               check("isPosInfCmp", isPosInfCmp, test.value, test.isPosInf)
+
+               check("isNotPosInf", isNotPosInf, test.value, !test.isPosInf)
+               check("isNotPosInfEq", isNotPosInfEq, test.value, !test.isPosInf)
+               check("isNotPosInfCmp", isNotPosInfCmp, test.value, !test.isPosInf && !test.isNaN)
+
+               check("isNegInf", isNegInf, test.value, test.isNegInf)
+               check("isNegInfEq", isNegInfEq, test.value, test.isNegInf)
+               check("isNegInfCmp", isNegInfCmp, test.value, test.isNegInf)
+
+               check("isNotNegInf", isNotNegInf, test.value, !test.isNegInf)
+               check("isNotNegInfEq", isNotNegInfEq, test.value, !test.isNegInf)
+               check("isNotNegInfCmp", isNotNegInfCmp, test.value, !test.isNegInf && !test.isNaN)
+       }
+}
+
 var sinkFloat float64
 
 func BenchmarkMul2(b *testing.B) {
index 4272e4ef887bff94cc97ec7bb0b9d7f9e5412c15..5b3e7272542387b7d168ebc5dccad55af7d53dd5 100644 (file)
@@ -154,6 +154,66 @@ func fnma(x, y, z float64) float64 {
        return math.FMA(x, -y, -z)
 }
 
+func isPosInf(x float64) bool {
+       // riscv64:"FCLASSD"
+       return math.IsInf(x, 1)
+}
+
+func isPosInfEq(x float64) bool {
+       // riscv64:"FCLASSD"
+       return x == math.Inf(1)
+}
+
+func isPosInfCmp(x float64) bool {
+       // riscv64:"FCLASSD"
+       return x > math.MaxFloat64
+}
+
+func isNotPosInf(x float64) bool {
+       // riscv64:"FCLASSD"
+       return !math.IsInf(x, 1)
+}
+
+func isNotPosInfEq(x float64) bool {
+       // riscv64:"FCLASSD"
+       return x != math.Inf(1)
+}
+
+func isNotPosInfCmp(x float64) bool {
+       // riscv64:"FCLASSD"
+       return x <= math.MaxFloat64
+}
+
+func isNegInf(x float64) bool {
+       // riscv64:"FCLASSD"
+       return math.IsInf(x, -1)
+}
+
+func isNegInfEq(x float64) bool {
+       // riscv64:"FCLASSD"
+       return x == math.Inf(-1)
+}
+
+func isNegInfCmp(x float64) bool {
+       // riscv64:"FCLASSD"
+       return x < -math.MaxFloat64
+}
+
+func isNotNegInf(x float64) bool {
+       // riscv64:"FCLASSD"
+       return !math.IsInf(x, -1)
+}
+
+func isNotNegInfEq(x float64) bool {
+       // riscv64:"FCLASSD"
+       return x != math.Inf(-1)
+}
+
+func isNotNegInfCmp(x float64) bool {
+       // riscv64:"FCLASSD"
+       return x >= -math.MaxFloat64
+}
+
 func fromFloat64(f64 float64) uint64 {
        // amd64:"MOVQ\tX.*, [^X].*"
        // arm64:"FMOVD\tF.*, R.*"