blob: 4ae5fcef8b63c32966e6a2acaeacfeca8f2898ab [file] [log] [blame]
// 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 amd64
import (
"cmd/compile/internal/gc"
"cmd/internal/obj"
"cmd/internal/obj/x86"
"fmt"
)
var gactive uint32
const (
exregoffset = x86.REG_R15
)
// do we need the carry bit
func needc(p *obj.Prog) bool {
for p != nil {
flags := progcarryflags(p)
if flags&gc.UseCarry != 0 {
return true
}
if flags&(gc.SetCarry|gc.KillCarry) != 0 {
return false
}
p = p.Link
}
return false
}
func rnops(r *gc.Flow) *gc.Flow {
if r != nil {
var p *obj.Prog
var r1 *gc.Flow
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) {
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
var p *obj.Prog
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)
}
}
}
}
var r *gc.Flow
var r1 *gc.Flow
var p1 *obj.Prog
var t int
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, 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 p *obj.Prog
var b *gc.Flow
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['P'] != 0 && gc.Debug['v'] != 0 {
fmt.Printf("no pushback: %v\n", r0.Prog)
if r != nil {
fmt.Printf("\t%v [%v]\n", r.Prog, gc.Uniqs(r) != nil)
}
}
return
}
if gc.Debug['P'] != 0 && 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['P'] != 0 && 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) {
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
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 arithmetic 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 int16) bool {
for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) {
p := r.Prog
if p.To.Type == obj.TYPE_REG && p.To.Reg == reg {
flags := progflags(p)
if flags&gc.RightWrite != 0 {
if 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 {
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
}
if p.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 p.Info.Reguse|p.Info.Regset != 0 {
if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
fmt.Printf("\tfound %v; return 0\n", p)
}
return false
}
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 {
copysub(&p.To, v1, v2, true)
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, true)
copysub(&p.To, v1, v2, true)
if gc.Debug['P'] != 0 {
fmt.Printf("%v\n", r.Prog)
}
}
v1.Reg, v2.Reg = v2.Reg, v1.Reg
if gc.Debug['P'] != 0 {
fmt.Printf("%v last\n", r.Prog)
}
return true
}
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, false) || copysub(&p.To, v1, v2, false) {
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
}
/*
* 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 {
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, false)
}
func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f bool) bool {
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=%v\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 && gc.Uniqp(r) == nil {
f = true
if gc.Debug['P'] != 0 {
fmt.Printf("; merge; f=%v", f)
}
}
switch t := copyu(p, v2, nil); 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 {
if gc.Debug['P'] == 0 {
return false
}
if t == 4 {
fmt.Printf("; %v used+set and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f)
} else {
fmt.Printf("; %v used and f=%v; 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 {
t := copyu(p, v1, nil)
if t == 2 || t == 3 || t == 4 {
f = true
if gc.Debug['P'] != 0 {
fmt.Printf("; %v set and !f; f=%v", 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 {
switch p.As {
case obj.AJMP:
if s != nil {
if copysub(&p.To, v, s, true) {
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, true) {
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
}
if (p.Info.Reguse|p.Info.Regset)&RtoB(int(v.Reg)) != 0 {
return 2
}
if (p.Info.Reguse|p.Info.Regset)&FtoB(int(v.Reg)) != 0 {
return 2
}
if p.Info.Flags&gc.LeftAddr != 0 {
if copyas(&p.From, v) {
return 2
}
}
if p.Info.Flags&(gc.RightRead|gc.RightWrite) == gc.RightRead|gc.RightWrite {
if copyas(&p.To, v) {
return 2
}
}
if p.Info.Flags&gc.RightWrite != 0 {
if copyas(&p.To, v) {
if s != nil {
if copysub(&p.From, v, s, true) {
return 1
}
return 0
}
if copyau(&p.From, v) {
return 4
}
return 3
}
}
if p.Info.Flags&(gc.LeftAddr|gc.LeftRead|gc.LeftWrite|gc.RightAddr|gc.RightRead|gc.RightWrite) != 0 {
if s != nil {
if copysub(&p.From, v, s, true) {
return 1
}
if copysub(&p.To, v, s, true) {
return 1
}
return 0
}
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.Fatalf("use of byte register")
}
if x86.REG_AL <= v.Reg && v.Reg <= x86.REG_R15B {
gc.Fatalf("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
}
// copysub substitute s for v in a.
// copysub returns true on failure to substitute. TODO(dfc) reverse this logic, copysub should return false on failure
func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f bool) bool {
if copyas(a, v) {
if s.Reg >= x86.REG_AX && s.Reg <= x86.REG_R15 || s.Reg >= x86.REG_X0 && s.Reg <= x86.REG_X0+15 {
if f {
a.Reg = s.Reg
}
}
return false
}
if regtyp(v) {
if a.Type == obj.TYPE_MEM && a.Reg == v.Reg {
if (s.Reg == x86.REG_BP || s.Reg == x86.REG_R13) && a.Index != x86.REG_NONE {
return true /* can't use BP-base with index */
}
if f {
a.Reg = s.Reg
}
}
if a.Index == v.Reg {
if f {
a.Index = s.Reg
}
}
}
return false
}
func conprop(r0 *gc.Flow) {
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.Val.(float64) == p0.From.Val.(float64) {
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
}