github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/inline/interleaved/interleaved.go (about)

     1  // Copyright 2023 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package interleaved implements the interleaved devirtualization and
     6  // inlining pass.
     7  package interleaved
     8  
     9  import (
    10  	"fmt"
    11  
    12  	"github.com/go-asm/go/cmd/compile/base"
    13  	"github.com/go-asm/go/cmd/compile/devirtualize"
    14  	"github.com/go-asm/go/cmd/compile/inline"
    15  	"github.com/go-asm/go/cmd/compile/inline/inlheur"
    16  	"github.com/go-asm/go/cmd/compile/ir"
    17  	"github.com/go-asm/go/cmd/compile/pgo"
    18  	"github.com/go-asm/go/cmd/compile/typecheck"
    19  )
    20  
    21  // DevirtualizeAndInlinePackage interleaves devirtualization and inlining on
    22  // all functions within pkg.
    23  func DevirtualizeAndInlinePackage(pkg *ir.Package, profile *pgo.Profile) {
    24  	if profile != nil && base.Debug.PGODevirtualize > 0 {
    25  		// TODO(mdempsky): Integrate into DevirtualizeAndInlineFunc below.
    26  		ir.VisitFuncsBottomUp(typecheck.Target.Funcs, func(list []*ir.Func, recursive bool) {
    27  			for _, fn := range list {
    28  				devirtualize.ProfileGuided(fn, profile)
    29  			}
    30  		})
    31  		ir.CurFunc = nil
    32  	}
    33  
    34  	if base.Flag.LowerL != 0 {
    35  		inlheur.SetupScoreAdjustments()
    36  	}
    37  
    38  	var inlProfile *pgo.Profile // copy of profile for inlining
    39  	if base.Debug.PGOInline != 0 {
    40  		inlProfile = profile
    41  	}
    42  	if inlProfile != nil {
    43  		inline.PGOInlinePrologue(inlProfile, pkg.Funcs)
    44  	}
    45  
    46  	ir.VisitFuncsBottomUp(pkg.Funcs, func(funcs []*ir.Func, recursive bool) {
    47  		// We visit functions within an SCC in fairly arbitrary order,
    48  		// so by computing inlinability for all functions in the SCC
    49  		// before performing any inlining, the results are less
    50  		// sensitive to the order within the SCC (see #58905 for an
    51  		// example).
    52  
    53  		// First compute inlinability for all functions in the SCC ...
    54  		inline.CanInlineSCC(funcs, recursive, inlProfile)
    55  
    56  		// ... then make a second pass to do devirtualization and inlining
    57  		// of calls.
    58  		for _, fn := range funcs {
    59  			DevirtualizeAndInlineFunc(fn, inlProfile)
    60  		}
    61  	})
    62  
    63  	if base.Flag.LowerL != 0 {
    64  		// Perform a garbage collection of hidden closures functions that
    65  		// are no longer reachable from top-level functions following
    66  		// inlining. See #59404 and #59638 for more context.
    67  		inline.GarbageCollectUnreferencedHiddenClosures()
    68  
    69  		if base.Debug.DumpInlFuncProps != "" {
    70  			inlheur.DumpFuncProps(nil, base.Debug.DumpInlFuncProps)
    71  		}
    72  		if inlheur.Enabled() {
    73  			inline.PostProcessCallSites(inlProfile)
    74  			inlheur.TearDown()
    75  		}
    76  	}
    77  }
    78  
    79  // DevirtualizeAndInlineFunc interleaves devirtualization and inlining
    80  // on a single function.
    81  func DevirtualizeAndInlineFunc(fn *ir.Func, profile *pgo.Profile) {
    82  	ir.WithFunc(fn, func() {
    83  		if base.Flag.LowerL != 0 {
    84  			if inlheur.Enabled() && !fn.Wrapper() {
    85  				inlheur.ScoreCalls(fn)
    86  				defer inlheur.ScoreCallsCleanup()
    87  			}
    88  			if base.Debug.DumpInlFuncProps != "" && !fn.Wrapper() {
    89  				inlheur.DumpFuncProps(fn, base.Debug.DumpInlFuncProps)
    90  			}
    91  		}
    92  
    93  		bigCaller := base.Flag.LowerL != 0 && inline.IsBigFunc(fn)
    94  		if bigCaller && base.Flag.LowerM > 1 {
    95  			fmt.Printf("%v: function %v considered 'big'; reducing max cost of inlinees\n", ir.Line(fn), fn)
    96  		}
    97  
    98  		// Walk fn's body and apply devirtualization and inlining.
    99  		var inlCalls []*ir.InlinedCallExpr
   100  		var edit func(ir.Node) ir.Node
   101  		edit = func(n ir.Node) ir.Node {
   102  			switch n := n.(type) {
   103  			case *ir.TailCallStmt:
   104  				n.Call.NoInline = true // can't inline yet
   105  			}
   106  
   107  			ir.EditChildren(n, edit)
   108  
   109  			if call, ok := n.(*ir.CallExpr); ok {
   110  				devirtualize.StaticCall(call)
   111  
   112  				if inlCall := inline.TryInlineCall(fn, call, bigCaller, profile); inlCall != nil {
   113  					inlCalls = append(inlCalls, inlCall)
   114  					n = inlCall
   115  				}
   116  			}
   117  
   118  			return n
   119  		}
   120  		ir.EditChildren(fn, edit)
   121  
   122  		// If we inlined any calls, we want to recursively visit their
   123  		// bodies for further devirtualization and inlining. However, we
   124  		// need to wait until *after* the original function body has been
   125  		// expanded, or else inlCallee can have false positives (e.g.,
   126  		// #54632).
   127  		for len(inlCalls) > 0 {
   128  			call := inlCalls[0]
   129  			inlCalls = inlCalls[1:]
   130  			ir.EditChildren(call, edit)
   131  		}
   132  	})
   133  }