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 := n.(*ir.StarExpr); 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 return keepAliveAt(getKeepAliveNodes(n.Pos(), n.X), n)
220 case *ir.AssignListStmt:
221 var ns ir.Nodes
222 for _, lhs := range n.Lhs {
223 ns = append(ns, getKeepAliveNodes(n.Pos(), lhs)...)
224 }
225 return keepAliveAt(ns, n)
226 case *ir.AssignOpStmt:
227 return keepAliveAt(getKeepAliveNodes(n.Pos(), n.X), n)
228 case *ir.CallExpr:
229
230 if n.Fun != nil && n.Fun.Type() != nil && n.Fun.Type().NumResults() != 0 {
231 return preserveCallResults(curFn, n)
232 }
233
234 return preserveCallArgs(curFn, n)
235 }
236 return stmt
237 }
238
239 func preserveStmts(curFn *ir.Func, list ir.Nodes) {
240 for i := range list {
241 list[i] = preserveStmt(curFn, list[i])
242 }
243 }
244
245
246
247 func isTestingBLoop(t ir.Node) bool {
248 if t.Op() != ir.OFOR {
249 return false
250 }
251 nFor, ok := t.(*ir.ForStmt)
252 if !ok || nFor.Cond == nil || nFor.Cond.Op() != ir.OCALLFUNC {
253 return false
254 }
255 n, ok := nFor.Cond.(*ir.CallExpr)
256 if !ok || n.Fun == nil || n.Fun.Op() != ir.OMETHEXPR {
257 return false
258 }
259 name := ir.MethodExprName(n.Fun)
260 if name == nil {
261 return false
262 }
263 if fSym := name.Sym(); fSym != nil && name.Class == ir.PFUNC && fSym.Pkg != nil &&
264 fSym.Name == "(*B).Loop" && fSym.Pkg.Path == "testing" {
265
266 return true
267 }
268 return false
269 }
270
271 type editor struct {
272 inBloop bool
273 curFn *ir.Func
274 }
275
276 func (e editor) edit(n ir.Node) ir.Node {
277 e.inBloop = isTestingBLoop(n) || e.inBloop
278
279 ir.EditChildren(n, e.edit)
280 if e.inBloop {
281 switch n := n.(type) {
282 case *ir.ForStmt:
283 preserveStmts(e.curFn, n.Body)
284 case *ir.IfStmt:
285 preserveStmts(e.curFn, n.Body)
286 preserveStmts(e.curFn, n.Else)
287 case *ir.BlockStmt:
288 preserveStmts(e.curFn, n.List)
289 case *ir.CaseClause:
290 preserveStmts(e.curFn, n.List)
291 preserveStmts(e.curFn, n.Body)
292 case *ir.CommClause:
293 preserveStmts(e.curFn, n.Body)
294 case *ir.RangeStmt:
295 preserveStmts(e.curFn, n.Body)
296 }
297 }
298 return n
299 }
300
301
302
303
304
305
306
307
308
309 func Walk(pkg *ir.Package) {
310 hasTesting := false
311 for _, i := range pkg.Imports {
312 if i.Path == "testing" {
313 hasTesting = true
314 break
315 }
316 }
317 if !hasTesting {
318 return
319 }
320 for _, fn := range pkg.Funcs {
321 e := editor{false, fn}
322 ir.EditChildren(fn, e.edit)
323 if ir.MatchAstDump(fn, "bloop") {
324 ir.AstDump(fn, "bloop, "+ir.FuncName(fn))
325 }
326 }
327
328 }
329
View as plain text