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