diff --git a/src/cmd/compile/internal/test/switch_test.go b/src/cmd/compile/internal/test/switch_test.go index 30dee6257e3..ddb39bfe5fa 100644 --- a/src/cmd/compile/internal/test/switch_test.go +++ b/src/cmd/compile/internal/test/switch_test.go @@ -120,6 +120,48 @@ func benchmarkSwitchString(b *testing.B, predictable bool) { sink = n } +func BenchmarkSwitchTypePredictable(b *testing.B) { + benchmarkSwitchType(b, true) +} +func BenchmarkSwitchTypeUnpredictable(b *testing.B) { + benchmarkSwitchType(b, false) +} +func benchmarkSwitchType(b *testing.B, predictable bool) { + a := []any{ + int8(1), + int16(2), + int32(3), + int64(4), + uint8(5), + uint16(6), + uint32(7), + uint64(8), + } + n := 0 + rng := newRNG() + for i := 0; i < b.N; i++ { + rng = rng.next(predictable) + switch a[rng.value()&7].(type) { + case int8: + n += 1 + case int16: + n += 2 + case int32: + n += 3 + case int64: + n += 4 + case uint8: + n += 5 + case uint16: + n += 6 + case uint32: + n += 7 + case uint64: + n += 8 + } + } +} + // A simple random number generator used to make switches conditionally predictable. type rng uint64 diff --git a/src/cmd/compile/internal/walk/switch.go b/src/cmd/compile/internal/walk/switch.go index ebd3128251a..67ccb2e5d16 100644 --- a/src/cmd/compile/internal/walk/switch.go +++ b/src/cmd/compile/internal/walk/switch.go @@ -7,6 +7,7 @@ package walk import ( "go/constant" "go/token" + "math/bits" "sort" "cmd/compile/internal/base" @@ -617,7 +618,9 @@ func (s *typeSwitch) flush() { } cc = merged - // TODO: figure out if we could use a jump table using some low bits of the type hashes. + if s.tryJumpTable(cc, &s.done) { + return + } binarySearch(len(cc), &s.done, func(i int) ir.Node { return ir.NewBinaryExpr(base.Pos, ir.OLE, s.hashname, ir.NewInt(base.Pos, int64(cc[i-1].hash))) @@ -632,6 +635,83 @@ func (s *typeSwitch) flush() { ) } +// Try to implement the clauses with a jump table. Returns true if successful. +func (s *typeSwitch) tryJumpTable(cc []typeClause, out *ir.Nodes) bool { + const minCases = 5 // have at least minCases cases in the switch + if base.Flag.N != 0 || !ssagen.Arch.LinkArch.CanJumpTable || base.Ctxt.Retpoline { + return false + } + if len(cc) < minCases { + return false // not enough cases for it to be worth it + } + hashes := make([]uint32, len(cc)) + // b = # of bits to use. Start with the minimum number of + // bits possible, but try a few larger sizes if needed. + b0 := bits.Len(uint(len(cc) - 1)) + for b := b0; b < b0+3; b++ { + pickI: + for i := 0; i <= 32-b; i++ { // starting bit position + // Compute the hash we'd get from all the cases, + // selecting b bits starting at bit i. + hashes = hashes[:0] + for _, c := range cc { + h := c.hash >> i & (1<> i & (1<