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
|
|
|
|
|
|
2016-09-12 17:30:35 -07:00
|
|
|
import "sort"
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
const (
|
2015-02-27 20:44:45 +00:00
|
|
|
// expression switch
|
|
|
|
|
switchKindExpr = iota // switch a {...} or switch 5 {...}
|
|
|
|
|
switchKindTrue // switch true {...} or switch {...}
|
|
|
|
|
switchKindFalse // switch false {...}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-27 20:44:45 +00:00
|
|
|
// type switch
|
|
|
|
|
switchKindType // switch a.(type) {...}
|
|
|
|
|
)
|
2015-02-13 14:40:36 -05: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
|
|
|
const (
|
|
|
|
|
binarySearchMin = 4 // minimum number of cases for binary search
|
|
|
|
|
integerRangeMin = 2 // minimum size of integer ranges
|
|
|
|
|
)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-27 20:44:45 +00:00
|
|
|
// An exprSwitch walks an expression switch.
|
|
|
|
|
type exprSwitch struct {
|
|
|
|
|
exprname *Node // node for the expression being switched on
|
|
|
|
|
kind int // kind of switch statement (switchKind*)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2015-02-27 20:44:45 +00:00
|
|
|
// A typeSwitch walks a type switch.
|
|
|
|
|
type typeSwitch struct {
|
|
|
|
|
hashname *Node // node for the hash of the type of the variable being switched on
|
|
|
|
|
facename *Node // node for the concrete type of the variable being switched on
|
|
|
|
|
okname *Node // boolean node used for comma-ok type assertions
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2015-02-27 20:44:45 +00:00
|
|
|
// A caseClause is a single case clause in a switch statement.
|
|
|
|
|
type caseClause struct {
|
|
|
|
|
node *Node // points at case statement
|
|
|
|
|
ordinal int // position in switch
|
|
|
|
|
hash uint32 // hash of a type switch
|
2016-06-01 15:21:56 -07:00
|
|
|
// isconst indicates whether this case clause is a constant,
|
|
|
|
|
// for the purposes of the switch code generation.
|
|
|
|
|
// For expression switches, that's generally literals (case 5:, not case x:).
|
|
|
|
|
// For type switches, that's concrete types (case time.Time:), not interfaces (case io.Reader:).
|
|
|
|
|
isconst bool
|
2015-02-27 20:44:45 +00:00
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-05-31 13:11:15 -07:00
|
|
|
// caseClauses are all the case clauses in a switch statement.
|
|
|
|
|
type caseClauses struct {
|
|
|
|
|
list []caseClause // general cases
|
|
|
|
|
defjmp *Node // OGOTO for default case or OBREAK if no default case present
|
|
|
|
|
niljmp *Node // OGOTO for nil type case in a type switch
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-27 20:44:45 +00:00
|
|
|
// typecheckswitch typechecks a switch statement.
|
|
|
|
|
func typecheckswitch(n *Node) {
|
2016-03-02 17:34:42 -08:00
|
|
|
lno := lineno
|
2016-03-19 17:02:01 -07:00
|
|
|
typecheckslice(n.Ninit.Slice(), Etop)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-27 20:44:45 +00:00
|
|
|
var nilonly string
|
|
|
|
|
var top int
|
|
|
|
|
var t *Type
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-05-26 21:30:20 -04:00
|
|
|
if n.Left != nil && n.Left.Op == OTYPESW {
|
2015-02-27 20:44:45 +00:00
|
|
|
// type switch
|
|
|
|
|
top = Etype
|
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
|
|
|
n.Left.Right = typecheck(n.Left.Right, Erv)
|
2015-05-26 21:30:20 -04:00
|
|
|
t = n.Left.Right.Type
|
2016-03-30 14:56:08 -07:00
|
|
|
if t != nil && !t.IsInterface() {
|
2016-09-09 21:08:46 -07:00
|
|
|
Yyerror("cannot type switch on non-interface value %L", n.Left.Right)
|
2015-02-27 20:44:45 +00:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// expression switch
|
|
|
|
|
top = Erv
|
2015-05-26 21:30:20 -04:00
|
|
|
if n.Left != nil {
|
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
|
|
|
n.Left = typecheck(n.Left, Erv)
|
|
|
|
|
n.Left = defaultlit(n.Left, nil)
|
2015-05-26 21:30:20 -04:00
|
|
|
t = n.Left.Type
|
2015-02-13 14:40:36 -05:00
|
|
|
} else {
|
2015-02-27 20:44:45 +00:00
|
|
|
t = Types[TBOOL]
|
|
|
|
|
}
|
|
|
|
|
if t != nil {
|
|
|
|
|
switch {
|
2015-03-01 07:54:01 +00:00
|
|
|
case !okforeq[t.Etype]:
|
2016-09-09 21:08:46 -07:00
|
|
|
Yyerror("cannot switch on %L", n.Left)
|
2016-04-01 11:22:03 -07:00
|
|
|
case t.IsSlice():
|
2015-02-27 20:44:45 +00:00
|
|
|
nilonly = "slice"
|
2016-04-01 11:22:03 -07:00
|
|
|
case t.IsArray() && !t.IsComparable():
|
2016-09-09 21:08:46 -07:00
|
|
|
Yyerror("cannot switch on %L", n.Left)
|
2016-04-01 11:22:03 -07:00
|
|
|
case t.IsStruct():
|
|
|
|
|
if f := t.IncomparableField(); f != nil {
|
2016-09-09 21:08:46 -07:00
|
|
|
Yyerror("cannot switch on %L (struct containing %v cannot be compared)", n.Left, f.Type)
|
2016-04-01 11:22:03 -07:00
|
|
|
}
|
2015-02-27 20:44:45 +00:00
|
|
|
case t.Etype == TFUNC:
|
|
|
|
|
nilonly = "func"
|
2016-03-30 14:56:08 -07:00
|
|
|
case t.IsMap():
|
2015-02-27 20:44:45 +00:00
|
|
|
nilonly = "map"
|
|
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-27 20:44:45 +00:00
|
|
|
n.Type = t
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-05-30 16:42:38 -07:00
|
|
|
var def, niltype *Node
|
2016-03-08 15:10:26 -08:00
|
|
|
for _, ncase := range n.List.Slice() {
|
2015-02-27 20:44:45 +00:00
|
|
|
setlineno(n)
|
2016-03-08 15:10:26 -08:00
|
|
|
if ncase.List.Len() == 0 {
|
2015-02-27 20:44:45 +00:00
|
|
|
// default
|
|
|
|
|
if def != nil {
|
2016-05-31 15:02:40 -07:00
|
|
|
setlineno(ncase)
|
2015-02-27 20:44:45 +00:00
|
|
|
Yyerror("multiple defaults in switch (first at %v)", def.Line())
|
|
|
|
|
} else {
|
|
|
|
|
def = ncase
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2016-03-09 12:39:36 -08:00
|
|
|
ls := ncase.List.Slice()
|
|
|
|
|
for i1, n1 := range ls {
|
2016-03-08 15:10:26 -08:00
|
|
|
setlineno(n1)
|
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
|
|
|
ls[i1] = typecheck(ls[i1], Erv|Etype)
|
2016-03-09 12:39:36 -08:00
|
|
|
n1 = ls[i1]
|
|
|
|
|
if n1.Type == nil || t == nil {
|
2015-02-27 20:44:45 +00:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
setlineno(ncase)
|
|
|
|
|
switch top {
|
|
|
|
|
// expression switch
|
|
|
|
|
case Erv:
|
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
|
|
|
ls[i1] = defaultlit(ls[i1], t)
|
2016-03-09 12:39:36 -08:00
|
|
|
n1 = ls[i1]
|
2015-02-27 20:44:45 +00:00
|
|
|
switch {
|
2016-03-09 12:39:36 -08:00
|
|
|
case n1.Op == OTYPE:
|
|
|
|
|
Yyerror("type %v is not an expression", n1.Type)
|
|
|
|
|
case n1.Type != nil && assignop(n1.Type, t, nil) == 0 && assignop(t, n1.Type, nil) == 0:
|
2015-05-26 21:30:20 -04:00
|
|
|
if n.Left != nil {
|
2016-03-09 12:39:36 -08:00
|
|
|
Yyerror("invalid case %v in switch on %v (mismatched types %v and %v)", n1, n.Left, n1.Type, t)
|
2015-02-27 20:44:45 +00:00
|
|
|
} else {
|
2016-03-09 12:39:36 -08:00
|
|
|
Yyerror("invalid case %v in switch (mismatched types %v and bool)", n1, n1.Type)
|
2015-02-27 20:44:45 +00:00
|
|
|
}
|
2016-03-09 12:39:36 -08:00
|
|
|
case nilonly != "" && !isnil(n1):
|
|
|
|
|
Yyerror("invalid case %v in switch (can only compare %s %v to nil)", n1, nilonly, n.Left)
|
2016-04-01 11:22:03 -07:00
|
|
|
case t.IsInterface() && !n1.Type.IsInterface() && !n1.Type.IsComparable():
|
2016-09-09 21:08:46 -07:00
|
|
|
Yyerror("invalid case %L in switch (incomparable type)", n1)
|
2015-02-27 20:44:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// type switch
|
|
|
|
|
case Etype:
|
2016-03-14 01:20:49 -07:00
|
|
|
var missing, have *Field
|
2015-02-27 20:44:45 +00:00
|
|
|
var ptr int
|
|
|
|
|
switch {
|
2016-04-01 13:36:24 -07:00
|
|
|
case n1.Op == OLITERAL && n1.Type.IsKind(TNIL):
|
2016-05-30 16:42:38 -07:00
|
|
|
// case nil:
|
|
|
|
|
if niltype != nil {
|
|
|
|
|
Yyerror("multiple nil cases in type switch (first at %v)", niltype.Line())
|
|
|
|
|
} else {
|
|
|
|
|
niltype = ncase
|
|
|
|
|
}
|
2016-03-09 12:39:36 -08:00
|
|
|
case n1.Op != OTYPE && n1.Type != nil: // should this be ||?
|
2016-09-09 21:08:46 -07:00
|
|
|
Yyerror("%L is not a type", n1)
|
2015-02-27 20:44:45 +00:00
|
|
|
// reset to original type
|
2016-03-09 12:39:36 -08:00
|
|
|
n1 = n.Left.Right
|
|
|
|
|
ls[i1] = n1
|
2016-03-30 14:56:08 -07:00
|
|
|
case !n1.Type.IsInterface() && t.IsInterface() && !implements(n1.Type, t, &missing, &have, &ptr):
|
2015-09-07 10:37:26 +10:00
|
|
|
if have != nil && !missing.Broke && !have.Broke {
|
2016-09-09 21:08:46 -07:00
|
|
|
Yyerror("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)
|
2015-09-07 10:37:26 +10:00
|
|
|
} else if !missing.Broke {
|
2016-09-09 21:08:46 -07:00
|
|
|
Yyerror("impossible type switch case: %L cannot have dynamic type %v"+
|
|
|
|
|
" (missing %v method)", n.Left.Right, n1.Type, missing.Sym)
|
2015-02-27 20:44:45 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if top == Etype && n.Type != nil {
|
2016-03-04 17:28:07 -08:00
|
|
|
ll := ncase.List
|
2016-03-08 15:10:26 -08:00
|
|
|
if ncase.Rlist.Len() != 0 {
|
|
|
|
|
nvar := ncase.Rlist.First()
|
2016-04-01 13:36:24 -07:00
|
|
|
if ll.Len() == 1 && ll.First().Type != nil && !ll.First().Type.IsKind(TNIL) {
|
2015-02-27 20:44:45 +00:00
|
|
|
// single entry type switch
|
2016-03-08 15:10:26 -08:00
|
|
|
nvar.Name.Param.Ntype = typenod(ll.First().Type)
|
2015-02-27 20:44:45 +00:00
|
|
|
} else {
|
|
|
|
|
// multiple entry type switch or default
|
2015-05-27 00:44:05 -04:00
|
|
|
nvar.Name.Param.Ntype = typenod(n.Type)
|
2015-02-27 20:44:45 +00:00
|
|
|
}
|
2015-02-13 14:40:36 -05: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
|
|
|
nvar = typecheck(nvar, Erv|Easgn)
|
2016-03-09 12:39:36 -08:00
|
|
|
ncase.Rlist.SetIndex(0, nvar)
|
2015-02-27 20:44:45 +00:00
|
|
|
}
|
|
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-03-19 17:02:01 -07:00
|
|
|
typecheckslice(ncase.Nbody.Slice(), Etop)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2016-03-02 17:34:42 -08:00
|
|
|
lineno = lno
|
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) {
|
|
|
|
|
// convert switch {...} to switch true {...}
|
2015-05-26 21:30:20 -04:00
|
|
|
if sw.Left == nil {
|
|
|
|
|
sw.Left = Nodbool(true)
|
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
|
|
|
sw.Left = typecheck(sw.Left, Erv)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2015-05-26 21:30:20 -04:00
|
|
|
if sw.Left.Op == OTYPESW {
|
2015-02-27 20:44:45 +00:00
|
|
|
var s typeSwitch
|
|
|
|
|
s.walk(sw)
|
|
|
|
|
} else {
|
|
|
|
|
var s exprSwitch
|
|
|
|
|
s.walk(sw)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-27 20:44:45 +00:00
|
|
|
// walk generates an AST implementing sw.
|
|
|
|
|
// sw is an expression switch.
|
|
|
|
|
// The AST is generally of the form of a linear
|
|
|
|
|
// search using if..goto, although binary search
|
|
|
|
|
// is used with long runs of constants.
|
|
|
|
|
func (s *exprSwitch) walk(sw *Node) {
|
|
|
|
|
casebody(sw, nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-05-26 21:30:20 -04:00
|
|
|
cond := sw.Left
|
|
|
|
|
sw.Left = nil
|
|
|
|
|
|
2015-02-27 20:44:45 +00:00
|
|
|
s.kind = switchKindExpr
|
2015-05-26 21:30:20 -04:00
|
|
|
if Isconst(cond, CTBOOL) {
|
2015-02-27 20:44:45 +00:00
|
|
|
s.kind = switchKindTrue
|
2015-05-27 00:47:05 -04:00
|
|
|
if !cond.Val().U.(bool) {
|
2015-02-27 20:44:45 +00:00
|
|
|
s.kind = switchKindFalse
|
2015-02-13 14:40:36 -05: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)
|
2015-02-27 20:44:45 +00:00
|
|
|
t := sw.Type
|
|
|
|
|
if t == nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-27 20:44:45 +00:00
|
|
|
// convert the switch into OIF statements
|
2016-02-27 14:31:33 -08:00
|
|
|
var cas []*Node
|
2015-02-27 20:44:45 +00:00
|
|
|
if s.kind == switchKindTrue || s.kind == switchKindFalse {
|
|
|
|
|
s.exprname = Nodbool(s.kind == switchKindTrue)
|
2015-05-26 21:30:20 -04:00
|
|
|
} else if consttype(cond) >= 0 {
|
2015-02-27 20:44:45 +00:00
|
|
|
// leave constants to enable dead code elimination (issue 9608)
|
2015-05-26 21:30:20 -04:00
|
|
|
s.exprname = cond
|
2015-02-13 14:40:36 -05:00
|
|
|
} else {
|
2015-05-26 21:30:20 -04:00
|
|
|
s.exprname = temp(cond.Type)
|
2016-02-27 14:31:33 -08:00
|
|
|
cas = []*Node{Nod(OAS, s.exprname, cond)}
|
|
|
|
|
typecheckslice(cas, Etop)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2016-05-31 13:11:15 -07:00
|
|
|
// Enumerate the cases and prepare the default case.
|
2016-06-03 12:05:32 -07:00
|
|
|
clauses := s.genCaseClauses(sw.List.Slice())
|
2016-03-08 15:10:26 -08:00
|
|
|
sw.List.Set(nil)
|
2016-05-31 13:11:15 -07:00
|
|
|
cc := clauses.list
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-27 20:44:45 +00:00
|
|
|
// handle the cases in order
|
|
|
|
|
for len(cc) > 0 {
|
|
|
|
|
// deal with expressions one at a time
|
2016-06-01 15:21:56 -07:00
|
|
|
if !okforcmp[t.Etype] || !cc[0].isconst {
|
2015-02-27 20:44:45 +00:00
|
|
|
a := s.walkCases(cc[:1])
|
2016-02-27 14:31:33 -08:00
|
|
|
cas = append(cas, a)
|
2015-02-27 20:44:45 +00:00
|
|
|
cc = cc[1:]
|
|
|
|
|
continue
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2015-02-27 20:44:45 +00:00
|
|
|
// do binary search on runs of constants
|
|
|
|
|
var run int
|
2016-06-01 15:21:56 -07:00
|
|
|
for run = 1; run < len(cc) && cc[run].isconst; run++ {
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2015-02-27 20:44:45 +00:00
|
|
|
// sort and compile constants
|
2016-06-17 14:50:39 -07:00
|
|
|
sort.Sort(caseClauseByConstVal(cc[:run]))
|
2015-02-27 20:44:45 +00:00
|
|
|
a := s.walkCases(cc[:run])
|
2016-02-27 14:31:33 -08:00
|
|
|
cas = append(cas, a)
|
2015-02-27 20:44:45 +00:00
|
|
|
cc = cc[run:]
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2015-02-27 20:44:45 +00:00
|
|
|
// handle default case
|
|
|
|
|
if nerrors == 0 {
|
2016-05-31 13:11:15 -07:00
|
|
|
cas = append(cas, clauses.defjmp)
|
2016-09-14 13:19:20 -07:00
|
|
|
sw.Nbody.Prepend(cas...)
|
2016-03-07 22:54:46 -08:00
|
|
|
walkstmtlist(sw.Nbody.Slice())
|
2015-02-27 20:44:45 +00:00
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2015-02-27 20:44:45 +00:00
|
|
|
// walkCases generates an AST implementing the cases in cc.
|
2016-05-31 13:11:15 -07:00
|
|
|
func (s *exprSwitch) walkCases(cc []caseClause) *Node {
|
2015-02-27 20:44:45 +00:00
|
|
|
if len(cc) < binarySearchMin {
|
|
|
|
|
// linear search
|
2016-03-08 10:26:20 -08:00
|
|
|
var cas []*Node
|
2015-02-27 20:44:45 +00:00
|
|
|
for _, c := range cc {
|
|
|
|
|
n := c.node
|
2016-03-02 17:34:42 -08:00
|
|
|
lno := setlineno(n)
|
2015-02-27 20:44:45 +00:00
|
|
|
|
|
|
|
|
a := Nod(OIF, nil, nil)
|
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
|
|
|
if rng := n.List.Slice(); rng != nil {
|
|
|
|
|
// Integer range.
|
|
|
|
|
// exprname is a temp or a constant,
|
|
|
|
|
// so it is safe to evaluate twice.
|
|
|
|
|
// In most cases, this conjunction will be
|
|
|
|
|
// rewritten by walkinrange into a single comparison.
|
|
|
|
|
low := Nod(OGE, s.exprname, rng[0])
|
|
|
|
|
high := Nod(OLE, s.exprname, rng[1])
|
|
|
|
|
a.Left = Nod(OANDAND, low, high)
|
|
|
|
|
a.Left = typecheck(a.Left, Erv)
|
|
|
|
|
a.Left = walkexpr(a.Left, nil) // give walk the opportunity to optimize the range check
|
|
|
|
|
} else if (s.kind != switchKindTrue && s.kind != switchKindFalse) || assignop(n.Left.Type, s.exprname.Type, nil) == OCONVIFACE || assignop(s.exprname.Type, n.Left.Type, nil) == OCONVIFACE {
|
2015-05-26 21:30:20 -04:00
|
|
|
a.Left = Nod(OEQ, s.exprname, n.Left) // if name == val
|
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
|
|
|
a.Left = typecheck(a.Left, Erv)
|
2015-02-27 20:44:45 +00:00
|
|
|
} else if s.kind == switchKindTrue {
|
2015-05-26 21:30:20 -04:00
|
|
|
a.Left = n.Left // if val
|
2015-02-27 20:44:45 +00:00
|
|
|
} else {
|
|
|
|
|
// s.kind == switchKindFalse
|
2015-05-26 21:30:20 -04:00
|
|
|
a.Left = Nod(ONOT, n.Left, nil) // if !val
|
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
|
|
|
a.Left = typecheck(a.Left, Erv)
|
2015-02-27 20:44:45 +00:00
|
|
|
}
|
2016-03-10 10:13:42 -08:00
|
|
|
a.Nbody.Set1(n.Right) // goto l
|
2015-02-27 20:44:45 +00:00
|
|
|
|
2016-03-08 10:26:20 -08:00
|
|
|
cas = append(cas, a)
|
2016-03-02 17:34:42 -08:00
|
|
|
lineno = lno
|
2015-02-27 20:44:45 +00:00
|
|
|
}
|
|
|
|
|
return liststmt(cas)
|
|
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-27 20:44:45 +00:00
|
|
|
// find the middle and recur
|
|
|
|
|
half := len(cc) / 2
|
|
|
|
|
a := Nod(OIF, nil, nil)
|
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
|
|
|
n := cc[half-1].node
|
|
|
|
|
var mid *Node
|
|
|
|
|
if rng := n.List.Slice(); rng != nil {
|
|
|
|
|
mid = rng[1] // high end of range
|
|
|
|
|
} else {
|
|
|
|
|
mid = n.Left
|
|
|
|
|
}
|
2015-03-17 16:10:31 -07:00
|
|
|
le := Nod(OLE, s.exprname, mid)
|
|
|
|
|
if Isconst(mid, CTSTR) {
|
2016-06-17 14:50:39 -07:00
|
|
|
// Search by length and then by value; see caseClauseByConstVal.
|
2015-03-17 16:10:31 -07:00
|
|
|
lenlt := Nod(OLT, Nod(OLEN, s.exprname, nil), Nod(OLEN, mid, nil))
|
|
|
|
|
leneq := Nod(OEQ, Nod(OLEN, s.exprname, nil), Nod(OLEN, mid, nil))
|
2015-05-26 21:30:20 -04:00
|
|
|
a.Left = Nod(OOROR, lenlt, Nod(OANDAND, leneq, le))
|
2015-03-17 16:10:31 -07:00
|
|
|
} else {
|
2015-05-26 21:30:20 -04:00
|
|
|
a.Left = le
|
2015-03-17 16:10:31 -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
|
|
|
a.Left = typecheck(a.Left, Erv)
|
2016-03-10 10:13:42 -08:00
|
|
|
a.Nbody.Set1(s.walkCases(cc[:half]))
|
|
|
|
|
a.Rlist.Set1(s.walkCases(cc[half:]))
|
2015-02-27 20:44:45 +00:00
|
|
|
return a
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2015-02-27 20:44:45 +00:00
|
|
|
// casebody builds separate lists of statements and cases.
|
|
|
|
|
// It makes labels between cases and statements
|
|
|
|
|
// and deals with fallthrough, break, and unreachable statements.
|
2015-02-13 14:40:36 -05:00
|
|
|
func casebody(sw *Node, typeswvar *Node) {
|
2016-03-08 15:10:26 -08:00
|
|
|
if sw.List.Len() == 0 {
|
2015-02-13 14:40:36 -05:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
lno := setlineno(sw)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-03-04 17:28:07 -08:00
|
|
|
var cas []*Node // cases
|
|
|
|
|
var stat []*Node // statements
|
|
|
|
|
var def *Node // defaults
|
2015-02-23 16:07:24 -05:00
|
|
|
br := Nod(OBREAK, nil, nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-03-09 12:39:36 -08:00
|
|
|
for i, n := range sw.List.Slice() {
|
2015-02-13 14:40:36 -05:00
|
|
|
setlineno(n)
|
|
|
|
|
if n.Op != OXCASE {
|
2016-04-27 15:10:10 +10:00
|
|
|
Fatalf("casebody %v", n.Op)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
n.Op = OCASE
|
2016-03-08 15:10:26 -08:00
|
|
|
needvar := n.List.Len() != 1 || n.List.First().Op == OLITERAL
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-08-15 21:09:39 -07:00
|
|
|
jmp := Nod(OGOTO, autolabel(".s"), nil)
|
2016-05-25 14:10:01 -07:00
|
|
|
switch n.List.Len() {
|
|
|
|
|
case 0:
|
|
|
|
|
// default
|
2015-02-13 14:40:36 -05:00
|
|
|
if def != nil {
|
|
|
|
|
Yyerror("more than one default case")
|
|
|
|
|
}
|
|
|
|
|
// reuse original default case
|
2015-02-27 20:44:45 +00:00
|
|
|
n.Right = jmp
|
2015-02-13 14:40:36 -05:00
|
|
|
def = n
|
2016-05-25 14:10:01 -07:00
|
|
|
case 1:
|
2015-02-27 20:44:45 +00:00
|
|
|
// one case -- reuse OCASE node
|
2016-03-08 15:10:26 -08:00
|
|
|
n.Left = n.List.First()
|
2015-02-27 20:44:45 +00:00
|
|
|
n.Right = jmp
|
2016-03-08 15:10:26 -08:00
|
|
|
n.List.Set(nil)
|
2016-03-04 17:28:07 -08:00
|
|
|
cas = append(cas, n)
|
2016-05-25 14:10:01 -07:00
|
|
|
default:
|
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
|
|
|
// Expand multi-valued cases and detect ranges of integer cases.
|
|
|
|
|
if typeswvar != nil || sw.Left.Type.IsInterface() || !n.List.First().Type.IsInteger() || n.List.Len() < integerRangeMin {
|
|
|
|
|
// Can't use integer ranges. Expand each case into a separate node.
|
|
|
|
|
for _, n1 := range n.List.Slice() {
|
|
|
|
|
cas = append(cas, Nod(OCASE, n1, jmp))
|
|
|
|
|
}
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
// Find integer ranges within runs of constants.
|
|
|
|
|
s := n.List.Slice()
|
|
|
|
|
j := 0
|
|
|
|
|
for j < len(s) {
|
|
|
|
|
// Find a run of constants.
|
|
|
|
|
var run int
|
|
|
|
|
for run = j; run < len(s) && Isconst(s[run], CTINT); run++ {
|
|
|
|
|
}
|
|
|
|
|
if run-j >= integerRangeMin {
|
|
|
|
|
// Search for integer ranges in s[j:run].
|
|
|
|
|
// Typechecking is done, so all values are already in an appropriate range.
|
|
|
|
|
search := s[j:run]
|
|
|
|
|
sort.Sort(constIntNodesByVal(search))
|
|
|
|
|
for beg, end := 0, 1; end <= len(search); end++ {
|
|
|
|
|
if end < len(search) && search[end].Int64() == search[end-1].Int64()+1 {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if end-beg >= integerRangeMin {
|
|
|
|
|
// Record range in List.
|
|
|
|
|
c := Nod(OCASE, nil, jmp)
|
|
|
|
|
c.List.Set2(search[beg], search[end-1])
|
|
|
|
|
cas = append(cas, c)
|
|
|
|
|
} else {
|
|
|
|
|
// Not large enough for range; record separately.
|
|
|
|
|
for _, n := range search[beg:end] {
|
|
|
|
|
cas = append(cas, Nod(OCASE, n, jmp))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
beg = end
|
|
|
|
|
}
|
|
|
|
|
j = run
|
|
|
|
|
}
|
|
|
|
|
// Advance to next constant, adding individual non-constant
|
|
|
|
|
// or as-yet-unhandled constant cases as we go.
|
|
|
|
|
for ; j < len(s) && (j < run || !Isconst(s[j], CTINT)); j++ {
|
|
|
|
|
cas = append(cas, Nod(OCASE, s[j], jmp))
|
|
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-27 14:31:33 -08:00
|
|
|
stat = append(stat, Nod(OLABEL, jmp.Left, nil))
|
2016-03-08 15:10:26 -08:00
|
|
|
if typeswvar != nil && needvar && n.Rlist.Len() != 0 {
|
2016-02-27 14:31:33 -08:00
|
|
|
l := []*Node{
|
2016-03-08 15:10:26 -08:00
|
|
|
Nod(ODCL, n.Rlist.First(), nil),
|
|
|
|
|
Nod(OAS, n.Rlist.First(), typeswvar),
|
2016-02-27 14:31:33 -08:00
|
|
|
}
|
|
|
|
|
typecheckslice(l, Etop)
|
|
|
|
|
stat = append(stat, l...)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2016-02-27 14:31:33 -08:00
|
|
|
stat = append(stat, n.Nbody.Slice()...)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-04-03 12:43:27 +01:00
|
|
|
// botch - shouldn't fall through declaration
|
2016-02-27 14:31:33 -08:00
|
|
|
last := stat[len(stat)-1]
|
2015-02-13 14:40:36 -05:00
|
|
|
if last.Xoffset == n.Xoffset && last.Op == OXFALL {
|
|
|
|
|
if typeswvar != nil {
|
|
|
|
|
setlineno(last)
|
|
|
|
|
Yyerror("cannot fallthrough in type switch")
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-09 12:39:36 -08:00
|
|
|
if i+1 >= sw.List.Len() {
|
2015-02-13 14:40:36 -05:00
|
|
|
setlineno(last)
|
|
|
|
|
Yyerror("cannot fallthrough final case in switch")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
last.Op = OFALL
|
|
|
|
|
} else {
|
2016-02-27 14:31:33 -08:00
|
|
|
stat = append(stat, br)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-27 14:31:33 -08:00
|
|
|
stat = append(stat, br)
|
2015-02-13 14:40:36 -05:00
|
|
|
if def != nil {
|
2016-03-04 17:28:07 -08:00
|
|
|
cas = append(cas, def)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2016-03-08 15:10:26 -08:00
|
|
|
sw.List.Set(cas)
|
2016-02-27 14:31:33 -08:00
|
|
|
sw.Nbody.Set(stat)
|
2015-02-13 14:40:36 -05:00
|
|
|
lineno = lno
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-03 12:05:32 -07:00
|
|
|
// genCaseClauses generates the caseClauses value for clauses.
|
|
|
|
|
func (s *exprSwitch) genCaseClauses(clauses []*Node) caseClauses {
|
2016-05-31 13:11:15 -07:00
|
|
|
var cc caseClauses
|
2016-06-03 12:05:32 -07:00
|
|
|
for _, n := range clauses {
|
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
|
|
|
if n.Left == nil && n.List.Len() == 0 {
|
2016-05-31 13:11:15 -07:00
|
|
|
// default case
|
|
|
|
|
if cc.defjmp != nil {
|
|
|
|
|
Fatalf("duplicate default case not detected during typechecking")
|
|
|
|
|
}
|
|
|
|
|
cc.defjmp = n.Right
|
2015-02-13 14:40:36 -05:00
|
|
|
continue
|
|
|
|
|
}
|
2016-06-03 12:05:32 -07:00
|
|
|
c := caseClause{node: n, ordinal: len(cc.list)}
|
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
|
|
|
if n.List.Len() > 0 {
|
|
|
|
|
c.isconst = true
|
|
|
|
|
}
|
2016-06-03 12:05:32 -07:00
|
|
|
switch consttype(n.Left) {
|
|
|
|
|
case CTFLT, CTINT, CTRUNE, CTSTR:
|
|
|
|
|
c.isconst = true
|
|
|
|
|
}
|
|
|
|
|
cc.list = append(cc.list, c)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if cc.defjmp == nil {
|
|
|
|
|
cc.defjmp = Nod(OBREAK, nil, nil)
|
|
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-06-03 12:05:32 -07:00
|
|
|
// diagnose duplicate cases
|
|
|
|
|
s.checkDupCases(cc.list)
|
|
|
|
|
return cc
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// genCaseClauses generates the caseClauses value for clauses.
|
|
|
|
|
func (s *typeSwitch) genCaseClauses(clauses []*Node) caseClauses {
|
|
|
|
|
var cc caseClauses
|
|
|
|
|
for _, n := range clauses {
|
|
|
|
|
switch {
|
|
|
|
|
case n.Left == nil:
|
|
|
|
|
// default case
|
|
|
|
|
if cc.defjmp != nil {
|
|
|
|
|
Fatalf("duplicate default case not detected during typechecking")
|
|
|
|
|
}
|
|
|
|
|
cc.defjmp = n.Right
|
|
|
|
|
continue
|
|
|
|
|
case n.Left.Op == OLITERAL:
|
2016-05-31 13:11:15 -07:00
|
|
|
// nil case in type switch
|
|
|
|
|
if cc.niljmp != nil {
|
|
|
|
|
Fatalf("duplicate nil case not detected during typechecking")
|
|
|
|
|
}
|
|
|
|
|
cc.niljmp = n.Right
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// general case
|
2016-06-03 12:05:32 -07:00
|
|
|
c := caseClause{
|
|
|
|
|
node: n,
|
|
|
|
|
ordinal: len(cc.list),
|
|
|
|
|
isconst: !n.Left.Type.IsInterface(),
|
|
|
|
|
hash: typehash(n.Left.Type),
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2016-05-31 13:11:15 -07:00
|
|
|
cc.list = append(cc.list, c)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2016-05-31 13:11:15 -07:00
|
|
|
if cc.defjmp == nil {
|
|
|
|
|
cc.defjmp = Nod(OBREAK, nil, nil)
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-03 12:05:32 -07:00
|
|
|
// diagnose duplicate cases
|
|
|
|
|
s.checkDupCases(cc.list)
|
2016-06-01 11:34:28 -07:00
|
|
|
return cc
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-03 12:05:32 -07:00
|
|
|
func (s *typeSwitch) checkDupCases(cc []caseClause) {
|
|
|
|
|
if len(cc) < 2 {
|
|
|
|
|
return
|
|
|
|
|
}
|
2016-06-01 11:34:28 -07:00
|
|
|
// We store seen types in a map keyed by type hash.
|
|
|
|
|
// It is possible, but very unlikely, for multiple distinct types to have the same hash.
|
|
|
|
|
seen := make(map[uint32][]*Node)
|
|
|
|
|
// To avoid many small allocations of length 1 slices,
|
|
|
|
|
// also set up a single large slice to slice into.
|
|
|
|
|
nn := make([]*Node, 0, len(cc))
|
|
|
|
|
Outer:
|
|
|
|
|
for _, c := range cc {
|
|
|
|
|
prev, ok := seen[c.hash]
|
|
|
|
|
if !ok {
|
|
|
|
|
// First entry for this hash.
|
|
|
|
|
nn = append(nn, c.node)
|
|
|
|
|
seen[c.hash] = nn[len(nn)-1 : len(nn):len(nn)]
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
for _, n := range prev {
|
2016-09-15 14:34:20 +10:00
|
|
|
if eqtype(n.Left.Type, c.node.Left.Type) {
|
2016-06-01 11:34:28 -07:00
|
|
|
yyerrorl(c.node.Lineno, "duplicate case %v in type switch\n\tprevious case at %v", c.node.Left.Type, n.Line())
|
|
|
|
|
// avoid double-reporting errors
|
|
|
|
|
continue Outer
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
2016-06-01 11:34:28 -07:00
|
|
|
seen[c.hash] = append(seen[c.hash], c.node)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2016-06-01 11:34:28 -07:00
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-06-03 12:05:32 -07:00
|
|
|
func (s *exprSwitch) checkDupCases(cc []caseClause) {
|
|
|
|
|
if len(cc) < 2 {
|
|
|
|
|
return
|
|
|
|
|
}
|
2016-06-05 17:28:28 -07:00
|
|
|
// The common case is that s's expression is not an interface.
|
|
|
|
|
// In that case, all constant clauses have the same type,
|
|
|
|
|
// so checking for duplicates can be done solely by value.
|
|
|
|
|
if !s.exprname.Type.IsInterface() {
|
|
|
|
|
seen := make(map[interface{}]*Node)
|
|
|
|
|
for _, c := range cc {
|
|
|
|
|
// Can't check for duplicates that aren't constants, per the spec. Issue 15896.
|
|
|
|
|
// 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 ct := consttype(c.node.Left); ct < 0 || ct == CTBOOL {
|
|
|
|
|
continue
|
|
|
|
|
}
|
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
|
|
|
if c.node.Left != nil {
|
|
|
|
|
// Single constant.
|
|
|
|
|
val := c.node.Left.Val().Interface()
|
|
|
|
|
prev, dup := seen[val]
|
|
|
|
|
if !dup {
|
|
|
|
|
seen[val] = c.node
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
setlineno(c.node)
|
|
|
|
|
Yyerror("duplicate case %v in switch\n\tprevious case at %v", prev.Left, prev.Line())
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if c.node.List.Len() == 2 {
|
|
|
|
|
// Range of integers.
|
|
|
|
|
low := c.node.List.Index(0).Int64()
|
|
|
|
|
high := c.node.List.Index(1).Int64()
|
|
|
|
|
for i := low; i <= high; i++ {
|
|
|
|
|
prev, dup := seen[i]
|
|
|
|
|
if !dup {
|
|
|
|
|
seen[i] = c.node
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
setlineno(c.node)
|
|
|
|
|
Yyerror("duplicate case %v in switch\n\tprevious case at %v", prev.Left, prev.Line())
|
|
|
|
|
}
|
2016-06-05 17:28:28 -07:00
|
|
|
continue
|
|
|
|
|
}
|
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
|
|
|
Fatalf("bad caseClause node in checkDupCases: %v", c.node)
|
2016-06-05 17:28:28 -07:00
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
// s's expression is an interface. This is fairly rare, so keep this simple.
|
|
|
|
|
// Duplicates are only duplicates if they have the same type and the same value.
|
|
|
|
|
type typeVal struct {
|
|
|
|
|
typ string
|
|
|
|
|
val interface{}
|
|
|
|
|
}
|
|
|
|
|
seen := make(map[typeVal]*Node)
|
|
|
|
|
for _, c := range cc {
|
|
|
|
|
if ct := consttype(c.node.Left); ct < 0 || ct == CTBOOL {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
n := c.node.Left
|
|
|
|
|
tv := typeVal{
|
2016-09-12 17:30:35 -07:00
|
|
|
// n.Type.tconv(FmtLeft | FmtUnsigned) here serves to completely describe the type.
|
2016-06-05 17:28:28 -07:00
|
|
|
// See the comments in func typehash.
|
2016-09-12 17:30:35 -07:00
|
|
|
typ: n.Type.tconv(FmtLeft | FmtUnsigned),
|
2016-06-05 17:28:28 -07:00
|
|
|
val: n.Val().Interface(),
|
|
|
|
|
}
|
|
|
|
|
prev, dup := seen[tv]
|
|
|
|
|
if !dup {
|
|
|
|
|
seen[tv] = c.node
|
2016-06-01 11:34:28 -07:00
|
|
|
continue
|
|
|
|
|
}
|
2016-06-05 17:28:28 -07:00
|
|
|
setlineno(c.node)
|
|
|
|
|
Yyerror("duplicate case %v in switch\n\tprevious case at %v", prev.Left, prev.Line())
|
2016-06-01 11:34:28 -07:00
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2015-02-27 20:44:45 +00:00
|
|
|
// walk generates an AST that implements sw,
|
|
|
|
|
// where sw is a type switch.
|
|
|
|
|
// The AST is generally of the form of a linear
|
|
|
|
|
// search using if..goto, although binary search
|
|
|
|
|
// is used with long runs of concrete types.
|
|
|
|
|
func (s *typeSwitch) walk(sw *Node) {
|
2015-05-26 21:30:20 -04:00
|
|
|
cond := sw.Left
|
|
|
|
|
sw.Left = nil
|
|
|
|
|
|
|
|
|
|
if cond == nil {
|
2016-03-08 15:10:26 -08:00
|
|
|
sw.List.Set(nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
return
|
|
|
|
|
}
|
2015-05-26 21:30:20 -04:00
|
|
|
if cond.Right == nil {
|
2015-02-13 14:40:36 -05:00
|
|
|
setlineno(sw)
|
|
|
|
|
Yyerror("type switch must have an assignment")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
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.Right = walkexpr(cond.Right, &sw.Ninit)
|
2016-04-01 13:36:24 -07:00
|
|
|
if !cond.Right.Type.IsInterface() {
|
2015-02-13 14:40:36 -05:00
|
|
|
Yyerror("type switch must be on an interface")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-27 14:31:33 -08:00
|
|
|
var cas []*Node
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-27 20:44:45 +00:00
|
|
|
// predeclare temporary variables and the boolean var
|
2015-05-26 21:30:20 -04:00
|
|
|
s.facename = temp(cond.Right.Type)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-05-26 21:30:20 -04:00
|
|
|
a := Nod(OAS, s.facename, cond.Right)
|
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
|
|
|
a = typecheck(a, Etop)
|
2016-02-27 14:31:33 -08:00
|
|
|
cas = append(cas, a)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-27 20:44:45 +00:00
|
|
|
s.okname = temp(Types[TBOOL])
|
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
|
|
|
s.okname = typecheck(s.okname, Erv)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-27 20:44:45 +00:00
|
|
|
s.hashname = temp(Types[TUINT32])
|
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
|
|
|
s.hashname = typecheck(s.hashname, Erv)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-27 20:44:45 +00:00
|
|
|
// set up labels and jumps
|
|
|
|
|
casebody(sw, s.facename)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-06-03 12:05:32 -07:00
|
|
|
clauses := s.genCaseClauses(sw.List.Slice())
|
2016-03-08 15:10:26 -08:00
|
|
|
sw.List.Set(nil)
|
2016-05-31 13:11:15 -07:00
|
|
|
def := clauses.defjmp
|
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.
|
|
|
|
|
|
|
|
|
|
// Get interface descriptor word.
|
|
|
|
|
typ := Nod(OITAB, s.facename, nil)
|
|
|
|
|
|
|
|
|
|
// Check for nil first.
|
|
|
|
|
i := Nod(OIF, nil, nil)
|
|
|
|
|
i.Left = Nod(OEQ, typ, nodnil())
|
2016-05-31 13:11:15 -07:00
|
|
|
if clauses.niljmp != nil {
|
2016-02-21 20:43:14 -08:00
|
|
|
// Do explicit nil case right here.
|
2016-05-31 13:11:15 -07:00
|
|
|
i.Nbody.Set1(clauses.niljmp)
|
2016-02-21 20:43:14 -08:00
|
|
|
} else {
|
|
|
|
|
// Jump to default case.
|
2016-08-15 21:09:39 -07:00
|
|
|
lbl := autolabel(".s")
|
2016-03-10 10:13:42 -08:00
|
|
|
i.Nbody.Set1(Nod(OGOTO, lbl, nil))
|
2016-02-21 20:43:14 -08:00
|
|
|
// Wrap default case with label.
|
|
|
|
|
blk := Nod(OBLOCK, nil, nil)
|
2016-03-08 15:10:26 -08:00
|
|
|
blk.List.Set([]*Node{Nod(OLABEL, lbl, nil), def})
|
2016-02-21 20:43:14 -08:00
|
|
|
def = blk
|
|
|
|
|
}
|
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
|
|
|
i.Left = typecheck(i.Left, Erv)
|
2016-02-27 14:31:33 -08:00
|
|
|
cas = append(cas, i)
|
2016-02-21 20:43:14 -08:00
|
|
|
|
2016-04-01 13:36:24 -07:00
|
|
|
if !cond.Right.Type.IsEmptyInterface() {
|
2016-02-21 20:43:14 -08:00
|
|
|
// Load type from itab.
|
2016-06-06 08:29:52 -07:00
|
|
|
typ = itabType(typ)
|
2016-02-21 20:43:14 -08:00
|
|
|
}
|
|
|
|
|
// Load hash from type.
|
cmd/compile: change ODOT and friends to use Sym, not Right
The Node type ODOT and its variants all represent a selector, with a
simple name to the right of the dot. Before this change this was
represented by using an ONAME Node in the Right field. This ONAME node
served no useful purpose. This CL changes these Node types to store the
symbol in the Sym field instead, thus not requiring allocating a Node
for each selector.
When compiling x/tools/go/types this CL eliminates nearly 5000 calls to
newname and reduces the total number of Nodes allocated by about 6.6%.
It seems to cut compilation time by 1 to 2 percent.
Getting this right was somewhat subtle, and I added two dubious changes
to produce the exact same output as before. One is to ishairy in
inl.go: the ONAME node increased the cost of ODOT and friends by 1, and
I retained that, although really ODOT is not more expensive than any
other node. The other is to varexpr in walk.go: because the ONAME in
the Right field of an ODOT has no class, varexpr would always return
false for an ODOT, although in fact for some ODOT's it seemingly ought
to return true; I added an && false for now. I will send separate CLs,
that will break toolstash -cmp, to clean these up.
This CL passes toolstash -cmp.
Change-Id: I4af8a10cc59078c436130ce472f25abc3a9b2f80
Reviewed-on: https://go-review.googlesource.com/20890
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
2016-03-18 16:52:30 -07:00
|
|
|
h := NodSym(ODOTPTR, typ, nil)
|
2016-02-21 20:43:14 -08:00
|
|
|
h.Type = Types[TUINT32]
|
|
|
|
|
h.Typecheck = 1
|
|
|
|
|
h.Xoffset = int64(2 * Widthptr) // offset of hash in runtime._type
|
|
|
|
|
h.Bounded = true // guaranteed not to fault
|
|
|
|
|
a = Nod(OAS, s.hashname, h)
|
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
|
|
|
a = typecheck(a, Etop)
|
2016-02-27 14:31:33 -08:00
|
|
|
cas = append(cas, a)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-05-31 13:11:15 -07:00
|
|
|
cc := clauses.list
|
|
|
|
|
|
2015-02-27 20:44:45 +00:00
|
|
|
// insert type equality check into each case block
|
|
|
|
|
for _, c := range cc {
|
2016-06-01 15:21:56 -07:00
|
|
|
c.node.Right = s.typeone(c.node)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2015-02-27 20:44:45 +00:00
|
|
|
// generate list of if statements, binary search for constant sequences
|
|
|
|
|
for len(cc) > 0 {
|
2016-06-01 15:21:56 -07:00
|
|
|
if !cc[0].isconst {
|
2015-02-27 20:44:45 +00:00
|
|
|
n := cc[0].node
|
2016-02-27 14:31:33 -08:00
|
|
|
cas = append(cas, n.Right)
|
2015-02-27 20:44:45 +00:00
|
|
|
cc = cc[1:]
|
2015-02-13 14:40:36 -05:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// identify run of constants
|
2015-02-27 20:44:45 +00:00
|
|
|
var run int
|
2016-06-01 15:21:56 -07:00
|
|
|
for run = 1; run < len(cc) && cc[run].isconst; run++ {
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// sort by hash
|
2015-02-27 20:44:45 +00:00
|
|
|
sort.Sort(caseClauseByType(cc[:run]))
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
// for debugging: linear search
|
|
|
|
|
if false {
|
2015-02-27 20:44:45 +00:00
|
|
|
for i := 0; i < run; i++ {
|
|
|
|
|
n := cc[i].node
|
2016-02-27 14:31:33 -08:00
|
|
|
cas = append(cas, n.Right)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// combine adjacent cases with the same hash
|
2015-02-27 20:44:45 +00:00
|
|
|
ncase := 0
|
|
|
|
|
for i := 0; i < run; i++ {
|
2015-02-13 14:40:36 -05:00
|
|
|
ncase++
|
2016-03-08 10:26:20 -08:00
|
|
|
hash := []*Node{cc[i].node.Right}
|
2015-02-27 20:44:45 +00:00
|
|
|
for j := i + 1; j < run && cc[i].hash == cc[j].hash; j++ {
|
2016-03-08 10:26:20 -08:00
|
|
|
hash = append(hash, cc[j].node.Right)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2015-02-27 20:44:45 +00:00
|
|
|
cc[i].node.Right = liststmt(hash)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// binary search among cases to narrow by hash
|
2016-02-27 14:31:33 -08:00
|
|
|
cas = append(cas, s.walkCases(cc[:ncase]))
|
2015-02-27 20:44:45 +00:00
|
|
|
cc = cc[ncase:]
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2015-02-27 20:44:45 +00:00
|
|
|
// handle default case
|
2015-02-13 14:40:36 -05:00
|
|
|
if nerrors == 0 {
|
2016-02-27 14:31:33 -08:00
|
|
|
cas = append(cas, def)
|
2016-09-14 13:19:20 -07:00
|
|
|
sw.Nbody.Prepend(cas...)
|
2016-03-08 15:10:26 -08:00
|
|
|
sw.List.Set(nil)
|
2016-03-07 22:54:46 -08:00
|
|
|
walkstmtlist(sw.Nbody.Slice())
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-27 20:44:45 +00:00
|
|
|
// typeone generates an AST that jumps to the
|
|
|
|
|
// case body if the variable is of type t.
|
|
|
|
|
func (s *typeSwitch) typeone(t *Node) *Node {
|
2015-05-27 10:43:53 -04:00
|
|
|
var name *Node
|
2016-03-08 10:26:20 -08:00
|
|
|
var init []*Node
|
2016-03-08 15:10:26 -08:00
|
|
|
if t.Rlist.Len() == 0 {
|
2015-02-27 20:44:45 +00:00
|
|
|
name = nblank
|
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
|
|
|
nblank = typecheck(nblank, Erv|Easgn)
|
2015-02-27 20:44:45 +00:00
|
|
|
} else {
|
2016-03-08 15:10:26 -08:00
|
|
|
name = t.Rlist.First()
|
2016-03-08 10:26:20 -08:00
|
|
|
init = []*Node{Nod(ODCL, name, nil)}
|
2015-07-30 00:46:42 -04:00
|
|
|
a := Nod(OAS, name, nil)
|
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
|
|
|
a = typecheck(a, Etop)
|
2016-03-08 10:26:20 -08:00
|
|
|
init = append(init, a)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2015-02-27 20:44:45 +00:00
|
|
|
a := Nod(OAS2, nil, nil)
|
2016-03-08 15:10:26 -08:00
|
|
|
a.List.Set([]*Node{name, s.okname}) // name, ok =
|
2015-02-27 20:44:45 +00:00
|
|
|
b := Nod(ODOTTYPE, s.facename, nil)
|
|
|
|
|
b.Type = t.Left.Type // interface.(type)
|
2016-03-10 10:13:42 -08:00
|
|
|
a.Rlist.Set1(b)
|
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
|
|
|
a = typecheck(a, Etop)
|
2016-03-08 10:26:20 -08:00
|
|
|
init = append(init, a)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-27 20:44:45 +00:00
|
|
|
c := Nod(OIF, nil, nil)
|
2015-05-26 21:30:20 -04:00
|
|
|
c.Left = s.okname
|
2016-03-10 10:13:42 -08:00
|
|
|
c.Nbody.Set1(t.Right) // if ok { goto l }
|
2015-02-27 20:44:45 +00:00
|
|
|
|
2016-03-08 10:26:20 -08:00
|
|
|
return liststmt(append(init, c))
|
2015-02-27 20:44:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// walkCases generates an AST implementing the cases in cc.
|
2016-05-31 13:11:15 -07:00
|
|
|
func (s *typeSwitch) walkCases(cc []caseClause) *Node {
|
2015-02-27 20:44:45 +00:00
|
|
|
if len(cc) < binarySearchMin {
|
2016-03-08 10:26:20 -08:00
|
|
|
var cas []*Node
|
2015-02-27 20:44:45 +00:00
|
|
|
for _, c := range cc {
|
|
|
|
|
n := c.node
|
2016-06-01 15:21:56 -07:00
|
|
|
if !c.isconst {
|
2015-08-30 23:10:03 +02:00
|
|
|
Fatalf("typeSwitch walkCases")
|
2015-02-27 20:44:45 +00:00
|
|
|
}
|
|
|
|
|
a := Nod(OIF, nil, nil)
|
2016-09-15 14:34:20 +10:00
|
|
|
a.Left = Nod(OEQ, s.hashname, nodintconst(int64(c.hash)))
|
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
|
|
|
a.Left = typecheck(a.Left, Erv)
|
2016-03-10 10:13:42 -08:00
|
|
|
a.Nbody.Set1(n.Right)
|
2016-03-08 10:26:20 -08:00
|
|
|
cas = append(cas, a)
|
2015-02-27 20:44:45 +00:00
|
|
|
}
|
|
|
|
|
return liststmt(cas)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2015-02-27 20:44:45 +00:00
|
|
|
// find the middle and recur
|
|
|
|
|
half := len(cc) / 2
|
|
|
|
|
a := Nod(OIF, nil, nil)
|
2016-09-15 14:34:20 +10:00
|
|
|
a.Left = Nod(OLE, s.hashname, nodintconst(int64(cc[half-1].hash)))
|
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
|
|
|
a.Left = typecheck(a.Left, Erv)
|
2016-03-10 10:13:42 -08:00
|
|
|
a.Nbody.Set1(s.walkCases(cc[:half]))
|
|
|
|
|
a.Rlist.Set1(s.walkCases(cc[half:]))
|
2015-02-27 20:44:45 +00:00
|
|
|
return a
|
|
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-06-17 14:50:39 -07:00
|
|
|
// caseClauseByConstVal sorts clauses by constant value to enable binary search.
|
|
|
|
|
type caseClauseByConstVal []caseClause
|
|
|
|
|
|
|
|
|
|
func (x caseClauseByConstVal) Len() int { return len(x) }
|
|
|
|
|
func (x caseClauseByConstVal) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
|
|
|
|
func (x caseClauseByConstVal) Less(i, j int) bool {
|
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
|
|
|
// n1 and n2 might be individual constants or integer ranges.
|
|
|
|
|
// We have checked for duplicates already,
|
|
|
|
|
// so ranges can be safely represented by any value in the range.
|
|
|
|
|
n1 := x[i].node
|
|
|
|
|
var v1 interface{}
|
|
|
|
|
if s := n1.List.Slice(); s != nil {
|
|
|
|
|
v1 = s[0].Val().U
|
|
|
|
|
} else {
|
|
|
|
|
v1 = n1.Left.Val().U
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
n2 := x[j].node
|
|
|
|
|
var v2 interface{}
|
|
|
|
|
if s := n2.List.Slice(); s != nil {
|
|
|
|
|
v2 = s[0].Val().U
|
|
|
|
|
} else {
|
|
|
|
|
v2 = n2.Left.Val().U
|
|
|
|
|
}
|
2016-06-17 14:50:39 -07:00
|
|
|
|
|
|
|
|
switch v1 := v1.(type) {
|
|
|
|
|
case *Mpflt:
|
|
|
|
|
return v1.Cmp(v2.(*Mpflt)) < 0
|
|
|
|
|
case *Mpint:
|
|
|
|
|
return v1.Cmp(v2.(*Mpint)) < 0
|
|
|
|
|
case string:
|
2015-03-17 16:10:31 -07:00
|
|
|
// 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 in exprSwitch.walkCases.
|
2016-06-17 14:50:39 -07:00
|
|
|
a := v1
|
|
|
|
|
b := v2.(string)
|
|
|
|
|
if len(a) != len(b) {
|
|
|
|
|
return len(a) < len(b)
|
2015-09-23 23:31:17 +02:00
|
|
|
}
|
2016-06-17 14:50:39 -07:00
|
|
|
return a < b
|
2015-02-27 20:44:45 +00:00
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-06-17 14:50:39 -07:00
|
|
|
Fatalf("caseClauseByConstVal passed bad clauses %v < %v", x[i].node.Left, x[j].node.Left)
|
|
|
|
|
return false
|
2015-02-27 20:44:45 +00:00
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-05-31 13:11:15 -07:00
|
|
|
type caseClauseByType []caseClause
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-27 20:44:45 +00:00
|
|
|
func (x caseClauseByType) Len() int { return len(x) }
|
|
|
|
|
func (x caseClauseByType) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
|
|
|
|
func (x caseClauseByType) Less(i, j int) bool {
|
|
|
|
|
c1, c2 := x[i], x[j]
|
2016-06-01 11:34:28 -07:00
|
|
|
// sort by hash code, then ordinal (for the rare case of hash collisions)
|
|
|
|
|
if c1.hash != c2.hash {
|
2015-02-27 20:44:45 +00:00
|
|
|
return c1.hash < c2.hash
|
|
|
|
|
}
|
|
|
|
|
return c1.ordinal < c2.ordinal
|
|
|
|
|
}
|
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
|
|
|
|
|
|
|
|
type constIntNodesByVal []*Node
|
|
|
|
|
|
|
|
|
|
func (x constIntNodesByVal) Len() int { return len(x) }
|
|
|
|
|
func (x constIntNodesByVal) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
|
|
|
|
func (x constIntNodesByVal) Less(i, j int) bool {
|
|
|
|
|
return x[i].Val().U.(*Mpint).Cmp(x[j].Val().U.(*Mpint)) < 0
|
|
|
|
|
}
|