2015-02-13 14:40:36 -05:00
|
|
|
// Copyright 2009 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.
|
|
|
|
|
|
|
|
|
|
package gc
|
|
|
|
|
|
2017-03-27 14:48:24 -07:00
|
|
|
import (
|
cmd/compile: factor out Pkg, Sym, and Type into package types
- created new package cmd/compile/internal/types
- moved Pkg, Sym, Type to new package
- to break cycles, for now we need the (ugly) types/utils.go
file which contains a handful of functions that must be installed
early by the gc frontend
- to break cycles, for now we need two functions to convert between
*gc.Node and *types.Node (the latter is a dummy type)
- adjusted the gc's code to use the new package and the conversion
functions as needed
- made several Pkg, Sym, and Type methods functions as needed
- renamed constructors typ, typPtr, typArray, etc. to types.New,
types.NewPtr, types.NewArray, etc.
Passes toolstash-check -all.
Change-Id: I8adfa5e85c731645d0a7fd2030375ed6ebf54b72
Reviewed-on: https://go-review.googlesource.com/39855
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2017-04-04 17:54:02 -07:00
|
|
|
"cmd/compile/internal/types"
|
2019-09-10 20:06:51 -07:00
|
|
|
"cmd/internal/src"
|
2017-03-27 14:48:24 -07:00
|
|
|
"sort"
|
|
|
|
|
)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-27 20:44:45 +00:00
|
|
|
// typecheckswitch typechecks a switch statement.
|
|
|
|
|
func typecheckswitch(n *Node) {
|
2018-11-18 08:34:38 -08:00
|
|
|
typecheckslice(n.Ninit.Slice(), ctxStmt)
|
2015-05-26 21:30:20 -04:00
|
|
|
if n.Left != nil && n.Left.Op == OTYPESW {
|
2019-09-10 10:36:47 -07:00
|
|
|
typecheckTypeSwitch(n)
|
2015-02-27 20:44:45 +00:00
|
|
|
} else {
|
2019-09-10 10:36:47 -07:00
|
|
|
typecheckExprSwitch(n)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func typecheckTypeSwitch(n *Node) {
|
|
|
|
|
n.Left.Right = typecheck(n.Left.Right, ctxExpr)
|
|
|
|
|
t := n.Left.Right.Type
|
|
|
|
|
if t != nil && !t.IsInterface() {
|
|
|
|
|
yyerrorl(n.Pos, "cannot type switch on non-interface value %L", n.Left.Right)
|
|
|
|
|
t = nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We don't actually declare the type switch's guarded
|
|
|
|
|
// declaration itself. So if there are no cases, we won't
|
|
|
|
|
// notice that it went unused.
|
|
|
|
|
if v := n.Left.Left; v != nil && !v.isBlank() && n.List.Len() == 0 {
|
2019-10-24 21:29:30 -04:00
|
|
|
yyerrorl(v.Pos, "%v declared but not used", v.Sym)
|
2019-09-10 10:36:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var defCase, nilCase *Node
|
2019-09-10 20:06:51 -07:00
|
|
|
var ts typeSet
|
2019-09-10 10:36:47 -07:00
|
|
|
for _, ncase := range n.List.Slice() {
|
|
|
|
|
ls := ncase.List.Slice()
|
|
|
|
|
if len(ls) == 0 { // default:
|
|
|
|
|
if defCase != nil {
|
|
|
|
|
yyerrorl(ncase.Pos, "multiple defaults in switch (first at %v)", defCase.Line())
|
|
|
|
|
} else {
|
|
|
|
|
defCase = ncase
|
|
|
|
|
}
|
2015-02-27 20:44:45 +00:00
|
|
|
}
|
2019-09-10 10:36:47 -07:00
|
|
|
|
|
|
|
|
for i := range ls {
|
|
|
|
|
ls[i] = typecheck(ls[i], ctxExpr|ctxType)
|
|
|
|
|
n1 := ls[i]
|
|
|
|
|
if t == nil || n1.Type == nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var missing, have *types.Field
|
|
|
|
|
var ptr int
|
2015-02-27 20:44:45 +00:00
|
|
|
switch {
|
2019-09-10 10:36:47 -07:00
|
|
|
case n1.isNil(): // case nil:
|
|
|
|
|
if nilCase != nil {
|
|
|
|
|
yyerrorl(ncase.Pos, "multiple nil cases in type switch (first at %v)", nilCase.Line())
|
|
|
|
|
} else {
|
|
|
|
|
nilCase = ncase
|
|
|
|
|
}
|
|
|
|
|
case n1.Op != OTYPE:
|
|
|
|
|
yyerrorl(ncase.Pos, "%L is not a type", n1)
|
|
|
|
|
case !n1.Type.IsInterface() && !implements(n1.Type, t, &missing, &have, &ptr) && !missing.Broke():
|
|
|
|
|
if have != nil && !have.Broke() {
|
|
|
|
|
yyerrorl(ncase.Pos, "impossible type switch case: %L cannot have dynamic type %v"+
|
|
|
|
|
" (wrong type for %v method)\n\thave %v%S\n\twant %v%S", n.Left.Right, n1.Type, missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type)
|
|
|
|
|
} else if ptr != 0 {
|
|
|
|
|
yyerrorl(ncase.Pos, "impossible type switch case: %L cannot have dynamic type %v"+
|
|
|
|
|
" (%v method has pointer receiver)", n.Left.Right, n1.Type, missing.Sym)
|
|
|
|
|
} else {
|
|
|
|
|
yyerrorl(ncase.Pos, "impossible type switch case: %L cannot have dynamic type %v"+
|
|
|
|
|
" (missing %v method)", n.Left.Right, n1.Type, missing.Sym)
|
2016-04-01 11:22:03 -07:00
|
|
|
}
|
2015-02-27 20:44:45 +00:00
|
|
|
}
|
2019-09-10 20:06:51 -07:00
|
|
|
|
|
|
|
|
if n1.Op == OTYPE {
|
|
|
|
|
ts.add(ncase.Pos, n1.Type)
|
|
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2019-09-10 10:36:47 -07:00
|
|
|
|
|
|
|
|
if ncase.Rlist.Len() != 0 {
|
|
|
|
|
// Assign the clause variable's type.
|
|
|
|
|
vt := t
|
|
|
|
|
if len(ls) == 1 {
|
|
|
|
|
if ls[0].Op == OTYPE {
|
|
|
|
|
vt = ls[0].Type
|
|
|
|
|
} else if ls[0].Op != OLITERAL { // TODO(mdempsky): Should be !ls[0].isNil()
|
|
|
|
|
// Invalid single-type case;
|
|
|
|
|
// mark variable as broken.
|
|
|
|
|
vt = nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO(mdempsky): It should be possible to
|
|
|
|
|
// still typecheck the case body.
|
|
|
|
|
if vt == nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nvar := ncase.Rlist.First()
|
|
|
|
|
nvar.Type = vt
|
|
|
|
|
nvar = typecheck(nvar, ctxExpr|ctxAssign)
|
|
|
|
|
ncase.Rlist.SetFirst(nvar)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
typecheckslice(ncase.Nbody.Slice(), ctxStmt)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2019-09-10 10:36:47 -07:00
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2019-09-10 20:06:51 -07:00
|
|
|
type typeSet struct {
|
|
|
|
|
m map[string][]typeSetEntry
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type typeSetEntry struct {
|
|
|
|
|
pos src.XPos
|
|
|
|
|
typ *types.Type
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *typeSet) add(pos src.XPos, typ *types.Type) {
|
|
|
|
|
if s.m == nil {
|
|
|
|
|
s.m = make(map[string][]typeSetEntry)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// LongString does not uniquely identify types, so we need to
|
|
|
|
|
// disambiguate collisions with types.Identical.
|
|
|
|
|
// TODO(mdempsky): Add a method that *is* unique.
|
|
|
|
|
ls := typ.LongString()
|
|
|
|
|
prevs := s.m[ls]
|
|
|
|
|
for _, prev := range prevs {
|
|
|
|
|
if types.Identical(typ, prev.typ) {
|
|
|
|
|
yyerrorl(pos, "duplicate case %v in type switch\n\tprevious case at %s", typ, linestr(prev.pos))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
s.m[ls] = append(prevs, typeSetEntry{pos, typ})
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-10 10:36:47 -07:00
|
|
|
func typecheckExprSwitch(n *Node) {
|
|
|
|
|
t := types.Types[TBOOL]
|
|
|
|
|
if n.Left != nil {
|
|
|
|
|
n.Left = typecheck(n.Left, ctxExpr)
|
|
|
|
|
n.Left = defaultlit(n.Left, nil)
|
|
|
|
|
t = n.Left.Type
|
|
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2019-09-10 10:36:47 -07:00
|
|
|
var nilonly string
|
|
|
|
|
if t != nil {
|
|
|
|
|
switch {
|
|
|
|
|
case t.IsMap():
|
|
|
|
|
nilonly = "map"
|
|
|
|
|
case t.Etype == TFUNC:
|
|
|
|
|
nilonly = "func"
|
|
|
|
|
case t.IsSlice():
|
|
|
|
|
nilonly = "slice"
|
|
|
|
|
|
|
|
|
|
case !IsComparable(t):
|
|
|
|
|
if t.IsStruct() {
|
|
|
|
|
yyerrorl(n.Pos, "cannot switch on %L (struct containing %v cannot be compared)", n.Left, IncomparableField(t).Type)
|
2015-02-27 20:44:45 +00:00
|
|
|
} else {
|
2019-09-10 10:36:47 -07:00
|
|
|
yyerrorl(n.Pos, "cannot switch on %L", n.Left)
|
2015-02-27 20:44:45 +00:00
|
|
|
}
|
2019-09-10 10:36:47 -07:00
|
|
|
t = nil
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-02-27 20:44:45 +00:00
|
|
|
|
2019-09-10 10:36:47 -07:00
|
|
|
var defCase *Node
|
|
|
|
|
var cs constSet
|
|
|
|
|
for _, ncase := range n.List.Slice() {
|
|
|
|
|
ls := ncase.List.Slice()
|
|
|
|
|
if len(ls) == 0 { // default:
|
|
|
|
|
if defCase != nil {
|
|
|
|
|
yyerrorl(ncase.Pos, "multiple defaults in switch (first at %v)", defCase.Line())
|
|
|
|
|
} else {
|
|
|
|
|
defCase = ncase
|
2015-02-27 20:44:45 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-10 10:36:47 -07:00
|
|
|
for i := range ls {
|
|
|
|
|
setlineno(ncase)
|
|
|
|
|
ls[i] = typecheck(ls[i], ctxExpr)
|
|
|
|
|
ls[i] = defaultlit(ls[i], t)
|
|
|
|
|
n1 := ls[i]
|
|
|
|
|
if t == nil || n1.Type == nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2020-10-13 15:58:10 +02:00
|
|
|
if nilonly != "" && !n1.isNil() {
|
2019-09-10 10:36:47 -07:00
|
|
|
yyerrorl(ncase.Pos, "invalid case %v in switch (can only compare %s %v to nil)", n1, nilonly, n.Left)
|
2020-10-13 15:58:10 +02:00
|
|
|
} else if t.IsInterface() && !n1.Type.IsInterface() && !IsComparable(n1.Type) {
|
2019-09-10 10:36:47 -07:00
|
|
|
yyerrorl(ncase.Pos, "invalid case %L in switch (incomparable type)", n1)
|
2020-10-13 15:58:10 +02:00
|
|
|
} else {
|
|
|
|
|
op1, _ := assignop(n1.Type, t)
|
|
|
|
|
op2, _ := assignop(t, n1.Type)
|
|
|
|
|
if op1 == OXXX && op2 == OXXX {
|
|
|
|
|
if n.Left != nil {
|
|
|
|
|
yyerrorl(ncase.Pos, "invalid case %v in switch on %v (mismatched types %v and %v)", n1, n.Left, n1.Type, t)
|
|
|
|
|
} else {
|
|
|
|
|
yyerrorl(ncase.Pos, "invalid case %v in switch (mismatched types %v and bool)", n1, n1.Type)
|
|
|
|
|
}
|
2019-01-18 21:43:56 +01:00
|
|
|
}
|
2019-09-10 10:36:47 -07:00
|
|
|
}
|
2019-01-18 21:43:56 +01:00
|
|
|
|
2019-09-10 10:36:47 -07:00
|
|
|
// Don't check for duplicate bools. Although the spec allows it,
|
|
|
|
|
// (1) the compiler hasn't checked it in the past, so compatibility mandates it, and
|
|
|
|
|
// (2) it would disallow useful things like
|
|
|
|
|
// case GOARCH == "arm" && GOARM == "5":
|
|
|
|
|
// case GOARCH == "arm":
|
|
|
|
|
// which would both evaluate to false for non-ARM compiles.
|
|
|
|
|
if !n1.Type.IsBoolean() {
|
|
|
|
|
cs.add(ncase.Pos, n1, "case", "switch")
|
2015-02-27 20:44:45 +00:00
|
|
|
}
|
|
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2018-11-18 08:34:38 -08:00
|
|
|
typecheckslice(ncase.Nbody.Slice(), ctxStmt)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-27 20:44:45 +00:00
|
|
|
// walkswitch walks a switch statement.
|
|
|
|
|
func walkswitch(sw *Node) {
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
// Guard against double walk, see #25776.
|
|
|
|
|
if sw.List.Len() == 0 && sw.Nbody.Len() > 0 {
|
|
|
|
|
return // Was fatal, but eliminating every possible source of double-walking is hard
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
if sw.Left != nil && sw.Left.Op == OTYPESW {
|
|
|
|
|
walkTypeSwitch(sw)
|
2015-02-27 20:44:45 +00:00
|
|
|
} else {
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
walkExprSwitch(sw)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
// walkExprSwitch generates an AST implementing sw. sw is an
|
|
|
|
|
// expression switch.
|
|
|
|
|
func walkExprSwitch(sw *Node) {
|
|
|
|
|
lno := setlineno(sw)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-05-26 21:30:20 -04:00
|
|
|
cond := sw.Left
|
|
|
|
|
sw.Left = nil
|
|
|
|
|
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
// convert switch {...} to switch true {...}
|
|
|
|
|
if cond == nil {
|
|
|
|
|
cond = nodbool(true)
|
|
|
|
|
cond = typecheck(cond, ctxExpr)
|
|
|
|
|
cond = defaultlit(cond, nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2018-04-18 18:28:34 -07:00
|
|
|
// Given "switch string(byteslice)",
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
// with all cases being side-effect free,
|
2018-04-18 18:28:34 -07:00
|
|
|
// use a zero-cost alias of the byte slice.
|
|
|
|
|
// Do this before calling walkexpr on cond,
|
|
|
|
|
// because walkexpr will lower the string
|
|
|
|
|
// conversion into a runtime call.
|
|
|
|
|
// See issue 24937 for more discussion.
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
if cond.Op == OBYTES2STR && allCaseExprsAreSideEffectFree(sw) {
|
|
|
|
|
cond.Op = OBYTES2STRTMP
|
2018-04-18 18:28:34 -07:00
|
|
|
}
|
|
|
|
|
|
cmd/compile: reduce use of **Node parameters
Escape analysis has a hard time with tree-like
structures (see #13493 and #14858).
This is unlikely to change.
As a result, when invoking a function that accepts
a **Node parameter, we usually allocate a *Node
on the heap. This happens a whole lot.
This CL changes functions from taking a **Node
to acting more like append: It both modifies
the input and returns a replacement for it.
Because of the cascading nature of escape analysis,
in order to get the benefits, I had to modify
almost all such functions. The remaining functions
are in racewalk and the backend. I would be happy
to update them as well in a separate CL.
This CL was created by manually updating the
function signatures and the directly impacted
bits of code. The callsites were then automatically
updated using a bespoke script:
https://gist.github.com/josharian/046b1be7aceae244de39
For ease of reviewing and future understanding,
this CL is also broken down into four CLs,
mailed separately, which show the manual
and the automated changes separately.
They are CLs 20990, 20991, 20992, and 20993.
Passes toolstash -cmp.
name old time/op new time/op delta
Template 335ms ± 5% 324ms ± 5% -3.35% (p=0.000 n=23+24)
Unicode 176ms ± 9% 165ms ± 6% -6.12% (p=0.000 n=23+24)
GoTypes 1.10s ± 4% 1.07s ± 2% -2.77% (p=0.000 n=24+24)
Compiler 5.31s ± 3% 5.15s ± 3% -2.95% (p=0.000 n=24+24)
MakeBash 41.6s ± 1% 41.7s ± 2% ~ (p=0.586 n=23+23)
name old alloc/op new alloc/op delta
Template 63.3MB ± 0% 62.4MB ± 0% -1.36% (p=0.000 n=25+23)
Unicode 42.4MB ± 0% 41.6MB ± 0% -1.99% (p=0.000 n=24+25)
GoTypes 220MB ± 0% 217MB ± 0% -1.11% (p=0.000 n=25+25)
Compiler 994MB ± 0% 973MB ± 0% -2.08% (p=0.000 n=24+25)
name old allocs/op new allocs/op delta
Template 681k ± 0% 574k ± 0% -15.71% (p=0.000 n=24+25)
Unicode 518k ± 0% 413k ± 0% -20.34% (p=0.000 n=25+24)
GoTypes 2.08M ± 0% 1.78M ± 0% -14.62% (p=0.000 n=25+25)
Compiler 9.26M ± 0% 7.64M ± 0% -17.48% (p=0.000 n=25+25)
name old text-bytes new text-bytes delta
HelloSize 578k ± 0% 578k ± 0% ~ (all samples are equal)
CmdGoSize 6.46M ± 0% 6.46M ± 0% ~ (all samples are equal)
name old data-bytes new data-bytes delta
HelloSize 128k ± 0% 128k ± 0% ~ (all samples are equal)
CmdGoSize 281k ± 0% 281k ± 0% ~ (all samples are equal)
name old exe-bytes new exe-bytes delta
HelloSize 921k ± 0% 921k ± 0% ~ (all samples are equal)
CmdGoSize 9.86M ± 0% 9.86M ± 0% ~ (all samples are equal)
Change-Id: I277d95bd56d51c166ef7f560647aeaa092f3f475
Reviewed-on: https://go-review.googlesource.com/20959
Reviewed-by: Dave Cheney <dave@cheney.net>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2016-03-20 08:03:31 -07:00
|
|
|
cond = walkexpr(cond, &sw.Ninit)
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
if cond.Op != OLITERAL {
|
|
|
|
|
cond = copyexpr(cond, cond.Type, &sw.Nbody)
|
2015-02-27 20:44:45 +00:00
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
lineno = lno
|
|
|
|
|
|
|
|
|
|
s := exprSwitch{
|
|
|
|
|
exprname: cond,
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
var defaultGoto *Node
|
|
|
|
|
var body Nodes
|
|
|
|
|
for _, ncase := range sw.List.Slice() {
|
|
|
|
|
label := autolabel(".s")
|
|
|
|
|
jmp := npos(ncase.Pos, nodSym(OGOTO, nil, label))
|
|
|
|
|
|
|
|
|
|
// Process case dispatch.
|
|
|
|
|
if ncase.List.Len() == 0 {
|
|
|
|
|
if defaultGoto != nil {
|
|
|
|
|
Fatalf("duplicate default case not detected during typechecking")
|
2017-09-27 11:30:10 +01:00
|
|
|
}
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
defaultGoto = jmp
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
for _, n1 := range ncase.List.Slice() {
|
|
|
|
|
s.Add(ncase.Pos, n1, jmp)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Process body.
|
|
|
|
|
body.Append(npos(ncase.Pos, nodSym(OLABEL, nil, label)))
|
|
|
|
|
body.Append(ncase.Nbody.Slice()...)
|
2019-09-25 15:20:10 -04:00
|
|
|
if fall, pos := hasFall(ncase.Nbody.Slice()); !fall {
|
|
|
|
|
br := nod(OBREAK, nil, nil)
|
|
|
|
|
br.Pos = pos
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
body.Append(br)
|
|
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
sw.List.Set(nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
if defaultGoto == nil {
|
2019-09-25 15:20:10 -04:00
|
|
|
br := nod(OBREAK, nil, nil)
|
|
|
|
|
br.Pos = br.Pos.WithNotStmt()
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
defaultGoto = br
|
2015-02-27 20:44:45 +00:00
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
s.Emit(&sw.Nbody)
|
|
|
|
|
sw.Nbody.Append(defaultGoto)
|
|
|
|
|
sw.Nbody.AppendNodes(&body)
|
|
|
|
|
walkstmtlist(sw.Nbody.Slice())
|
|
|
|
|
}
|
2015-02-27 20:44:45 +00:00
|
|
|
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
// An exprSwitch walks an expression switch.
|
|
|
|
|
type exprSwitch struct {
|
|
|
|
|
exprname *Node // value being switched on
|
2015-02-27 20:44:45 +00:00
|
|
|
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
done Nodes
|
|
|
|
|
clauses []exprClause
|
|
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
type exprClause struct {
|
|
|
|
|
pos src.XPos
|
|
|
|
|
lo, hi *Node
|
|
|
|
|
jmp *Node
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
func (s *exprSwitch) Add(pos src.XPos, expr, jmp *Node) {
|
|
|
|
|
c := exprClause{pos: pos, lo: expr, hi: expr, jmp: jmp}
|
|
|
|
|
if okforcmp[s.exprname.Type.Etype] && expr.Op == OLITERAL {
|
|
|
|
|
s.clauses = append(s.clauses, c)
|
2015-02-13 14:40:36 -05:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
s.flush()
|
|
|
|
|
s.clauses = append(s.clauses, c)
|
|
|
|
|
s.flush()
|
|
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
func (s *exprSwitch) Emit(out *Nodes) {
|
|
|
|
|
s.flush()
|
|
|
|
|
out.AppendNodes(&s.done)
|
|
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
func (s *exprSwitch) flush() {
|
|
|
|
|
cc := s.clauses
|
|
|
|
|
s.clauses = nil
|
|
|
|
|
if len(cc) == 0 {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Caution: If len(cc) == 1, then cc[0] might not an OLITERAL.
|
|
|
|
|
// The code below is structured to implicitly handle this case
|
|
|
|
|
// (e.g., sort.Slice doesn't need to invoke the less function
|
|
|
|
|
// when there's only a single slice element).
|
|
|
|
|
|
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 16:02:23 -07:00
|
|
|
if s.exprname.Type.IsString() && len(cc) >= 2 {
|
|
|
|
|
// Sort strings by length and then by value. It is
|
|
|
|
|
// much cheaper to compare lengths than values, and
|
|
|
|
|
// all we need here is consistency. We respect this
|
|
|
|
|
// sorting below.
|
|
|
|
|
sort.Slice(cc, func(i, j int) bool {
|
2020-10-12 15:02:59 +02:00
|
|
|
si := cc[i].lo.StringVal()
|
|
|
|
|
sj := cc[j].lo.StringVal()
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
if len(si) != len(sj) {
|
|
|
|
|
return len(si) < len(sj)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
return si < sj
|
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 16:02:23 -07:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// runLen returns the string length associated with a
|
|
|
|
|
// particular run of exprClauses.
|
2020-10-12 15:02:59 +02:00
|
|
|
runLen := func(run []exprClause) int64 { return int64(len(run[0].lo.StringVal())) }
|
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 16:02:23 -07:00
|
|
|
|
|
|
|
|
// Collapse runs of consecutive strings with the same length.
|
|
|
|
|
var runs [][]exprClause
|
|
|
|
|
start := 0
|
|
|
|
|
for i := 1; i < len(cc); i++ {
|
|
|
|
|
if runLen(cc[start:]) != runLen(cc[i:]) {
|
|
|
|
|
runs = append(runs, cc[start:i])
|
|
|
|
|
start = i
|
|
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
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 16:02:23 -07:00
|
|
|
runs = append(runs, cc[start:])
|
|
|
|
|
|
|
|
|
|
// Perform two-level binary search.
|
|
|
|
|
nlen := nod(OLEN, s.exprname, nil)
|
|
|
|
|
binarySearch(len(runs), &s.done,
|
|
|
|
|
func(i int) *Node {
|
|
|
|
|
return nod(OLE, nlen, nodintconst(runLen(runs[i-1])))
|
|
|
|
|
},
|
|
|
|
|
func(i int, nif *Node) {
|
|
|
|
|
run := runs[i]
|
|
|
|
|
nif.Left = nod(OEQ, nlen, nodintconst(runLen(run)))
|
|
|
|
|
s.search(run, &nif.Nbody)
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
return
|
|
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
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 16:02:23 -07:00
|
|
|
sort.Slice(cc, func(i, j int) bool {
|
|
|
|
|
return compareOp(cc[i].lo.Val(), OLT, cc[j].lo.Val())
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// Merge consecutive integer cases.
|
|
|
|
|
if s.exprname.Type.IsInteger() {
|
|
|
|
|
merged := cc[:1]
|
|
|
|
|
for _, c := range cc[1:] {
|
|
|
|
|
last := &merged[len(merged)-1]
|
2020-10-12 15:02:59 +02:00
|
|
|
if last.jmp == c.jmp && last.hi.Int64Val()+1 == c.lo.Int64Val() {
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
last.hi = c.lo
|
|
|
|
|
} else {
|
|
|
|
|
merged = append(merged, c)
|
2016-02-27 14:31:33 -08:00
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
cc = merged
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
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 16:02:23 -07:00
|
|
|
s.search(cc, &s.done)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *exprSwitch) search(cc []exprClause, out *Nodes) {
|
|
|
|
|
binarySearch(len(cc), out,
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
func(i int) *Node {
|
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 16:02:23 -07:00
|
|
|
return nod(OLE, s.exprname, cc[i-1].hi)
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
},
|
2019-09-13 15:13:22 -07:00
|
|
|
func(i int, nif *Node) {
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
c := &cc[i]
|
2019-09-13 15:13:22 -07:00
|
|
|
nif.Left = c.test(s.exprname)
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
nif.Nbody.Set1(c.jmp)
|
|
|
|
|
},
|
|
|
|
|
)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
func (c *exprClause) test(exprname *Node) *Node {
|
|
|
|
|
// Integer range.
|
|
|
|
|
if c.hi != c.lo {
|
|
|
|
|
low := nodl(c.pos, OGE, exprname, c.lo)
|
|
|
|
|
high := nodl(c.pos, OLE, exprname, c.hi)
|
|
|
|
|
return nodl(c.pos, OANDAND, low, high)
|
2016-06-03 12:05:32 -07:00
|
|
|
}
|
|
|
|
|
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
// Optimize "switch true { ...}" and "switch false { ... }".
|
|
|
|
|
if Isconst(exprname, CTBOOL) && !c.lo.Type.IsInterface() {
|
2020-10-12 15:02:59 +02:00
|
|
|
if exprname.BoolVal() {
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
return c.lo
|
|
|
|
|
} else {
|
|
|
|
|
return nodl(c.pos, ONOT, c.lo, nil)
|
|
|
|
|
}
|
2016-06-03 12:05:32 -07:00
|
|
|
}
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
|
|
|
|
|
return nodl(c.pos, OEQ, exprname, c.lo)
|
2016-06-03 12:05:32 -07:00
|
|
|
}
|
|
|
|
|
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
func allCaseExprsAreSideEffectFree(sw *Node) bool {
|
|
|
|
|
// In theory, we could be more aggressive, allowing any
|
|
|
|
|
// side-effect-free expressions in cases, but it's a bit
|
|
|
|
|
// tricky because some of that information is unavailable due
|
|
|
|
|
// to the introduction of temporaries during order.
|
|
|
|
|
// Restricting to constants is simple and probably powerful
|
|
|
|
|
// enough.
|
|
|
|
|
|
|
|
|
|
for _, ncase := range sw.List.Slice() {
|
2019-09-17 18:32:04 -07:00
|
|
|
if ncase.Op != OCASE {
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
Fatalf("switch string(byteslice) bad op: %v", ncase.Op)
|
2016-05-31 13:11:15 -07:00
|
|
|
}
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
for _, v := range ncase.List.Slice() {
|
|
|
|
|
if v.Op != OLITERAL {
|
|
|
|
|
return false
|
|
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2016-05-31 13:11:15 -07:00
|
|
|
}
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
return true
|
2016-06-01 11:34:28 -07:00
|
|
|
}
|
|
|
|
|
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
// hasFall reports whether stmts ends with a "fallthrough" statement.
|
2019-09-25 15:20:10 -04:00
|
|
|
func hasFall(stmts []*Node) (bool, src.XPos) {
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
// Search backwards for the index of the fallthrough
|
|
|
|
|
// statement. Do not assume it'll be in the last
|
|
|
|
|
// position, since in some cases (e.g. when the statement
|
|
|
|
|
// list contains autotmp_ variables), one or more OVARKILL
|
|
|
|
|
// nodes will be at the end of the list.
|
2015-05-26 21:30:20 -04:00
|
|
|
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
i := len(stmts) - 1
|
|
|
|
|
for i >= 0 && stmts[i].Op == OVARKILL {
|
|
|
|
|
i--
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2019-09-25 15:20:10 -04:00
|
|
|
if i < 0 {
|
|
|
|
|
return false, src.NoXPos
|
|
|
|
|
}
|
|
|
|
|
return stmts[i].Op == OFALL, stmts[i].Pos
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
// walkTypeSwitch generates an AST that implements sw, where sw is a
|
|
|
|
|
// type switch.
|
|
|
|
|
func walkTypeSwitch(sw *Node) {
|
|
|
|
|
var s typeSwitch
|
|
|
|
|
s.facename = sw.Left.Right
|
|
|
|
|
sw.Left = nil
|
2015-02-13 14:40:36 -05:00
|
|
|
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
s.facename = walkexpr(s.facename, &sw.Ninit)
|
|
|
|
|
s.facename = copyexpr(s.facename, s.facename.Type, &sw.Nbody)
|
cmd/compile: factor out Pkg, Sym, and Type into package types
- created new package cmd/compile/internal/types
- moved Pkg, Sym, Type to new package
- to break cycles, for now we need the (ugly) types/utils.go
file which contains a handful of functions that must be installed
early by the gc frontend
- to break cycles, for now we need two functions to convert between
*gc.Node and *types.Node (the latter is a dummy type)
- adjusted the gc's code to use the new package and the conversion
functions as needed
- made several Pkg, Sym, and Type methods functions as needed
- renamed constructors typ, typPtr, typArray, etc. to types.New,
types.NewPtr, types.NewArray, etc.
Passes toolstash-check -all.
Change-Id: I8adfa5e85c731645d0a7fd2030375ed6ebf54b72
Reviewed-on: https://go-review.googlesource.com/39855
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2017-04-04 17:54:02 -07:00
|
|
|
s.okname = temp(types.Types[TBOOL])
|
2015-02-13 14:40:36 -05:00
|
|
|
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
// Get interface descriptor word.
|
|
|
|
|
// For empty interfaces this will be the type.
|
|
|
|
|
// For non-empty interfaces this will be the itab.
|
|
|
|
|
itab := nod(OITAB, s.facename, nil)
|
2016-02-21 20:43:14 -08:00
|
|
|
|
|
|
|
|
// For empty interfaces, do:
|
|
|
|
|
// if e._type == nil {
|
|
|
|
|
// do nil case if it exists, otherwise default
|
|
|
|
|
// }
|
|
|
|
|
// h := e._type.hash
|
|
|
|
|
// Use a similar strategy for non-empty interfaces.
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
ifNil := nod(OIF, nil, nil)
|
|
|
|
|
ifNil.Left = nod(OEQ, itab, nodnil())
|
2019-10-03 13:44:33 -04:00
|
|
|
lineno = lineno.WithNotStmt() // disable statement marks after the first check.
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
ifNil.Left = typecheck(ifNil.Left, ctxExpr)
|
|
|
|
|
ifNil.Left = defaultlit(ifNil.Left, nil)
|
|
|
|
|
// ifNil.Nbody assigned at end.
|
|
|
|
|
sw.Nbody.Append(ifNil)
|
2016-02-21 20:43:14 -08:00
|
|
|
|
2017-01-03 16:15:38 -08:00
|
|
|
// Load hash from type or itab.
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
dotHash := nodSym(ODOTPTR, itab, nil)
|
|
|
|
|
dotHash.Type = types.Types[TUINT32]
|
|
|
|
|
dotHash.SetTypecheck(1)
|
|
|
|
|
if s.facename.Type.IsEmptyInterface() {
|
|
|
|
|
dotHash.Xoffset = int64(2 * Widthptr) // offset of hash in runtime._type
|
2017-01-03 16:15:38 -08:00
|
|
|
} else {
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
dotHash.Xoffset = int64(2 * Widthptr) // offset of hash in runtime.itab
|
2017-01-03 16:15:38 -08:00
|
|
|
}
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
dotHash.SetBounded(true) // guaranteed not to fault
|
|
|
|
|
s.hashname = copyexpr(dotHash, dotHash.Type, &sw.Nbody)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
br := nod(OBREAK, nil, nil)
|
|
|
|
|
var defaultGoto, nilGoto *Node
|
|
|
|
|
var body Nodes
|
|
|
|
|
for _, ncase := range sw.List.Slice() {
|
|
|
|
|
var caseVar *Node
|
|
|
|
|
if ncase.Rlist.Len() != 0 {
|
|
|
|
|
caseVar = ncase.Rlist.First()
|
|
|
|
|
}
|
2016-05-31 13:11:15 -07:00
|
|
|
|
cmd/compile: improve generated code for concrete cases in type switches
Consider
switch x:= x.(type) {
case int:
// int stmts
case error:
// error stmts
}
Prior to this change, we lowered this roughly as:
if x, ok := x.(int); ok {
// int stmts
} else if x, ok := x.(error); ok {
// error stmts
}
x, ok := x.(error) is implemented with a call to runtime.assertE2I2 or runtime.assertI2I2.
x, ok := x.(int) generates inline code that checks whether x has type int,
and populates x and ok as appropriate. We then immediately branch again on ok.
The shortcircuit pass in the SSA backend is designed to recognize situations
like this, in which we are immediately branching on a bool value
that we just calculated with a branch.
However, the shortcircuit pass has limitations when the intermediate state has phis.
In this case, the phi value is x (the int).
CL 222923 improved the situation, but many cases are still unhandled.
I have further improvements in progress, which is how I found this particular problem,
but they are expensive, and may or may not see the light of day.
In the common case of a lone concrete type in a type switch case,
it is easier and cheaper to simply lower a different way, roughly:
if _, ok := x.(int); ok {
x := x.(int)
// int stmts
}
Instead of using a type assertion, though, we extract the value of x
from the interface directly.
This removes the need to track x (the int) across the branch on ok,
which removes the phi, which lets the shortcircuit pass do its job.
Benchmarks for encoding/binary show improvements, as well as some
wild swings on the super fast benchmarks (alignment effects?):
name old time/op new time/op delta
ReadSlice1000Int32s-8 5.25µs ± 2% 4.87µs ± 3% -7.11% (p=0.000 n=44+49)
ReadStruct-8 451ns ± 2% 417ns ± 2% -7.39% (p=0.000 n=45+46)
WriteStruct-8 412ns ± 2% 405ns ± 3% -1.58% (p=0.000 n=46+48)
ReadInts-8 296ns ± 8% 275ns ± 3% -7.23% (p=0.000 n=48+50)
WriteInts-8 324ns ± 1% 318ns ± 2% -1.67% (p=0.000 n=44+49)
WriteSlice1000Int32s-8 5.21µs ± 2% 4.92µs ± 1% -5.67% (p=0.000 n=46+44)
PutUint16-8 0.58ns ± 2% 0.59ns ± 2% +0.63% (p=0.000 n=49+49)
PutUint32-8 0.87ns ± 1% 0.58ns ± 1% -33.10% (p=0.000 n=46+44)
PutUint64-8 0.66ns ± 2% 0.87ns ± 2% +33.07% (p=0.000 n=47+48)
LittleEndianPutUint16-8 0.86ns ± 2% 0.87ns ± 2% +0.55% (p=0.003 n=47+50)
LittleEndianPutUint32-8 0.87ns ± 1% 0.87ns ± 1% ~ (p=0.547 n=45+47)
LittleEndianPutUint64-8 0.87ns ± 2% 0.87ns ± 1% ~ (p=0.451 n=46+47)
ReadFloats-8 79.8ns ± 5% 75.9ns ± 2% -4.83% (p=0.000 n=50+47)
WriteFloats-8 89.3ns ± 1% 88.9ns ± 1% -0.48% (p=0.000 n=46+44)
ReadSlice1000Float32s-8 5.51µs ± 1% 4.87µs ± 2% -11.74% (p=0.000 n=47+46)
WriteSlice1000Float32s-8 5.51µs ± 1% 4.93µs ± 1% -10.60% (p=0.000 n=48+47)
PutUvarint32-8 25.9ns ± 2% 24.0ns ± 2% -7.02% (p=0.000 n=48+50)
PutUvarint64-8 75.1ns ± 1% 61.5ns ± 2% -18.12% (p=0.000 n=45+47)
[Geo mean] 57.3ns 54.3ns -5.33%
Despite the rarity of type switches, this generates noticeably smaller binaries.
file before after Δ %
addr2line 4413296 4409200 -4096 -0.093%
api 5982648 5962168 -20480 -0.342%
cgo 4854168 4833688 -20480 -0.422%
compile 19694784 19682560 -12224 -0.062%
cover 5278008 5265720 -12288 -0.233%
doc 4694824 4682536 -12288 -0.262%
fix 3411336 3394952 -16384 -0.480%
link 6721496 6717400 -4096 -0.061%
nm 4371152 4358864 -12288 -0.281%
objdump 4760960 4752768 -8192 -0.172%
pprof 14810820 14790340 -20480 -0.138%
trace 11681076 11668788 -12288 -0.105%
vet 8285464 8244504 -40960 -0.494%
total 115824120 115627576 -196544 -0.170%
Compiler performance is marginally improved (note that go/types has many type switches):
name old alloc/op new alloc/op delta
Template 35.0MB ± 0% 35.0MB ± 0% +0.09% (p=0.008 n=5+5)
Unicode 28.5MB ± 0% 28.5MB ± 0% ~ (p=0.548 n=5+5)
GoTypes 114MB ± 0% 114MB ± 0% -0.76% (p=0.008 n=5+5)
Compiler 541MB ± 0% 541MB ± 0% -0.03% (p=0.008 n=5+5)
SSA 1.17GB ± 0% 1.17GB ± 0% ~ (p=0.841 n=5+5)
Flate 21.9MB ± 0% 21.9MB ± 0% ~ (p=0.421 n=5+5)
GoParser 26.9MB ± 0% 26.9MB ± 0% ~ (p=0.222 n=5+5)
Reflect 74.6MB ± 0% 74.6MB ± 0% ~ (p=1.000 n=5+5)
Tar 32.9MB ± 0% 32.8MB ± 0% ~ (p=0.056 n=5+5)
XML 42.4MB ± 0% 42.1MB ± 0% -0.77% (p=0.008 n=5+5)
[Geo mean] 73.2MB 73.1MB -0.15%
name old allocs/op new allocs/op delta
Template 377k ± 0% 377k ± 0% +0.06% (p=0.008 n=5+5)
Unicode 354k ± 0% 354k ± 0% ~ (p=0.095 n=5+5)
GoTypes 1.31M ± 0% 1.30M ± 0% -0.73% (p=0.008 n=5+5)
Compiler 5.44M ± 0% 5.44M ± 0% -0.04% (p=0.008 n=5+5)
SSA 11.7M ± 0% 11.7M ± 0% ~ (p=1.000 n=5+5)
Flate 239k ± 0% 239k ± 0% ~ (p=1.000 n=5+5)
GoParser 302k ± 0% 302k ± 0% -0.04% (p=0.008 n=5+5)
Reflect 977k ± 0% 977k ± 0% ~ (p=0.690 n=5+5)
Tar 346k ± 0% 346k ± 0% ~ (p=0.889 n=5+5)
XML 431k ± 0% 430k ± 0% -0.25% (p=0.008 n=5+5)
[Geo mean] 806k 806k -0.10%
For packages with many type switches, this considerably shrinks function text size.
Some examples:
file before after Δ %
encoding/binary.s 30726 29504 -1222 -3.977%
go/printer.s 77597 76005 -1592 -2.052%
cmd/vendor/golang.org/x/tools/go/ast/astutil.s 65704 63318 -2386 -3.631%
cmd/vendor/golang.org/x/tools/go/analysis/passes/unreachable.s 8047 7714 -333 -4.138%
Text size regressions are rare.
Change-Id: Ic10982bbb04876250eaa5bfee97990141ae5fc28
Reviewed-on: https://go-review.googlesource.com/c/go/+/228106
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Keith Randall <khr@golang.org>
2020-04-12 17:34:33 -07:00
|
|
|
// For single-type cases with an interface type,
|
|
|
|
|
// we initialize the case variable as part of the type assertion.
|
|
|
|
|
// In other cases, we initialize it in the body.
|
|
|
|
|
var singleType *types.Type
|
|
|
|
|
if ncase.List.Len() == 1 && ncase.List.First().Op == OTYPE {
|
|
|
|
|
singleType = ncase.List.First().Type
|
|
|
|
|
}
|
|
|
|
|
caseVarInitialized := false
|
2015-02-13 14:40:36 -05:00
|
|
|
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
label := autolabel(".s")
|
|
|
|
|
jmp := npos(ncase.Pos, nodSym(OGOTO, nil, label))
|
2019-09-13 15:13:22 -07:00
|
|
|
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
if ncase.List.Len() == 0 { // default:
|
|
|
|
|
if defaultGoto != nil {
|
|
|
|
|
Fatalf("duplicate default case not detected during typechecking")
|
|
|
|
|
}
|
|
|
|
|
defaultGoto = jmp
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
for _, n1 := range ncase.List.Slice() {
|
|
|
|
|
if n1.isNil() { // case nil:
|
|
|
|
|
if nilGoto != nil {
|
|
|
|
|
Fatalf("duplicate nil case not detected during typechecking")
|
|
|
|
|
}
|
|
|
|
|
nilGoto = jmp
|
|
|
|
|
continue
|
|
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
cmd/compile: improve generated code for concrete cases in type switches
Consider
switch x:= x.(type) {
case int:
// int stmts
case error:
// error stmts
}
Prior to this change, we lowered this roughly as:
if x, ok := x.(int); ok {
// int stmts
} else if x, ok := x.(error); ok {
// error stmts
}
x, ok := x.(error) is implemented with a call to runtime.assertE2I2 or runtime.assertI2I2.
x, ok := x.(int) generates inline code that checks whether x has type int,
and populates x and ok as appropriate. We then immediately branch again on ok.
The shortcircuit pass in the SSA backend is designed to recognize situations
like this, in which we are immediately branching on a bool value
that we just calculated with a branch.
However, the shortcircuit pass has limitations when the intermediate state has phis.
In this case, the phi value is x (the int).
CL 222923 improved the situation, but many cases are still unhandled.
I have further improvements in progress, which is how I found this particular problem,
but they are expensive, and may or may not see the light of day.
In the common case of a lone concrete type in a type switch case,
it is easier and cheaper to simply lower a different way, roughly:
if _, ok := x.(int); ok {
x := x.(int)
// int stmts
}
Instead of using a type assertion, though, we extract the value of x
from the interface directly.
This removes the need to track x (the int) across the branch on ok,
which removes the phi, which lets the shortcircuit pass do its job.
Benchmarks for encoding/binary show improvements, as well as some
wild swings on the super fast benchmarks (alignment effects?):
name old time/op new time/op delta
ReadSlice1000Int32s-8 5.25µs ± 2% 4.87µs ± 3% -7.11% (p=0.000 n=44+49)
ReadStruct-8 451ns ± 2% 417ns ± 2% -7.39% (p=0.000 n=45+46)
WriteStruct-8 412ns ± 2% 405ns ± 3% -1.58% (p=0.000 n=46+48)
ReadInts-8 296ns ± 8% 275ns ± 3% -7.23% (p=0.000 n=48+50)
WriteInts-8 324ns ± 1% 318ns ± 2% -1.67% (p=0.000 n=44+49)
WriteSlice1000Int32s-8 5.21µs ± 2% 4.92µs ± 1% -5.67% (p=0.000 n=46+44)
PutUint16-8 0.58ns ± 2% 0.59ns ± 2% +0.63% (p=0.000 n=49+49)
PutUint32-8 0.87ns ± 1% 0.58ns ± 1% -33.10% (p=0.000 n=46+44)
PutUint64-8 0.66ns ± 2% 0.87ns ± 2% +33.07% (p=0.000 n=47+48)
LittleEndianPutUint16-8 0.86ns ± 2% 0.87ns ± 2% +0.55% (p=0.003 n=47+50)
LittleEndianPutUint32-8 0.87ns ± 1% 0.87ns ± 1% ~ (p=0.547 n=45+47)
LittleEndianPutUint64-8 0.87ns ± 2% 0.87ns ± 1% ~ (p=0.451 n=46+47)
ReadFloats-8 79.8ns ± 5% 75.9ns ± 2% -4.83% (p=0.000 n=50+47)
WriteFloats-8 89.3ns ± 1% 88.9ns ± 1% -0.48% (p=0.000 n=46+44)
ReadSlice1000Float32s-8 5.51µs ± 1% 4.87µs ± 2% -11.74% (p=0.000 n=47+46)
WriteSlice1000Float32s-8 5.51µs ± 1% 4.93µs ± 1% -10.60% (p=0.000 n=48+47)
PutUvarint32-8 25.9ns ± 2% 24.0ns ± 2% -7.02% (p=0.000 n=48+50)
PutUvarint64-8 75.1ns ± 1% 61.5ns ± 2% -18.12% (p=0.000 n=45+47)
[Geo mean] 57.3ns 54.3ns -5.33%
Despite the rarity of type switches, this generates noticeably smaller binaries.
file before after Δ %
addr2line 4413296 4409200 -4096 -0.093%
api 5982648 5962168 -20480 -0.342%
cgo 4854168 4833688 -20480 -0.422%
compile 19694784 19682560 -12224 -0.062%
cover 5278008 5265720 -12288 -0.233%
doc 4694824 4682536 -12288 -0.262%
fix 3411336 3394952 -16384 -0.480%
link 6721496 6717400 -4096 -0.061%
nm 4371152 4358864 -12288 -0.281%
objdump 4760960 4752768 -8192 -0.172%
pprof 14810820 14790340 -20480 -0.138%
trace 11681076 11668788 -12288 -0.105%
vet 8285464 8244504 -40960 -0.494%
total 115824120 115627576 -196544 -0.170%
Compiler performance is marginally improved (note that go/types has many type switches):
name old alloc/op new alloc/op delta
Template 35.0MB ± 0% 35.0MB ± 0% +0.09% (p=0.008 n=5+5)
Unicode 28.5MB ± 0% 28.5MB ± 0% ~ (p=0.548 n=5+5)
GoTypes 114MB ± 0% 114MB ± 0% -0.76% (p=0.008 n=5+5)
Compiler 541MB ± 0% 541MB ± 0% -0.03% (p=0.008 n=5+5)
SSA 1.17GB ± 0% 1.17GB ± 0% ~ (p=0.841 n=5+5)
Flate 21.9MB ± 0% 21.9MB ± 0% ~ (p=0.421 n=5+5)
GoParser 26.9MB ± 0% 26.9MB ± 0% ~ (p=0.222 n=5+5)
Reflect 74.6MB ± 0% 74.6MB ± 0% ~ (p=1.000 n=5+5)
Tar 32.9MB ± 0% 32.8MB ± 0% ~ (p=0.056 n=5+5)
XML 42.4MB ± 0% 42.1MB ± 0% -0.77% (p=0.008 n=5+5)
[Geo mean] 73.2MB 73.1MB -0.15%
name old allocs/op new allocs/op delta
Template 377k ± 0% 377k ± 0% +0.06% (p=0.008 n=5+5)
Unicode 354k ± 0% 354k ± 0% ~ (p=0.095 n=5+5)
GoTypes 1.31M ± 0% 1.30M ± 0% -0.73% (p=0.008 n=5+5)
Compiler 5.44M ± 0% 5.44M ± 0% -0.04% (p=0.008 n=5+5)
SSA 11.7M ± 0% 11.7M ± 0% ~ (p=1.000 n=5+5)
Flate 239k ± 0% 239k ± 0% ~ (p=1.000 n=5+5)
GoParser 302k ± 0% 302k ± 0% -0.04% (p=0.008 n=5+5)
Reflect 977k ± 0% 977k ± 0% ~ (p=0.690 n=5+5)
Tar 346k ± 0% 346k ± 0% ~ (p=0.889 n=5+5)
XML 431k ± 0% 430k ± 0% -0.25% (p=0.008 n=5+5)
[Geo mean] 806k 806k -0.10%
For packages with many type switches, this considerably shrinks function text size.
Some examples:
file before after Δ %
encoding/binary.s 30726 29504 -1222 -3.977%
go/printer.s 77597 76005 -1592 -2.052%
cmd/vendor/golang.org/x/tools/go/ast/astutil.s 65704 63318 -2386 -3.631%
cmd/vendor/golang.org/x/tools/go/analysis/passes/unreachable.s 8047 7714 -333 -4.138%
Text size regressions are rare.
Change-Id: Ic10982bbb04876250eaa5bfee97990141ae5fc28
Reviewed-on: https://go-review.googlesource.com/c/go/+/228106
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Keith Randall <khr@golang.org>
2020-04-12 17:34:33 -07:00
|
|
|
if singleType != nil && singleType.IsInterface() {
|
2020-04-13 23:28:32 -07:00
|
|
|
s.Add(ncase.Pos, n1.Type, caseVar, jmp)
|
cmd/compile: improve generated code for concrete cases in type switches
Consider
switch x:= x.(type) {
case int:
// int stmts
case error:
// error stmts
}
Prior to this change, we lowered this roughly as:
if x, ok := x.(int); ok {
// int stmts
} else if x, ok := x.(error); ok {
// error stmts
}
x, ok := x.(error) is implemented with a call to runtime.assertE2I2 or runtime.assertI2I2.
x, ok := x.(int) generates inline code that checks whether x has type int,
and populates x and ok as appropriate. We then immediately branch again on ok.
The shortcircuit pass in the SSA backend is designed to recognize situations
like this, in which we are immediately branching on a bool value
that we just calculated with a branch.
However, the shortcircuit pass has limitations when the intermediate state has phis.
In this case, the phi value is x (the int).
CL 222923 improved the situation, but many cases are still unhandled.
I have further improvements in progress, which is how I found this particular problem,
but they are expensive, and may or may not see the light of day.
In the common case of a lone concrete type in a type switch case,
it is easier and cheaper to simply lower a different way, roughly:
if _, ok := x.(int); ok {
x := x.(int)
// int stmts
}
Instead of using a type assertion, though, we extract the value of x
from the interface directly.
This removes the need to track x (the int) across the branch on ok,
which removes the phi, which lets the shortcircuit pass do its job.
Benchmarks for encoding/binary show improvements, as well as some
wild swings on the super fast benchmarks (alignment effects?):
name old time/op new time/op delta
ReadSlice1000Int32s-8 5.25µs ± 2% 4.87µs ± 3% -7.11% (p=0.000 n=44+49)
ReadStruct-8 451ns ± 2% 417ns ± 2% -7.39% (p=0.000 n=45+46)
WriteStruct-8 412ns ± 2% 405ns ± 3% -1.58% (p=0.000 n=46+48)
ReadInts-8 296ns ± 8% 275ns ± 3% -7.23% (p=0.000 n=48+50)
WriteInts-8 324ns ± 1% 318ns ± 2% -1.67% (p=0.000 n=44+49)
WriteSlice1000Int32s-8 5.21µs ± 2% 4.92µs ± 1% -5.67% (p=0.000 n=46+44)
PutUint16-8 0.58ns ± 2% 0.59ns ± 2% +0.63% (p=0.000 n=49+49)
PutUint32-8 0.87ns ± 1% 0.58ns ± 1% -33.10% (p=0.000 n=46+44)
PutUint64-8 0.66ns ± 2% 0.87ns ± 2% +33.07% (p=0.000 n=47+48)
LittleEndianPutUint16-8 0.86ns ± 2% 0.87ns ± 2% +0.55% (p=0.003 n=47+50)
LittleEndianPutUint32-8 0.87ns ± 1% 0.87ns ± 1% ~ (p=0.547 n=45+47)
LittleEndianPutUint64-8 0.87ns ± 2% 0.87ns ± 1% ~ (p=0.451 n=46+47)
ReadFloats-8 79.8ns ± 5% 75.9ns ± 2% -4.83% (p=0.000 n=50+47)
WriteFloats-8 89.3ns ± 1% 88.9ns ± 1% -0.48% (p=0.000 n=46+44)
ReadSlice1000Float32s-8 5.51µs ± 1% 4.87µs ± 2% -11.74% (p=0.000 n=47+46)
WriteSlice1000Float32s-8 5.51µs ± 1% 4.93µs ± 1% -10.60% (p=0.000 n=48+47)
PutUvarint32-8 25.9ns ± 2% 24.0ns ± 2% -7.02% (p=0.000 n=48+50)
PutUvarint64-8 75.1ns ± 1% 61.5ns ± 2% -18.12% (p=0.000 n=45+47)
[Geo mean] 57.3ns 54.3ns -5.33%
Despite the rarity of type switches, this generates noticeably smaller binaries.
file before after Δ %
addr2line 4413296 4409200 -4096 -0.093%
api 5982648 5962168 -20480 -0.342%
cgo 4854168 4833688 -20480 -0.422%
compile 19694784 19682560 -12224 -0.062%
cover 5278008 5265720 -12288 -0.233%
doc 4694824 4682536 -12288 -0.262%
fix 3411336 3394952 -16384 -0.480%
link 6721496 6717400 -4096 -0.061%
nm 4371152 4358864 -12288 -0.281%
objdump 4760960 4752768 -8192 -0.172%
pprof 14810820 14790340 -20480 -0.138%
trace 11681076 11668788 -12288 -0.105%
vet 8285464 8244504 -40960 -0.494%
total 115824120 115627576 -196544 -0.170%
Compiler performance is marginally improved (note that go/types has many type switches):
name old alloc/op new alloc/op delta
Template 35.0MB ± 0% 35.0MB ± 0% +0.09% (p=0.008 n=5+5)
Unicode 28.5MB ± 0% 28.5MB ± 0% ~ (p=0.548 n=5+5)
GoTypes 114MB ± 0% 114MB ± 0% -0.76% (p=0.008 n=5+5)
Compiler 541MB ± 0% 541MB ± 0% -0.03% (p=0.008 n=5+5)
SSA 1.17GB ± 0% 1.17GB ± 0% ~ (p=0.841 n=5+5)
Flate 21.9MB ± 0% 21.9MB ± 0% ~ (p=0.421 n=5+5)
GoParser 26.9MB ± 0% 26.9MB ± 0% ~ (p=0.222 n=5+5)
Reflect 74.6MB ± 0% 74.6MB ± 0% ~ (p=1.000 n=5+5)
Tar 32.9MB ± 0% 32.8MB ± 0% ~ (p=0.056 n=5+5)
XML 42.4MB ± 0% 42.1MB ± 0% -0.77% (p=0.008 n=5+5)
[Geo mean] 73.2MB 73.1MB -0.15%
name old allocs/op new allocs/op delta
Template 377k ± 0% 377k ± 0% +0.06% (p=0.008 n=5+5)
Unicode 354k ± 0% 354k ± 0% ~ (p=0.095 n=5+5)
GoTypes 1.31M ± 0% 1.30M ± 0% -0.73% (p=0.008 n=5+5)
Compiler 5.44M ± 0% 5.44M ± 0% -0.04% (p=0.008 n=5+5)
SSA 11.7M ± 0% 11.7M ± 0% ~ (p=1.000 n=5+5)
Flate 239k ± 0% 239k ± 0% ~ (p=1.000 n=5+5)
GoParser 302k ± 0% 302k ± 0% -0.04% (p=0.008 n=5+5)
Reflect 977k ± 0% 977k ± 0% ~ (p=0.690 n=5+5)
Tar 346k ± 0% 346k ± 0% ~ (p=0.889 n=5+5)
XML 431k ± 0% 430k ± 0% -0.25% (p=0.008 n=5+5)
[Geo mean] 806k 806k -0.10%
For packages with many type switches, this considerably shrinks function text size.
Some examples:
file before after Δ %
encoding/binary.s 30726 29504 -1222 -3.977%
go/printer.s 77597 76005 -1592 -2.052%
cmd/vendor/golang.org/x/tools/go/ast/astutil.s 65704 63318 -2386 -3.631%
cmd/vendor/golang.org/x/tools/go/analysis/passes/unreachable.s 8047 7714 -333 -4.138%
Text size regressions are rare.
Change-Id: Ic10982bbb04876250eaa5bfee97990141ae5fc28
Reviewed-on: https://go-review.googlesource.com/c/go/+/228106
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Keith Randall <khr@golang.org>
2020-04-12 17:34:33 -07:00
|
|
|
caseVarInitialized = true
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
} else {
|
2020-04-13 23:28:32 -07:00
|
|
|
s.Add(ncase.Pos, n1.Type, nil, jmp)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
body.Append(npos(ncase.Pos, nodSym(OLABEL, nil, label)))
|
cmd/compile: improve generated code for concrete cases in type switches
Consider
switch x:= x.(type) {
case int:
// int stmts
case error:
// error stmts
}
Prior to this change, we lowered this roughly as:
if x, ok := x.(int); ok {
// int stmts
} else if x, ok := x.(error); ok {
// error stmts
}
x, ok := x.(error) is implemented with a call to runtime.assertE2I2 or runtime.assertI2I2.
x, ok := x.(int) generates inline code that checks whether x has type int,
and populates x and ok as appropriate. We then immediately branch again on ok.
The shortcircuit pass in the SSA backend is designed to recognize situations
like this, in which we are immediately branching on a bool value
that we just calculated with a branch.
However, the shortcircuit pass has limitations when the intermediate state has phis.
In this case, the phi value is x (the int).
CL 222923 improved the situation, but many cases are still unhandled.
I have further improvements in progress, which is how I found this particular problem,
but they are expensive, and may or may not see the light of day.
In the common case of a lone concrete type in a type switch case,
it is easier and cheaper to simply lower a different way, roughly:
if _, ok := x.(int); ok {
x := x.(int)
// int stmts
}
Instead of using a type assertion, though, we extract the value of x
from the interface directly.
This removes the need to track x (the int) across the branch on ok,
which removes the phi, which lets the shortcircuit pass do its job.
Benchmarks for encoding/binary show improvements, as well as some
wild swings on the super fast benchmarks (alignment effects?):
name old time/op new time/op delta
ReadSlice1000Int32s-8 5.25µs ± 2% 4.87µs ± 3% -7.11% (p=0.000 n=44+49)
ReadStruct-8 451ns ± 2% 417ns ± 2% -7.39% (p=0.000 n=45+46)
WriteStruct-8 412ns ± 2% 405ns ± 3% -1.58% (p=0.000 n=46+48)
ReadInts-8 296ns ± 8% 275ns ± 3% -7.23% (p=0.000 n=48+50)
WriteInts-8 324ns ± 1% 318ns ± 2% -1.67% (p=0.000 n=44+49)
WriteSlice1000Int32s-8 5.21µs ± 2% 4.92µs ± 1% -5.67% (p=0.000 n=46+44)
PutUint16-8 0.58ns ± 2% 0.59ns ± 2% +0.63% (p=0.000 n=49+49)
PutUint32-8 0.87ns ± 1% 0.58ns ± 1% -33.10% (p=0.000 n=46+44)
PutUint64-8 0.66ns ± 2% 0.87ns ± 2% +33.07% (p=0.000 n=47+48)
LittleEndianPutUint16-8 0.86ns ± 2% 0.87ns ± 2% +0.55% (p=0.003 n=47+50)
LittleEndianPutUint32-8 0.87ns ± 1% 0.87ns ± 1% ~ (p=0.547 n=45+47)
LittleEndianPutUint64-8 0.87ns ± 2% 0.87ns ± 1% ~ (p=0.451 n=46+47)
ReadFloats-8 79.8ns ± 5% 75.9ns ± 2% -4.83% (p=0.000 n=50+47)
WriteFloats-8 89.3ns ± 1% 88.9ns ± 1% -0.48% (p=0.000 n=46+44)
ReadSlice1000Float32s-8 5.51µs ± 1% 4.87µs ± 2% -11.74% (p=0.000 n=47+46)
WriteSlice1000Float32s-8 5.51µs ± 1% 4.93µs ± 1% -10.60% (p=0.000 n=48+47)
PutUvarint32-8 25.9ns ± 2% 24.0ns ± 2% -7.02% (p=0.000 n=48+50)
PutUvarint64-8 75.1ns ± 1% 61.5ns ± 2% -18.12% (p=0.000 n=45+47)
[Geo mean] 57.3ns 54.3ns -5.33%
Despite the rarity of type switches, this generates noticeably smaller binaries.
file before after Δ %
addr2line 4413296 4409200 -4096 -0.093%
api 5982648 5962168 -20480 -0.342%
cgo 4854168 4833688 -20480 -0.422%
compile 19694784 19682560 -12224 -0.062%
cover 5278008 5265720 -12288 -0.233%
doc 4694824 4682536 -12288 -0.262%
fix 3411336 3394952 -16384 -0.480%
link 6721496 6717400 -4096 -0.061%
nm 4371152 4358864 -12288 -0.281%
objdump 4760960 4752768 -8192 -0.172%
pprof 14810820 14790340 -20480 -0.138%
trace 11681076 11668788 -12288 -0.105%
vet 8285464 8244504 -40960 -0.494%
total 115824120 115627576 -196544 -0.170%
Compiler performance is marginally improved (note that go/types has many type switches):
name old alloc/op new alloc/op delta
Template 35.0MB ± 0% 35.0MB ± 0% +0.09% (p=0.008 n=5+5)
Unicode 28.5MB ± 0% 28.5MB ± 0% ~ (p=0.548 n=5+5)
GoTypes 114MB ± 0% 114MB ± 0% -0.76% (p=0.008 n=5+5)
Compiler 541MB ± 0% 541MB ± 0% -0.03% (p=0.008 n=5+5)
SSA 1.17GB ± 0% 1.17GB ± 0% ~ (p=0.841 n=5+5)
Flate 21.9MB ± 0% 21.9MB ± 0% ~ (p=0.421 n=5+5)
GoParser 26.9MB ± 0% 26.9MB ± 0% ~ (p=0.222 n=5+5)
Reflect 74.6MB ± 0% 74.6MB ± 0% ~ (p=1.000 n=5+5)
Tar 32.9MB ± 0% 32.8MB ± 0% ~ (p=0.056 n=5+5)
XML 42.4MB ± 0% 42.1MB ± 0% -0.77% (p=0.008 n=5+5)
[Geo mean] 73.2MB 73.1MB -0.15%
name old allocs/op new allocs/op delta
Template 377k ± 0% 377k ± 0% +0.06% (p=0.008 n=5+5)
Unicode 354k ± 0% 354k ± 0% ~ (p=0.095 n=5+5)
GoTypes 1.31M ± 0% 1.30M ± 0% -0.73% (p=0.008 n=5+5)
Compiler 5.44M ± 0% 5.44M ± 0% -0.04% (p=0.008 n=5+5)
SSA 11.7M ± 0% 11.7M ± 0% ~ (p=1.000 n=5+5)
Flate 239k ± 0% 239k ± 0% ~ (p=1.000 n=5+5)
GoParser 302k ± 0% 302k ± 0% -0.04% (p=0.008 n=5+5)
Reflect 977k ± 0% 977k ± 0% ~ (p=0.690 n=5+5)
Tar 346k ± 0% 346k ± 0% ~ (p=0.889 n=5+5)
XML 431k ± 0% 430k ± 0% -0.25% (p=0.008 n=5+5)
[Geo mean] 806k 806k -0.10%
For packages with many type switches, this considerably shrinks function text size.
Some examples:
file before after Δ %
encoding/binary.s 30726 29504 -1222 -3.977%
go/printer.s 77597 76005 -1592 -2.052%
cmd/vendor/golang.org/x/tools/go/ast/astutil.s 65704 63318 -2386 -3.631%
cmd/vendor/golang.org/x/tools/go/analysis/passes/unreachable.s 8047 7714 -333 -4.138%
Text size regressions are rare.
Change-Id: Ic10982bbb04876250eaa5bfee97990141ae5fc28
Reviewed-on: https://go-review.googlesource.com/c/go/+/228106
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Keith Randall <khr@golang.org>
2020-04-12 17:34:33 -07:00
|
|
|
if caseVar != nil && !caseVarInitialized {
|
|
|
|
|
val := s.facename
|
|
|
|
|
if singleType != nil {
|
|
|
|
|
// We have a single concrete type. Extract the data.
|
|
|
|
|
if singleType.IsInterface() {
|
|
|
|
|
Fatalf("singleType interface should have been handled in Add")
|
|
|
|
|
}
|
2020-04-13 23:29:17 -07:00
|
|
|
val = ifaceData(ncase.Pos, s.facename, singleType)
|
cmd/compile: improve generated code for concrete cases in type switches
Consider
switch x:= x.(type) {
case int:
// int stmts
case error:
// error stmts
}
Prior to this change, we lowered this roughly as:
if x, ok := x.(int); ok {
// int stmts
} else if x, ok := x.(error); ok {
// error stmts
}
x, ok := x.(error) is implemented with a call to runtime.assertE2I2 or runtime.assertI2I2.
x, ok := x.(int) generates inline code that checks whether x has type int,
and populates x and ok as appropriate. We then immediately branch again on ok.
The shortcircuit pass in the SSA backend is designed to recognize situations
like this, in which we are immediately branching on a bool value
that we just calculated with a branch.
However, the shortcircuit pass has limitations when the intermediate state has phis.
In this case, the phi value is x (the int).
CL 222923 improved the situation, but many cases are still unhandled.
I have further improvements in progress, which is how I found this particular problem,
but they are expensive, and may or may not see the light of day.
In the common case of a lone concrete type in a type switch case,
it is easier and cheaper to simply lower a different way, roughly:
if _, ok := x.(int); ok {
x := x.(int)
// int stmts
}
Instead of using a type assertion, though, we extract the value of x
from the interface directly.
This removes the need to track x (the int) across the branch on ok,
which removes the phi, which lets the shortcircuit pass do its job.
Benchmarks for encoding/binary show improvements, as well as some
wild swings on the super fast benchmarks (alignment effects?):
name old time/op new time/op delta
ReadSlice1000Int32s-8 5.25µs ± 2% 4.87µs ± 3% -7.11% (p=0.000 n=44+49)
ReadStruct-8 451ns ± 2% 417ns ± 2% -7.39% (p=0.000 n=45+46)
WriteStruct-8 412ns ± 2% 405ns ± 3% -1.58% (p=0.000 n=46+48)
ReadInts-8 296ns ± 8% 275ns ± 3% -7.23% (p=0.000 n=48+50)
WriteInts-8 324ns ± 1% 318ns ± 2% -1.67% (p=0.000 n=44+49)
WriteSlice1000Int32s-8 5.21µs ± 2% 4.92µs ± 1% -5.67% (p=0.000 n=46+44)
PutUint16-8 0.58ns ± 2% 0.59ns ± 2% +0.63% (p=0.000 n=49+49)
PutUint32-8 0.87ns ± 1% 0.58ns ± 1% -33.10% (p=0.000 n=46+44)
PutUint64-8 0.66ns ± 2% 0.87ns ± 2% +33.07% (p=0.000 n=47+48)
LittleEndianPutUint16-8 0.86ns ± 2% 0.87ns ± 2% +0.55% (p=0.003 n=47+50)
LittleEndianPutUint32-8 0.87ns ± 1% 0.87ns ± 1% ~ (p=0.547 n=45+47)
LittleEndianPutUint64-8 0.87ns ± 2% 0.87ns ± 1% ~ (p=0.451 n=46+47)
ReadFloats-8 79.8ns ± 5% 75.9ns ± 2% -4.83% (p=0.000 n=50+47)
WriteFloats-8 89.3ns ± 1% 88.9ns ± 1% -0.48% (p=0.000 n=46+44)
ReadSlice1000Float32s-8 5.51µs ± 1% 4.87µs ± 2% -11.74% (p=0.000 n=47+46)
WriteSlice1000Float32s-8 5.51µs ± 1% 4.93µs ± 1% -10.60% (p=0.000 n=48+47)
PutUvarint32-8 25.9ns ± 2% 24.0ns ± 2% -7.02% (p=0.000 n=48+50)
PutUvarint64-8 75.1ns ± 1% 61.5ns ± 2% -18.12% (p=0.000 n=45+47)
[Geo mean] 57.3ns 54.3ns -5.33%
Despite the rarity of type switches, this generates noticeably smaller binaries.
file before after Δ %
addr2line 4413296 4409200 -4096 -0.093%
api 5982648 5962168 -20480 -0.342%
cgo 4854168 4833688 -20480 -0.422%
compile 19694784 19682560 -12224 -0.062%
cover 5278008 5265720 -12288 -0.233%
doc 4694824 4682536 -12288 -0.262%
fix 3411336 3394952 -16384 -0.480%
link 6721496 6717400 -4096 -0.061%
nm 4371152 4358864 -12288 -0.281%
objdump 4760960 4752768 -8192 -0.172%
pprof 14810820 14790340 -20480 -0.138%
trace 11681076 11668788 -12288 -0.105%
vet 8285464 8244504 -40960 -0.494%
total 115824120 115627576 -196544 -0.170%
Compiler performance is marginally improved (note that go/types has many type switches):
name old alloc/op new alloc/op delta
Template 35.0MB ± 0% 35.0MB ± 0% +0.09% (p=0.008 n=5+5)
Unicode 28.5MB ± 0% 28.5MB ± 0% ~ (p=0.548 n=5+5)
GoTypes 114MB ± 0% 114MB ± 0% -0.76% (p=0.008 n=5+5)
Compiler 541MB ± 0% 541MB ± 0% -0.03% (p=0.008 n=5+5)
SSA 1.17GB ± 0% 1.17GB ± 0% ~ (p=0.841 n=5+5)
Flate 21.9MB ± 0% 21.9MB ± 0% ~ (p=0.421 n=5+5)
GoParser 26.9MB ± 0% 26.9MB ± 0% ~ (p=0.222 n=5+5)
Reflect 74.6MB ± 0% 74.6MB ± 0% ~ (p=1.000 n=5+5)
Tar 32.9MB ± 0% 32.8MB ± 0% ~ (p=0.056 n=5+5)
XML 42.4MB ± 0% 42.1MB ± 0% -0.77% (p=0.008 n=5+5)
[Geo mean] 73.2MB 73.1MB -0.15%
name old allocs/op new allocs/op delta
Template 377k ± 0% 377k ± 0% +0.06% (p=0.008 n=5+5)
Unicode 354k ± 0% 354k ± 0% ~ (p=0.095 n=5+5)
GoTypes 1.31M ± 0% 1.30M ± 0% -0.73% (p=0.008 n=5+5)
Compiler 5.44M ± 0% 5.44M ± 0% -0.04% (p=0.008 n=5+5)
SSA 11.7M ± 0% 11.7M ± 0% ~ (p=1.000 n=5+5)
Flate 239k ± 0% 239k ± 0% ~ (p=1.000 n=5+5)
GoParser 302k ± 0% 302k ± 0% -0.04% (p=0.008 n=5+5)
Reflect 977k ± 0% 977k ± 0% ~ (p=0.690 n=5+5)
Tar 346k ± 0% 346k ± 0% ~ (p=0.889 n=5+5)
XML 431k ± 0% 430k ± 0% -0.25% (p=0.008 n=5+5)
[Geo mean] 806k 806k -0.10%
For packages with many type switches, this considerably shrinks function text size.
Some examples:
file before after Δ %
encoding/binary.s 30726 29504 -1222 -3.977%
go/printer.s 77597 76005 -1592 -2.052%
cmd/vendor/golang.org/x/tools/go/ast/astutil.s 65704 63318 -2386 -3.631%
cmd/vendor/golang.org/x/tools/go/analysis/passes/unreachable.s 8047 7714 -333 -4.138%
Text size regressions are rare.
Change-Id: Ic10982bbb04876250eaa5bfee97990141ae5fc28
Reviewed-on: https://go-review.googlesource.com/c/go/+/228106
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Keith Randall <khr@golang.org>
2020-04-12 17:34:33 -07:00
|
|
|
}
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
l := []*Node{
|
|
|
|
|
nodl(ncase.Pos, ODCL, caseVar, nil),
|
cmd/compile: improve generated code for concrete cases in type switches
Consider
switch x:= x.(type) {
case int:
// int stmts
case error:
// error stmts
}
Prior to this change, we lowered this roughly as:
if x, ok := x.(int); ok {
// int stmts
} else if x, ok := x.(error); ok {
// error stmts
}
x, ok := x.(error) is implemented with a call to runtime.assertE2I2 or runtime.assertI2I2.
x, ok := x.(int) generates inline code that checks whether x has type int,
and populates x and ok as appropriate. We then immediately branch again on ok.
The shortcircuit pass in the SSA backend is designed to recognize situations
like this, in which we are immediately branching on a bool value
that we just calculated with a branch.
However, the shortcircuit pass has limitations when the intermediate state has phis.
In this case, the phi value is x (the int).
CL 222923 improved the situation, but many cases are still unhandled.
I have further improvements in progress, which is how I found this particular problem,
but they are expensive, and may or may not see the light of day.
In the common case of a lone concrete type in a type switch case,
it is easier and cheaper to simply lower a different way, roughly:
if _, ok := x.(int); ok {
x := x.(int)
// int stmts
}
Instead of using a type assertion, though, we extract the value of x
from the interface directly.
This removes the need to track x (the int) across the branch on ok,
which removes the phi, which lets the shortcircuit pass do its job.
Benchmarks for encoding/binary show improvements, as well as some
wild swings on the super fast benchmarks (alignment effects?):
name old time/op new time/op delta
ReadSlice1000Int32s-8 5.25µs ± 2% 4.87µs ± 3% -7.11% (p=0.000 n=44+49)
ReadStruct-8 451ns ± 2% 417ns ± 2% -7.39% (p=0.000 n=45+46)
WriteStruct-8 412ns ± 2% 405ns ± 3% -1.58% (p=0.000 n=46+48)
ReadInts-8 296ns ± 8% 275ns ± 3% -7.23% (p=0.000 n=48+50)
WriteInts-8 324ns ± 1% 318ns ± 2% -1.67% (p=0.000 n=44+49)
WriteSlice1000Int32s-8 5.21µs ± 2% 4.92µs ± 1% -5.67% (p=0.000 n=46+44)
PutUint16-8 0.58ns ± 2% 0.59ns ± 2% +0.63% (p=0.000 n=49+49)
PutUint32-8 0.87ns ± 1% 0.58ns ± 1% -33.10% (p=0.000 n=46+44)
PutUint64-8 0.66ns ± 2% 0.87ns ± 2% +33.07% (p=0.000 n=47+48)
LittleEndianPutUint16-8 0.86ns ± 2% 0.87ns ± 2% +0.55% (p=0.003 n=47+50)
LittleEndianPutUint32-8 0.87ns ± 1% 0.87ns ± 1% ~ (p=0.547 n=45+47)
LittleEndianPutUint64-8 0.87ns ± 2% 0.87ns ± 1% ~ (p=0.451 n=46+47)
ReadFloats-8 79.8ns ± 5% 75.9ns ± 2% -4.83% (p=0.000 n=50+47)
WriteFloats-8 89.3ns ± 1% 88.9ns ± 1% -0.48% (p=0.000 n=46+44)
ReadSlice1000Float32s-8 5.51µs ± 1% 4.87µs ± 2% -11.74% (p=0.000 n=47+46)
WriteSlice1000Float32s-8 5.51µs ± 1% 4.93µs ± 1% -10.60% (p=0.000 n=48+47)
PutUvarint32-8 25.9ns ± 2% 24.0ns ± 2% -7.02% (p=0.000 n=48+50)
PutUvarint64-8 75.1ns ± 1% 61.5ns ± 2% -18.12% (p=0.000 n=45+47)
[Geo mean] 57.3ns 54.3ns -5.33%
Despite the rarity of type switches, this generates noticeably smaller binaries.
file before after Δ %
addr2line 4413296 4409200 -4096 -0.093%
api 5982648 5962168 -20480 -0.342%
cgo 4854168 4833688 -20480 -0.422%
compile 19694784 19682560 -12224 -0.062%
cover 5278008 5265720 -12288 -0.233%
doc 4694824 4682536 -12288 -0.262%
fix 3411336 3394952 -16384 -0.480%
link 6721496 6717400 -4096 -0.061%
nm 4371152 4358864 -12288 -0.281%
objdump 4760960 4752768 -8192 -0.172%
pprof 14810820 14790340 -20480 -0.138%
trace 11681076 11668788 -12288 -0.105%
vet 8285464 8244504 -40960 -0.494%
total 115824120 115627576 -196544 -0.170%
Compiler performance is marginally improved (note that go/types has many type switches):
name old alloc/op new alloc/op delta
Template 35.0MB ± 0% 35.0MB ± 0% +0.09% (p=0.008 n=5+5)
Unicode 28.5MB ± 0% 28.5MB ± 0% ~ (p=0.548 n=5+5)
GoTypes 114MB ± 0% 114MB ± 0% -0.76% (p=0.008 n=5+5)
Compiler 541MB ± 0% 541MB ± 0% -0.03% (p=0.008 n=5+5)
SSA 1.17GB ± 0% 1.17GB ± 0% ~ (p=0.841 n=5+5)
Flate 21.9MB ± 0% 21.9MB ± 0% ~ (p=0.421 n=5+5)
GoParser 26.9MB ± 0% 26.9MB ± 0% ~ (p=0.222 n=5+5)
Reflect 74.6MB ± 0% 74.6MB ± 0% ~ (p=1.000 n=5+5)
Tar 32.9MB ± 0% 32.8MB ± 0% ~ (p=0.056 n=5+5)
XML 42.4MB ± 0% 42.1MB ± 0% -0.77% (p=0.008 n=5+5)
[Geo mean] 73.2MB 73.1MB -0.15%
name old allocs/op new allocs/op delta
Template 377k ± 0% 377k ± 0% +0.06% (p=0.008 n=5+5)
Unicode 354k ± 0% 354k ± 0% ~ (p=0.095 n=5+5)
GoTypes 1.31M ± 0% 1.30M ± 0% -0.73% (p=0.008 n=5+5)
Compiler 5.44M ± 0% 5.44M ± 0% -0.04% (p=0.008 n=5+5)
SSA 11.7M ± 0% 11.7M ± 0% ~ (p=1.000 n=5+5)
Flate 239k ± 0% 239k ± 0% ~ (p=1.000 n=5+5)
GoParser 302k ± 0% 302k ± 0% -0.04% (p=0.008 n=5+5)
Reflect 977k ± 0% 977k ± 0% ~ (p=0.690 n=5+5)
Tar 346k ± 0% 346k ± 0% ~ (p=0.889 n=5+5)
XML 431k ± 0% 430k ± 0% -0.25% (p=0.008 n=5+5)
[Geo mean] 806k 806k -0.10%
For packages with many type switches, this considerably shrinks function text size.
Some examples:
file before after Δ %
encoding/binary.s 30726 29504 -1222 -3.977%
go/printer.s 77597 76005 -1592 -2.052%
cmd/vendor/golang.org/x/tools/go/ast/astutil.s 65704 63318 -2386 -3.631%
cmd/vendor/golang.org/x/tools/go/analysis/passes/unreachable.s 8047 7714 -333 -4.138%
Text size regressions are rare.
Change-Id: Ic10982bbb04876250eaa5bfee97990141ae5fc28
Reviewed-on: https://go-review.googlesource.com/c/go/+/228106
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Keith Randall <khr@golang.org>
2020-04-12 17:34:33 -07:00
|
|
|
nodl(ncase.Pos, OAS, caseVar, val),
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
typecheckslice(l, ctxStmt)
|
|
|
|
|
body.Append(l...)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
body.Append(ncase.Nbody.Slice()...)
|
|
|
|
|
body.Append(br)
|
|
|
|
|
}
|
|
|
|
|
sw.List.Set(nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
if defaultGoto == nil {
|
|
|
|
|
defaultGoto = br
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2019-10-07 13:33:39 -07:00
|
|
|
if nilGoto == nil {
|
|
|
|
|
nilGoto = defaultGoto
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2019-10-07 13:33:39 -07:00
|
|
|
ifNil.Nbody.Set1(nilGoto)
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
|
|
|
|
|
s.Emit(&sw.Nbody)
|
|
|
|
|
sw.Nbody.Append(defaultGoto)
|
|
|
|
|
sw.Nbody.AppendNodes(&body)
|
|
|
|
|
|
|
|
|
|
walkstmtlist(sw.Nbody.Slice())
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
// A typeSwitch walks a type switch.
|
|
|
|
|
type typeSwitch struct {
|
|
|
|
|
// Temporary variables (i.e., ONAMEs) used by type switch dispatch logic:
|
|
|
|
|
facename *Node // value being type-switched on
|
|
|
|
|
hashname *Node // type hash of the value being type-switched on
|
|
|
|
|
okname *Node // boolean used for comma-ok type assertions
|
|
|
|
|
|
|
|
|
|
done Nodes
|
|
|
|
|
clauses []typeClause
|
2015-02-27 20:44:45 +00:00
|
|
|
}
|
|
|
|
|
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
type typeClause struct {
|
|
|
|
|
hash uint32
|
|
|
|
|
body Nodes
|
2015-02-27 20:44:45 +00:00
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2020-04-13 23:28:32 -07:00
|
|
|
func (s *typeSwitch) Add(pos src.XPos, typ *types.Type, caseVar, jmp *Node) {
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
var body Nodes
|
|
|
|
|
if caseVar != nil {
|
|
|
|
|
l := []*Node{
|
2020-04-13 23:28:32 -07:00
|
|
|
nodl(pos, ODCL, caseVar, nil),
|
|
|
|
|
nodl(pos, OAS, caseVar, nil),
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
}
|
|
|
|
|
typecheckslice(l, ctxStmt)
|
|
|
|
|
body.Append(l...)
|
cmd/compile: recognize integer ranges in switch statements
Consider a switch statement like:
switch x {
case 1:
// ...
case 2, 3, 4, 5, 6:
// ...
case 5:
// ...
}
Prior to this CL, the generated code treated
2, 3, 4, 5, and 6 independently in a binary search.
With this CL, the generated code checks whether
2 <= x && x <= 6.
walkinrange then optimizes that range check
into a single unsigned comparison.
Experiments suggest that the best min range size
is 2, using binary size as a proxy for optimization.
Binary sizes before/after this CL:
cmd/compile: 14209728 / 14165360
cmd/go: 9543100 / 9539004
Change-Id: If2f7fb97ca80468fa70351ef540866200c4c996c
Reviewed-on: https://go-review.googlesource.com/26770
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2016-06-17 16:27:23 -07:00
|
|
|
} else {
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
caseVar = nblank
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// cv, ok = iface.(type)
|
2020-04-13 23:28:32 -07:00
|
|
|
as := nodl(pos, OAS2, nil, nil)
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
as.List.Set2(caseVar, s.okname) // cv, ok =
|
2020-04-13 23:28:32 -07:00
|
|
|
dot := nodl(pos, ODOTTYPE, s.facename, nil)
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
dot.Type = typ // iface.(type)
|
|
|
|
|
as.Rlist.Set1(dot)
|
|
|
|
|
as = typecheck(as, ctxStmt)
|
|
|
|
|
as = walkexpr(as, &body)
|
|
|
|
|
body.Append(as)
|
|
|
|
|
|
|
|
|
|
// if ok { goto label }
|
2020-04-13 23:28:32 -07:00
|
|
|
nif := nodl(pos, OIF, nil, nil)
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
nif.Left = s.okname
|
|
|
|
|
nif.Nbody.Set1(jmp)
|
|
|
|
|
body.Append(nif)
|
|
|
|
|
|
|
|
|
|
if !typ.IsInterface() {
|
|
|
|
|
s.clauses = append(s.clauses, typeClause{
|
|
|
|
|
hash: typehash(typ),
|
|
|
|
|
body: body,
|
|
|
|
|
})
|
|
|
|
|
return
|
cmd/compile: recognize integer ranges in switch statements
Consider a switch statement like:
switch x {
case 1:
// ...
case 2, 3, 4, 5, 6:
// ...
case 5:
// ...
}
Prior to this CL, the generated code treated
2, 3, 4, 5, and 6 independently in a binary search.
With this CL, the generated code checks whether
2 <= x && x <= 6.
walkinrange then optimizes that range check
into a single unsigned comparison.
Experiments suggest that the best min range size
is 2, using binary size as a proxy for optimization.
Binary sizes before/after this CL:
cmd/compile: 14209728 / 14165360
cmd/go: 9543100 / 9539004
Change-Id: If2f7fb97ca80468fa70351ef540866200c4c996c
Reviewed-on: https://go-review.googlesource.com/26770
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2016-06-17 16:27:23 -07:00
|
|
|
}
|
|
|
|
|
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
s.flush()
|
|
|
|
|
s.done.AppendNodes(&body)
|
|
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
func (s *typeSwitch) Emit(out *Nodes) {
|
|
|
|
|
s.flush()
|
|
|
|
|
out.AppendNodes(&s.done)
|
2015-02-27 20:44:45 +00:00
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
func (s *typeSwitch) flush() {
|
|
|
|
|
cc := s.clauses
|
|
|
|
|
s.clauses = nil
|
|
|
|
|
if len(cc) == 0 {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sort.Slice(cc, func(i, j int) bool { return cc[i].hash < cc[j].hash })
|
2015-02-13 14:40:36 -05:00
|
|
|
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
// Combine adjacent cases with the same hash.
|
|
|
|
|
merged := cc[:1]
|
|
|
|
|
for _, c := range cc[1:] {
|
|
|
|
|
last := &merged[len(merged)-1]
|
|
|
|
|
if last.hash == c.hash {
|
|
|
|
|
last.body.AppendNodes(&c.body)
|
|
|
|
|
} else {
|
|
|
|
|
merged = append(merged, c)
|
|
|
|
|
}
|
2015-02-27 20:44:45 +00:00
|
|
|
}
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
cc = merged
|
|
|
|
|
|
|
|
|
|
binarySearch(len(cc), &s.done,
|
|
|
|
|
func(i int) *Node {
|
|
|
|
|
return nod(OLE, s.hashname, nodintconst(int64(cc[i-1].hash)))
|
|
|
|
|
},
|
2019-09-13 15:13:22 -07:00
|
|
|
func(i int, nif *Node) {
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
// TODO(mdempsky): Omit hash equality check if
|
|
|
|
|
// there's only one type.
|
|
|
|
|
c := cc[i]
|
2019-09-13 15:13:22 -07:00
|
|
|
nif.Left = nod(OEQ, s.hashname, nodintconst(int64(c.hash)))
|
|
|
|
|
nif.Nbody.AppendNodes(&c.body)
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
},
|
|
|
|
|
)
|
2015-02-27 20:44:45 +00:00
|
|
|
}
|
cmd/compile: recognize integer ranges in switch statements
Consider a switch statement like:
switch x {
case 1:
// ...
case 2, 3, 4, 5, 6:
// ...
case 5:
// ...
}
Prior to this CL, the generated code treated
2, 3, 4, 5, and 6 independently in a binary search.
With this CL, the generated code checks whether
2 <= x && x <= 6.
walkinrange then optimizes that range check
into a single unsigned comparison.
Experiments suggest that the best min range size
is 2, using binary size as a proxy for optimization.
Binary sizes before/after this CL:
cmd/compile: 14209728 / 14165360
cmd/go: 9543100 / 9539004
Change-Id: If2f7fb97ca80468fa70351ef540866200c4c996c
Reviewed-on: https://go-review.googlesource.com/26770
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2016-06-17 16:27:23 -07:00
|
|
|
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
// binarySearch constructs a binary search tree for handling n cases,
|
|
|
|
|
// and appends it to out. It's used for efficiently implementing
|
|
|
|
|
// switch statements.
|
|
|
|
|
//
|
|
|
|
|
// less(i) should return a boolean expression. If it evaluates true,
|
2019-09-13 15:13:22 -07:00
|
|
|
// then cases before i will be tested; otherwise, cases i and later.
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
//
|
2019-09-13 15:13:22 -07:00
|
|
|
// base(i, nif) should setup nif (an OIF node) to test case i. In
|
|
|
|
|
// particular, it should set nif.Left and nif.Nbody.
|
|
|
|
|
func binarySearch(n int, out *Nodes, less func(i int) *Node, base func(i int, nif *Node)) {
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
const binarySearchMin = 4 // minimum number of cases for binary search
|
|
|
|
|
|
|
|
|
|
var do func(lo, hi int, out *Nodes)
|
|
|
|
|
do = func(lo, hi int, out *Nodes) {
|
|
|
|
|
n := hi - lo
|
|
|
|
|
if n < binarySearchMin {
|
|
|
|
|
for i := lo; i < hi; i++ {
|
2019-09-13 15:13:22 -07:00
|
|
|
nif := nod(OIF, nil, nil)
|
|
|
|
|
base(i, nif)
|
2019-10-03 12:19:42 -04:00
|
|
|
lineno = lineno.WithNotStmt()
|
2019-09-13 15:13:22 -07:00
|
|
|
nif.Left = typecheck(nif.Left, ctxExpr)
|
|
|
|
|
nif.Left = defaultlit(nif.Left, nil)
|
|
|
|
|
out.Append(nif)
|
|
|
|
|
out = &nif.Rlist
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
half := lo + n/2
|
|
|
|
|
nif := nod(OIF, nil, nil)
|
|
|
|
|
nif.Left = less(half)
|
2019-10-03 12:19:42 -04:00
|
|
|
lineno = lineno.WithNotStmt()
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
nif.Left = typecheck(nif.Left, ctxExpr)
|
|
|
|
|
nif.Left = defaultlit(nif.Left, nil)
|
|
|
|
|
do(lo, half, &nif.Nbody)
|
|
|
|
|
do(half, hi, &nif.Rlist)
|
|
|
|
|
out.Append(nif)
|
|
|
|
|
}
|
cmd/compile: recognize integer ranges in switch statements
Consider a switch statement like:
switch x {
case 1:
// ...
case 2, 3, 4, 5, 6:
// ...
case 5:
// ...
}
Prior to this CL, the generated code treated
2, 3, 4, 5, and 6 independently in a binary search.
With this CL, the generated code checks whether
2 <= x && x <= 6.
walkinrange then optimizes that range check
into a single unsigned comparison.
Experiments suggest that the best min range size
is 2, using binary size as a proxy for optimization.
Binary sizes before/after this CL:
cmd/compile: 14209728 / 14165360
cmd/go: 9543100 / 9539004
Change-Id: If2f7fb97ca80468fa70351ef540866200c4c996c
Reviewed-on: https://go-review.googlesource.com/26770
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2016-06-17 16:27:23 -07:00
|
|
|
|
cmd/compile: major refactoring of switch walking
There are a lot of complexities to handling switches efficiently:
1. Order matters for expression switches with non-constant cases and
for type expressions with interface types. We have to respect
side-effects, and we also can't allow later cases to accidentally take
precedence over earlier cases.
2. For runs of integers, floats, and string constants in expression
switches or runs of concrete types in type switches, we want to emit
efficient binary searches.
3. For runs of consecutive integers in expression switches, we want to
collapse them into range comparisons.
4. For binary searches of strings, we want to compare by length first,
because that's more efficient and we don't need to respect any
particular ordering.
5. For "switch true { ... }" and "switch false { ... }", we want to
optimize "case x:" as simply "if x" or "if !x", respectively, unless x
is interface-typed.
The current swt.go code reflects how these constraints have been
incrementally added over time, with each of them being handled ad
hocly in different parts of the code. Also, the existing code tries
very hard to reuse logic between expression and type switches, even
though the similarities are very superficial.
This CL rewrites switch handling to better abstract away the logic
involved in constructing the binary searches. In particular, it's
intended to make further optimizations to switch dispatch much easier.
It also eliminates the need for both OXCASE and OCASE ops, and a
subsequent CL can collapse the two.
Passes toolstash-check.
Change-Id: Ifcd1e56f81f858117a412971d82e98abe7c4481f
Reviewed-on: https://go-review.googlesource.com/c/go/+/194660
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2019-09-10 16:03:03 -07:00
|
|
|
do(0, n, out)
|
cmd/compile: recognize integer ranges in switch statements
Consider a switch statement like:
switch x {
case 1:
// ...
case 2, 3, 4, 5, 6:
// ...
case 5:
// ...
}
Prior to this CL, the generated code treated
2, 3, 4, 5, and 6 independently in a binary search.
With this CL, the generated code checks whether
2 <= x && x <= 6.
walkinrange then optimizes that range check
into a single unsigned comparison.
Experiments suggest that the best min range size
is 2, using binary size as a proxy for optimization.
Binary sizes before/after this CL:
cmd/compile: 14209728 / 14165360
cmd/go: 9543100 / 9539004
Change-Id: If2f7fb97ca80468fa70351ef540866200c4c996c
Reviewed-on: https://go-review.googlesource.com/26770
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2016-06-17 16:27:23 -07:00
|
|
|
}
|