mirror of
https://github.com/golang/go
synced 2024-10-05 07:11:22 -06:00
d47fe8092e
Also clean up code a little. Change-Id: I23b7d2b7871b31e0974f1305e54f0c18dcab05d9 Reviewed-on: https://go-review.googlesource.com/7746 Reviewed-by: Dave Cheney <dave@cheney.net> Reviewed-by: Rob Pike <r@golang.org>
1039 lines
21 KiB
Go
1039 lines
21 KiB
Go
// 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/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.Graph)(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 := (*gc.Flow)(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 := (*gc.Flow)(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 := (*gc.Flow)(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 := (*obj.Prog)(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 [%d]\n", r.Prog, gc.Uniqs(r) != nil)
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
|
|
fmt.Printf("pushback\n")
|
|
for r := (*gc.Flow)(b); ; r = r.Link {
|
|
fmt.Printf("\t%v\n", r.Prog)
|
|
if r == r0 {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
t := obj.Prog(*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 := (*gc.Flow)(b); ; r = r.Link {
|
|
fmt.Printf("\t%v\n", r.Prog)
|
|
if r == r0 {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func excise(r *gc.Flow) {
|
|
p := (*obj.Prog)(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 := (*gc.Flow)(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 {
|
|
for r := (*gc.Flow)(gc.Uniqp(r0)); r != nil; r = gc.Uniqp(r) {
|
|
p := r.Prog
|
|
if p.To.Type == obj.TYPE_REG && int(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 := (*obj.Prog)(r0.Prog)
|
|
v1 := (*obj.Addr)(&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 := (*obj.Addr)(&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, 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(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
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
/*
|
|
* 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 := (*obj.Prog)(r0.Prog)
|
|
v1 := (*obj.Addr)(&p.From)
|
|
v2 := (*obj.Addr)(&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 {
|
|
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)
|
|
}
|
|
var t int
|
|
var p *obj.Prog
|
|
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 {
|
|
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
|
|
}
|
|
|
|
if (p.Info.Reguse|p.Info.Regset)&RtoB(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 {
|
|
return copysub(&p.From, v, s, 1)
|
|
}
|
|
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, 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 {
|
|
if copyas(a, v) {
|
|
reg := int(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(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 p *obj.Prog
|
|
var t int
|
|
|
|
p0 := (*obj.Prog)(r0.Prog)
|
|
v0 := (*obj.Addr)(&p0.To)
|
|
r := (*gc.Flow)(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
|
|
}
|