Commit ce9bef26 authored by Matthew Dempsky's avatar Matthew Dempsky

cmd/compile/internal/gc: cleanup mkinlcall

I had too many failed attempts trying to remove iterFields that I
decided to overhaul this function. Much simpler and easier to
understand now (at least IMO).

Passes toolstash-check -all.

Change-Id: I41d00642a969698df3f4689e41a386346b966638
Reviewed-on: https://go-review.googlesource.com/39856
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarRobert Griesemer <gri@golang.org>
parent 94a9bc96
...@@ -564,74 +564,70 @@ func tinlvar(t *types.Field, inlvars map[*Node]*Node) *Node { ...@@ -564,74 +564,70 @@ func tinlvar(t *types.Field, inlvars map[*Node]*Node) *Node {
var inlgen int var inlgen int
// if *np is a call, and fn is a function with an inlinable body, substitute *np with an OINLCALL. // If n is a call, and fn is a function with an inlinable body,
// return an OINLCALL.
// On return ninit has the parameter assignments, the nbody is the // On return ninit has the parameter assignments, the nbody is the
// inlined function body and list, rlist contain the input, output // inlined function body and list, rlist contain the input, output
// parameters. // parameters.
// 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 *Node, fn *Node, isddd bool) *Node { func mkinlcall1(n *Node, fn *Node, isddd bool) *Node {
// For variadic fn.
if fn.Func.Inl.Len() == 0 { if fn.Func.Inl.Len() == 0 {
// No inlinable body.
return n return n
} }
if fn == Curfn || fn.Name.Defn == Curfn { if fn == Curfn || fn.Name.Defn == Curfn {
// Can't recursively inline a function into itself.
return n return n
} }
inlvars := make(map[*Node]*Node)
if Debug['l'] < 2 { if Debug['l'] < 2 {
typecheckinl(fn) typecheckinl(fn)
} }
// Bingo, 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, fn.Func.Inl)
} 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)
} }
if Debug['m'] > 2 { if Debug['m'] > 2 {
fmt.Printf("%v: Before inlining: %+v\n", n.Line(), n) fmt.Printf("%v: Before inlining: %+v\n", n.Line(), n)
} }
ninit := n.Ninit ninit := n.Ninit
//dumplist("ninit pre", ninit); // Find declarations corresponding to inlineable body.
var dcl []*Node var dcl []*Node
if fn.Name.Defn != nil { if fn.Name.Defn != nil {
// local function dcl = fn.Func.Inldcl.Slice() // local function
dcl = fn.Func.Inldcl.Slice()
} else { } else {
// imported function dcl = fn.Func.Dcl // imported function
dcl = fn.Func.Dcl
} }
var retvars []*Node // Make temp names to use instead of the originals.
i := 0 inlvars := make(map[*Node]*Node)
// Make temp names to use instead of the originals
for _, ln := range dcl { for _, ln := range dcl {
if ln.Op != ONAME {
continue
}
if ln.Class == PPARAMOUT { // return values handled below. if ln.Class == PPARAMOUT { // return values handled below.
continue continue
} }
if ln.isParamStackCopy() { // ignore the on-stack copy of a parameter that moved to the heap if ln.isParamStackCopy() { // ignore the on-stack copy of a parameter that moved to the heap
continue continue
} }
if ln.Op == ONAME {
inlvars[ln] = typecheck(inlvar(ln), Erv) inlvars[ln] = typecheck(inlvar(ln), Erv)
if ln.Class == PPARAM || ln.Name.Param.Stackcopy != nil && ln.Name.Param.Stackcopy.Class == PPARAM { if ln.Class == PPARAM || ln.Name.Param.Stackcopy != nil && ln.Name.Param.Stackcopy.Class == PPARAM {
ninit.Append(nod(ODCL, inlvars[ln], nil)) ninit.Append(nod(ODCL, inlvars[ln], nil))
} }
} }
}
// temporaries for return values. // temporaries for return values.
var retvars []*Node
for i, t := range fn.Type.Results().Fields().Slice() {
var m *Node var m *Node
for _, t := range fn.Type.Results().Fields().Slice() {
if t != nil && asNode(t.Nname) != nil && !isblank(asNode(t.Nname)) { if t != nil && asNode(t.Nname) != nil && !isblank(asNode(t.Nname)) {
m = inlvar(asNode(t.Nname)) m = inlvar(asNode(t.Nname))
m = typecheck(m, Erv) m = typecheck(m, Erv)
...@@ -639,152 +635,73 @@ func mkinlcall1(n *Node, fn *Node, isddd bool) *Node { ...@@ -639,152 +635,73 @@ func mkinlcall1(n *Node, fn *Node, isddd bool) *Node {
} 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)
i++
} }
ninit.Append(nod(ODCL, m, nil)) ninit.Append(nod(ODCL, m, nil))
retvars = append(retvars, m) retvars = append(retvars, m)
} }
// assign receiver. // Assign arguments to the parameters' temp names.
if fn.IsMethod() && n.Left.Op == ODOTMETH {
// method call with a receiver.
t := fn.Type.Recv()
if t != nil && t.Nname != nil && !isblank(asNode(t.Nname)) && inlvars[asNode(t.Nname)] == nil {
Fatalf("missing inlvar for %v\n", asNode(t.Nname))
}
if n.Left.Left == nil {
Fatalf("method call without receiver: %+v", n)
}
if t == nil {
Fatalf("method call unknown receiver type: %+v", n)
}
as := nod(OAS, tinlvar(t, inlvars), n.Left.Left)
if as != nil {
as = typecheck(as, Etop)
ninit.Append(as)
}
}
// check if inlined function is variadic.
variadic := false
var varargtype *types.Type
varargcount := 0
for _, t := range fn.Type.Params().Fields().Slice() {
if t.Isddd() {
variadic = true
varargtype = t.Type
}
}
// but if argument is dotted too forget about variadicity.
if variadic && isddd {
variadic = false
}
// check if argument is actually a returned tuple from call.
multiret := 0
if n.List.Len() == 1 {
switch n.List.First().Op {
case OCALL, OCALLFUNC, OCALLINTER, OCALLMETH:
if n.List.First().Left.Type.Results().NumFields() > 1 {
multiret = n.List.First().Left.Type.Results().NumFields() - 1
}
}
}
if variadic {
varargcount = n.List.Len() + multiret
if n.Left.Op != ODOTMETH {
varargcount -= fn.Type.Recvs().NumFields()
}
varargcount -= fn.Type.Params().NumFields() - 1
}
// assign arguments to the parameters' temp names
as := nod(OAS2, nil, nil) as := nod(OAS2, nil, nil)
as.Rlist.Set(n.List.Slice()) as.Rlist.Set(n.List.Slice())
li := 0
// TODO: if len(nlist) == 1 but multiple args, check that n->list->n is a call? // For non-dotted calls to variadic functions, we assign the
if fn.IsMethod() && n.Left.Op != ODOTMETH { // variadic parameter's temp name separately.
// non-method call to method var vas *Node
if n.List.Len() == 0 {
Fatalf("non-method call to method without first arg: %+v", n)
}
// append receiver inlvar to LHS. if fn.IsMethod() {
t := fn.Type.Recv() rcv := fn.Type.Recv()
if t != nil && t.Nname != nil && !isblank(asNode(t.Nname)) && inlvars[asNode(t.Nname)] == nil { if n.Left.Op == ODOTMETH {
Fatalf("missing inlvar for %v\n", asNode(t.Nname)) // For x.M(...), assign x directly to the
// receiver parameter.
if n.Left.Left == nil {
Fatalf("method call without receiver: %+v", n)
} }
if t == nil { ras := nod(OAS, tinlvar(rcv, inlvars), n.Left.Left)
Fatalf("method call unknown receiver type: %+v", n) ras = typecheck(ras, Etop)
ninit.Append(ras)
} else {
// For T.M(...), add the receiver parameter to
// as.List, so it's assigned by the normal
// arguments.
if as.Rlist.Len() == 0 {
Fatalf("non-method call to method without first arg: %+v", n)
} }
as.List.Append(tinlvar(t, inlvars)) as.List.Append(tinlvar(rcv, inlvars))
li++
} }
// append ordinary arguments to LHS.
chkargcount := n.List.Len() > 1
var vararg *Node // the slice argument to a variadic call
var varargs []*Node // the list of LHS names to put in vararg.
if !chkargcount {
// 0 or 1 expression on RHS.
var i int
for _, t := range fn.Type.Params().Fields().Slice() {
if variadic && t.Isddd() {
vararg = tinlvar(t, inlvars)
for i = 0; i < varargcount && li < n.List.Len(); i++ {
m = argvar(varargtype, i)
varargs = append(varargs, m)
as.List.Append(m)
} }
break for _, param := range fn.Type.Params().Fields().Slice() {
// For ordinary parameters or variadic parameters in
// dotted calls, just add the variable to the
// assignment list, and we're done.
if !param.Isddd() || isddd {
as.List.Append(tinlvar(param, inlvars))
continue
} }
as.List.Append(tinlvar(t, inlvars)) // Otherwise, we need to collect the remaining values
} // to pass as a slice.
} else {
// match arguments except final variadic (unless the call is dotted itself)
t, it := types.IterFields(fn.Type.Params())
for t != nil {
if li >= n.List.Len() {
break
}
if variadic && t.Isddd() {
break
}
as.List.Append(tinlvar(t, inlvars))
t = it.Next()
li++
}
// match varargcount arguments with variadic parameters. numvals := n.List.Len()
if variadic && t != nil && t.Isddd() { if numvals == 1 && n.List.First().Type.IsFuncArgStruct() {
vararg = tinlvar(t, inlvars) numvals = n.List.First().Type.NumFields()
var i int
for i = 0; i < varargcount && li < n.List.Len(); i++ {
m = argvar(varargtype, i)
varargs = append(varargs, m)
as.List.Append(m)
li++
} }
if i == varargcount { x := as.List.Len()
t = it.Next() for as.List.Len() < numvals {
} as.List.Append(argvar(param.Type, as.List.Len()))
} }
varargs := as.List.Slice()[x:]
if li < n.List.Len() || t != nil { vas = nod(OAS, tinlvar(param, inlvars), nil)
Fatalf("arg count mismatch: %#v vs %.v\n", fn.Type.Params(), n.List) if len(varargs) == 0 {
vas.Right = nodnil()
vas.Right.Type = param.Type
} else {
vas.Right = nod(OCOMPLIT, nil, typenod(param.Type))
vas.Right.List.Set(varargs)
} }
} }
...@@ -793,23 +710,12 @@ func mkinlcall1(n *Node, fn *Node, isddd bool) *Node { ...@@ -793,23 +710,12 @@ func mkinlcall1(n *Node, fn *Node, isddd bool) *Node {
ninit.Append(as) ninit.Append(as)
} }
// turn the variadic args into a slice. if vas != nil {
if variadic { vas = typecheck(vas, Etop)
as = nod(OAS, vararg, nil) ninit.Append(vas)
if varargcount == 0 {
as.Right = nodnil()
as.Right.Type = varargtype
} else {
varslicetype := types.NewSlice(varargtype.Elem())
as.Right = nod(OCOMPLIT, nil, typenod(varslicetype))
as.Right.List.Set(varargs)
}
as = typecheck(as, Etop)
ninit.Append(as)
} }
// zero the outparams // Zero the return parameters.
for _, n := range retvars { for _, n := range retvars {
as = nod(OAS, n, nil) as = nod(OAS, n, nil)
as = typecheck(as, Etop) as = typecheck(as, Etop)
...@@ -838,7 +744,6 @@ func mkinlcall1(n *Node, fn *Node, isddd bool) *Node { ...@@ -838,7 +744,6 @@ func mkinlcall1(n *Node, fn *Node, isddd bool) *Node {
//dumplist("ninit post", ninit); //dumplist("ninit post", ninit);
call := nod(OINLCALL, nil, nil) call := nod(OINLCALL, nil, nil)
call.Ninit.Set(ninit.Slice()) call.Ninit.Set(ninit.Slice())
call.Nbody.Set(body) call.Nbody.Set(body)
call.Rlist.Set(retvars) call.Rlist.Set(retvars)
......
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