...

Source file src/cmd/compile/internal/inline/interleaved/interleaved.go

Documentation: cmd/compile/internal/inline/interleaved

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

View as plain text