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