]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/compile/internal/x86/peep.go
all: make copyright headers consistent with one space after period
[gostls13.git] / src / cmd / compile / internal / x86 / peep.go
1 // Derived from Inferno utils/6c/peep.c
2 // http://code.google.com/p/inferno-os/source/browse/utils/6c/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
31 package x86
32
33 import (
34         "cmd/compile/internal/gc"
35         "cmd/internal/obj"
36         "cmd/internal/obj/x86"
37         "fmt"
38 )
39
40 const (
41         REGEXT      = 0
42         exregoffset = x86.REG_DI
43 )
44
45 var gactive uint32
46
47 // do we need the carry bit
48 func needc(p *obj.Prog) bool {
49         for p != nil {
50                 if p.Info.Flags&gc.UseCarry != 0 {
51                         return true
52                 }
53                 if p.Info.Flags&(gc.SetCarry|gc.KillCarry) != 0 {
54                         return false
55                 }
56                 p = p.Link
57         }
58
59         return false
60 }
61
62 func rnops(r *gc.Flow) *gc.Flow {
63         if r != nil {
64                 var p *obj.Prog
65                 var r1 *gc.Flow
66                 for {
67                         p = r.Prog
68                         if p.As != obj.ANOP || p.From.Type != obj.TYPE_NONE || p.To.Type != obj.TYPE_NONE {
69                                 break
70                         }
71                         r1 = gc.Uniqs(r)
72                         if r1 == nil {
73                                 break
74                         }
75                         r = r1
76                 }
77         }
78
79         return r
80 }
81
82 func peep(firstp *obj.Prog) {
83         g := gc.Flowstart(firstp, nil)
84         if g == nil {
85                 return
86         }
87         gactive = 0
88
89         // byte, word arithmetic elimination.
90         elimshortmov(g)
91
92         // constant propagation
93         // find MOV $con,R followed by
94         // another MOV $con,R without
95         // setting R in the interim
96         var p *obj.Prog
97         for r := g.Start; r != nil; r = r.Link {
98                 p = r.Prog
99                 switch p.As {
100                 case x86.ALEAL:
101                         if regtyp(&p.To) {
102                                 if p.From.Sym != nil {
103                                         if p.From.Index == x86.REG_NONE {
104                                                 conprop(r)
105                                         }
106                                 }
107                         }
108
109                 case x86.AMOVB,
110                         x86.AMOVW,
111                         x86.AMOVL,
112                         x86.AMOVSS,
113                         x86.AMOVSD:
114                         if regtyp(&p.To) {
115                                 if p.From.Type == obj.TYPE_CONST || p.From.Type == obj.TYPE_FCONST {
116                                         conprop(r)
117                                 }
118                         }
119                 }
120         }
121
122         var r1 *gc.Flow
123         var p1 *obj.Prog
124         var r *gc.Flow
125         var t int
126 loop1:
127         if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
128                 gc.Dumpit("loop1", g.Start, 0)
129         }
130
131         t = 0
132         for r = g.Start; r != nil; r = r.Link {
133                 p = r.Prog
134                 switch p.As {
135                 case x86.AMOVL,
136                         x86.AMOVSS,
137                         x86.AMOVSD:
138                         if regtyp(&p.To) {
139                                 if regtyp(&p.From) {
140                                         if copyprop(g, r) {
141                                                 excise(r)
142                                                 t++
143                                         } else if subprop(r) && copyprop(g, r) {
144                                                 excise(r)
145                                                 t++
146                                         }
147                                 }
148                         }
149
150                 case x86.AMOVBLZX,
151                         x86.AMOVWLZX,
152                         x86.AMOVBLSX,
153                         x86.AMOVWLSX:
154                         if regtyp(&p.To) {
155                                 r1 = rnops(gc.Uniqs(r))
156                                 if r1 != nil {
157                                         p1 = r1.Prog
158                                         if p.As == p1.As && p.To.Type == p1.From.Type && p.To.Reg == p1.From.Reg {
159                                                 p1.As = x86.AMOVL
160                                                 t++
161                                         }
162                                 }
163                         }
164
165                 case x86.AADDL,
166                         x86.AADDW:
167                         if p.From.Type != obj.TYPE_CONST || needc(p.Link) {
168                                 break
169                         }
170                         if p.From.Offset == -1 {
171                                 if p.As == x86.AADDL {
172                                         p.As = x86.ADECL
173                                 } else {
174                                         p.As = x86.ADECW
175                                 }
176                                 p.From = obj.Addr{}
177                                 break
178                         }
179
180                         if p.From.Offset == 1 {
181                                 if p.As == x86.AADDL {
182                                         p.As = x86.AINCL
183                                 } else {
184                                         p.As = x86.AINCW
185                                 }
186                                 p.From = obj.Addr{}
187                                 break
188                         }
189
190                 case x86.ASUBL,
191                         x86.ASUBW:
192                         if p.From.Type != obj.TYPE_CONST || needc(p.Link) {
193                                 break
194                         }
195                         if p.From.Offset == -1 {
196                                 if p.As == x86.ASUBL {
197                                         p.As = x86.AINCL
198                                 } else {
199                                         p.As = x86.AINCW
200                                 }
201                                 p.From = obj.Addr{}
202                                 break
203                         }
204
205                         if p.From.Offset == 1 {
206                                 if p.As == x86.ASUBL {
207                                         p.As = x86.ADECL
208                                 } else {
209                                         p.As = x86.ADECW
210                                 }
211                                 p.From = obj.Addr{}
212                                 break
213                         }
214                 }
215         }
216
217         if t != 0 {
218                 goto loop1
219         }
220
221         // MOVSD removal.
222         // We never use packed registers, so a MOVSD between registers
223         // can be replaced by MOVAPD, which moves the pair of float64s
224         // instead of just the lower one. We only use the lower one, but
225         // the processor can do better if we do moves using both.
226         for r := g.Start; r != nil; r = r.Link {
227                 p = r.Prog
228                 if p.As == x86.AMOVSD {
229                         if regtyp(&p.From) {
230                                 if regtyp(&p.To) {
231                                         p.As = x86.AMOVAPD
232                                 }
233                         }
234                 }
235         }
236
237         gc.Flowend(g)
238 }
239
240 func excise(r *gc.Flow) {
241         p := r.Prog
242         if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
243                 fmt.Printf("%v ===delete===\n", p)
244         }
245
246         obj.Nopout(p)
247
248         gc.Ostats.Ndelmov++
249 }
250
251 func regtyp(a *obj.Addr) bool {
252         if gc.Ctxt.Flag_shared && a.Type == obj.TYPE_REG && a.Reg == x86.REG_CX {
253                 // don't propagate CX, it is used implicitly by PIC global references
254                 return false
255         }
256         return a.Type == obj.TYPE_REG && (x86.REG_AX <= a.Reg && a.Reg <= x86.REG_DI || x86.REG_X0 <= a.Reg && a.Reg <= x86.REG_X7)
257 }
258
259 // movb elimination.
260 // movb is simulated by the linker
261 // when a register other than ax, bx, cx, dx
262 // is used, so rewrite to other instructions
263 // when possible.  a movb into a register
264 // can smash the entire 64-bit register without
265 // causing any trouble.
266 func elimshortmov(g *gc.Graph) {
267         var p *obj.Prog
268
269         for r := g.Start; r != nil; r = r.Link {
270                 p = r.Prog
271                 if regtyp(&p.To) {
272                         switch p.As {
273                         case x86.AINCB,
274                                 x86.AINCW:
275                                 p.As = x86.AINCL
276
277                         case x86.ADECB,
278                                 x86.ADECW:
279                                 p.As = x86.ADECL
280
281                         case x86.ANEGB,
282                                 x86.ANEGW:
283                                 p.As = x86.ANEGL
284
285                         case x86.ANOTB,
286                                 x86.ANOTW:
287                                 p.As = x86.ANOTL
288                         }
289
290                         if regtyp(&p.From) || p.From.Type == obj.TYPE_CONST {
291                                 // move or arithmetic into partial register.
292                                 // from another register or constant can be movl.
293                                 // we don't switch to 32-bit arithmetic if it can
294                                 // change how the carry bit is set (and the carry bit is needed).
295                                 switch p.As {
296                                 case x86.AMOVB,
297                                         x86.AMOVW:
298                                         p.As = x86.AMOVL
299
300                                 case x86.AADDB,
301                                         x86.AADDW:
302                                         if !needc(p.Link) {
303                                                 p.As = x86.AADDL
304                                         }
305
306                                 case x86.ASUBB,
307                                         x86.ASUBW:
308                                         if !needc(p.Link) {
309                                                 p.As = x86.ASUBL
310                                         }
311
312                                 case x86.AMULB,
313                                         x86.AMULW:
314                                         p.As = x86.AMULL
315
316                                 case x86.AIMULB,
317                                         x86.AIMULW:
318                                         p.As = x86.AIMULL
319
320                                 case x86.AANDB,
321                                         x86.AANDW:
322                                         p.As = x86.AANDL
323
324                                 case x86.AORB,
325                                         x86.AORW:
326                                         p.As = x86.AORL
327
328                                 case x86.AXORB,
329                                         x86.AXORW:
330                                         p.As = x86.AXORL
331
332                                 case x86.ASHLB,
333                                         x86.ASHLW:
334                                         p.As = x86.ASHLL
335                                 }
336                         } else {
337                                 // explicit zero extension
338                                 switch p.As {
339                                 case x86.AMOVB:
340                                         p.As = x86.AMOVBLZX
341
342                                 case x86.AMOVW:
343                                         p.As = x86.AMOVWLZX
344                                 }
345                         }
346                 }
347         }
348 }
349
350 /*
351  * the idea is to substitute
352  * one register for another
353  * from one MOV to another
354  *      MOV     a, R0
355  *      ADD     b, R0   / no use of R1
356  *      MOV     R0, R1
357  * would be converted to
358  *      MOV     a, R1
359  *      ADD     b, R1
360  *      MOV     R1, R0
361  * hopefully, then the former or latter MOV
362  * will be eliminated by copy propagation.
363  */
364 func subprop(r0 *gc.Flow) bool {
365         p := r0.Prog
366         v1 := &p.From
367         if !regtyp(v1) {
368                 return false
369         }
370         v2 := &p.To
371         if !regtyp(v2) {
372                 return false
373         }
374         for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) {
375                 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
376                         fmt.Printf("\t? %v\n", r.Prog)
377                 }
378                 if gc.Uniqs(r) == nil {
379                         break
380                 }
381                 p = r.Prog
382                 if p.As == obj.AVARDEF || p.As == obj.AVARKILL {
383                         continue
384                 }
385                 if p.Info.Flags&gc.Call != 0 {
386                         return false
387                 }
388
389                 if p.Info.Reguse|p.Info.Regset != 0 {
390                         return false
391                 }
392
393                 if (p.Info.Flags&gc.Move != 0) && (p.Info.Flags&(gc.SizeL|gc.SizeQ|gc.SizeF|gc.SizeD) != 0) && p.To.Type == v1.Type && p.To.Reg == v1.Reg {
394                         copysub(&p.To, v1, v2, true)
395                         if gc.Debug['P'] != 0 {
396                                 fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog)
397                                 if p.From.Type == v2.Type && p.From.Reg == v2.Reg {
398                                         fmt.Printf(" excise")
399                                 }
400                                 fmt.Printf("\n")
401                         }
402
403                         for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) {
404                                 p = r.Prog
405                                 copysub(&p.From, v1, v2, true)
406                                 copysub(&p.To, v1, v2, true)
407                                 if gc.Debug['P'] != 0 {
408                                         fmt.Printf("%v\n", r.Prog)
409                                 }
410                         }
411
412                         t := int(v1.Reg)
413                         v1.Reg = v2.Reg
414                         v2.Reg = int16(t)
415                         if gc.Debug['P'] != 0 {
416                                 fmt.Printf("%v last\n", r.Prog)
417                         }
418                         return true
419                 }
420
421                 if copyau(&p.From, v2) || copyau(&p.To, v2) {
422                         break
423                 }
424                 if copysub(&p.From, v1, v2, false) || copysub(&p.To, v1, v2, false) {
425                         break
426                 }
427         }
428
429         return false
430 }
431
432 /*
433  * The idea is to remove redundant copies.
434  *      v1->v2  F=0
435  *      (use v2 s/v2/v1/)*
436  *      set v1  F=1
437  *      use v2  return fail
438  *      -----------------
439  *      v1->v2  F=0
440  *      (use v2 s/v2/v1/)*
441  *      set v1  F=1
442  *      set v2  return success
443  */
444 func copyprop(g *gc.Graph, r0 *gc.Flow) bool {
445         p := r0.Prog
446         v1 := &p.From
447         v2 := &p.To
448         if copyas(v1, v2) {
449                 return true
450         }
451         gactive++
452         return copy1(v1, v2, r0.S1, false)
453 }
454
455 func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f bool) bool {
456         if uint32(r.Active) == gactive {
457                 if gc.Debug['P'] != 0 {
458                         fmt.Printf("act set; return 1\n")
459                 }
460                 return true
461         }
462
463         r.Active = int32(gactive)
464         if gc.Debug['P'] != 0 {
465                 fmt.Printf("copy %v->%v f=%v\n", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), f)
466         }
467         for ; r != nil; r = r.S1 {
468                 p := r.Prog
469                 if gc.Debug['P'] != 0 {
470                         fmt.Printf("%v", p)
471                 }
472                 if !f && gc.Uniqp(r) == nil {
473                         f = true
474                         if gc.Debug['P'] != 0 {
475                                 fmt.Printf("; merge; f=%v", f)
476                         }
477                 }
478
479                 switch t := copyu(p, v2, nil); t {
480                 case 2: /* rar, can't split */
481                         if gc.Debug['P'] != 0 {
482                                 fmt.Printf("; %v rar; return 0\n", gc.Ctxt.Dconv(v2))
483                         }
484                         return false
485
486                 case 3: /* set */
487                         if gc.Debug['P'] != 0 {
488                                 fmt.Printf("; %v set; return 1\n", gc.Ctxt.Dconv(v2))
489                         }
490                         return true
491
492                 case 1, /* used, substitute */
493                         4: /* use and set */
494                         if f {
495                                 if gc.Debug['P'] == 0 {
496                                         return false
497                                 }
498                                 if t == 4 {
499                                         fmt.Printf("; %v used+set and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f)
500                                 } else {
501                                         fmt.Printf("; %v used and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f)
502                                 }
503                                 return false
504                         }
505
506                         if copyu(p, v2, v1) != 0 {
507                                 if gc.Debug['P'] != 0 {
508                                         fmt.Printf("; sub fail; return 0\n")
509                                 }
510                                 return false
511                         }
512
513                         if gc.Debug['P'] != 0 {
514                                 fmt.Printf("; sub %v/%v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1))
515                         }
516                         if t == 4 {
517                                 if gc.Debug['P'] != 0 {
518                                         fmt.Printf("; %v used+set; return 1\n", gc.Ctxt.Dconv(v2))
519                                 }
520                                 return true
521                         }
522                 }
523
524                 if !f {
525                         t := copyu(p, v1, nil)
526                         if t == 2 || t == 3 || t == 4 {
527                                 f = true
528                                 if gc.Debug['P'] != 0 {
529                                         fmt.Printf("; %v set and !f; f=%v", gc.Ctxt.Dconv(v1), f)
530                                 }
531                         }
532                 }
533
534                 if gc.Debug['P'] != 0 {
535                         fmt.Printf("\n")
536                 }
537                 if r.S2 != nil {
538                         if !copy1(v1, v2, r.S2, f) {
539                                 return false
540                         }
541                 }
542         }
543         return true
544 }
545
546 /*
547  * return
548  * 1 if v only used (and substitute),
549  * 2 if read-alter-rewrite
550  * 3 if set
551  * 4 if set and used
552  * 0 otherwise (not touched)
553  */
554 func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int {
555         switch p.As {
556         case obj.AJMP:
557                 if s != nil {
558                         if copysub(&p.To, v, s, true) {
559                                 return 1
560                         }
561                         return 0
562                 }
563
564                 if copyau(&p.To, v) {
565                         return 1
566                 }
567                 return 0
568
569         case obj.ARET:
570                 if s != nil {
571                         return 1
572                 }
573                 return 3
574
575         case obj.ACALL:
576                 if REGEXT != 0 /*TypeKind(100016)*/ && v.Type == obj.TYPE_REG && v.Reg <= REGEXT && v.Reg > exregoffset {
577                         return 2
578                 }
579                 if x86.REGARG >= 0 && v.Type == obj.TYPE_REG && v.Reg == x86.REGARG {
580                         return 2
581                 }
582                 if v.Type == p.From.Type && v.Reg == p.From.Reg {
583                         return 2
584                 }
585
586                 if s != nil {
587                         if copysub(&p.To, v, s, true) {
588                                 return 1
589                         }
590                         return 0
591                 }
592
593                 if copyau(&p.To, v) {
594                         return 4
595                 }
596                 return 3
597
598         case obj.ATEXT:
599                 if x86.REGARG >= 0 && v.Type == obj.TYPE_REG && v.Reg == x86.REGARG {
600                         return 3
601                 }
602                 return 0
603         }
604
605         if p.As == obj.AVARDEF || p.As == obj.AVARKILL {
606                 return 0
607         }
608
609         if (p.Info.Reguse|p.Info.Regset)&RtoB(int(v.Reg)) != 0 {
610                 return 2
611         }
612
613         if p.Info.Flags&gc.LeftAddr != 0 {
614                 if copyas(&p.From, v) {
615                         return 2
616                 }
617         }
618
619         if p.Info.Flags&(gc.RightRead|gc.RightWrite) == gc.RightRead|gc.RightWrite {
620                 if copyas(&p.To, v) {
621                         return 2
622                 }
623         }
624
625         if p.Info.Flags&gc.RightWrite != 0 {
626                 if copyas(&p.To, v) {
627                         if s != nil {
628                                 if copysub(&p.From, v, s, true) {
629                                         return 1
630                                 }
631                                 return 0
632                         }
633                         if copyau(&p.From, v) {
634                                 return 4
635                         }
636                         return 3
637                 }
638         }
639
640         if p.Info.Flags&(gc.LeftAddr|gc.LeftRead|gc.LeftWrite|gc.RightAddr|gc.RightRead|gc.RightWrite) != 0 {
641                 if s != nil {
642                         if copysub(&p.From, v, s, true) {
643                                 return 1
644                         }
645                         if copysub(&p.To, v, s, true) {
646                                 return 1
647                         }
648                         return 0
649                 }
650                 if copyau(&p.From, v) {
651                         return 1
652                 }
653                 if copyau(&p.To, v) {
654                         return 1
655                 }
656         }
657         return 0
658 }
659
660 /*
661  * direct reference,
662  * could be set/use depending on
663  * semantics
664  */
665 func copyas(a *obj.Addr, v *obj.Addr) bool {
666         if x86.REG_AL <= a.Reg && a.Reg <= x86.REG_BL {
667                 gc.Fatalf("use of byte register")
668         }
669         if x86.REG_AL <= v.Reg && v.Reg <= x86.REG_BL {
670                 gc.Fatalf("use of byte register")
671         }
672
673         if a.Type != v.Type || a.Name != v.Name || a.Reg != v.Reg {
674                 return false
675         }
676         if regtyp(v) {
677                 return true
678         }
679         if (v.Type == obj.TYPE_MEM || v.Type == obj.TYPE_ADDR) && (v.Name == obj.NAME_AUTO || v.Name == obj.NAME_PARAM) {
680                 if v.Offset == a.Offset {
681                         return true
682                 }
683         }
684         return false
685 }
686
687 func sameaddr(a *obj.Addr, v *obj.Addr) bool {
688         if a.Type != v.Type || a.Name != v.Name || a.Reg != v.Reg {
689                 return false
690         }
691         if regtyp(v) {
692                 return true
693         }
694         if (v.Type == obj.TYPE_MEM || v.Type == obj.TYPE_ADDR) && (v.Name == obj.NAME_AUTO || v.Name == obj.NAME_PARAM) {
695                 if v.Offset == a.Offset {
696                         return true
697                 }
698         }
699         return false
700 }
701
702 /*
703  * either direct or indirect
704  */
705 func copyau(a *obj.Addr, v *obj.Addr) bool {
706         if copyas(a, v) {
707                 return true
708         }
709         if regtyp(v) {
710                 if (a.Type == obj.TYPE_MEM || a.Type == obj.TYPE_ADDR) && a.Reg == v.Reg {
711                         return true
712                 }
713                 if a.Index == v.Reg {
714                         return true
715                 }
716         }
717
718         return false
719 }
720
721 // copysub substitute s for v in a.
722 // copysub returns true on failure to substitute.
723 // TODO(dfc) reverse this logic to return false on sunstitution failure.
724 func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f bool) bool {
725         if copyas(a, v) {
726                 if s.Reg >= x86.REG_AX && s.Reg <= x86.REG_DI || s.Reg >= x86.REG_X0 && s.Reg <= x86.REG_X7 {
727                         if f {
728                                 a.Reg = s.Reg
729                         }
730                 }
731                 return false
732         }
733
734         if regtyp(v) {
735                 if (a.Type == obj.TYPE_MEM || a.Type == obj.TYPE_ADDR) && a.Reg == v.Reg {
736                         if (s.Reg == x86.REG_BP) && a.Index != x86.REG_NONE {
737                                 return true /* can't use BP-base with index */
738                         }
739                         if f {
740                                 a.Reg = s.Reg
741                         }
742                 }
743
744                 if a.Index == v.Reg {
745                         if f {
746                                 a.Index = s.Reg
747                         }
748                 }
749         }
750         return false
751 }
752
753 func conprop(r0 *gc.Flow) {
754         var p *obj.Prog
755
756         p0 := r0.Prog
757         v0 := &p0.To
758         r := r0
759
760 loop:
761         r = gc.Uniqs(r)
762         if r == nil || r == r0 {
763                 return
764         }
765         if gc.Uniqp(r) == nil {
766                 return
767         }
768
769         p = r.Prog
770         switch copyu(p, v0, nil) {
771         case 0, // miss
772                 1: // use
773                 goto loop
774
775         case 2, // rar
776                 4: // use and set
777                 break
778
779         case 3: // set
780                 if p.As == p0.As {
781                         if p.From.Type == p0.From.Type {
782                                 if p.From.Reg == p0.From.Reg {
783                                         if p.From.Node == p0.From.Node {
784                                                 if p.From.Offset == p0.From.Offset {
785                                                         if p.From.Scale == p0.From.Scale {
786                                                                 if p.From.Type == obj.TYPE_FCONST && p.From.Val.(float64) == p0.From.Val.(float64) {
787                                                                         if p.From.Index == p0.From.Index {
788                                                                                 excise(r)
789                                                                                 goto loop
790                                                                         }
791                                                                 }
792                                                         }
793                                                 }
794                                         }
795                                 }
796                         }
797                 }
798         }
799 }
800
801 func smallindir(a *obj.Addr, reg *obj.Addr) bool {
802         return regtyp(reg) && a.Type == obj.TYPE_MEM && a.Reg == reg.Reg && a.Index == x86.REG_NONE && 0 <= a.Offset && a.Offset < 4096
803 }
804
805 func stackaddr(a *obj.Addr) bool {
806         return a.Type == obj.TYPE_REG && a.Reg == x86.REG_SP
807 }