diff --git a/src/cmd/compile/internal/gc/inl.go b/src/cmd/compile/internal/gc/inl.go index 137675aa20..098c0c99d5 100644 --- a/src/cmd/compile/internal/gc/inl.go +++ b/src/cmd/compile/internal/gc/inl.go @@ -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) } diff --git a/src/cmd/compile/internal/gc/scc.go b/src/cmd/compile/internal/gc/scc.go index 60e0a9b8b5..5c7935aa87 100644 --- a/src/cmd/compile/internal/gc/scc.go +++ b/src/cmd/compile/internal/gc/scc.go @@ -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