mirror of
https://github.com/golang/go
synced 2024-11-23 14:30:02 -07:00
cmd/compile: improve inlining and static analysis
When inlining a function call "f()", if "f" contains exactly 1 "return" statement and doesn't name its result parameters, it's inlined to declare+initialize the result value using the AST representation that's compatible with staticValue. Also, extend staticValue to skip over OCONVNOP nodes (often introduced by inlining), and fix various bits of code related to handling method expressions. Updates #33160. Change-Id: If8652e319f0a5700cf9d40a7a62e369a2a359229 Reviewed-on: https://go-review.googlesource.com/c/go/+/266199 Trust: Matthew Dempsky <mdempsky@google.com> Run-TryBot: Matthew Dempsky <mdempsky@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: David Chase <drchase@google.com>
This commit is contained in:
parent
0b798c46cd
commit
f2c0c2b902
@ -257,21 +257,39 @@ func inlFlood(n *Node) {
|
|||||||
|
|
||||||
typecheckinl(n)
|
typecheckinl(n)
|
||||||
|
|
||||||
|
// Recursively identify all referenced functions for
|
||||||
|
// reexport. We want to include even non-called functions,
|
||||||
|
// because after inlining they might be callable.
|
||||||
inspectList(asNodes(n.Func.Inl.Body), 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
|
switch n.Class() {
|
||||||
// functions for reexport. Skip methods,
|
case PFUNC:
|
||||||
// because they're reexported alongside their
|
if n.isMethodExpression() {
|
||||||
// receiver type.
|
inlFlood(asNode(n.Type.Nname()))
|
||||||
if n.Class() == PEXTERN || n.Class() == PFUNC && !n.isMethodExpression() {
|
} else {
|
||||||
|
inlFlood(n)
|
||||||
|
exportsym(n)
|
||||||
|
}
|
||||||
|
case PEXTERN:
|
||||||
exportsym(n)
|
exportsym(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
case OCALLFUNC, OCALLMETH:
|
case ODOTMETH:
|
||||||
// Recursively flood any functions called by
|
fn := asNode(n.Type.Nname())
|
||||||
// this one.
|
inlFlood(fn)
|
||||||
inlFlood(asNode(n.Left.Type.Nname()))
|
|
||||||
|
case OCALLPART:
|
||||||
|
// Okay, because we don't yet inline indirect
|
||||||
|
// calls to method values.
|
||||||
|
case OCLOSURE:
|
||||||
|
// If the closure is inlinable, we'll need to
|
||||||
|
// flood it too. But today we don't support
|
||||||
|
// inlining functions that contain closures.
|
||||||
|
//
|
||||||
|
// When we do, we'll probably want:
|
||||||
|
// inlFlood(n.Func.Closure.Func.Nname)
|
||||||
|
Fatalf("unexpected closure in inlinable function")
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
@ -706,7 +724,14 @@ func inlCallee(fn *Node) *Node {
|
|||||||
switch {
|
switch {
|
||||||
case fn.Op == ONAME && fn.Class() == PFUNC:
|
case fn.Op == ONAME && fn.Class() == PFUNC:
|
||||||
if fn.isMethodExpression() {
|
if fn.isMethodExpression() {
|
||||||
return asNode(fn.Sym.Def)
|
n := asNode(fn.Type.Nname())
|
||||||
|
// Check that receiver type matches fn.Left.
|
||||||
|
// TODO(mdempsky): Handle implicit dereference
|
||||||
|
// of pointer receiver argument?
|
||||||
|
if n == nil || !types.Identical(n.Type.Recv().Type, fn.Left.Type) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return n
|
||||||
}
|
}
|
||||||
return fn
|
return fn
|
||||||
case fn.Op == OCLOSURE:
|
case fn.Op == OCLOSURE:
|
||||||
@ -719,6 +744,11 @@ func inlCallee(fn *Node) *Node {
|
|||||||
|
|
||||||
func staticValue(n *Node) *Node {
|
func staticValue(n *Node) *Node {
|
||||||
for {
|
for {
|
||||||
|
if n.Op == OCONVNOP {
|
||||||
|
n = n.Left
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
n1 := staticValue1(n)
|
n1 := staticValue1(n)
|
||||||
if n1 == nil {
|
if n1 == nil {
|
||||||
return n
|
return n
|
||||||
@ -1009,15 +1039,28 @@ func mkinlcall(n, fn *Node, maxCost int32, inlMap map[*Node]bool) *Node {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nreturns := 0
|
||||||
|
inspectList(asNodes(fn.Func.Inl.Body), func(n *Node) bool {
|
||||||
|
if n != nil && n.Op == ORETURN {
|
||||||
|
nreturns++
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
// We can delay declaring+initializing result parameters if:
|
||||||
|
// (1) there's only one "return" statement in the inlined
|
||||||
|
// function, and (2) the result parameters aren't named.
|
||||||
|
delayretvars := nreturns == 1
|
||||||
|
|
||||||
// temporaries for return values.
|
// temporaries for return values.
|
||||||
var retvars []*Node
|
var retvars []*Node
|
||||||
for i, t := range fn.Type.Results().Fields().Slice() {
|
for i, t := range fn.Type.Results().Fields().Slice() {
|
||||||
var m *Node
|
var m *Node
|
||||||
mpos := t.Pos
|
|
||||||
if n := asNode(t.Nname); n != nil && !n.isBlank() {
|
if n := asNode(t.Nname); n != nil && !n.isBlank() {
|
||||||
m = inlvar(n)
|
m = inlvar(n)
|
||||||
m = typecheck(m, ctxExpr)
|
m = typecheck(m, ctxExpr)
|
||||||
inlvars[n] = m
|
inlvars[n] = m
|
||||||
|
delayretvars = false // found a named result parameter
|
||||||
} else {
|
} else {
|
||||||
// anonymous return values, synthesize names for use in assignment that replaces return
|
// anonymous return values, synthesize names for use in assignment that replaces return
|
||||||
m = retvar(t, i)
|
m = retvar(t, i)
|
||||||
@ -1029,12 +1072,11 @@ func mkinlcall(n, fn *Node, maxCost int32, inlMap map[*Node]bool) *Node {
|
|||||||
// were not part of the original callee.
|
// were not part of the original callee.
|
||||||
if !strings.HasPrefix(m.Sym.Name, "~R") {
|
if !strings.HasPrefix(m.Sym.Name, "~R") {
|
||||||
m.Name.SetInlFormal(true)
|
m.Name.SetInlFormal(true)
|
||||||
m.Pos = mpos
|
m.Pos = t.Pos
|
||||||
inlfvars = append(inlfvars, m)
|
inlfvars = append(inlfvars, m)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ninit.Append(nod(ODCL, m, nil))
|
|
||||||
retvars = append(retvars, m)
|
retvars = append(retvars, m)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1095,12 +1137,15 @@ func mkinlcall(n, fn *Node, maxCost int32, inlMap map[*Node]bool) *Node {
|
|||||||
ninit.Append(vas)
|
ninit.Append(vas)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !delayretvars {
|
||||||
// Zero the return parameters.
|
// Zero the return parameters.
|
||||||
for _, n := range retvars {
|
for _, n := range retvars {
|
||||||
|
ninit.Append(nod(ODCL, n, nil))
|
||||||
ras := nod(OAS, n, nil)
|
ras := nod(OAS, n, nil)
|
||||||
ras = typecheck(ras, ctxStmt)
|
ras = typecheck(ras, ctxStmt)
|
||||||
ninit.Append(ras)
|
ninit.Append(ras)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
retlabel := autolabel(".i")
|
retlabel := autolabel(".i")
|
||||||
|
|
||||||
@ -1132,6 +1177,7 @@ func mkinlcall(n, fn *Node, maxCost int32, inlMap map[*Node]bool) *Node {
|
|||||||
subst := inlsubst{
|
subst := inlsubst{
|
||||||
retlabel: retlabel,
|
retlabel: retlabel,
|
||||||
retvars: retvars,
|
retvars: retvars,
|
||||||
|
delayretvars: delayretvars,
|
||||||
inlvars: inlvars,
|
inlvars: inlvars,
|
||||||
bases: make(map[*src.PosBase]*src.PosBase),
|
bases: make(map[*src.PosBase]*src.PosBase),
|
||||||
newInlIndex: newIndex,
|
newInlIndex: newIndex,
|
||||||
@ -1230,6 +1276,10 @@ type inlsubst struct {
|
|||||||
// Temporary result variables.
|
// Temporary result variables.
|
||||||
retvars []*Node
|
retvars []*Node
|
||||||
|
|
||||||
|
// Whether result variables should be initialized at the
|
||||||
|
// "return" statement.
|
||||||
|
delayretvars bool
|
||||||
|
|
||||||
inlvars map[*Node]*Node
|
inlvars map[*Node]*Node
|
||||||
|
|
||||||
// bases maps from original PosBase to PosBase with an extra
|
// bases maps from original PosBase to PosBase with an extra
|
||||||
@ -1298,6 +1348,14 @@ func (subst *inlsubst) node(n *Node) *Node {
|
|||||||
as.List.Append(n)
|
as.List.Append(n)
|
||||||
}
|
}
|
||||||
as.Rlist.Set(subst.list(n.List))
|
as.Rlist.Set(subst.list(n.List))
|
||||||
|
|
||||||
|
if subst.delayretvars {
|
||||||
|
for _, n := range as.List.Slice() {
|
||||||
|
as.Ninit.Append(nod(ODCL, n, nil))
|
||||||
|
n.Name.Defn = as
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
as = typecheck(as, ctxStmt)
|
as = typecheck(as, ctxStmt)
|
||||||
m.Ninit.Append(as)
|
m.Ninit.Append(as)
|
||||||
}
|
}
|
||||||
|
@ -75,8 +75,19 @@ func (v *bottomUpVisitor) visit(n *Node) uint32 {
|
|||||||
|
|
||||||
inspectList(n.Nbody, func(n *Node) bool {
|
inspectList(n.Nbody, func(n *Node) bool {
|
||||||
switch n.Op {
|
switch n.Op {
|
||||||
case OCALLFUNC, OCALLMETH:
|
case ONAME:
|
||||||
fn := asNode(n.Left.Type.Nname())
|
if n.Class() == PFUNC {
|
||||||
|
if n.isMethodExpression() {
|
||||||
|
n = asNode(n.Type.Nname())
|
||||||
|
}
|
||||||
|
if n != nil && n.Name.Defn != nil {
|
||||||
|
if m := v.visit(n.Name.Defn); m < min {
|
||||||
|
min = m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case ODOTMETH:
|
||||||
|
fn := asNode(n.Type.Nname())
|
||||||
if fn != nil && fn.Op == ONAME && fn.Class() == PFUNC && fn.Name.Defn != nil {
|
if fn != nil && fn.Op == ONAME && fn.Class() == PFUNC && fn.Name.Defn != nil {
|
||||||
if m := v.visit(fn.Name.Defn); m < min {
|
if m := v.visit(fn.Name.Defn); m < min {
|
||||||
min = m
|
min = m
|
||||||
|
Loading…
Reference in New Issue
Block a user