github.com/bir3/gocompiler@v0.9.2202/src/cmd/compile/internal/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 "github.com/bir3/gocompiler/src/cmd/compile/internal/base" 11 "github.com/bir3/gocompiler/src/cmd/compile/internal/devirtualize" 12 "github.com/bir3/gocompiler/src/cmd/compile/internal/inline" 13 "github.com/bir3/gocompiler/src/cmd/compile/internal/inline/inlheur" 14 "github.com/bir3/gocompiler/src/cmd/compile/internal/ir" 15 "github.com/bir3/gocompiler/src/cmd/compile/internal/pgo" 16 "github.com/bir3/gocompiler/src/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 }