1
2
3
4
5 package bloop
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39 import (
40 "cmd/compile/internal/base"
41 "cmd/compile/internal/ir"
42 "cmd/compile/internal/typecheck"
43 "cmd/compile/internal/types"
44 "cmd/internal/src"
45 )
46
47
48
49 func getNameFromNode(n ir.Node) *ir.Name {
50
51 for n != nil {
52 switch n.Op() {
53 case ir.ONAME:
54
55 return n.(*ir.Name)
56 case ir.OSLICE, ir.OSLICE3:
57 n = n.(*ir.SliceExpr).X
58 case ir.ODOT:
59 n = n.(*ir.SelectorExpr).X
60 case ir.OCONV, ir.OCONVIFACE, ir.OCONVNOP:
61 n = n.(*ir.ConvExpr).X
62 case ir.OADDR:
63 n = n.(*ir.AddrExpr).X
64 case ir.ODOTPTR:
65 n = n.(*ir.SelectorExpr).X
66 case ir.OINDEX, ir.OINDEXMAP:
67 n = n.(*ir.IndexExpr).X
68 default:
69 n = nil
70 }
71 }
72 return nil
73 }
74
75
76 func getAddressableNameFromNode(n ir.Node) *ir.Name {
77 if name := getNameFromNode(n); name != nil && ir.IsAddressable(name) {
78 return name
79 }
80 return nil
81 }
82
83
84 func getKeepAliveNodes(pos src.XPos, n ir.Node) ir.Nodes {
85 name := getAddressableNameFromNode(n)
86 if name != nil {
87 debugName(name, pos)
88 return ir.Nodes{name}
89 } else if deref, ok := n.(*ir.StarExpr); ok && deref != nil {
90 if base.Flag.LowerM > 1 {
91 base.WarnfAt(pos, "dereference will be kept alive")
92 }
93 return ir.Nodes{deref}
94 } else if base.Flag.LowerM > 1 {
95 base.WarnfAt(pos, "expr is unknown to bloop pass")
96 }
97 return nil
98 }
99
100
101
102
103
104 func keepAliveAt(ns ir.Nodes, curNode ir.Node) ir.Node {
105 if len(ns) == 0 {
106 return curNode
107 }
108
109 pos := curNode.Pos()
110 calls := ir.Nodes{curNode}
111 for _, n := range ns {
112 if n == nil || n.Sym() == nil || n.Sym().IsBlank() {
113 continue
114 }
115 if !ir.IsAddressable(n) {
116 base.FatalfAt(n.Pos(), "keepAliveAt: node %v is not addressable", n)
117 }
118 arg := ir.NewConvExpr(pos, ir.OCONV, types.Types[types.TUNSAFEPTR], typecheck.NodAddr(n))
119 callExpr := typecheck.Call(pos, typecheck.LookupRuntime("KeepAlive"), ir.Nodes{arg}, false).(*ir.CallExpr)
120 callExpr.IsCompilerVarLive = true
121 callExpr.NoInline = true
122 calls = append(calls, callExpr)
123 }
124
125 return ir.NewBlockStmt(pos, calls)
126 }
127
128 func debugName(name *ir.Name, pos src.XPos) {
129 if base.Flag.LowerM > 1 {
130 if name.Linksym() != nil {
131 base.WarnfAt(pos, "%s will be kept alive", name.Linksym().Name)
132 } else {
133 base.WarnfAt(pos, "expr will be kept alive")
134 }
135 }
136 }
137
138
139 func preserveCallResults(curFn *ir.Func, call *ir.CallExpr) ir.Node {
140 var ns ir.Nodes
141 lhs := make(ir.Nodes, call.Fun.Type().NumResults())
142 for i, res := range call.Fun.Type().Results() {
143 tmp := typecheck.TempAt(call.Pos(), curFn, res.Type)
144 lhs[i] = tmp
145 ns = append(ns, tmp)
146 }
147
148 if base.Flag.LowerM > 1 {
149 plural := ""
150 if call.Fun.Type().NumResults() > 1 {
151 plural = "s"
152 }
153 base.WarnfAt(call.Pos(), "function result%s will be kept alive", plural)
154 }
155
156 assign := typecheck.AssignExpr(ir.NewAssignListStmt(call.Pos(), ir.OAS2, lhs, ir.Nodes{call})).(*ir.AssignListStmt)
157 assign.Def = true
158 for _, tmp := range lhs {
159
160 assign.PtrInit().Append(typecheck.Stmt(ir.NewDecl(assign.Pos(), ir.ODCL, tmp.(*ir.Name))))
161 }
162 return keepAliveAt(ns, assign)
163 }
164
165
166 func preserveCallArgs(curFn *ir.Func, call *ir.CallExpr) ir.Node {
167 var argTmps ir.Nodes
168 var names ir.Nodes
169 preserveTmp := func(pos src.XPos, n ir.Node) ir.Node {
170 tmp := typecheck.TempAt(pos, curFn, n.Type())
171 assign := ir.NewAssignStmt(pos, tmp, n)
172 assign.Def = true
173
174 assign.PtrInit().Append(typecheck.Stmt(ir.NewDecl(assign.Pos(), ir.ODCL, tmp)))
175 argTmps = append(argTmps, typecheck.AssignExpr(assign))
176 names = append(names, tmp)
177 if base.Flag.LowerM > 1 {
178 base.WarnfAt(call.Pos(), "function arg will be kept alive")
179 }
180 return tmp
181 }
182 for i, a := range call.Args {
183 if name := getAddressableNameFromNode(a); name != nil {
184
185 debugName(name, call.Pos())
186 names = append(names, name)
187 } else if a.Op() == ir.OSLICELIT {
188
189 s := a.(*ir.CompLitExpr)
190 var ns ir.Nodes
191 for i, elem := range s.List {
192 if name := getAddressableNameFromNode(elem); name != nil {
193 debugName(name, call.Pos())
194 ns = append(ns, name)
195 } else {
196
197 s.List[i] = preserveTmp(elem.Pos(), elem)
198 }
199 }
200 names = append(names, ns...)
201 } else {
202
203 call.Args[i] = preserveTmp(call.Pos(), a)
204 }
205 }
206 if len(argTmps) > 0 {
207 argTmps = append(argTmps, call)
208 return keepAliveAt(names, ir.NewBlockStmt(call.Pos(), argTmps))
209 }
210 return keepAliveAt(names, call)
211 }
212
213
214
215
216 func preserveStmt(curFn *ir.Func, stmt ir.Node) ir.Node {
217 switch n := stmt.(type) {
218 case *ir.AssignStmt:
219
220
221 if ir.IsBlank(n.X) {
222 tmp := typecheck.TempAt(n.Pos(), curFn, n.Y.Type())
223 n.X = tmp
224 n.Def = true
225 n.PtrInit().Append(typecheck.Stmt(ir.NewDecl(n.Pos(), ir.ODCL, tmp)))
226 stmt = typecheck.AssignExpr(n)
227 n = stmt.(*ir.AssignStmt)
228 }
229 return keepAliveAt(getKeepAliveNodes(n.Pos(), n.X), n)
230 case *ir.AssignListStmt:
231 var ns ir.Nodes
232 hasBlank := false
233 for i, lhs := range n.Lhs {
234 if ir.IsBlank(lhs) {
235
236
237 var typ *types.Type
238
239 if len(n.Rhs) == 1 && n.Rhs[0].Type() != nil &&
240 n.Rhs[0].Type().IsTuple() &&
241 len(n.Lhs) == n.Rhs[0].Type().NumFields() {
242 typ = n.Rhs[0].Type().Field(i).Type
243 } else if len(n.Rhs) == len(n.Lhs) {
244 typ = n.Rhs[i].Type()
245 } else {
246
247 base.WarnfAt(n.Pos(), "unrecognized shape for assign list stmt for blank assignment")
248 continue
249 }
250 tmp := typecheck.TempAt(n.Pos(), curFn, typ)
251 n.Lhs[i] = tmp
252 n.PtrInit().Append(typecheck.Stmt(ir.NewDecl(n.Pos(), ir.ODCL, tmp)))
253 hasBlank = true
254 }
255 ns = append(ns, getKeepAliveNodes(n.Pos(), n.Lhs[i])...)
256 }
257 if hasBlank {
258
259 n.Def = true
260 stmt = typecheck.AssignExpr(n)
261 n = stmt.(*ir.AssignListStmt)
262 }
263 return keepAliveAt(ns, n)
264 case *ir.AssignOpStmt:
265 return keepAliveAt(getKeepAliveNodes(n.Pos(), n.X), n)
266 case *ir.CallExpr:
267
268 if n.Fun != nil && n.Fun.Type() != nil && n.Fun.Type().NumResults() != 0 {
269 return preserveCallResults(curFn, n)
270 }
271
272 return preserveCallArgs(curFn, n)
273 }
274 return stmt
275 }
276
277 func preserveStmts(curFn *ir.Func, list ir.Nodes) {
278 for i := range list {
279 list[i] = preserveStmt(curFn, list[i])
280 }
281 }
282
283
284
285 func isTestingBLoop(t ir.Node) bool {
286 if t.Op() != ir.OFOR {
287 return false
288 }
289 nFor, ok := t.(*ir.ForStmt)
290 if !ok || nFor.Cond == nil || nFor.Cond.Op() != ir.OCALLFUNC {
291 return false
292 }
293 n, ok := nFor.Cond.(*ir.CallExpr)
294 if !ok || n.Fun == nil || n.Fun.Op() != ir.OMETHEXPR {
295 return false
296 }
297 name := ir.MethodExprName(n.Fun)
298 if name == nil {
299 return false
300 }
301 if fSym := name.Sym(); fSym != nil && name.Class == ir.PFUNC && fSym.Pkg != nil &&
302 fSym.Name == "(*B).Loop" && fSym.Pkg.Path == "testing" {
303
304 return true
305 }
306 return false
307 }
308
309 type editor struct {
310 inBloop bool
311 curFn *ir.Func
312 }
313
314 func (e editor) edit(n ir.Node) ir.Node {
315 e.inBloop = isTestingBLoop(n) || e.inBloop
316
317 ir.EditChildren(n, e.edit)
318 if e.inBloop {
319 switch n := n.(type) {
320 case *ir.ForStmt:
321 preserveStmts(e.curFn, n.Body)
322 case *ir.IfStmt:
323 preserveStmts(e.curFn, n.Body)
324 preserveStmts(e.curFn, n.Else)
325 case *ir.BlockStmt:
326 preserveStmts(e.curFn, n.List)
327 case *ir.CaseClause:
328 preserveStmts(e.curFn, n.List)
329 preserveStmts(e.curFn, n.Body)
330 case *ir.CommClause:
331 preserveStmts(e.curFn, n.Body)
332 case *ir.RangeStmt:
333 preserveStmts(e.curFn, n.Body)
334 }
335 }
336 return n
337 }
338
339
340
341
342
343
344
345
346
347 func Walk(pkg *ir.Package) {
348 hasTesting := false
349 for _, i := range pkg.Imports {
350 if i.Path == "testing" {
351 hasTesting = true
352 break
353 }
354 }
355 if !hasTesting {
356 return
357 }
358 for _, fn := range pkg.Funcs {
359 e := editor{false, fn}
360 ir.EditChildren(fn, e.edit)
361 if ir.MatchAstDump(fn, "bloop") {
362 ir.AstDump(fn, "bloop, "+ir.FuncName(fn))
363 }
364 }
365
366 }
367
View as plain text