aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/6g/peep.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/6g/peep.go')
-rw-r--r--src/cmd/6g/peep.go1077
1 files changed, 1077 insertions, 0 deletions
diff --git a/src/cmd/6g/peep.go b/src/cmd/6g/peep.go
new file mode 100644
index 0000000000..9870ca5e4e
--- /dev/null
+++ b/src/cmd/6g/peep.go
@@ -0,0 +1,1077 @@
+// Derived from Inferno utils/6c/peep.c
+// http://code.google.com/p/inferno-os/source/browse/utils/6c/peep.c
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package main
+
+import (
+ "cmd/internal/obj"
+ "cmd/internal/obj/x86"
+ "fmt"
+)
+import "cmd/internal/gc"
+
+var gactive uint32
+
+const (
+ exregoffset = x86.REG_R15
+)
+
+// do we need the carry bit
+func needc(p *obj.Prog) bool {
+ var info gc.ProgInfo
+
+ for p != nil {
+ proginfo(&info, p)
+ if info.Flags&gc.UseCarry != 0 {
+ return true
+ }
+ if info.Flags&(gc.SetCarry|gc.KillCarry) != 0 {
+ return false
+ }
+ p = p.Link
+ }
+
+ return false
+}
+
+func rnops(r *gc.Flow) *gc.Flow {
+ var p *obj.Prog
+ var r1 *gc.Flow
+
+ if r != nil {
+ for {
+ p = r.Prog
+ if p.As != obj.ANOP || p.From.Type != obj.TYPE_NONE || p.To.Type != obj.TYPE_NONE {
+ break
+ }
+ r1 = gc.Uniqs(r)
+ if r1 == nil {
+ break
+ }
+ r = r1
+ }
+ }
+
+ return r
+}
+
+func peep(firstp *obj.Prog) {
+ var r *gc.Flow
+ var r1 *gc.Flow
+ var g *gc.Graph
+ var p *obj.Prog
+ var p1 *obj.Prog
+ var t int
+
+ g = gc.Flowstart(firstp, nil)
+ if g == nil {
+ return
+ }
+ gactive = 0
+
+ // byte, word arithmetic elimination.
+ elimshortmov(g)
+
+ // constant propagation
+ // find MOV $con,R followed by
+ // another MOV $con,R without
+ // setting R in the interim
+ for r = g.Start; r != nil; r = r.Link {
+ p = r.Prog
+ switch p.As {
+ case x86.ALEAL,
+ x86.ALEAQ:
+ if regtyp(&p.To) {
+ if p.From.Sym != nil {
+ if p.From.Index == x86.REG_NONE {
+ conprop(r)
+ }
+ }
+ }
+
+ case x86.AMOVB,
+ x86.AMOVW,
+ x86.AMOVL,
+ x86.AMOVQ,
+ x86.AMOVSS,
+ x86.AMOVSD:
+ if regtyp(&p.To) {
+ if p.From.Type == obj.TYPE_CONST || p.From.Type == obj.TYPE_FCONST {
+ conprop(r)
+ }
+ }
+ }
+ }
+
+loop1:
+ if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
+ gc.Dumpit("loop1", g.Start, 0)
+ }
+
+ t = 0
+ for r = g.Start; r != nil; r = r.Link {
+ p = r.Prog
+ switch p.As {
+ case x86.AMOVL,
+ x86.AMOVQ,
+ x86.AMOVSS,
+ x86.AMOVSD:
+ if regtyp(&p.To) {
+ if regtyp(&p.From) {
+ if copyprop(g, r) {
+ excise(r)
+ t++
+ } else if subprop(r) && copyprop(g, r) {
+ excise(r)
+ t++
+ }
+ }
+ }
+
+ case x86.AMOVBLZX,
+ x86.AMOVWLZX,
+ x86.AMOVBLSX,
+ x86.AMOVWLSX:
+ if regtyp(&p.To) {
+ r1 = rnops(gc.Uniqs(r))
+ if r1 != nil {
+ p1 = r1.Prog
+ if p.As == p1.As && p.To.Type == p1.From.Type && p.To.Reg == p1.From.Reg {
+ p1.As = x86.AMOVL
+ t++
+ }
+ }
+ }
+
+ case x86.AMOVBQSX,
+ x86.AMOVBQZX,
+ x86.AMOVWQSX,
+ x86.AMOVWQZX,
+ x86.AMOVLQSX,
+ x86.AMOVLQZX,
+ x86.AMOVQL:
+ if regtyp(&p.To) {
+ r1 = rnops(gc.Uniqs(r))
+ if r1 != nil {
+ p1 = r1.Prog
+ if p.As == p1.As && p.To.Type == p1.From.Type && p.To.Reg == p1.From.Reg {
+ p1.As = x86.AMOVQ
+ t++
+ }
+ }
+ }
+
+ case x86.AADDL,
+ x86.AADDQ,
+ x86.AADDW:
+ if p.From.Type != obj.TYPE_CONST || needc(p.Link) {
+ break
+ }
+ if p.From.Offset == -1 {
+ if p.As == x86.AADDQ {
+ p.As = x86.ADECQ
+ } else if p.As == x86.AADDL {
+ p.As = x86.ADECL
+ } else {
+ p.As = x86.ADECW
+ }
+ p.From = obj.Addr{}
+ break
+ }
+
+ if p.From.Offset == 1 {
+ if p.As == x86.AADDQ {
+ p.As = x86.AINCQ
+ } else if p.As == x86.AADDL {
+ p.As = x86.AINCL
+ } else {
+ p.As = x86.AINCW
+ }
+ p.From = obj.Addr{}
+ break
+ }
+
+ case x86.ASUBL,
+ x86.ASUBQ,
+ x86.ASUBW:
+ if p.From.Type != obj.TYPE_CONST || needc(p.Link) {
+ break
+ }
+ if p.From.Offset == -1 {
+ if p.As == x86.ASUBQ {
+ p.As = x86.AINCQ
+ } else if p.As == x86.ASUBL {
+ p.As = x86.AINCL
+ } else {
+ p.As = x86.AINCW
+ }
+ p.From = obj.Addr{}
+ break
+ }
+
+ if p.From.Offset == 1 {
+ if p.As == x86.ASUBQ {
+ p.As = x86.ADECQ
+ } else if p.As == x86.ASUBL {
+ p.As = x86.ADECL
+ } else {
+ p.As = x86.ADECW
+ }
+ p.From = obj.Addr{}
+ break
+ }
+ }
+ }
+
+ if t != 0 {
+ goto loop1
+ }
+
+ // MOVLQZX removal.
+ // The MOVLQZX exists to avoid being confused for a
+ // MOVL that is just copying 32-bit data around during
+ // copyprop. Now that copyprop is done, remov MOVLQZX R1, R2
+ // if it is dominated by an earlier ADDL/MOVL/etc into R1 that
+ // will have already cleared the high bits.
+ //
+ // MOVSD removal.
+ // We never use packed registers, so a MOVSD between registers
+ // can be replaced by MOVAPD, which moves the pair of float64s
+ // instead of just the lower one. We only use the lower one, but
+ // the processor can do better if we do moves using both.
+ for r = g.Start; r != nil; r = r.Link {
+ p = r.Prog
+ if p.As == x86.AMOVLQZX {
+ if regtyp(&p.From) {
+ if p.From.Type == p.To.Type && p.From.Reg == p.To.Reg {
+ if prevl(r, int(p.From.Reg)) {
+ excise(r)
+ }
+ }
+ }
+ }
+
+ if p.As == x86.AMOVSD {
+ if regtyp(&p.From) {
+ if regtyp(&p.To) {
+ p.As = x86.AMOVAPD
+ }
+ }
+ }
+ }
+
+ // load pipelining
+ // push any load from memory as early as possible
+ // to give it time to complete before use.
+ for r = g.Start; r != nil; r = r.Link {
+ p = r.Prog
+ switch p.As {
+ case x86.AMOVB,
+ x86.AMOVW,
+ x86.AMOVL,
+ x86.AMOVQ,
+ x86.AMOVLQZX:
+ if regtyp(&p.To) && !regconsttyp(&p.From) {
+ pushback(r)
+ }
+ }
+ }
+
+ gc.Flowend(g)
+}
+
+func pushback(r0 *gc.Flow) {
+ var r *gc.Flow
+ var b *gc.Flow
+ var p0 *obj.Prog
+ var p *obj.Prog
+ var t obj.Prog
+
+ b = nil
+ p0 = r0.Prog
+ for r = gc.Uniqp(r0); r != nil && gc.Uniqs(r) != nil; r = gc.Uniqp(r) {
+ p = r.Prog
+ if p.As != obj.ANOP {
+ if !regconsttyp(&p.From) || !regtyp(&p.To) {
+ break
+ }
+ if copyu(p, &p0.To, nil) != 0 || copyu(p0, &p.To, nil) != 0 {
+ break
+ }
+ }
+
+ if p.As == obj.ACALL {
+ break
+ }
+ b = r
+ }
+
+ if b == nil {
+ if gc.Debug['v'] != 0 {
+ fmt.Printf("no pushback: %v\n", r0.Prog)
+ if r != nil {
+ fmt.Printf("\t%v [%d]\n", r.Prog, gc.Uniqs(r) != nil)
+ }
+ }
+
+ return
+ }
+
+ if gc.Debug['v'] != 0 {
+ fmt.Printf("pushback\n")
+ for r = b; ; r = r.Link {
+ fmt.Printf("\t%v\n", r.Prog)
+ if r == r0 {
+ break
+ }
+ }
+ }
+
+ t = *r0.Prog
+ for r = gc.Uniqp(r0); ; r = gc.Uniqp(r) {
+ p0 = r.Link.Prog
+ p = r.Prog
+ p0.As = p.As
+ p0.Lineno = p.Lineno
+ p0.From = p.From
+ p0.To = p.To
+
+ if r == b {
+ break
+ }
+ }
+
+ p0 = r.Prog
+ p0.As = t.As
+ p0.Lineno = t.Lineno
+ p0.From = t.From
+ p0.To = t.To
+
+ if gc.Debug['v'] != 0 {
+ fmt.Printf("\tafter\n")
+ for r = b; ; r = r.Link {
+ fmt.Printf("\t%v\n", r.Prog)
+ if r == r0 {
+ break
+ }
+ }
+ }
+}
+
+func excise(r *gc.Flow) {
+ var p *obj.Prog
+
+ p = r.Prog
+ if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
+ fmt.Printf("%v ===delete===\n", p)
+ }
+
+ obj.Nopout(p)
+
+ gc.Ostats.Ndelmov++
+}
+
+func regtyp(a *obj.Addr) bool {
+ return a.Type == obj.TYPE_REG && (x86.REG_AX <= a.Reg && a.Reg <= x86.REG_R15 || x86.REG_X0 <= a.Reg && a.Reg <= x86.REG_X15)
+}
+
+// movb elimination.
+// movb is simulated by the linker
+// when a register other than ax, bx, cx, dx
+// is used, so rewrite to other instructions
+// when possible. a movb into a register
+// can smash the entire 32-bit register without
+// causing any trouble.
+//
+// TODO: Using the Q forms here instead of the L forms
+// seems unnecessary, and it makes the instructions longer.
+func elimshortmov(g *gc.Graph) {
+ var p *obj.Prog
+ var r *gc.Flow
+
+ for r = g.Start; r != nil; r = r.Link {
+ p = r.Prog
+ if regtyp(&p.To) {
+ switch p.As {
+ case x86.AINCB,
+ x86.AINCW:
+ p.As = x86.AINCQ
+
+ case x86.ADECB,
+ x86.ADECW:
+ p.As = x86.ADECQ
+
+ case x86.ANEGB,
+ x86.ANEGW:
+ p.As = x86.ANEGQ
+
+ case x86.ANOTB,
+ x86.ANOTW:
+ p.As = x86.ANOTQ
+ }
+
+ if regtyp(&p.From) || p.From.Type == obj.TYPE_CONST {
+ // move or artihmetic into partial register.
+ // from another register or constant can be movl.
+ // we don't switch to 64-bit arithmetic if it can
+ // change how the carry bit is set (and the carry bit is needed).
+ switch p.As {
+ case x86.AMOVB,
+ x86.AMOVW:
+ p.As = x86.AMOVQ
+
+ case x86.AADDB,
+ x86.AADDW:
+ if !needc(p.Link) {
+ p.As = x86.AADDQ
+ }
+
+ case x86.ASUBB,
+ x86.ASUBW:
+ if !needc(p.Link) {
+ p.As = x86.ASUBQ
+ }
+
+ case x86.AMULB,
+ x86.AMULW:
+ p.As = x86.AMULQ
+
+ case x86.AIMULB,
+ x86.AIMULW:
+ p.As = x86.AIMULQ
+
+ case x86.AANDB,
+ x86.AANDW:
+ p.As = x86.AANDQ
+
+ case x86.AORB,
+ x86.AORW:
+ p.As = x86.AORQ
+
+ case x86.AXORB,
+ x86.AXORW:
+ p.As = x86.AXORQ
+
+ case x86.ASHLB,
+ x86.ASHLW:
+ p.As = x86.ASHLQ
+ }
+ } else if p.From.Type != obj.TYPE_REG {
+ // explicit zero extension, but don't
+ // do that if source is a byte register
+ // (only AH can occur and it's forbidden).
+ switch p.As {
+ case x86.AMOVB:
+ p.As = x86.AMOVBQZX
+
+ case x86.AMOVW:
+ p.As = x86.AMOVWQZX
+ }
+ }
+ }
+ }
+}
+
+// is 'a' a register or constant?
+func regconsttyp(a *obj.Addr) bool {
+ if regtyp(a) {
+ return true
+ }
+ switch a.Type {
+ case obj.TYPE_CONST,
+ obj.TYPE_FCONST,
+ obj.TYPE_SCONST,
+ obj.TYPE_ADDR: // TODO(rsc): Not all TYPE_ADDRs are constants.
+ return true
+ }
+
+ return false
+}
+
+// is reg guaranteed to be truncated by a previous L instruction?
+func prevl(r0 *gc.Flow, reg int) bool {
+ var p *obj.Prog
+ var r *gc.Flow
+ var info gc.ProgInfo
+
+ for r = gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) {
+ p = r.Prog
+ if p.To.Type == obj.TYPE_REG && int(p.To.Reg) == reg {
+ proginfo(&info, p)
+ if info.Flags&gc.RightWrite != 0 {
+ if info.Flags&gc.SizeL != 0 {
+ return true
+ }
+ return false
+ }
+ }
+ }
+
+ return false
+}
+
+/*
+ * the idea is to substitute
+ * one register for another
+ * from one MOV to another
+ * MOV a, R0
+ * ADD b, R0 / no use of R1
+ * MOV R0, R1
+ * would be converted to
+ * MOV a, R1
+ * ADD b, R1
+ * MOV R1, R0
+ * hopefully, then the former or latter MOV
+ * will be eliminated by copy propagation.
+ */
+func subprop(r0 *gc.Flow) bool {
+ var p *obj.Prog
+ var info gc.ProgInfo
+ var v1 *obj.Addr
+ var v2 *obj.Addr
+ var r *gc.Flow
+ var t int
+
+ if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
+ fmt.Printf("subprop %v\n", r0.Prog)
+ }
+ p = r0.Prog
+ v1 = &p.From
+ if !regtyp(v1) {
+ if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
+ fmt.Printf("\tnot regtype %v; return 0\n", gc.Ctxt.Dconv(v1))
+ }
+ return false
+ }
+
+ v2 = &p.To
+ if !regtyp(v2) {
+ if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
+ fmt.Printf("\tnot regtype %v; return 0\n", gc.Ctxt.Dconv(v2))
+ }
+ return false
+ }
+
+ for r = gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) {
+ if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
+ fmt.Printf("\t? %v\n", r.Prog)
+ }
+ if gc.Uniqs(r) == nil {
+ if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
+ fmt.Printf("\tno unique successor\n")
+ }
+ break
+ }
+
+ p = r.Prog
+ if p.As == obj.AVARDEF || p.As == obj.AVARKILL {
+ continue
+ }
+ proginfo(&info, p)
+ if info.Flags&gc.Call != 0 {
+ if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
+ fmt.Printf("\tfound %v; return 0\n", p)
+ }
+ return false
+ }
+
+ if info.Reguse|info.Regset != 0 {
+ if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
+ fmt.Printf("\tfound %v; return 0\n", p)
+ }
+ return false
+ }
+
+ if (info.Flags&gc.Move != 0) && (info.Flags&(gc.SizeL|gc.SizeQ|gc.SizeF|gc.SizeD) != 0) && p.To.Type == v1.Type && p.To.Reg == v1.Reg {
+ goto gotit
+ }
+
+ if copyau(&p.From, v2) || copyau(&p.To, v2) {
+ if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
+ fmt.Printf("\tcopyau %v failed\n", gc.Ctxt.Dconv(v2))
+ }
+ break
+ }
+
+ if copysub(&p.From, v1, v2, 0) != 0 || copysub(&p.To, v1, v2, 0) != 0 {
+ if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
+ fmt.Printf("\tcopysub failed\n")
+ }
+ break
+ }
+ }
+
+ if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
+ fmt.Printf("\tran off end; return 0\n")
+ }
+ return false
+
+gotit:
+ copysub(&p.To, v1, v2, 1)
+ if gc.Debug['P'] != 0 {
+ fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog)
+ if p.From.Type == v2.Type && p.From.Reg == v2.Reg {
+ fmt.Printf(" excise")
+ }
+ fmt.Printf("\n")
+ }
+
+ for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) {
+ p = r.Prog
+ copysub(&p.From, v1, v2, 1)
+ copysub(&p.To, v1, v2, 1)
+ if gc.Debug['P'] != 0 {
+ fmt.Printf("%v\n", r.Prog)
+ }
+ }
+
+ t = int(v1.Reg)
+ v1.Reg = v2.Reg
+ v2.Reg = int16(t)
+ if gc.Debug['P'] != 0 {
+ fmt.Printf("%v last\n", r.Prog)
+ }
+ return true
+}
+
+/*
+ * The idea is to remove redundant copies.
+ * v1->v2 F=0
+ * (use v2 s/v2/v1/)*
+ * set v1 F=1
+ * use v2 return fail
+ * -----------------
+ * v1->v2 F=0
+ * (use v2 s/v2/v1/)*
+ * set v1 F=1
+ * set v2 return success
+ */
+func copyprop(g *gc.Graph, r0 *gc.Flow) bool {
+ var p *obj.Prog
+ var v1 *obj.Addr
+ var v2 *obj.Addr
+
+ if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
+ fmt.Printf("copyprop %v\n", r0.Prog)
+ }
+ p = r0.Prog
+ v1 = &p.From
+ v2 = &p.To
+ if copyas(v1, v2) {
+ return true
+ }
+ gactive++
+ return copy1(v1, v2, r0.S1, 0)
+}
+
+func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f int) bool {
+ var t int
+ var p *obj.Prog
+
+ if uint32(r.Active) == gactive {
+ if gc.Debug['P'] != 0 {
+ fmt.Printf("act set; return 1\n")
+ }
+ return true
+ }
+
+ r.Active = int32(gactive)
+ if gc.Debug['P'] != 0 {
+ fmt.Printf("copy %v->%v f=%d\n", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), f)
+ }
+ for ; r != nil; r = r.S1 {
+ p = r.Prog
+ if gc.Debug['P'] != 0 {
+ fmt.Printf("%v", p)
+ }
+ if f == 0 && gc.Uniqp(r) == nil {
+ f = 1
+ if gc.Debug['P'] != 0 {
+ fmt.Printf("; merge; f=%d", f)
+ }
+ }
+
+ t = copyu(p, v2, nil)
+ switch t {
+ case 2: /* rar, can't split */
+ if gc.Debug['P'] != 0 {
+ fmt.Printf("; %v rar; return 0\n", gc.Ctxt.Dconv(v2))
+ }
+ return false
+
+ case 3: /* set */
+ if gc.Debug['P'] != 0 {
+ fmt.Printf("; %v set; return 1\n", gc.Ctxt.Dconv(v2))
+ }
+ return true
+
+ case 1, /* used, substitute */
+ 4: /* use and set */
+ if f != 0 {
+ if gc.Debug['P'] == 0 {
+ return false
+ }
+ if t == 4 {
+ fmt.Printf("; %v used+set and f=%d; return 0\n", gc.Ctxt.Dconv(v2), f)
+ } else {
+ fmt.Printf("; %v used and f=%d; return 0\n", gc.Ctxt.Dconv(v2), f)
+ }
+ return false
+ }
+
+ if copyu(p, v2, v1) != 0 {
+ if gc.Debug['P'] != 0 {
+ fmt.Printf("; sub fail; return 0\n")
+ }
+ return false
+ }
+
+ if gc.Debug['P'] != 0 {
+ fmt.Printf("; sub %v/%v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1))
+ }
+ if t == 4 {
+ if gc.Debug['P'] != 0 {
+ fmt.Printf("; %v used+set; return 1\n", gc.Ctxt.Dconv(v2))
+ }
+ return true
+ }
+ }
+
+ if f == 0 {
+ t = copyu(p, v1, nil)
+ if f == 0 && (t == 2 || t == 3 || t == 4) {
+ f = 1
+ if gc.Debug['P'] != 0 {
+ fmt.Printf("; %v set and !f; f=%d", gc.Ctxt.Dconv(v1), f)
+ }
+ }
+ }
+
+ if gc.Debug['P'] != 0 {
+ fmt.Printf("\n")
+ }
+ if r.S2 != nil {
+ if !copy1(v1, v2, r.S2, f) {
+ return false
+ }
+ }
+ }
+
+ return true
+}
+
+/*
+ * return
+ * 1 if v only used (and substitute),
+ * 2 if read-alter-rewrite
+ * 3 if set
+ * 4 if set and used
+ * 0 otherwise (not touched)
+ */
+func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int {
+ var info gc.ProgInfo
+
+ switch p.As {
+ case obj.AJMP:
+ if s != nil {
+ if copysub(&p.To, v, s, 1) != 0 {
+ return 1
+ }
+ return 0
+ }
+
+ if copyau(&p.To, v) {
+ return 1
+ }
+ return 0
+
+ case obj.ARET:
+ if s != nil {
+ return 1
+ }
+ return 3
+
+ case obj.ACALL:
+ if x86.REGEXT != 0 /*TypeKind(100016)*/ && v.Type == obj.TYPE_REG && v.Reg <= x86.REGEXT && v.Reg > exregoffset {
+ return 2
+ }
+ if x86.REGARG >= 0 && v.Type == obj.TYPE_REG && v.Reg == x86.REGARG {
+ return 2
+ }
+ if v.Type == p.From.Type && v.Reg == p.From.Reg {
+ return 2
+ }
+
+ if s != nil {
+ if copysub(&p.To, v, s, 1) != 0 {
+ return 1
+ }
+ return 0
+ }
+
+ if copyau(&p.To, v) {
+ return 4
+ }
+ return 3
+
+ case obj.ATEXT:
+ if x86.REGARG >= 0 && v.Type == obj.TYPE_REG && v.Reg == x86.REGARG {
+ return 3
+ }
+ return 0
+ }
+
+ if p.As == obj.AVARDEF || p.As == obj.AVARKILL {
+ return 0
+ }
+ proginfo(&info, p)
+
+ if (info.Reguse|info.Regset)&RtoB(int(v.Reg)) != 0 {
+ return 2
+ }
+
+ if info.Flags&gc.LeftAddr != 0 {
+ if copyas(&p.From, v) {
+ return 2
+ }
+ }
+
+ if info.Flags&(gc.RightRead|gc.RightWrite) == gc.RightRead|gc.RightWrite {
+ if copyas(&p.To, v) {
+ return 2
+ }
+ }
+
+ if info.Flags&gc.RightWrite != 0 {
+ if copyas(&p.To, v) {
+ if s != nil {
+ return copysub(&p.From, v, s, 1)
+ }
+ if copyau(&p.From, v) {
+ return 4
+ }
+ return 3
+ }
+ }
+
+ if info.Flags&(gc.LeftAddr|gc.LeftRead|gc.LeftWrite|gc.RightAddr|gc.RightRead|gc.RightWrite) != 0 {
+ if s != nil {
+ if copysub(&p.From, v, s, 1) != 0 {
+ return 1
+ }
+ return copysub(&p.To, v, s, 1)
+ }
+
+ if copyau(&p.From, v) {
+ return 1
+ }
+ if copyau(&p.To, v) {
+ return 1
+ }
+ }
+
+ return 0
+}
+
+/*
+ * direct reference,
+ * could be set/use depending on
+ * semantics
+ */
+func copyas(a *obj.Addr, v *obj.Addr) bool {
+ if x86.REG_AL <= a.Reg && a.Reg <= x86.REG_R15B {
+ gc.Fatal("use of byte register")
+ }
+ if x86.REG_AL <= v.Reg && v.Reg <= x86.REG_R15B {
+ gc.Fatal("use of byte register")
+ }
+
+ if a.Type != v.Type || a.Name != v.Name || a.Reg != v.Reg {
+ return false
+ }
+ if regtyp(v) {
+ return true
+ }
+ if v.Type == obj.TYPE_MEM && (v.Name == obj.NAME_AUTO || v.Name == obj.NAME_PARAM) {
+ if v.Offset == a.Offset {
+ return true
+ }
+ }
+ return false
+}
+
+func sameaddr(a *obj.Addr, v *obj.Addr) bool {
+ if a.Type != v.Type || a.Name != v.Name || a.Reg != v.Reg {
+ return false
+ }
+ if regtyp(v) {
+ return true
+ }
+ if v.Type == obj.TYPE_MEM && (v.Name == obj.NAME_AUTO || v.Name == obj.NAME_PARAM) {
+ if v.Offset == a.Offset {
+ return true
+ }
+ }
+ return false
+}
+
+/*
+ * either direct or indirect
+ */
+func copyau(a *obj.Addr, v *obj.Addr) bool {
+ if copyas(a, v) {
+ if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
+ fmt.Printf("\tcopyau: copyas returned 1\n")
+ }
+ return true
+ }
+
+ if regtyp(v) {
+ if a.Type == obj.TYPE_MEM && a.Reg == v.Reg {
+ if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
+ fmt.Printf("\tcopyau: found indir use - return 1\n")
+ }
+ return true
+ }
+
+ if a.Index == v.Reg {
+ if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
+ fmt.Printf("\tcopyau: found index use - return 1\n")
+ }
+ return true
+ }
+ }
+
+ return false
+}
+
+/*
+ * substitute s for v in a
+ * return failure to substitute
+ */
+func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f int) int {
+ var reg int
+
+ if copyas(a, v) {
+ reg = int(s.Reg)
+ if reg >= x86.REG_AX && reg <= x86.REG_R15 || reg >= x86.REG_X0 && reg <= x86.REG_X0+15 {
+ if f != 0 {
+ a.Reg = int16(reg)
+ }
+ }
+
+ return 0
+ }
+
+ if regtyp(v) {
+ reg = int(v.Reg)
+ if a.Type == obj.TYPE_MEM && int(a.Reg) == reg {
+ if (s.Reg == x86.REG_BP || s.Reg == x86.REG_R13) && a.Index != x86.REG_NONE {
+ return 1 /* can't use BP-base with index */
+ }
+ if f != 0 {
+ a.Reg = s.Reg
+ }
+ }
+
+ // return 0;
+ if int(a.Index) == reg {
+ if f != 0 {
+ a.Index = s.Reg
+ }
+ return 0
+ }
+
+ return 0
+ }
+
+ return 0
+}
+
+func conprop(r0 *gc.Flow) {
+ var r *gc.Flow
+ var p *obj.Prog
+ var p0 *obj.Prog
+ var t int
+ var v0 *obj.Addr
+
+ p0 = r0.Prog
+ v0 = &p0.To
+ r = r0
+
+loop:
+ r = gc.Uniqs(r)
+ if r == nil || r == r0 {
+ return
+ }
+ if gc.Uniqp(r) == nil {
+ return
+ }
+
+ p = r.Prog
+ t = copyu(p, v0, nil)
+ switch t {
+ case 0, // miss
+ 1: // use
+ goto loop
+
+ case 2, // rar
+ 4: // use and set
+ break
+
+ case 3: // set
+ if p.As == p0.As {
+ if p.From.Type == p0.From.Type {
+ if p.From.Reg == p0.From.Reg {
+ if p.From.Node == p0.From.Node {
+ if p.From.Offset == p0.From.Offset {
+ if p.From.Scale == p0.From.Scale {
+ if p.From.Type == obj.TYPE_FCONST && p.From.U.Dval == p0.From.U.Dval {
+ if p.From.Index == p0.From.Index {
+ excise(r)
+ goto loop
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+func smallindir(a *obj.Addr, reg *obj.Addr) bool {
+ return regtyp(reg) && a.Type == obj.TYPE_MEM && a.Reg == reg.Reg && a.Index == x86.REG_NONE && 0 <= a.Offset && a.Offset < 4096
+}
+
+func stackaddr(a *obj.Addr) bool {
+ return a.Type == obj.TYPE_REG && a.Reg == x86.REG_SP
+}