cmd/compile: optimize switch on strings
When compiling expression switches, we try to optimize runs of
constants into binary searches. The ordering used isn't visible to the
application, so it's unimportant as long as we're consistent between
sorting and searching.
For strings, it's much cheaper to compare string lengths than strings
themselves, so instead of ordering strings by "si <= sj", we currently
order them by "len(si) < len(sj) || len(si) == len(sj) && si <= sj"
(i.e., the lexicographical ordering on the 2-tuple (len(s), s)).
However, it's also somewhat cheaper to compare strings for equality
(i.e., ==) than for ordering (i.e., <=). And if there were two or
three string constants of the same length in a switch statement, we
might unnecessarily emit ordering comparisons.
For example, given:
switch s {
case "", "1", "2", "3": // ordered by length then content
goto L
}
we currently compile this as:
if len(s) < 1 || len(s) == 1 && s <= "1" {
if s == "" { goto L }
else if s == "1" { goto L }
} else {
if s == "2" { goto L }
else if s == "3" { goto L }
}
This CL switches to using a 2-level binary search---first on len(s),
then on s itself---so that string ordering comparisons are only needed
when there are 4 or more strings of the same length. (4 being the
cut-off for when using binary search is actually worthwhile.)
So the above switch instead now compiles to:
if len(s) == 0 {
if s == "" { goto L }
} else if len(s) == 1 {
if s == "1" { goto L }
else if s == "2" { goto L }
else if s == "3" { goto L }
}
which is better optimized by walk and SSA. (Notably, because there are
only two distinct lengths and no more than three strings of any
particular length, this example ends up falling back to simply using
linear search.)
Test case by khr@ from CL 195138.
Fixes #33934.
Change-Id: I8eeebcaf7e26343223be5f443d6a97a0daf84f07
Reviewed-on: https://go-review.googlesource.com/c/go/+/195340
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-13 17:02:23 -06:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
}
|
2022-04-14 14:14:18 -06:00
|
|
|
|
|
|
|
// use jump tables for 8+ int cases
|
|
|
|
func square(x int) int {
|
|
|
|
// amd64:`JMP\s\(.*\)\(.*\)$`
|
2022-04-25 15:18:19 -06:00
|
|
|
// arm64:`MOVD\s\(R.*\)\(R.*<<3\)`,`JMP\s\(R.*\)$`
|
2022-04-14 14:14:18 -06:00
|
|
|
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\s\(.*\)\(.*\)$`
|
2022-04-25 15:18:19 -06:00
|
|
|
// arm64:`MOVD\s\(R.*\)\(R.*<<3\)`,`JMP\s\(R.*\)$`
|
2022-04-14 14:14:18 -06:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
2022-06-25 12:11:07 -06:00
|
|
|
|
|
|
|
// Use single-byte ordered comparisons for binary searching strings.
|
|
|
|
// See issue 53333.
|
|
|
|
func mimetype(ext string) string {
|
|
|
|
// amd64: `CMPB\s1\(.*\), \$104$`,-`cmpstring`
|
|
|
|
// arm64: `MOVB\s1\(R.*\), R.*$`, `CMPW\s\$104, R.*$`, -`cmpstring`
|
|
|
|
switch ext {
|
|
|
|
// amd64: `CMPL\s\(.*\), \$1836345390$`
|
|
|
|
// arm64: `CMPW\s\$1836345390, R.*$`
|
|
|
|
case ".htm":
|
|
|
|
return "A"
|
|
|
|
// amd64: `CMPL\s\(.*\), \$1953457454$`
|
|
|
|
// arm64: `CMPW\s\$1953457454, R.*$`
|
|
|
|
case ".eot":
|
|
|
|
return "B"
|
|
|
|
// amd64: `CMPL\s\(.*\), \$1735815982$`
|
|
|
|
// arm64: `CMPW\s\$1735815982, R.*$`
|
|
|
|
case ".svg":
|
|
|
|
return "C"
|
|
|
|
// amd64: `CMPL\s\(.*\), \$1718907950$`
|
|
|
|
// arm64: `CMPW\s\$1718907950, R.*$`
|
|
|
|
case ".ttf":
|
|
|
|
return "D"
|
|
|
|
default:
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
}
|