2009-11-06 17:51:49 -07:00
|
|
|
// 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.
|
|
|
|
|
2011-08-25 14:25:10 -06:00
|
|
|
#include <u.h>
|
|
|
|
#include <libc.h>
|
2009-11-06 17:51:49 -07:00
|
|
|
#include "gg.h"
|
|
|
|
#include "opt.h"
|
|
|
|
|
|
|
|
#define REGEXT 0
|
|
|
|
|
2013-08-12 20:02:10 -06:00
|
|
|
static void conprop(Flow *r);
|
|
|
|
static void elimshortmov(Graph*);
|
|
|
|
static int subprop(Flow*);
|
|
|
|
static int copyprop(Graph*, Flow*);
|
|
|
|
static int copy1(Adr*, Adr*, Flow*, int);
|
|
|
|
static int copyas(Adr*, Adr*);
|
|
|
|
static int copyau(Adr*, Adr*);
|
|
|
|
static int copysub(Adr*, Adr*, Adr*, int);
|
2009-11-06 17:51:49 -07:00
|
|
|
|
2013-09-11 13:22:11 -06:00
|
|
|
static uint32 gactive;
|
|
|
|
|
2009-11-06 17:51:49 -07:00
|
|
|
// do we need the carry bit
|
|
|
|
static int
|
|
|
|
needc(Prog *p)
|
|
|
|
{
|
2013-08-12 11:05:40 -06:00
|
|
|
ProgInfo info;
|
|
|
|
|
2009-11-06 17:51:49 -07:00
|
|
|
while(p != P) {
|
2013-08-12 11:05:40 -06:00
|
|
|
proginfo(&info, p);
|
|
|
|
if(info.flags & UseCarry)
|
2009-11-06 17:51:49 -07:00
|
|
|
return 1;
|
2013-08-12 11:05:40 -06:00
|
|
|
if(info.flags & (SetCarry|KillCarry))
|
2009-11-06 17:51:49 -07:00
|
|
|
return 0;
|
|
|
|
p = p->link;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-08-12 20:02:10 -06:00
|
|
|
static Flow*
|
|
|
|
rnops(Flow *r)
|
2009-11-06 17:51:49 -07:00
|
|
|
{
|
|
|
|
Prog *p;
|
2013-08-12 20:02:10 -06:00
|
|
|
Flow *r1;
|
2009-11-06 17:51:49 -07:00
|
|
|
|
2013-08-12 20:02:10 -06:00
|
|
|
if(r != nil)
|
2009-11-06 17:51:49 -07:00
|
|
|
for(;;) {
|
|
|
|
p = r->prog;
|
|
|
|
if(p->as != ANOP || p->from.type != D_NONE || p->to.type != D_NONE)
|
|
|
|
break;
|
|
|
|
r1 = uniqs(r);
|
2013-08-12 20:02:10 -06:00
|
|
|
if(r1 == nil)
|
2009-11-06 17:51:49 -07:00
|
|
|
break;
|
|
|
|
r = r1;
|
|
|
|
}
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2013-08-12 20:02:10 -06:00
|
|
|
peep(Prog *firstp)
|
2009-11-06 17:51:49 -07:00
|
|
|
{
|
2013-08-12 20:02:10 -06:00
|
|
|
Flow *r, *r1;
|
|
|
|
Graph *g;
|
2009-11-06 17:51:49 -07:00
|
|
|
Prog *p, *p1;
|
|
|
|
int t;
|
|
|
|
|
2013-08-12 20:02:10 -06:00
|
|
|
g = flowstart(firstp, sizeof(Flow));
|
|
|
|
if(g == nil)
|
|
|
|
return;
|
2013-09-11 13:22:11 -06:00
|
|
|
gactive = 0;
|
2013-01-02 14:55:23 -07:00
|
|
|
|
2012-09-01 08:40:54 -06:00
|
|
|
// byte, word arithmetic elimination.
|
2013-08-12 20:02:10 -06:00
|
|
|
elimshortmov(g);
|
2009-11-06 17:51:49 -07:00
|
|
|
|
|
|
|
// constant propagation
|
2013-08-14 13:24:48 -06:00
|
|
|
// find MOV $con,R followed by
|
|
|
|
// another MOV $con,R without
|
|
|
|
// setting R in the interim
|
2013-08-12 20:02:10 -06:00
|
|
|
for(r=g->start; r!=nil; r=r->link) {
|
2009-11-06 17:51:49 -07:00
|
|
|
p = r->prog;
|
|
|
|
switch(p->as) {
|
|
|
|
case ALEAL:
|
|
|
|
if(regtyp(&p->to))
|
|
|
|
if(p->from.sym != S)
|
2013-07-05 08:11:22 -06:00
|
|
|
if(p->from.index == D_NONE || p->from.index == D_CONST)
|
2009-11-06 17:51:49 -07:00
|
|
|
conprop(r);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case AMOVB:
|
|
|
|
case AMOVW:
|
|
|
|
case AMOVL:
|
2013-01-02 14:55:23 -07:00
|
|
|
case AMOVSS:
|
|
|
|
case AMOVSD:
|
2009-11-06 17:51:49 -07:00
|
|
|
if(regtyp(&p->to))
|
|
|
|
if(p->from.type == D_CONST)
|
|
|
|
conprop(r);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
loop1:
|
|
|
|
if(debug['P'] && debug['v'])
|
2013-08-12 20:02:10 -06:00
|
|
|
dumpit("loop1", g->start, 0);
|
2009-11-06 17:51:49 -07:00
|
|
|
|
|
|
|
t = 0;
|
2013-08-12 20:02:10 -06:00
|
|
|
for(r=g->start; r!=nil; r=r->link) {
|
2009-11-06 17:51:49 -07:00
|
|
|
p = r->prog;
|
|
|
|
switch(p->as) {
|
|
|
|
case AMOVL:
|
2013-01-02 14:55:23 -07:00
|
|
|
case AMOVSS:
|
|
|
|
case AMOVSD:
|
2009-11-06 17:51:49 -07:00
|
|
|
if(regtyp(&p->to))
|
|
|
|
if(regtyp(&p->from)) {
|
2013-08-12 20:02:10 -06:00
|
|
|
if(copyprop(g, r)) {
|
2009-11-06 17:51:49 -07:00
|
|
|
excise(r);
|
|
|
|
t++;
|
|
|
|
} else
|
2013-08-12 20:02:10 -06:00
|
|
|
if(subprop(r) && copyprop(g, r)) {
|
2009-11-06 17:51:49 -07:00
|
|
|
excise(r);
|
|
|
|
t++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case AMOVBLZX:
|
|
|
|
case AMOVWLZX:
|
|
|
|
case AMOVBLSX:
|
|
|
|
case AMOVWLSX:
|
|
|
|
if(regtyp(&p->to)) {
|
|
|
|
r1 = rnops(uniqs(r));
|
2013-08-12 20:02:10 -06:00
|
|
|
if(r1 != nil) {
|
2009-11-06 17:51:49 -07:00
|
|
|
p1 = r1->prog;
|
|
|
|
if(p->as == p1->as && p->to.type == p1->from.type){
|
|
|
|
p1->as = AMOVL;
|
|
|
|
t++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case AADDL:
|
|
|
|
case AADDW:
|
|
|
|
if(p->from.type != D_CONST || needc(p->link))
|
|
|
|
break;
|
|
|
|
if(p->from.offset == -1){
|
|
|
|
if(p->as == AADDL)
|
|
|
|
p->as = ADECL;
|
|
|
|
else
|
|
|
|
p->as = ADECW;
|
|
|
|
p->from = zprog.from;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if(p->from.offset == 1){
|
|
|
|
if(p->as == AADDL)
|
|
|
|
p->as = AINCL;
|
|
|
|
else
|
|
|
|
p->as = AINCW;
|
|
|
|
p->from = zprog.from;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ASUBL:
|
|
|
|
case ASUBW:
|
|
|
|
if(p->from.type != D_CONST || needc(p->link))
|
|
|
|
break;
|
|
|
|
if(p->from.offset == -1) {
|
|
|
|
if(p->as == ASUBL)
|
|
|
|
p->as = AINCL;
|
|
|
|
else
|
|
|
|
p->as = AINCW;
|
|
|
|
p->from = zprog.from;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if(p->from.offset == 1){
|
|
|
|
if(p->as == ASUBL)
|
|
|
|
p->as = ADECL;
|
|
|
|
else
|
|
|
|
p->as = ADECW;
|
|
|
|
p->from = zprog.from;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(t)
|
|
|
|
goto loop1;
|
2013-01-02 14:55:23 -07:00
|
|
|
|
|
|
|
// 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.
|
2013-08-12 20:02:10 -06:00
|
|
|
for(r=g->start; r!=nil; r=r->link) {
|
2013-01-02 14:55:23 -07:00
|
|
|
p = r->prog;
|
|
|
|
if(p->as == AMOVSD)
|
|
|
|
if(regtyp(&p->from))
|
|
|
|
if(regtyp(&p->to))
|
|
|
|
p->as = AMOVAPD;
|
|
|
|
}
|
cmd/gc: add temporary-merging optimization pass
The compilers assume they can generate temporary variables
as needed to preserve the right semantics or simplify code
generation and the back end will still generate good code.
This turns out not to be true. The back ends will only
track the first 128 variables per function and give up
on the remainder. That needs to be fixed too, in a later CL.
This CL merges temporary variables with equal types and
non-overlapping lifetimes using the greedy algorithm in
Poletto and Sarkar, "Linear Scan Register Allocation",
ACM TOPLAS 1999.
The result can be striking in the right functions.
Top 20 frame size changes in a 6g godoc binary by bytes saved:
5464 1984 (-3480, -63.7%) go/build.(*Context).Import
4456 1824 (-2632, -59.1%) go/printer.(*printer).expr1
2560 80 (-2480, -96.9%) time.nextStdChunk
3496 1608 (-1888, -54.0%) go/printer.(*printer).stmt
1896 272 (-1624, -85.7%) net/http.init
2688 1400 (-1288, -47.9%) fmt.(*pp).printReflectValue
2800 1512 (-1288, -46.0%) main.main
3296 2016 (-1280, -38.8%) crypto/tls.(*Conn).clientHandshake
1664 488 (-1176, -70.7%) time.loadZoneZip
1760 608 (-1152, -65.5%) time.parse
4104 3072 (-1032, -25.1%) runtime/pprof.writeHeap
1680 712 ( -968, -57.6%) go/ast.Walk
2488 1560 ( -928, -37.3%) crypto/x509.parseCertificate
1128 392 ( -736, -65.2%) math/big.nat.divLarge
1528 864 ( -664, -43.5%) go/printer.(*printer).fieldList
1360 712 ( -648, -47.6%) regexp/syntax.(*parser).factor
2104 1528 ( -576, -27.4%) encoding/asn1.parseField
1064 504 ( -560, -52.6%) encoding/xml.(*Decoder).text
584 48 ( -536, -91.8%) html.init
1400 864 ( -536, -38.3%) go/doc.playExample
In the same godoc build, cuts the number of functions with
too many vars from 83 to 32.
R=ken2
CC=golang-dev
https://golang.org/cl/12829043
2013-08-12 22:09:31 -06:00
|
|
|
|
|
|
|
flowend(g);
|
2009-11-06 17:51:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2013-08-12 20:02:10 -06:00
|
|
|
excise(Flow *r)
|
2009-11-06 17:51:49 -07:00
|
|
|
{
|
|
|
|
Prog *p;
|
|
|
|
|
|
|
|
p = r->prog;
|
|
|
|
if(debug['P'] && debug['v'])
|
|
|
|
print("%P ===delete===\n", p);
|
|
|
|
|
|
|
|
p->as = ANOP;
|
|
|
|
p->from = zprog.from;
|
|
|
|
p->to = zprog.to;
|
|
|
|
|
|
|
|
ostats.ndelmov++;
|
|
|
|
}
|
|
|
|
|
2013-08-15 12:38:32 -06:00
|
|
|
int
|
2009-11-06 17:51:49 -07:00
|
|
|
regtyp(Adr *a)
|
|
|
|
{
|
|
|
|
int t;
|
|
|
|
|
|
|
|
t = a->type;
|
|
|
|
if(t >= D_AX && t <= D_DI)
|
|
|
|
return 1;
|
2013-01-02 14:55:23 -07:00
|
|
|
if(t >= D_X0 && t <= D_X7)
|
|
|
|
return 1;
|
2009-11-06 17:51:49 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-09-01 08:40:54 -06:00
|
|
|
// 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 64-bit register without
|
|
|
|
// causing any trouble.
|
|
|
|
static void
|
2013-08-12 20:02:10 -06:00
|
|
|
elimshortmov(Graph *g)
|
2012-09-01 08:40:54 -06:00
|
|
|
{
|
|
|
|
Prog *p;
|
2013-08-12 20:02:10 -06:00
|
|
|
Flow *r;
|
2012-09-01 08:40:54 -06:00
|
|
|
|
2013-08-12 20:02:10 -06:00
|
|
|
for(r=g->start; r!=nil; r=r->link) {
|
2012-09-01 08:40:54 -06:00
|
|
|
p = r->prog;
|
|
|
|
if(regtyp(&p->to)) {
|
|
|
|
switch(p->as) {
|
|
|
|
case AINCB:
|
|
|
|
case AINCW:
|
|
|
|
p->as = AINCL;
|
|
|
|
break;
|
|
|
|
case ADECB:
|
|
|
|
case ADECW:
|
|
|
|
p->as = ADECL;
|
|
|
|
break;
|
|
|
|
case ANEGB:
|
|
|
|
case ANEGW:
|
|
|
|
p->as = ANEGL;
|
|
|
|
break;
|
|
|
|
case ANOTB:
|
|
|
|
case ANOTW:
|
|
|
|
p->as = ANOTL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if(regtyp(&p->from) || p->from.type == D_CONST) {
|
|
|
|
// move or artihmetic into partial register.
|
|
|
|
// from another register or constant can be movl.
|
|
|
|
// we don't switch to 32-bit arithmetic if it can
|
|
|
|
// change how the carry bit is set (and the carry bit is needed).
|
|
|
|
switch(p->as) {
|
|
|
|
case AMOVB:
|
|
|
|
case AMOVW:
|
|
|
|
p->as = AMOVL;
|
|
|
|
break;
|
|
|
|
case AADDB:
|
|
|
|
case AADDW:
|
|
|
|
if(!needc(p->link))
|
|
|
|
p->as = AADDL;
|
|
|
|
break;
|
|
|
|
case ASUBB:
|
|
|
|
case ASUBW:
|
|
|
|
if(!needc(p->link))
|
|
|
|
p->as = ASUBL;
|
|
|
|
break;
|
|
|
|
case AMULB:
|
|
|
|
case AMULW:
|
|
|
|
p->as = AMULL;
|
|
|
|
break;
|
|
|
|
case AIMULB:
|
|
|
|
case AIMULW:
|
|
|
|
p->as = AIMULL;
|
|
|
|
break;
|
|
|
|
case AANDB:
|
|
|
|
case AANDW:
|
|
|
|
p->as = AANDL;
|
|
|
|
break;
|
|
|
|
case AORB:
|
|
|
|
case AORW:
|
|
|
|
p->as = AORL;
|
|
|
|
break;
|
|
|
|
case AXORB:
|
|
|
|
case AXORW:
|
|
|
|
p->as = AXORL;
|
|
|
|
break;
|
|
|
|
case ASHLB:
|
|
|
|
case ASHLW:
|
|
|
|
p->as = ASHLL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// explicit zero extension
|
|
|
|
switch(p->as) {
|
|
|
|
case AMOVB:
|
|
|
|
p->as = AMOVBLZX;
|
|
|
|
break;
|
|
|
|
case AMOVW:
|
|
|
|
p->as = AMOVWLZX;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-11-06 17:51:49 -07:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2013-08-12 20:02:10 -06:00
|
|
|
static int
|
|
|
|
subprop(Flow *r0)
|
2009-11-06 17:51:49 -07:00
|
|
|
{
|
|
|
|
Prog *p;
|
|
|
|
Adr *v1, *v2;
|
2013-08-12 20:02:10 -06:00
|
|
|
Flow *r;
|
2009-11-06 17:51:49 -07:00
|
|
|
int t;
|
2013-08-12 11:05:40 -06:00
|
|
|
ProgInfo info;
|
2009-11-06 17:51:49 -07:00
|
|
|
|
|
|
|
p = r0->prog;
|
|
|
|
v1 = &p->from;
|
|
|
|
if(!regtyp(v1))
|
|
|
|
return 0;
|
|
|
|
v2 = &p->to;
|
|
|
|
if(!regtyp(v2))
|
|
|
|
return 0;
|
2013-08-12 20:02:10 -06:00
|
|
|
for(r=uniqp(r0); r!=nil; r=uniqp(r)) {
|
2013-08-12 11:05:40 -06:00
|
|
|
if(debug['P'] && debug['v'])
|
|
|
|
print("\t? %P\n", r->prog);
|
2013-08-12 20:02:10 -06:00
|
|
|
if(uniqs(r) == nil)
|
2009-11-06 17:51:49 -07:00
|
|
|
break;
|
|
|
|
p = r->prog;
|
2013-08-12 11:05:40 -06:00
|
|
|
proginfo(&info, p);
|
|
|
|
if(info.flags & Call)
|
2009-11-06 17:51:49 -07:00
|
|
|
return 0;
|
|
|
|
|
2013-08-12 11:05:40 -06:00
|
|
|
if(info.reguse | info.regset)
|
2009-11-06 17:51:49 -07:00
|
|
|
return 0;
|
|
|
|
|
2013-08-12 11:05:40 -06:00
|
|
|
if((info.flags & Move) && (info.flags & (SizeL|SizeQ|SizeF|SizeD)) && p->to.type == v1->type)
|
|
|
|
goto gotit;
|
|
|
|
|
|
|
|
if(copyau(&p->from, v2) || copyau(&p->to, v2))
|
2009-11-06 17:51:49 -07:00
|
|
|
break;
|
2013-08-12 11:05:40 -06:00
|
|
|
if(copysub(&p->from, v1, v2, 0) || copysub(&p->to, v1, v2, 0))
|
2009-11-06 17:51:49 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
gotit:
|
|
|
|
copysub(&p->to, v1, v2, 1);
|
|
|
|
if(debug['P']) {
|
|
|
|
print("gotit: %D->%D\n%P", v1, v2, r->prog);
|
|
|
|
if(p->from.type == v2->type)
|
|
|
|
print(" excise");
|
|
|
|
print("\n");
|
|
|
|
}
|
|
|
|
for(r=uniqs(r); r!=r0; r=uniqs(r)) {
|
|
|
|
p = r->prog;
|
|
|
|
copysub(&p->from, v1, v2, 1);
|
|
|
|
copysub(&p->to, v1, v2, 1);
|
|
|
|
if(debug['P'])
|
|
|
|
print("%P\n", r->prog);
|
|
|
|
}
|
|
|
|
t = v1->type;
|
|
|
|
v1->type = v2->type;
|
|
|
|
v2->type = t;
|
|
|
|
if(debug['P'])
|
|
|
|
print("%P last\n", r->prog);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
*/
|
2013-08-12 20:02:10 -06:00
|
|
|
static int
|
|
|
|
copyprop(Graph *g, Flow *r0)
|
2009-11-06 17:51:49 -07:00
|
|
|
{
|
|
|
|
Prog *p;
|
|
|
|
Adr *v1, *v2;
|
|
|
|
|
2013-09-11 13:22:11 -06:00
|
|
|
USED(g);
|
2009-11-06 17:51:49 -07:00
|
|
|
p = r0->prog;
|
|
|
|
v1 = &p->from;
|
|
|
|
v2 = &p->to;
|
|
|
|
if(copyas(v1, v2))
|
|
|
|
return 1;
|
2013-09-11 13:22:11 -06:00
|
|
|
gactive++;
|
2009-11-06 17:51:49 -07:00
|
|
|
return copy1(v1, v2, r0->s1, 0);
|
|
|
|
}
|
|
|
|
|
2013-08-12 20:02:10 -06:00
|
|
|
static int
|
|
|
|
copy1(Adr *v1, Adr *v2, Flow *r, int f)
|
2009-11-06 17:51:49 -07:00
|
|
|
{
|
|
|
|
int t;
|
|
|
|
Prog *p;
|
|
|
|
|
2013-09-11 13:22:11 -06:00
|
|
|
if(r->active == gactive) {
|
2009-11-06 17:51:49 -07:00
|
|
|
if(debug['P'])
|
|
|
|
print("act set; return 1\n");
|
|
|
|
return 1;
|
|
|
|
}
|
2013-09-11 13:22:11 -06:00
|
|
|
r->active = gactive;
|
2009-11-06 17:51:49 -07:00
|
|
|
if(debug['P'])
|
|
|
|
print("copy %D->%D f=%d\n", v1, v2, f);
|
2013-08-12 20:02:10 -06:00
|
|
|
for(; r != nil; r = r->s1) {
|
2009-11-06 17:51:49 -07:00
|
|
|
p = r->prog;
|
|
|
|
if(debug['P'])
|
|
|
|
print("%P", p);
|
2013-08-12 20:02:10 -06:00
|
|
|
if(!f && uniqp(r) == nil) {
|
2009-11-06 17:51:49 -07:00
|
|
|
f = 1;
|
|
|
|
if(debug['P'])
|
|
|
|
print("; merge; f=%d", f);
|
|
|
|
}
|
|
|
|
t = copyu(p, v2, A);
|
|
|
|
switch(t) {
|
2013-06-09 07:50:24 -06:00
|
|
|
case 2: /* rar, can't split */
|
2009-11-06 17:51:49 -07:00
|
|
|
if(debug['P'])
|
|
|
|
print("; %D rar; return 0\n", v2);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case 3: /* set */
|
|
|
|
if(debug['P'])
|
|
|
|
print("; %D set; return 1\n", v2);
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
case 1: /* used, substitute */
|
|
|
|
case 4: /* use and set */
|
|
|
|
if(f) {
|
|
|
|
if(!debug['P'])
|
|
|
|
return 0;
|
|
|
|
if(t == 4)
|
|
|
|
print("; %D used+set and f=%d; return 0\n", v2, f);
|
|
|
|
else
|
|
|
|
print("; %D used and f=%d; return 0\n", v2, f);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if(copyu(p, v2, v1)) {
|
|
|
|
if(debug['P'])
|
|
|
|
print("; sub fail; return 0\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if(debug['P'])
|
|
|
|
print("; sub %D/%D", v2, v1);
|
|
|
|
if(t == 4) {
|
|
|
|
if(debug['P'])
|
|
|
|
print("; %D used+set; return 1\n", v2);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if(!f) {
|
|
|
|
t = copyu(p, v1, A);
|
|
|
|
if(!f && (t == 2 || t == 3 || t == 4)) {
|
|
|
|
f = 1;
|
|
|
|
if(debug['P'])
|
|
|
|
print("; %D set and !f; f=%d", v1, f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(debug['P'])
|
|
|
|
print("\n");
|
|
|
|
if(r->s2)
|
|
|
|
if(!copy1(v1, v2, r->s2, f))
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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)
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
copyu(Prog *p, Adr *v, Adr *s)
|
|
|
|
{
|
2013-08-12 11:05:40 -06:00
|
|
|
ProgInfo info;
|
2009-11-06 17:51:49 -07:00
|
|
|
|
|
|
|
switch(p->as) {
|
2013-08-12 11:05:40 -06:00
|
|
|
case AJMP:
|
2009-11-06 17:51:49 -07:00
|
|
|
if(s != A) {
|
|
|
|
if(copysub(&p->to, v, s, 1))
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if(copyau(&p->to, v))
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
|
2013-08-12 11:05:40 -06:00
|
|
|
case ARET:
|
2009-11-06 17:51:49 -07:00
|
|
|
if(s != A)
|
|
|
|
return 1;
|
|
|
|
return 3;
|
|
|
|
|
2013-08-12 11:05:40 -06:00
|
|
|
case ACALL:
|
2009-11-06 17:51:49 -07:00
|
|
|
if(REGEXT && v->type <= REGEXT && v->type > exregoffset)
|
|
|
|
return 2;
|
|
|
|
if(REGARG >= 0 && v->type == (uchar)REGARG)
|
|
|
|
return 2;
|
2013-02-22 12:25:50 -07:00
|
|
|
if(v->type == p->from.type)
|
|
|
|
return 2;
|
2009-11-06 17:51:49 -07:00
|
|
|
|
|
|
|
if(s != A) {
|
|
|
|
if(copysub(&p->to, v, s, 1))
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if(copyau(&p->to, v))
|
|
|
|
return 4;
|
|
|
|
return 3;
|
|
|
|
|
2013-08-12 11:05:40 -06:00
|
|
|
case ATEXT:
|
2009-11-06 17:51:49 -07:00
|
|
|
if(REGARG >= 0 && v->type == (uchar)REGARG)
|
|
|
|
return 3;
|
|
|
|
return 0;
|
|
|
|
}
|
2013-08-12 11:05:40 -06:00
|
|
|
|
|
|
|
proginfo(&info, p);
|
|
|
|
|
|
|
|
if((info.reguse|info.regset) & RtoB(v->type))
|
|
|
|
return 2;
|
|
|
|
|
|
|
|
if(info.flags & LeftAddr)
|
|
|
|
if(copyas(&p->from, v))
|
|
|
|
return 2;
|
|
|
|
|
|
|
|
if((info.flags & (RightRead|RightWrite)) == (RightRead|RightWrite))
|
|
|
|
if(copyas(&p->to, v))
|
|
|
|
return 2;
|
|
|
|
|
|
|
|
if(info.flags & RightWrite) {
|
|
|
|
if(copyas(&p->to, v)) {
|
|
|
|
if(s != A)
|
|
|
|
return copysub(&p->from, v, s, 1);
|
|
|
|
if(copyau(&p->from, v))
|
|
|
|
return 4;
|
|
|
|
return 3;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(info.flags & (LeftAddr|LeftRead|LeftWrite|RightAddr|RightRead|RightWrite)) {
|
|
|
|
if(s != A) {
|
|
|
|
if(copysub(&p->from, v, s, 1))
|
|
|
|
return 1;
|
|
|
|
return copysub(&p->to, v, s, 1);
|
|
|
|
}
|
|
|
|
if(copyau(&p->from, v))
|
|
|
|
return 1;
|
|
|
|
if(copyau(&p->to, v))
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2009-11-06 17:51:49 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* direct reference,
|
|
|
|
* could be set/use depending on
|
|
|
|
* semantics
|
|
|
|
*/
|
2013-08-12 20:02:10 -06:00
|
|
|
static int
|
2009-11-06 17:51:49 -07:00
|
|
|
copyas(Adr *a, Adr *v)
|
|
|
|
{
|
|
|
|
if(a->type != v->type)
|
|
|
|
return 0;
|
|
|
|
if(regtyp(v))
|
|
|
|
return 1;
|
|
|
|
if(v->type == D_AUTO || v->type == D_PARAM)
|
|
|
|
if(v->offset == a->offset)
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-09-17 14:54:22 -06:00
|
|
|
int
|
|
|
|
sameaddr(Addr *a, Addr *v)
|
|
|
|
{
|
|
|
|
if(a->type != v->type)
|
|
|
|
return 0;
|
|
|
|
if(regtyp(v))
|
|
|
|
return 1;
|
|
|
|
if(v->type == D_AUTO || v->type == D_PARAM)
|
|
|
|
if(v->offset == a->offset)
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-11-06 17:51:49 -07:00
|
|
|
/*
|
|
|
|
* either direct or indirect
|
|
|
|
*/
|
2013-08-12 20:02:10 -06:00
|
|
|
static int
|
2009-11-06 17:51:49 -07:00
|
|
|
copyau(Adr *a, Adr *v)
|
|
|
|
{
|
|
|
|
|
|
|
|
if(copyas(a, v))
|
|
|
|
return 1;
|
|
|
|
if(regtyp(v)) {
|
|
|
|
if(a->type-D_INDIR == v->type)
|
|
|
|
return 1;
|
|
|
|
if(a->index == v->type)
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* substitute s for v in a
|
|
|
|
* return failure to substitute
|
|
|
|
*/
|
2013-08-12 20:02:10 -06:00
|
|
|
static int
|
2009-11-06 17:51:49 -07:00
|
|
|
copysub(Adr *a, Adr *v, Adr *s, int f)
|
|
|
|
{
|
|
|
|
int t;
|
|
|
|
|
|
|
|
if(copyas(a, v)) {
|
|
|
|
t = s->type;
|
2013-01-02 14:55:23 -07:00
|
|
|
if(t >= D_AX && t <= D_DI || t >= D_X0 && t <= D_X7) {
|
2009-11-06 17:51:49 -07:00
|
|
|
if(f)
|
|
|
|
a->type = t;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if(regtyp(v)) {
|
|
|
|
t = v->type;
|
|
|
|
if(a->type == t+D_INDIR) {
|
|
|
|
if((s->type == D_BP) && a->index != D_NONE)
|
|
|
|
return 1; /* can't use BP-base with index */
|
|
|
|
if(f)
|
|
|
|
a->type = s->type+D_INDIR;
|
|
|
|
// return 0;
|
|
|
|
}
|
|
|
|
if(a->index == t) {
|
|
|
|
if(f)
|
|
|
|
a->index = s->type;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2013-08-12 20:02:10 -06:00
|
|
|
conprop(Flow *r0)
|
2009-11-06 17:51:49 -07:00
|
|
|
{
|
2013-08-12 20:02:10 -06:00
|
|
|
Flow *r;
|
2009-11-06 17:51:49 -07:00
|
|
|
Prog *p, *p0;
|
|
|
|
int t;
|
|
|
|
Adr *v0;
|
|
|
|
|
|
|
|
p0 = r0->prog;
|
|
|
|
v0 = &p0->to;
|
|
|
|
r = r0;
|
|
|
|
|
|
|
|
loop:
|
|
|
|
r = uniqs(r);
|
2013-08-12 20:02:10 -06:00
|
|
|
if(r == nil || r == r0)
|
2009-11-06 17:51:49 -07:00
|
|
|
return;
|
2013-08-12 20:02:10 -06:00
|
|
|
if(uniqp(r) == nil)
|
2009-11-06 17:51:49 -07:00
|
|
|
return;
|
|
|
|
|
|
|
|
p = r->prog;
|
|
|
|
t = copyu(p, v0, A);
|
|
|
|
switch(t) {
|
|
|
|
case 0: // miss
|
|
|
|
case 1: // use
|
|
|
|
goto loop;
|
|
|
|
|
|
|
|
case 2: // rar
|
|
|
|
case 4: // use and set
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 3: // set
|
|
|
|
if(p->as == p0->as)
|
|
|
|
if(p->from.type == p0->from.type)
|
2012-02-08 08:25:13 -07:00
|
|
|
if(p->from.node == p0->from.node)
|
2009-11-06 17:51:49 -07:00
|
|
|
if(p->from.offset == p0->from.offset)
|
|
|
|
if(p->from.scale == p0->from.scale)
|
2012-12-13 12:20:24 -07:00
|
|
|
if(p->from.u.vval == p0->from.u.vval)
|
2009-11-06 17:51:49 -07:00
|
|
|
if(p->from.index == p0->from.index) {
|
|
|
|
excise(r);
|
|
|
|
goto loop;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2013-09-17 14:54:22 -06:00
|
|
|
|
|
|
|
int
|
|
|
|
smallindir(Addr *a, Addr *reg)
|
|
|
|
{
|
|
|
|
return regtyp(reg) &&
|
|
|
|
a->type == D_INDIR + reg->type &&
|
|
|
|
a->index == D_NONE &&
|
|
|
|
0 <= a->offset && a->offset < 4096;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
stackaddr(Addr *a)
|
|
|
|
{
|
|
|
|
return regtyp(a) && a->type == D_SP;
|
|
|
|
}
|