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 name := getAddressableNameFromNode(n.X)
144 if name != nil {
145 debugName(name, n.Pos())
146 ret = keepAliveAt([]ir.Node{name}, n)
147 } else if deref := n.X.(*ir.StarExpr); deref != nil {
148 ret = keepAliveAt([]ir.Node{deref}, n)
149 if base.Flag.LowerM > 1 {
150 base.WarnfAt(n.Pos(), "dereference will be kept alive")
151 }
152 } else if base.Flag.LowerM > 1 {
153 base.WarnfAt(n.Pos(), "expr is unknown to bloop pass")
154 }
155 case *ir.AssignListStmt:
156 ns := []ir.Node{}
157 for _, lhs := range n.Lhs {
158 name := getAddressableNameFromNode(lhs)
159 if name != nil {
160 debugName(name, n.Pos())
161 ns = append(ns, name)
162 } else if deref := lhs.(*ir.StarExpr); deref != nil {
163 ns = append(ns, deref)
164 if base.Flag.LowerM > 1 {
165 base.WarnfAt(n.Pos(), "dereference will be kept alive")
166 }
167 } else if base.Flag.LowerM > 1 {
168 base.WarnfAt(n.Pos(), "expr is unknown to bloop pass")
169 }
170 }
171 ret = keepAliveAt(ns, n)
172 case *ir.AssignOpStmt:
173 name := getAddressableNameFromNode(n.X)
174 if name != nil {
175 debugName(name, n.Pos())
176 ret = keepAliveAt([]ir.Node{name}, n)
177 } else if deref := n.X.(*ir.StarExpr); deref != nil {
178 ret = keepAliveAt([]ir.Node{deref}, n)
179 if base.Flag.LowerM > 1 {
180 base.WarnfAt(n.Pos(), "dereference will be kept alive")
181 }
182 } else if base.Flag.LowerM > 1 {
183 base.WarnfAt(n.Pos(), "expr is unknown to bloop pass")
184 }
185 case *ir.CallExpr:
186 curNode := stmt
187 if n.Fun != nil && n.Fun.Type() != nil && n.Fun.Type().NumResults() != 0 {
188 ns := []ir.Node{}
189
190
191
192
193 results := n.Fun.Type().Results()
194 lhs := make([]ir.Node, len(results))
195 for i, res := range results {
196 tmp := typecheck.TempAt(n.Pos(), curFn, res.Type)
197 lhs[i] = tmp
198 ns = append(ns, tmp)
199 }
200
201
202 assign := typecheck.AssignExpr(
203 ir.NewAssignListStmt(n.Pos(), ir.OAS2, lhs,
204 []ir.Node{n})).(*ir.AssignListStmt)
205 assign.Def = true
206 for _, tmp := range lhs {
207
208 assign.PtrInit().Append(typecheck.Stmt(ir.NewDecl(assign.Pos(), ir.ODCL, tmp.(*ir.Name))))
209 }
210 curNode = assign
211 plural := ""
212 if len(results) > 1 {
213 plural = "s"
214 }
215 if base.Flag.LowerM > 1 {
216 base.WarnfAt(n.Pos(), "function result%s will be kept alive", plural)
217 }
218 ret = keepAliveAt(ns, curNode)
219 } else {
220
221 argTmps := []ir.Node{}
222 names := []ir.Node{}
223 for i, a := range n.Args {
224 if name := getAddressableNameFromNode(a); name != nil {
225
226 debugName(name, n.Pos())
227 names = append(names, name)
228 } else if a.Op() == ir.OSLICELIT {
229
230 s := a.(*ir.CompLitExpr)
231 ns := []ir.Node{}
232 for i, elem := range s.List {
233 if name := getAddressableNameFromNode(elem); name != nil {
234 debugName(name, n.Pos())
235 ns = append(ns, name)
236 } else {
237
238 tmp := typecheck.TempAt(elem.Pos(), curFn, elem.Type())
239 assign := ir.NewAssignStmt(elem.Pos(), tmp, elem)
240 assign.Def = true
241
242 assign.PtrInit().Append(typecheck.Stmt(ir.NewDecl(assign.Pos(), ir.ODCL, tmp)))
243 argTmps = append(argTmps, typecheck.AssignExpr(assign))
244 names = append(names, tmp)
245 s.List[i] = tmp
246 if base.Flag.LowerM > 1 {
247 base.WarnfAt(n.Pos(), "function arg will be kept alive")
248 }
249 }
250 }
251 names = append(names, ns...)
252 } else {
253
254
255 tmp := typecheck.TempAt(n.Pos(), curFn, a.Type())
256 assign := ir.NewAssignStmt(n.Pos(), tmp, a)
257 assign.Def = true
258
259 assign.PtrInit().Append(typecheck.Stmt(ir.NewDecl(assign.Pos(), ir.ODCL, tmp)))
260 argTmps = append(argTmps, typecheck.AssignExpr(assign))
261 names = append(names, tmp)
262 n.Args[i] = tmp
263 if base.Flag.LowerM > 1 {
264 base.WarnfAt(n.Pos(), "function arg will be kept alive")
265 }
266 }
267 }
268 if len(argTmps) > 0 {
269 argTmps = append(argTmps, n)
270 curNode = ir.NewBlockStmt(n.Pos(), argTmps)
271 }
272 ret = keepAliveAt(names, curNode)
273 }
274 }
275 return
276 }
277
278 func preserveStmts(curFn *ir.Func, list ir.Nodes) {
279 for i := range list {
280 list[i] = preserveStmt(curFn, list[i])
281 }
282 }
283
284
285
286 func isTestingBLoop(t ir.Node) bool {
287 if t.Op() != ir.OFOR {
288 return false
289 }
290 nFor, ok := t.(*ir.ForStmt)
291 if !ok || nFor.Cond == nil || nFor.Cond.Op() != ir.OCALLFUNC {
292 return false
293 }
294 n, ok := nFor.Cond.(*ir.CallExpr)
295 if !ok || n.Fun == nil || n.Fun.Op() != ir.OMETHEXPR {
296 return false
297 }
298 name := ir.MethodExprName(n.Fun)
299 if name == nil {
300 return false
301 }
302 if fSym := name.Sym(); fSym != nil && name.Class == ir.PFUNC && fSym.Pkg != nil &&
303 fSym.Name == "(*B).Loop" && fSym.Pkg.Path == "testing" {
304
305 return true
306 }
307 return false
308 }
309
310 type editor struct {
311 inBloop bool
312 curFn *ir.Func
313 }
314
315 func (e editor) edit(n ir.Node) ir.Node {
316 e.inBloop = isTestingBLoop(n) || e.inBloop
317
318 ir.EditChildren(n, e.edit)
319 if e.inBloop {
320 switch n := n.(type) {
321 case *ir.ForStmt:
322 preserveStmts(e.curFn, n.Body)
323 case *ir.IfStmt:
324 preserveStmts(e.curFn, n.Body)
325 preserveStmts(e.curFn, n.Else)
326 case *ir.BlockStmt:
327 preserveStmts(e.curFn, n.List)
328 case *ir.CaseClause:
329 preserveStmts(e.curFn, n.List)
330 preserveStmts(e.curFn, n.Body)
331 case *ir.CommClause:
332 preserveStmts(e.curFn, n.Body)
333 case *ir.RangeStmt:
334 preserveStmts(e.curFn, n.Body)
335 }
336 }
337 return n
338 }
339
340
341
342
343
344
345
346
347
348 func BloopWalk(pkg *ir.Package) {
349 hasTesting := false
350 for _, i := range pkg.Imports {
351 if i.Path == "testing" {
352 hasTesting = true
353 break
354 }
355 }
356 if !hasTesting {
357 return
358 }
359 for _, fn := range pkg.Funcs {
360 e := editor{false, fn}
361 ir.EditChildren(fn, e.edit)
362 }
363 }
364
View as plain text