mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
1402 lines
29 KiB
Go
1402 lines
29 KiB
Go
|
|
// Derived from Inferno utils/6c/reg.c
|
||
|
|
// http://code.google.com/p/inferno-os/source/browse/utils/6c/reg.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 gc
|
||
|
|
|
||
|
|
import (
|
||
|
|
"cmd/internal/obj"
|
||
|
|
"fmt"
|
||
|
|
"sort"
|
||
|
|
)
|
||
|
|
|
||
|
|
var firstf *Flow
|
||
|
|
|
||
|
|
var first int = 1
|
||
|
|
|
||
|
|
type rcmp []Rgn
|
||
|
|
|
||
|
|
func (x rcmp) Len() int {
|
||
|
|
return len(x)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (x rcmp) Swap(i, j int) {
|
||
|
|
x[i], x[j] = x[j], x[i]
|
||
|
|
}
|
||
|
|
|
||
|
|
func (x rcmp) Less(i, j int) bool {
|
||
|
|
var p1 *Rgn
|
||
|
|
var p2 *Rgn
|
||
|
|
|
||
|
|
p1 = &x[i]
|
||
|
|
p2 = &x[j]
|
||
|
|
if p1.cost != p2.cost {
|
||
|
|
return int(p2.cost)-int(p1.cost) < 0
|
||
|
|
}
|
||
|
|
if p1.varno != p2.varno {
|
||
|
|
return int(p2.varno)-int(p1.varno) < 0
|
||
|
|
}
|
||
|
|
if p1.enter != p2.enter {
|
||
|
|
return int(p2.enter.Id-p1.enter.Id) < 0
|
||
|
|
}
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
|
||
|
|
func setaddrs(bit Bits) {
|
||
|
|
var i int
|
||
|
|
var n int
|
||
|
|
var v *Var
|
||
|
|
var node *Node
|
||
|
|
|
||
|
|
for bany(&bit) != 0 {
|
||
|
|
// convert each bit to a variable
|
||
|
|
i = bnum(bit)
|
||
|
|
|
||
|
|
node = var_[i].node
|
||
|
|
n = int(var_[i].name)
|
||
|
|
biclr(&bit, uint(i))
|
||
|
|
|
||
|
|
// disable all pieces of that variable
|
||
|
|
for i = 0; i < nvar; i++ {
|
||
|
|
v = &var_[i:][0]
|
||
|
|
if v.node == node && int(v.name) == n {
|
||
|
|
v.addr = 2
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
var regnodes [64]*Node
|
||
|
|
|
||
|
|
func walkvardef(n *Node, f *Flow, active int) {
|
||
|
|
var f1 *Flow
|
||
|
|
var f2 *Flow
|
||
|
|
var bn int
|
||
|
|
var v *Var
|
||
|
|
|
||
|
|
for f1 = f; f1 != nil; f1 = f1.S1 {
|
||
|
|
if f1.Active == int32(active) {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
f1.Active = int32(active)
|
||
|
|
if f1.Prog.As == obj.AVARKILL && f1.Prog.To.Node == n {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
for v, _ = n.Opt.(*Var); v != nil; v = v.nextinnode {
|
||
|
|
bn = v.id
|
||
|
|
biset(&(f1.Data.(*Reg)).act, uint(bn))
|
||
|
|
}
|
||
|
|
|
||
|
|
if f1.Prog.As == obj.ACALL {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
for f2 = f; f2 != f1; f2 = f2.S1 {
|
||
|
|
if f2.S2 != nil {
|
||
|
|
walkvardef(n, f2.S2, active)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
* add mov b,rn
|
||
|
|
* just after r
|
||
|
|
*/
|
||
|
|
func addmove(r *Flow, bn int, rn int, f int) {
|
||
|
|
var p *obj.Prog
|
||
|
|
var p1 *obj.Prog
|
||
|
|
var a *obj.Addr
|
||
|
|
var v *Var
|
||
|
|
|
||
|
|
p1 = Ctxt.NewProg()
|
||
|
|
Clearp(p1)
|
||
|
|
p1.Pc = 9999
|
||
|
|
|
||
|
|
p = r.Prog
|
||
|
|
p1.Link = p.Link
|
||
|
|
p.Link = p1
|
||
|
|
p1.Lineno = p.Lineno
|
||
|
|
|
||
|
|
v = &var_[bn:][0]
|
||
|
|
|
||
|
|
a = &p1.To
|
||
|
|
a.Offset = v.offset
|
||
|
|
a.Etype = uint8(v.etype)
|
||
|
|
a.Type = obj.TYPE_MEM
|
||
|
|
a.Name = v.name
|
||
|
|
a.Node = v.node
|
||
|
|
a.Sym = Linksym(v.node.Sym)
|
||
|
|
|
||
|
|
/* NOTE(rsc): 9g did
|
||
|
|
if(a->etype == TARRAY)
|
||
|
|
a->type = TYPE_ADDR;
|
||
|
|
else if(a->sym == nil)
|
||
|
|
a->type = TYPE_CONST;
|
||
|
|
*/
|
||
|
|
p1.As = int16(Thearch.Optoas(OAS, Types[uint8(v.etype)]))
|
||
|
|
|
||
|
|
// TODO(rsc): Remove special case here.
|
||
|
|
if (Thearch.Thechar == '9' || Thearch.Thechar == '5') && v.etype == TBOOL {
|
||
|
|
p1.As = int16(Thearch.Optoas(OAS, Types[TUINT8]))
|
||
|
|
}
|
||
|
|
p1.From.Type = obj.TYPE_REG
|
||
|
|
p1.From.Reg = int16(rn)
|
||
|
|
p1.From.Name = obj.NAME_NONE
|
||
|
|
if !(f != 0) {
|
||
|
|
p1.From = *a
|
||
|
|
*a = obj.Zprog.From
|
||
|
|
a.Type = obj.TYPE_REG
|
||
|
|
a.Reg = int16(rn)
|
||
|
|
}
|
||
|
|
|
||
|
|
if Debug['R'] != 0 && Debug['v'] != 0 {
|
||
|
|
fmt.Printf("%v ===add=== %v\n", p, p1)
|
||
|
|
}
|
||
|
|
Ostats.Nspill++
|
||
|
|
}
|
||
|
|
|
||
|
|
func overlap_reg(o1 int64, w1 int, o2 int64, w2 int) int {
|
||
|
|
var t1 int64
|
||
|
|
var t2 int64
|
||
|
|
|
||
|
|
t1 = o1 + int64(w1)
|
||
|
|
t2 = o2 + int64(w2)
|
||
|
|
|
||
|
|
if !(t1 > o2 && t2 > o1) {
|
||
|
|
return 0
|
||
|
|
}
|
||
|
|
|
||
|
|
return 1
|
||
|
|
}
|
||
|
|
|
||
|
|
func mkvar(f *Flow, a *obj.Addr) Bits {
|
||
|
|
var v *Var
|
||
|
|
var i int
|
||
|
|
var n int
|
||
|
|
var et int
|
||
|
|
var z int
|
||
|
|
var flag int
|
||
|
|
var w int64
|
||
|
|
var regu uint64
|
||
|
|
var o int64
|
||
|
|
var bit Bits
|
||
|
|
var node *Node
|
||
|
|
var r *Reg
|
||
|
|
|
||
|
|
/*
|
||
|
|
* mark registers used
|
||
|
|
*/
|
||
|
|
if a.Type == obj.TYPE_NONE {
|
||
|
|
goto none
|
||
|
|
}
|
||
|
|
|
||
|
|
r = f.Data.(*Reg)
|
||
|
|
r.use1.b[0] |= Thearch.Doregbits(int(a.Index)) // TODO: Use RtoB
|
||
|
|
|
||
|
|
switch a.Type {
|
||
|
|
default:
|
||
|
|
regu = Thearch.Doregbits(int(a.Reg)) | Thearch.RtoB(int(a.Reg)) // TODO: Use RtoB
|
||
|
|
if regu == 0 {
|
||
|
|
goto none
|
||
|
|
}
|
||
|
|
bit = zbits
|
||
|
|
bit.b[0] = regu
|
||
|
|
return bit
|
||
|
|
|
||
|
|
// TODO(rsc): Remove special case here.
|
||
|
|
case obj.TYPE_ADDR:
|
||
|
|
if Thearch.Thechar == '9' || Thearch.Thechar == '5' {
|
||
|
|
goto memcase
|
||
|
|
}
|
||
|
|
a.Type = obj.TYPE_MEM
|
||
|
|
bit = mkvar(f, a)
|
||
|
|
setaddrs(bit)
|
||
|
|
a.Type = obj.TYPE_ADDR
|
||
|
|
Ostats.Naddr++
|
||
|
|
goto none
|
||
|
|
|
||
|
|
memcase:
|
||
|
|
fallthrough
|
||
|
|
|
||
|
|
case obj.TYPE_MEM:
|
||
|
|
if r != R {
|
||
|
|
r.use1.b[0] |= Thearch.RtoB(int(a.Reg))
|
||
|
|
}
|
||
|
|
|
||
|
|
/* NOTE: 5g did
|
||
|
|
if(r->f.prog->scond & (C_PBIT|C_WBIT))
|
||
|
|
r->set.b[0] |= RtoB(a->reg);
|
||
|
|
*/
|
||
|
|
switch a.Name {
|
||
|
|
default:
|
||
|
|
goto none
|
||
|
|
|
||
|
|
case obj.NAME_EXTERN,
|
||
|
|
obj.NAME_STATIC,
|
||
|
|
obj.NAME_PARAM,
|
||
|
|
obj.NAME_AUTO:
|
||
|
|
n = int(a.Name)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
node, _ = a.Node.(*Node)
|
||
|
|
if node == nil || node.Op != ONAME || node.Orig == nil {
|
||
|
|
goto none
|
||
|
|
}
|
||
|
|
node = node.Orig
|
||
|
|
if node.Orig != node {
|
||
|
|
Fatal("%v: bad node", Ctxt.Dconv(a))
|
||
|
|
}
|
||
|
|
if node.Sym == nil || node.Sym.Name[0] == '.' {
|
||
|
|
goto none
|
||
|
|
}
|
||
|
|
et = int(a.Etype)
|
||
|
|
o = a.Offset
|
||
|
|
w = a.Width
|
||
|
|
if w < 0 {
|
||
|
|
Fatal("bad width %d for %v", w, Ctxt.Dconv(a))
|
||
|
|
}
|
||
|
|
|
||
|
|
flag = 0
|
||
|
|
for i = 0; i < nvar; i++ {
|
||
|
|
v = &var_[i:][0]
|
||
|
|
if v.node == node && int(v.name) == n {
|
||
|
|
if v.offset == o {
|
||
|
|
if int(v.etype) == et {
|
||
|
|
if int64(v.width) == w {
|
||
|
|
// TODO(rsc): Remove special case for arm here.
|
||
|
|
if !(flag != 0) || Thearch.Thechar != '5' {
|
||
|
|
return blsh(uint(i))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// if they overlap, disable both
|
||
|
|
if overlap_reg(v.offset, v.width, o, int(w)) != 0 {
|
||
|
|
// print("disable overlap %s %d %d %d %d, %E != %E\n", s->name, v->offset, v->width, o, w, v->etype, et);
|
||
|
|
v.addr = 1
|
||
|
|
|
||
|
|
flag = 1
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
switch et {
|
||
|
|
case 0,
|
||
|
|
TFUNC:
|
||
|
|
goto none
|
||
|
|
}
|
||
|
|
|
||
|
|
if nvar >= NVAR {
|
||
|
|
if Debug['w'] > 1 && node != nil {
|
||
|
|
Fatal("variable not optimized: %v", Nconv(node, obj.FmtSharp))
|
||
|
|
}
|
||
|
|
|
||
|
|
// If we're not tracking a word in a variable, mark the rest as
|
||
|
|
// having its address taken, so that we keep the whole thing
|
||
|
|
// live at all calls. otherwise we might optimize away part of
|
||
|
|
// a variable but not all of it.
|
||
|
|
for i = 0; i < nvar; i++ {
|
||
|
|
v = &var_[i:][0]
|
||
|
|
if v.node == node {
|
||
|
|
v.addr = 1
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
goto none
|
||
|
|
}
|
||
|
|
|
||
|
|
i = nvar
|
||
|
|
nvar++
|
||
|
|
v = &var_[i:][0]
|
||
|
|
v.id = i
|
||
|
|
v.offset = o
|
||
|
|
v.name = int8(n)
|
||
|
|
v.etype = int8(et)
|
||
|
|
v.width = int(w)
|
||
|
|
v.addr = int8(flag) // funny punning
|
||
|
|
v.node = node
|
||
|
|
|
||
|
|
// node->opt is the head of a linked list
|
||
|
|
// of Vars within the given Node, so that
|
||
|
|
// we can start at a Var and find all the other
|
||
|
|
// Vars in the same Go variable.
|
||
|
|
v.nextinnode, _ = node.Opt.(*Var)
|
||
|
|
|
||
|
|
node.Opt = v
|
||
|
|
|
||
|
|
bit = blsh(uint(i))
|
||
|
|
if n == obj.NAME_EXTERN || n == obj.NAME_STATIC {
|
||
|
|
for z = 0; z < BITS; z++ {
|
||
|
|
externs.b[z] |= bit.b[z]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if n == obj.NAME_PARAM {
|
||
|
|
for z = 0; z < BITS; z++ {
|
||
|
|
params.b[z] |= bit.b[z]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if node.Class == PPARAM {
|
||
|
|
for z = 0; z < BITS; z++ {
|
||
|
|
ivar.b[z] |= bit.b[z]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if node.Class == PPARAMOUT {
|
||
|
|
for z = 0; z < BITS; z++ {
|
||
|
|
ovar.b[z] |= bit.b[z]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Treat values with their address taken as live at calls,
|
||
|
|
// because the garbage collector's liveness analysis in ../gc/plive.c does.
|
||
|
|
// These must be consistent or else we will elide stores and the garbage
|
||
|
|
// collector will see uninitialized data.
|
||
|
|
// The typical case where our own analysis is out of sync is when the
|
||
|
|
// node appears to have its address taken but that code doesn't actually
|
||
|
|
// get generated and therefore doesn't show up as an address being
|
||
|
|
// taken when we analyze the instruction stream.
|
||
|
|
// One instance of this case is when a closure uses the same name as
|
||
|
|
// an outer variable for one of its own variables declared with :=.
|
||
|
|
// The parser flags the outer variable as possibly shared, and therefore
|
||
|
|
// sets addrtaken, even though it ends up not being actually shared.
|
||
|
|
// If we were better about _ elision, _ = &x would suffice too.
|
||
|
|
// The broader := in a closure problem is mentioned in a comment in
|
||
|
|
// closure.c:/^typecheckclosure and dcl.c:/^oldname.
|
||
|
|
if node.Addrtaken != 0 {
|
||
|
|
v.addr = 1
|
||
|
|
}
|
||
|
|
|
||
|
|
// Disable registerization for globals, because:
|
||
|
|
// (1) we might panic at any time and we want the recovery code
|
||
|
|
// to see the latest values (issue 1304).
|
||
|
|
// (2) we don't know what pointers might point at them and we want
|
||
|
|
// loads via those pointers to see updated values and vice versa (issue 7995).
|
||
|
|
//
|
||
|
|
// Disable registerization for results if using defer, because the deferred func
|
||
|
|
// might recover and return, causing the current values to be used.
|
||
|
|
if node.Class == PEXTERN || (Hasdefer != 0 && node.Class == PPARAMOUT) {
|
||
|
|
v.addr = 1
|
||
|
|
}
|
||
|
|
|
||
|
|
if Debug['R'] != 0 {
|
||
|
|
fmt.Printf("bit=%2d et=%v w=%d+%d %v %v flag=%d\n", i, Econv(int(et), 0), o, w, Nconv(node, obj.FmtSharp), Ctxt.Dconv(a), v.addr)
|
||
|
|
}
|
||
|
|
Ostats.Nvar++
|
||
|
|
|
||
|
|
return bit
|
||
|
|
|
||
|
|
none:
|
||
|
|
return zbits
|
||
|
|
}
|
||
|
|
|
||
|
|
func prop(f *Flow, ref Bits, cal Bits) {
|
||
|
|
var f1 *Flow
|
||
|
|
var f2 *Flow
|
||
|
|
var r *Reg
|
||
|
|
var r1 *Reg
|
||
|
|
var z int
|
||
|
|
var i int
|
||
|
|
var v *Var
|
||
|
|
var v1 *Var
|
||
|
|
|
||
|
|
for f1 = f; f1 != nil; f1 = f1.P1 {
|
||
|
|
r1 = f1.Data.(*Reg)
|
||
|
|
for z = 0; z < BITS; z++ {
|
||
|
|
ref.b[z] |= r1.refahead.b[z]
|
||
|
|
if ref.b[z] != r1.refahead.b[z] {
|
||
|
|
r1.refahead.b[z] = ref.b[z]
|
||
|
|
change++
|
||
|
|
}
|
||
|
|
|
||
|
|
cal.b[z] |= r1.calahead.b[z]
|
||
|
|
if cal.b[z] != r1.calahead.b[z] {
|
||
|
|
r1.calahead.b[z] = cal.b[z]
|
||
|
|
change++
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
switch f1.Prog.As {
|
||
|
|
case obj.ACALL:
|
||
|
|
if Noreturn(f1.Prog) != 0 {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
|
||
|
|
// Mark all input variables (ivar) as used, because that's what the
|
||
|
|
// liveness bitmaps say. The liveness bitmaps say that so that a
|
||
|
|
// panic will not show stale values in the parameter dump.
|
||
|
|
// Mark variables with a recent VARDEF (r1->act) as used,
|
||
|
|
// so that the optimizer flushes initializations to memory,
|
||
|
|
// so that if a garbage collection happens during this CALL,
|
||
|
|
// the collector will see initialized memory. Again this is to
|
||
|
|
// match what the liveness bitmaps say.
|
||
|
|
for z = 0; z < BITS; z++ {
|
||
|
|
cal.b[z] |= ref.b[z] | externs.b[z] | ivar.b[z] | r1.act.b[z]
|
||
|
|
ref.b[z] = 0
|
||
|
|
}
|
||
|
|
|
||
|
|
// cal.b is the current approximation of what's live across the call.
|
||
|
|
// Every bit in cal.b is a single stack word. For each such word,
|
||
|
|
// find all the other tracked stack words in the same Go variable
|
||
|
|
// (struct/slice/string/interface) and mark them live too.
|
||
|
|
// This is necessary because the liveness analysis for the garbage
|
||
|
|
// collector works at variable granularity, not at word granularity.
|
||
|
|
// It is fundamental for slice/string/interface: the garbage collector
|
||
|
|
// needs the whole value, not just some of the words, in order to
|
||
|
|
// interpret the other bits correctly. Specifically, slice needs a consistent
|
||
|
|
// ptr and cap, string needs a consistent ptr and len, and interface
|
||
|
|
// needs a consistent type word and data word.
|
||
|
|
for z = 0; z < BITS; z++ {
|
||
|
|
if cal.b[z] == 0 {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
for i = 0; i < 64; i++ {
|
||
|
|
if z*64+i >= nvar || (cal.b[z]>>uint(i))&1 == 0 {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
v = &var_[z*64+i:][0]
|
||
|
|
if v.node.Opt == nil { // v represents fixed register, not Go variable
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
|
||
|
|
// v->node->opt is the head of a linked list of Vars
|
||
|
|
// corresponding to tracked words from the Go variable v->node.
|
||
|
|
// Walk the list and set all the bits.
|
||
|
|
// For a large struct this could end up being quadratic:
|
||
|
|
// after the first setting, the outer loop (for z, i) would see a 1 bit
|
||
|
|
// for all of the remaining words in the struct, and for each such
|
||
|
|
// word would go through and turn on all the bits again.
|
||
|
|
// To avoid the quadratic behavior, we only turn on the bits if
|
||
|
|
// v is the head of the list or if the head's bit is not yet turned on.
|
||
|
|
// This will set the bits at most twice, keeping the overall loop linear.
|
||
|
|
v1, _ = v.node.Opt.(*Var)
|
||
|
|
|
||
|
|
if v == v1 || !(btest(&cal, uint(v1.id)) != 0) {
|
||
|
|
for ; v1 != nil; v1 = v1.nextinnode {
|
||
|
|
biset(&cal, uint(v1.id))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
case obj.ATEXT:
|
||
|
|
for z = 0; z < BITS; z++ {
|
||
|
|
cal.b[z] = 0
|
||
|
|
ref.b[z] = 0
|
||
|
|
}
|
||
|
|
|
||
|
|
case obj.ARET:
|
||
|
|
for z = 0; z < BITS; z++ {
|
||
|
|
cal.b[z] = externs.b[z] | ovar.b[z]
|
||
|
|
ref.b[z] = 0
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
for z = 0; z < BITS; z++ {
|
||
|
|
ref.b[z] = ref.b[z]&^r1.set.b[z] | r1.use1.b[z] | r1.use2.b[z]
|
||
|
|
cal.b[z] &^= (r1.set.b[z] | r1.use1.b[z] | r1.use2.b[z])
|
||
|
|
r1.refbehind.b[z] = ref.b[z]
|
||
|
|
r1.calbehind.b[z] = cal.b[z]
|
||
|
|
}
|
||
|
|
|
||
|
|
if f1.Active != 0 {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
f1.Active = 1
|
||
|
|
}
|
||
|
|
|
||
|
|
for ; f != f1; f = f.P1 {
|
||
|
|
r = f.Data.(*Reg)
|
||
|
|
for f2 = f.P2; f2 != nil; f2 = f2.P2link {
|
||
|
|
prop(f2, r.refbehind, r.calbehind)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func synch(f *Flow, dif Bits) {
|
||
|
|
var f1 *Flow
|
||
|
|
var r1 *Reg
|
||
|
|
var z int
|
||
|
|
|
||
|
|
for f1 = f; f1 != nil; f1 = f1.S1 {
|
||
|
|
r1 = f1.Data.(*Reg)
|
||
|
|
for z = 0; z < BITS; z++ {
|
||
|
|
dif.b[z] = dif.b[z]&^(^r1.refbehind.b[z]&r1.refahead.b[z]) | r1.set.b[z] | r1.regdiff.b[z]
|
||
|
|
if dif.b[z] != r1.regdiff.b[z] {
|
||
|
|
r1.regdiff.b[z] = dif.b[z]
|
||
|
|
change++
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if f1.Active != 0 {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
f1.Active = 1
|
||
|
|
for z = 0; z < BITS; z++ {
|
||
|
|
dif.b[z] &^= (^r1.calbehind.b[z] & r1.calahead.b[z])
|
||
|
|
}
|
||
|
|
if f1.S2 != nil {
|
||
|
|
synch(f1.S2, dif)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func allreg(b uint64, r *Rgn) uint64 {
|
||
|
|
var v *Var
|
||
|
|
var i int
|
||
|
|
|
||
|
|
v = &var_[r.varno:][0]
|
||
|
|
r.regno = 0
|
||
|
|
switch v.etype {
|
||
|
|
default:
|
||
|
|
Fatal("unknown etype %d/%v", Bitno(b), Econv(int(v.etype), 0))
|
||
|
|
|
||
|
|
case TINT8,
|
||
|
|
TUINT8,
|
||
|
|
TINT16,
|
||
|
|
TUINT16,
|
||
|
|
TINT32,
|
||
|
|
TUINT32,
|
||
|
|
TINT64,
|
||
|
|
TUINT64,
|
||
|
|
TINT,
|
||
|
|
TUINT,
|
||
|
|
TUINTPTR,
|
||
|
|
TBOOL,
|
||
|
|
TPTR32,
|
||
|
|
TPTR64:
|
||
|
|
i = Thearch.BtoR(^b)
|
||
|
|
if i != 0 && r.cost > 0 {
|
||
|
|
r.regno = int16(i)
|
||
|
|
return Thearch.RtoB(i)
|
||
|
|
}
|
||
|
|
|
||
|
|
case TFLOAT32,
|
||
|
|
TFLOAT64:
|
||
|
|
i = Thearch.BtoF(^b)
|
||
|
|
if i != 0 && r.cost > 0 {
|
||
|
|
r.regno = int16(i)
|
||
|
|
return Thearch.FtoB(i)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0
|
||
|
|
}
|
||
|
|
|
||
|
|
func LOAD(r *Reg, z int) uint64 {
|
||
|
|
return ^r.refbehind.b[z] & r.refahead.b[z]
|
||
|
|
}
|
||
|
|
|
||
|
|
func STORE(r *Reg, z int) uint64 {
|
||
|
|
return ^r.calbehind.b[z] & r.calahead.b[z]
|
||
|
|
}
|
||
|
|
|
||
|
|
func paint1(f *Flow, bn int) {
|
||
|
|
var f1 *Flow
|
||
|
|
var r *Reg
|
||
|
|
var r1 *Reg
|
||
|
|
var z int
|
||
|
|
var bb uint64
|
||
|
|
|
||
|
|
z = bn / 64
|
||
|
|
bb = 1 << uint(bn%64)
|
||
|
|
r = f.Data.(*Reg)
|
||
|
|
if r.act.b[z]&bb != 0 {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
for {
|
||
|
|
if !(r.refbehind.b[z]&bb != 0) {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
f1 = f.P1
|
||
|
|
if f1 == nil {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
r1 = f1.Data.(*Reg)
|
||
|
|
if !(r1.refahead.b[z]&bb != 0) {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
if r1.act.b[z]&bb != 0 {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
f = f1
|
||
|
|
r = r1
|
||
|
|
}
|
||
|
|
|
||
|
|
if LOAD(r, z)&^(r.set.b[z]&^(r.use1.b[z]|r.use2.b[z]))&bb != 0 {
|
||
|
|
change -= CLOAD * int(f.Loop)
|
||
|
|
}
|
||
|
|
|
||
|
|
for {
|
||
|
|
r.act.b[z] |= bb
|
||
|
|
|
||
|
|
if f.Prog.As != obj.ANOP { // don't give credit for NOPs
|
||
|
|
if r.use1.b[z]&bb != 0 {
|
||
|
|
change += CREF * int(f.Loop)
|
||
|
|
}
|
||
|
|
if (r.use2.b[z]|r.set.b[z])&bb != 0 {
|
||
|
|
change += CREF * int(f.Loop)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if STORE(r, z)&r.regdiff.b[z]&bb != 0 {
|
||
|
|
change -= CLOAD * int(f.Loop)
|
||
|
|
}
|
||
|
|
|
||
|
|
if r.refbehind.b[z]&bb != 0 {
|
||
|
|
for f1 = f.P2; f1 != nil; f1 = f1.P2link {
|
||
|
|
if (f1.Data.(*Reg)).refahead.b[z]&bb != 0 {
|
||
|
|
paint1(f1, bn)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if !(r.refahead.b[z]&bb != 0) {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
f1 = f.S2
|
||
|
|
if f1 != nil {
|
||
|
|
if (f1.Data.(*Reg)).refbehind.b[z]&bb != 0 {
|
||
|
|
paint1(f1, bn)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
f = f.S1
|
||
|
|
if f == nil {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
r = f.Data.(*Reg)
|
||
|
|
if r.act.b[z]&bb != 0 {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
if !(r.refbehind.b[z]&bb != 0) {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func paint2(f *Flow, bn int, depth int) uint64 {
|
||
|
|
var f1 *Flow
|
||
|
|
var r *Reg
|
||
|
|
var r1 *Reg
|
||
|
|
var z int
|
||
|
|
var bb uint64
|
||
|
|
var vreg uint64
|
||
|
|
|
||
|
|
z = bn / 64
|
||
|
|
bb = 1 << uint(bn%64)
|
||
|
|
vreg = regbits
|
||
|
|
r = f.Data.(*Reg)
|
||
|
|
if !(r.act.b[z]&bb != 0) {
|
||
|
|
return vreg
|
||
|
|
}
|
||
|
|
for {
|
||
|
|
if !(r.refbehind.b[z]&bb != 0) {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
f1 = f.P1
|
||
|
|
if f1 == nil {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
r1 = f1.Data.(*Reg)
|
||
|
|
if !(r1.refahead.b[z]&bb != 0) {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
if !(r1.act.b[z]&bb != 0) {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
f = f1
|
||
|
|
r = r1
|
||
|
|
}
|
||
|
|
|
||
|
|
for {
|
||
|
|
if Debug['R'] != 0 && Debug['v'] != 0 {
|
||
|
|
fmt.Printf(" paint2 %d %v\n", depth, f.Prog)
|
||
|
|
}
|
||
|
|
|
||
|
|
r.act.b[z] &^= bb
|
||
|
|
|
||
|
|
vreg |= r.regu
|
||
|
|
|
||
|
|
if r.refbehind.b[z]&bb != 0 {
|
||
|
|
for f1 = f.P2; f1 != nil; f1 = f1.P2link {
|
||
|
|
if (f1.Data.(*Reg)).refahead.b[z]&bb != 0 {
|
||
|
|
vreg |= paint2(f1, bn, depth+1)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if !(r.refahead.b[z]&bb != 0) {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
f1 = f.S2
|
||
|
|
if f1 != nil {
|
||
|
|
if (f1.Data.(*Reg)).refbehind.b[z]&bb != 0 {
|
||
|
|
vreg |= paint2(f1, bn, depth+1)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
f = f.S1
|
||
|
|
if f == nil {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
r = f.Data.(*Reg)
|
||
|
|
if !(r.act.b[z]&bb != 0) {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
if !(r.refbehind.b[z]&bb != 0) {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return vreg
|
||
|
|
}
|
||
|
|
|
||
|
|
func paint3(f *Flow, bn int, rb uint64, rn int) {
|
||
|
|
var f1 *Flow
|
||
|
|
var r *Reg
|
||
|
|
var r1 *Reg
|
||
|
|
var p *obj.Prog
|
||
|
|
var z int
|
||
|
|
var bb uint64
|
||
|
|
|
||
|
|
z = bn / 64
|
||
|
|
bb = 1 << uint(bn%64)
|
||
|
|
r = f.Data.(*Reg)
|
||
|
|
if r.act.b[z]&bb != 0 {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
for {
|
||
|
|
if !(r.refbehind.b[z]&bb != 0) {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
f1 = f.P1
|
||
|
|
if f1 == nil {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
r1 = f1.Data.(*Reg)
|
||
|
|
if !(r1.refahead.b[z]&bb != 0) {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
if r1.act.b[z]&bb != 0 {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
f = f1
|
||
|
|
r = r1
|
||
|
|
}
|
||
|
|
|
||
|
|
if LOAD(r, z)&^(r.set.b[z]&^(r.use1.b[z]|r.use2.b[z]))&bb != 0 {
|
||
|
|
addmove(f, bn, rn, 0)
|
||
|
|
}
|
||
|
|
for {
|
||
|
|
r.act.b[z] |= bb
|
||
|
|
p = f.Prog
|
||
|
|
|
||
|
|
if r.use1.b[z]&bb != 0 {
|
||
|
|
if Debug['R'] != 0 && Debug['v'] != 0 {
|
||
|
|
fmt.Printf("%v", p)
|
||
|
|
}
|
||
|
|
addreg(&p.From, rn)
|
||
|
|
if Debug['R'] != 0 && Debug['v'] != 0 {
|
||
|
|
fmt.Printf(" ===change== %v\n", p)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (r.use2.b[z]|r.set.b[z])&bb != 0 {
|
||
|
|
if Debug['R'] != 0 && Debug['v'] != 0 {
|
||
|
|
fmt.Printf("%v", p)
|
||
|
|
}
|
||
|
|
addreg(&p.To, rn)
|
||
|
|
if Debug['R'] != 0 && Debug['v'] != 0 {
|
||
|
|
fmt.Printf(" ===change== %v\n", p)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if STORE(r, z)&r.regdiff.b[z]&bb != 0 {
|
||
|
|
addmove(f, bn, rn, 1)
|
||
|
|
}
|
||
|
|
r.regu |= rb
|
||
|
|
|
||
|
|
if r.refbehind.b[z]&bb != 0 {
|
||
|
|
for f1 = f.P2; f1 != nil; f1 = f1.P2link {
|
||
|
|
if (f1.Data.(*Reg)).refahead.b[z]&bb != 0 {
|
||
|
|
paint3(f1, bn, rb, rn)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if !(r.refahead.b[z]&bb != 0) {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
f1 = f.S2
|
||
|
|
if f1 != nil {
|
||
|
|
if (f1.Data.(*Reg)).refbehind.b[z]&bb != 0 {
|
||
|
|
paint3(f1, bn, rb, rn)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
f = f.S1
|
||
|
|
if f == nil {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
r = f.Data.(*Reg)
|
||
|
|
if r.act.b[z]&bb != 0 {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
if !(r.refbehind.b[z]&bb != 0) {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func addreg(a *obj.Addr, rn int) {
|
||
|
|
a.Sym = nil
|
||
|
|
a.Node = nil
|
||
|
|
a.Offset = 0
|
||
|
|
a.Type = obj.TYPE_REG
|
||
|
|
a.Reg = int16(rn)
|
||
|
|
a.Name = 0
|
||
|
|
|
||
|
|
Ostats.Ncvtreg++
|
||
|
|
}
|
||
|
|
|
||
|
|
func dumpone(f *Flow, isreg int) {
|
||
|
|
var z int
|
||
|
|
var bit Bits
|
||
|
|
var r *Reg
|
||
|
|
|
||
|
|
fmt.Printf("%d:%v", f.Loop, f.Prog)
|
||
|
|
if isreg != 0 {
|
||
|
|
r = f.Data.(*Reg)
|
||
|
|
for z = 0; z < BITS; z++ {
|
||
|
|
bit.b[z] = r.set.b[z] | r.use1.b[z] | r.use2.b[z] | r.refbehind.b[z] | r.refahead.b[z] | r.calbehind.b[z] | r.calahead.b[z] | r.regdiff.b[z] | r.act.b[z] | 0
|
||
|
|
}
|
||
|
|
if bany(&bit) != 0 {
|
||
|
|
fmt.Printf("\t")
|
||
|
|
if bany(&r.set) != 0 {
|
||
|
|
fmt.Printf(" s:%v", Qconv(r.set, 0))
|
||
|
|
}
|
||
|
|
if bany(&r.use1) != 0 {
|
||
|
|
fmt.Printf(" u1:%v", Qconv(r.use1, 0))
|
||
|
|
}
|
||
|
|
if bany(&r.use2) != 0 {
|
||
|
|
fmt.Printf(" u2:%v", Qconv(r.use2, 0))
|
||
|
|
}
|
||
|
|
if bany(&r.refbehind) != 0 {
|
||
|
|
fmt.Printf(" rb:%v ", Qconv(r.refbehind, 0))
|
||
|
|
}
|
||
|
|
if bany(&r.refahead) != 0 {
|
||
|
|
fmt.Printf(" ra:%v ", Qconv(r.refahead, 0))
|
||
|
|
}
|
||
|
|
if bany(&r.calbehind) != 0 {
|
||
|
|
fmt.Printf(" cb:%v ", Qconv(r.calbehind, 0))
|
||
|
|
}
|
||
|
|
if bany(&r.calahead) != 0 {
|
||
|
|
fmt.Printf(" ca:%v ", Qconv(r.calahead, 0))
|
||
|
|
}
|
||
|
|
if bany(&r.regdiff) != 0 {
|
||
|
|
fmt.Printf(" d:%v ", Qconv(r.regdiff, 0))
|
||
|
|
}
|
||
|
|
if bany(&r.act) != 0 {
|
||
|
|
fmt.Printf(" a:%v ", Qconv(r.act, 0))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
fmt.Printf("\n")
|
||
|
|
}
|
||
|
|
|
||
|
|
func Dumpit(str string, r0 *Flow, isreg int) {
|
||
|
|
var r *Flow
|
||
|
|
var r1 *Flow
|
||
|
|
|
||
|
|
fmt.Printf("\n%s\n", str)
|
||
|
|
for r = r0; r != nil; r = r.Link {
|
||
|
|
dumpone(r, isreg)
|
||
|
|
r1 = r.P2
|
||
|
|
if r1 != nil {
|
||
|
|
fmt.Printf("\tpred:")
|
||
|
|
for ; r1 != nil; r1 = r1.P2link {
|
||
|
|
fmt.Printf(" %.4d", uint(int(r1.Prog.Pc)))
|
||
|
|
}
|
||
|
|
if r.P1 != nil {
|
||
|
|
fmt.Printf(" (and %.4d)", uint(int(r.P1.Prog.Pc)))
|
||
|
|
} else {
|
||
|
|
fmt.Printf(" (only)")
|
||
|
|
}
|
||
|
|
fmt.Printf("\n")
|
||
|
|
}
|
||
|
|
|
||
|
|
// Print successors if it's not just the next one
|
||
|
|
if r.S1 != r.Link || r.S2 != nil {
|
||
|
|
fmt.Printf("\tsucc:")
|
||
|
|
if r.S1 != nil {
|
||
|
|
fmt.Printf(" %.4d", uint(int(r.S1.Prog.Pc)))
|
||
|
|
}
|
||
|
|
if r.S2 != nil {
|
||
|
|
fmt.Printf(" %.4d", uint(int(r.S2.Prog.Pc)))
|
||
|
|
}
|
||
|
|
fmt.Printf("\n")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func regopt(firstp *obj.Prog) {
|
||
|
|
var f *Flow
|
||
|
|
var f1 *Flow
|
||
|
|
var r *Reg
|
||
|
|
var p *obj.Prog
|
||
|
|
var g *Graph
|
||
|
|
var info ProgInfo
|
||
|
|
var i int
|
||
|
|
var z int
|
||
|
|
var active int
|
||
|
|
var vreg uint64
|
||
|
|
var usedreg uint64
|
||
|
|
var mask uint64
|
||
|
|
var nreg int
|
||
|
|
var regnames []string
|
||
|
|
var bit Bits
|
||
|
|
var rgp *Rgn
|
||
|
|
|
||
|
|
if first != 0 {
|
||
|
|
first = 0
|
||
|
|
}
|
||
|
|
|
||
|
|
mergetemp(firstp)
|
||
|
|
|
||
|
|
/*
|
||
|
|
* control flow is more complicated in generated go code
|
||
|
|
* than in generated c code. define pseudo-variables for
|
||
|
|
* registers, so we have complete register usage information.
|
||
|
|
*/
|
||
|
|
regnames = Thearch.Regnames(&nreg)
|
||
|
|
|
||
|
|
nvar = nreg
|
||
|
|
for i = 0; i < nreg; i++ {
|
||
|
|
var_[i] = Var{}
|
||
|
|
}
|
||
|
|
for i = 0; i < nreg; i++ {
|
||
|
|
if regnodes[i] == nil {
|
||
|
|
regnodes[i] = newname(Lookup(regnames[i]))
|
||
|
|
}
|
||
|
|
var_[i].node = regnodes[i]
|
||
|
|
}
|
||
|
|
|
||
|
|
regbits = Thearch.Excludedregs()
|
||
|
|
externs = zbits
|
||
|
|
params = zbits
|
||
|
|
consts = zbits
|
||
|
|
addrs = zbits
|
||
|
|
ivar = zbits
|
||
|
|
ovar = zbits
|
||
|
|
|
||
|
|
/*
|
||
|
|
* pass 1
|
||
|
|
* build aux data structure
|
||
|
|
* allocate pcs
|
||
|
|
* find use and set of variables
|
||
|
|
*/
|
||
|
|
g = Flowstart(firstp, func() interface{} { return new(Reg) })
|
||
|
|
|
||
|
|
if g == nil {
|
||
|
|
for i = 0; i < nvar; i++ {
|
||
|
|
var_[i].node.Opt = nil
|
||
|
|
}
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
firstf = g.Start
|
||
|
|
|
||
|
|
for f = firstf; f != nil; f = f.Link {
|
||
|
|
p = f.Prog
|
||
|
|
if p.As == obj.AVARDEF || p.As == obj.AVARKILL {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
Thearch.Proginfo(&info, p)
|
||
|
|
|
||
|
|
// Avoid making variables for direct-called functions.
|
||
|
|
if p.As == obj.ACALL && p.To.Type == obj.TYPE_MEM && p.To.Name == obj.NAME_EXTERN {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
|
||
|
|
// from vs to doesn't matter for registers.
|
||
|
|
r = f.Data.(*Reg)
|
||
|
|
|
||
|
|
r.use1.b[0] |= info.Reguse | info.Regindex
|
||
|
|
r.set.b[0] |= info.Regset
|
||
|
|
|
||
|
|
bit = mkvar(f, &p.From)
|
||
|
|
if bany(&bit) != 0 {
|
||
|
|
if info.Flags&LeftAddr != 0 {
|
||
|
|
setaddrs(bit)
|
||
|
|
}
|
||
|
|
if info.Flags&LeftRead != 0 {
|
||
|
|
for z = 0; z < BITS; z++ {
|
||
|
|
r.use1.b[z] |= bit.b[z]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if info.Flags&LeftWrite != 0 {
|
||
|
|
for z = 0; z < BITS; z++ {
|
||
|
|
r.set.b[z] |= bit.b[z]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Compute used register for reg
|
||
|
|
if info.Flags&RegRead != 0 {
|
||
|
|
r.use1.b[0] |= Thearch.RtoB(int(p.Reg))
|
||
|
|
}
|
||
|
|
|
||
|
|
// Currently we never generate three register forms.
|
||
|
|
// If we do, this will need to change.
|
||
|
|
if p.From3.Type != obj.TYPE_NONE {
|
||
|
|
Fatal("regopt not implemented for from3")
|
||
|
|
}
|
||
|
|
|
||
|
|
bit = mkvar(f, &p.To)
|
||
|
|
if bany(&bit) != 0 {
|
||
|
|
if info.Flags&RightAddr != 0 {
|
||
|
|
setaddrs(bit)
|
||
|
|
}
|
||
|
|
if info.Flags&RightRead != 0 {
|
||
|
|
for z = 0; z < BITS; z++ {
|
||
|
|
r.use2.b[z] |= bit.b[z]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if info.Flags&RightWrite != 0 {
|
||
|
|
for z = 0; z < BITS; z++ {
|
||
|
|
r.set.b[z] |= bit.b[z]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
for i = 0; i < nvar; i++ {
|
||
|
|
var v *Var
|
||
|
|
v = &var_[i:][0]
|
||
|
|
if v.addr != 0 {
|
||
|
|
bit = blsh(uint(i))
|
||
|
|
for z = 0; z < BITS; z++ {
|
||
|
|
addrs.b[z] |= bit.b[z]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if Debug['R'] != 0 && Debug['v'] != 0 {
|
||
|
|
fmt.Printf("bit=%2d addr=%d et=%v w=%-2d s=%v + %d\n", i, v.addr, Econv(int(v.etype), 0), v.width, Nconv(v.node, 0), v.offset)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if Debug['R'] != 0 && Debug['v'] != 0 {
|
||
|
|
Dumpit("pass1", firstf, 1)
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
* pass 2
|
||
|
|
* find looping structure
|
||
|
|
*/
|
||
|
|
flowrpo(g)
|
||
|
|
|
||
|
|
if Debug['R'] != 0 && Debug['v'] != 0 {
|
||
|
|
Dumpit("pass2", firstf, 1)
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
* pass 2.5
|
||
|
|
* iterate propagating fat vardef covering forward
|
||
|
|
* r->act records vars with a VARDEF since the last CALL.
|
||
|
|
* (r->act will be reused in pass 5 for something else,
|
||
|
|
* but we'll be done with it by then.)
|
||
|
|
*/
|
||
|
|
active = 0
|
||
|
|
|
||
|
|
for f = firstf; f != nil; f = f.Link {
|
||
|
|
f.Active = 0
|
||
|
|
r = f.Data.(*Reg)
|
||
|
|
r.act = zbits
|
||
|
|
}
|
||
|
|
|
||
|
|
for f = firstf; f != nil; f = f.Link {
|
||
|
|
p = f.Prog
|
||
|
|
if p.As == obj.AVARDEF && Isfat(((p.To.Node).(*Node)).Type) != 0 && ((p.To.Node).(*Node)).Opt != nil {
|
||
|
|
active++
|
||
|
|
walkvardef(p.To.Node.(*Node), f, active)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
* pass 3
|
||
|
|
* iterate propagating usage
|
||
|
|
* back until flow graph is complete
|
||
|
|
*/
|
||
|
|
loop1:
|
||
|
|
change = 0
|
||
|
|
|
||
|
|
for f = firstf; f != nil; f = f.Link {
|
||
|
|
f.Active = 0
|
||
|
|
}
|
||
|
|
for f = firstf; f != nil; f = f.Link {
|
||
|
|
if f.Prog.As == obj.ARET {
|
||
|
|
prop(f, zbits, zbits)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* pick up unreachable code */
|
||
|
|
loop11:
|
||
|
|
i = 0
|
||
|
|
|
||
|
|
for f = firstf; f != nil; f = f1 {
|
||
|
|
f1 = f.Link
|
||
|
|
if f1 != nil && f1.Active != 0 && !(f.Active != 0) {
|
||
|
|
prop(f, zbits, zbits)
|
||
|
|
i = 1
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if i != 0 {
|
||
|
|
goto loop11
|
||
|
|
}
|
||
|
|
if change != 0 {
|
||
|
|
goto loop1
|
||
|
|
}
|
||
|
|
|
||
|
|
if Debug['R'] != 0 && Debug['v'] != 0 {
|
||
|
|
Dumpit("pass3", firstf, 1)
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
* pass 4
|
||
|
|
* iterate propagating register/variable synchrony
|
||
|
|
* forward until graph is complete
|
||
|
|
*/
|
||
|
|
loop2:
|
||
|
|
change = 0
|
||
|
|
|
||
|
|
for f = firstf; f != nil; f = f.Link {
|
||
|
|
f.Active = 0
|
||
|
|
}
|
||
|
|
synch(firstf, zbits)
|
||
|
|
if change != 0 {
|
||
|
|
goto loop2
|
||
|
|
}
|
||
|
|
|
||
|
|
if Debug['R'] != 0 && Debug['v'] != 0 {
|
||
|
|
Dumpit("pass4", firstf, 1)
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
* pass 4.5
|
||
|
|
* move register pseudo-variables into regu.
|
||
|
|
*/
|
||
|
|
mask = (1 << uint(nreg)) - 1
|
||
|
|
for f = firstf; f != nil; f = f.Link {
|
||
|
|
r = f.Data.(*Reg)
|
||
|
|
r.regu = (r.refbehind.b[0] | r.set.b[0]) & mask
|
||
|
|
r.set.b[0] &^= mask
|
||
|
|
r.use1.b[0] &^= mask
|
||
|
|
r.use2.b[0] &^= mask
|
||
|
|
r.refbehind.b[0] &^= mask
|
||
|
|
r.refahead.b[0] &^= mask
|
||
|
|
r.calbehind.b[0] &^= mask
|
||
|
|
r.calahead.b[0] &^= mask
|
||
|
|
r.regdiff.b[0] &^= mask
|
||
|
|
r.act.b[0] &^= mask
|
||
|
|
}
|
||
|
|
|
||
|
|
if Debug['R'] != 0 && Debug['v'] != 0 {
|
||
|
|
Dumpit("pass4.5", firstf, 1)
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
* pass 5
|
||
|
|
* isolate regions
|
||
|
|
* calculate costs (paint1)
|
||
|
|
*/
|
||
|
|
f = firstf
|
||
|
|
|
||
|
|
if f != nil {
|
||
|
|
r = f.Data.(*Reg)
|
||
|
|
for z = 0; z < BITS; z++ {
|
||
|
|
bit.b[z] = (r.refahead.b[z] | r.calahead.b[z]) &^ (externs.b[z] | params.b[z] | addrs.b[z] | consts.b[z])
|
||
|
|
}
|
||
|
|
if bany(&bit) != 0 && !(f.Refset != 0) {
|
||
|
|
// should never happen - all variables are preset
|
||
|
|
if Debug['w'] != 0 {
|
||
|
|
fmt.Printf("%v: used and not set: %v\n", f.Prog.Line(), Qconv(bit, 0))
|
||
|
|
}
|
||
|
|
f.Refset = 1
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
for f = firstf; f != nil; f = f.Link {
|
||
|
|
(f.Data.(*Reg)).act = zbits
|
||
|
|
}
|
||
|
|
nregion = 0
|
||
|
|
for f = firstf; f != nil; f = f.Link {
|
||
|
|
r = f.Data.(*Reg)
|
||
|
|
for z = 0; z < BITS; z++ {
|
||
|
|
bit.b[z] = r.set.b[z] &^ (r.refahead.b[z] | r.calahead.b[z] | addrs.b[z])
|
||
|
|
}
|
||
|
|
if bany(&bit) != 0 && !(f.Refset != 0) {
|
||
|
|
if Debug['w'] != 0 {
|
||
|
|
fmt.Printf("%v: set and not used: %v\n", f.Prog.Line(), Qconv(bit, 0))
|
||
|
|
}
|
||
|
|
f.Refset = 1
|
||
|
|
Thearch.Excise(f)
|
||
|
|
}
|
||
|
|
|
||
|
|
for z = 0; z < BITS; z++ {
|
||
|
|
bit.b[z] = LOAD(r, z) &^ (r.act.b[z] | addrs.b[z])
|
||
|
|
}
|
||
|
|
for bany(&bit) != 0 {
|
||
|
|
i = bnum(bit)
|
||
|
|
change = 0
|
||
|
|
paint1(f, i)
|
||
|
|
biclr(&bit, uint(i))
|
||
|
|
if change <= 0 {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
if nregion >= NRGN {
|
||
|
|
if Debug['R'] != 0 && Debug['v'] != 0 {
|
||
|
|
fmt.Printf("too many regions\n")
|
||
|
|
}
|
||
|
|
goto brk
|
||
|
|
}
|
||
|
|
|
||
|
|
rgp = ®ion[nregion]
|
||
|
|
rgp.enter = f
|
||
|
|
rgp.varno = int16(i)
|
||
|
|
rgp.cost = int16(change)
|
||
|
|
nregion++
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
brk:
|
||
|
|
sort.Sort(rcmp(region[:nregion]))
|
||
|
|
|
||
|
|
if Debug['R'] != 0 && Debug['v'] != 0 {
|
||
|
|
Dumpit("pass5", firstf, 1)
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
* pass 6
|
||
|
|
* determine used registers (paint2)
|
||
|
|
* replace code (paint3)
|
||
|
|
*/
|
||
|
|
if Debug['R'] != 0 && Debug['v'] != 0 {
|
||
|
|
fmt.Printf("\nregisterizing\n")
|
||
|
|
}
|
||
|
|
for i = 0; i < nregion; i++ {
|
||
|
|
rgp = ®ion[i]
|
||
|
|
if Debug['R'] != 0 && Debug['v'] != 0 {
|
||
|
|
fmt.Printf("region %d: cost %d varno %d enter %d\n", i, rgp.cost, rgp.varno, rgp.enter.Prog.Pc)
|
||
|
|
}
|
||
|
|
bit = blsh(uint(rgp.varno))
|
||
|
|
usedreg = paint2(rgp.enter, int(rgp.varno), 0)
|
||
|
|
vreg = allreg(usedreg, rgp)
|
||
|
|
if rgp.regno != 0 {
|
||
|
|
if Debug['R'] != 0 && Debug['v'] != 0 {
|
||
|
|
var v *Var
|
||
|
|
|
||
|
|
v = &var_[rgp.varno:][0]
|
||
|
|
fmt.Printf("registerize %v+%d (bit=%2d et=%v) in %v usedreg=%#x vreg=%#x\n", Nconv(v.node, 0), v.offset, rgp.varno, Econv(int(v.etype), 0), Ctxt.Rconv(int(rgp.regno)), usedreg, vreg)
|
||
|
|
}
|
||
|
|
|
||
|
|
paint3(rgp.enter, int(rgp.varno), vreg, int(rgp.regno))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
* free aux structures. peep allocates new ones.
|
||
|
|
*/
|
||
|
|
for i = 0; i < nvar; i++ {
|
||
|
|
var_[i].node.Opt = nil
|
||
|
|
}
|
||
|
|
Flowend(g)
|
||
|
|
firstf = nil
|
||
|
|
|
||
|
|
if Debug['R'] != 0 && Debug['v'] != 0 {
|
||
|
|
// Rebuild flow graph, since we inserted instructions
|
||
|
|
g = Flowstart(firstp, nil)
|
||
|
|
|
||
|
|
firstf = g.Start
|
||
|
|
Dumpit("pass6", firstf, 0)
|
||
|
|
Flowend(g)
|
||
|
|
firstf = nil
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
* pass 7
|
||
|
|
* peep-hole on basic block
|
||
|
|
*/
|
||
|
|
if !(Debug['R'] != 0) || Debug['P'] != 0 {
|
||
|
|
Thearch.Peep(firstp)
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
* eliminate nops
|
||
|
|
*/
|
||
|
|
for p = firstp; p != nil; p = p.Link {
|
||
|
|
for p.Link != nil && p.Link.As == obj.ANOP {
|
||
|
|
p.Link = p.Link.Link
|
||
|
|
}
|
||
|
|
if p.To.Type == obj.TYPE_BRANCH {
|
||
|
|
for p.To.U.Branch != nil && p.To.U.Branch.As == obj.ANOP {
|
||
|
|
p.To.U.Branch = p.To.U.Branch.Link
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if Debug['R'] != 0 {
|
||
|
|
if Ostats.Ncvtreg != 0 || Ostats.Nspill != 0 || Ostats.Nreload != 0 || Ostats.Ndelmov != 0 || Ostats.Nvar != 0 || Ostats.Naddr != 0 || false {
|
||
|
|
fmt.Printf("\nstats\n")
|
||
|
|
}
|
||
|
|
|
||
|
|
if Ostats.Ncvtreg != 0 {
|
||
|
|
fmt.Printf("\t%4d cvtreg\n", Ostats.Ncvtreg)
|
||
|
|
}
|
||
|
|
if Ostats.Nspill != 0 {
|
||
|
|
fmt.Printf("\t%4d spill\n", Ostats.Nspill)
|
||
|
|
}
|
||
|
|
if Ostats.Nreload != 0 {
|
||
|
|
fmt.Printf("\t%4d reload\n", Ostats.Nreload)
|
||
|
|
}
|
||
|
|
if Ostats.Ndelmov != 0 {
|
||
|
|
fmt.Printf("\t%4d delmov\n", Ostats.Ndelmov)
|
||
|
|
}
|
||
|
|
if Ostats.Nvar != 0 {
|
||
|
|
fmt.Printf("\t%4d var\n", Ostats.Nvar)
|
||
|
|
}
|
||
|
|
if Ostats.Naddr != 0 {
|
||
|
|
fmt.Printf("\t%4d addr\n", Ostats.Naddr)
|
||
|
|
}
|
||
|
|
|
||
|
|
Ostats = OptStats{}
|
||
|
|
}
|
||
|
|
}
|