mirror of
https://github.com/golang/go.git
synced 2026-04-18 09:50:34 +00:00
Switch statement containing integer constant cases and case bodies just
returning a constant should be optimizable to a simpler and faster table
lookup instead of a jump table.
That is, a switch like this:
switch x {
case 0: return 10
case 1: return 20
case 2: return 30
case 3: return 40
default: return -1
}
Could be optimized to this:
var table = [4]int{10, 20, 30, 40}
if uint(x) < 4 { return table[x] }
return -1
The resulting code is smaller and faster, especially on platforms where
jump tables are not supported.
goos: windows
goarch: arm64
pkg: cmd/compile/internal/test
│ .\old.txt │ .\new.txt │
│ sec/op │ sec/op vs base │
SwitchLookup8Predictable-12 2.708n ± 6% 2.249n ± 5% -16.97% (p=0.000 n=10)
SwitchLookup8Unpredictable-12 8.758n ± 7% 3.272n ± 4% -62.65% (p=0.000 n=10)
SwitchLookup32Predictable-12 2.672n ± 5% 2.373n ± 6% -11.21% (p=0.000 n=10)
SwitchLookup32Unpredictable-12 9.372n ± 7% 3.385n ± 6% -63.89% (p=0.000 n=10)
geomean 4.937n 2.772n -43.84%
Fixes #78203
Change-Id: I74fa3d77ef618412951b2e5c3cb6ebc760ce4ff1
Reviewed-on: https://go-review.googlesource.com/c/go/+/756340
Reviewed-by: Keith Randall <khr@google.com>
Reviewed-by: Junyang Shao <shaojunyang@google.com>
Reviewed-by: Keith Randall <khr@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
229 lines
4.4 KiB
Go
229 lines
4.4 KiB
Go
// asmcheck
|
|
|
|
// Copyright 2019 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// These tests check code generation of switch statements.
|
|
|
|
package codegen
|
|
|
|
// see issue 33934
|
|
func f(x string) int {
|
|
// amd64:-`cmpstring`
|
|
switch x {
|
|
case "":
|
|
return -1
|
|
case "1", "2", "3":
|
|
return -2
|
|
default:
|
|
return -3
|
|
}
|
|
}
|
|
|
|
// use jump tables for 8+ string cases
|
|
// Using multiple return values prevent lookup tables.
|
|
func squareJump(x int) (int, int) {
|
|
// amd64:`JMP \(.*\)\(.*\)$`
|
|
// arm64:`MOVD \(R.*\)\(R.*<<3\)` `JMP \(R.*\)$`
|
|
// loong64: `ALSLV` `MOVV` `JMP`
|
|
switch x {
|
|
case 1:
|
|
return 1, 1
|
|
case 2:
|
|
return 4, 2
|
|
case 3:
|
|
return 9, 3
|
|
case 4:
|
|
return 16, 4
|
|
case 5:
|
|
return 25, 5
|
|
case 6:
|
|
return 36, 6
|
|
case 7:
|
|
return 49, 7
|
|
case 8:
|
|
return 64, 8
|
|
default:
|
|
return x * x, x
|
|
}
|
|
}
|
|
|
|
// use lookup tables for 8+ int cases returning constants
|
|
func squareLookup(x int) int {
|
|
// amd64:`LEAQ .*\(SB\)` `MOVQ .*\(.*\)\(.*\*8\)` -`JMP \(.*\)\(.*\)$`
|
|
// arm64:`MOVD \(R.*\)\(R.*<<3\)` -`JMP \(R.*\)$`
|
|
// loong64:`SLLV` `MOVV \(R.*\)\(R.*\)` -`ALSLV`
|
|
switch x {
|
|
case 1:
|
|
return 1
|
|
case 2:
|
|
return 4
|
|
case 3:
|
|
return 9
|
|
case 4:
|
|
return 16
|
|
case 5:
|
|
return 25
|
|
case 6:
|
|
return 36
|
|
case 7:
|
|
return 49
|
|
case 8:
|
|
return 64
|
|
default:
|
|
return x * x
|
|
}
|
|
}
|
|
|
|
// use jump tables for 8+ string lengths
|
|
func length(x string) int {
|
|
// amd64:`JMP \(.*\)\(.*\)$`
|
|
// arm64:`MOVD \(R.*\)\(R.*<<3\)` `JMP \(R.*\)$`
|
|
// loong64:`ALSLV` `MOVV` `JMP`
|
|
switch x {
|
|
case "a":
|
|
return 1
|
|
case "bb":
|
|
return 2
|
|
case "ccc":
|
|
return 3
|
|
case "dddd":
|
|
return 4
|
|
case "eeeee":
|
|
return 5
|
|
case "ffffff":
|
|
return 6
|
|
case "ggggggg":
|
|
return 7
|
|
case "hhhhhhhh":
|
|
return 8
|
|
default:
|
|
return len(x)
|
|
}
|
|
}
|
|
|
|
// Use single-byte ordered comparisons for binary searching strings.
|
|
// See issue 53333.
|
|
func mimetype(ext string) string {
|
|
// amd64: `CMPB 1\(.*\), \$104$` -`cmpstring`
|
|
// arm64: `MOVB 1\(R.*\), R.*$` `CMPW \$104, R.*$` -`cmpstring`
|
|
switch ext {
|
|
// amd64: `CMPL \(.*\), \$1836345390$`
|
|
// arm64: `MOVD \$1836345390` `CMPW R.*, R.*$`
|
|
case ".htm":
|
|
return "A"
|
|
// amd64: `CMPL \(.*\), \$1953457454$`
|
|
// arm64: `MOVD \$1953457454` `CMPW R.*, R.*$`
|
|
case ".eot":
|
|
return "B"
|
|
// amd64: `CMPL \(.*\), \$1735815982$`
|
|
// arm64: `MOVD \$1735815982` `CMPW R.*, R.*$`
|
|
case ".svg":
|
|
return "C"
|
|
// amd64: `CMPL \(.*\), \$1718907950$`
|
|
// arm64: `MOVD \$1718907950` `CMPW R.*, R.*$`
|
|
case ".ttf":
|
|
return "D"
|
|
default:
|
|
return ""
|
|
}
|
|
}
|
|
|
|
// use jump tables for type switches to concrete types.
|
|
func typeSwitch(x any) int {
|
|
// amd64:`JMP \(.*\)\(.*\)$`
|
|
// arm64:`MOVD \(R.*\)\(R.*<<3\)` `JMP \(R.*\)$`
|
|
switch x.(type) {
|
|
case int:
|
|
return 0
|
|
case int8:
|
|
return 1
|
|
case int16:
|
|
return 2
|
|
case int32:
|
|
return 3
|
|
case int64:
|
|
return 4
|
|
}
|
|
return 7
|
|
}
|
|
|
|
type I interface {
|
|
foo()
|
|
}
|
|
type J interface {
|
|
bar()
|
|
}
|
|
type IJ interface {
|
|
I
|
|
J
|
|
}
|
|
type K interface {
|
|
baz()
|
|
}
|
|
|
|
// use a runtime call for type switches to interface types.
|
|
func interfaceSwitch(x any) int {
|
|
// amd64:`CALL runtime.interfaceSwitch` `MOVL 16\(AX\)` `MOVQ 8\(.*\)(.*\*8)`
|
|
// arm64:`CALL runtime.interfaceSwitch` `LDAR` `MOVWU 16\(R0\)` `MOVD \(R.*\)\(R.*\)`
|
|
switch x.(type) {
|
|
case I:
|
|
return 1
|
|
case J:
|
|
return 2
|
|
default:
|
|
return 3
|
|
}
|
|
}
|
|
|
|
func interfaceSwitch2(x K) int {
|
|
// amd64:`CALL runtime.interfaceSwitch` `MOVL 16\(AX\)` `MOVQ 8\(.*\)(.*\*8)`
|
|
// arm64:`CALL runtime.interfaceSwitch` `LDAR` `MOVWU 16\(R0\)` `MOVD \(R.*\)\(R.*\)`
|
|
switch x.(type) {
|
|
case I:
|
|
return 1
|
|
case J:
|
|
return 2
|
|
default:
|
|
return 3
|
|
}
|
|
}
|
|
|
|
func interfaceCast(x any) int {
|
|
// amd64:`CALL runtime.typeAssert` `MOVL 16\(AX\)` `MOVQ 8\(.*\)(.*\*1)`
|
|
// arm64:`CALL runtime.typeAssert` `LDAR` `MOVWU 16\(R0\)` `MOVD \(R.*\)\(R.*\)`
|
|
if _, ok := x.(I); ok {
|
|
return 3
|
|
}
|
|
return 5
|
|
}
|
|
|
|
func interfaceCast2(x K) int {
|
|
// amd64:`CALL runtime.typeAssert` `MOVL 16\(AX\)` `MOVQ 8\(.*\)(.*\*1)`
|
|
// arm64:`CALL runtime.typeAssert` `LDAR` `MOVWU 16\(R0\)` `MOVD \(R.*\)\(R.*\)`
|
|
if _, ok := x.(I); ok {
|
|
return 3
|
|
}
|
|
return 5
|
|
}
|
|
|
|
func interfaceConv(x IJ) I {
|
|
// amd64:`CALL runtime.typeAssert` `MOVL 16\(AX\)` `MOVQ 8\(.*\)(.*\*1)`
|
|
// arm64:`CALL runtime.typeAssert` `LDAR` `MOVWU 16\(R0\)` `MOVD \(R.*\)\(R.*\)`
|
|
return x
|
|
}
|
|
|
|
// Make sure we can constant fold after inlining. See issue 71699.
|
|
func stringSwitchInlineable(s string) {
|
|
switch s {
|
|
case "foo", "bar", "baz", "goo":
|
|
default:
|
|
println("no")
|
|
}
|
|
}
|
|
func stringSwitch() {
|
|
// amd64:-"CMP" -"CALL"
|
|
// arm64:-"CMP" -"CALL"
|
|
stringSwitchInlineable("foo")
|
|
}
|