cmd/compile: separate inline cost analysis from applying inlining

This CL separates the pass that computes inlinability from the pass
that performs inlinability. In particular, the latter can now happen
in any flat order, rather than bottom-up order. This also allows
inlining of calls exposed by devirtualization.

Change-Id: I389c0665fdc8288a6e25129a6744bfb1ace1eff7
Reviewed-on: https://go-review.googlesource.com/c/go/+/562319
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
This commit is contained in:
Matthew Dempsky 2024-02-07 10:23:53 -08:00 committed by Gopher Robot
parent a51713103b
commit b158ca9ae3
3 changed files with 25 additions and 45 deletions

View File

@ -141,38 +141,29 @@ func CanInlineFuncs(funcs []*ir.Func, profile *pgo.Profile) {
PGOInlinePrologue(profile)
}
ir.VisitFuncsBottomUp(funcs, func(list []*ir.Func, recursive bool) {
CanInlineSCC(list, recursive, profile)
})
}
// CanInlineSCC computes the inlinability of functions within an SCC
// (strongly connected component).
//
// CanInlineSCC is designed to be used by ir.VisitFuncsBottomUp
// callbacks.
func CanInlineSCC(funcs []*ir.Func, recursive bool, profile *pgo.Profile) {
if base.Flag.LowerL == 0 {
return
}
numfns := numNonClosures(funcs)
ir.VisitFuncsBottomUp(funcs, func(funcs []*ir.Func, recursive bool) {
numfns := numNonClosures(funcs)
for _, fn := range funcs {
if !recursive || numfns > 1 {
// We allow inlining if there is no
// recursion, or the recursion cycle is
// across more than one function.
CanInline(fn, profile)
} else {
if base.Flag.LowerM > 1 && fn.OClosure == nil {
fmt.Printf("%v: cannot inline %v: recursive\n", ir.Line(fn), fn.Nname)
for _, fn := range funcs {
if !recursive || numfns > 1 {
// We allow inlining if there is no
// recursion, or the recursion cycle is
// across more than one function.
CanInline(fn, profile)
} else {
if base.Flag.LowerM > 1 && fn.OClosure == nil {
fmt.Printf("%v: cannot inline %v: recursive\n", ir.Line(fn), fn.Nname)
}
}
if inlheur.Enabled() {
analyzeFuncProps(fn, profile)
}
}
if inlheur.Enabled() {
analyzeFuncProps(fn, profile)
}
}
})
}
// GarbageCollectUnreferencedHiddenClosures makes a pass over all the

View File

@ -38,27 +38,16 @@ func DevirtualizeAndInlinePackage(pkg *ir.Package, profile *pgo.Profile) {
if base.Debug.PGOInline != 0 {
inlProfile = profile
}
if inlProfile != nil {
inline.PGOInlinePrologue(inlProfile)
// First compute inlinability of all functions in the package.
inline.CanInlineFuncs(pkg.Funcs, inlProfile)
// Now we make a second pass to do devirtualization and inlining of
// calls. Order here should not matter.
for _, fn := range pkg.Funcs {
DevirtualizeAndInlineFunc(fn, inlProfile)
}
ir.VisitFuncsBottomUp(pkg.Funcs, func(funcs []*ir.Func, recursive bool) {
// We visit functions within an SCC in fairly arbitrary order,
// so by computing inlinability for all functions in the SCC
// before performing any inlining, the results are less
// sensitive to the order within the SCC (see #58905 for an
// example).
// First compute inlinability for all functions in the SCC ...
inline.CanInlineSCC(funcs, recursive, inlProfile)
// ... then make a second pass to do devirtualization and inlining
// of calls.
for _, fn := range funcs {
DevirtualizeAndInlineFunc(fn, inlProfile)
}
})
if base.Flag.LowerL != 0 {
// Perform a garbage collection of hidden closures functions that
// are no longer reachable from top-level functions following

View File

@ -32,7 +32,7 @@ func F(peerShare []byte) ([]byte, error) { // ERROR "leaking param: peerShare"
func f() { // ERROR "can inline f"
var i interface{ m() } = T(0) // ERROR "T\(0\) does not escape"
i.m() // ERROR "devirtualizing i.m"
i.m() // ERROR "devirtualizing i.m" "inlining call to T.m"
}
type T int