1
2
3
4
5 package inlheur
6
7 import (
8 "cmd/compile/internal/ir"
9 "cmd/compile/internal/pgoir"
10 "cmd/compile/internal/typecheck"
11 "fmt"
12 "os"
13 "strings"
14 )
15
16 type callSiteAnalyzer struct {
17 fn *ir.Func
18 *nameFinder
19 }
20
21 type callSiteTableBuilder struct {
22 fn *ir.Func
23 *nameFinder
24 cstab CallSiteTab
25 ptab map[ir.Node]pstate
26 nstack []ir.Node
27 loopNest int
28 isInit bool
29 }
30
31 func makeCallSiteAnalyzer(fn *ir.Func) *callSiteAnalyzer {
32 return &callSiteAnalyzer{
33 fn: fn,
34 nameFinder: newNameFinder(fn),
35 }
36 }
37
38 func makeCallSiteTableBuilder(fn *ir.Func, cstab CallSiteTab, ptab map[ir.Node]pstate, loopNestingLevel int, nf *nameFinder) *callSiteTableBuilder {
39 isInit := fn.IsPackageInit() || strings.HasPrefix(fn.Sym().Name, "init.")
40 return &callSiteTableBuilder{
41 fn: fn,
42 cstab: cstab,
43 ptab: ptab,
44 isInit: isInit,
45 loopNest: loopNestingLevel,
46 nstack: []ir.Node{fn},
47 nameFinder: nf,
48 }
49 }
50
51
52
53
54
55
56 func computeCallSiteTable(fn *ir.Func, region ir.Nodes, cstab CallSiteTab, ptab map[ir.Node]pstate, loopNestingLevel int, nf *nameFinder) CallSiteTab {
57 cstb := makeCallSiteTableBuilder(fn, cstab, ptab, loopNestingLevel, nf)
58 var doNode func(ir.Node) bool
59 doNode = func(n ir.Node) bool {
60 cstb.nodeVisitPre(n)
61 ir.DoChildren(n, doNode)
62 cstb.nodeVisitPost(n)
63 return false
64 }
65 for _, n := range region {
66 doNode(n)
67 }
68 return cstb.cstab
69 }
70
71 func (cstb *callSiteTableBuilder) flagsForNode(call *ir.CallExpr) CSPropBits {
72 var r CSPropBits
73
74 if debugTrace&debugTraceCalls != 0 {
75 fmt.Fprintf(os.Stderr, "=-= analyzing call at %s\n",
76 fmtFullPos(call.Pos()))
77 }
78
79
80 if cstb.loopNest > 0 {
81 r |= CallSiteInLoop
82 }
83
84
85
86 if cstb.isInit {
87 r |= CallSiteInInitFunc
88 }
89
90
91
92
93 if !isMainMain(cstb.fn) {
94 r = cstb.determinePanicPathBits(call, r)
95 }
96
97 return r
98 }
99
100
101
102
103
104
105 func (cstb *callSiteTableBuilder) determinePanicPathBits(call ir.Node, r CSPropBits) CSPropBits {
106 cstb.nstack = append(cstb.nstack, call)
107 defer func() {
108 cstb.nstack = cstb.nstack[:len(cstb.nstack)-1]
109 }()
110
111 for ri := range cstb.nstack[:len(cstb.nstack)-1] {
112 i := len(cstb.nstack) - ri - 1
113 n := cstb.nstack[i]
114 _, isCallExpr := n.(*ir.CallExpr)
115 _, isStmt := n.(ir.Stmt)
116 if isCallExpr {
117 isStmt = false
118 }
119
120 if debugTrace&debugTraceCalls != 0 {
121 ps, inps := cstb.ptab[n]
122 fmt.Fprintf(os.Stderr, "=-= callpar %d op=%s ps=%s inptab=%v stmt=%v\n", i, n.Op().String(), ps.String(), inps, isStmt)
123 }
124
125 if n.Op() == ir.OPANIC {
126 r |= CallSiteOnPanicPath
127 break
128 }
129 if v, ok := cstb.ptab[n]; ok {
130 if v == psCallsPanic {
131 r |= CallSiteOnPanicPath
132 break
133 }
134 if isStmt {
135 break
136 }
137 }
138 }
139 return r
140 }
141
142
143 func (cstb *callSiteTableBuilder) propsForArg(arg ir.Node) ActualExprPropBits {
144 if cval := cstb.constValue(arg); cval != nil {
145 return ActualExprConstant
146 }
147 if cstb.isConcreteConvIface(arg) {
148 return ActualExprIsConcreteConvIface
149 }
150 fname := cstb.funcName(arg)
151 if fname != nil {
152 if fn := fname.Func; fn != nil && typecheck.HaveInlineBody(fn) {
153 return ActualExprIsInlinableFunc
154 }
155 return ActualExprIsFunc
156 }
157 return 0
158 }
159
160
161
162
163
164
165 func (cstb *callSiteTableBuilder) argPropsForCall(ce *ir.CallExpr) []ActualExprPropBits {
166 rv := make([]ActualExprPropBits, len(ce.Args))
167 somethingInteresting := false
168 for idx := range ce.Args {
169 argProp := cstb.propsForArg(ce.Args[idx])
170 somethingInteresting = somethingInteresting || (argProp != 0)
171 rv[idx] = argProp
172 }
173 if !somethingInteresting {
174 return nil
175 }
176 return rv
177 }
178
179 func (cstb *callSiteTableBuilder) addCallSite(callee *ir.Func, call *ir.CallExpr) {
180 flags := cstb.flagsForNode(call)
181 argProps := cstb.argPropsForCall(call)
182 if debugTrace&debugTraceCalls != 0 {
183 fmt.Fprintf(os.Stderr, "=-= props %+v for call %v\n", argProps, call)
184 }
185
186 cs := &CallSite{
187 Call: call,
188 Callee: callee,
189 Assign: cstb.containingAssignment(call),
190 ArgProps: argProps,
191 Flags: flags,
192 ID: uint(len(cstb.cstab)),
193 }
194 if _, ok := cstb.cstab[call]; ok {
195 fmt.Fprintf(os.Stderr, "*** cstab duplicate entry at: %s\n",
196 fmtFullPos(call.Pos()))
197 fmt.Fprintf(os.Stderr, "*** call: %+v\n", call)
198 panic("bad")
199 }
200
201
202
203 cs.Score = int(callee.Inl.Cost)
204
205 if cstb.cstab == nil {
206 cstb.cstab = make(CallSiteTab)
207 }
208 cstb.cstab[call] = cs
209 if debugTrace&debugTraceCalls != 0 {
210 fmt.Fprintf(os.Stderr, "=-= added callsite: caller=%v callee=%v n=%s\n",
211 cstb.fn, callee, fmtFullPos(call.Pos()))
212 }
213 }
214
215 func (cstb *callSiteTableBuilder) nodeVisitPre(n ir.Node) {
216 switch n.Op() {
217 case ir.ORANGE, ir.OFOR:
218 if !hasTopLevelLoopBodyReturnOrBreak(loopBody(n)) {
219 cstb.loopNest++
220 }
221 case ir.OCALLFUNC:
222 ce := n.(*ir.CallExpr)
223 callee := pgoir.DirectCallee(ce.Fun)
224 if callee != nil && callee.Inl != nil {
225 cstb.addCallSite(callee, ce)
226 }
227 }
228 cstb.nstack = append(cstb.nstack, n)
229 }
230
231 func (cstb *callSiteTableBuilder) nodeVisitPost(n ir.Node) {
232 cstb.nstack = cstb.nstack[:len(cstb.nstack)-1]
233 switch n.Op() {
234 case ir.ORANGE, ir.OFOR:
235 if !hasTopLevelLoopBodyReturnOrBreak(loopBody(n)) {
236 cstb.loopNest--
237 }
238 }
239 }
240
241 func loopBody(n ir.Node) ir.Nodes {
242 if forst, ok := n.(*ir.ForStmt); ok {
243 return forst.Body
244 }
245 if rst, ok := n.(*ir.RangeStmt); ok {
246 return rst.Body
247 }
248 return nil
249 }
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267 func hasTopLevelLoopBodyReturnOrBreak(loopBody ir.Nodes) bool {
268 for _, n := range loopBody {
269 if n.Op() == ir.ORETURN || n.Op() == ir.OBREAK {
270 return true
271 }
272 }
273 return false
274 }
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297 func (cstb *callSiteTableBuilder) containingAssignment(n ir.Node) ir.Node {
298 parent := cstb.nstack[len(cstb.nstack)-1]
299
300
301
302 assignsOnlyAutoTemps := func(x ir.Node) bool {
303 alst := x.(*ir.AssignListStmt)
304 oa2init := alst.Init()
305 if len(oa2init) == 0 {
306 return false
307 }
308 for _, v := range oa2init {
309 d := v.(*ir.Decl)
310 if !ir.IsAutoTmp(d.X) {
311 return false
312 }
313 }
314 return true
315 }
316
317
318 if parent.Op() == ir.OAS {
319 return parent
320 }
321
322
323 if parent.Op() == ir.OAS2FUNC {
324
325
326
327
328
329
330 if assignsOnlyAutoTemps(parent) {
331 par2 := cstb.nstack[len(cstb.nstack)-2]
332 if par2.Op() == ir.OAS2 {
333 return par2
334 }
335 if par2.Op() == ir.OCONVNOP {
336 par3 := cstb.nstack[len(cstb.nstack)-3]
337 if par3.Op() == ir.OAS2 {
338 return par3
339 }
340 }
341 }
342 }
343
344 return nil
345 }
346
347
348
349
350
351
352
353 func UpdateCallsiteTable(callerfn *ir.Func, n *ir.CallExpr, ic *ir.InlinedCallExpr) {
354 enableDebugTraceIfEnv()
355 defer disableDebugTrace()
356
357 funcInlHeur, ok := fpmap[callerfn]
358 if !ok {
359
360 if debugTrace&debugTraceCalls != 0 {
361 fmt.Fprintf(os.Stderr, "=-= early exit, no entry for caller fn %v\n", callerfn)
362 }
363 return
364 }
365
366 if debugTrace&debugTraceCalls != 0 {
367 fmt.Fprintf(os.Stderr, "=-= UpdateCallsiteTable(caller=%v, cs=%s)\n",
368 callerfn, fmtFullPos(n.Pos()))
369 }
370
371
372 oldcs, ok := funcInlHeur.cstab[n]
373 if !ok {
374
375 return
376 }
377 oldcs.aux |= csAuxInlined
378
379 if debugTrace&debugTraceCalls != 0 {
380 fmt.Fprintf(os.Stderr, "=-= marked as inlined: callee=%v %s\n",
381 oldcs.Callee, EncodeCallSiteKey(oldcs))
382 }
383
384
385 var icp pstate
386 if oldcs.Flags&CallSiteOnPanicPath != 0 {
387 icp = psCallsPanic
388 }
389 var loopNestLevel int
390 if oldcs.Flags&CallSiteInLoop != 0 {
391 loopNestLevel = 1
392 }
393 ptab := map[ir.Node]pstate{ic: icp}
394 nf := newNameFinder(nil)
395 icstab := computeCallSiteTable(callerfn, ic.Body, nil, ptab, loopNestLevel, nf)
396
397
398 for _, cs := range icstab {
399 cs.parent = oldcs
400 }
401
402
403
404
405
406
407
408
409
410 csa := makeCallSiteAnalyzer(nil)
411 const doCallResults = false
412 csa.scoreCallsRegion(callerfn, ic.Body, icstab, doCallResults, ic)
413 }
414
View as plain text