mirror of
https://github.com/golang/go.git
synced 2026-06-27 19:30:52 +00:00
cmd/compile: support optimizing switch statements with fallthroughs to lookup tables
Switch cases that end in a fallthrough, and the case that follows it, can't be optimized to a lookup table. Others should still be eligible for optimization. Change-Id: Iebdde2ab590f2be89ba08a2dc3326553c5a4083c Reviewed-on: https://go-review.googlesource.com/c/go/+/764440 Reviewed-by: Keith Randall <khr@golang.org> Reviewed-by: Keith Randall <khr@google.com> Reviewed-by: David Chase <drchase@google.com> LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
parent
d79a0079f5
commit
1f5c165a81
2 changed files with 41 additions and 15 deletions
|
|
@ -388,15 +388,6 @@ func tryLookupTable(sw *ir.SwitchStmt, cond ir.Node) {
|
|||
return // 64-bit switches on 32-bit archs
|
||||
}
|
||||
|
||||
// Bail out if any case uses fallthrough. Removing cases from the switch
|
||||
// would break fallthrough chains between adjacent cases.
|
||||
// TODO: we could still optimize cases that don't fall through, even if some cases do.
|
||||
for _, ncase := range sw.Cases {
|
||||
if fall, _ := endsInFallthrough(ncase.Body); fall {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
fn := ir.CurFunc
|
||||
if fn == nil || fn.Type().NumResults() != 1 {
|
||||
return // only handle single return value
|
||||
|
|
@ -427,11 +418,17 @@ func tryLookupTable(sw *ir.SwitchStmt, cond ir.Node) {
|
|||
excludeSet := make(map[int64]bool) // case values with non-const bodies
|
||||
var defaultVal ir.Node // non-nil if default returns a constant
|
||||
minVal, maxVal := int64(math.MaxInt64), int64(math.MinInt64)
|
||||
var excludeNextCase bool // true if the previous case ends in fallthrough
|
||||
|
||||
for i, ncase := range sw.Cases {
|
||||
// A case that is the target of a fallthrough must be excluded,
|
||||
// since removing it would break the fallthrough chain.
|
||||
isFallthroughTarget := excludeNextCase
|
||||
excludeNextCase, _ = endsInFallthrough(ncase.Body)
|
||||
|
||||
if len(ncase.List) == 0 {
|
||||
// Default case: check if it returns a constant (for gap filling).
|
||||
if isConstReturn(ncase) {
|
||||
if isConstReturn(ncase) && !isFallthroughTarget {
|
||||
defaultVal = ncase.Body[0].(*ir.ReturnStmt).Results[0]
|
||||
}
|
||||
continue
|
||||
|
|
@ -451,11 +448,11 @@ func tryLookupTable(sw *ir.SwitchStmt, cond ir.Node) {
|
|||
return
|
||||
}
|
||||
|
||||
if !isConstReturn(ncase) {
|
||||
// Non-const case body: exclude these values from the table
|
||||
// so the mask redirects them to the normal switch, preserving
|
||||
// Go's top-to-bottom case evaluation order. For example:
|
||||
// case 3: sideEffect(); return 30 → exclude slot 3
|
||||
if !isConstReturn(ncase) || isFallthroughTarget || excludeNextCase {
|
||||
// Non-const body, fallthrough source, or fallthrough target:
|
||||
// exclude these values from the table so the mask redirects
|
||||
// them to the normal switch, preserving Go's top-to-bottom
|
||||
// case evaluation order.
|
||||
for _, v := range vals {
|
||||
excludeSet[v] = true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,6 +76,35 @@ func squareLookup(x int) int {
|
|||
}
|
||||
}
|
||||
|
||||
// lookup tables work even when some cases use fallthrough,
|
||||
// as long as enough non-fallthrough cases return constants.
|
||||
func fallthroughLookup(x int) int {
|
||||
// amd64:`LEAQ .*\(SB\)` `MOVQ .*\(.*\)\(.*\*8\)` -`JMP \(.*\)\(.*\)$`
|
||||
// arm64:`MOVD \(R.*\)\(R.*<<3\)` -`JMP \(R.*\)$`
|
||||
switch x {
|
||||
case 1:
|
||||
return 1
|
||||
case 2:
|
||||
return 2
|
||||
case 3:
|
||||
fallthrough
|
||||
case 4:
|
||||
return 40
|
||||
case 5:
|
||||
return 5
|
||||
case 6:
|
||||
return 6
|
||||
case 7:
|
||||
return 7
|
||||
case 8:
|
||||
return 8
|
||||
case 9:
|
||||
fallthrough
|
||||
default:
|
||||
return x * x
|
||||
}
|
||||
}
|
||||
|
||||
// use lookup tables for 8+ bool-returning cases
|
||||
func boolLookup(x int) bool {
|
||||
// amd64:`LEAQ .*\(SB\)` `MOVBLZX .*\(.*\)` -`JMP \(.*\)\(.*\)$`
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue