1 // Derived from Inferno utils/6c/peep.c
2 // http://code.google.com/p/inferno-os/source/browse/utils/6c/peep.c
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.
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:
20 // The above copyright notice and this permission notice shall be included in
21 // all copies or substantial portions of the Software.
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
34 "cmd/compile/internal/gc"
36 "cmd/internal/obj/x86"
42 exregoffset = x86.REG_DI
47 // do we need the carry bit
48 func needc(p *obj.Prog) bool {
50 if p.Info.Flags&gc.UseCarry != 0 {
53 if p.Info.Flags&(gc.SetCarry|gc.KillCarry) != 0 {
62 func rnops(r *gc.Flow) *gc.Flow {
68 if p.As != obj.ANOP || p.From.Type != obj.TYPE_NONE || p.To.Type != obj.TYPE_NONE {
82 func peep(firstp *obj.Prog) {
83 g := gc.Flowstart(firstp, nil)
89 // byte, word arithmetic elimination.
92 // constant propagation
93 // find MOV $con,R followed by
94 // another MOV $con,R without
95 // setting R in the interim
97 for r := g.Start; r != nil; r = r.Link {
102 if p.From.Sym != nil {
103 if p.From.Index == x86.REG_NONE {
115 if p.From.Type == obj.TYPE_CONST || p.From.Type == obj.TYPE_FCONST {
127 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
128 gc.Dumpit("loop1", g.Start, 0)
132 for r = g.Start; r != nil; r = r.Link {
143 } else if subprop(r) && copyprop(g, r) {
155 r1 = rnops(gc.Uniqs(r))
158 if p.As == p1.As && p.To.Type == p1.From.Type && p.To.Reg == p1.From.Reg {
167 if p.From.Type != obj.TYPE_CONST || needc(p.Link) {
170 if p.From.Offset == -1 {
171 if p.As == x86.AADDL {
180 if p.From.Offset == 1 {
181 if p.As == x86.AADDL {
192 if p.From.Type != obj.TYPE_CONST || needc(p.Link) {
195 if p.From.Offset == -1 {
196 if p.As == x86.ASUBL {
205 if p.From.Offset == 1 {
206 if p.As == x86.ASUBL {
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 {
228 if p.As == x86.AMOVSD {
240 func excise(r *gc.Flow) {
242 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
243 fmt.Printf("%v ===delete===\n", p)
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
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)
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) {
269 for r := g.Start; r != nil; r = r.Link {
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).
337 // explicit zero extension
351 * the idea is to substitute
352 * one register for another
353 * from one MOV to another
355 * ADD b, R0 / no use of R1
357 * would be converted to
361 * hopefully, then the former or latter MOV
362 * will be eliminated by copy propagation.
364 func subprop(r0 *gc.Flow) bool {
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)
378 if gc.Uniqs(r) == nil {
382 if p.As == obj.AVARDEF || p.As == obj.AVARKILL {
385 if p.Info.Flags&gc.Call != 0 {
389 if p.Info.Reguse|p.Info.Regset != 0 {
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")
403 for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) {
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)
415 if gc.Debug['P'] != 0 {
416 fmt.Printf("%v last\n", r.Prog)
421 if copyau(&p.From, v2) || copyau(&p.To, v2) {
424 if copysub(&p.From, v1, v2, false) || copysub(&p.To, v1, v2, false) {
433 * The idea is to remove redundant copies.
442 * set v2 return success
444 func copyprop(g *gc.Graph, r0 *gc.Flow) bool {
452 return copy1(v1, v2, r0.S1, false)
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")
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)
467 for ; r != nil; r = r.S1 {
469 if gc.Debug['P'] != 0 {
472 if !f && gc.Uniqp(r) == nil {
474 if gc.Debug['P'] != 0 {
475 fmt.Printf("; merge; f=%v", f)
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))
487 if gc.Debug['P'] != 0 {
488 fmt.Printf("; %v set; return 1\n", gc.Ctxt.Dconv(v2))
492 case 1, /* used, substitute */
495 if gc.Debug['P'] == 0 {
499 fmt.Printf("; %v used+set and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f)
501 fmt.Printf("; %v used and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f)
506 if copyu(p, v2, v1) != 0 {
507 if gc.Debug['P'] != 0 {
508 fmt.Printf("; sub fail; return 0\n")
513 if gc.Debug['P'] != 0 {
514 fmt.Printf("; sub %v/%v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1))
517 if gc.Debug['P'] != 0 {
518 fmt.Printf("; %v used+set; return 1\n", gc.Ctxt.Dconv(v2))
525 t := copyu(p, v1, nil)
526 if t == 2 || t == 3 || t == 4 {
528 if gc.Debug['P'] != 0 {
529 fmt.Printf("; %v set and !f; f=%v", gc.Ctxt.Dconv(v1), f)
534 if gc.Debug['P'] != 0 {
538 if !copy1(v1, v2, r.S2, f) {
548 * 1 if v only used (and substitute),
549 * 2 if read-alter-rewrite
552 * 0 otherwise (not touched)
554 func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int {
558 if copysub(&p.To, v, s, true) {
564 if copyau(&p.To, v) {
576 if REGEXT != 0 /*TypeKind(100016)*/ && v.Type == obj.TYPE_REG && v.Reg <= REGEXT && v.Reg > exregoffset {
579 if x86.REGARG >= 0 && v.Type == obj.TYPE_REG && v.Reg == x86.REGARG {
582 if v.Type == p.From.Type && v.Reg == p.From.Reg {
587 if copysub(&p.To, v, s, true) {
593 if copyau(&p.To, v) {
599 if x86.REGARG >= 0 && v.Type == obj.TYPE_REG && v.Reg == x86.REGARG {
605 if p.As == obj.AVARDEF || p.As == obj.AVARKILL {
609 if (p.Info.Reguse|p.Info.Regset)&RtoB(int(v.Reg)) != 0 {
613 if p.Info.Flags&gc.LeftAddr != 0 {
614 if copyas(&p.From, v) {
619 if p.Info.Flags&(gc.RightRead|gc.RightWrite) == gc.RightRead|gc.RightWrite {
620 if copyas(&p.To, v) {
625 if p.Info.Flags&gc.RightWrite != 0 {
626 if copyas(&p.To, v) {
628 if copysub(&p.From, v, s, true) {
633 if copyau(&p.From, v) {
640 if p.Info.Flags&(gc.LeftAddr|gc.LeftRead|gc.LeftWrite|gc.RightAddr|gc.RightRead|gc.RightWrite) != 0 {
642 if copysub(&p.From, v, s, true) {
645 if copysub(&p.To, v, s, true) {
650 if copyau(&p.From, v) {
653 if copyau(&p.To, v) {
662 * could be set/use depending on
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")
669 if x86.REG_AL <= v.Reg && v.Reg <= x86.REG_BL {
670 gc.Fatalf("use of byte register")
673 if a.Type != v.Type || a.Name != v.Name || a.Reg != v.Reg {
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 {
687 func sameaddr(a *obj.Addr, v *obj.Addr) bool {
688 if a.Type != v.Type || a.Name != v.Name || a.Reg != v.Reg {
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 {
703 * either direct or indirect
705 func copyau(a *obj.Addr, v *obj.Addr) bool {
710 if (a.Type == obj.TYPE_MEM || a.Type == obj.TYPE_ADDR) && a.Reg == v.Reg {
713 if a.Index == v.Reg {
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 {
726 if s.Reg >= x86.REG_AX && s.Reg <= x86.REG_DI || s.Reg >= x86.REG_X0 && s.Reg <= x86.REG_X7 {
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 */
744 if a.Index == v.Reg {
753 func conprop(r0 *gc.Flow) {
762 if r == nil || r == r0 {
765 if gc.Uniqp(r) == nil {
770 switch copyu(p, v0, nil) {
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 {
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
805 func stackaddr(a *obj.Addr) bool {
806 return a.Type == obj.TYPE_REG && a.Reg == x86.REG_SP