Commit 562a1999 authored by Matthew Dempsky's avatar Matthew Dempsky

cmd/compile: extract inline related fields into separate Inline type

Inl, Inldcl, and InlCost are only applicable to functions with bodies
that can be inlined, so pull them out into a separate Inline type to
make understanding them easier.

A side benefit is that we can check if a function can be inlined by
just checking if n.Func.Inl is non-nil, which simplifies handling of
empty function bodies.

While here, remove some unnecessary Curfn twiddling, and make imported
functions use Inl.Dcl instead of Func.Dcl for consistency for local
functions.

Passes toolstash-check.

Change-Id: Ifd4a80349d85d9e8e4484952b38ec4a63182e81f
Reviewed-on: https://go-review.googlesource.com/104756
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarRobert Griesemer <gri@golang.org>
parent f2b5f750
...@@ -377,11 +377,11 @@ func export(out *bufio.Writer, trace bool) int { ...@@ -377,11 +377,11 @@ func export(out *bufio.Writer, trace bool) int {
// function has inlineable body: // function has inlineable body:
// write index and body // write index and body
if p.trace { if p.trace {
p.tracef("\n----\nfunc { %#v }\n", f.Inl) p.tracef("\n----\nfunc { %#v }\n", asNodes(f.Inl.Body))
} }
p.int(i) p.int(i)
p.int(int(f.InlCost)) p.int(int(f.Inl.Cost))
p.stmtList(f.Inl) p.stmtList(asNodes(f.Inl.Body))
if p.trace { if p.trace {
p.tracef("\n") p.tracef("\n")
} }
......
...@@ -188,28 +188,22 @@ func Import(imp *types.Pkg, in *bufio.Reader) { ...@@ -188,28 +188,22 @@ func Import(imp *types.Pkg, in *bufio.Reader) {
// parameter renaming which doesn't matter if we don't have a body. // parameter renaming which doesn't matter if we don't have a body.
inlCost := p.int() inlCost := p.int()
if f := p.funcList[i]; f != nil && f.Func.Inl.Len() == 0 { if f := p.funcList[i]; f != nil && f.Func.Inl == nil {
// function not yet imported - read body and set it // function not yet imported - read body and set it
funchdr(f) funchdr(f)
body := p.stmtList() body := p.stmtList()
if body == nil { funcbody()
// Make sure empty body is not interpreted as f.Func.Inl = &Inline{
// no inlineable body (see also parser.fnbody) Cost: int32(inlCost),
// (not doing so can cause significant performance Body: body,
// degradation due to unnecessary calls to empty }
// functions). if Debug['E'] > 0 && Debug['m'] > 2 {
body = []*Node{nod(OEMPTY, nil, nil)}
}
f.Func.Inl.Set(body)
f.Func.InlCost = int32(inlCost)
if Debug['E'] > 0 && Debug['m'] > 2 && f.Func.Inl.Len() != 0 {
if Debug['m'] > 3 { if Debug['m'] > 3 {
fmt.Printf("inl body for %v: %+v\n", f, f.Func.Inl) fmt.Printf("inl body for %v: %+v\n", f, asNodes(body))
} else { } else {
fmt.Printf("inl body for %v: %v\n", f, f.Func.Inl) fmt.Printf("inl body for %v: %v\n", f, asNodes(body))
} }
} }
funcbody()
} else { } else {
// function already imported - read body but discard declarations // function already imported - read body but discard declarations
dclcontext = PDISCARD // throw away any declarations dclcontext = PDISCARD // throw away any declarations
......
...@@ -70,7 +70,7 @@ func typecheckinl(fn *Node) { ...@@ -70,7 +70,7 @@ func typecheckinl(fn *Node) {
} }
if Debug['m'] > 2 || Debug_export != 0 { if Debug['m'] > 2 || Debug_export != 0 {
fmt.Printf("typecheck import [%v] %L { %#v }\n", fn.Sym, fn, fn.Func.Inl) fmt.Printf("typecheck import [%v] %L { %#v }\n", fn.Sym, fn, asNodes(fn.Func.Inl.Body))
} }
save_safemode := safemode save_safemode := safemode
...@@ -78,9 +78,16 @@ func typecheckinl(fn *Node) { ...@@ -78,9 +78,16 @@ func typecheckinl(fn *Node) {
savefn := Curfn savefn := Curfn
Curfn = fn Curfn = fn
typecheckslice(fn.Func.Inl.Slice(), Etop) typecheckslice(fn.Func.Inl.Body, Etop)
Curfn = savefn Curfn = savefn
// During typechecking, declarations are added to
// Curfn.Func.Dcl. Move them to Inl.Dcl for consistency with
// how local functions behave. (Append because typecheckinl
// may be called multiple times.)
fn.Func.Inl.Dcl = append(fn.Func.Inl.Dcl, fn.Func.Dcl...)
fn.Func.Dcl = nil
safemode = save_safemode safemode = save_safemode
lineno = lno lineno = lno
...@@ -155,26 +162,21 @@ func caninl(fn *Node) { ...@@ -155,26 +162,21 @@ func caninl(fn *Node) {
return return
} }
savefn := Curfn n.Func.Inl = &Inline{
Curfn = fn Cost: maxBudget - visitor.budget,
Dcl: inlcopylist(n.Name.Defn.Func.Dcl),
n.Func.Inl.Set(fn.Nbody.Slice()) Body: inlcopylist(fn.Nbody.Slice()),
fn.Nbody.Set(inlcopylist(n.Func.Inl.Slice())) }
inldcl := inlcopylist(n.Name.Defn.Func.Dcl)
n.Func.Inldcl.Set(inldcl)
n.Func.InlCost = maxBudget - visitor.budget
// hack, TODO, check for better way to link method nodes back to the thing with the ->inl // hack, TODO, check for better way to link method nodes back to the thing with the ->inl
// this is so export can find the body of a method // this is so export can find the body of a method
fn.Type.FuncType().Nname = asTypesNode(n) fn.Type.FuncType().Nname = asTypesNode(n)
if Debug['m'] > 1 { if Debug['m'] > 1 {
fmt.Printf("%v: can inline %#v as: %#v { %#v }\n", fn.Line(), n, fn.Type, n.Func.Inl) fmt.Printf("%v: can inline %#v as: %#v { %#v }\n", fn.Line(), n, fn.Type, asNodes(n.Func.Inl.Body))
} else if Debug['m'] != 0 { } else if Debug['m'] != 0 {
fmt.Printf("%v: can inline %v\n", fn.Line(), n) fmt.Printf("%v: can inline %v\n", fn.Line(), n)
} }
Curfn = savefn
} }
// inlFlood marks n's inline body for export and recursively ensures // inlFlood marks n's inline body for export and recursively ensures
...@@ -189,7 +191,7 @@ func inlFlood(n *Node) { ...@@ -189,7 +191,7 @@ func inlFlood(n *Node) {
if n.Func == nil { if n.Func == nil {
Fatalf("inlFlood: missing Func on %v", n) Fatalf("inlFlood: missing Func on %v", n)
} }
if n.Func.Inl.Len() == 0 { if n.Func.Inl == nil {
return return
} }
...@@ -200,7 +202,7 @@ func inlFlood(n *Node) { ...@@ -200,7 +202,7 @@ func inlFlood(n *Node) {
typecheckinl(n) typecheckinl(n)
inspectList(n.Func.Inl, func(n *Node) bool { inspectList(asNodes(n.Func.Inl.Body), func(n *Node) bool {
switch n.Op { switch n.Op {
case ONAME: case ONAME:
// Mark any referenced global variables or // Mark any referenced global variables or
...@@ -259,13 +261,13 @@ func (v *hairyVisitor) visit(n *Node) bool { ...@@ -259,13 +261,13 @@ func (v *hairyVisitor) visit(n *Node) bool {
} }
} }
if fn := n.Left.Func; fn != nil && fn.Inl.Len() != 0 { if fn := n.Left.Func; fn != nil && fn.Inl != nil {
v.budget -= fn.InlCost v.budget -= fn.Inl.Cost
break break
} }
if n.Left.isMethodExpression() { if n.Left.isMethodExpression() {
if d := asNode(n.Left.Sym.Def); d != nil && d.Func.Inl.Len() != 0 { if d := asNode(n.Left.Sym.Def); d != nil && d.Func.Inl != nil {
v.budget -= d.Func.InlCost v.budget -= d.Func.Inl.Cost
break break
} }
} }
...@@ -300,8 +302,8 @@ func (v *hairyVisitor) visit(n *Node) bool { ...@@ -300,8 +302,8 @@ func (v *hairyVisitor) visit(n *Node) bool {
break break
} }
} }
if inlfn := asNode(t.FuncType().Nname).Func; inlfn.Inl.Len() != 0 { if inlfn := asNode(t.FuncType().Nname).Func; inlfn.Inl != nil {
v.budget -= inlfn.InlCost v.budget -= inlfn.Inl.Cost
break break
} }
if Debug['l'] < 4 { if Debug['l'] < 4 {
...@@ -394,7 +396,7 @@ func inlcopy(n *Node) *Node { ...@@ -394,7 +396,7 @@ func inlcopy(n *Node) *Node {
m := n.copy() m := n.copy()
if m.Func != nil { if m.Func != nil {
m.Func.Inl.Set(nil) Fatalf("unexpected Func: %v", m)
} }
m.Left = inlcopy(n.Left) m.Left = inlcopy(n.Left)
m.Right = inlcopy(n.Right) m.Right = inlcopy(n.Right)
...@@ -583,7 +585,7 @@ func inlnode(n *Node) *Node { ...@@ -583,7 +585,7 @@ func inlnode(n *Node) *Node {
if Debug['m'] > 3 { if Debug['m'] > 3 {
fmt.Printf("%v:call to func %+v\n", n.Line(), n.Left) fmt.Printf("%v:call to func %+v\n", n.Line(), n.Left)
} }
if n.Left.Func != nil && n.Left.Func.Inl.Len() != 0 && !isIntrinsicCall(n) { // normal case if n.Left.Func != nil && n.Left.Func.Inl != nil && !isIntrinsicCall(n) { // normal case
n = mkinlcall(n, n.Left) n = mkinlcall(n, n.Left)
} else if n.Left.isMethodExpression() && asNode(n.Left.Sym.Def) != nil { } else if n.Left.isMethodExpression() && asNode(n.Left.Sym.Def) != nil {
n = mkinlcall(n, asNode(n.Left.Sym.Def)) n = mkinlcall(n, asNode(n.Left.Sym.Def))
...@@ -647,7 +649,7 @@ func inlinableClosure(n *Node) *Node { ...@@ -647,7 +649,7 @@ func inlinableClosure(n *Node) *Node {
c := n.Func.Closure c := n.Func.Closure
caninl(c) caninl(c)
f := c.Func.Nname f := c.Func.Nname
if f == nil || f.Func.Inl.Len() == 0 { if f == nil || f.Func.Inl == nil {
return nil return nil
} }
return f return f
...@@ -772,7 +774,7 @@ var inlgen int ...@@ -772,7 +774,7 @@ var inlgen int
// The result of mkinlcall1 MUST be assigned back to n, e.g. // The result of mkinlcall1 MUST be assigned back to n, e.g.
// n.Left = mkinlcall1(n.Left, fn, isddd) // n.Left = mkinlcall1(n.Left, fn, isddd)
func mkinlcall1(n, fn *Node) *Node { func mkinlcall1(n, fn *Node) *Node {
if fn.Func.Inl.Len() == 0 { if fn.Func.Inl == nil {
// No inlinable body. // No inlinable body.
return n return n
} }
...@@ -798,7 +800,7 @@ func mkinlcall1(n, fn *Node) *Node { ...@@ -798,7 +800,7 @@ func mkinlcall1(n, fn *Node) *Node {
// We have a function node, and it has an inlineable body. // We have a function node, and it has an inlineable body.
if Debug['m'] > 1 { if Debug['m'] > 1 {
fmt.Printf("%v: inlining call to %v %#v { %#v }\n", n.Line(), fn.Sym, fn.Type, fn.Func.Inl) fmt.Printf("%v: inlining call to %v %#v { %#v }\n", n.Line(), fn.Sym, fn.Type, asNodes(fn.Func.Inl.Body))
} else if Debug['m'] != 0 { } else if Debug['m'] != 0 {
fmt.Printf("%v: inlining call to %v\n", n.Line(), fn) fmt.Printf("%v: inlining call to %v\n", n.Line(), fn)
} }
...@@ -814,12 +816,8 @@ func mkinlcall1(n, fn *Node) *Node { ...@@ -814,12 +816,8 @@ func mkinlcall1(n, fn *Node) *Node {
// record formals/locals for later post-processing // record formals/locals for later post-processing
var inlfvars []*Node var inlfvars []*Node
// Find declarations corresponding to inlineable body. // Handle captured variables when inlining closures.
var dcl []*Node
if fn.Name.Defn != nil { if fn.Name.Defn != nil {
dcl = fn.Func.Inldcl.Slice() // local function
// handle captured variables when inlining closures
if c := fn.Name.Defn.Func.Closure; c != nil { if c := fn.Name.Defn.Func.Closure; c != nil {
for _, v := range c.Func.Closure.Func.Cvars.Slice() { for _, v := range c.Func.Closure.Func.Cvars.Slice() {
if v.Op == OXXX { if v.Op == OXXX {
...@@ -854,11 +852,9 @@ func mkinlcall1(n, fn *Node) *Node { ...@@ -854,11 +852,9 @@ func mkinlcall1(n, fn *Node) *Node {
} }
} }
} }
} else {
dcl = fn.Func.Dcl // imported function
} }
for _, ln := range dcl { for _, ln := range fn.Func.Inl.Dcl {
if ln.Op != ONAME { if ln.Op != ONAME {
continue continue
} }
...@@ -1020,7 +1016,7 @@ func mkinlcall1(n, fn *Node) *Node { ...@@ -1020,7 +1016,7 @@ func mkinlcall1(n, fn *Node) *Node {
newInlIndex: newIndex, newInlIndex: newIndex,
} }
body := subst.list(fn.Func.Inl) body := subst.list(asNodes(fn.Func.Inl.Body))
lab := nod(OLABEL, retlabel, nil) lab := nod(OLABEL, retlabel, nil)
body = append(body, lab) body = append(body, lab)
......
...@@ -544,7 +544,7 @@ func Main(archInit func(*Arch)) { ...@@ -544,7 +544,7 @@ func Main(archInit func(*Arch)) {
// Typecheck imported function bodies if debug['l'] > 1, // Typecheck imported function bodies if debug['l'] > 1,
// otherwise lazily when used or re-exported. // otherwise lazily when used or re-exported.
for _, n := range importlist { for _, n := range importlist {
if n.Func.Inl.Len() != 0 { if n.Func.Inl != nil {
saveerrors() saveerrors()
typecheckinl(n) typecheckinl(n)
} }
......
...@@ -570,13 +570,8 @@ func createDwarfVars(fnsym *obj.LSym, fn *Func, automDecls []*Node) ([]*Node, [] ...@@ -570,13 +570,8 @@ func createDwarfVars(fnsym *obj.LSym, fn *Func, automDecls []*Node) ([]*Node, []
// with local vars; disregard this versioning when sorting. // with local vars; disregard this versioning when sorting.
func preInliningDcls(fnsym *obj.LSym) []*Node { func preInliningDcls(fnsym *obj.LSym) []*Node {
fn := Ctxt.DwFixups.GetPrecursorFunc(fnsym).(*Node) fn := Ctxt.DwFixups.GetPrecursorFunc(fnsym).(*Node)
var dcl, rdcl []*Node var rdcl []*Node
if fn.Name.Defn != nil { for _, n := range fn.Func.Inl.Dcl {
dcl = fn.Func.Inldcl.Slice() // local function
} else {
dcl = fn.Func.Dcl // imported function
}
for _, n := range dcl {
c := n.Sym.Name[0] c := n.Sym.Name[0]
// Avoid reporting "_" parameters, since if there are more than // Avoid reporting "_" parameters, since if there are more than
// one, it can result in a collision later on, as in #23179. // one, it can result in a collision later on, as in #23179.
......
...@@ -22,7 +22,7 @@ func TestSizeof(t *testing.T) { ...@@ -22,7 +22,7 @@ func TestSizeof(t *testing.T) {
_32bit uintptr // size on 32bit platforms _32bit uintptr // size on 32bit platforms
_64bit uintptr // size on 64bit platforms _64bit uintptr // size on 64bit platforms
}{ }{
{Func{}, 124, 224}, {Func{}, 116, 208},
{Name{}, 32, 56}, {Name{}, 32, 56},
{Param{}, 24, 48}, {Param{}, 24, 48},
{Node{}, 76, 128}, {Node{}, 76, 128},
......
...@@ -461,7 +461,6 @@ type Func struct { ...@@ -461,7 +461,6 @@ type Func struct {
Exit Nodes Exit Nodes
Cvars Nodes // closure params Cvars Nodes // closure params
Dcl []*Node // autodcl for this func/closure Dcl []*Node // autodcl for this func/closure
Inldcl Nodes // copy of dcl for use in inlining
// Parents records the parent scope of each scope within a // Parents records the parent scope of each scope within a
// function. The root scope (0) has no parent, so the i'th // function. The root scope (0) has no parent, so the i'th
...@@ -484,8 +483,7 @@ type Func struct { ...@@ -484,8 +483,7 @@ type Func struct {
Nname *Node Nname *Node
lsym *obj.LSym lsym *obj.LSym
Inl Nodes // copy of the body for use in inlining Inl *Inline
InlCost int32
Label int32 // largest auto-generated label in this function Label int32 // largest auto-generated label in this function
...@@ -502,6 +500,15 @@ type Func struct { ...@@ -502,6 +500,15 @@ type Func struct {
nwbrCalls *[]nowritebarrierrecCallSym nwbrCalls *[]nowritebarrierrecCallSym
} }
// An Inline holds fields used for function bodies that can be inlined.
type Inline struct {
Cost int32 // heuristic cost of inlining this function
// Copies of Func.Dcl and Nbody for use during inlining.
Dcl []*Node
Body []*Node
}
// A Mark represents a scope boundary. // A Mark represents a scope boundary.
type Mark struct { type Mark struct {
// Pos is the position of the token that marks the scope // Pos is the position of the token that marks the scope
...@@ -737,6 +744,11 @@ const ( ...@@ -737,6 +744,11 @@ const (
// a slice to save space. // a slice to save space.
type Nodes struct{ slice *[]*Node } type Nodes struct{ slice *[]*Node }
// asNodes returns a slice of *Node as a Nodes value.
func asNodes(s []*Node) Nodes {
return Nodes{&s}
}
// Slice returns the entries in Nodes as a slice. // Slice returns the entries in Nodes as a slice.
// Changes to the slice entries (as in s[i] = n) will be reflected in // Changes to the slice entries (as in s[i] = n) will be reflected in
// the Nodes. // the Nodes.
......
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