Commit e4cae432 authored by Josh Bleecher Snyder's avatar Josh Bleecher Snyder

cmd/compile: add inline explainer

When compiling with -m -m, this adds output
for every non-inlined function explaining why
it was not inlined.

Change-Id: Icb59ae912a835c996e6b3475b163ee5125113001
Reviewed-on: https://go-review.googlesource.com/22782
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
Reviewed-by: default avatarBrad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent ef6fde26
...@@ -92,13 +92,24 @@ func caninl(fn *Node) { ...@@ -92,13 +92,24 @@ func caninl(fn *Node) {
Fatalf("caninl no nname %v", Nconv(fn, FmtSign)) Fatalf("caninl no nname %v", Nconv(fn, FmtSign))
} }
var reason string // reason, if any, that the function was not inlined
if Debug['m'] > 1 {
defer func() {
if reason != "" {
fmt.Printf("%v: cannot inline %v: %s\n", fn.Line(), fn.Func.Nname, reason)
}
}()
}
// If marked "go:noinline", don't inline // If marked "go:noinline", don't inline
if fn.Func.Pragma&Noinline != 0 { if fn.Func.Pragma&Noinline != 0 {
reason = "marked go:noinline"
return return
} }
// If fn has no body (is defined outside of Go), cannot inline it. // If fn has no body (is defined outside of Go), cannot inline it.
if fn.Nbody.Len() == 0 { if fn.Nbody.Len() == 0 {
reason = "no function body"
return return
} }
...@@ -111,6 +122,7 @@ func caninl(fn *Node) { ...@@ -111,6 +122,7 @@ func caninl(fn *Node) {
f := fn.Type.Params().Fields() f := fn.Type.Params().Fields()
if len := f.Len(); len > 0 { if len := f.Len(); len > 0 {
if t := f.Index(len - 1); t.Isddd { if t := f.Index(len - 1); t.Isddd {
reason = "has ... args"
return return
} }
} }
...@@ -123,12 +135,17 @@ func caninl(fn *Node) { ...@@ -123,12 +135,17 @@ func caninl(fn *Node) {
// The example that we observed is inlining of LockOSThread, // The example that we observed is inlining of LockOSThread,
// which lead to false race reports on m contents. // which lead to false race reports on m contents.
if instrumenting && myimportpath == "runtime" { if instrumenting && myimportpath == "runtime" {
reason = "instrumenting and is runtime function"
return return
} }
const maxBudget = 80 const maxBudget = 80
budget := int32(maxBudget) // allowed hairyness budget := int32(maxBudget) // allowed hairyness
if ishairylist(fn.Nbody, &budget) || budget < 0 { if ishairylist(fn.Nbody, &budget, &reason) {
return
}
if budget < 0 {
reason = "function too complex"
return return
} }
...@@ -157,16 +174,16 @@ func caninl(fn *Node) { ...@@ -157,16 +174,16 @@ func caninl(fn *Node) {
} }
// Look for anything we want to punt on. // Look for anything we want to punt on.
func ishairylist(ll Nodes, budget *int32) bool { func ishairylist(ll Nodes, budget *int32, reason *string) bool {
for _, n := range ll.Slice() { for _, n := range ll.Slice() {
if ishairy(n, budget) { if ishairy(n, budget, reason) {
return true return true
} }
} }
return false return false
} }
func ishairy(n *Node, budget *int32) bool { func ishairy(n *Node, budget *int32, reason *string) bool {
if n == nil { if n == nil {
return false return false
} }
...@@ -186,6 +203,7 @@ func ishairy(n *Node, budget *int32) bool { ...@@ -186,6 +203,7 @@ func ishairy(n *Node, budget *int32) bool {
} }
} }
if Debug['l'] < 4 { if Debug['l'] < 4 {
*reason = "non-leaf function"
return true return true
} }
...@@ -203,12 +221,14 @@ func ishairy(n *Node, budget *int32) bool { ...@@ -203,12 +221,14 @@ func ishairy(n *Node, budget *int32) bool {
break break
} }
if Debug['l'] < 4 { if Debug['l'] < 4 {
*reason = "non-leaf method"
return true return true
} }
// Things that are too hairy, irrespective of the budget // Things that are too hairy, irrespective of the budget
case OCALL, OCALLINTER, OPANIC, ORECOVER: case OCALL, OCALLINTER, OPANIC, ORECOVER:
if Debug['l'] < 4 { if Debug['l'] < 4 {
*reason = "non-leaf op " + n.Op.String()
return true return true
} }
...@@ -223,12 +243,15 @@ func ishairy(n *Node, budget *int32) bool { ...@@ -223,12 +243,15 @@ func ishairy(n *Node, budget *int32) bool {
ODCLTYPE, // can't print yet ODCLTYPE, // can't print yet
OBREAK, OBREAK,
ORETJMP: ORETJMP:
*reason = "unhandled op " + n.Op.String()
return true return true
} }
(*budget)-- (*budget)--
return *budget < 0 || ishairy(n.Left, budget) || ishairy(n.Right, budget) || ishairylist(n.List, budget) || ishairylist(n.Rlist, budget) || ishairylist(n.Ninit, budget) || ishairylist(n.Nbody, budget) return *budget < 0 || ishairy(n.Left, budget, reason) || ishairy(n.Right, budget, reason) ||
ishairylist(n.List, budget, reason) || ishairylist(n.Rlist, budget, reason) ||
ishairylist(n.Ninit, budget, reason) || ishairylist(n.Nbody, budget, reason)
} }
// Inlcopy and inlcopylist recursively copy the body of a function. // Inlcopy and inlcopylist recursively copy the body of a function.
......
...@@ -440,6 +440,10 @@ func Main() { ...@@ -440,6 +440,10 @@ func Main() {
for _, n := range list { for _, n := range list {
if !recursive { if !recursive {
caninl(n) caninl(n)
} else {
if Debug['m'] > 1 {
fmt.Printf("%v: cannot inline %v: recursive\n", n.Line(), n.Func.Nname)
}
} }
inlcalls(n) inlcalls(n)
} }
......
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