cmd/compile: add OSTRUCTKEY for keyed struct literals

Previously, we used OKEY nodes to represent keyed struct literal
elements. The field names were represented by an ONAME node, but this
is clumsy because it's the only remaining case where ONAME was used to
represent a bare identifier and not a variable.

This CL introduces a new OSTRUCTKEY node op for use in struct
literals. These ops instead store the field name in the node's own Sym
field. This is similar in spirit to golang.org/cl/20890.

Significant reduction in allocations for struct literal heavy code
like package unicode:

name       old time/op     new time/op     delta
Template       345ms ± 6%      341ms ± 6%     ~           (p=0.141 n=29+28)
Unicode        200ms ± 9%      184ms ± 7%   -7.77%        (p=0.000 n=29+30)
GoTypes        1.04s ± 3%      1.05s ± 3%     ~           (p=0.096 n=30+30)
Compiler       4.47s ± 9%      4.49s ± 6%     ~           (p=0.890 n=29+29)

name       old user-ns/op  new user-ns/op  delta
Template        523M ±13%       516M ±17%     ~           (p=0.400 n=29+30)
Unicode         334M ±27%       314M ±30%     ~           (p=0.093 n=30+30)
GoTypes        1.53G ±10%      1.52G ±10%     ~           (p=0.572 n=30+30)
Compiler       6.28G ± 7%      6.34G ±11%     ~           (p=0.300 n=30+30)

name       old alloc/op    new alloc/op    delta
Template      44.5MB ± 0%     44.4MB ± 0%   -0.35%        (p=0.000 n=27+30)
Unicode       39.2MB ± 0%     34.5MB ± 0%  -11.79%        (p=0.000 n=26+30)
GoTypes        125MB ± 0%      125MB ± 0%   -0.12%        (p=0.000 n=29+30)
Compiler       515MB ± 0%      515MB ± 0%   -0.10%        (p=0.000 n=29+30)

name       old allocs/op   new allocs/op   delta
Template        426k ± 0%       424k ± 0%   -0.39%        (p=0.000 n=29+30)
Unicode         374k ± 0%       323k ± 0%  -13.67%        (p=0.000 n=29+30)
GoTypes        1.21M ± 0%      1.21M ± 0%   -0.14%        (p=0.000 n=29+29)
Compiler       4.40M ± 0%      4.39M ± 0%   -0.13%        (p=0.000 n=29+30)

Passes toolstash/buildall.

Change-Id: Iba4ee765dd1748f67e52fcade1cd75c9f6e13fa9
Reviewed-on: https://go-review.googlesource.com/30974
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
Matthew Dempsky 2016-10-12 15:48:18 -07:00
parent 032e2bd1eb
commit add3ff549a
11 changed files with 110 additions and 99 deletions

View file

@ -622,6 +622,9 @@ func getdyn(n *Node, top bool) initGenType {
var mode initGenType
for _, n1 := range n.List.Slice() {
value := n1.Right
if n.Op == OSTRUCTLIT {
value = n1.Left
}
mode |= getdyn(value, false)
if mode == initDynamic|initConst {
break
@ -635,17 +638,22 @@ func isStaticCompositeLiteral(n *Node) bool {
switch n.Op {
case OSLICELIT:
return false
case OARRAYLIT, OSTRUCTLIT:
case OARRAYLIT:
for _, r := range n.List.Slice() {
if r.Op != OKEY {
Fatalf("isStaticCompositeLiteral: rhs not OKEY: %v", r)
}
index := r.Left
if n.Op == OARRAYLIT && index.Op != OLITERAL {
if r.Left.Op != OLITERAL || !isStaticCompositeLiteral(r.Right) {
return false
}
value := r.Right
if !isStaticCompositeLiteral(value) {
}
return true
case OSTRUCTLIT:
for _, r := range n.List.Slice() {
if r.Op != OSTRUCTKEY {
Fatalf("isStaticCompositeLiteral: rhs not OSTRUCTKEY: %v", r)
}
if !isStaticCompositeLiteral(r.Left) {
return false
}
}
@ -689,40 +697,44 @@ const (
// fixedlit handles struct, array, and slice literals.
// TODO: expand documentation.
func fixedlit(ctxt initContext, kind initKind, n *Node, var_ *Node, init *Nodes) {
var indexnode func(*Node) *Node
var splitnode func(*Node) (a *Node, value *Node)
switch n.Op {
case OARRAYLIT, OSLICELIT:
indexnode = func(index *Node) *Node { return nod(OINDEX, var_, index) }
splitnode = func(r *Node) (*Node, *Node) {
if r.Op != OKEY {
Fatalf("fixedlit: rhs not OKEY: %v", r)
}
return nod(OINDEX, var_, r.Left), r.Right
}
case OSTRUCTLIT:
indexnode = func(index *Node) *Node { return nodSym(ODOT, var_, index.Sym) }
splitnode = func(r *Node) (*Node, *Node) {
if r.Op != OSTRUCTKEY {
Fatalf("fixedlit: rhs not OSTRUCTKEY: %v", r)
}
return nodSym(ODOT, var_, r.Sym), r.Left
}
default:
Fatalf("fixedlit bad op: %v", n.Op)
}
for _, r := range n.List.Slice() {
if r.Op != OKEY {
Fatalf("fixedlit: rhs not OKEY: %v", r)
}
index := r.Left
value := r.Right
a, value := splitnode(r)
switch value.Op {
case OSLICELIT:
if (kind == initKindStatic && ctxt == inNonInitFunction) || (kind == initKindDynamic && ctxt == inInitFunction) {
a := indexnode(index)
slicelit(ctxt, value, a, init)
continue
}
case OARRAYLIT, OSTRUCTLIT:
a := indexnode(index)
fixedlit(ctxt, kind, value, a, init)
continue
}
islit := isliteral(value)
if n.Op == OARRAYLIT {
islit = islit && isliteral(index)
islit = islit && isliteral(r.Left)
}
if (kind == initKindStatic && !islit) || (kind == initKindDynamic && islit) {
continue
@ -730,7 +742,7 @@ func fixedlit(ctxt initContext, kind initKind, n *Node, var_ *Node, init *Nodes)
// build list of assignments: var[index] = expr
setlineno(value)
a := nod(OAS, indexnode(index), value)
a = nod(OAS, a, value)
a = typecheck(a, Etop)
switch kind {
case initKindStatic:
@ -1235,10 +1247,10 @@ func initplan(n *Node) {
case OSTRUCTLIT:
for _, a := range n.List.Slice() {
if a.Op != OKEY || a.Left.Type != structkey {
if a.Op != OSTRUCTKEY {
Fatalf("initplan fixedlit")
}
addvalue(p, a.Left.Xoffset, a.Right)
addvalue(p, a.Xoffset, a.Left)
}
case OMAPLIT:
@ -1294,13 +1306,21 @@ func iszero(n *Node) bool {
return u.Real.CmpFloat64(0) == 0 && u.Imag.CmpFloat64(0) == 0
}
case OARRAYLIT, OSTRUCTLIT:
case OARRAYLIT:
for _, n1 := range n.List.Slice() {
if !iszero(n1.Right) {
return false
}
}
return true
case OSTRUCTLIT:
for _, n1 := range n.List.Slice() {
if !iszero(n1.Left) {
return false
}
}
return true
}
return false