blob: 5305e4b7f6bb2f3d15ba26f9b8de064a4e83287e [file] [log] [blame]
Russ Cox8c195bd2015-02-13 14:40:36 -05001// Inferno utils/5c/peep.c
2// http://code.google.com/p/inferno-os/source/browse/utils/5c/peep.c
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
31package main
32
33import (
Russ Cox5a16d6f2015-03-03 22:20:44 -050034 "cmd/internal/gc"
Russ Cox8c195bd2015-02-13 14:40:36 -050035 "cmd/internal/obj"
36 "cmd/internal/obj/arm"
37 "fmt"
38)
Russ Cox8c195bd2015-02-13 14:40:36 -050039
40var gactive uint32
41
42// UNUSED
43func peep(firstp *obj.Prog) {
Russ Cox382b44e2015-02-23 16:07:24 -050044 g := (*gc.Graph)(gc.Flowstart(firstp, nil))
Russ Cox8c195bd2015-02-13 14:40:36 -050045 if g == nil {
46 return
47 }
48 gactive = 0
49
Russ Cox382b44e2015-02-23 16:07:24 -050050 var r *gc.Flow
51 var p *obj.Prog
52 var t int
Russ Cox8c195bd2015-02-13 14:40:36 -050053loop1:
54 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
55 gc.Dumpit("loop1", g.Start, 0)
56 }
57
58 t = 0
59 for r = g.Start; r != nil; r = r.Link {
60 p = r.Prog
61 switch p.As {
62 /*
63 * elide shift into TYPE_SHIFT operand of subsequent instruction
64 */
65 // if(shiftprop(r)) {
66 // excise(r);
67 // t++;
68 // break;
69 // }
70 case arm.ASLL,
71 arm.ASRL,
72 arm.ASRA:
73 break
74
75 case arm.AMOVB,
76 arm.AMOVH,
77 arm.AMOVW,
78 arm.AMOVF,
79 arm.AMOVD:
Russ Coxdc7b54b2015-02-17 22:13:49 -050080 if regtyp(&p.From) {
Russ Cox8c195bd2015-02-13 14:40:36 -050081 if p.From.Type == p.To.Type && isfloatreg(&p.From) == isfloatreg(&p.To) {
82 if p.Scond == arm.C_SCOND_NONE {
Russ Coxdc7b54b2015-02-17 22:13:49 -050083 if copyprop(g, r) {
Russ Cox8c195bd2015-02-13 14:40:36 -050084 excise(r)
85 t++
86 break
87 }
88
Russ Coxdc7b54b2015-02-17 22:13:49 -050089 if subprop(r) && copyprop(g, r) {
Russ Cox8c195bd2015-02-13 14:40:36 -050090 excise(r)
91 t++
92 break
93 }
94 }
95 }
96 }
97
98 case arm.AMOVHS,
99 arm.AMOVHU,
100 arm.AMOVBS,
101 arm.AMOVBU:
102 if p.From.Type == obj.TYPE_REG {
Russ Coxdc7b54b2015-02-17 22:13:49 -0500103 if shortprop(r) {
Russ Cox8c195bd2015-02-13 14:40:36 -0500104 t++
105 }
106 }
107 }
108 }
109
110 /*
111 if(p->scond == C_SCOND_NONE)
112 if(regtyp(&p->to))
113 if(isdconst(&p->from)) {
114 constprop(&p->from, &p->to, r->s1);
115 }
116 break;
117 */
118 if t != 0 {
119 goto loop1
120 }
121
Russ Cox382b44e2015-02-23 16:07:24 -0500122 for r := (*gc.Flow)(g.Start); r != nil; r = r.Link {
Russ Cox8c195bd2015-02-13 14:40:36 -0500123 p = r.Prog
124 switch p.As {
125 /*
126 * EOR -1,x,y => MVN x,y
127 */
128 case arm.AEOR:
Russ Coxdc7b54b2015-02-17 22:13:49 -0500129 if isdconst(&p.From) && p.From.Offset == -1 {
Russ Cox8c195bd2015-02-13 14:40:36 -0500130 p.As = arm.AMVN
131 p.From.Type = obj.TYPE_REG
132 if p.Reg != 0 {
133 p.From.Reg = p.Reg
134 } else {
135 p.From.Reg = p.To.Reg
136 }
137 p.Reg = 0
138 }
139 }
140 }
141
Russ Cox382b44e2015-02-23 16:07:24 -0500142 for r := (*gc.Flow)(g.Start); r != nil; r = r.Link {
Russ Cox8c195bd2015-02-13 14:40:36 -0500143 p = r.Prog
144 switch p.As {
145 case arm.AMOVW,
146 arm.AMOVB,
147 arm.AMOVBS,
148 arm.AMOVBU:
149 if p.From.Type == obj.TYPE_MEM && p.From.Offset == 0 {
150 xtramodes(g, r, &p.From)
151 } else if p.To.Type == obj.TYPE_MEM && p.To.Offset == 0 {
152 xtramodes(g, r, &p.To)
153 } else {
154 continue
155 }
156 }
157 }
158
159 // case ACMP:
160 // /*
161 // * elide CMP $0,x if calculation of x can set condition codes
162 // */
163 // if(isdconst(&p->from) || p->from.offset != 0)
164 // continue;
165 // r2 = r->s1;
166 // if(r2 == nil)
167 // continue;
168 // t = r2->prog->as;
169 // switch(t) {
170 // default:
171 // continue;
172 // case ABEQ:
173 // case ABNE:
174 // case ABMI:
175 // case ABPL:
176 // break;
177 // case ABGE:
178 // t = ABPL;
179 // break;
180 // case ABLT:
181 // t = ABMI;
182 // break;
183 // case ABHI:
184 // t = ABNE;
185 // break;
186 // case ABLS:
187 // t = ABEQ;
188 // break;
189 // }
190 // r1 = r;
191 // do
192 // r1 = uniqp(r1);
193 // while (r1 != nil && r1->prog->as == ANOP);
194 // if(r1 == nil)
195 // continue;
196 // p1 = r1->prog;
197 // if(p1->to.type != TYPE_REG)
198 // continue;
199 // if(p1->to.reg != p->reg)
200 // if(!(p1->as == AMOVW && p1->from.type == TYPE_REG && p1->from.reg == p->reg))
201 // continue;
202 //
203 // switch(p1->as) {
204 // default:
205 // continue;
206 // case AMOVW:
207 // if(p1->from.type != TYPE_REG)
208 // continue;
209 // case AAND:
210 // case AEOR:
211 // case AORR:
212 // case ABIC:
213 // case AMVN:
214 // case ASUB:
215 // case ARSB:
216 // case AADD:
217 // case AADC:
218 // case ASBC:
219 // case ARSC:
220 // break;
221 // }
222 // p1->scond |= C_SBIT;
223 // r2->prog->as = t;
224 // excise(r);
225 // continue;
226
227 // predicate(g);
228
229 gc.Flowend(g)
230}
231
Russ Coxdc7b54b2015-02-17 22:13:49 -0500232func regtyp(a *obj.Addr) bool {
233 return a.Type == obj.TYPE_REG && (arm.REG_R0 <= a.Reg && a.Reg <= arm.REG_R15 || arm.REG_F0 <= a.Reg && a.Reg <= arm.REG_F15)
Russ Cox8c195bd2015-02-13 14:40:36 -0500234}
235
236/*
237 * the idea is to substitute
238 * one register for another
239 * from one MOV to another
240 * MOV a, R0
241 * ADD b, R0 / no use of R1
242 * MOV R0, R1
243 * would be converted to
244 * MOV a, R1
245 * ADD b, R1
246 * MOV R1, R0
247 * hopefully, then the former or latter MOV
248 * will be eliminated by copy propagation.
249 */
Russ Coxdc7b54b2015-02-17 22:13:49 -0500250func subprop(r0 *gc.Flow) bool {
Russ Cox382b44e2015-02-23 16:07:24 -0500251 p := (*obj.Prog)(r0.Prog)
252 v1 := (*obj.Addr)(&p.From)
Russ Coxdc7b54b2015-02-17 22:13:49 -0500253 if !regtyp(v1) {
254 return false
Russ Cox8c195bd2015-02-13 14:40:36 -0500255 }
Russ Cox382b44e2015-02-23 16:07:24 -0500256 v2 := (*obj.Addr)(&p.To)
Russ Coxdc7b54b2015-02-17 22:13:49 -0500257 if !regtyp(v2) {
258 return false
Russ Cox8c195bd2015-02-13 14:40:36 -0500259 }
Russ Cox79f727a2015-03-02 12:35:15 -0500260 for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) {
Russ Cox8c195bd2015-02-13 14:40:36 -0500261 if gc.Uniqs(r) == nil {
262 break
263 }
264 p = r.Prog
265 if p.As == obj.AVARDEF || p.As == obj.AVARKILL {
266 continue
267 }
Russ Coxfd38dbc2015-03-16 16:46:25 -0400268 if p.Info.Flags&gc.Call != 0 {
Russ Coxdc7b54b2015-02-17 22:13:49 -0500269 return false
Russ Cox8c195bd2015-02-13 14:40:36 -0500270 }
271
Russ Coxfd38dbc2015-03-16 16:46:25 -0400272 // TODO(rsc): Whatever invalidated the info should have done this call.
273 proginfo(p)
274
275 if (p.Info.Flags&gc.CanRegRead != 0) && p.To.Type == obj.TYPE_REG {
276 p.Info.Flags |= gc.RegRead
277 p.Info.Flags &^= (gc.CanRegRead | gc.RightRead)
Russ Cox8c195bd2015-02-13 14:40:36 -0500278 p.Reg = p.To.Reg
279 }
280
281 switch p.As {
282 case arm.AMULLU,
283 arm.AMULA,
284 arm.AMVN:
Russ Coxdc7b54b2015-02-17 22:13:49 -0500285 return false
Russ Cox8c195bd2015-02-13 14:40:36 -0500286 }
287
Russ Coxfd38dbc2015-03-16 16:46:25 -0400288 if p.Info.Flags&(gc.RightRead|gc.RightWrite) == gc.RightWrite {
Russ Cox8c195bd2015-02-13 14:40:36 -0500289 if p.To.Type == v1.Type {
290 if p.To.Reg == v1.Reg {
291 if p.Scond == arm.C_SCOND_NONE {
Russ Cox79f727a2015-03-02 12:35:15 -0500292 copysub(&p.To, v1, v2, 1)
293 if gc.Debug['P'] != 0 {
294 fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog)
295 if p.From.Type == v2.Type {
296 fmt.Printf(" excise")
297 }
298 fmt.Printf("\n")
299 }
300
301 for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) {
302 p = r.Prog
303 copysub(&p.From, v1, v2, 1)
304 copysub1(p, v1, v2, 1)
305 copysub(&p.To, v1, v2, 1)
306 if gc.Debug['P'] != 0 {
307 fmt.Printf("%v\n", r.Prog)
308 }
309 }
310
311 t := int(int(v1.Reg))
312 v1.Reg = v2.Reg
313 v2.Reg = int16(t)
314 if gc.Debug['P'] != 0 {
315 fmt.Printf("%v last\n", r.Prog)
316 }
317 return true
Russ Cox8c195bd2015-02-13 14:40:36 -0500318 }
319 }
320 }
321 }
322
Russ Coxdc7b54b2015-02-17 22:13:49 -0500323 if copyau(&p.From, v2) || copyau1(p, v2) || copyau(&p.To, v2) {
Russ Cox8c195bd2015-02-13 14:40:36 -0500324 break
325 }
326 if copysub(&p.From, v1, v2, 0) != 0 || copysub1(p, v1, v2, 0) != 0 || copysub(&p.To, v1, v2, 0) != 0 {
327 break
328 }
329 }
330
Russ Coxdc7b54b2015-02-17 22:13:49 -0500331 return false
Russ Cox8c195bd2015-02-13 14:40:36 -0500332}
333
334/*
335 * The idea is to remove redundant copies.
336 * v1->v2 F=0
337 * (use v2 s/v2/v1/)*
338 * set v1 F=1
339 * use v2 return fail
340 * -----------------
341 * v1->v2 F=0
342 * (use v2 s/v2/v1/)*
343 * set v1 F=1
344 * set v2 return success
345 */
Russ Coxdc7b54b2015-02-17 22:13:49 -0500346func copyprop(g *gc.Graph, r0 *gc.Flow) bool {
Russ Cox382b44e2015-02-23 16:07:24 -0500347 p := (*obj.Prog)(r0.Prog)
348 v1 := (*obj.Addr)(&p.From)
349 v2 := (*obj.Addr)(&p.To)
Russ Coxdc7b54b2015-02-17 22:13:49 -0500350 if copyas(v1, v2) {
351 return true
Russ Cox8c195bd2015-02-13 14:40:36 -0500352 }
353 gactive++
354 return copy1(v1, v2, r0.S1, 0)
355}
356
Russ Coxdc7b54b2015-02-17 22:13:49 -0500357func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f int) bool {
Russ Cox8c195bd2015-02-13 14:40:36 -0500358 if uint32(r.Active) == gactive {
359 if gc.Debug['P'] != 0 {
360 fmt.Printf("act set; return 1\n")
361 }
Russ Coxdc7b54b2015-02-17 22:13:49 -0500362 return true
Russ Cox8c195bd2015-02-13 14:40:36 -0500363 }
364
365 r.Active = int32(gactive)
366 if gc.Debug['P'] != 0 {
367 fmt.Printf("copy %v->%v f=%d\n", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), f)
368 }
Russ Cox382b44e2015-02-23 16:07:24 -0500369 var t int
370 var p *obj.Prog
Russ Cox8c195bd2015-02-13 14:40:36 -0500371 for ; r != nil; r = r.S1 {
372 p = r.Prog
373 if gc.Debug['P'] != 0 {
374 fmt.Printf("%v", p)
375 }
Russ Coxdc7b54b2015-02-17 22:13:49 -0500376 if f == 0 && gc.Uniqp(r) == nil {
Russ Cox8c195bd2015-02-13 14:40:36 -0500377 f = 1
378 if gc.Debug['P'] != 0 {
379 fmt.Printf("; merge; f=%d", f)
380 }
381 }
382
383 t = copyu(p, v2, nil)
384 switch t {
385 case 2: /* rar, can't split */
386 if gc.Debug['P'] != 0 {
387 fmt.Printf("; %vrar; return 0\n", gc.Ctxt.Dconv(v2))
388 }
Russ Coxdc7b54b2015-02-17 22:13:49 -0500389 return false
Russ Cox8c195bd2015-02-13 14:40:36 -0500390
391 case 3: /* set */
392 if gc.Debug['P'] != 0 {
393 fmt.Printf("; %vset; return 1\n", gc.Ctxt.Dconv(v2))
394 }
Russ Coxdc7b54b2015-02-17 22:13:49 -0500395 return true
Russ Cox8c195bd2015-02-13 14:40:36 -0500396
397 case 1, /* used, substitute */
398 4: /* use and set */
399 if f != 0 {
Russ Coxdc7b54b2015-02-17 22:13:49 -0500400 if gc.Debug['P'] == 0 {
401 return false
Russ Cox8c195bd2015-02-13 14:40:36 -0500402 }
403 if t == 4 {
404 fmt.Printf("; %vused+set and f=%d; return 0\n", gc.Ctxt.Dconv(v2), f)
405 } else {
406 fmt.Printf("; %vused and f=%d; return 0\n", gc.Ctxt.Dconv(v2), f)
407 }
Russ Coxdc7b54b2015-02-17 22:13:49 -0500408 return false
Russ Cox8c195bd2015-02-13 14:40:36 -0500409 }
410
411 if copyu(p, v2, v1) != 0 {
412 if gc.Debug['P'] != 0 {
413 fmt.Printf("; sub fail; return 0\n")
414 }
Russ Coxdc7b54b2015-02-17 22:13:49 -0500415 return false
Russ Cox8c195bd2015-02-13 14:40:36 -0500416 }
417
418 if gc.Debug['P'] != 0 {
419 fmt.Printf("; sub%v/%v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1))
420 }
421 if t == 4 {
422 if gc.Debug['P'] != 0 {
423 fmt.Printf("; %vused+set; return 1\n", gc.Ctxt.Dconv(v2))
424 }
Russ Coxdc7b54b2015-02-17 22:13:49 -0500425 return true
Russ Cox8c195bd2015-02-13 14:40:36 -0500426 }
427 }
428
Russ Coxdc7b54b2015-02-17 22:13:49 -0500429 if f == 0 {
Russ Cox8c195bd2015-02-13 14:40:36 -0500430 t = copyu(p, v1, nil)
Russ Coxdc7b54b2015-02-17 22:13:49 -0500431 if f == 0 && (t == 2 || t == 3 || t == 4) {
Russ Cox8c195bd2015-02-13 14:40:36 -0500432 f = 1
433 if gc.Debug['P'] != 0 {
434 fmt.Printf("; %vset and !f; f=%d", gc.Ctxt.Dconv(v1), f)
435 }
436 }
437 }
438
439 if gc.Debug['P'] != 0 {
440 fmt.Printf("\n")
441 }
442 if r.S2 != nil {
Russ Coxdc7b54b2015-02-17 22:13:49 -0500443 if !copy1(v1, v2, r.S2, f) {
444 return false
Russ Cox8c195bd2015-02-13 14:40:36 -0500445 }
446 }
447 }
448
Russ Coxdc7b54b2015-02-17 22:13:49 -0500449 return true
Russ Cox8c195bd2015-02-13 14:40:36 -0500450}
451
452// UNUSED
453/*
454 * The idea is to remove redundant constants.
455 * $c1->v1
456 * ($c1->v2 s/$c1/v1)*
457 * set v1 return
458 * The v1->v2 should be eliminated by copy propagation.
459 */
460func constprop(c1 *obj.Addr, v1 *obj.Addr, r *gc.Flow) {
Russ Cox8c195bd2015-02-13 14:40:36 -0500461 if gc.Debug['P'] != 0 {
462 fmt.Printf("constprop %v->%v\n", gc.Ctxt.Dconv(c1), gc.Ctxt.Dconv(v1))
463 }
Russ Cox382b44e2015-02-23 16:07:24 -0500464 var p *obj.Prog
Russ Cox8c195bd2015-02-13 14:40:36 -0500465 for ; r != nil; r = r.S1 {
466 p = r.Prog
467 if gc.Debug['P'] != 0 {
468 fmt.Printf("%v", p)
469 }
470 if gc.Uniqp(r) == nil {
471 if gc.Debug['P'] != 0 {
472 fmt.Printf("; merge; return\n")
473 }
474 return
475 }
476
Russ Coxdc7b54b2015-02-17 22:13:49 -0500477 if p.As == arm.AMOVW && copyas(&p.From, c1) {
Russ Cox8c195bd2015-02-13 14:40:36 -0500478 if gc.Debug['P'] != 0 {
479 fmt.Printf("; sub%v/%v", gc.Ctxt.Dconv(&p.From), gc.Ctxt.Dconv(v1))
480 }
481 p.From = *v1
482 } else if copyu(p, v1, nil) > 1 {
483 if gc.Debug['P'] != 0 {
484 fmt.Printf("; %vset; return\n", gc.Ctxt.Dconv(v1))
485 }
486 return
487 }
488
489 if gc.Debug['P'] != 0 {
490 fmt.Printf("\n")
491 }
492 if r.S2 != nil {
493 constprop(c1, v1, r.S2)
494 }
495 }
496}
497
498/*
499 * shortprop eliminates redundant zero/sign extensions.
500 *
501 * MOVBS x, R
502 * <no use R>
503 * MOVBS R, R'
504 *
505 * changed to
506 *
507 * MOVBS x, R
508 * ...
509 * MOVB R, R' (compiled to mov)
510 *
511 * MOVBS above can be a MOVBS, MOVBU, MOVHS or MOVHU.
512 */
Russ Coxdc7b54b2015-02-17 22:13:49 -0500513func shortprop(r *gc.Flow) bool {
Russ Cox382b44e2015-02-23 16:07:24 -0500514 p := (*obj.Prog)(r.Prog)
515 r1 := (*gc.Flow)(findpre(r, &p.From))
Russ Cox8c195bd2015-02-13 14:40:36 -0500516 if r1 == nil {
Russ Coxdc7b54b2015-02-17 22:13:49 -0500517 return false
Russ Cox8c195bd2015-02-13 14:40:36 -0500518 }
519
Russ Cox382b44e2015-02-23 16:07:24 -0500520 p1 := (*obj.Prog)(r1.Prog)
Russ Cox8c195bd2015-02-13 14:40:36 -0500521 if p1.As == p.As {
522 // Two consecutive extensions.
523 goto gotit
524 }
525
Russ Coxdc7b54b2015-02-17 22:13:49 -0500526 if p1.As == arm.AMOVW && isdconst(&p1.From) && p1.From.Offset >= 0 && p1.From.Offset < 128 {
Russ Cox8c195bd2015-02-13 14:40:36 -0500527 // Loaded an immediate.
528 goto gotit
529 }
530
Russ Coxdc7b54b2015-02-17 22:13:49 -0500531 return false
Russ Cox8c195bd2015-02-13 14:40:36 -0500532
533gotit:
534 if gc.Debug['P'] != 0 {
535 fmt.Printf("shortprop\n%v\n%v", p1, p)
536 }
537 switch p.As {
538 case arm.AMOVBS,
539 arm.AMOVBU:
540 p.As = arm.AMOVB
541
542 case arm.AMOVHS,
543 arm.AMOVHU:
544 p.As = arm.AMOVH
545 }
546
547 if gc.Debug['P'] != 0 {
Rob Pike74e88df2015-03-02 20:17:20 -0800548 fmt.Printf(" => %v\n", obj.Aconv(int(p.As)))
Russ Cox8c195bd2015-02-13 14:40:36 -0500549 }
Russ Coxdc7b54b2015-02-17 22:13:49 -0500550 return true
Russ Cox8c195bd2015-02-13 14:40:36 -0500551}
552
553// UNUSED
554/*
555 * ASLL x,y,w
556 * .. (not use w, not set x y w)
557 * AXXX w,a,b (a != w)
558 * .. (not use w)
559 * (set w)
560 * ----------- changed to
561 * ..
562 * AXXX (x<<y),a,b
563 * ..
564 */
Russ Coxdc7b54b2015-02-17 22:13:49 -0500565func shiftprop(r *gc.Flow) bool {
Russ Cox382b44e2015-02-23 16:07:24 -0500566 p := (*obj.Prog)(r.Prog)
Russ Cox8c195bd2015-02-13 14:40:36 -0500567 if p.To.Type != obj.TYPE_REG {
568 if gc.Debug['P'] != 0 {
569 fmt.Printf("\tBOTCH: result not reg; FAILURE\n")
570 }
Russ Coxdc7b54b2015-02-17 22:13:49 -0500571 return false
Russ Cox8c195bd2015-02-13 14:40:36 -0500572 }
573
Russ Cox382b44e2015-02-23 16:07:24 -0500574 n := int(int(p.To.Reg))
575 a := obj.Addr(obj.Addr{})
Russ Cox8c195bd2015-02-13 14:40:36 -0500576 if p.Reg != 0 && p.Reg != p.To.Reg {
577 a.Type = obj.TYPE_REG
578 a.Reg = p.Reg
579 }
580
581 if gc.Debug['P'] != 0 {
582 fmt.Printf("shiftprop\n%v", p)
583 }
Russ Cox382b44e2015-02-23 16:07:24 -0500584 r1 := (*gc.Flow)(r)
585 var p1 *obj.Prog
Russ Cox8c195bd2015-02-13 14:40:36 -0500586 for {
587 /* find first use of shift result; abort if shift operands or result are changed */
588 r1 = gc.Uniqs(r1)
589
590 if r1 == nil {
591 if gc.Debug['P'] != 0 {
592 fmt.Printf("\tbranch; FAILURE\n")
593 }
Russ Coxdc7b54b2015-02-17 22:13:49 -0500594 return false
Russ Cox8c195bd2015-02-13 14:40:36 -0500595 }
596
597 if gc.Uniqp(r1) == nil {
598 if gc.Debug['P'] != 0 {
599 fmt.Printf("\tmerge; FAILURE\n")
600 }
Russ Coxdc7b54b2015-02-17 22:13:49 -0500601 return false
Russ Cox8c195bd2015-02-13 14:40:36 -0500602 }
603
604 p1 = r1.Prog
605 if gc.Debug['P'] != 0 {
606 fmt.Printf("\n%v", p1)
607 }
608 switch copyu(p1, &p.To, nil) {
609 case 0: /* not used or set */
610 if (p.From.Type == obj.TYPE_REG && copyu(p1, &p.From, nil) > 1) || (a.Type == obj.TYPE_REG && copyu(p1, &a, nil) > 1) {
611 if gc.Debug['P'] != 0 {
612 fmt.Printf("\targs modified; FAILURE\n")
613 }
Russ Coxdc7b54b2015-02-17 22:13:49 -0500614 return false
Russ Cox8c195bd2015-02-13 14:40:36 -0500615 }
616
617 continue
618 case 3: /* set, not used */
619 {
620 if gc.Debug['P'] != 0 {
621 fmt.Printf("\tBOTCH: noref; FAILURE\n")
622 }
Russ Coxdc7b54b2015-02-17 22:13:49 -0500623 return false
Russ Cox8c195bd2015-02-13 14:40:36 -0500624 }
625 }
626
627 break
628 }
629
630 /* check whether substitution can be done */
631 switch p1.As {
632 default:
633 if gc.Debug['P'] != 0 {
634 fmt.Printf("\tnon-dpi; FAILURE\n")
635 }
Russ Coxdc7b54b2015-02-17 22:13:49 -0500636 return false
Russ Cox8c195bd2015-02-13 14:40:36 -0500637
638 case arm.AAND,
639 arm.AEOR,
640 arm.AADD,
641 arm.AADC,
642 arm.AORR,
643 arm.ASUB,
644 arm.ASBC,
645 arm.ARSB,
646 arm.ARSC:
647 if int(p1.Reg) == n || (p1.Reg == 0 && p1.To.Type == obj.TYPE_REG && int(p1.To.Reg) == n) {
648 if p1.From.Type != obj.TYPE_REG {
649 if gc.Debug['P'] != 0 {
650 fmt.Printf("\tcan't swap; FAILURE\n")
651 }
Russ Coxdc7b54b2015-02-17 22:13:49 -0500652 return false
Russ Cox8c195bd2015-02-13 14:40:36 -0500653 }
654
655 p1.Reg = p1.From.Reg
656 p1.From.Reg = int16(n)
657 switch p1.As {
658 case arm.ASUB:
659 p1.As = arm.ARSB
660
661 case arm.ARSB:
662 p1.As = arm.ASUB
663
664 case arm.ASBC:
665 p1.As = arm.ARSC
666
667 case arm.ARSC:
668 p1.As = arm.ASBC
669 }
670
671 if gc.Debug['P'] != 0 {
672 fmt.Printf("\t=>%v", p1)
673 }
674 }
675 fallthrough
676
677 case arm.ABIC,
678 arm.ATST,
679 arm.ACMP,
680 arm.ACMN:
681 if int(p1.Reg) == n {
682 if gc.Debug['P'] != 0 {
683 fmt.Printf("\tcan't swap; FAILURE\n")
684 }
Russ Coxdc7b54b2015-02-17 22:13:49 -0500685 return false
Russ Cox8c195bd2015-02-13 14:40:36 -0500686 }
687
688 if p1.Reg == 0 && int(p1.To.Reg) == n {
689 if gc.Debug['P'] != 0 {
690 fmt.Printf("\tshift result used twice; FAILURE\n")
691 }
Russ Coxdc7b54b2015-02-17 22:13:49 -0500692 return false
Russ Cox8c195bd2015-02-13 14:40:36 -0500693 }
694
695 // case AMVN:
696 if p1.From.Type == obj.TYPE_SHIFT {
697 if gc.Debug['P'] != 0 {
698 fmt.Printf("\tshift result used in shift; FAILURE\n")
699 }
Russ Coxdc7b54b2015-02-17 22:13:49 -0500700 return false
Russ Cox8c195bd2015-02-13 14:40:36 -0500701 }
702
703 if p1.From.Type != obj.TYPE_REG || int(p1.From.Reg) != n {
704 if gc.Debug['P'] != 0 {
705 fmt.Printf("\tBOTCH: where is it used?; FAILURE\n")
706 }
Russ Coxdc7b54b2015-02-17 22:13:49 -0500707 return false
Russ Cox8c195bd2015-02-13 14:40:36 -0500708 }
709 }
710
711 /* check whether shift result is used subsequently */
Russ Cox382b44e2015-02-23 16:07:24 -0500712 p2 := (*obj.Prog)(p1)
Russ Cox8c195bd2015-02-13 14:40:36 -0500713
714 if int(p1.To.Reg) != n {
Russ Cox382b44e2015-02-23 16:07:24 -0500715 var p1 *obj.Prog
Russ Cox8c195bd2015-02-13 14:40:36 -0500716 for {
717 r1 = gc.Uniqs(r1)
718 if r1 == nil {
719 if gc.Debug['P'] != 0 {
720 fmt.Printf("\tinconclusive; FAILURE\n")
721 }
Russ Coxdc7b54b2015-02-17 22:13:49 -0500722 return false
Russ Cox8c195bd2015-02-13 14:40:36 -0500723 }
724
725 p1 = r1.Prog
726 if gc.Debug['P'] != 0 {
727 fmt.Printf("\n%v", p1)
728 }
729 switch copyu(p1, &p.To, nil) {
730 case 0: /* not used or set */
731 continue
732
733 case 3: /* set, not used */
734 break
735
736 default: /* used */
737 if gc.Debug['P'] != 0 {
738 fmt.Printf("\treused; FAILURE\n")
739 }
Russ Coxdc7b54b2015-02-17 22:13:49 -0500740 return false
Russ Cox8c195bd2015-02-13 14:40:36 -0500741 }
742
743 break
744 }
745 }
746
747 /* make the substitution */
748 p2.From.Reg = 0
749
Russ Cox382b44e2015-02-23 16:07:24 -0500750 o := int(int(p.Reg))
Russ Cox8c195bd2015-02-13 14:40:36 -0500751 if o == 0 {
752 o = int(p.To.Reg)
753 }
754 o &= 15
755
756 switch p.From.Type {
757 case obj.TYPE_CONST:
758 o |= int((p.From.Offset & 0x1f) << 7)
759
760 case obj.TYPE_REG:
761 o |= 1<<4 | (int(p.From.Reg)&15)<<8
762 }
763
764 switch p.As {
765 case arm.ASLL:
766 o |= 0 << 5
767
768 case arm.ASRL:
769 o |= 1 << 5
770
771 case arm.ASRA:
772 o |= 2 << 5
773 }
774
Russ Coxdc7b54b2015-02-17 22:13:49 -0500775 p2.From = obj.Addr{}
Russ Cox8c195bd2015-02-13 14:40:36 -0500776 p2.From.Type = obj.TYPE_SHIFT
777 p2.From.Offset = int64(o)
778 if gc.Debug['P'] != 0 {
779 fmt.Printf("\t=>%v\tSUCCEED\n", p2)
780 }
Russ Coxdc7b54b2015-02-17 22:13:49 -0500781 return true
Russ Cox8c195bd2015-02-13 14:40:36 -0500782}
783
784/*
785 * findpre returns the last instruction mentioning v
786 * before r. It must be a set, and there must be
787 * a unique path from that instruction to r.
788 */
789func findpre(r *gc.Flow, v *obj.Addr) *gc.Flow {
790 var r1 *gc.Flow
791
Russ Coxd7f6d462015-03-09 00:31:13 -0400792 for r1 = gc.Uniqp(r); r1 != nil; r, r1 = r1, gc.Uniqp(r1) {
Russ Cox8c195bd2015-02-13 14:40:36 -0500793 if gc.Uniqs(r1) != r {
794 return nil
795 }
796 switch copyu(r1.Prog, v, nil) {
797 case 1, /* used */
798 2: /* read-alter-rewrite */
799 return nil
800
801 case 3, /* set */
802 4: /* set and used */
803 return r1
804 }
805 }
806
807 return nil
808}
809
810/*
811 * findinc finds ADD instructions with a constant
812 * argument which falls within the immed_12 range.
813 */
814func findinc(r *gc.Flow, r2 *gc.Flow, v *obj.Addr) *gc.Flow {
815 var r1 *gc.Flow
816 var p *obj.Prog
817
Russ Coxd7f6d462015-03-09 00:31:13 -0400818 for r1 = gc.Uniqs(r); r1 != nil && r1 != r2; r, r1 = r1, gc.Uniqs(r1) {
Russ Cox8c195bd2015-02-13 14:40:36 -0500819 if gc.Uniqp(r1) != r {
820 return nil
821 }
822 switch copyu(r1.Prog, v, nil) {
823 case 0: /* not touched */
824 continue
825
826 case 4: /* set and used */
827 p = r1.Prog
828
829 if p.As == arm.AADD {
Russ Coxdc7b54b2015-02-17 22:13:49 -0500830 if isdconst(&p.From) {
Russ Cox8c195bd2015-02-13 14:40:36 -0500831 if p.From.Offset > -4096 && p.From.Offset < 4096 {
832 return r1
833 }
834 }
835 }
836 fallthrough
837
838 default:
839 return nil
840 }
841 }
842
843 return nil
844}
845
Russ Coxdc7b54b2015-02-17 22:13:49 -0500846func nochange(r *gc.Flow, r2 *gc.Flow, p *obj.Prog) bool {
Russ Cox8c195bd2015-02-13 14:40:36 -0500847 if r == r2 {
Russ Coxdc7b54b2015-02-17 22:13:49 -0500848 return true
Russ Cox8c195bd2015-02-13 14:40:36 -0500849 }
Russ Cox382b44e2015-02-23 16:07:24 -0500850 n := int(0)
851 var a [3]obj.Addr
Russ Cox8c195bd2015-02-13 14:40:36 -0500852 if p.Reg != 0 && p.Reg != p.To.Reg {
853 a[n].Type = obj.TYPE_REG
854 a[n].Reg = p.Reg
855 n++
856 }
857
858 switch p.From.Type {
859 case obj.TYPE_SHIFT:
860 a[n].Type = obj.TYPE_REG
861 a[n].Reg = int16(arm.REG_R0 + (p.From.Offset & 0xf))
862 n++
863 fallthrough
864
865 case obj.TYPE_REG:
866 a[n].Type = obj.TYPE_REG
867 a[n].Reg = p.From.Reg
868 n++
869 }
870
871 if n == 0 {
Russ Coxdc7b54b2015-02-17 22:13:49 -0500872 return true
Russ Cox8c195bd2015-02-13 14:40:36 -0500873 }
Russ Cox382b44e2015-02-23 16:07:24 -0500874 var i int
Russ Cox8c195bd2015-02-13 14:40:36 -0500875 for ; r != nil && r != r2; r = gc.Uniqs(r) {
876 p = r.Prog
877 for i = 0; i < n; i++ {
878 if copyu(p, &a[i], nil) > 1 {
Russ Coxdc7b54b2015-02-17 22:13:49 -0500879 return false
Russ Cox8c195bd2015-02-13 14:40:36 -0500880 }
881 }
882 }
883
Russ Coxdc7b54b2015-02-17 22:13:49 -0500884 return true
Russ Cox8c195bd2015-02-13 14:40:36 -0500885}
886
Russ Coxdc7b54b2015-02-17 22:13:49 -0500887func findu1(r *gc.Flow, v *obj.Addr) bool {
Russ Cox8c195bd2015-02-13 14:40:36 -0500888 for ; r != nil; r = r.S1 {
889 if r.Active != 0 {
Russ Coxdc7b54b2015-02-17 22:13:49 -0500890 return false
Russ Cox8c195bd2015-02-13 14:40:36 -0500891 }
892 r.Active = 1
893 switch copyu(r.Prog, v, nil) {
894 case 1, /* used */
895 2, /* read-alter-rewrite */
896 4: /* set and used */
Russ Coxdc7b54b2015-02-17 22:13:49 -0500897 return true
Russ Cox8c195bd2015-02-13 14:40:36 -0500898
899 case 3: /* set */
Russ Coxdc7b54b2015-02-17 22:13:49 -0500900 return false
Russ Cox8c195bd2015-02-13 14:40:36 -0500901 }
902
903 if r.S2 != nil {
Russ Coxdc7b54b2015-02-17 22:13:49 -0500904 if findu1(r.S2, v) {
905 return true
Russ Cox8c195bd2015-02-13 14:40:36 -0500906 }
907 }
908 }
909
Russ Coxdc7b54b2015-02-17 22:13:49 -0500910 return false
Russ Cox8c195bd2015-02-13 14:40:36 -0500911}
912
Russ Coxdc7b54b2015-02-17 22:13:49 -0500913func finduse(g *gc.Graph, r *gc.Flow, v *obj.Addr) bool {
Russ Cox382b44e2015-02-23 16:07:24 -0500914 for r1 := (*gc.Flow)(g.Start); r1 != nil; r1 = r1.Link {
Russ Cox8c195bd2015-02-13 14:40:36 -0500915 r1.Active = 0
916 }
917 return findu1(r, v)
918}
919
920/*
921 * xtramodes enables the ARM post increment and
922 * shift offset addressing modes to transform
923 * MOVW 0(R3),R1
924 * ADD $4,R3,R3
925 * into
926 * MOVW.P 4(R3),R1
927 * and
928 * ADD R0,R1
929 * MOVBU 0(R1),R0
930 * into
931 * MOVBU R0<<0(R1),R0
932 */
Russ Coxdc7b54b2015-02-17 22:13:49 -0500933func xtramodes(g *gc.Graph, r *gc.Flow, a *obj.Addr) bool {
Russ Cox382b44e2015-02-23 16:07:24 -0500934 p := (*obj.Prog)(r.Prog)
935 v := obj.Addr(*a)
Russ Cox8c195bd2015-02-13 14:40:36 -0500936 v.Type = obj.TYPE_REG
Russ Cox382b44e2015-02-23 16:07:24 -0500937 r1 := (*gc.Flow)(findpre(r, &v))
Russ Cox8c195bd2015-02-13 14:40:36 -0500938 if r1 != nil {
Russ Cox382b44e2015-02-23 16:07:24 -0500939 p1 := r1.Prog
Russ Cox8c195bd2015-02-13 14:40:36 -0500940 if p1.To.Type == obj.TYPE_REG && p1.To.Reg == v.Reg {
941 switch p1.As {
942 case arm.AADD:
943 if p1.Scond&arm.C_SBIT != 0 {
944 // avoid altering ADD.S/ADC sequences.
945 break
946 }
947
948 if p1.From.Type == obj.TYPE_REG || (p1.From.Type == obj.TYPE_SHIFT && p1.From.Offset&(1<<4) == 0 && ((p.As != arm.AMOVB && p.As != arm.AMOVBS) || (a == &p.From && p1.From.Offset&^0xf == 0))) || ((p1.From.Type == obj.TYPE_ADDR || p1.From.Type == obj.TYPE_CONST) && p1.From.Offset > -4096 && p1.From.Offset < 4096) {
Russ Coxdc7b54b2015-02-17 22:13:49 -0500949 if nochange(gc.Uniqs(r1), r, p1) {
Russ Cox8c195bd2015-02-13 14:40:36 -0500950 if a != &p.From || v.Reg != p.To.Reg {
Russ Coxdc7b54b2015-02-17 22:13:49 -0500951 if finduse(g, r.S1, &v) {
Russ Cox8c195bd2015-02-13 14:40:36 -0500952 if p1.Reg == 0 || p1.Reg == v.Reg {
953 /* pre-indexing */
954 p.Scond |= arm.C_WBIT
955 } else {
Russ Coxdc7b54b2015-02-17 22:13:49 -0500956 return false
Russ Cox8c195bd2015-02-13 14:40:36 -0500957 }
958 }
959 }
960
961 switch p1.From.Type {
962 /* register offset */
963 case obj.TYPE_REG:
964 if gc.Nacl {
Russ Coxdc7b54b2015-02-17 22:13:49 -0500965 return false
Russ Cox8c195bd2015-02-13 14:40:36 -0500966 }
Russ Coxdc7b54b2015-02-17 22:13:49 -0500967 *a = obj.Addr{}
Russ Cox8c195bd2015-02-13 14:40:36 -0500968 a.Type = obj.TYPE_SHIFT
969 a.Offset = int64(p1.From.Reg) & 15
970
971 /* scaled register offset */
972 case obj.TYPE_SHIFT:
973 if gc.Nacl {
Russ Coxdc7b54b2015-02-17 22:13:49 -0500974 return false
Russ Cox8c195bd2015-02-13 14:40:36 -0500975 }
Russ Coxdc7b54b2015-02-17 22:13:49 -0500976 *a = obj.Addr{}
Russ Cox8c195bd2015-02-13 14:40:36 -0500977 a.Type = obj.TYPE_SHIFT
978 fallthrough
979
980 /* immediate offset */
981 case obj.TYPE_CONST,
982 obj.TYPE_ADDR:
983 a.Offset = p1.From.Offset
984 }
985
986 if p1.Reg != 0 {
987 a.Reg = p1.Reg
988 }
989 excise(r1)
Russ Coxdc7b54b2015-02-17 22:13:49 -0500990 return true
Russ Cox8c195bd2015-02-13 14:40:36 -0500991 }
992 }
993
994 case arm.AMOVW:
995 if p1.From.Type == obj.TYPE_REG {
Russ Cox382b44e2015-02-23 16:07:24 -0500996 r2 := (*gc.Flow)(findinc(r1, r, &p1.From))
Russ Cox8c195bd2015-02-13 14:40:36 -0500997 if r2 != nil {
Russ Cox382b44e2015-02-23 16:07:24 -0500998 var r3 *gc.Flow
Russ Cox8c195bd2015-02-13 14:40:36 -0500999 for r3 = gc.Uniqs(r2); r3.Prog.As == obj.ANOP; r3 = gc.Uniqs(r3) {
1000 }
1001 if r3 == r {
1002 /* post-indexing */
Russ Cox382b44e2015-02-23 16:07:24 -05001003 p1 := r2.Prog
Russ Cox8c195bd2015-02-13 14:40:36 -05001004
1005 a.Reg = p1.To.Reg
1006 a.Offset = p1.From.Offset
1007 p.Scond |= arm.C_PBIT
Russ Coxdc7b54b2015-02-17 22:13:49 -05001008 if !finduse(g, r, &r1.Prog.To) {
Russ Cox8c195bd2015-02-13 14:40:36 -05001009 excise(r1)
1010 }
1011 excise(r2)
Russ Coxdc7b54b2015-02-17 22:13:49 -05001012 return true
Russ Cox8c195bd2015-02-13 14:40:36 -05001013 }
1014 }
1015 }
1016 }
1017 }
1018 }
1019
1020 if a != &p.From || a.Reg != p.To.Reg {
Russ Cox382b44e2015-02-23 16:07:24 -05001021 r1 := (*gc.Flow)(findinc(r, nil, &v))
Russ Cox8c195bd2015-02-13 14:40:36 -05001022 if r1 != nil {
1023 /* post-indexing */
Russ Cox382b44e2015-02-23 16:07:24 -05001024 p1 := r1.Prog
Russ Cox8c195bd2015-02-13 14:40:36 -05001025
1026 a.Offset = p1.From.Offset
1027 p.Scond |= arm.C_PBIT
1028 excise(r1)
Russ Coxdc7b54b2015-02-17 22:13:49 -05001029 return true
Russ Cox8c195bd2015-02-13 14:40:36 -05001030 }
1031 }
1032
Russ Coxdc7b54b2015-02-17 22:13:49 -05001033 return false
Russ Cox8c195bd2015-02-13 14:40:36 -05001034}
1035
1036/*
1037 * return
1038 * 1 if v only used (and substitute),
1039 * 2 if read-alter-rewrite
1040 * 3 if set
1041 * 4 if set and used
1042 * 0 otherwise (not touched)
1043 */
1044func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int {
1045 switch p.As {
1046 default:
Rob Pike74e88df2015-03-02 20:17:20 -08001047 fmt.Printf("copyu: can't find %v\n", obj.Aconv(int(p.As)))
Russ Cox8c195bd2015-02-13 14:40:36 -05001048 return 2
1049
1050 case arm.AMOVM:
1051 if v.Type != obj.TYPE_REG {
1052 return 0
1053 }
1054 if p.From.Type == obj.TYPE_CONST { /* read reglist, read/rar */
1055 if s != nil {
1056 if p.From.Offset&(1<<uint(v.Reg)) != 0 {
1057 return 1
1058 }
1059 if copysub(&p.To, v, s, 1) != 0 {
1060 return 1
1061 }
1062 return 0
1063 }
1064
Russ Coxdc7b54b2015-02-17 22:13:49 -05001065 if copyau(&p.To, v) {
Russ Cox8c195bd2015-02-13 14:40:36 -05001066 if p.Scond&arm.C_WBIT != 0 {
1067 return 2
1068 }
1069 return 1
1070 }
1071
1072 if p.From.Offset&(1<<uint(v.Reg)) != 0 {
1073 return 1 /* read/rar, write reglist */
1074 }
1075 } else {
1076 if s != nil {
1077 if p.To.Offset&(1<<uint(v.Reg)) != 0 {
1078 return 1
1079 }
1080 if copysub(&p.From, v, s, 1) != 0 {
1081 return 1
1082 }
1083 return 0
1084 }
1085
Russ Coxdc7b54b2015-02-17 22:13:49 -05001086 if copyau(&p.From, v) {
Russ Cox8c195bd2015-02-13 14:40:36 -05001087 if p.Scond&arm.C_WBIT != 0 {
1088 return 2
1089 }
1090 if p.To.Offset&(1<<uint(v.Reg)) != 0 {
1091 return 4
1092 }
1093 return 1
1094 }
1095
1096 if p.To.Offset&(1<<uint(v.Reg)) != 0 {
1097 return 3
1098 }
1099 }
1100
1101 return 0
1102
1103 case obj.ANOP, /* read,, write */
1104 arm.AMOVW,
1105 arm.AMOVF,
1106 arm.AMOVD,
1107 arm.AMOVH,
1108 arm.AMOVHS,
1109 arm.AMOVHU,
1110 arm.AMOVB,
1111 arm.AMOVBS,
1112 arm.AMOVBU,
1113 arm.AMOVFW,
1114 arm.AMOVWF,
1115 arm.AMOVDW,
1116 arm.AMOVWD,
1117 arm.AMOVFD,
1118 arm.AMOVDF:
1119 if p.Scond&(arm.C_WBIT|arm.C_PBIT) != 0 {
1120 if v.Type == obj.TYPE_REG {
1121 if p.From.Type == obj.TYPE_MEM || p.From.Type == obj.TYPE_SHIFT {
1122 if p.From.Reg == v.Reg {
1123 return 2
1124 }
1125 } else {
1126 if p.To.Reg == v.Reg {
1127 return 2
1128 }
1129 }
1130 }
1131 }
1132
1133 if s != nil {
1134 if copysub(&p.From, v, s, 1) != 0 {
1135 return 1
1136 }
Russ Coxdc7b54b2015-02-17 22:13:49 -05001137 if !copyas(&p.To, v) {
Russ Cox8c195bd2015-02-13 14:40:36 -05001138 if copysub(&p.To, v, s, 1) != 0 {
1139 return 1
1140 }
1141 }
1142 return 0
1143 }
1144
Russ Coxdc7b54b2015-02-17 22:13:49 -05001145 if copyas(&p.To, v) {
Russ Cox8c195bd2015-02-13 14:40:36 -05001146 if p.Scond != arm.C_SCOND_NONE {
1147 return 2
1148 }
Russ Coxdc7b54b2015-02-17 22:13:49 -05001149 if copyau(&p.From, v) {
Russ Cox8c195bd2015-02-13 14:40:36 -05001150 return 4
1151 }
1152 return 3
1153 }
1154
Russ Coxdc7b54b2015-02-17 22:13:49 -05001155 if copyau(&p.From, v) {
Russ Cox8c195bd2015-02-13 14:40:36 -05001156 return 1
1157 }
Russ Coxdc7b54b2015-02-17 22:13:49 -05001158 if copyau(&p.To, v) {
Russ Cox8c195bd2015-02-13 14:40:36 -05001159 return 1
1160 }
1161 return 0
1162
1163 case arm.AMULLU, /* read, read, write, write */
1164 arm.AMULL,
1165 arm.AMULA,
1166 arm.AMVN:
1167 return 2
1168
1169 case arm.AADD, /* read, read, write */
1170 arm.AADC,
1171 arm.ASUB,
1172 arm.ASBC,
1173 arm.ARSB,
1174 arm.ASLL,
1175 arm.ASRL,
1176 arm.ASRA,
1177 arm.AORR,
1178 arm.AAND,
1179 arm.AEOR,
1180 arm.AMUL,
1181 arm.AMULU,
1182 arm.ADIV,
1183 arm.ADIVU,
1184 arm.AMOD,
1185 arm.AMODU,
1186 arm.AADDF,
1187 arm.AADDD,
1188 arm.ASUBF,
1189 arm.ASUBD,
1190 arm.AMULF,
1191 arm.AMULD,
1192 arm.ADIVF,
1193 arm.ADIVD,
1194 obj.ACHECKNIL,
1195 /* read */
1196 arm.ACMPF, /* read, read, */
1197 arm.ACMPD,
1198 arm.ACMP,
1199 arm.ACMN,
1200 arm.ACASE,
1201 arm.ATST:
1202 /* read,, */
1203 if s != nil {
1204 if copysub(&p.From, v, s, 1) != 0 {
1205 return 1
1206 }
1207 if copysub1(p, v, s, 1) != 0 {
1208 return 1
1209 }
Russ Coxdc7b54b2015-02-17 22:13:49 -05001210 if !copyas(&p.To, v) {
Russ Cox8c195bd2015-02-13 14:40:36 -05001211 if copysub(&p.To, v, s, 1) != 0 {
1212 return 1
1213 }
1214 }
1215 return 0
1216 }
1217
Russ Coxdc7b54b2015-02-17 22:13:49 -05001218 if copyas(&p.To, v) {
Russ Cox8c195bd2015-02-13 14:40:36 -05001219 if p.Scond != arm.C_SCOND_NONE {
1220 return 2
1221 }
1222 if p.Reg == 0 {
1223 p.Reg = p.To.Reg
1224 }
Russ Coxdc7b54b2015-02-17 22:13:49 -05001225 if copyau(&p.From, v) {
Russ Cox8c195bd2015-02-13 14:40:36 -05001226 return 4
1227 }
Russ Coxdc7b54b2015-02-17 22:13:49 -05001228 if copyau1(p, v) {
Russ Cox8c195bd2015-02-13 14:40:36 -05001229 return 4
1230 }
1231 return 3
1232 }
1233
Russ Coxdc7b54b2015-02-17 22:13:49 -05001234 if copyau(&p.From, v) {
Russ Cox8c195bd2015-02-13 14:40:36 -05001235 return 1
1236 }
Russ Coxdc7b54b2015-02-17 22:13:49 -05001237 if copyau1(p, v) {
Russ Cox8c195bd2015-02-13 14:40:36 -05001238 return 1
1239 }
Russ Coxdc7b54b2015-02-17 22:13:49 -05001240 if copyau(&p.To, v) {
Russ Cox8c195bd2015-02-13 14:40:36 -05001241 return 1
1242 }
1243 return 0
1244
1245 case arm.ABEQ, /* read, read */
1246 arm.ABNE,
1247 arm.ABCS,
1248 arm.ABHS,
1249 arm.ABCC,
1250 arm.ABLO,
1251 arm.ABMI,
1252 arm.ABPL,
1253 arm.ABVS,
1254 arm.ABVC,
1255 arm.ABHI,
1256 arm.ABLS,
1257 arm.ABGE,
1258 arm.ABLT,
1259 arm.ABGT,
1260 arm.ABLE:
1261 if s != nil {
1262 if copysub(&p.From, v, s, 1) != 0 {
1263 return 1
1264 }
1265 return copysub1(p, v, s, 1)
1266 }
1267
Russ Coxdc7b54b2015-02-17 22:13:49 -05001268 if copyau(&p.From, v) {
Russ Cox8c195bd2015-02-13 14:40:36 -05001269 return 1
1270 }
Russ Coxdc7b54b2015-02-17 22:13:49 -05001271 if copyau1(p, v) {
Russ Cox8c195bd2015-02-13 14:40:36 -05001272 return 1
1273 }
1274 return 0
1275
1276 case arm.AB: /* funny */
1277 if s != nil {
1278 if copysub(&p.To, v, s, 1) != 0 {
1279 return 1
1280 }
1281 return 0
1282 }
1283
Russ Coxdc7b54b2015-02-17 22:13:49 -05001284 if copyau(&p.To, v) {
Russ Cox8c195bd2015-02-13 14:40:36 -05001285 return 1
1286 }
1287 return 0
1288
1289 case obj.ARET: /* funny */
1290 if s != nil {
1291 return 1
1292 }
1293 return 3
1294
1295 case arm.ABL: /* funny */
1296 if v.Type == obj.TYPE_REG {
1297 // TODO(rsc): REG_R0 and REG_F0 used to be
1298 // (when register numbers started at 0) exregoffset and exfregoffset,
1299 // which are unset entirely.
1300 // It's strange that this handles R0 and F0 differently from the other
1301 // registers. Possible failure to optimize?
1302 if arm.REG_R0 < v.Reg && v.Reg <= arm.REGEXT {
1303 return 2
1304 }
1305 if v.Reg == arm.REGARG {
1306 return 2
1307 }
1308 if arm.REG_F0 < v.Reg && v.Reg <= arm.FREGEXT {
1309 return 2
1310 }
1311 }
1312
1313 if p.From.Type == obj.TYPE_REG && v.Type == obj.TYPE_REG && p.From.Reg == v.Reg {
1314 return 2
1315 }
1316
1317 if s != nil {
1318 if copysub(&p.To, v, s, 1) != 0 {
1319 return 1
1320 }
1321 return 0
1322 }
1323
Russ Coxdc7b54b2015-02-17 22:13:49 -05001324 if copyau(&p.To, v) {
Russ Cox8c195bd2015-02-13 14:40:36 -05001325 return 4
1326 }
1327 return 3
1328
1329 // R0 is zero, used by DUFFZERO, cannot be substituted.
1330 // R1 is ptr to memory, used and set, cannot be substituted.
1331 case obj.ADUFFZERO:
1332 if v.Type == obj.TYPE_REG {
Russ Coxb115c352015-03-18 17:26:36 -04001333 if v.Reg == arm.REG_R0 {
Russ Cox8c195bd2015-02-13 14:40:36 -05001334 return 1
1335 }
Russ Coxb115c352015-03-18 17:26:36 -04001336 if v.Reg == arm.REG_R0+1 {
Russ Cox8c195bd2015-02-13 14:40:36 -05001337 return 2
1338 }
1339 }
1340
1341 return 0
1342
1343 // R0 is scratch, set by DUFFCOPY, cannot be substituted.
1344 // R1, R2 areptr to src, dst, used and set, cannot be substituted.
1345 case obj.ADUFFCOPY:
1346 if v.Type == obj.TYPE_REG {
Russ Coxb115c352015-03-18 17:26:36 -04001347 if v.Reg == arm.REG_R0 {
Russ Cox8c195bd2015-02-13 14:40:36 -05001348 return 3
1349 }
Russ Coxb115c352015-03-18 17:26:36 -04001350 if v.Reg == arm.REG_R0+1 || v.Reg == arm.REG_R0+2 {
Russ Cox8c195bd2015-02-13 14:40:36 -05001351 return 2
1352 }
1353 }
1354
1355 return 0
1356
1357 case obj.ATEXT: /* funny */
1358 if v.Type == obj.TYPE_REG {
1359 if v.Reg == arm.REGARG {
1360 return 3
1361 }
1362 }
1363 return 0
1364
1365 case obj.APCDATA,
1366 obj.AFUNCDATA,
1367 obj.AVARDEF,
1368 obj.AVARKILL:
1369 return 0
1370 }
1371}
1372
1373/*
1374 * direct reference,
1375 * could be set/use depending on
1376 * semantics
1377 */
Russ Coxdc7b54b2015-02-17 22:13:49 -05001378func copyas(a *obj.Addr, v *obj.Addr) bool {
1379 if regtyp(v) {
Russ Cox8c195bd2015-02-13 14:40:36 -05001380 if a.Type == v.Type {
1381 if a.Reg == v.Reg {
Russ Coxdc7b54b2015-02-17 22:13:49 -05001382 return true
Russ Cox8c195bd2015-02-13 14:40:36 -05001383 }
1384 }
1385 } else if v.Type == obj.TYPE_CONST { /* for constprop */
1386 if a.Type == v.Type {
1387 if a.Name == v.Name {
1388 if a.Sym == v.Sym {
1389 if a.Reg == v.Reg {
1390 if a.Offset == v.Offset {
Russ Coxdc7b54b2015-02-17 22:13:49 -05001391 return true
Russ Cox8c195bd2015-02-13 14:40:36 -05001392 }
1393 }
1394 }
1395 }
1396 }
1397 }
1398
Russ Coxdc7b54b2015-02-17 22:13:49 -05001399 return false
Russ Cox8c195bd2015-02-13 14:40:36 -05001400}
1401
Russ Coxdc7b54b2015-02-17 22:13:49 -05001402func sameaddr(a *obj.Addr, v *obj.Addr) bool {
Russ Cox8c195bd2015-02-13 14:40:36 -05001403 if a.Type != v.Type {
Russ Coxdc7b54b2015-02-17 22:13:49 -05001404 return false
Russ Cox8c195bd2015-02-13 14:40:36 -05001405 }
Russ Coxdc7b54b2015-02-17 22:13:49 -05001406 if regtyp(v) && a.Reg == v.Reg {
1407 return true
Russ Cox8c195bd2015-02-13 14:40:36 -05001408 }
1409
1410 // TODO(rsc): Change v->type to v->name and enable.
1411 //if(v->type == NAME_AUTO || v->type == NAME_PARAM) {
1412 // if(v->offset == a->offset)
1413 // return 1;
1414 //}
Russ Coxdc7b54b2015-02-17 22:13:49 -05001415 return false
Russ Cox8c195bd2015-02-13 14:40:36 -05001416}
1417
1418/*
1419 * either direct or indirect
1420 */
Russ Coxdc7b54b2015-02-17 22:13:49 -05001421func copyau(a *obj.Addr, v *obj.Addr) bool {
1422 if copyas(a, v) {
1423 return true
Russ Cox8c195bd2015-02-13 14:40:36 -05001424 }
1425 if v.Type == obj.TYPE_REG {
1426 if a.Type == obj.TYPE_ADDR && a.Reg != 0 {
1427 if a.Reg == v.Reg {
Russ Coxdc7b54b2015-02-17 22:13:49 -05001428 return true
Russ Cox8c195bd2015-02-13 14:40:36 -05001429 }
1430 } else if a.Type == obj.TYPE_MEM {
1431 if a.Reg == v.Reg {
Russ Coxdc7b54b2015-02-17 22:13:49 -05001432 return true
Russ Cox8c195bd2015-02-13 14:40:36 -05001433 }
1434 } else if a.Type == obj.TYPE_REGREG || a.Type == obj.TYPE_REGREG2 {
1435 if a.Reg == v.Reg {
Russ Coxdc7b54b2015-02-17 22:13:49 -05001436 return true
Russ Cox8c195bd2015-02-13 14:40:36 -05001437 }
1438 if a.Offset == int64(v.Reg) {
Russ Coxdc7b54b2015-02-17 22:13:49 -05001439 return true
Russ Cox8c195bd2015-02-13 14:40:36 -05001440 }
1441 } else if a.Type == obj.TYPE_SHIFT {
1442 if a.Offset&0xf == int64(v.Reg-arm.REG_R0) {
Russ Coxdc7b54b2015-02-17 22:13:49 -05001443 return true
Russ Cox8c195bd2015-02-13 14:40:36 -05001444 }
1445 if (a.Offset&(1<<4) != 0) && (a.Offset>>8)&0xf == int64(v.Reg-arm.REG_R0) {
Russ Coxdc7b54b2015-02-17 22:13:49 -05001446 return true
Russ Cox8c195bd2015-02-13 14:40:36 -05001447 }
1448 }
1449 }
1450
Russ Coxdc7b54b2015-02-17 22:13:49 -05001451 return false
Russ Cox8c195bd2015-02-13 14:40:36 -05001452}
1453
1454/*
1455 * compare v to the center
1456 * register in p (p->reg)
1457 */
Russ Coxdc7b54b2015-02-17 22:13:49 -05001458func copyau1(p *obj.Prog, v *obj.Addr) bool {
Russ Cox8c195bd2015-02-13 14:40:36 -05001459 if v.Type == obj.TYPE_REG && v.Reg == 0 {
Russ Coxdc7b54b2015-02-17 22:13:49 -05001460 return false
Russ Cox8c195bd2015-02-13 14:40:36 -05001461 }
Russ Coxdc7b54b2015-02-17 22:13:49 -05001462 return p.Reg == v.Reg
Russ Cox8c195bd2015-02-13 14:40:36 -05001463}
1464
1465/*
1466 * substitute s for v in a
1467 * return failure to substitute
1468 */
1469func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f int) int {
1470 if f != 0 {
Russ Coxdc7b54b2015-02-17 22:13:49 -05001471 if copyau(a, v) {
Russ Cox8c195bd2015-02-13 14:40:36 -05001472 if a.Type == obj.TYPE_SHIFT {
1473 if a.Offset&0xf == int64(v.Reg-arm.REG_R0) {
1474 a.Offset = a.Offset&^0xf | int64(s.Reg)&0xf
1475 }
1476 if (a.Offset&(1<<4) != 0) && (a.Offset>>8)&0xf == int64(v.Reg-arm.REG_R0) {
1477 a.Offset = a.Offset&^(0xf<<8) | (int64(s.Reg)&0xf)<<8
1478 }
1479 } else if a.Type == obj.TYPE_REGREG || a.Type == obj.TYPE_REGREG2 {
1480 if a.Offset == int64(v.Reg) {
1481 a.Offset = int64(s.Reg)
1482 }
1483 if a.Reg == v.Reg {
1484 a.Reg = s.Reg
1485 }
1486 } else {
1487 a.Reg = s.Reg
1488 }
1489 }
1490 }
1491
1492 return 0
1493}
1494
1495func copysub1(p1 *obj.Prog, v *obj.Addr, s *obj.Addr, f int) int {
1496 if f != 0 {
Russ Coxdc7b54b2015-02-17 22:13:49 -05001497 if copyau1(p1, v) {
Russ Cox8c195bd2015-02-13 14:40:36 -05001498 p1.Reg = s.Reg
1499 }
1500 }
1501 return 0
1502}
1503
1504var predinfo = []struct {
1505 opcode int
1506 notopcode int
1507 scond int
1508 notscond int
1509}{
1510 struct {
1511 opcode int
1512 notopcode int
1513 scond int
1514 notscond int
1515 }{arm.ABEQ, arm.ABNE, 0x0, 0x1},
1516 struct {
1517 opcode int
1518 notopcode int
1519 scond int
1520 notscond int
1521 }{arm.ABNE, arm.ABEQ, 0x1, 0x0},
1522 struct {
1523 opcode int
1524 notopcode int
1525 scond int
1526 notscond int
1527 }{arm.ABCS, arm.ABCC, 0x2, 0x3},
1528 struct {
1529 opcode int
1530 notopcode int
1531 scond int
1532 notscond int
1533 }{arm.ABHS, arm.ABLO, 0x2, 0x3},
1534 struct {
1535 opcode int
1536 notopcode int
1537 scond int
1538 notscond int
1539 }{arm.ABCC, arm.ABCS, 0x3, 0x2},
1540 struct {
1541 opcode int
1542 notopcode int
1543 scond int
1544 notscond int
1545 }{arm.ABLO, arm.ABHS, 0x3, 0x2},
1546 struct {
1547 opcode int
1548 notopcode int
1549 scond int
1550 notscond int
1551 }{arm.ABMI, arm.ABPL, 0x4, 0x5},
1552 struct {
1553 opcode int
1554 notopcode int
1555 scond int
1556 notscond int
1557 }{arm.ABPL, arm.ABMI, 0x5, 0x4},
1558 struct {
1559 opcode int
1560 notopcode int
1561 scond int
1562 notscond int
1563 }{arm.ABVS, arm.ABVC, 0x6, 0x7},
1564 struct {
1565 opcode int
1566 notopcode int
1567 scond int
1568 notscond int
1569 }{arm.ABVC, arm.ABVS, 0x7, 0x6},
1570 struct {
1571 opcode int
1572 notopcode int
1573 scond int
1574 notscond int
1575 }{arm.ABHI, arm.ABLS, 0x8, 0x9},
1576 struct {
1577 opcode int
1578 notopcode int
1579 scond int
1580 notscond int
1581 }{arm.ABLS, arm.ABHI, 0x9, 0x8},
1582 struct {
1583 opcode int
1584 notopcode int
1585 scond int
1586 notscond int
1587 }{arm.ABGE, arm.ABLT, 0xA, 0xB},
1588 struct {
1589 opcode int
1590 notopcode int
1591 scond int
1592 notscond int
1593 }{arm.ABLT, arm.ABGE, 0xB, 0xA},
1594 struct {
1595 opcode int
1596 notopcode int
1597 scond int
1598 notscond int
1599 }{arm.ABGT, arm.ABLE, 0xC, 0xD},
1600 struct {
1601 opcode int
1602 notopcode int
1603 scond int
1604 notscond int
1605 }{arm.ABLE, arm.ABGT, 0xD, 0xC},
1606}
1607
1608type Joininfo struct {
1609 start *gc.Flow
1610 last *gc.Flow
1611 end *gc.Flow
1612 len int
1613}
1614
1615const (
1616 Join = iota
1617 Split
1618 End
1619 Branch
1620 Setcond
1621 Toolong
1622)
1623
1624const (
1625 Falsecond = iota
1626 Truecond
1627 Delbranch
1628 Keepbranch
1629)
1630
Russ Coxdc7b54b2015-02-17 22:13:49 -05001631func isbranch(p *obj.Prog) bool {
1632 return (arm.ABEQ <= p.As) && (p.As <= arm.ABLE)
Russ Cox8c195bd2015-02-13 14:40:36 -05001633}
1634
Russ Coxdc7b54b2015-02-17 22:13:49 -05001635func predicable(p *obj.Prog) bool {
Russ Cox8c195bd2015-02-13 14:40:36 -05001636 switch p.As {
1637 case obj.ANOP,
1638 obj.AXXX,
1639 obj.ADATA,
1640 obj.AGLOBL,
1641 obj.ATEXT,
1642 arm.AWORD,
1643 arm.ABCASE,
1644 arm.ACASE:
Russ Coxdc7b54b2015-02-17 22:13:49 -05001645 return false
Russ Cox8c195bd2015-02-13 14:40:36 -05001646 }
1647
Russ Coxdc7b54b2015-02-17 22:13:49 -05001648 if isbranch(p) {
1649 return false
Russ Cox8c195bd2015-02-13 14:40:36 -05001650 }
Russ Coxdc7b54b2015-02-17 22:13:49 -05001651 return true
Russ Cox8c195bd2015-02-13 14:40:36 -05001652}
1653
1654/*
1655 * Depends on an analysis of the encodings performed by 5l.
1656 * These seem to be all of the opcodes that lead to the "S" bit
1657 * being set in the instruction encodings.
1658 *
1659 * C_SBIT may also have been set explicitly in p->scond.
1660 */
Russ Coxdc7b54b2015-02-17 22:13:49 -05001661func modifiescpsr(p *obj.Prog) bool {
Russ Cox8c195bd2015-02-13 14:40:36 -05001662 switch p.As {
1663 case arm.AMULLU,
1664 arm.AMULA,
1665 arm.AMULU,
1666 arm.ADIVU,
1667 arm.ATEQ,
1668 arm.ACMN,
1669 arm.ATST,
1670 arm.ACMP,
1671 arm.AMUL,
1672 arm.ADIV,
1673 arm.AMOD,
1674 arm.AMODU,
1675 arm.ABL:
Russ Coxdc7b54b2015-02-17 22:13:49 -05001676 return true
Russ Cox8c195bd2015-02-13 14:40:36 -05001677 }
1678
1679 if p.Scond&arm.C_SBIT != 0 {
Russ Coxdc7b54b2015-02-17 22:13:49 -05001680 return true
Russ Cox8c195bd2015-02-13 14:40:36 -05001681 }
Russ Coxdc7b54b2015-02-17 22:13:49 -05001682 return false
Russ Cox8c195bd2015-02-13 14:40:36 -05001683}
1684
1685/*
1686 * Find the maximal chain of instructions starting with r which could
1687 * be executed conditionally
1688 */
1689func joinsplit(r *gc.Flow, j *Joininfo) int {
1690 j.start = r
1691 j.last = r
1692 j.len = 0
1693 for {
1694 if r.P2 != nil && (r.P1 != nil || r.P2.P2link != nil) {
1695 j.end = r
1696 return Join
1697 }
1698
1699 if r.S1 != nil && r.S2 != nil {
1700 j.end = r
1701 return Split
1702 }
1703
1704 j.last = r
1705 if r.Prog.As != obj.ANOP {
1706 j.len++
1707 }
Russ Coxdc7b54b2015-02-17 22:13:49 -05001708 if r.S1 == nil && r.S2 == nil {
Russ Cox8c195bd2015-02-13 14:40:36 -05001709 j.end = r.Link
1710 return End
1711 }
1712
1713 if r.S2 != nil {
1714 j.end = r.S2
1715 return Branch
1716 }
1717
Russ Coxdc7b54b2015-02-17 22:13:49 -05001718 if modifiescpsr(r.Prog) {
Russ Cox8c195bd2015-02-13 14:40:36 -05001719 j.end = r.S1
1720 return Setcond
1721 }
1722
1723 r = r.S1
Russ Coxdc7b54b2015-02-17 22:13:49 -05001724 if j.len >= 4 {
Russ Cox8c195bd2015-02-13 14:40:36 -05001725 break
1726 }
1727 }
1728
1729 j.end = r
1730 return Toolong
1731}
1732
1733func successor(r *gc.Flow) *gc.Flow {
1734 if r.S1 != nil {
1735 return r.S1
1736 } else {
1737 return r.S2
1738 }
1739}
1740
1741func applypred(rstart *gc.Flow, j *Joininfo, cond int, branch int) {
Russ Cox8c195bd2015-02-13 14:40:36 -05001742 if j.len == 0 {
1743 return
1744 }
Russ Cox382b44e2015-02-23 16:07:24 -05001745 var pred int
Russ Cox8c195bd2015-02-13 14:40:36 -05001746 if cond == Truecond {
1747 pred = predinfo[rstart.Prog.As-arm.ABEQ].scond
1748 } else {
1749 pred = predinfo[rstart.Prog.As-arm.ABEQ].notscond
1750 }
1751
Russ Cox382b44e2015-02-23 16:07:24 -05001752 for r := (*gc.Flow)(j.start); ; r = successor(r) {
Russ Cox8c195bd2015-02-13 14:40:36 -05001753 if r.Prog.As == arm.AB {
1754 if r != j.last || branch == Delbranch {
1755 excise(r)
1756 } else {
1757 if cond == Truecond {
1758 r.Prog.As = int16(predinfo[rstart.Prog.As-arm.ABEQ].opcode)
1759 } else {
1760 r.Prog.As = int16(predinfo[rstart.Prog.As-arm.ABEQ].notopcode)
1761 }
1762 }
Russ Coxdc7b54b2015-02-17 22:13:49 -05001763 } else if predicable(r.Prog) {
Russ Cox8c195bd2015-02-13 14:40:36 -05001764 r.Prog.Scond = uint8(int(r.Prog.Scond&^arm.C_SCOND) | pred)
1765 }
1766 if r.S1 != r.Link {
1767 r.S1 = r.Link
1768 r.Link.P1 = r
1769 }
1770
1771 if r == j.last {
1772 break
1773 }
1774 }
1775}
1776
1777func predicate(g *gc.Graph) {
Russ Cox8c195bd2015-02-13 14:40:36 -05001778 var t1 int
1779 var t2 int
1780 var j1 Joininfo
1781 var j2 Joininfo
1782
Russ Cox382b44e2015-02-23 16:07:24 -05001783 for r := (*gc.Flow)(g.Start); r != nil; r = r.Link {
Russ Coxdc7b54b2015-02-17 22:13:49 -05001784 if isbranch(r.Prog) {
Russ Cox8c195bd2015-02-13 14:40:36 -05001785 t1 = joinsplit(r.S1, &j1)
1786 t2 = joinsplit(r.S2, &j2)
1787 if j1.last.Link != j2.start {
1788 continue
1789 }
1790 if j1.end == j2.end {
1791 if (t1 == Branch && (t2 == Join || t2 == Setcond)) || (t2 == Join && (t1 == Join || t1 == Setcond)) {
1792 applypred(r, &j1, Falsecond, Delbranch)
1793 applypred(r, &j2, Truecond, Delbranch)
1794 excise(r)
1795 continue
1796 }
1797 }
1798
1799 if t1 == End || t1 == Branch {
1800 applypred(r, &j1, Falsecond, Keepbranch)
1801 excise(r)
1802 continue
1803 }
1804 }
1805 }
1806}
1807
Russ Coxdc7b54b2015-02-17 22:13:49 -05001808func isdconst(a *obj.Addr) bool {
1809 return a.Type == obj.TYPE_CONST
Russ Cox8c195bd2015-02-13 14:40:36 -05001810}
1811
Russ Coxdc7b54b2015-02-17 22:13:49 -05001812func isfloatreg(a *obj.Addr) bool {
1813 return arm.REG_F0 <= a.Reg && a.Reg <= arm.REG_F15
Russ Cox8c195bd2015-02-13 14:40:36 -05001814}
1815
Russ Coxdc7b54b2015-02-17 22:13:49 -05001816func stackaddr(a *obj.Addr) bool {
1817 return regtyp(a) && a.Reg == arm.REGSP
Russ Cox8c195bd2015-02-13 14:40:36 -05001818}
1819
Russ Coxdc7b54b2015-02-17 22:13:49 -05001820func smallindir(a *obj.Addr, reg *obj.Addr) bool {
1821 return reg.Type == obj.TYPE_REG && a.Type == obj.TYPE_MEM && a.Reg == reg.Reg && 0 <= a.Offset && a.Offset < 4096
Russ Cox8c195bd2015-02-13 14:40:36 -05001822}
1823
1824func excise(r *gc.Flow) {
Russ Cox382b44e2015-02-23 16:07:24 -05001825 p := (*obj.Prog)(r.Prog)
Russ Cox8c195bd2015-02-13 14:40:36 -05001826 obj.Nopout(p)
1827}