Commit d553c29d authored by Matthew Dempsky's avatar Matthew Dempsky

cmd/compile: directly construct Fields instead of ODCLFIELD nodes

Avoids some garbage allocations while loading import data. Seems to
especially benefit html/template for some reason, but significant
allocation improvements for other packages too.

name       old time/op     new time/op     delta
Template       345ms ± 6%      332ms ± 6%   -3.76%        (p=0.000 n=49+47)
Unicode        185ms ±10%      184ms ±12%     ~           (p=0.401 n=50+49)
GoTypes        1.04s ± 3%      1.04s ± 3%   -0.72%        (p=0.012 n=48+47)
Compiler       4.52s ± 7%      4.49s ± 9%     ~           (p=0.465 n=48+47)

name       old user-ns/op  new user-ns/op  delta
Template        532M ±17%       471M ±23%  -11.48%        (p=0.000 n=50+50)
Unicode         298M ±29%       311M ±28%     ~           (p=0.065 n=50+50)
GoTypes        1.52G ± 7%      1.54G ± 9%     ~           (p=0.062 n=49+50)
Compiler       6.37G ± 7%      6.42G ± 8%     ~           (p=0.157 n=49+48)

name       old alloc/op    new alloc/op    delta
Template      43.9MB ± 0%     42.3MB ± 0%   -3.51%        (p=0.000 n=48+48)
Unicode       34.3MB ± 0%     34.3MB ± 0%     ~           (p=0.945 n=50+50)
GoTypes        123MB ± 0%      122MB ± 0%   -0.82%        (p=0.000 n=50+50)
Compiler       522MB ± 0%      519MB ± 0%   -0.51%        (p=0.000 n=50+50)

name       old allocs/op   new allocs/op   delta
Template        414k ± 0%       397k ± 0%   -4.14%        (p=0.000 n=50+49)
Unicode         320k ± 0%       320k ± 0%     ~           (p=0.988 n=48+49)
GoTypes        1.18M ± 0%      1.17M ± 0%   -0.97%        (p=0.000 n=50+50)
Compiler       4.44M ± 0%      4.41M ± 0%   -0.66%        (p=0.000 n=50+50)

Passes toolstash.

