diff options
Diffstat (limited to 'src/cmd/compile/internal/gc/inl.go')
-rw-r--r-- | src/cmd/compile/internal/gc/inl.go | 51 |
1 files changed, 34 insertions, 17 deletions
diff --git a/src/cmd/compile/internal/gc/inl.go b/src/cmd/compile/internal/gc/inl.go index b9460ed6d6..29210ff8de 100644 --- a/src/cmd/compile/internal/gc/inl.go +++ b/src/cmd/compile/internal/gc/inl.go @@ -496,7 +496,14 @@ func inlcalls(fn *Node) { if countNodes(fn) >= inlineBigFunctionNodes { maxCost = inlineBigFunctionMaxCost } - fn = inlnode(fn, maxCost) + // Map to keep track of functions that have been inlined at a particular + // call site, in order to stop inlining when we reach the beginning of a + // recursion cycle again. We don't inline immediately recursive functions, + // but allow inlining if there is a recursion cycle of many functions. + // Most likely, the inlining will stop before we even hit the beginning of + // the cycle again, but the map catches the unusual case. + inlMap := make(map[*Node]bool) + fn = inlnode(fn, maxCost, inlMap) if fn != Curfn { Fatalf("inlnode replaced curfn") } @@ -537,10 +544,10 @@ func inlconv2list(n *Node) []*Node { return s } -func inlnodelist(l Nodes, maxCost int32) { +func inlnodelist(l Nodes, maxCost int32, inlMap map[*Node]bool) { s := l.Slice() for i := range s { - s[i] = inlnode(s[i], maxCost) + s[i] = inlnode(s[i], maxCost, inlMap) } } @@ -557,7 +564,7 @@ func inlnodelist(l Nodes, maxCost int32) { // shorter and less complicated. // The result of inlnode MUST be assigned back to n, e.g. // n.Left = inlnode(n.Left) -func inlnode(n *Node, maxCost int32) *Node { +func inlnode(n *Node, maxCost int32, inlMap map[*Node]bool) *Node { if n == nil { return n } @@ -585,19 +592,19 @@ func inlnode(n *Node, maxCost int32) *Node { lno := setlineno(n) - inlnodelist(n.Ninit, maxCost) + inlnodelist(n.Ninit, maxCost, inlMap) for _, n1 := range n.Ninit.Slice() { if n1.Op == OINLCALL { inlconv2stmt(n1) } } - n.Left = inlnode(n.Left, maxCost) + n.Left = inlnode(n.Left, maxCost, inlMap) if n.Left != nil && n.Left.Op == OINLCALL { n.Left = inlconv2expr(n.Left) } - n.Right = inlnode(n.Right, maxCost) + n.Right = inlnode(n.Right, maxCost, inlMap) if n.Right != nil && n.Right.Op == OINLCALL { if n.Op == OFOR || n.Op == OFORUNTIL { inlconv2stmt(n.Right) @@ -612,7 +619,7 @@ func inlnode(n *Node, maxCost int32) *Node { } } - inlnodelist(n.List, maxCost) + inlnodelist(n.List, maxCost, inlMap) if n.Op == OBLOCK { for _, n2 := range n.List.Slice() { if n2.Op == OINLCALL { @@ -628,7 +635,7 @@ func inlnode(n *Node, maxCost int32) *Node { } } - inlnodelist(n.Rlist, maxCost) + inlnodelist(n.Rlist, maxCost, inlMap) s := n.Rlist.Slice() for i1, n1 := range s { if n1.Op == OINLCALL { @@ -640,7 +647,7 @@ func inlnode(n *Node, maxCost int32) *Node { } } - inlnodelist(n.Nbody, maxCost) + inlnodelist(n.Nbody, maxCost, inlMap) for _, n := range n.Nbody.Slice() { if n.Op == OINLCALL { inlconv2stmt(n) @@ -663,12 +670,12 @@ func inlnode(n *Node, maxCost int32) *Node { fmt.Printf("%v:call to func %+v\n", n.Line(), n.Left) } if n.Left.Func != nil && n.Left.Func.Inl != nil && !isIntrinsicCall(n) { // normal case - n = mkinlcall(n, n.Left, maxCost) + n = mkinlcall(n, n.Left, maxCost, inlMap) } else if n.Left.isMethodExpression() && asNode(n.Left.Sym.Def) != nil { - n = mkinlcall(n, asNode(n.Left.Sym.Def), maxCost) + n = mkinlcall(n, asNode(n.Left.Sym.Def), maxCost, inlMap) } else if n.Left.Op == OCLOSURE { if f := inlinableClosure(n.Left); f != nil { - n = mkinlcall(n, f, maxCost) + n = mkinlcall(n, f, maxCost, inlMap) } } else if n.Left.Op == ONAME && n.Left.Name != nil && n.Left.Name.Defn != nil { if d := n.Left.Name.Defn; d.Op == OAS && d.Right.Op == OCLOSURE { @@ -694,7 +701,7 @@ func inlnode(n *Node, maxCost int32) *Node { } break } - n = mkinlcall(n, f, maxCost) + n = mkinlcall(n, f, maxCost, inlMap) } } } @@ -713,7 +720,7 @@ func inlnode(n *Node, maxCost int32) *Node { Fatalf("no function definition for [%p] %+v\n", n.Left.Type, n.Left.Type) } - n = mkinlcall(n, asNode(n.Left.Type.FuncType().Nname), maxCost) + n = mkinlcall(n, asNode(n.Left.Type.FuncType().Nname), maxCost, inlMap) } lineno = lno @@ -833,7 +840,7 @@ var inlgen int // parameters. // The result of mkinlcall MUST be assigned back to n, e.g. // n.Left = mkinlcall(n.Left, fn, isddd) -func mkinlcall(n, fn *Node, maxCost int32) *Node { +func mkinlcall(n, fn *Node, maxCost int32, inlMap map[*Node]bool) *Node { if fn.Func.Inl == nil { // No inlinable body. return n @@ -866,6 +873,16 @@ func mkinlcall(n, fn *Node, maxCost int32) *Node { return n } + if inlMap[fn] { + if Debug['m'] > 1 { + fmt.Printf("%v: cannot inline %v into %v: repeated recursive cycle\n", n.Line(), fn, Curfn.funcname()) + } + return n + } + inlMap[fn] = true + defer func() { + inlMap[fn] = false + }() if Debug_typecheckinl == 0 { typecheckinl(fn) } @@ -1129,7 +1146,7 @@ func mkinlcall(n, fn *Node, maxCost int32) *Node { // instead we emit the things that the body needs // and each use must redo the inlining. // luckily these are small. - inlnodelist(call.Nbody, maxCost) + inlnodelist(call.Nbody, maxCost, inlMap) for _, n := range call.Nbody.Slice() { if n.Op == OINLCALL { inlconv2stmt(n) |