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 }