blob: ac6dd5eeb6870f19d53241c95cd9abd1fb5bac05 [file] [log] [blame]
Russ Cox8c195bd2015-02-13 14:40:36 -05001// Derived from Inferno utils/6c/gc.h
2// http://code.google.com/p/inferno-os/source/browse/utils/6c/gc.h
3//
4// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
5// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
6// Portions Copyright © 1997-1999 Vita Nuova Limited
7// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
8// Portions Copyright © 2004,2006 Bruce Ellis
9// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
10// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
11// Portions Copyright © 2009 The Go Authors. All rights reserved.
12//
13// Permission is hereby granted, free of charge, to any person obtaining a copy
14// of this software and associated documentation files (the "Software"), to deal
15// in the Software without restriction, including without limitation the rights
16// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17// copies of the Software, and to permit persons to whom the Software is
18// furnished to do so, subject to the following conditions:
19//
20// The above copyright notice and this permission notice shall be included in
21// all copies or substantial portions of the Software.
22//
23// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
29// THE SOFTWARE.
30
Russ Coxd47fe802015-03-09 15:34:06 -040031// "Portable" optimizations.
32
33package gc
34
35import (
36 "cmd/internal/obj"
37 "fmt"
38 "sort"
39 "strings"
Russ Cox8c195bd2015-02-13 14:40:36 -050040)
41
Russ Cox8c195bd2015-02-13 14:40:36 -050042type OptStats struct {
43 Ncvtreg int32
44 Nspill int32
45 Nreload int32
46 Ndelmov int32
47 Nvar int32
48 Naddr int32
49}
50
51var Ostats OptStats
52
Russ Cox8c195bd2015-02-13 14:40:36 -050053var noreturn_symlist [10]*Sym
54
Keith Randallcd5b1442015-03-11 12:58:47 -070055// p is a call instruction. Does the call fail to return?
Russ Coxdc7b54b2015-02-17 22:13:49 -050056func Noreturn(p *obj.Prog) bool {
Russ Cox8c195bd2015-02-13 14:40:36 -050057 if noreturn_symlist[0] == nil {
58 noreturn_symlist[0] = Pkglookup("panicindex", Runtimepkg)
59 noreturn_symlist[1] = Pkglookup("panicslice", Runtimepkg)
60 noreturn_symlist[2] = Pkglookup("throwinit", Runtimepkg)
61 noreturn_symlist[3] = Pkglookup("gopanic", Runtimepkg)
62 noreturn_symlist[4] = Pkglookup("panicwrap", Runtimepkg)
63 noreturn_symlist[5] = Pkglookup("throwreturn", Runtimepkg)
64 noreturn_symlist[6] = Pkglookup("selectgo", Runtimepkg)
65 noreturn_symlist[7] = Pkglookup("block", Runtimepkg)
66 }
67
68 if p.To.Node == nil {
Russ Coxdc7b54b2015-02-17 22:13:49 -050069 return false
Russ Cox8c195bd2015-02-13 14:40:36 -050070 }
Russ Cox382b44e2015-02-23 16:07:24 -050071 s := ((p.To.Node).(*Node)).Sym
Russ Cox8c195bd2015-02-13 14:40:36 -050072 if s == nil {
Russ Coxdc7b54b2015-02-17 22:13:49 -050073 return false
Russ Cox8c195bd2015-02-13 14:40:36 -050074 }
Russ Cox382b44e2015-02-23 16:07:24 -050075 for i := 0; noreturn_symlist[i] != nil; i++ {
Russ Cox8c195bd2015-02-13 14:40:36 -050076 if s == noreturn_symlist[i] {
Russ Coxdc7b54b2015-02-17 22:13:49 -050077 return true
Russ Cox8c195bd2015-02-13 14:40:36 -050078 }
79 }
Russ Coxdc7b54b2015-02-17 22:13:49 -050080 return false
Russ Cox8c195bd2015-02-13 14:40:36 -050081}
82
83// JMP chasing and removal.
84//
85// The code generator depends on being able to write out jump
86// instructions that it can jump to now but fill in later.
87// the linker will resolve them nicely, but they make the code
88// longer and more difficult to follow during debugging.
89// Remove them.
90
91/* what instruction does a JMP to p eventually land on? */
92func chasejmp(p *obj.Prog, jmploop *int) *obj.Prog {
Russ Cox382b44e2015-02-23 16:07:24 -050093 n := 0
Russ Cox8c195bd2015-02-13 14:40:36 -050094 for p != nil && p.As == obj.AJMP && p.To.Type == obj.TYPE_BRANCH {
95 n++
96 if n > 10 {
97 *jmploop = 1
98 break
99 }
100
Russ Cox532ccae2015-03-16 15:54:44 -0400101 p = p.To.Val.(*obj.Prog)
Russ Cox8c195bd2015-02-13 14:40:36 -0500102 }
103
104 return p
105}
106
107/*
108 * reuse reg pointer for mark/sweep state.
109 * leave reg==nil at end because alive==nil.
110 */
111var alive interface{} = nil
112var dead interface{} = 1
113
114/* mark all code reachable from firstp as alive */
115func mark(firstp *obj.Prog) {
Russ Cox382b44e2015-02-23 16:07:24 -0500116 for p := firstp; p != nil; p = p.Link {
Russ Cox8c195bd2015-02-13 14:40:36 -0500117 if p.Opt != dead {
118 break
119 }
120 p.Opt = alive
Russ Cox532ccae2015-03-16 15:54:44 -0400121 if p.As != obj.ACALL && p.To.Type == obj.TYPE_BRANCH && p.To.Val.(*obj.Prog) != nil {
122 mark(p.To.Val.(*obj.Prog))
Russ Cox8c195bd2015-02-13 14:40:36 -0500123 }
124 if p.As == obj.AJMP || p.As == obj.ARET || p.As == obj.AUNDEF {
125 break
126 }
127 }
128}
129
130func fixjmp(firstp *obj.Prog) {
Russ Cox8c195bd2015-02-13 14:40:36 -0500131 if Debug['R'] != 0 && Debug['v'] != 0 {
132 fmt.Printf("\nfixjmp\n")
133 }
134
135 // pass 1: resolve jump to jump, mark all code as dead.
Russ Cox382b44e2015-02-23 16:07:24 -0500136 jmploop := 0
Russ Cox8c195bd2015-02-13 14:40:36 -0500137
Russ Cox382b44e2015-02-23 16:07:24 -0500138 for p := firstp; p != nil; p = p.Link {
Russ Cox8c195bd2015-02-13 14:40:36 -0500139 if Debug['R'] != 0 && Debug['v'] != 0 {
140 fmt.Printf("%v\n", p)
141 }
Russ Cox532ccae2015-03-16 15:54:44 -0400142 if p.As != obj.ACALL && p.To.Type == obj.TYPE_BRANCH && p.To.Val.(*obj.Prog) != nil && p.To.Val.(*obj.Prog).As == obj.AJMP {
143 p.To.Val = chasejmp(p.To.Val.(*obj.Prog), &jmploop)
Russ Cox8c195bd2015-02-13 14:40:36 -0500144 if Debug['R'] != 0 && Debug['v'] != 0 {
145 fmt.Printf("->%v\n", p)
146 }
147 }
148
149 p.Opt = dead
150 }
151
152 if Debug['R'] != 0 && Debug['v'] != 0 {
153 fmt.Printf("\n")
154 }
155
156 // pass 2: mark all reachable code alive
157 mark(firstp)
158
159 // pass 3: delete dead code (mostly JMPs).
Russ Cox175929b2015-03-02 14:22:05 -0500160 var last *obj.Prog
Russ Cox8c195bd2015-02-13 14:40:36 -0500161
Russ Cox382b44e2015-02-23 16:07:24 -0500162 for p := firstp; p != nil; p = p.Link {
Russ Cox8c195bd2015-02-13 14:40:36 -0500163 if p.Opt == dead {
164 if p.Link == nil && p.As == obj.ARET && last != nil && last.As != obj.ARET {
165 // This is the final ARET, and the code so far doesn't have one.
166 // Let it stay. The register allocator assumes that all live code in
167 // the function can be traversed by starting at all the RET instructions
168 // and following predecessor links. If we remove the final RET,
169 // this assumption will not hold in the case of an infinite loop
170 // at the end of a function.
171 // Keep the RET but mark it dead for the liveness analysis.
172 p.Mode = 1
173 } else {
174 if Debug['R'] != 0 && Debug['v'] != 0 {
175 fmt.Printf("del %v\n", p)
176 }
177 continue
178 }
179 }
180
181 if last != nil {
182 last.Link = p
183 }
184 last = p
185 }
186
187 last.Link = nil
188
189 // pass 4: elide JMP to next instruction.
190 // only safe if there are no jumps to JMPs anymore.
Russ Coxdc7b54b2015-02-17 22:13:49 -0500191 if jmploop == 0 {
Russ Cox175929b2015-03-02 14:22:05 -0500192 var last *obj.Prog
Russ Cox382b44e2015-02-23 16:07:24 -0500193 for p := firstp; p != nil; p = p.Link {
Russ Cox532ccae2015-03-16 15:54:44 -0400194 if p.As == obj.AJMP && p.To.Type == obj.TYPE_BRANCH && p.To.Val == p.Link {
Russ Cox8c195bd2015-02-13 14:40:36 -0500195 if Debug['R'] != 0 && Debug['v'] != 0 {
196 fmt.Printf("del %v\n", p)
197 }
198 continue
199 }
200
201 if last != nil {
202 last.Link = p
203 }
204 last = p
205 }
206
207 last.Link = nil
208 }
209
210 if Debug['R'] != 0 && Debug['v'] != 0 {
211 fmt.Printf("\n")
Russ Cox382b44e2015-02-23 16:07:24 -0500212 for p := firstp; p != nil; p = p.Link {
Russ Cox8c195bd2015-02-13 14:40:36 -0500213 fmt.Printf("%v\n", p)
214 }
215 fmt.Printf("\n")
216 }
217}
218
219// Control flow analysis. The Flow structures hold predecessor and successor
220// information as well as basic loop analysis.
221//
222// graph = flowstart(firstp, 0);
223// ... use flow graph ...
224// flowend(graph); // free graph
225//
226// Typical uses of the flow graph are to iterate over all the flow-relevant instructions:
227//
228// for(f = graph->start; f != nil; f = f->link)
229//
230// or, given an instruction f, to iterate over all the predecessors, which is
231// f->p1 and this list:
232//
233// for(f2 = f->p2; f2 != nil; f2 = f2->p2link)
234//
235// The size argument to flowstart specifies an amount of zeroed memory
236// to allocate in every f->data field, for use by the client.
237// If size == 0, f->data will be nil.
238
Russ Cox4bbd7ae2015-03-02 15:22:19 -0500239var flowmark int
240
Russ Coxd47fe802015-03-09 15:34:06 -0400241// MaxFlowProg is the maximum size program (counted in instructions)
242// for which the flow code will build a graph. Functions larger than this limit
243// will not have flow graphs and consequently will not be optimized.
244const MaxFlowProg = 50000
245
Russ Cox8c195bd2015-02-13 14:40:36 -0500246func Flowstart(firstp *obj.Prog, newData func() interface{}) *Graph {
Russ Cox8c195bd2015-02-13 14:40:36 -0500247 // Count and mark instructions to annotate.
Russ Cox382b44e2015-02-23 16:07:24 -0500248 nf := 0
Russ Cox8c195bd2015-02-13 14:40:36 -0500249
Russ Cox382b44e2015-02-23 16:07:24 -0500250 for p := firstp; p != nil; p = p.Link {
Russ Cox8c195bd2015-02-13 14:40:36 -0500251 p.Opt = nil // should be already, but just in case
Russ Coxfd38dbc2015-03-16 16:46:25 -0400252 Thearch.Proginfo(p)
253 if p.Info.Flags&Skip != 0 {
Russ Cox8c195bd2015-02-13 14:40:36 -0500254 continue
255 }
Russ Cox4bbd7ae2015-03-02 15:22:19 -0500256 p.Opt = &flowmark
Russ Cox8c195bd2015-02-13 14:40:36 -0500257 nf++
258 }
259
260 if nf == 0 {
261 return nil
262 }
263
Russ Coxd47fe802015-03-09 15:34:06 -0400264 if nf >= MaxFlowProg {
265 if Debug['v'] != 0 {
266 Warn("%v is too big (%d instructions)", Sconv(Curfn.Nname.Sym, 0), nf)
267 }
Russ Cox8c195bd2015-02-13 14:40:36 -0500268 return nil
269 }
270
271 // Allocate annotations and assign to instructions.
Russ Cox382b44e2015-02-23 16:07:24 -0500272 graph := new(Graph)
Russ Cox8c195bd2015-02-13 14:40:36 -0500273 ff := make([]Flow, nf)
Russ Cox382b44e2015-02-23 16:07:24 -0500274 start := &ff[0]
275 id := 0
276 var last *Flow
277 for p := firstp; p != nil; p = p.Link {
Russ Cox8c195bd2015-02-13 14:40:36 -0500278 if p.Opt == nil {
279 continue
280 }
281 f := &ff[0]
282 ff = ff[1:]
283 p.Opt = f
284 f.Prog = p
285 if last != nil {
286 last.Link = f
287 }
288 last = f
289 if newData != nil {
290 f.Data = newData()
291 }
292 f.Id = int32(id)
293 id++
294 }
295
296 // Fill in pred/succ information.
Russ Cox382b44e2015-02-23 16:07:24 -0500297 var f1 *Flow
298 var p *obj.Prog
299 for f := start; f != nil; f = f.Link {
Russ Cox8c195bd2015-02-13 14:40:36 -0500300 p = f.Prog
Russ Coxfd38dbc2015-03-16 16:46:25 -0400301 if p.Info.Flags&Break == 0 {
Russ Cox8c195bd2015-02-13 14:40:36 -0500302 f1 = f.Link
303 f.S1 = f1
304 f1.P1 = f
305 }
306
307 if p.To.Type == obj.TYPE_BRANCH {
Russ Cox532ccae2015-03-16 15:54:44 -0400308 if p.To.Val == nil {
Russ Cox8c195bd2015-02-13 14:40:36 -0500309 Fatal("pnil %v", p)
310 }
Russ Cox532ccae2015-03-16 15:54:44 -0400311 f1 = p.To.Val.(*obj.Prog).Opt.(*Flow)
Russ Cox8c195bd2015-02-13 14:40:36 -0500312 if f1 == nil {
Russ Cox532ccae2015-03-16 15:54:44 -0400313 Fatal("fnil %v / %v", p, p.To.Val.(*obj.Prog))
Russ Cox8c195bd2015-02-13 14:40:36 -0500314 }
315 if f1 == f {
316 //fatal("self loop %P", p);
317 continue
318 }
319
320 f.S2 = f1
321 f.P2link = f1.P2
322 f1.P2 = f
323 }
324 }
325
326 graph.Start = start
327 graph.Num = nf
328 return graph
329}
330
331func Flowend(graph *Graph) {
Russ Cox382b44e2015-02-23 16:07:24 -0500332 for f := graph.Start; f != nil; f = f.Link {
Russ Coxfd38dbc2015-03-16 16:46:25 -0400333 f.Prog.Info.Flags = 0 // drop cached proginfo
Russ Cox8c195bd2015-02-13 14:40:36 -0500334 f.Prog.Opt = nil
335 }
336}
337
338/*
339 * find looping structure
340 *
341 * 1) find reverse postordering
342 * 2) find approximate dominators,
343 * the actual dominators if the flow graph is reducible
344 * otherwise, dominators plus some other non-dominators.
345 * See Matthew S. Hecht and Jeffrey D. Ullman,
346 * "Analysis of a Simple Algorithm for Global Data Flow Problems",
347 * Conf. Record of ACM Symp. on Principles of Prog. Langs, Boston, Massachusetts,
348 * Oct. 1-3, 1973, pp. 207-217.
349 * 3) find all nodes with a predecessor dominated by the current node.
350 * such a node is a loop head.
351 * recursively, all preds with a greater rpo number are in the loop
352 */
353func postorder(r *Flow, rpo2r []*Flow, n int32) int32 {
Russ Cox8c195bd2015-02-13 14:40:36 -0500354 r.Rpo = 1
Russ Cox382b44e2015-02-23 16:07:24 -0500355 r1 := r.S1
Russ Coxdc7b54b2015-02-17 22:13:49 -0500356 if r1 != nil && r1.Rpo == 0 {
Russ Cox8c195bd2015-02-13 14:40:36 -0500357 n = postorder(r1, rpo2r, n)
358 }
359 r1 = r.S2
Russ Coxdc7b54b2015-02-17 22:13:49 -0500360 if r1 != nil && r1.Rpo == 0 {
Russ Cox8c195bd2015-02-13 14:40:36 -0500361 n = postorder(r1, rpo2r, n)
362 }
363 rpo2r[n] = r
364 n++
365 return n
366}
367
368func rpolca(idom []int32, rpo1 int32, rpo2 int32) int32 {
Russ Cox8c195bd2015-02-13 14:40:36 -0500369 if rpo1 == -1 {
370 return rpo2
371 }
Russ Cox382b44e2015-02-23 16:07:24 -0500372 var t int32
Russ Cox8c195bd2015-02-13 14:40:36 -0500373 for rpo1 != rpo2 {
374 if rpo1 > rpo2 {
375 t = rpo2
376 rpo2 = rpo1
377 rpo1 = t
378 }
379
380 for rpo1 < rpo2 {
381 t = idom[rpo2]
382 if t >= rpo2 {
383 Fatal("bad idom")
384 }
385 rpo2 = t
386 }
387 }
388
389 return rpo1
390}
391
Russ Coxdc7b54b2015-02-17 22:13:49 -0500392func doms(idom []int32, r int32, s int32) bool {
Russ Cox8c195bd2015-02-13 14:40:36 -0500393 for s > r {
394 s = idom[s]
395 }
Russ Coxdc7b54b2015-02-17 22:13:49 -0500396 return s == r
Russ Cox8c195bd2015-02-13 14:40:36 -0500397}
398
Russ Coxdc7b54b2015-02-17 22:13:49 -0500399func loophead(idom []int32, r *Flow) bool {
Russ Cox382b44e2015-02-23 16:07:24 -0500400 src := r.Rpo
Russ Coxdc7b54b2015-02-17 22:13:49 -0500401 if r.P1 != nil && doms(idom, src, r.P1.Rpo) {
402 return true
Russ Cox8c195bd2015-02-13 14:40:36 -0500403 }
404 for r = r.P2; r != nil; r = r.P2link {
Russ Coxdc7b54b2015-02-17 22:13:49 -0500405 if doms(idom, src, r.Rpo) {
406 return true
Russ Cox8c195bd2015-02-13 14:40:36 -0500407 }
408 }
Russ Coxdc7b54b2015-02-17 22:13:49 -0500409 return false
Russ Cox8c195bd2015-02-13 14:40:36 -0500410}
411
412func loopmark(rpo2r **Flow, head int32, r *Flow) {
413 if r.Rpo < head || r.Active == head {
414 return
415 }
416 r.Active = head
417 r.Loop += LOOP
418 if r.P1 != nil {
419 loopmark(rpo2r, head, r.P1)
420 }
421 for r = r.P2; r != nil; r = r.P2link {
422 loopmark(rpo2r, head, r)
423 }
424}
425
426func flowrpo(g *Graph) {
Russ Cox8c195bd2015-02-13 14:40:36 -0500427 g.Rpo = make([]*Flow, g.Num)
Russ Cox382b44e2015-02-23 16:07:24 -0500428 idom := make([]int32, g.Num)
Russ Cox8c195bd2015-02-13 14:40:36 -0500429
Russ Cox382b44e2015-02-23 16:07:24 -0500430 for r1 := g.Start; r1 != nil; r1 = r1.Link {
Russ Cox8c195bd2015-02-13 14:40:36 -0500431 r1.Active = 0
432 }
433
Russ Cox382b44e2015-02-23 16:07:24 -0500434 rpo2r := g.Rpo
435 d := postorder(g.Start, rpo2r, 0)
436 nr := int32(g.Num)
Russ Cox8c195bd2015-02-13 14:40:36 -0500437 if d > nr {
438 Fatal("too many reg nodes %d %d", d, nr)
439 }
440 nr = d
Russ Cox382b44e2015-02-23 16:07:24 -0500441 var r1 *Flow
442 for i := int32(0); i < nr/2; i++ {
Russ Cox8c195bd2015-02-13 14:40:36 -0500443 r1 = rpo2r[i]
444 rpo2r[i] = rpo2r[nr-1-i]
445 rpo2r[nr-1-i] = r1
446 }
447
Russ Cox382b44e2015-02-23 16:07:24 -0500448 for i := int32(0); i < nr; i++ {
Russ Cox8c195bd2015-02-13 14:40:36 -0500449 rpo2r[i].Rpo = i
450 }
451
452 idom[0] = 0
Russ Cox382b44e2015-02-23 16:07:24 -0500453 var me int32
454 for i := int32(0); i < nr; i++ {
Russ Cox8c195bd2015-02-13 14:40:36 -0500455 r1 = rpo2r[i]
456 me = r1.Rpo
457 d = -1
458
459 // rpo2r[r->rpo] == r protects against considering dead code,
460 // which has r->rpo == 0.
461 if r1.P1 != nil && rpo2r[r1.P1.Rpo] == r1.P1 && r1.P1.Rpo < me {
462 d = r1.P1.Rpo
463 }
464 for r1 = r1.P2; r1 != nil; r1 = r1.P2link {
465 if rpo2r[r1.Rpo] == r1 && r1.Rpo < me {
466 d = rpolca(idom, d, r1.Rpo)
467 }
468 }
469 idom[i] = d
470 }
471
Russ Cox382b44e2015-02-23 16:07:24 -0500472 for i := int32(0); i < nr; i++ {
Russ Cox8c195bd2015-02-13 14:40:36 -0500473 r1 = rpo2r[i]
474 r1.Loop++
Russ Coxdc7b54b2015-02-17 22:13:49 -0500475 if r1.P2 != nil && loophead(idom, r1) {
Russ Cox8c195bd2015-02-13 14:40:36 -0500476 loopmark(&rpo2r[0], i, r1)
477 }
478 }
479
Russ Cox382b44e2015-02-23 16:07:24 -0500480 for r1 := g.Start; r1 != nil; r1 = r1.Link {
Russ Cox8c195bd2015-02-13 14:40:36 -0500481 r1.Active = 0
482 }
483}
484
485func Uniqp(r *Flow) *Flow {
Russ Cox382b44e2015-02-23 16:07:24 -0500486 r1 := r.P1
Russ Cox8c195bd2015-02-13 14:40:36 -0500487 if r1 == nil {
488 r1 = r.P2
489 if r1 == nil || r1.P2link != nil {
490 return nil
491 }
492 } else if r.P2 != nil {
493 return nil
494 }
495 return r1
496}
497
498func Uniqs(r *Flow) *Flow {
Russ Cox382b44e2015-02-23 16:07:24 -0500499 r1 := r.S1
Russ Cox8c195bd2015-02-13 14:40:36 -0500500 if r1 == nil {
501 r1 = r.S2
502 if r1 == nil {
503 return nil
504 }
505 } else if r.S2 != nil {
506 return nil
507 }
508 return r1
509}
510
511// The compilers assume they can generate temporary variables
512// as needed to preserve the right semantics or simplify code
513// generation and the back end will still generate good code.
514// This results in a large number of ephemeral temporary variables.
515// Merge temps with non-overlapping lifetimes and equal types using the
516// greedy algorithm in Poletto and Sarkar, "Linear Scan Register Allocation",
517// ACM TOPLAS 1999.
518
519type TempVar struct {
520 node *Node
Russ Coxcdb7d7d2015-03-05 13:57:36 -0500521 def *Flow // definition of temp var
522 use *Flow // use list, chained through Flow.data
523 freelink *TempVar // next free temp in Type.opt list
524 merge *TempVar // merge var with this one
525 start int64 // smallest Prog.pc in live range
526 end int64 // largest Prog.pc in live range
527 addr uint8 // address taken - no accurate end
528 removed uint8 // removed from program
Russ Cox8c195bd2015-02-13 14:40:36 -0500529}
530
531type startcmp []*TempVar
532
533func (x startcmp) Len() int {
534 return len(x)
535}
536
537func (x startcmp) Swap(i, j int) {
538 x[i], x[j] = x[j], x[i]
539}
540
541func (x startcmp) Less(i, j int) bool {
Russ Cox382b44e2015-02-23 16:07:24 -0500542 a := x[i]
543 b := x[j]
Russ Cox8c195bd2015-02-13 14:40:36 -0500544
545 if a.start < b.start {
546 return true
547 }
548 if a.start > b.start {
549 return false
550 }
551
552 // Order what's left by id or symbol name,
553 // just so that sort is forced into a specific ordering,
554 // so that the result of the sort does not depend on
555 // the sort implementation.
556 if a.def != b.def {
557 return int(a.def.Id-b.def.Id) < 0
558 }
559 if a.node != b.node {
560 return stringsCompare(a.node.Sym.Name, b.node.Sym.Name) < 0
561 }
562 return false
563}
564
565// Is n available for merging?
Russ Coxdc7b54b2015-02-17 22:13:49 -0500566func canmerge(n *Node) bool {
567 return n.Class == PAUTO && strings.HasPrefix(n.Sym.Name, "autotmp")
Russ Cox8c195bd2015-02-13 14:40:36 -0500568}
569
570func mergetemp(firstp *obj.Prog) {
Russ Cox8c195bd2015-02-13 14:40:36 -0500571 const (
Russ Coxd47fe802015-03-09 15:34:06 -0400572 debugmerge = 0
Russ Cox8c195bd2015-02-13 14:40:36 -0500573 )
574
Russ Cox382b44e2015-02-23 16:07:24 -0500575 g := Flowstart(firstp, nil)
Russ Cox8c195bd2015-02-13 14:40:36 -0500576 if g == nil {
577 return
578 }
579
580 // Build list of all mergeable variables.
Russ Cox382b44e2015-02-23 16:07:24 -0500581 nvar := 0
582 for l := Curfn.Dcl; l != nil; l = l.Next {
Russ Coxdc7b54b2015-02-17 22:13:49 -0500583 if canmerge(l.N) {
Russ Cox8c195bd2015-02-13 14:40:36 -0500584 nvar++
585 }
586 }
587
Russ Cox382b44e2015-02-23 16:07:24 -0500588 var_ := make([]TempVar, nvar)
Russ Cox8c195bd2015-02-13 14:40:36 -0500589 nvar = 0
Russ Cox382b44e2015-02-23 16:07:24 -0500590 var n *Node
591 var v *TempVar
592 for l := Curfn.Dcl; l != nil; l = l.Next {
Russ Cox8c195bd2015-02-13 14:40:36 -0500593 n = l.N
Russ Coxdc7b54b2015-02-17 22:13:49 -0500594 if canmerge(n) {
Russ Cox8c195bd2015-02-13 14:40:36 -0500595 v = &var_[nvar]
596 nvar++
597 n.Opt = v
598 v.node = n
599 }
600 }
601
602 // Build list of uses.
603 // We assume that the earliest reference to a temporary is its definition.
604 // This is not true of variables in general but our temporaries are all
605 // single-use (that's why we have so many!).
Russ Cox382b44e2015-02-23 16:07:24 -0500606 for f := g.Start; f != nil; f = f.Link {
Russ Coxfd38dbc2015-03-16 16:46:25 -0400607 p := f.Prog
Russ Cox8c195bd2015-02-13 14:40:36 -0500608 if p.From.Node != nil && ((p.From.Node).(*Node)).Opt != nil && p.To.Node != nil && ((p.To.Node).(*Node)).Opt != nil {
609 Fatal("double node %v", p)
610 }
611 v = nil
612 n, _ = p.From.Node.(*Node)
613 if n != nil {
614 v, _ = n.Opt.(*TempVar)
615 }
616 if v == nil {
617 n, _ = p.To.Node.(*Node)
618 if n != nil {
619 v, _ = n.Opt.(*TempVar)
620 }
621 }
622 if v != nil {
623 if v.def == nil {
624 v.def = f
625 }
626 f.Data = v.use
627 v.use = f
Russ Coxfd38dbc2015-03-16 16:46:25 -0400628 if n == p.From.Node && (p.Info.Flags&LeftAddr != 0) {
Russ Cox8c195bd2015-02-13 14:40:36 -0500629 v.addr = 1
630 }
631 }
632 }
633
634 if debugmerge > 1 && Debug['v'] != 0 {
635 Dumpit("before", g.Start, 0)
636 }
637
Russ Cox382b44e2015-02-23 16:07:24 -0500638 nkill := 0
Russ Cox8c195bd2015-02-13 14:40:36 -0500639
640 // Special case.
Russ Cox382b44e2015-02-23 16:07:24 -0500641 for i := 0; i < len(var_); i++ {
Russ Cox8c195bd2015-02-13 14:40:36 -0500642 v = &var_[i]
643 if v.addr != 0 {
644 continue
645 }
646
647 // Used in only one instruction, which had better be a write.
Russ Coxfd38dbc2015-03-16 16:46:25 -0400648 f := v.use
Russ Cox8c195bd2015-02-13 14:40:36 -0500649 if f != nil && f.Data.(*Flow) == nil {
Russ Coxfd38dbc2015-03-16 16:46:25 -0400650 p := f.Prog
651 if p.To.Node == v.node && (p.Info.Flags&RightWrite != 0) && p.Info.Flags&RightRead == 0 {
Russ Cox8c195bd2015-02-13 14:40:36 -0500652 p.As = obj.ANOP
Russ Coxdc7b54b2015-02-17 22:13:49 -0500653 p.To = obj.Addr{}
Russ Cox8c195bd2015-02-13 14:40:36 -0500654 v.removed = 1
655 if debugmerge > 0 && Debug['v'] != 0 {
656 fmt.Printf("drop write-only %v\n", Sconv(v.node.Sym, 0))
657 }
658 } else {
659 Fatal("temp used and not set: %v", p)
660 }
661 nkill++
662 continue
663 }
664
665 // Written in one instruction, read in the next, otherwise unused,
666 // no jumps to the next instruction. Happens mainly in 386 compiler.
667 f = v.use
668 if f != nil && f.Link == f.Data.(*Flow) && (f.Data.(*Flow)).Data.(*Flow) == nil && Uniqp(f.Link) == f {
Russ Coxfd38dbc2015-03-16 16:46:25 -0400669 p := f.Prog
670 p1 := f.Link.Prog
Russ Cox8c195bd2015-02-13 14:40:36 -0500671 const (
672 SizeAny = SizeB | SizeW | SizeL | SizeQ | SizeF | SizeD
673 )
Russ Coxfd38dbc2015-03-16 16:46:25 -0400674 if p.From.Node == v.node && p1.To.Node == v.node && (p.Info.Flags&Move != 0) && (p.Info.Flags|p1.Info.Flags)&(LeftAddr|RightAddr) == 0 && p.Info.Flags&SizeAny == p1.Info.Flags&SizeAny {
Russ Cox8c195bd2015-02-13 14:40:36 -0500675 p1.From = p.From
676 Thearch.Excise(f)
677 v.removed = 1
678 if debugmerge > 0 && Debug['v'] != 0 {
679 fmt.Printf("drop immediate-use %v\n", Sconv(v.node.Sym, 0))
680 }
681 }
682
683 nkill++
684 continue
685 }
686 }
687
688 // Traverse live range of each variable to set start, end.
689 // Each flood uses a new value of gen so that we don't have
690 // to clear all the r->active words after each variable.
Russ Cox382b44e2015-02-23 16:07:24 -0500691 gen := int32(0)
Russ Cox8c195bd2015-02-13 14:40:36 -0500692
Russ Cox382b44e2015-02-23 16:07:24 -0500693 for i := 0; i < len(var_); i++ {
Russ Cox8c195bd2015-02-13 14:40:36 -0500694 v = &var_[i]
695 gen++
Russ Coxfd38dbc2015-03-16 16:46:25 -0400696 for f := v.use; f != nil; f = f.Data.(*Flow) {
Russ Cox8c195bd2015-02-13 14:40:36 -0500697 mergewalk(v, f, uint32(gen))
698 }
699 if v.addr != 0 {
700 gen++
Russ Coxfd38dbc2015-03-16 16:46:25 -0400701 for f := v.use; f != nil; f = f.Data.(*Flow) {
Russ Cox8c195bd2015-02-13 14:40:36 -0500702 varkillwalk(v, f, uint32(gen))
703 }
704 }
705 }
706
707 // Sort variables by start.
Russ Cox382b44e2015-02-23 16:07:24 -0500708 bystart := make([]*TempVar, len(var_))
Russ Cox8c195bd2015-02-13 14:40:36 -0500709
Russ Cox382b44e2015-02-23 16:07:24 -0500710 for i := 0; i < len(var_); i++ {
Russ Cox8c195bd2015-02-13 14:40:36 -0500711 bystart[i] = &var_[i]
712 }
713 sort.Sort(startcmp(bystart[:len(var_)]))
714
715 // List of in-use variables, sorted by end, so that the ones that
716 // will last the longest are the earliest ones in the array.
717 // The tail inuse[nfree:] holds no-longer-used variables.
718 // In theory we should use a sorted tree so that insertions are
719 // guaranteed O(log n) and then the loop is guaranteed O(n log n).
720 // In practice, it doesn't really matter.
Russ Cox382b44e2015-02-23 16:07:24 -0500721 inuse := make([]*TempVar, len(var_))
Russ Cox8c195bd2015-02-13 14:40:36 -0500722
Russ Cox382b44e2015-02-23 16:07:24 -0500723 ninuse := 0
724 nfree := len(var_)
725 var t *Type
726 var v1 *TempVar
727 var j int
728 for i := 0; i < len(var_); i++ {
Russ Cox8c195bd2015-02-13 14:40:36 -0500729 v = bystart[i]
730 if debugmerge > 0 && Debug['v'] != 0 {
731 fmt.Printf("consider %v: removed=%d\n", Nconv(v.node, obj.FmtSharp), v.removed)
732 }
733
734 if v.removed != 0 {
735 continue
736 }
737
738 // Expire no longer in use.
739 for ninuse > 0 && inuse[ninuse-1].end < v.start {
740 ninuse--
741 v1 = inuse[ninuse]
742 nfree--
743 inuse[nfree] = v1
744 }
745
746 if debugmerge > 0 && Debug['v'] != 0 {
747 fmt.Printf("consider %v: removed=%d nfree=%d nvar=%d\n", Nconv(v.node, obj.FmtSharp), v.removed, nfree, len(var_))
748 }
749
750 // Find old temp to reuse if possible.
751 t = v.node.Type
752
753 for j = nfree; j < len(var_); j++ {
754 v1 = inuse[j]
755 if debugmerge > 0 && Debug['v'] != 0 {
Dave Cheneyb006d382015-03-06 18:42:58 +1100756 fmt.Printf("consider %v: maybe %v: type=%v,%v addrtaken=%v,%v\n", Nconv(v.node, obj.FmtSharp), Nconv(v1.node, obj.FmtSharp), Tconv(t, 0), Tconv(v1.node.Type, 0), v.node.Addrtaken, v1.node.Addrtaken)
Russ Cox8c195bd2015-02-13 14:40:36 -0500757 }
758
759 // Require the types to match but also require the addrtaken bits to match.
760 // If a variable's address is taken, that disables registerization for the individual
761 // words of the variable (for example, the base,len,cap of a slice).
762 // We don't want to merge a non-addressed var with an addressed one and
763 // inhibit registerization of the former.
764 if Eqtype(t, v1.node.Type) && v.node.Addrtaken == v1.node.Addrtaken {
765 inuse[j] = inuse[nfree]
766 nfree++
767 if v1.merge != nil {
768 v.merge = v1.merge
769 } else {
770 v.merge = v1
771 }
772 nkill++
773 break
774 }
775 }
776
777 // Sort v into inuse.
778 j = ninuse
779 ninuse++
780
781 for j > 0 && inuse[j-1].end < v.end {
782 inuse[j] = inuse[j-1]
783 j--
784 }
785
786 inuse[j] = v
787 }
788
789 if debugmerge > 0 && Debug['v'] != 0 {
790 fmt.Printf("%v [%d - %d]\n", Sconv(Curfn.Nname.Sym, 0), len(var_), nkill)
Russ Cox382b44e2015-02-23 16:07:24 -0500791 var v *TempVar
792 for i := 0; i < len(var_); i++ {
Russ Cox8c195bd2015-02-13 14:40:36 -0500793 v = &var_[i]
794 fmt.Printf("var %v %v %d-%d", Nconv(v.node, obj.FmtSharp), Tconv(v.node.Type, 0), v.start, v.end)
795 if v.addr != 0 {
796 fmt.Printf(" addr=1")
797 }
798 if v.removed != 0 {
799 fmt.Printf(" dead=1")
800 }
801 if v.merge != nil {
802 fmt.Printf(" merge %v", Nconv(v.merge.node, obj.FmtSharp))
803 }
804 if v.start == v.end && v.def != nil {
805 fmt.Printf(" %v", v.def.Prog)
806 }
807 fmt.Printf("\n")
808 }
809
810 if debugmerge > 1 && Debug['v'] != 0 {
811 Dumpit("after", g.Start, 0)
812 }
813 }
814
815 // Update node references to use merged temporaries.
Russ Cox382b44e2015-02-23 16:07:24 -0500816 for f := g.Start; f != nil; f = f.Link {
Russ Coxfd38dbc2015-03-16 16:46:25 -0400817 p := f.Prog
Russ Cox8c195bd2015-02-13 14:40:36 -0500818 n, _ = p.From.Node.(*Node)
819 if n != nil {
820 v, _ = n.Opt.(*TempVar)
821 if v != nil && v.merge != nil {
822 p.From.Node = v.merge.node
823 }
824 }
825 n, _ = p.To.Node.(*Node)
826 if n != nil {
827 v, _ = n.Opt.(*TempVar)
828 if v != nil && v.merge != nil {
829 p.To.Node = v.merge.node
830 }
831 }
832 }
833
834 // Delete merged nodes from declaration list.
Russ Cox382b44e2015-02-23 16:07:24 -0500835 var l *NodeList
836 for lp := &Curfn.Dcl; ; {
Russ Cox8c195bd2015-02-13 14:40:36 -0500837 l = *lp
Russ Coxdc7b54b2015-02-17 22:13:49 -0500838 if l == nil {
Russ Cox8c195bd2015-02-13 14:40:36 -0500839 break
840 }
841
842 Curfn.Dcl.End = l
843 n = l.N
844 v, _ = n.Opt.(*TempVar)
845 if v != nil && (v.merge != nil || v.removed != 0) {
846 *lp = l.Next
847 continue
848 }
849
850 lp = &l.Next
851 }
852
853 // Clear aux structures.
Russ Cox382b44e2015-02-23 16:07:24 -0500854 for i := 0; i < len(var_); i++ {
Russ Cox8c195bd2015-02-13 14:40:36 -0500855 var_[i].node.Opt = nil
856 }
857
858 Flowend(g)
859}
860
861func mergewalk(v *TempVar, f0 *Flow, gen uint32) {
862 var p *obj.Prog
863 var f1 *Flow
Russ Cox8c195bd2015-02-13 14:40:36 -0500864
865 for f1 = f0; f1 != nil; f1 = f1.P1 {
866 if uint32(f1.Active) == gen {
867 break
868 }
869 f1.Active = int32(gen)
870 p = f1.Prog
871 if v.end < p.Pc {
872 v.end = p.Pc
873 }
874 if f1 == v.def {
875 v.start = p.Pc
876 break
877 }
878 }
879
Russ Cox382b44e2015-02-23 16:07:24 -0500880 var f2 *Flow
881 for f := f0; f != f1; f = f.P1 {
Russ Cox8c195bd2015-02-13 14:40:36 -0500882 for f2 = f.P2; f2 != nil; f2 = f2.P2link {
883 mergewalk(v, f2, gen)
884 }
885 }
886}
887
888func varkillwalk(v *TempVar, f0 *Flow, gen uint32) {
889 var p *obj.Prog
890 var f1 *Flow
Russ Cox8c195bd2015-02-13 14:40:36 -0500891
892 for f1 = f0; f1 != nil; f1 = f1.S1 {
893 if uint32(f1.Active) == gen {
894 break
895 }
896 f1.Active = int32(gen)
897 p = f1.Prog
898 if v.end < p.Pc {
899 v.end = p.Pc
900 }
901 if v.start > p.Pc {
902 v.start = p.Pc
903 }
904 if p.As == obj.ARET || (p.As == obj.AVARKILL && p.To.Node == v.node) {
905 break
906 }
907 }
908
Russ Cox382b44e2015-02-23 16:07:24 -0500909 for f := f0; f != f1; f = f.S1 {
Russ Cox8c195bd2015-02-13 14:40:36 -0500910 varkillwalk(v, f.S2, gen)
911 }
912}
913
914// Eliminate redundant nil pointer checks.
915//
916// The code generation pass emits a CHECKNIL for every possibly nil pointer.
917// This pass removes a CHECKNIL if every predecessor path has already
918// checked this value for nil.
919//
920// Simple backwards flood from check to definition.
921// Run prog loop backward from end of program to beginning to avoid quadratic
922// behavior removing a run of checks.
923//
924// Assume that stack variables with address not taken can be loaded multiple times
925// from memory without being rechecked. Other variables need to be checked on
926// each load.
927type NilVar struct {
928}
929
930var killed int // f->data is either nil or &killed
931
932func nilopt(firstp *obj.Prog) {
Russ Cox382b44e2015-02-23 16:07:24 -0500933 g := Flowstart(firstp, nil)
Russ Cox8c195bd2015-02-13 14:40:36 -0500934 if g == nil {
935 return
936 }
937
938 if Debug_checknil > 1 { /* || strcmp(curfn->nname->sym->name, "f1") == 0 */
939 Dumpit("nilopt", g.Start, 0)
940 }
941
Russ Cox382b44e2015-02-23 16:07:24 -0500942 ncheck := 0
943 nkill := 0
944 var p *obj.Prog
945 for f := g.Start; f != nil; f = f.Link {
Russ Cox8c195bd2015-02-13 14:40:36 -0500946 p = f.Prog
Russ Coxdc7b54b2015-02-17 22:13:49 -0500947 if p.As != obj.ACHECKNIL || !Thearch.Regtyp(&p.From) {
Russ Cox8c195bd2015-02-13 14:40:36 -0500948 continue
949 }
950 ncheck++
Russ Coxdc7b54b2015-02-17 22:13:49 -0500951 if Thearch.Stackaddr(&p.From) {
Russ Cox8c195bd2015-02-13 14:40:36 -0500952 if Debug_checknil != 0 && p.Lineno > 1 {
953 Warnl(int(p.Lineno), "removed nil check of SP address")
954 }
955 f.Data = &killed
956 continue
957 }
958
959 nilwalkfwd(f)
960 if f.Data != nil {
961 if Debug_checknil != 0 && p.Lineno > 1 {
962 Warnl(int(p.Lineno), "removed nil check before indirect")
963 }
964 continue
965 }
966
967 nilwalkback(f)
968 if f.Data != nil {
969 if Debug_checknil != 0 && p.Lineno > 1 {
970 Warnl(int(p.Lineno), "removed repeated nil check")
971 }
972 continue
973 }
974 }
975
Russ Cox382b44e2015-02-23 16:07:24 -0500976 for f := g.Start; f != nil; f = f.Link {
Russ Cox8c195bd2015-02-13 14:40:36 -0500977 if f.Data != nil {
978 nkill++
979 Thearch.Excise(f)
980 }
981 }
982
983 Flowend(g)
984
985 if Debug_checknil > 1 {
986 fmt.Printf("%v: removed %d of %d nil checks\n", Sconv(Curfn.Nname.Sym, 0), nkill, ncheck)
987 }
988}
989
990func nilwalkback(fcheck *Flow) {
Russ Cox382b44e2015-02-23 16:07:24 -0500991 for f := fcheck; f != nil; f = Uniqp(f) {
Russ Coxfd38dbc2015-03-16 16:46:25 -0400992 p := f.Prog
993 if (p.Info.Flags&RightWrite != 0) && Thearch.Sameaddr(&p.To, &fcheck.Prog.From) {
Russ Cox8c195bd2015-02-13 14:40:36 -0500994 // Found initialization of value we're checking for nil.
995 // without first finding the check, so this one is unchecked.
996 return
997 }
998
Russ Coxdc7b54b2015-02-17 22:13:49 -0500999 if f != fcheck && p.As == obj.ACHECKNIL && Thearch.Sameaddr(&p.From, &fcheck.Prog.From) {
Russ Cox8c195bd2015-02-13 14:40:36 -05001000 fcheck.Data = &killed
1001 return
1002 }
1003 }
1004}
1005
1006// Here is a more complex version that scans backward across branches.
1007// It assumes fcheck->kill = 1 has been set on entry, and its job is to find a reason
1008// to keep the check (setting fcheck->kill = 0).
1009// It doesn't handle copying of aggregates as well as I would like,
1010// nor variables with their address taken,
1011// and it's too subtle to turn on this late in Go 1.2. Perhaps for Go 1.3.
1012/*
1013for(f1 = f0; f1 != nil; f1 = f1->p1) {
1014 if(f1->active == gen)
1015 break;
1016 f1->active = gen;
1017 p = f1->prog;
1018
1019 // If same check, stop this loop but still check
1020 // alternate predecessors up to this point.
1021 if(f1 != fcheck && p->as == ACHECKNIL && thearch.sameaddr(&p->from, &fcheck->prog->from))
1022 break;
1023
Russ Coxfd38dbc2015-03-16 16:46:25 -04001024 if((p.Info.flags & RightWrite) && thearch.sameaddr(&p->to, &fcheck->prog->from)) {
Russ Cox8c195bd2015-02-13 14:40:36 -05001025 // Found initialization of value we're checking for nil.
1026 // without first finding the check, so this one is unchecked.
1027 fcheck->kill = 0;
1028 return;
1029 }
1030
1031 if(f1->p1 == nil && f1->p2 == nil) {
1032 print("lost pred for %P\n", fcheck->prog);
1033 for(f1=f0; f1!=nil; f1=f1->p1) {
1034 thearch.proginfo(&info, f1->prog);
1035 print("\t%P %d %d %D %D\n", r1->prog, info.flags&RightWrite, thearch.sameaddr(&f1->prog->to, &fcheck->prog->from), &f1->prog->to, &fcheck->prog->from);
1036 }
1037 fatal("lost pred trail");
1038 }
1039}
1040
1041for(f = f0; f != f1; f = f->p1)
1042 for(f2 = f->p2; f2 != nil; f2 = f2->p2link)
1043 nilwalkback(fcheck, f2, gen);
1044*/
Russ Cox8c195bd2015-02-13 14:40:36 -05001045
Russ Coxfd38dbc2015-03-16 16:46:25 -04001046func nilwalkfwd(fcheck *Flow) {
Russ Cox8c195bd2015-02-13 14:40:36 -05001047 // If the path down from rcheck dereferences the address
1048 // (possibly with a small offset) before writing to memory
1049 // and before any subsequent checks, it's okay to wait for
1050 // that implicit check. Only consider this basic block to
1051 // avoid problems like:
1052 // _ = *x // should panic
1053 // for {} // no writes but infinite loop may be considered visible
Russ Coxfd38dbc2015-03-16 16:46:25 -04001054
Russ Cox175929b2015-03-02 14:22:05 -05001055 var last *Flow
Russ Cox382b44e2015-02-23 16:07:24 -05001056 for f := Uniqs(fcheck); f != nil; f = Uniqs(f) {
Russ Coxfd38dbc2015-03-16 16:46:25 -04001057 p := f.Prog
1058 if (p.Info.Flags&LeftRead != 0) && Thearch.Smallindir(&p.From, &fcheck.Prog.From) {
Russ Cox8c195bd2015-02-13 14:40:36 -05001059 fcheck.Data = &killed
1060 return
1061 }
1062
Russ Coxfd38dbc2015-03-16 16:46:25 -04001063 if (p.Info.Flags&(RightRead|RightWrite) != 0) && Thearch.Smallindir(&p.To, &fcheck.Prog.From) {
Russ Cox8c195bd2015-02-13 14:40:36 -05001064 fcheck.Data = &killed
1065 return
1066 }
1067
1068 // Stop if another nil check happens.
1069 if p.As == obj.ACHECKNIL {
1070 return
1071 }
1072
1073 // Stop if value is lost.
Russ Coxfd38dbc2015-03-16 16:46:25 -04001074 if (p.Info.Flags&RightWrite != 0) && Thearch.Sameaddr(&p.To, &fcheck.Prog.From) {
Russ Cox8c195bd2015-02-13 14:40:36 -05001075 return
1076 }
1077
1078 // Stop if memory write.
Russ Coxfd38dbc2015-03-16 16:46:25 -04001079 if (p.Info.Flags&RightWrite != 0) && !Thearch.Regtyp(&p.To) {
Russ Cox8c195bd2015-02-13 14:40:36 -05001080 return
1081 }
1082
1083 // Stop if we jump backward.
1084 if last != nil && f.Id <= last.Id {
1085 return
1086 }
1087 last = f
1088 }
1089}