2015-02-13 14:40:36 -05:00
|
|
|
// Copyright 2011 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
//
|
|
|
|
// The inlining facility makes 2 passes: first caninl determines which
|
|
|
|
// functions are suitable for inlining, and for those that are it
|
|
|
|
// saves a copy of the body. Then inlcalls walks each function body to
|
|
|
|
// expand calls to inlinable functions.
|
|
|
|
//
|
|
|
|
// The debug['l'] flag controls the agressiveness. Note that main() swaps level 0 and 1,
|
|
|
|
// making 1 the default and -l disable. -ll and more is useful to flush out bugs.
|
|
|
|
// These additional levels (beyond -l) may be buggy and are not supported.
|
|
|
|
// 0: disabled
|
|
|
|
// 1: 40-nodes leaf functions, oneliners, lazy typechecking (default)
|
|
|
|
// 2: early typechecking of all imported bodies
|
|
|
|
// 3: allow variadic functions
|
|
|
|
// 4: allow non-leaf functions , (breaks runtime.Caller)
|
|
|
|
//
|
|
|
|
// At some point this may get another default and become switch-offable with -N.
|
|
|
|
//
|
|
|
|
// The debug['m'] flag enables diagnostic output. a single -m is useful for verifying
|
|
|
|
// which calls get inlined or not, more is for debugging, and may go away at any point.
|
|
|
|
//
|
|
|
|
// TODO:
|
|
|
|
// - inline functions with ... args
|
|
|
|
// - handle T.meth(f()) with func f() (t T, arg, arg, )
|
|
|
|
|
|
|
|
package gc
|
|
|
|
|
|
|
|
import (
|
|
|
|
"cmd/internal/obj"
|
|
|
|
"fmt"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Used by caninl.
|
|
|
|
|
|
|
|
// Used by inlcalls
|
|
|
|
|
|
|
|
// Used during inlsubst[list]
|
|
|
|
var inlfn *Node // function currently being inlined
|
|
|
|
|
|
|
|
var inlretlabel *Node // target of the goto substituted in place of a return
|
|
|
|
|
|
|
|
var inlretvars *NodeList // temp out variables
|
|
|
|
|
|
|
|
// Get the function's package. For ordinary functions it's on the ->sym, but for imported methods
|
|
|
|
// the ->sym can be re-used in the local package, so peel it off the receiver's type.
|
|
|
|
func fnpkg(fn *Node) *Pkg {
|
|
|
|
if fn.Type.Thistuple != 0 {
|
|
|
|
// method
|
2015-02-23 16:07:24 -05:00
|
|
|
rcvr := getthisx(fn.Type).Type.Type
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-03-01 07:54:01 +00:00
|
|
|
if Isptr[rcvr.Etype] {
|
2015-02-13 14:40:36 -05:00
|
|
|
rcvr = rcvr.Type
|
|
|
|
}
|
2015-02-17 22:13:49 -05:00
|
|
|
if rcvr.Sym == nil {
|
2015-04-17 12:03:22 -04:00
|
|
|
Fatal("receiver with no sym: [%v] %v (%v)", fn.Sym, Nconv(fn, obj.FmtLong), rcvr)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
return rcvr.Sym.Pkg
|
|
|
|
}
|
|
|
|
|
|
|
|
// non-method
|
|
|
|
return fn.Sym.Pkg
|
|
|
|
}
|
|
|
|
|
|
|
|
// Lazy typechecking of imported bodies. For local functions, caninl will set ->typecheck
|
|
|
|
// because they're a copy of an already checked body.
|
|
|
|
func typecheckinl(fn *Node) {
|
2015-02-23 16:07:24 -05:00
|
|
|
lno := int(setlineno(fn))
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
// typecheckinl is only for imported functions;
|
|
|
|
// their bodies may refer to unsafe as long as the package
|
|
|
|
// was marked safe during import (which was checked then).
|
|
|
|
// the ->inl of a local function has been typechecked before caninl copied it.
|
2015-02-23 16:07:24 -05:00
|
|
|
pkg := fnpkg(fn)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
if pkg == localpkg || pkg == nil {
|
|
|
|
return // typecheckinl on local function
|
|
|
|
}
|
|
|
|
|
|
|
|
if Debug['m'] > 2 {
|
2015-04-17 12:03:22 -04:00
|
|
|
fmt.Printf("typecheck import [%v] %v { %v }\n", fn.Sym, Nconv(fn, obj.FmtLong), Hconv(fn.Func.Inl, obj.FmtSharp))
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
save_safemode := safemode
|
2015-02-13 14:40:36 -05:00
|
|
|
safemode = 0
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
savefn := Curfn
|
2015-02-13 14:40:36 -05:00
|
|
|
Curfn = fn
|
2015-03-25 19:33:01 -07:00
|
|
|
typechecklist(fn.Func.Inl, Etop)
|
2015-02-13 14:40:36 -05:00
|
|
|
Curfn = savefn
|
|
|
|
|
|
|
|
safemode = save_safemode
|
|
|
|
|
|
|
|
lineno = int32(lno)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Caninl determines whether fn is inlineable.
|
|
|
|
// If so, caninl saves fn->nbody in fn->inl and substitutes it with a copy.
|
|
|
|
// fn and ->nbody will already have been typechecked.
|
|
|
|
func caninl(fn *Node) {
|
|
|
|
if fn.Op != ODCLFUNC {
|
2015-04-17 12:03:22 -04:00
|
|
|
Fatal("caninl %v", fn)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2015-02-17 22:13:49 -05:00
|
|
|
if fn.Nname == nil {
|
2015-02-13 14:40:36 -05:00
|
|
|
Fatal("caninl no nname %v", Nconv(fn, obj.FmtSign))
|
|
|
|
}
|
|
|
|
|
|
|
|
// If fn has no body (is defined outside of Go), cannot inline it.
|
|
|
|
if fn.Nbody == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if fn.Typecheck == 0 {
|
2015-04-17 12:03:22 -04:00
|
|
|
Fatal("caninl on non-typechecked function %v", fn)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// can't handle ... args yet
|
|
|
|
if Debug['l'] < 3 {
|
2015-02-23 16:07:24 -05:00
|
|
|
for t := fn.Type.Type.Down.Down.Type; t != nil; t = t.Down {
|
2015-03-09 16:24:07 +11:00
|
|
|
if t.Isddd {
|
2015-02-13 14:40:36 -05:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-09 10:08:29 +03:00
|
|
|
// Runtime package must not be race instrumented.
|
|
|
|
// Racewalk skips runtime package. However, some runtime code can be
|
|
|
|
// inlined into other packages and instrumented there. To avoid this,
|
|
|
|
// we disable inlining of runtime functions in race mode.
|
|
|
|
// The example that we observed is inlining of LockOSThread,
|
|
|
|
// which lead to false race reports on m contents.
|
|
|
|
if flag_race != 0 && myimportpath == "runtime" {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2015-02-24 12:19:01 -05:00
|
|
|
const maxBudget = 80
|
|
|
|
budget := maxBudget // allowed hairyness
|
|
|
|
if ishairylist(fn.Nbody, &budget) || budget < 0 {
|
2015-02-13 14:40:36 -05:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
savefn := Curfn
|
2015-02-13 14:40:36 -05:00
|
|
|
Curfn = fn
|
|
|
|
|
2015-03-25 19:33:01 -07:00
|
|
|
fn.Nname.Func.Inl = fn.Nbody
|
|
|
|
fn.Nbody = inlcopylist(fn.Nname.Func.Inl)
|
|
|
|
fn.Nname.Func.Inldcl = inlcopylist(fn.Nname.Defn.Func.Dcl)
|
|
|
|
fn.Nname.Func.InlCost = int32(maxBudget - budget)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
// 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
|
|
|
|
fn.Type.Nname = fn.Nname
|
|
|
|
|
|
|
|
if Debug['m'] > 1 {
|
2015-03-25 19:33:01 -07:00
|
|
|
fmt.Printf("%v: can inline %v as: %v { %v }\n", fn.Line(), Nconv(fn.Nname, obj.FmtSharp), Tconv(fn.Type, obj.FmtSharp), Hconv(fn.Nname.Func.Inl, obj.FmtSharp))
|
2015-02-13 14:40:36 -05:00
|
|
|
} else if Debug['m'] != 0 {
|
2015-04-17 12:03:22 -04:00
|
|
|
fmt.Printf("%v: can inline %v\n", fn.Line(), fn.Nname)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
Curfn = savefn
|
|
|
|
}
|
|
|
|
|
|
|
|
// Look for anything we want to punt on.
|
2015-02-17 22:13:49 -05:00
|
|
|
func ishairylist(ll *NodeList, budget *int) bool {
|
2015-02-13 14:40:36 -05:00
|
|
|
for ; ll != nil; ll = ll.Next {
|
2015-02-17 22:13:49 -05:00
|
|
|
if ishairy(ll.N, budget) {
|
|
|
|
return true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
}
|
2015-02-17 22:13:49 -05:00
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
func ishairy(n *Node, budget *int) bool {
|
|
|
|
if n == nil {
|
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
switch n.Op {
|
2015-02-24 12:19:01 -05:00
|
|
|
// Call is okay if inlinable and we have the budget for the body.
|
|
|
|
case OCALLFUNC:
|
2015-03-25 19:33:01 -07:00
|
|
|
if n.Left.Func != nil && n.Left.Func.Inl != nil {
|
|
|
|
*budget -= int(n.Left.Func.InlCost)
|
2015-02-24 12:19:01 -05:00
|
|
|
break
|
|
|
|
}
|
|
|
|
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
|
2015-03-25 19:33:01 -07:00
|
|
|
if n.Left.Sym.Def != nil && n.Left.Sym.Def.Func.Inl != nil {
|
|
|
|
*budget -= int(n.Left.Sym.Def.Func.InlCost)
|
2015-02-24 12:19:01 -05:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if Debug['l'] < 4 {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// Call is okay if inlinable and we have the budget for the body.
|
|
|
|
case OCALLMETH:
|
|
|
|
if n.Left.Type == nil {
|
|
|
|
Fatal("no function type for [%p] %v\n", n.Left, Nconv(n.Left, obj.FmtSign))
|
|
|
|
}
|
|
|
|
if n.Left.Type.Nname == nil {
|
|
|
|
Fatal("no function definition for [%p] %v\n", n.Left.Type, Tconv(n.Left.Type, obj.FmtSign))
|
|
|
|
}
|
2015-03-25 19:33:01 -07:00
|
|
|
if n.Left.Type.Nname.Func.Inl != nil {
|
|
|
|
*budget -= int(n.Left.Type.Nname.Func.InlCost)
|
2015-02-24 12:19:01 -05:00
|
|
|
break
|
|
|
|
}
|
|
|
|
if Debug['l'] < 4 {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// Things that are too hairy, irrespective of the budget
|
2015-04-01 09:38:44 -07:00
|
|
|
case OCALL, OCALLINTER, OPANIC, ORECOVER:
|
2015-02-13 14:40:36 -05:00
|
|
|
if Debug['l'] < 4 {
|
2015-02-17 22:13:49 -05:00
|
|
|
return true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
case OCLOSURE,
|
|
|
|
OCALLPART,
|
|
|
|
ORANGE,
|
|
|
|
OFOR,
|
|
|
|
OSELECT,
|
|
|
|
OSWITCH,
|
|
|
|
OPROC,
|
|
|
|
ODEFER,
|
|
|
|
ODCLTYPE, // can't print yet
|
|
|
|
ODCLCONST, // can't print yet
|
|
|
|
ORETJMP:
|
2015-02-17 22:13:49 -05:00
|
|
|
return true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
(*budget)--
|
|
|
|
|
2015-05-22 01:16:52 -04:00
|
|
|
return *budget < 0 || ishairy(n.Left, budget) || ishairy(n.Right, budget) || ishairylist(n.List, budget) || ishairylist(n.Rlist, budget) || ishairylist(n.Ninit, budget) || ishairy(n.Ntest, budget) || ishairylist(n.Nbody, budget)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// Inlcopy and inlcopylist recursively copy the body of a function.
|
|
|
|
// Any name-like node of non-local class is marked for re-export by adding it to
|
|
|
|
// the exportlist.
|
|
|
|
func inlcopylist(ll *NodeList) *NodeList {
|
2015-03-02 14:22:05 -05:00
|
|
|
var l *NodeList
|
2015-02-13 14:40:36 -05:00
|
|
|
for ; ll != nil; ll = ll.Next {
|
|
|
|
l = list(l, inlcopy(ll.N))
|
|
|
|
}
|
|
|
|
return l
|
|
|
|
}
|
|
|
|
|
|
|
|
func inlcopy(n *Node) *Node {
|
|
|
|
if n == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
switch n.Op {
|
2015-04-01 09:38:44 -07:00
|
|
|
case ONAME, OTYPE, OLITERAL:
|
2015-02-13 14:40:36 -05:00
|
|
|
return n
|
|
|
|
}
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
m := Nod(OXXX, nil, nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
*m = *n
|
2015-03-10 21:37:13 -07:00
|
|
|
if m.Func != nil {
|
2015-03-25 19:33:01 -07:00
|
|
|
m.Func.Inl = nil
|
2015-03-10 21:37:13 -07:00
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
m.Left = inlcopy(n.Left)
|
|
|
|
m.Right = inlcopy(n.Right)
|
|
|
|
m.List = inlcopylist(n.List)
|
|
|
|
m.Rlist = inlcopylist(n.Rlist)
|
|
|
|
m.Ninit = inlcopylist(n.Ninit)
|
|
|
|
m.Ntest = inlcopy(n.Ntest)
|
|
|
|
m.Nbody = inlcopylist(n.Nbody)
|
|
|
|
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
|
|
|
// Inlcalls/nodelist/node walks fn's statements and expressions and substitutes any
|
|
|
|
// calls made to inlineable functions. This is the external entry point.
|
|
|
|
func inlcalls(fn *Node) {
|
2015-02-23 16:07:24 -05:00
|
|
|
savefn := Curfn
|
2015-02-13 14:40:36 -05:00
|
|
|
Curfn = fn
|
|
|
|
inlnode(&fn)
|
|
|
|
if fn != Curfn {
|
|
|
|
Fatal("inlnode replaced curfn")
|
|
|
|
}
|
|
|
|
Curfn = savefn
|
|
|
|
}
|
|
|
|
|
|
|
|
// Turn an OINLCALL into a statement.
|
|
|
|
func inlconv2stmt(n *Node) {
|
|
|
|
n.Op = OBLOCK
|
|
|
|
|
|
|
|
// n->ninit stays
|
|
|
|
n.List = n.Nbody
|
|
|
|
|
|
|
|
n.Nbody = nil
|
|
|
|
n.Rlist = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Turn an OINLCALL into a single valued expression.
|
|
|
|
func inlconv2expr(np **Node) {
|
2015-02-23 16:07:24 -05:00
|
|
|
n := *np
|
|
|
|
r := n.Rlist.N
|
2015-02-13 14:40:36 -05:00
|
|
|
addinit(&r, concat(n.Ninit, n.Nbody))
|
|
|
|
*np = r
|
|
|
|
}
|
|
|
|
|
|
|
|
// Turn the rlist (with the return values) of the OINLCALL in
|
|
|
|
// n into an expression list lumping the ninit and body
|
|
|
|
// containing the inlined statements on the first list element so
|
|
|
|
// order will be preserved Used in return, oas2func and call
|
|
|
|
// statements.
|
|
|
|
func inlconv2list(n *Node) *NodeList {
|
|
|
|
if n.Op != OINLCALL || n.Rlist == nil {
|
|
|
|
Fatal("inlconv2list %v\n", Nconv(n, obj.FmtSign))
|
|
|
|
}
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
l := n.Rlist
|
2015-02-13 14:40:36 -05:00
|
|
|
addinit(&l.N, concat(n.Ninit, n.Nbody))
|
|
|
|
return l
|
|
|
|
}
|
|
|
|
|
|
|
|
func inlnodelist(l *NodeList) {
|
|
|
|
for ; l != nil; l = l.Next {
|
|
|
|
inlnode(&l.N)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// inlnode recurses over the tree to find inlineable calls, which will
|
|
|
|
// be turned into OINLCALLs by mkinlcall. When the recursion comes
|
|
|
|
// back up will examine left, right, list, rlist, ninit, ntest, nincr,
|
|
|
|
// nbody and nelse and use one of the 4 inlconv/glue functions above
|
|
|
|
// to turn the OINLCALL into an expression, a statement, or patch it
|
|
|
|
// in to this nodes list or rlist as appropriate.
|
|
|
|
// NOTE it makes no sense to pass the glue functions down the
|
|
|
|
// recursion to the level where the OINLCALL gets created because they
|
|
|
|
// have to edit /this/ n, so you'd have to push that one down as well,
|
|
|
|
// but then you may as well do it here. so this is cleaner and
|
|
|
|
// shorter and less complicated.
|
|
|
|
func inlnode(np **Node) {
|
|
|
|
if *np == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
n := *np
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
switch n.Op {
|
|
|
|
// inhibit inlining of their argument
|
2015-04-01 09:38:44 -07:00
|
|
|
case ODEFER, OPROC:
|
2015-02-13 14:40:36 -05:00
|
|
|
switch n.Left.Op {
|
2015-04-01 09:38:44 -07:00
|
|
|
case OCALLFUNC, OCALLMETH:
|
2015-02-13 14:40:36 -05:00
|
|
|
n.Left.Etype = n.Op
|
|
|
|
}
|
|
|
|
fallthrough
|
|
|
|
|
|
|
|
// TODO do them here (or earlier),
|
|
|
|
// so escape analysis can avoid more heapmoves.
|
|
|
|
case OCLOSURE:
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
lno := int(setlineno(n))
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
inlnodelist(n.Ninit)
|
2015-02-23 16:07:24 -05:00
|
|
|
for l := n.Ninit; l != nil; l = l.Next {
|
2015-02-13 14:40:36 -05:00
|
|
|
if l.N.Op == OINLCALL {
|
|
|
|
inlconv2stmt(l.N)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inlnode(&n.Left)
|
|
|
|
if n.Left != nil && n.Left.Op == OINLCALL {
|
|
|
|
inlconv2expr(&n.Left)
|
|
|
|
}
|
|
|
|
|
|
|
|
inlnode(&n.Right)
|
|
|
|
if n.Right != nil && n.Right.Op == OINLCALL {
|
2015-05-22 01:16:52 -04:00
|
|
|
if n.Op == OFOR {
|
|
|
|
inlconv2stmt(n.Right)
|
|
|
|
} else {
|
|
|
|
inlconv2expr(&n.Right)
|
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
inlnodelist(n.List)
|
|
|
|
switch n.Op {
|
|
|
|
case OBLOCK:
|
2015-02-23 16:07:24 -05:00
|
|
|
for l := n.List; l != nil; l = l.Next {
|
2015-02-13 14:40:36 -05:00
|
|
|
if l.N.Op == OINLCALL {
|
|
|
|
inlconv2stmt(l.N)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// if we just replaced arg in f(arg()) or return arg with an inlined call
|
|
|
|
// and arg returns multiple values, glue as list
|
|
|
|
case ORETURN,
|
|
|
|
OCALLFUNC,
|
|
|
|
OCALLMETH,
|
|
|
|
OCALLINTER,
|
|
|
|
OAPPEND,
|
|
|
|
OCOMPLEX:
|
|
|
|
if count(n.List) == 1 && n.List.N.Op == OINLCALL && count(n.List.N.Rlist) > 1 {
|
|
|
|
n.List = inlconv2list(n.List.N)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
fallthrough
|
|
|
|
|
|
|
|
default:
|
2015-02-23 16:07:24 -05:00
|
|
|
for l := n.List; l != nil; l = l.Next {
|
2015-02-13 14:40:36 -05:00
|
|
|
if l.N.Op == OINLCALL {
|
|
|
|
inlconv2expr(&l.N)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inlnodelist(n.Rlist)
|
|
|
|
switch n.Op {
|
|
|
|
case OAS2FUNC:
|
|
|
|
if n.Rlist.N.Op == OINLCALL {
|
|
|
|
n.Rlist = inlconv2list(n.Rlist.N)
|
|
|
|
n.Op = OAS2
|
|
|
|
n.Typecheck = 0
|
|
|
|
typecheck(np, Etop)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
fallthrough
|
|
|
|
|
|
|
|
default:
|
2015-02-23 16:07:24 -05:00
|
|
|
for l := n.Rlist; l != nil; l = l.Next {
|
2015-02-13 14:40:36 -05:00
|
|
|
if l.N.Op == OINLCALL {
|
2015-05-22 01:16:52 -04:00
|
|
|
if n.Op == OIF {
|
|
|
|
inlconv2stmt(l.N)
|
|
|
|
} else {
|
|
|
|
inlconv2expr(&l.N)
|
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inlnode(&n.Ntest)
|
|
|
|
if n.Ntest != nil && n.Ntest.Op == OINLCALL {
|
|
|
|
inlconv2expr(&n.Ntest)
|
|
|
|
}
|
|
|
|
|
|
|
|
inlnodelist(n.Nbody)
|
2015-02-23 16:07:24 -05:00
|
|
|
for l := n.Nbody; l != nil; l = l.Next {
|
2015-02-13 14:40:36 -05:00
|
|
|
if l.N.Op == OINLCALL {
|
|
|
|
inlconv2stmt(l.N)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// with all the branches out of the way, it is now time to
|
|
|
|
// transmogrify this node itself unless inhibited by the
|
|
|
|
// switch at the top of this function.
|
|
|
|
switch n.Op {
|
2015-04-01 09:38:44 -07:00
|
|
|
case OCALLFUNC, OCALLMETH:
|
2015-02-13 14:40:36 -05:00
|
|
|
if n.Etype == OPROC || n.Etype == ODEFER {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch n.Op {
|
|
|
|
case OCALLFUNC:
|
|
|
|
if Debug['m'] > 3 {
|
|
|
|
fmt.Printf("%v:call to func %v\n", n.Line(), Nconv(n.Left, obj.FmtSign))
|
|
|
|
}
|
2015-03-25 19:33:01 -07:00
|
|
|
if n.Left.Func != nil && n.Left.Func.Inl != nil { // normal case
|
2015-03-09 16:24:07 +11:00
|
|
|
mkinlcall(np, n.Left, n.Isddd)
|
2015-02-13 14:40:36 -05:00
|
|
|
} 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 {
|
2015-03-09 16:24:07 +11:00
|
|
|
mkinlcall(np, n.Left.Sym.Def, n.Isddd)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
case OCALLMETH:
|
|
|
|
if Debug['m'] > 3 {
|
|
|
|
fmt.Printf("%v:call to meth %v\n", n.Line(), Nconv(n.Left.Right, obj.FmtLong))
|
|
|
|
}
|
|
|
|
|
|
|
|
// typecheck should have resolved ODOTMETH->type, whose nname points to the actual function.
|
|
|
|
if n.Left.Type == nil {
|
|
|
|
Fatal("no function type for [%p] %v\n", n.Left, Nconv(n.Left, obj.FmtSign))
|
|
|
|
}
|
|
|
|
|
|
|
|
if n.Left.Type.Nname == nil {
|
|
|
|
Fatal("no function definition for [%p] %v\n", n.Left.Type, Tconv(n.Left.Type, obj.FmtSign))
|
|
|
|
}
|
|
|
|
|
2015-03-09 16:24:07 +11:00
|
|
|
mkinlcall(np, n.Left.Type.Nname, n.Isddd)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
lineno = int32(lno)
|
|
|
|
}
|
|
|
|
|
2015-03-09 16:24:07 +11:00
|
|
|
func mkinlcall(np **Node, fn *Node, isddd bool) {
|
2015-02-23 16:07:24 -05:00
|
|
|
save_safemode := safemode
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
// imported functions may refer to unsafe as long as the
|
|
|
|
// package was marked safe during import (already checked).
|
2015-02-23 16:07:24 -05:00
|
|
|
pkg := fnpkg(fn)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
if pkg != localpkg && pkg != nil {
|
|
|
|
safemode = 0
|
|
|
|
}
|
|
|
|
mkinlcall1(np, fn, isddd)
|
|
|
|
safemode = save_safemode
|
|
|
|
}
|
|
|
|
|
|
|
|
func tinlvar(t *Type) *Node {
|
|
|
|
if t.Nname != nil && !isblank(t.Nname) {
|
2015-05-15 10:02:19 -07:00
|
|
|
if t.Nname.Name.Inlvar == nil {
|
2015-04-17 12:03:22 -04:00
|
|
|
Fatal("missing inlvar for %v\n", t.Nname)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2015-05-15 10:02:19 -07:00
|
|
|
return t.Nname.Name.Inlvar
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
typecheck(&nblank, Erv|Easgn)
|
|
|
|
return nblank
|
|
|
|
}
|
|
|
|
|
|
|
|
var inlgen int
|
|
|
|
|
|
|
|
// if *np is a call, and fn is a function with an inlinable body, substitute *np with an OINLCALL.
|
|
|
|
// On return ninit has the parameter assignments, the nbody is the
|
|
|
|
// inlined function body and list, rlist contain the input, output
|
|
|
|
// parameters.
|
2015-03-09 16:24:07 +11:00
|
|
|
func mkinlcall1(np **Node, fn *Node, isddd bool) {
|
2015-02-13 14:40:36 -05:00
|
|
|
// For variadic fn.
|
2015-03-25 19:33:01 -07:00
|
|
|
if fn.Func.Inl == nil {
|
2015-02-13 14:40:36 -05:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if fn == Curfn || fn.Defn == Curfn {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if Debug['l'] < 2 {
|
|
|
|
typecheckinl(fn)
|
|
|
|
}
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
n := *np
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
// Bingo, we have a function node, and it has an inlineable body
|
|
|
|
if Debug['m'] > 1 {
|
2015-04-17 12:03:22 -04:00
|
|
|
fmt.Printf("%v: inlining call to %v %v { %v }\n", n.Line(), fn.Sym, Tconv(fn.Type, obj.FmtSharp), Hconv(fn.Func.Inl, obj.FmtSharp))
|
2015-02-13 14:40:36 -05:00
|
|
|
} else if Debug['m'] != 0 {
|
2015-04-17 12:03:22 -04:00
|
|
|
fmt.Printf("%v: inlining call to %v\n", n.Line(), fn)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if Debug['m'] > 2 {
|
|
|
|
fmt.Printf("%v: Before inlining: %v\n", n.Line(), Nconv(n, obj.FmtSign))
|
|
|
|
}
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
saveinlfn := inlfn
|
2015-02-13 14:40:36 -05:00
|
|
|
inlfn = fn
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
ninit := n.Ninit
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
//dumplist("ninit pre", ninit);
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
var dcl *NodeList
|
2015-02-13 14:40:36 -05:00
|
|
|
if fn.Defn != nil { // local function
|
2015-03-25 19:33:01 -07:00
|
|
|
dcl = fn.Func.Inldcl // imported function
|
2015-02-13 14:40:36 -05:00
|
|
|
} else {
|
2015-03-25 19:33:01 -07:00
|
|
|
dcl = fn.Func.Dcl
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
inlretvars = nil
|
2015-02-23 16:07:24 -05:00
|
|
|
i := 0
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
// Make temp names to use instead of the originals
|
2015-02-23 16:07:24 -05:00
|
|
|
for ll := dcl; ll != nil; ll = ll.Next {
|
2015-02-13 14:40:36 -05:00
|
|
|
if ll.N.Class == PPARAMOUT { // return values handled below.
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if ll.N.Op == ONAME {
|
2015-05-15 10:02:19 -07:00
|
|
|
ll.N.Name.Inlvar = inlvar(ll.N)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
// Typecheck because inlvar is not necessarily a function parameter.
|
2015-05-15 10:02:19 -07:00
|
|
|
typecheck(&ll.N.Name.Inlvar, Erv)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
if ll.N.Class&^PHEAP != PAUTO {
|
2015-05-15 10:02:19 -07:00
|
|
|
ninit = list(ninit, Nod(ODCL, ll.N.Name.Inlvar, nil)) // otherwise gen won't emit the allocations for heapallocs
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// temporaries for return values.
|
2015-02-23 16:07:24 -05:00
|
|
|
var m *Node
|
|
|
|
for t := getoutargx(fn.Type).Type; t != nil; t = t.Down {
|
2015-02-13 14:40:36 -05:00
|
|
|
if t != nil && t.Nname != nil && !isblank(t.Nname) {
|
|
|
|
m = inlvar(t.Nname)
|
|
|
|
typecheck(&m, Erv)
|
2015-05-15 10:02:19 -07:00
|
|
|
t.Nname.Name.Inlvar = m
|
2015-02-13 14:40:36 -05:00
|
|
|
} else {
|
|
|
|
// anonymous return values, synthesize names for use in assignment that replaces return
|
|
|
|
m = retvar(t, i)
|
|
|
|
i++
|
|
|
|
}
|
|
|
|
|
|
|
|
ninit = list(ninit, Nod(ODCL, m, nil))
|
|
|
|
inlretvars = list(inlretvars, m)
|
|
|
|
}
|
|
|
|
|
|
|
|
// assign receiver.
|
2015-02-23 16:07:24 -05:00
|
|
|
var as *Node
|
2015-02-13 14:40:36 -05:00
|
|
|
if fn.Type.Thistuple != 0 && n.Left.Op == ODOTMETH {
|
|
|
|
// method call with a receiver.
|
2015-02-23 16:07:24 -05:00
|
|
|
t := getthisx(fn.Type).Type
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-05-15 10:02:19 -07:00
|
|
|
if t != nil && t.Nname != nil && !isblank(t.Nname) && t.Nname.Name.Inlvar == nil {
|
2015-04-17 12:03:22 -04:00
|
|
|
Fatal("missing inlvar for %v\n", t.Nname)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2015-02-17 22:13:49 -05:00
|
|
|
if n.Left.Left == nil {
|
2015-02-13 14:40:36 -05:00
|
|
|
Fatal("method call without receiver: %v", Nconv(n, obj.FmtSign))
|
|
|
|
}
|
|
|
|
if t == nil {
|
|
|
|
Fatal("method call unknown receiver type: %v", Nconv(n, obj.FmtSign))
|
|
|
|
}
|
|
|
|
as = Nod(OAS, tinlvar(t), n.Left.Left)
|
|
|
|
if as != nil {
|
|
|
|
typecheck(&as, Etop)
|
|
|
|
ninit = list(ninit, as)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if inlined function is variadic.
|
2015-02-23 16:07:24 -05:00
|
|
|
variadic := false
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-03-02 14:22:05 -05:00
|
|
|
var varargtype *Type
|
2015-02-23 16:07:24 -05:00
|
|
|
varargcount := 0
|
|
|
|
for t := fn.Type.Type.Down.Down.Type; t != nil; t = t.Down {
|
2015-03-09 16:24:07 +11:00
|
|
|
if t.Isddd {
|
2015-02-17 22:13:49 -05:00
|
|
|
variadic = true
|
2015-02-13 14:40:36 -05:00
|
|
|
varargtype = t.Type
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// but if argument is dotted too forget about variadicity.
|
2015-03-09 16:24:07 +11:00
|
|
|
if variadic && isddd {
|
2015-02-17 22:13:49 -05:00
|
|
|
variadic = false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// check if argument is actually a returned tuple from call.
|
2015-02-23 16:07:24 -05:00
|
|
|
multiret := 0
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
if n.List != nil && n.List.Next == nil {
|
2015-02-13 14:40:36 -05:00
|
|
|
switch n.List.N.Op {
|
2015-04-01 09:38:44 -07:00
|
|
|
case OCALL, OCALLFUNC, OCALLINTER, OCALLMETH:
|
2015-02-13 14:40:36 -05:00
|
|
|
if n.List.N.Left.Type.Outtuple > 1 {
|
|
|
|
multiret = n.List.N.Left.Type.Outtuple - 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
if variadic {
|
2015-02-13 14:40:36 -05:00
|
|
|
varargcount = count(n.List) + multiret
|
|
|
|
if n.Left.Op != ODOTMETH {
|
|
|
|
varargcount -= fn.Type.Thistuple
|
|
|
|
}
|
|
|
|
varargcount -= fn.Type.Intuple - 1
|
|
|
|
}
|
|
|
|
|
|
|
|
// assign arguments to the parameters' temp names
|
|
|
|
as = Nod(OAS2, nil, nil)
|
|
|
|
|
|
|
|
as.Rlist = n.List
|
2015-02-23 16:07:24 -05:00
|
|
|
ll := n.List
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
// TODO: if len(nlist) == 1 but multiple args, check that n->list->n is a call?
|
|
|
|
if fn.Type.Thistuple != 0 && n.Left.Op != ODOTMETH {
|
|
|
|
// non-method call to method
|
2015-02-17 22:13:49 -05:00
|
|
|
if n.List == nil {
|
2015-02-13 14:40:36 -05:00
|
|
|
Fatal("non-method call to method without first arg: %v", Nconv(n, obj.FmtSign))
|
|
|
|
}
|
|
|
|
|
|
|
|
// append receiver inlvar to LHS.
|
2015-02-23 16:07:24 -05:00
|
|
|
t := getthisx(fn.Type).Type
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-05-15 10:02:19 -07:00
|
|
|
if t != nil && t.Nname != nil && !isblank(t.Nname) && t.Nname.Name.Inlvar == nil {
|
2015-04-17 12:03:22 -04:00
|
|
|
Fatal("missing inlvar for %v\n", t.Nname)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
if t == nil {
|
|
|
|
Fatal("method call unknown receiver type: %v", Nconv(n, obj.FmtSign))
|
|
|
|
}
|
|
|
|
as.List = list(as.List, tinlvar(t))
|
|
|
|
ll = ll.Next // track argument count.
|
|
|
|
}
|
|
|
|
|
|
|
|
// append ordinary arguments to LHS.
|
2015-02-23 16:07:24 -05:00
|
|
|
chkargcount := n.List != nil && n.List.Next != nil
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-03-02 14:22:05 -05:00
|
|
|
var vararg *Node // the slice argument to a variadic call
|
|
|
|
var varargs *NodeList // the list of LHS names to put in vararg.
|
2015-02-17 22:13:49 -05:00
|
|
|
if !chkargcount {
|
2015-02-13 14:40:36 -05:00
|
|
|
// 0 or 1 expression on RHS.
|
2015-02-23 16:07:24 -05:00
|
|
|
var i int
|
|
|
|
for t := getinargx(fn.Type).Type; t != nil; t = t.Down {
|
2015-03-09 16:24:07 +11:00
|
|
|
if variadic && t.Isddd {
|
2015-02-13 14:40:36 -05:00
|
|
|
vararg = tinlvar(t)
|
|
|
|
for i = 0; i < varargcount && ll != nil; i++ {
|
|
|
|
m = argvar(varargtype, i)
|
|
|
|
varargs = list(varargs, m)
|
|
|
|
as.List = list(as.List, m)
|
|
|
|
}
|
|
|
|
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
as.List = list(as.List, tinlvar(t))
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// match arguments except final variadic (unless the call is dotted itself)
|
2015-02-23 16:07:24 -05:00
|
|
|
var t *Type
|
2015-02-13 14:40:36 -05:00
|
|
|
for t = getinargx(fn.Type).Type; t != nil; {
|
2015-02-17 22:13:49 -05:00
|
|
|
if ll == nil {
|
2015-02-13 14:40:36 -05:00
|
|
|
break
|
|
|
|
}
|
2015-03-09 16:24:07 +11:00
|
|
|
if variadic && t.Isddd {
|
2015-02-13 14:40:36 -05:00
|
|
|
break
|
|
|
|
}
|
|
|
|
as.List = list(as.List, tinlvar(t))
|
|
|
|
t = t.Down
|
|
|
|
ll = ll.Next
|
|
|
|
}
|
|
|
|
|
|
|
|
// match varargcount arguments with variadic parameters.
|
2015-03-09 16:24:07 +11:00
|
|
|
if variadic && t != nil && t.Isddd {
|
2015-02-13 14:40:36 -05:00
|
|
|
vararg = tinlvar(t)
|
2015-02-23 16:07:24 -05:00
|
|
|
var i int
|
2015-02-13 14:40:36 -05:00
|
|
|
for i = 0; i < varargcount && ll != nil; i++ {
|
|
|
|
m = argvar(varargtype, i)
|
|
|
|
varargs = list(varargs, m)
|
|
|
|
as.List = list(as.List, m)
|
|
|
|
ll = ll.Next
|
|
|
|
}
|
|
|
|
|
|
|
|
if i == varargcount {
|
|
|
|
t = t.Down
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ll != nil || t != nil {
|
|
|
|
Fatal("arg count mismatch: %v vs %v\n", Tconv(getinargx(fn.Type), obj.FmtSharp), Hconv(n.List, obj.FmtComma))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if as.Rlist != nil {
|
|
|
|
typecheck(&as, Etop)
|
|
|
|
ninit = list(ninit, as)
|
|
|
|
}
|
|
|
|
|
|
|
|
// turn the variadic args into a slice.
|
2015-02-17 22:13:49 -05:00
|
|
|
if variadic {
|
2015-02-13 14:40:36 -05:00
|
|
|
as = Nod(OAS, vararg, nil)
|
2015-02-17 22:13:49 -05:00
|
|
|
if varargcount == 0 {
|
2015-02-13 14:40:36 -05:00
|
|
|
as.Right = nodnil()
|
|
|
|
as.Right.Type = varargtype
|
|
|
|
} else {
|
2015-02-23 16:07:24 -05:00
|
|
|
vararrtype := typ(TARRAY)
|
2015-02-13 14:40:36 -05:00
|
|
|
vararrtype.Type = varargtype.Type
|
|
|
|
vararrtype.Bound = int64(varargcount)
|
|
|
|
|
|
|
|
as.Right = Nod(OCOMPLIT, nil, typenod(varargtype))
|
|
|
|
as.Right.List = varargs
|
|
|
|
as.Right = Nod(OSLICE, as.Right, Nod(OKEY, nil, nil))
|
|
|
|
}
|
|
|
|
|
|
|
|
typecheck(&as, Etop)
|
|
|
|
ninit = list(ninit, as)
|
|
|
|
}
|
|
|
|
|
|
|
|
// zero the outparams
|
2015-02-23 16:07:24 -05:00
|
|
|
for ll := inlretvars; ll != nil; ll = ll.Next {
|
2015-02-13 14:40:36 -05:00
|
|
|
as = Nod(OAS, ll.N, nil)
|
|
|
|
typecheck(&as, Etop)
|
|
|
|
ninit = list(ninit, as)
|
|
|
|
}
|
|
|
|
|
|
|
|
inlretlabel = newlabel_inl()
|
|
|
|
inlgen++
|
2015-03-25 19:33:01 -07:00
|
|
|
body := inlsubstlist(fn.Func.Inl)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
body = list(body, Nod(OGOTO, inlretlabel, nil)) // avoid 'not used' when function doesnt have return
|
|
|
|
body = list(body, Nod(OLABEL, inlretlabel, nil))
|
|
|
|
|
|
|
|
typechecklist(body, Etop)
|
|
|
|
|
|
|
|
//dumplist("ninit post", ninit);
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
call := Nod(OINLCALL, nil, nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
call.Ninit = ninit
|
|
|
|
call.Nbody = body
|
|
|
|
call.Rlist = inlretvars
|
|
|
|
call.Type = n.Type
|
|
|
|
call.Typecheck = 1
|
|
|
|
|
|
|
|
setlno(call, int(n.Lineno))
|
|
|
|
|
|
|
|
//dumplist("call body", body);
|
|
|
|
|
|
|
|
*np = call
|
|
|
|
|
|
|
|
inlfn = saveinlfn
|
|
|
|
|
|
|
|
// transitive inlining
|
2015-02-24 12:19:01 -05:00
|
|
|
// might be nice to do this before exporting the body,
|
|
|
|
// but can't emit the body with inlining expanded.
|
|
|
|
// instead we emit the things that the body needs
|
|
|
|
// and each use must redo the inlining.
|
|
|
|
// luckily these are small.
|
2015-03-25 19:33:01 -07:00
|
|
|
body = fn.Func.Inl
|
|
|
|
fn.Func.Inl = nil // prevent infinite recursion (shouldn't happen anyway)
|
2015-02-24 12:19:01 -05:00
|
|
|
inlnodelist(call.Nbody)
|
|
|
|
for ll := call.Nbody; ll != nil; ll = ll.Next {
|
|
|
|
if ll.N.Op == OINLCALL {
|
|
|
|
inlconv2stmt(ll.N)
|
|
|
|
}
|
|
|
|
}
|
2015-03-25 19:33:01 -07:00
|
|
|
fn.Func.Inl = body
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
if Debug['m'] > 2 {
|
|
|
|
fmt.Printf("%v: After inlining %v\n\n", n.Line(), Nconv(*np, obj.FmtSign))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Every time we expand a function we generate a new set of tmpnames,
|
|
|
|
// PAUTO's in the calling functions, and link them off of the
|
|
|
|
// PPARAM's, PAUTOS and PPARAMOUTs of the called function.
|
|
|
|
func inlvar(var_ *Node) *Node {
|
|
|
|
if Debug['m'] > 3 {
|
|
|
|
fmt.Printf("inlvar %v\n", Nconv(var_, obj.FmtSign))
|
|
|
|
}
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
n := newname(var_.Sym)
|
2015-02-13 14:40:36 -05:00
|
|
|
n.Type = var_.Type
|
|
|
|
n.Class = PAUTO
|
2015-03-06 21:18:41 +11:00
|
|
|
n.Used = true
|
2015-02-13 14:40:36 -05:00
|
|
|
n.Curfn = Curfn // the calling function, not the called one
|
|
|
|
n.Addrtaken = var_.Addrtaken
|
|
|
|
|
|
|
|
// Esc pass wont run if we're inlining into a iface wrapper.
|
|
|
|
// Luckily, we can steal the results from the target func.
|
|
|
|
// If inlining a function defined in another package after
|
|
|
|
// escape analysis is done, treat all local vars as escaping.
|
|
|
|
// See issue 9537.
|
|
|
|
if var_.Esc == EscHeap || (inl_nonlocal != 0 && var_.Op == ONAME) {
|
|
|
|
addrescapes(n)
|
|
|
|
}
|
|
|
|
|
2015-03-25 19:33:01 -07:00
|
|
|
Curfn.Func.Dcl = list(Curfn.Func.Dcl, n)
|
2015-02-13 14:40:36 -05:00
|
|
|
return n
|
|
|
|
}
|
|
|
|
|
|
|
|
// Synthesize a variable to store the inlined function's results in.
|
|
|
|
func retvar(t *Type, i int) *Node {
|
2015-03-06 12:02:24 -08:00
|
|
|
n := newname(Lookupf("~r%d", i))
|
2015-02-13 14:40:36 -05:00
|
|
|
n.Type = t.Type
|
|
|
|
n.Class = PAUTO
|
2015-03-06 21:18:41 +11:00
|
|
|
n.Used = true
|
2015-02-13 14:40:36 -05:00
|
|
|
n.Curfn = Curfn // the calling function, not the called one
|
2015-03-25 19:33:01 -07:00
|
|
|
Curfn.Func.Dcl = list(Curfn.Func.Dcl, n)
|
2015-02-13 14:40:36 -05:00
|
|
|
return n
|
|
|
|
}
|
|
|
|
|
|
|
|
// Synthesize a variable to store the inlined function's arguments
|
|
|
|
// when they come from a multiple return call.
|
|
|
|
func argvar(t *Type, i int) *Node {
|
2015-03-06 12:02:24 -08:00
|
|
|
n := newname(Lookupf("~arg%d", i))
|
2015-02-13 14:40:36 -05:00
|
|
|
n.Type = t.Type
|
|
|
|
n.Class = PAUTO
|
2015-03-06 21:18:41 +11:00
|
|
|
n.Used = true
|
2015-02-13 14:40:36 -05:00
|
|
|
n.Curfn = Curfn // the calling function, not the called one
|
2015-03-25 19:33:01 -07:00
|
|
|
Curfn.Func.Dcl = list(Curfn.Func.Dcl, n)
|
2015-02-13 14:40:36 -05:00
|
|
|
return n
|
|
|
|
}
|
|
|
|
|
|
|
|
var newlabel_inl_label int
|
|
|
|
|
|
|
|
func newlabel_inl() *Node {
|
|
|
|
newlabel_inl_label++
|
2015-03-06 12:02:24 -08:00
|
|
|
n := newname(Lookupf(".inlret%.6d", newlabel_inl_label))
|
2015-02-13 14:40:36 -05:00
|
|
|
n.Etype = 1 // flag 'safe' for escape analysis (no backjumps)
|
|
|
|
return n
|
|
|
|
}
|
|
|
|
|
|
|
|
// inlsubst and inlsubstlist recursively copy the body of the saved
|
|
|
|
// pristine ->inl body of the function while substituting references
|
|
|
|
// to input/output parameters with ones to the tmpnames, and
|
|
|
|
// substituting returns with assignments to the output.
|
|
|
|
func inlsubstlist(ll *NodeList) *NodeList {
|
2015-03-02 14:22:05 -05:00
|
|
|
var l *NodeList
|
2015-02-13 14:40:36 -05:00
|
|
|
for ; ll != nil; ll = ll.Next {
|
|
|
|
l = list(l, inlsubst(ll.N))
|
|
|
|
}
|
|
|
|
return l
|
|
|
|
}
|
|
|
|
|
|
|
|
func inlsubst(n *Node) *Node {
|
|
|
|
if n == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
switch n.Op {
|
|
|
|
case ONAME:
|
2015-05-15 10:02:19 -07:00
|
|
|
if n.Name.Inlvar != nil { // These will be set during inlnode
|
2015-02-13 14:40:36 -05:00
|
|
|
if Debug['m'] > 2 {
|
2015-05-15 10:02:19 -07:00
|
|
|
fmt.Printf("substituting name %v -> %v\n", Nconv(n, obj.FmtSign), Nconv(n.Name.Inlvar, obj.FmtSign))
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2015-05-15 10:02:19 -07:00
|
|
|
return n.Name.Inlvar
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if Debug['m'] > 2 {
|
|
|
|
fmt.Printf("not substituting name %v\n", Nconv(n, obj.FmtSign))
|
|
|
|
}
|
|
|
|
return n
|
|
|
|
|
2015-04-01 09:38:44 -07:00
|
|
|
case OLITERAL, OTYPE:
|
2015-02-13 14:40:36 -05:00
|
|
|
return n
|
|
|
|
|
|
|
|
// Since we don't handle bodies with closures, this return is guaranteed to belong to the current inlined function.
|
|
|
|
|
|
|
|
// dump("Return before substitution", n);
|
|
|
|
case ORETURN:
|
2015-02-23 16:07:24 -05:00
|
|
|
m := Nod(OGOTO, inlretlabel, nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
m.Ninit = inlsubstlist(n.Ninit)
|
|
|
|
|
|
|
|
if inlretvars != nil && n.List != nil {
|
2015-02-23 16:07:24 -05:00
|
|
|
as := Nod(OAS2, nil, nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
// shallow copy or OINLCALL->rlist will be the same list, and later walk and typecheck may clobber that.
|
2015-02-23 16:07:24 -05:00
|
|
|
for ll := inlretvars; ll != nil; ll = ll.Next {
|
2015-02-13 14:40:36 -05:00
|
|
|
as.List = list(as.List, ll.N)
|
|
|
|
}
|
|
|
|
as.Rlist = inlsubstlist(n.List)
|
|
|
|
typecheck(&as, Etop)
|
|
|
|
m.Ninit = list(m.Ninit, as)
|
|
|
|
}
|
|
|
|
|
|
|
|
typechecklist(m.Ninit, Etop)
|
|
|
|
typecheck(&m, Etop)
|
|
|
|
|
|
|
|
// dump("Return after substitution", m);
|
|
|
|
return m
|
|
|
|
|
2015-04-01 09:38:44 -07:00
|
|
|
case OGOTO, OLABEL:
|
2015-02-23 16:07:24 -05:00
|
|
|
m := Nod(OXXX, nil, nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
*m = *n
|
|
|
|
m.Ninit = nil
|
2015-02-23 16:07:24 -05:00
|
|
|
p := fmt.Sprintf("%s·%d", n.Left.Sym.Name, inlgen)
|
2015-02-13 14:40:36 -05:00
|
|
|
m.Left = newname(Lookup(p))
|
|
|
|
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
m := Nod(OXXX, nil, nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
*m = *n
|
|
|
|
m.Ninit = nil
|
|
|
|
|
|
|
|
if n.Op == OCLOSURE {
|
|
|
|
Fatal("cannot inline function containing closure: %v", Nconv(n, obj.FmtSign))
|
|
|
|
}
|
|
|
|
|
|
|
|
m.Left = inlsubst(n.Left)
|
|
|
|
m.Right = inlsubst(n.Right)
|
|
|
|
m.List = inlsubstlist(n.List)
|
|
|
|
m.Rlist = inlsubstlist(n.Rlist)
|
|
|
|
m.Ninit = concat(m.Ninit, inlsubstlist(n.Ninit))
|
|
|
|
m.Ntest = inlsubst(n.Ntest)
|
|
|
|
m.Nbody = inlsubstlist(n.Nbody)
|
|
|
|
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
|
|
|
// Plaster over linenumbers
|
|
|
|
func setlnolist(ll *NodeList, lno int) {
|
|
|
|
for ; ll != nil; ll = ll.Next {
|
|
|
|
setlno(ll.N, lno)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func setlno(n *Node, lno int) {
|
2015-02-17 22:13:49 -05:00
|
|
|
if n == nil {
|
2015-02-13 14:40:36 -05:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// don't clobber names, unless they're freshly synthesized
|
|
|
|
if n.Op != ONAME || n.Lineno == 0 {
|
|
|
|
n.Lineno = int32(lno)
|
|
|
|
}
|
|
|
|
|
|
|
|
setlno(n.Left, lno)
|
|
|
|
setlno(n.Right, lno)
|
|
|
|
setlnolist(n.List, lno)
|
|
|
|
setlnolist(n.Rlist, lno)
|
|
|
|
setlnolist(n.Ninit, lno)
|
|
|
|
setlno(n.Ntest, lno)
|
|
|
|
setlnolist(n.Nbody, lno)
|
|
|
|
}
|