diff --git a/src/cmd/compile/internal/ssa/_gen/RISCV64.rules b/src/cmd/compile/internal/ssa/_gen/RISCV64.rules
index 49bdbc8..b43b7e2 100644
--- a/src/cmd/compile/internal/ssa/_gen/RISCV64.rules
+++ b/src/cmd/compile/internal/ssa/_gen/RISCV64.rules
@@ -834,6 +834,16 @@
(FEQD x (FMOVDconst [c])) && float64ExactBits(c, math.Inf(1)) => (SNEZ (ANDI <typ.Int64> [1<<7] (FCLASSD x)))
(FNED x (FMOVDconst [c])) && float64ExactBits(c, math.Inf(1)) => (SEQZ (ANDI <typ.Int64> [1<<7] (FCLASSD x)))
+// Test for subnormal numbers using 64 bit classify instruction.
+(FLTD x (FMOVDconst [+0x1p-1022])) => (SNEZ (ANDI <typ.Int64> [0b00_0011_1111] (FCLASSD x)))
+(FLED (FMOVDconst [+0x1p-1022]) x) => (SNEZ (ANDI <typ.Int64> [0b00_1100_0000] (FCLASSD x)))
+(FLED x (FMOVDconst [-0x1p-1022])) => (SNEZ (ANDI <typ.Int64> [0b00_0000_0011] (FCLASSD x)))
+(FLTD (FMOVDconst [-0x1p-1022]) x) => (SNEZ (ANDI <typ.Int64> [0b00_1111_1100] (FCLASSD x)))
+
+// Absorb unary sign bit operations into 64 bit classify instruction.
+(ANDI [c] (FCLASSD (FNEGD x))) => (ANDI [(c&0b11_0000_0000)|int64(bits.Reverse8(uint8(c))&0b1111_1111)] (FCLASSD x))
+(ANDI [c] (FCLASSD (FABSD x))) => (ANDI [(c&0b11_1111_0000)|int64(bits.Reverse8(uint8(c))&0b0000_1111)] (FCLASSD x))
+
//
// Optimisations for rva22u64 and above.
//
diff --git a/src/cmd/compile/internal/ssa/rewriteRISCV64.go b/src/cmd/compile/internal/ssa/rewriteRISCV64.go
index 8a390eb..96e8b55 100644
--- a/src/cmd/compile/internal/ssa/rewriteRISCV64.go
+++ b/src/cmd/compile/internal/ssa/rewriteRISCV64.go
@@ -4,6 +4,7 @@
import "internal/buildcfg"
import "math"
+import "math/bits"
import "cmd/compile/internal/types"
func rewriteValueRISCV64(v *Value) bool {
@@ -3479,6 +3480,8 @@
}
func rewriteValueRISCV64_OpRISCV64ANDI(v *Value) bool {
v_0 := v.Args[0]
+ b := v.Block
+ typ := &b.Func.Config.Types
// match: (ANDI [0] x)
// result: (MOVDconst [0])
for {
@@ -3525,6 +3528,44 @@
v.AddArg(z)
return true
}
+ // match: (ANDI [c] (FCLASSD (FNEGD x)))
+ // result: (ANDI [(c&0b11_0000_0000)|int64(bits.Reverse8(uint8(c))&0b1111_1111)] (FCLASSD x))
+ for {
+ c := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpRISCV64FCLASSD {
+ break
+ }
+ v_0_0 := v_0.Args[0]
+ if v_0_0.Op != OpRISCV64FNEGD {
+ break
+ }
+ x := v_0_0.Args[0]
+ v.reset(OpRISCV64ANDI)
+ v.AuxInt = int64ToAuxInt((c & 0b11_0000_0000) | int64(bits.Reverse8(uint8(c))&0b1111_1111))
+ v0 := b.NewValue0(v.Pos, OpRISCV64FCLASSD, typ.Int64)
+ v0.AddArg(x)
+ v.AddArg(v0)
+ return true
+ }
+ // match: (ANDI [c] (FCLASSD (FABSD x)))
+ // result: (ANDI [(c&0b11_1111_0000)|int64(bits.Reverse8(uint8(c))&0b0000_1111)] (FCLASSD x))
+ for {
+ c := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpRISCV64FCLASSD {
+ break
+ }
+ v_0_0 := v_0.Args[0]
+ if v_0_0.Op != OpRISCV64FABSD {
+ break
+ }
+ x := v_0_0.Args[0]
+ v.reset(OpRISCV64ANDI)
+ v.AuxInt = int64ToAuxInt((c & 0b11_1111_0000) | int64(bits.Reverse8(uint8(c))&0b0000_1111))
+ v0 := b.NewValue0(v.Pos, OpRISCV64FCLASSD, typ.Int64)
+ v0.AddArg(x)
+ v.AddArg(v0)
+ return true
+ }
return false
}
func rewriteValueRISCV64_OpRISCV64FADDD(v *Value) bool {
@@ -3677,6 +3718,38 @@
v.AddArg(v0)
return true
}
+ // match: (FLED (FMOVDconst [+0x1p-1022]) x)
+ // result: (SNEZ (ANDI <typ.Int64> [0b00_1100_0000] (FCLASSD x)))
+ for {
+ if v_0.Op != OpRISCV64FMOVDconst || auxIntToFloat64(v_0.AuxInt) != +0x1p-1022 {
+ break
+ }
+ x := v_1
+ v.reset(OpRISCV64SNEZ)
+ v0 := b.NewValue0(v.Pos, OpRISCV64ANDI, typ.Int64)
+ v0.AuxInt = int64ToAuxInt(0b00_1100_0000)
+ v1 := b.NewValue0(v.Pos, OpRISCV64FCLASSD, typ.Int64)
+ v1.AddArg(x)
+ v0.AddArg(v1)
+ v.AddArg(v0)
+ return true
+ }
+ // match: (FLED x (FMOVDconst [-0x1p-1022]))
+ // result: (SNEZ (ANDI <typ.Int64> [0b00_0000_0011] (FCLASSD x)))
+ for {
+ x := v_0
+ if v_1.Op != OpRISCV64FMOVDconst || auxIntToFloat64(v_1.AuxInt) != -0x1p-1022 {
+ break
+ }
+ v.reset(OpRISCV64SNEZ)
+ v0 := b.NewValue0(v.Pos, OpRISCV64ANDI, typ.Int64)
+ v0.AuxInt = int64ToAuxInt(0b00_0000_0011)
+ 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 {
@@ -3724,6 +3797,38 @@
v.AddArg(v0)
return true
}
+ // match: (FLTD x (FMOVDconst [+0x1p-1022]))
+ // result: (SNEZ (ANDI <typ.Int64> [0b00_0011_1111] (FCLASSD x)))
+ for {
+ x := v_0
+ if v_1.Op != OpRISCV64FMOVDconst || auxIntToFloat64(v_1.AuxInt) != +0x1p-1022 {
+ break
+ }
+ v.reset(OpRISCV64SNEZ)
+ v0 := b.NewValue0(v.Pos, OpRISCV64ANDI, typ.Int64)
+ v0.AuxInt = int64ToAuxInt(0b00_0011_1111)
+ v1 := b.NewValue0(v.Pos, OpRISCV64FCLASSD, typ.Int64)
+ v1.AddArg(x)
+ v0.AddArg(v1)
+ v.AddArg(v0)
+ return true
+ }
+ // match: (FLTD (FMOVDconst [-0x1p-1022]) x)
+ // result: (SNEZ (ANDI <typ.Int64> [0b00_1111_1100] (FCLASSD x)))
+ for {
+ if v_0.Op != OpRISCV64FMOVDconst || auxIntToFloat64(v_0.AuxInt) != -0x1p-1022 {
+ break
+ }
+ x := v_1
+ v.reset(OpRISCV64SNEZ)
+ v0 := b.NewValue0(v.Pos, OpRISCV64ANDI, typ.Int64)
+ v0.AuxInt = int64ToAuxInt(0b00_1111_1100)
+ 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 {
diff --git a/src/cmd/compile/internal/test/float_test.go b/src/cmd/compile/internal/test/float_test.go
index 7a5e278..00735e3 100644
--- a/src/cmd/compile/internal/test/float_test.go
+++ b/src/cmd/compile/internal/test/float_test.go
@@ -727,6 +727,65 @@
}
}
+// minNormal64 is the smallest float64 value that is not subnormal.
+const minNormal64 = 2.2250738585072014e-308
+
+//go:noinline
+func isAbsLessThanMinNormal64(x float64) bool {
+ return math.Abs(x) < minNormal64
+}
+
+//go:noinline
+func isLessThanMinNormal64(x float64) bool {
+ return x < minNormal64
+}
+
+//go:noinline
+func isGreaterThanNegMinNormal64(x float64) bool {
+ return x > -minNormal64
+}
+
+//go:noinline
+func isGreaterThanOrEqualToMinNormal64(x float64) bool {
+ return math.Abs(x) >= minNormal64
+}
+
+func TestSubnormalComparisons(t *testing.T) {
+ tests := []struct {
+ value float64
+ isAbsLessThanMinNormal bool
+ isPositive bool
+ isNegative bool
+ isNaN bool
+ }{
+ {value: math.Inf(1), isPositive: true},
+ {value: math.MaxFloat64, isPositive: true},
+ {value: math.Inf(-1), isNegative: true},
+ {value: -math.MaxFloat64, isNegative: true},
+ {value: math.NaN(), isNaN: true},
+ {value: minNormal64, isPositive: true},
+ {value: minNormal64 / 2, isAbsLessThanMinNormal: true, isPositive: true},
+ {value: -minNormal64, isNegative: true},
+ {value: -minNormal64 / 2, isAbsLessThanMinNormal: true, isNegative: true},
+ {value: 0, isAbsLessThanMinNormal: true, isPositive: true},
+ {value: math.Copysign(0, -1), isAbsLessThanMinNormal: true, isNegative: 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("isAbsLessThanMinNormal64", isAbsLessThanMinNormal64, test.value, test.isAbsLessThanMinNormal)
+ check("isLessThanMinNormal64", isLessThanMinNormal64, test.value, test.isAbsLessThanMinNormal || test.isNegative)
+ check("isGreaterThanNegMinNormal64", isGreaterThanNegMinNormal64, test.value, test.isAbsLessThanMinNormal || test.isPositive)
+ check("isGreaterThanOrEqualToMinNormal64", isGreaterThanOrEqualToMinNormal64, test.value, !test.isAbsLessThanMinNormal && !test.isNaN)
+ }
+}
+
var sinkFloat float64
func BenchmarkMul2(b *testing.B) {
diff --git a/test/codegen/floats.go b/test/codegen/floats.go
index 3942cb5..343f8fa 100644
--- a/test/codegen/floats.go
+++ b/test/codegen/floats.go
@@ -6,6 +6,8 @@
package codegen
+import "math"
+
// This file contains codegen tests related to arithmetic
// simplifications and optimizations on float types.
// For codegen tests on integer types, see arithmetic.go.
@@ -277,3 +279,37 @@
// riscv64: "MOVD [$]f64.4015ba5e353f7cee"
*p = 5.432
}
+
+// ------------------------ //
+// Subnormal tests //
+// ------------------------ //
+
+func isSubnormal(x float64) bool {
+ // riscv64:"FCLASSD" -"FABSD"
+ return math.Abs(x) < 2.2250738585072014e-308
+}
+
+func isNormal(x float64) bool {
+ // riscv64:"FCLASSD" -"FABSD"
+ return math.Abs(x) >= 0x1p-1022
+}
+
+func isPosSubnormal(x float64) bool {
+ // riscv64:"FCLASSD"
+ return x > 0 && x < 2.2250738585072014e-308
+}
+
+func isNegSubnormal(x float64) bool {
+ // riscv64:"FCLASSD"
+ return x < 0 && x > -0x1p-1022
+}
+
+func isPosNormal(x float64) bool {
+ // riscv64:"FCLASSD"
+ return x >= 2.2250738585072014e-308
+}
+
+func isNegNormal(x float64) bool {
+ // riscv64:"FCLASSD"
+ return x <= -2.2250738585072014e-308
+}