1
2
3
4
5
6
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/pgoir"
16 "cmd/compile/internal/typecheck"
17 "fmt"
18 )
19
20
21
22 func DevirtualizeAndInlinePackage(pkg *ir.Package, profile *pgoir.Profile) {
23 if base.Flag.W > 1 {
24 for _, fn := range typecheck.Target.Funcs {
25 s := fmt.Sprintf("\nbefore devirtualize-and-inline %v", fn.Sym())
26 ir.DumpList(s, fn.Body)
27 }
28 }
29
30 if profile != nil && base.Debug.PGODevirtualize > 0 {
31
32 ir.VisitFuncsBottomUp(typecheck.Target.Funcs, func(list []*ir.Func, recursive bool) {
33 for _, fn := range list {
34 devirtualize.ProfileGuided(fn, profile)
35 }
36 })
37 }
38
39 if base.Flag.LowerL != 0 {
40 inlheur.SetupScoreAdjustments()
41 }
42
43 var inlProfile *pgoir.Profile
44 if base.Debug.PGOInline != 0 {
45 inlProfile = profile
46 }
47
48
49 inline.CanInlineFuncs(pkg.Funcs, inlProfile)
50
51 inlState := make(map[*ir.Func]*inlClosureState)
52 calleeUseCounts := make(map[*ir.Func]int)
53
54 var state devirtualize.State
55
56
57 for _, fn := range typecheck.Target.Funcs {
58 bigCaller := base.Flag.LowerL != 0 && inline.IsBigFunc(fn)
59 if bigCaller && base.Flag.LowerM > 1 {
60 fmt.Printf("%v: function %v considered 'big'; reducing max cost of inlinees\n", ir.Line(fn), fn)
61 }
62
63 s := &inlClosureState{bigCaller: bigCaller, profile: profile, fn: fn, callSites: make(map[*ir.ParenExpr]bool), useCounts: calleeUseCounts}
64 s.parenthesize()
65 inlState[fn] = s
66
67
68 for i := range s.parens {
69 s.resolve(&state, i)
70 }
71 }
72
73 ir.VisitFuncsBottomUp(typecheck.Target.Funcs, func(list []*ir.Func, recursive bool) {
74
75 anyInlineHeuristics := false
76
77
78 for _, fn := range list {
79 if base.Flag.LowerL != 0 {
80 if inlheur.Enabled() && !fn.Wrapper() {
81 inlheur.ScoreCalls(fn)
82 anyInlineHeuristics = true
83 }
84 if base.Debug.DumpInlFuncProps != "" && !fn.Wrapper() {
85 inlheur.DumpFuncProps(fn, base.Debug.DumpInlFuncProps)
86 }
87 }
88 }
89
90 if anyInlineHeuristics {
91 defer inlheur.ScoreCallsCleanup()
92 }
93
94
95 done := false
96 for !done {
97 done = true
98 for _, fn := range list {
99 s := inlState[fn]
100
101 ir.WithPos(fn, func() {
102 l1 := len(s.parens)
103 l0 := 0
104
105
106
107
108
109
110 for {
111 for i := l0; i < l1; i++ {
112 paren := s.parens[i]
113 if origCall, inlinedCall := s.edit(&state, i); inlinedCall != nil {
114
115 paren.X = inlinedCall
116 ir.EditChildren(inlinedCall, s.mark)
117 state.InlinedCall(s.fn, origCall, inlinedCall)
118 done = false
119 }
120 }
121 l0, l1 = l1, len(s.parens)
122 if l0 == l1 {
123 break
124 }
125 for i := l0; i < l1; i++ {
126 s.resolve(&state, i)
127 }
128
129 }
130
131 })
132
133 }
134 }
135 })
136
137 if base.Flag.LowerL != 0 {
138 if base.Debug.DumpInlFuncProps != "" {
139 inlheur.DumpFuncProps(nil, base.Debug.DumpInlFuncProps)
140 }
141 if inlheur.Enabled() {
142 inline.PostProcessCallSites(inlProfile)
143 inlheur.TearDown()
144 }
145 }
146
147
148 for _, fn := range typecheck.Target.Funcs {
149 inlState[fn].unparenthesize()
150 }
151
152 }
153
154
155
156 func DevirtualizeAndInlineFunc(fn *ir.Func, profile *pgoir.Profile) {
157 ir.WithPos(fn, func() {
158 if base.Flag.LowerL != 0 {
159 if inlheur.Enabled() && !fn.Wrapper() {
160 inlheur.ScoreCalls(fn)
161 defer inlheur.ScoreCallsCleanup()
162 }
163 if base.Debug.DumpInlFuncProps != "" && !fn.Wrapper() {
164 inlheur.DumpFuncProps(fn, base.Debug.DumpInlFuncProps)
165 }
166 }
167
168 bigCaller := base.Flag.LowerL != 0 && inline.IsBigFunc(fn)
169 if bigCaller && base.Flag.LowerM > 1 {
170 fmt.Printf("%v: function %v considered 'big'; reducing max cost of inlinees\n", ir.Line(fn), fn)
171 }
172
173 s := &inlClosureState{bigCaller: bigCaller, profile: profile, fn: fn, callSites: make(map[*ir.ParenExpr]bool), useCounts: make(map[*ir.Func]int)}
174 s.parenthesize()
175 s.fixpoint()
176 s.unparenthesize()
177 })
178 }
179
180 type callSite struct {
181 fn *ir.Func
182 whichParen int
183 }
184
185 type inlClosureState struct {
186 fn *ir.Func
187 profile *pgoir.Profile
188 callSites map[*ir.ParenExpr]bool
189 resolved []*ir.Func
190 useCounts map[*ir.Func]int
191 parens []*ir.ParenExpr
192 bigCaller bool
193 }
194
195
196
197
198 func (s *inlClosureState) resolve(state *devirtualize.State, i int) (*ir.Func, int) {
199 p := s.parens[i]
200 if i < len(s.resolved) {
201 if callee := s.resolved[i]; callee != nil {
202 return callee, s.useCounts[callee]
203 }
204 }
205 n := p.X
206 call, ok := n.(*ir.CallExpr)
207 if !ok {
208 return nil, -1
209 }
210 devirtualize.StaticCall(s.fn, state, call)
211 if callee := inline.InlineCallTarget(s.fn, call, s.profile); callee != nil {
212 for len(s.resolved) <= i {
213 s.resolved = append(s.resolved, nil)
214 }
215 s.resolved[i] = callee
216 c := s.useCounts[callee] + 1
217 s.useCounts[callee] = c
218 return callee, c
219 }
220 return nil, 0
221 }
222
223 func (s *inlClosureState) edit(state *devirtualize.State, i int) (*ir.CallExpr, *ir.InlinedCallExpr) {
224 n := s.parens[i].X
225 call, ok := n.(*ir.CallExpr)
226 if !ok {
227 return nil, nil
228 }
229
230
231
232 callee, count := s.resolve(state, i)
233 if count <= 0 {
234 return nil, nil
235 }
236 if inlCall := inline.TryInlineCall(s.fn, call, s.bigCaller, s.profile, count == 1 && callee.ClosureParent != nil); inlCall != nil {
237 return call, inlCall
238 }
239 return nil, nil
240 }
241
242
243
244
245 func (s *inlClosureState) mark(n ir.Node) ir.Node {
246
247
248
249
250
251
252
253
254
255
256 p, _ := n.(*ir.ParenExpr)
257 if p != nil && s.callSites[p] {
258 return n
259 }
260
261 if p != nil {
262 n = p.X
263 }
264
265 ok := match(n)
266
267
268 if t, ok := n.(*ir.TailCallStmt); ok {
269 ir.EditChildren(t.Call, s.mark)
270 } else {
271 ir.EditChildren(n, s.mark)
272 }
273
274 if ok {
275 if p == nil {
276 p = ir.NewParenExpr(n.Pos(), n)
277 p.SetType(n.Type())
278 p.SetTypecheck(n.Typecheck())
279 s.callSites[p] = true
280 }
281
282 s.parens = append(s.parens, p)
283 n = p
284 } else if p != nil {
285 n = p
286 }
287 return n
288 }
289
290
291
292 func (s *inlClosureState) parenthesize() {
293 ir.EditChildren(s.fn, s.mark)
294 }
295
296 func (s *inlClosureState) unparenthesize() {
297 if s == nil {
298 return
299 }
300 if len(s.parens) == 0 {
301 return
302 }
303
304 var unparen func(ir.Node) ir.Node
305 unparen = func(n ir.Node) ir.Node {
306 if paren, ok := n.(*ir.ParenExpr); ok {
307 n = paren.X
308 }
309 ir.EditChildren(n, unparen)
310 return n
311 }
312 ir.EditChildren(s.fn, unparen)
313 }
314
315
316
317
318
319
320
321
322
323
324 func (s *inlClosureState) fixpoint() bool {
325 changed := false
326 var state devirtualize.State
327 ir.WithPos(s.fn, func() {
328 done := false
329 for !done {
330 done = true
331 for i := 0; i < len(s.parens); i++ {
332 paren := s.parens[i]
333 if origCall, inlinedCall := s.edit(&state, i); inlinedCall != nil {
334
335 paren.X = inlinedCall
336 ir.EditChildren(inlinedCall, s.mark)
337 state.InlinedCall(s.fn, origCall, inlinedCall)
338 done = false
339 changed = true
340 }
341 }
342 }
343 })
344 return changed
345 }
346
347 func match(n ir.Node) bool {
348 switch n := n.(type) {
349 case *ir.CallExpr:
350 return true
351 case *ir.TailCallStmt:
352 n.Call.NoInline = true
353 }
354 return false
355 }
356
View as plain text