]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: improve inlining and static analysis
authorMatthew Dempsky <mdempsky@google.com>
Sat, 24 Oct 2020 09:08:06 +0000 (02:08 -0700)
committerMatthew Dempsky <mdempsky@google.com>
Thu, 29 Oct 2020 19:03:09 +0000 (19:03 +0000)
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>
src/cmd/compile/internal/gc/inl.go
src/cmd/compile/internal/gc/scc.go

index 137675aa209fd78cd5333515b9725490e0518b20..098c0c99d5ed1a86753ba836a4c8fa273838da50 100644 (file)
@@ -257,21 +257,39 @@ func inlFlood(n *Node) {
 
        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 {
                switch n.Op {
                case ONAME:
-                       // Mark any referenced global variables or
-                       // functions for reexport. Skip methods,
-                       // because they're reexported alongside their
-                       // receiver type.
-                       if n.Class() == PEXTERN || n.Class() == PFUNC && !n.isMethodExpression() {
+                       switch n.Class() {
+                       case PFUNC:
+                               if n.isMethodExpression() {
+                                       inlFlood(asNode(n.Type.Nname()))
+                               } else {
+                                       inlFlood(n)
+                                       exportsym(n)
+                               }
+                       case PEXTERN:
                                exportsym(n)
                        }
 
-               case OCALLFUNC, OCALLMETH:
-                       // Recursively flood any functions called by
-                       // this one.
-                       inlFlood(asNode(n.Left.Type.Nname()))
+               case ODOTMETH:
+                       fn := asNode(n.Type.Nname())
+                       inlFlood(fn)
+
+               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
        })
@@ -706,7 +724,14 @@ func inlCallee(fn *Node) *Node {
        switch {
        case fn.Op == ONAME && fn.Class() == PFUNC:
                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
        case fn.Op == OCLOSURE:
@@ -719,6 +744,11 @@ func inlCallee(fn *Node) *Node {
 
 func staticValue(n *Node) *Node {
        for {
+               if n.Op == OCONVNOP {
+                       n = n.Left
+                       continue
+               }
+
                n1 := staticValue1(n)
                if n1 == nil {
                        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.
        var retvars []*Node
        for i, t := range fn.Type.Results().Fields().Slice() {
                var m *Node
-               mpos := t.Pos
                if n := asNode(t.Nname); n != nil && !n.isBlank() {
                        m = inlvar(n)
                        m = typecheck(m, ctxExpr)
                        inlvars[n] = m
+                       delayretvars = false // found a named result parameter
                } else {
                        // anonymous return values, synthesize names for use in assignment that replaces return
                        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.
                        if !strings.HasPrefix(m.Sym.Name, "~R") {
                                m.Name.SetInlFormal(true)
-                               m.Pos = mpos
+                               m.Pos = t.Pos
                                inlfvars = append(inlfvars, m)
                        }
                }
 
-               ninit.Append(nod(ODCL, m, nil))
                retvars = append(retvars, m)
        }
 
@@ -1095,11 +1137,14 @@ func mkinlcall(n, fn *Node, maxCost int32, inlMap map[*Node]bool) *Node {
                ninit.Append(vas)
        }
 
-       // Zero the return parameters.
-       for _, n := range retvars {
-               ras := nod(OAS, n, nil)
-               ras = typecheck(ras, ctxStmt)
-               ninit.Append(ras)
+       if !delayretvars {
+               // Zero the return parameters.
+               for _, n := range retvars {
+                       ninit.Append(nod(ODCL, n, nil))
+                       ras := nod(OAS, n, nil)
+                       ras = typecheck(ras, ctxStmt)
+                       ninit.Append(ras)
+               }
        }
 
        retlabel := autolabel(".i")
@@ -1130,11 +1175,12 @@ func mkinlcall(n, fn *Node, maxCost int32, inlMap map[*Node]bool) *Node {
        }
 
        subst := inlsubst{
-               retlabel:    retlabel,
-               retvars:     retvars,
-               inlvars:     inlvars,
-               bases:       make(map[*src.PosBase]*src.PosBase),
-               newInlIndex: newIndex,
+               retlabel:     retlabel,
+               retvars:      retvars,
+               delayretvars: delayretvars,
+               inlvars:      inlvars,
+               bases:        make(map[*src.PosBase]*src.PosBase),
+               newInlIndex:  newIndex,
        }
 
        body := subst.list(asNodes(fn.Func.Inl.Body))
@@ -1230,6 +1276,10 @@ type inlsubst struct {
        // Temporary result variables.
        retvars []*Node
 
+       // Whether result variables should be initialized at the
+       // "return" statement.
+       delayretvars bool
+
        inlvars map[*Node]*Node
 
        // 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.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)
                        m.Ninit.Append(as)
                }
index 60e0a9b8b5106b3067bd77047122e897b1e5350f..5c7935aa87674dd1348d23fbdb838d2112daf665 100644 (file)
@@ -75,8 +75,19 @@ func (v *bottomUpVisitor) visit(n *Node) uint32 {
 
        inspectList(n.Nbody, func(n *Node) bool {
                switch n.Op {
-               case OCALLFUNC, OCALLMETH:
-                       fn := asNode(n.Left.Type.Nname())
+               case ONAME:
+                       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 m := v.visit(fn.Name.Defn); m < min {
                                        min = m