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
78
79
80 func keepAliveAt(ns []ir.Node, curNode ir.Node) ir.Node {
81 if len(ns) == 0 {
82 return curNode
83 }
84
85 pos := curNode.Pos()
86 calls := []ir.Node{curNode}
87 for _, n := range ns {
88 if n == nil {
89 continue
90 }
91 if n.Sym() == nil {
92 continue
93 }
94 if n.Sym().IsBlank() {
95 continue
96 }
97 arg := ir.NewConvExpr(pos, ir.OCONV, types.Types[types.TUNSAFEPTR], typecheck.NodAddr(n))
98 if !n.Type().IsInterface() {
99 srcRType0 := reflectdata.TypePtrAt(pos, n.Type())
100 arg.TypeWord = srcRType0
101 arg.SrcRType = srcRType0
102 }
103 callExpr := typecheck.Call(pos,
104 typecheck.LookupRuntime("KeepAlive"),
105 []ir.Node{arg}, false).(*ir.CallExpr)
106 callExpr.IsCompilerVarLive = true
107 callExpr.NoInline = true
108 calls = append(calls, callExpr)
109 }
110
111 return ir.NewBlockStmt(pos, calls)
112 }
113
114 func debugName(name *ir.Name, pos src.XPos) {
115 if base.Flag.LowerM > 1 {
116 if name.Linksym() != nil {
117 base.WarnfAt(pos, "%s will be kept alive", name.Linksym().Name)
118 } else {
119 base.WarnfAt(pos, "expr will be kept alive")
120 }
121 }
122 }
123
124
125
126
127 func preserveStmt(curFn *ir.Func, stmt ir.Node) (ret ir.Node) {
128 ret = stmt
129 switch n := stmt.(type) {
130 case *ir.AssignStmt:
131
132 name := getNameFromNode(n.X)
133 if name != nil {
134 debugName(name, n.Pos())
135 ret = keepAliveAt([]ir.Node{name}, n)
136 } else if deref := n.X.(*ir.StarExpr); deref != nil {
137 ret = keepAliveAt([]ir.Node{deref}, n)
138 if base.Flag.LowerM > 1 {
139 base.WarnfAt(n.Pos(), "dereference will be kept alive")
140 }
141 } else if base.Flag.LowerM > 1 {
142 base.WarnfAt(n.Pos(), "expr is unknown to bloop pass")
143 }
144 case *ir.AssignListStmt:
145 ns := []ir.Node{}
146 for _, lhs := range n.Lhs {
147 name := getNameFromNode(lhs)
148 if name != nil {
149 debugName(name, n.Pos())
150 ns = append(ns, name)
151 } else if deref := lhs.(*ir.StarExpr); deref != nil {
152 ns = append(ns, deref)
153 if base.Flag.LowerM > 1 {
154 base.WarnfAt(n.Pos(), "dereference will be kept alive")
155 }
156 } else if base.Flag.LowerM > 1 {
157 base.WarnfAt(n.Pos(), "expr is unknown to bloop pass")
158 }
159 }
160 ret = keepAliveAt(ns, n)
161 case *ir.AssignOpStmt:
162 name := getNameFromNode(n.X)
163 if name != nil {
164 debugName(name, n.Pos())
165 ret = keepAliveAt([]ir.Node{name}, n)
166 } else if deref := n.X.(*ir.StarExpr); deref != nil {
167 ret = keepAliveAt([]ir.Node{deref}, n)
168 if base.Flag.LowerM > 1 {
169 base.WarnfAt(n.Pos(), "dereference will be kept alive")
170 }
171 } else if base.Flag.LowerM > 1 {
172 base.WarnfAt(n.Pos(), "expr is unknown to bloop pass")
173 }
174 case *ir.CallExpr:
175 curNode := stmt
176 if n.Fun != nil && n.Fun.Type() != nil && n.Fun.Type().NumResults() != 0 {
177 ns := []ir.Node{}
178
179
180
181
182 results := n.Fun.Type().Results()
183 lhs := make([]ir.Node, len(results))
184 for i, res := range results {
185 tmp := typecheck.TempAt(n.Pos(), curFn, res.Type)
186 lhs[i] = tmp
187 ns = append(ns, tmp)
188 }
189
190
191 assign := typecheck.AssignExpr(
192 ir.NewAssignListStmt(n.Pos(), ir.OAS2, lhs,
193 []ir.Node{n})).(*ir.AssignListStmt)
194 assign.Def = true
195 curNode = assign
196 plural := ""
197 if len(results) > 1 {
198 plural = "s"
199 }
200 if base.Flag.LowerM > 1 {
201 base.WarnfAt(n.Pos(), "function result%s will be kept alive", plural)
202 }
203 ret = keepAliveAt(ns, curNode)
204 } else {
205
206 argTmps := []ir.Node{}
207 names := []ir.Node{}
208 for i, a := range n.Args {
209 if name := getNameFromNode(a); name != nil {
210
211 debugName(name, n.Pos())
212 names = append(names, name)
213 } else if a.Op() == ir.OSLICELIT {
214
215 s := a.(*ir.CompLitExpr)
216 ns := []ir.Node{}
217 for i, elem := range s.List {
218 if name := getNameFromNode(elem); name != nil {
219 debugName(name, n.Pos())
220 ns = append(ns, name)
221 } else {
222
223 tmp := typecheck.TempAt(elem.Pos(), curFn, elem.Type())
224 argTmps = append(argTmps, typecheck.AssignExpr(ir.NewAssignStmt(elem.Pos(), tmp, elem)))
225 names = append(names, tmp)
226 s.List[i] = tmp
227 if base.Flag.LowerM > 1 {
228 base.WarnfAt(n.Pos(), "function arg will be kept alive")
229 }
230 }
231 }
232 names = append(names, ns...)
233 } else {
234
235
236 tmp := typecheck.TempAt(n.Pos(), curFn, a.Type())
237 argTmps = append(argTmps, typecheck.AssignExpr(ir.NewAssignStmt(n.Pos(), tmp, a)))
238 names = append(names, tmp)
239 n.Args[i] = tmp
240 if base.Flag.LowerM > 1 {
241 base.WarnfAt(n.Pos(), "function arg will be kept alive")
242 }
243 }
244 }
245 if len(argTmps) > 0 {
246 argTmps = append(argTmps, n)
247 curNode = ir.NewBlockStmt(n.Pos(), argTmps)
248 }
249 ret = keepAliveAt(names, curNode)
250 }
251 }
252 return
253 }
254
255 func preserveStmts(curFn *ir.Func, list ir.Nodes) {
256 for i := range list {
257 list[i] = preserveStmt(curFn, list[i])
258 }
259 }
260
261
262
263 func isTestingBLoop(t ir.Node) bool {
264 if t.Op() != ir.OFOR {
265 return false
266 }
267 nFor, ok := t.(*ir.ForStmt)
268 if !ok || nFor.Cond == nil || nFor.Cond.Op() != ir.OCALLFUNC {
269 return false
270 }
271 n, ok := nFor.Cond.(*ir.CallExpr)
272 if !ok || n.Fun == nil || n.Fun.Op() != ir.OMETHEXPR {
273 return false
274 }
275 name := ir.MethodExprName(n.Fun)
276 if name == nil {
277 return false
278 }
279 if fSym := name.Sym(); fSym != nil && name.Class == ir.PFUNC && fSym.Pkg != nil &&
280 fSym.Name == "(*B).Loop" && fSym.Pkg.Path == "testing" {
281
282 return true
283 }
284 return false
285 }
286
287 type editor struct {
288 inBloop bool
289 curFn *ir.Func
290 }
291
292 func (e editor) edit(n ir.Node) ir.Node {
293 e.inBloop = isTestingBLoop(n) || e.inBloop
294
295 ir.EditChildren(n, e.edit)
296 if e.inBloop {
297 switch n := n.(type) {
298 case *ir.ForStmt:
299 preserveStmts(e.curFn, n.Body)
300 case *ir.IfStmt:
301 preserveStmts(e.curFn, n.Body)
302 preserveStmts(e.curFn, n.Else)
303 case *ir.BlockStmt:
304 preserveStmts(e.curFn, n.List)
305 case *ir.CaseClause:
306 preserveStmts(e.curFn, n.List)
307 preserveStmts(e.curFn, n.Body)
308 case *ir.CommClause:
309 preserveStmts(e.curFn, n.Body)
310 case *ir.RangeStmt:
311 preserveStmts(e.curFn, n.Body)
312 }
313 }
314 return n
315 }
316
317
318
319
320
321
322
323
324
325 func BloopWalk(pkg *ir.Package) {
326 hasTesting := false
327 for _, i := range pkg.Imports {
328 if i.Path == "testing" {
329 hasTesting = true
330 break
331 }
332 }
333 if !hasTesting {
334 return
335 }
336 for _, fn := range pkg.Funcs {
337 e := editor{false, fn}
338 ir.EditChildren(fn, e.edit)
339 }
340 }
341
View as plain text