Commit 57279ba7 authored by Josh Bleecher Snyder's avatar Josh Bleecher Snyder

cmd/internal/gc: separate func-only Node fields

Nodes dominate gc's memory usage, but many fields are only used
for a subset of kinds of nodes. This change pulls out fields
used only for func-like Nodes. This reduces the size of the
Node struct on a 64-bit machine from 504 bytes to 416 bytes (-17%).

Compiling the runtime, 1.5% of nodes have a non-nil Func.
In html/template, 2.7% of nodes have a non-nil Func.

This change introduces an extra alloc and associated GC overhead
when Func is non-nil. However, when Func is nil, as it almost
always is, it spares the garbage collector scanning some Node fields.
Empirically, this change appears to be roughly neutral with regard to GC.

To keep the diff readable, this CL uses an embedded Func field.
A subsequent CL will unembed the field.

Passes toolstash -cmp.

Change-Id: Ide86aa954b097fb8e6154f0811d3691497477004
Reviewed-on: https://go-review.googlesource.com/7360Reviewed-by: default avatarRuss Cox <rsc@golang.org>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent 9fd87bd5
...@@ -191,7 +191,7 @@ func makeclosure(func_ *Node) *Node { ...@@ -191,7 +191,7 @@ func makeclosure(func_ *Node) *Node {
// create the function // create the function
xfunc := Nod(ODCLFUNC, nil, nil) xfunc := Nod(ODCLFUNC, nil, nil)
xfunc.Nname = newname(closurename(func_)) xfunc.Nname = newfuncname(closurename(func_))
xfunc.Nname.Sym.Flags |= SymExported // disable export xfunc.Nname.Sym.Flags |= SymExported // disable export
xfunc.Nname.Ntype = xtype xfunc.Nname.Ntype = xtype
xfunc.Nname.Defn = xfunc xfunc.Nname.Defn = xfunc
...@@ -581,7 +581,7 @@ func makepartialcall(fn *Node, t0 *Type, meth *Node) *Node { ...@@ -581,7 +581,7 @@ func makepartialcall(fn *Node, t0 *Type, meth *Node) *Node {
xtype.Rlist = l xtype.Rlist = l
xfunc.Dupok = true xfunc.Dupok = true
xfunc.Nname = newname(sym) xfunc.Nname = newfuncname(sym)
xfunc.Nname.Sym.Flags |= SymExported // disable export xfunc.Nname.Sym.Flags |= SymExported // disable export
xfunc.Nname.Ntype = xtype xfunc.Nname.Ntype = xtype
xfunc.Nname.Defn = xfunc xfunc.Nname.Defn = xfunc
......
...@@ -373,6 +373,14 @@ func newname(s *Sym) *Node { ...@@ -373,6 +373,14 @@ func newname(s *Sym) *Node {
return n return n
} }
// newfuncname generates a new name node for a function or method.
// TODO(rsc): Use an ODCLFUNC node instead. See comment in CL 7360.
func newfuncname(s *Sym) *Node {
n := newname(s)
n.Func = new(Func)
return n
}
/* /*
* this generates a new name node for a name * this generates a new name node for a name
* being declared. * being declared.
...@@ -542,6 +550,7 @@ func ifacedcl(n *Node) { ...@@ -542,6 +550,7 @@ func ifacedcl(n *Node) {
Yyerror("methods must have a unique non-blank name") Yyerror("methods must have a unique non-blank name")
} }
n.Func = new(Func)
dclcontext = PPARAM dclcontext = PPARAM
markdcl() markdcl()
Funcdepth++ Funcdepth++
...@@ -1312,7 +1321,7 @@ func methodname1(n *Node, t *Node) *Node { ...@@ -1312,7 +1321,7 @@ func methodname1(n *Node, t *Node) *Node {
} }
if t.Sym == nil || isblank(n) { if t.Sym == nil || isblank(n) {
return newname(n.Sym) return newfuncname(n.Sym)
} }
var p string var p string
...@@ -1323,9 +1332,9 @@ func methodname1(n *Node, t *Node) *Node { ...@@ -1323,9 +1332,9 @@ func methodname1(n *Node, t *Node) *Node {
} }
if exportname(t.Sym.Name) { if exportname(t.Sym.Name) {
n = newname(Lookup(p)) n = newfuncname(Lookup(p))
} else { } else {
n = newname(Pkglookup(p, t.Sym.Pkg)) n = newfuncname(Pkglookup(p, t.Sym.Pkg))
} }
return n return n
...@@ -1476,7 +1485,7 @@ func funcsym(s *Sym) *Sym { ...@@ -1476,7 +1485,7 @@ func funcsym(s *Sym) *Sym {
s1 := Pkglookup(s.Name+"·f", s.Pkg) s1 := Pkglookup(s.Name+"·f", s.Pkg)
if s1.Def == nil { if s1.Def == nil {
s1.Def = newname(s1) s1.Def = newfuncname(s1)
s1.Def.Shortname = newname(s) s1.Def.Shortname = newname(s)
funcsyms = list(funcsyms, s1.Def) funcsyms = list(funcsyms, s1.Def)
} }
......
...@@ -239,7 +239,7 @@ func dumpexportvar(s *Sym) { ...@@ -239,7 +239,7 @@ func dumpexportvar(s *Sym) {
dumpexporttype(t) dumpexporttype(t)
if t.Etype == TFUNC && n.Class == PFUNC { if t.Etype == TFUNC && n.Class == PFUNC {
if n.Inl != nil { if n.Func != nil && n.Inl != nil {
// when lazily typechecking inlined bodies, some re-exported ones may not have been typechecked yet. // when lazily typechecking inlined bodies, some re-exported ones may not have been typechecked yet.
// currently that can leave unresolved ONONAMEs in import-dot-ed packages in the wrong package // currently that can leave unresolved ONONAMEs in import-dot-ed packages in the wrong package
if Debug['l'] < 2 { if Debug['l'] < 2 {
......
...@@ -1358,7 +1358,7 @@ fndcl: ...@@ -1358,7 +1358,7 @@ fndcl:
t.Rlist = $5; t.Rlist = $5;
$$ = Nod(ODCLFUNC, nil, nil); $$ = Nod(ODCLFUNC, nil, nil);
$$.Nname = newname($1); $$.Nname = newfuncname($1);
$$.Nname.Defn = $$; $$.Nname.Defn = $$;
$$.Nname.Ntype = t; // TODO: check if nname already has an ntype $$.Nname.Ntype = t; // TODO: check if nname already has an ntype
declare($$.Nname, PFUNC); declare($$.Nname, PFUNC);
...@@ -1392,7 +1392,7 @@ fndcl: ...@@ -1392,7 +1392,7 @@ fndcl:
t.Rlist = $8; t.Rlist = $8;
$$ = Nod(ODCLFUNC, nil, nil); $$ = Nod(ODCLFUNC, nil, nil);
$$.Shortname = newname($4); $$.Shortname = newfuncname($4);
$$.Nname = methodname1($$.Shortname, rcvr.Right); $$.Nname = methodname1($$.Shortname, rcvr.Right);
$$.Nname.Defn = $$; $$.Nname.Defn = $$;
$$.Nname.Ntype = t; $$.Nname.Ntype = t;
...@@ -1422,7 +1422,7 @@ hidden_fndcl: ...@@ -1422,7 +1422,7 @@ hidden_fndcl:
Yyerror("inconsistent definition for func %v during import\n\t%v\n\t%v", Sconv(s, 0), Tconv(s.Def.Type, 0), Tconv(t, 0)); Yyerror("inconsistent definition for func %v during import\n\t%v\n\t%v", Sconv(s, 0), Tconv(s.Def.Type, 0), Tconv(t, 0));
} }
$$ = newname(s); $$ = newfuncname(s);
$$.Type = t; $$.Type = t;
declare($$, PFUNC); declare($$, PFUNC);
......
...@@ -169,7 +169,7 @@ func ishairy(n *Node, budget *int) bool { ...@@ -169,7 +169,7 @@ func ishairy(n *Node, budget *int) bool {
switch n.Op { switch n.Op {
// Call is okay if inlinable and we have the budget for the body. // Call is okay if inlinable and we have the budget for the body.
case OCALLFUNC: case OCALLFUNC:
if n.Left.Inl != nil { if n.Left.Func != nil && n.Left.Inl != nil {
*budget -= int(n.Left.InlCost) *budget -= int(n.Left.InlCost)
break break
} }
...@@ -247,7 +247,9 @@ func inlcopy(n *Node) *Node { ...@@ -247,7 +247,9 @@ func inlcopy(n *Node) *Node {
m := Nod(OXXX, nil, nil) m := Nod(OXXX, nil, nil)
*m = *n *m = *n
m.Inl = nil if m.Func != nil {
m.Inl = nil
}
m.Left = inlcopy(n.Left) m.Left = inlcopy(n.Left)
m.Right = inlcopy(n.Right) m.Right = inlcopy(n.Right)
m.List = inlcopylist(n.List) m.List = inlcopylist(n.List)
...@@ -457,7 +459,7 @@ func inlnode(np **Node) { ...@@ -457,7 +459,7 @@ func inlnode(np **Node) {
if Debug['m'] > 3 { if Debug['m'] > 3 {
fmt.Printf("%v:call to func %v\n", n.Line(), Nconv(n.Left, obj.FmtSign)) fmt.Printf("%v:call to func %v\n", n.Line(), Nconv(n.Left, obj.FmtSign))
} }
if n.Left.Inl != nil { // normal case if n.Left.Func != nil && n.Left.Inl != nil { // normal case
mkinlcall(np, n.Left, n.Isddd) mkinlcall(np, n.Left, n.Isddd)
} else if n.Left.Op == ONAME && n.Left.Left != nil && n.Left.Left.Op == OTYPE && n.Left.Right != nil && n.Left.Right.Op == ONAME { // methods called as functions } else if n.Left.Op == ONAME && n.Left.Left != nil && n.Left.Left.Op == OTYPE && n.Left.Right != nil && n.Left.Right.Op == ONAME { // methods called as functions
if n.Left.Sym.Def != nil { if n.Left.Sym.Def != nil {
......
...@@ -394,6 +394,10 @@ func Nod(op int, nleft *Node, nright *Node) *Node { ...@@ -394,6 +394,10 @@ func Nod(op int, nleft *Node, nright *Node) *Node {
n.Xoffset = BADWIDTH n.Xoffset = BADWIDTH
n.Orig = n n.Orig = n
n.Curfn = Curfn n.Curfn = Curfn
switch op {
case OCLOSURE, ODCLFUNC:
n.Func = new(Func)
}
return n return n
} }
......
...@@ -23,58 +23,45 @@ type Node struct { ...@@ -23,58 +23,45 @@ type Node struct {
List *NodeList List *NodeList
Rlist *NodeList Rlist *NodeList
Op uint8 Op uint8
Nointerface bool Nointerface bool
Ullman uint8 // sethi/ullman number Ullman uint8 // sethi/ullman number
Addable uint8 // type of addressability - 0 is not addressable Addable uint8 // type of addressability - 0 is not addressable
Etype uint8 // op for OASOP, etype for OTYPE, exclam for export Etype uint8 // op for OASOP, etype for OTYPE, exclam for export
Bounded bool // bounds check unnecessary Bounded bool // bounds check unnecessary
Class uint8 // PPARAM, PAUTO, PEXTERN, etc Class uint8 // PPARAM, PAUTO, PEXTERN, etc
Method uint8 // OCALLMETH name Method uint8 // OCALLMETH name
Embedded uint8 // ODCLFIELD embedded type Embedded uint8 // ODCLFIELD embedded type
Colas uint8 // OAS resulting from := Colas uint8 // OAS resulting from :=
Diag uint8 // already printed error about this Diag uint8 // already printed error about this
Noescape bool // func arguments do not escape Noescape bool // func arguments do not escape; TODO(rsc): move Noescape to Func struct (see CL 7360)
Nosplit bool // func should not execute on separate stack Walkdef uint8
Nowritebarrier bool // emit compiler error instead of write barrier Typecheck uint8
Walkdef uint8 Local bool
Typecheck uint8 Dodata uint8
Local bool Initorder uint8
Dodata uint8 Used bool
Initorder uint8 Isddd bool // is the argument variadic
Used bool Readonly bool
Isddd bool // is the argument variadic Implicit bool
Readonly bool Addrtaken bool // address taken, even if not moved to heap
Implicit bool Assigned bool // is the variable ever assigned to
Addrtaken bool // address taken, even if not moved to heap Captured bool // is the variable captured by a closure
Assigned bool // is the variable ever assigned to Byval bool // is the variable captured by value or by reference
Captured bool // is the variable captured by a closure Reslice bool // this is a reslice x = x[0:y] or x = append(x, ...)
Byval bool // is the variable captured by value or by reference Likely int8 // likeliness of if statement
Dupok bool // duplicate definitions ok (for func) Hasbreak bool // has break statement
Wrapper bool // is method wrapper (for func) Needzero bool // if it contains pointers, needs to be zeroed on function entry
Reslice bool // this is a reslice x = x[0:y] or x = append(x, ...) Esc uint8 // EscXXX
Likely int8 // likeliness of if statement Funcdepth int32
Hasbreak bool // has break statement
Needzero bool // if it contains pointers, needs to be zeroed on function entry
Needctxt bool // function uses context register (has closure variables)
Esc uint8 // EscXXX
Funcdepth int32
// most nodes // most nodes
Type *Type Type *Type
Orig *Node // original form, for printing, and tracking copies of ONAMEs Orig *Node // original form, for printing, and tracking copies of ONAMEs
Nname *Node
// func // func
Nname *Node *Func
Shortname *Node
Enter *NodeList
Exit *NodeList
Cvars *NodeList // closure params
Dcl *NodeList // autodcl for this func/closure
Inl *NodeList // copy of the body for use in inlining
Inldcl *NodeList // copy of dcl for use in inlining
Closgen int
Outerfunc *Node
// OLITERAL/OREGISTER // OLITERAL/OREGISTER
Val Val Val Val
...@@ -112,18 +99,39 @@ type Node struct { ...@@ -112,18 +99,39 @@ type Node struct {
Escretval *NodeList // on OCALLxxx, list of dummy return values Escretval *NodeList // on OCALLxxx, list of dummy return values
Escloopdepth int // -1: global, 0: return variables, 1:function top level, increased inside function for every loop or label to mark scopes Escloopdepth int // -1: global, 0: return variables, 1:function top level, increased inside function for every loop or label to mark scopes
Sym *Sym // various Sym *Sym // various
InlCost int32 // unique name for OTYPE/ONAME Vargen int32 // unique name for OTYPE/ONAME
Vargen int32 Lineno int32
Lineno int32 Xoffset int64
Stkdelta int64 // offset added by stack frame compaction phase.
Ostk int32 // 6g only
Iota int32
Walkgen uint32
Esclevel int32
Opt interface{} // for optimization passes
}
// Func holds Node fields used only with function-like nodes.
type Func struct {
Shortname *Node
Enter *NodeList
Exit *NodeList
Cvars *NodeList // closure params
Dcl *NodeList // autodcl for this func/closure
Inldcl *NodeList // copy of dcl for use in inlining
Closgen int
Outerfunc *Node
Inl *NodeList // copy of the body for use in inlining
InlCost int32
Endlineno int32 Endlineno int32
Xoffset int64
Stkdelta int64 // offset added by stack frame compaction phase. Nosplit bool // func should not execute on separate stack
Ostk int32 Nowritebarrier bool // emit compiler error instead of write barrier
Iota int32 Dupok bool // duplicate definitions ok
Walkgen uint32 Wrapper bool // is method wrapper
Esclevel int32 Needctxt bool // function uses context register (has closure variables)
Opt interface{} // for optimization passes
} }
// Node ops. // Node ops.
......
...@@ -2523,7 +2523,7 @@ yydefault: ...@@ -2523,7 +2523,7 @@ yydefault:
t.Rlist = yyDollar[5].list t.Rlist = yyDollar[5].list
yyVAL.node = Nod(ODCLFUNC, nil, nil) yyVAL.node = Nod(ODCLFUNC, nil, nil)
yyVAL.node.Nname = newname(yyDollar[1].sym) yyVAL.node.Nname = newfuncname(yyDollar[1].sym)
yyVAL.node.Nname.Defn = yyVAL.node yyVAL.node.Nname.Defn = yyVAL.node
yyVAL.node.Nname.Ntype = t // TODO: check if nname already has an ntype yyVAL.node.Nname.Ntype = t // TODO: check if nname already has an ntype
declare(yyVAL.node.Nname, PFUNC) declare(yyVAL.node.Nname, PFUNC)
...@@ -2559,7 +2559,7 @@ yydefault: ...@@ -2559,7 +2559,7 @@ yydefault:
t.Rlist = yyDollar[8].list t.Rlist = yyDollar[8].list
yyVAL.node = Nod(ODCLFUNC, nil, nil) yyVAL.node = Nod(ODCLFUNC, nil, nil)
yyVAL.node.Shortname = newname(yyDollar[4].sym) yyVAL.node.Shortname = newfuncname(yyDollar[4].sym)
yyVAL.node.Nname = methodname1(yyVAL.node.Shortname, rcvr.Right) yyVAL.node.Nname = methodname1(yyVAL.node.Shortname, rcvr.Right)
yyVAL.node.Nname.Defn = yyVAL.node yyVAL.node.Nname.Defn = yyVAL.node
yyVAL.node.Nname.Ntype = t yyVAL.node.Nname.Ntype = t
...@@ -2589,7 +2589,7 @@ yydefault: ...@@ -2589,7 +2589,7 @@ yydefault:
Yyerror("inconsistent definition for func %v during import\n\t%v\n\t%v", Sconv(s, 0), Tconv(s.Def.Type, 0), Tconv(t, 0)) Yyerror("inconsistent definition for func %v during import\n\t%v\n\t%v", Sconv(s, 0), Tconv(s.Def.Type, 0), Tconv(t, 0))
} }
yyVAL.node = newname(s) yyVAL.node = newfuncname(s)
yyVAL.node.Type = t yyVAL.node.Type = t
declare(yyVAL.node, PFUNC) declare(yyVAL.node, PFUNC)
......
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