Change-Id: I0f54c0fa420d4f4ed3584c47cec0dde100c70c03
Reviewed-on: https://go-review.googlesource.com/31670
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: 's avatarRobert Griesemer <gri@golang.org>
parent 6ca662ca
......@@ -330,7 +330,7 @@ func (p *importer) obj(tag int) {
params := p.paramList()
result := p.paramList()
sig := functype(nil, params, result)
sig := functypefield(nil, params, result)
importsym(sym, ONAME)
if sym.Def != nil && sym.Def.Op == ONAME {
// function was imported before (via another import)
......@@ -465,8 +465,15 @@ func (p *importer) typ() *Type {
result := p.paramList()
nointerface := p.bool()
n := methodname(newname(sym), recv[0].Right)
n.Type = functype(recv[0], params, result)
base := recv[0].Type
star := false
if base.IsPtr() {
base = base.Elem()
star = true
}
n := methodname0(sym, star, base.Sym)
n.Type = functypefield(recv[0], params, result)
checkwidth(n.Type)
addmethod(sym, n.Type, false, nointerface)
p.funcList = append(p.funcList, n)
......@@ -506,7 +513,8 @@ func (p *importer) typ() *Type {
case structTag:
t = p.newtyp(TSTRUCT)
tostruct0(t, p.fieldList())
t.SetFields(p.fieldList())
checkwidth(t)
case pointerTag:
t = p.newtyp(Tptr)
......@@ -516,14 +524,15 @@ func (p *importer) typ() *Type {
t = p.newtyp(TFUNC)
params := p.paramList()
result := p.paramList()
functype0(t, nil, params, result)
functypefield0(t, nil, params, result)
case interfaceTag:
t = p.newtyp(TINTER)
if p.int() != 0 {
formatErrorf("unexpected embedded interface")
}
tointerface0(t, p.methodList())
t.SetFields(p.methodList())
checkwidth(t)
case mapTag:
t = p.newtyp(TMAP)
......@@ -555,9 +564,9 @@ func (p *importer) qualifiedName() *Sym {
}
// parser.go:hidden_structdcl_list
func (p *importer) fieldList() (fields []*Node) {
func (p *importer) fieldList() (fields []*Field) {
if n := p.int(); n > 0 {
fields = make([]*Node, n)
fields = make([]*Field, n)
for i := range fields {
fields[i] = p.field()
}
......@@ -566,37 +575,35 @@ func (p *importer) fieldList() (fields []*Node) {
}
// parser.go:hidden_structdcl
func (p *importer) field() *Node {
func (p *importer) field() *Field {
p.pos()
sym := p.fieldName()
typ := p.typ()
note := p.string()
var n *Node
if sym.Name != "" {
n = nod(ODCLFIELD, newname(sym), typenod(typ))
} else {
f := newField()
if sym.Name == "" {
// anonymous field - typ must be T or *T and T must be a type name
s := typ.Sym
if s == nil && typ.IsPtr() {
s = typ.Elem().Sym // deref
}
pkg := importpkg
if sym != nil {
pkg = sym.Pkg
}
n = embedded(s, pkg)
n.Right = typenod(typ)
sym = sym.Pkg.Lookup(s.Name)
f.Embedded = 1
}
n.SetVal(Val{U: note})
return n
f.Sym = sym
f.Nname = newname(sym)
f.Type = typ
f.Note = note
return f
}
// parser.go:hidden_interfacedcl_list
func (p *importer) methodList() (methods []*Node) {
func (p *importer) methodList() (methods []*Field) {
if n := p.int(); n > 0 {
methods = make([]*Node, n)
methods = make([]*Field, n)
for i := range methods {
methods[i] = p.method()
}
......@@ -605,12 +612,17 @@ func (p *importer) methodList() (methods []*Node) {
}
// parser.go:hidden_interfacedcl
func (p *importer) method() *Node {
func (p *importer) method() *Field {
p.pos()
sym := p.fieldName()
params := p.paramList()
result := p.paramList()
return nod(ODCLFIELD, newname(sym), typenod(functype(fakethis(), params, result)))
f := newField()
f.Sym = sym
f.Nname = newname(sym)
f.Type = functypefield(fakethisfield(), params, result)
return f
}
// parser.go:sym,hidden_importsym
......@@ -632,7 +644,7 @@ func (p *importer) fieldName() *Sym {
}
// parser.go:ohidden_funarg_list
func (p *importer) paramList() []*Node {
func (p *importer) paramList() []*Field {
i := p.int()
if i == 0 {
return nil
......@@ -644,27 +656,23 @@ func (p *importer) paramList() []*Node {
named = false
}
// i > 0
n := make([]*Node, i)
for i := range n {
n[i] = p.param(named)
fs := make([]*Field, i)
for i := range fs {
fs[i] = p.param(named)
}
return n
return fs
}
// parser.go:hidden_funarg
func (p *importer) param(named bool) *Node {
typ := p.typ()
isddd := false
if typ.Etype == TDDDFIELD {
func (p *importer) param(named bool) *Field {
f := newField()
f.Type = p.typ()
if f.Type.Etype == TDDDFIELD {
// TDDDFIELD indicates wrapped ... slice type
typ = typSlice(typ.DDDField())
isddd = true
f.Type = typSlice(f.Type.DDDField())
f.Isddd = true
}
n := nod(ODCLFIELD, nil, typenod(typ))
n.Isddd = isddd
if named {
name := p.string()
if name == "" {
......@@ -676,14 +684,15 @@ func (p *importer) param(named bool) *Node {
if name != "_" {
pkg = p.pkg()
}
n.Left = newname(pkg.Lookup(name))
f.Sym = pkg.Lookup(name)
f.Nname = newname(f.Sym)
}
// TODO(gri) This is compiler-specific (escape info).
// Move into compiler-specific section eventually?
n.SetVal(Val{U: p.string()})
f.Note = p.string()
return n
return f
}
func (p *importer) value(typ *Type) (x Val) {
......
......@@ -853,6 +853,22 @@ func tofunargs(l []*Node, funarg Funarg) *Type {
return t
}
func tofunargsfield(fields []*Field, funarg Funarg) *Type {
t := typ(TSTRUCT)
t.StructType().Funarg = funarg
for _, f := range fields {
f.Funarg = funarg
// esc.go needs to find f given a PPARAM to add the tag.
if f.Nname != nil && f.Nname.Class == PPARAM {
f.Nname.Name.Param.Field = f
}
}
t.SetFields(fields)
return t
}
func interfacefield(n *Node) *Field {
lno := lineno
lineno = n.Lineno
......@@ -994,6 +1010,12 @@ func fakethis() *Node {
return n
}
func fakethisfield() *Field {
f := newField()
f.Type = ptrto(typ(TSTRUCT))
return f
}
// Is this field a method on an interface?
// Those methods have an anonymous *struct{} as the receiver.
// (See fakethis above.)
......@@ -1048,6 +1070,30 @@ func functype0(t *Type, this *Node, in, out []*Node) {
}
}
func functypefield(this *Field, in, out []*Field) *Type {
t := typ(TFUNC)
functypefield0(t, this, in, out)
return t
}
func functypefield0(t *Type, this *Field, in, out []*Field) {
var rcvr []*Field
if this != nil {
rcvr = []*Field{this}
}
t.FuncType().Receiver = tofunargsfield(rcvr, FunargRcvr)
t.FuncType().Results = tofunargsfield(out, FunargRcvr)
t.FuncType().Params = tofunargsfield(in, FunargRcvr)
t.FuncType().Outnamed = false
if len(out) > 0 && out[0].Nname != nil && out[0].Nname.Orig != nil {
s := out[0].Nname.Orig.Sym
if s != nil && (s.Name[0] != '~' || s.Name[1] != 'r') { // ~r%d is the name invented for an unnamed result
t.FuncType().Outnamed = true
}
}
}
var methodsym_toppkg *Pkg
func methodsym(nsym *Sym, t0 *Type, iface int) *Sym {
......@@ -1119,30 +1165,34 @@ bad:
}
func methodname(n *Node, t *Node) *Node {
star := ""
star := false
if t.Op == OIND {
star = "*"
star = true
t = t.Left
}
if t.Sym == nil || isblank(n) {
return newfuncname(n.Sym)
return methodname0(n.Sym, star, t.Sym)
}
func methodname0(s *Sym, star bool, tsym *Sym) *Node {
if tsym == nil || isblanksym(s) {
return newfuncname(s)
}
var p string
if star != "" {
p = fmt.Sprintf("(%s%v).%v", star, t.Sym, n.Sym)
if star {
p = fmt.Sprintf("(*%v).%v", tsym, s)
} else {
p = fmt.Sprintf("%v.%v", t.Sym, n.Sym)
p = fmt.Sprintf("%v.%v", tsym, s)
}
if exportname(t.Sym.Name) {
n = newfuncname(lookup(p))
if exportname(tsym.Name) {
s = lookup(p)
} else {
n = newfuncname(Pkglookup(p, t.Sym.Pkg))
s = Pkglookup(p, tsym.Pkg)
}
return n
return newfuncname(s)
}
// Add a method, declared as a function.
......@@ -1208,9 +1258,6 @@ func addmethod(msym *Sym, t *Type, local, nointerface bool) {
}
}
n := nod(ODCLFIELD, newname(msym), nil)
n.Type = t
for _, f := range mt.Methods().Slice() {
if msym.Name != f.Sym.Name {
continue
......@@ -1223,7 +1270,10 @@ func addmethod(msym *Sym, t *Type, local, nointerface bool) {
return
}
f := structfield(n)
f := newField()
f.Sym = msym
f.Nname = newname(msym)
f.Type = t
f.Nointerface = nointerface
mt.Methods().Append(f)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment