Initial revision
This commit is contained in:
parent
1833451151
commit
6a9e49f683
40
util/ego/lv/Makefile
Normal file
40
util/ego/lv/Makefile
Normal file
|
@ -0,0 +1,40 @@
|
|||
EMH=../../../h
|
||||
EML=../../../lib
|
||||
CFLAGS=
|
||||
SHARE=../share
|
||||
LV=.
|
||||
OBJECTS=lv.o
|
||||
SHOBJECTS=$(SHARE)/get.o $(SHARE)/aux.o $(SHARE)/put.o $(SHARE)/map.o $(SHARE)/alloc.o $(SHARE)/global.o $(SHARE)/debug.o $(SHARE)/lset.o $(SHARE)/cset.o $(SHARE)/parser.o $(SHARE)/files.o $(SHARE)/locals.o $(SHARE)/init_glob.o $(SHARE)/go.o
|
||||
SRC=lv.h lv.c
|
||||
.c.o:
|
||||
cc $(CFLAGS) -c $<
|
||||
all: $(OBJECTS)
|
||||
lv: \
|
||||
$(OBJECTS) $(SHOBJECTS)
|
||||
cc -o lv -i $(OBJECTS) $(SHOBJECTS) $(EML)/em_data.a
|
||||
opr:
|
||||
pr $(SRC) | opr
|
||||
dumpflop:
|
||||
tar -uf /mnt/ego/lv/lv.tarf $(SRC)
|
||||
# the next lines are generated automatically
|
||||
# AUTOAUTOAUTOAUTOAUTOAUTO
|
||||
lv.o: ../../../h/em_mnem.h
|
||||
lv.o: ../../../h/em_pseu.h
|
||||
lv.o: ../../../h/em_spec.h
|
||||
lv.o: ../share/alloc.h
|
||||
lv.o: ../share/aux.h
|
||||
lv.o: ../share/cset.h
|
||||
lv.o: ../share/debug.h
|
||||
lv.o: ../share/def.h
|
||||
lv.o: ../share/files.h
|
||||
lv.o: ../share/get.h
|
||||
lv.o: ../share/global.h
|
||||
lv.o: ../share/go.h
|
||||
lv.o: ../share/init_glob.h
|
||||
lv.o: ../share/locals.h
|
||||
lv.o: ../share/lset.h
|
||||
lv.o: ../share/map.h
|
||||
lv.o: ../share/parser.h
|
||||
lv.o: ../share/put.h
|
||||
lv.o: ../share/types.h
|
||||
lv.o: lv.h
|
584
util/ego/lv/lv.c
Normal file
584
util/ego/lv/lv.c
Normal file
|
@ -0,0 +1,584 @@
|
|||
|
||||
/* L I V E V A R I A B L E S A N A L Y S I S */
|
||||
|
||||
#include <stdio.h>
|
||||
#include "../share/types.h"
|
||||
#include "lv.h"
|
||||
#include "../share/debug.h"
|
||||
#include "../share/global.h"
|
||||
#include "../share/lset.h"
|
||||
#include "../share/cset.h"
|
||||
#include "../share/def.h"
|
||||
#include "../share/files.h"
|
||||
#include "../share/alloc.h"
|
||||
#include "../share/map.h"
|
||||
#include "../share/get.h"
|
||||
#include "../share/put.h"
|
||||
#include "../share/aux.h"
|
||||
#include "../share/init_glob.h"
|
||||
#include "../share/locals.h"
|
||||
#include "../share/go.h"
|
||||
#include "../../../h/em_mnem.h"
|
||||
#include "../../../h/em_pseu.h"
|
||||
#include "../../../h/em_spec.h"
|
||||
#include "../share/parser.h"
|
||||
|
||||
/* TEMPORARY: should be put in ../../../h/em_mes.h: */
|
||||
#define ms_liv 9
|
||||
#define ms_ded 10
|
||||
|
||||
short nrglobals;
|
||||
short nrvars;
|
||||
|
||||
STATIC int Slv;
|
||||
STATIC bool mesgflag = FALSE; /* Suppress generation of live/dead info */
|
||||
|
||||
|
||||
STATIC clean_up()
|
||||
{
|
||||
local_p *p;
|
||||
|
||||
for (p = &locals[1]; p <= &locals[nrlocals]; p++) {
|
||||
oldlocal(*p);
|
||||
}
|
||||
oldmap(locals,nrlocals);
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC bool is_dir_use(l)
|
||||
line_p l;
|
||||
{
|
||||
/* See if l is a direct use of some variable
|
||||
* (i.e. not through a pointer). A LIL is a
|
||||
* direct use of some pointer variable
|
||||
* (and an indirect use of some other variable).
|
||||
* A SIL is also a direct use.
|
||||
* A LOI, however, is not an direct use of a variable.
|
||||
* An an increment/decrement instruction is regarded
|
||||
* as a use here, and not as a definition, as the
|
||||
* variable is first used and than defined.
|
||||
*/
|
||||
|
||||
switch(INSTR(l)) {
|
||||
case op_dee:
|
||||
case op_del:
|
||||
case op_ine:
|
||||
case op_inl:
|
||||
case op_lde:
|
||||
case op_ldl:
|
||||
case op_lil:
|
||||
case op_loe:
|
||||
case op_lol:
|
||||
case op_sil:
|
||||
return TRUE;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC bool is_indir_use(l)
|
||||
line_p l;
|
||||
{
|
||||
/* See if instruction l uses some variable(s) indirectly,
|
||||
* i.e. through a pointer or via a procedure call.
|
||||
*/
|
||||
|
||||
switch(INSTR(l)) {
|
||||
case op_blm:
|
||||
case op_bls:
|
||||
case op_cai:
|
||||
case op_cal:
|
||||
case op_lar:
|
||||
case op_ldf:
|
||||
case op_lil:
|
||||
case op_lof:
|
||||
case op_loi:
|
||||
case op_los:
|
||||
case op_mon:
|
||||
return TRUE;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC bool is_def(l)
|
||||
line_p l;
|
||||
{
|
||||
/* See if l does a direct definition */
|
||||
|
||||
switch(INSTR(l)) {
|
||||
case op_sde:
|
||||
case op_sdl:
|
||||
case op_ste:
|
||||
case op_stl:
|
||||
case op_zre:
|
||||
case op_zrl:
|
||||
return TRUE;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
|
||||
STATIC def_use(p)
|
||||
proc_p p;
|
||||
{
|
||||
/* Compute DEF(b) and USE(b), for every basic block b
|
||||
* of procedure p. DEF(b) contains the variables that
|
||||
* are certain to be defined (assigned) in b
|
||||
* before being used. USE(b) contains the variables
|
||||
* that may be used in b, before being defined.
|
||||
* (Note that uncertainty arises in the presence of
|
||||
* pointers and procedure calls).
|
||||
* We compute these sets, by scanning the text of
|
||||
* the basic block from beginning till end.
|
||||
*/
|
||||
|
||||
register bblock_p b;
|
||||
register line_p l;
|
||||
short v;
|
||||
bool found;
|
||||
cset all_ind_uses;
|
||||
|
||||
all_ind_uses = Cempty_set(nrvars);
|
||||
for (v = 1; v < nrlocals; v++) {
|
||||
if (!IS_REGVAR(locals[v])) {
|
||||
Cadd(LOC_TO_VARNR(v),&all_ind_uses);
|
||||
}
|
||||
}
|
||||
for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
|
||||
USE(b) = Cempty_set(nrvars);
|
||||
DEF(b) = Cempty_set(nrvars);
|
||||
for (l = b->b_start; l != (line_p) 0; l = l->l_next) {
|
||||
if (is_def(l)) {
|
||||
/* An direct definition (i.e. not
|
||||
* through a pointer).
|
||||
*/
|
||||
var_nr(l,&v,&found);
|
||||
if (found && !Cis_elem(v,USE(b))) {
|
||||
/* We do maintain live-dead info
|
||||
* for this variable, and it was
|
||||
* not used earlier in b.
|
||||
*/
|
||||
Cadd(v, &DEF(b));
|
||||
}
|
||||
} else {
|
||||
if (is_dir_use(l)) {
|
||||
var_nr(l,&v,&found);
|
||||
if (found && !Cis_elem(v,DEF(b))) {
|
||||
Cadd(v, &USE(b));
|
||||
}
|
||||
}
|
||||
if (is_indir_use(l)) {
|
||||
/* Add variable that may be used
|
||||
* by l to USE(b).
|
||||
*/
|
||||
Cjoin(all_ind_uses,&USE(b));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Cdeleteset(all_ind_uses);
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC unite_ins(bbset,setp)
|
||||
lset bbset;
|
||||
cset *setp;
|
||||
{
|
||||
/* Take the union of L_IN(b), for all b in bbset,
|
||||
* and put the result in setp.
|
||||
*/
|
||||
|
||||
Lindex i;
|
||||
|
||||
Cclear_set(setp);
|
||||
for (i = Lfirst(bbset); i != (Lindex) 0; i = Lnext(i,bbset)) {
|
||||
Cjoin(L_IN((bblock_p) Lelem(i)), setp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC solve_lv(p)
|
||||
proc_p p;
|
||||
{
|
||||
/* Solve the data flow equations for Live Variables,
|
||||
* for procedure p. These equations are:
|
||||
* (1) IN[b] = OUT[b] - DEF[b] + USE[b]
|
||||
* (2) OUT(b) = IN(s1) + ... + IN(sn) ;
|
||||
* where SUCC(b) = {s1, ... , sn}
|
||||
*/
|
||||
|
||||
register bblock_p b;
|
||||
cset newout = Cempty_set(nrvars);
|
||||
bool change = TRUE;
|
||||
|
||||
for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
|
||||
L_IN(b) = Cempty_set(nrvars);
|
||||
Ccopy_set(USE(b), &L_IN(b));
|
||||
L_OUT(b) = Cempty_set(nrvars);
|
||||
}
|
||||
while (change) {
|
||||
change = FALSE;
|
||||
for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
|
||||
unite_ins(b->b_succ,&newout);
|
||||
if (!Cequal(newout,L_OUT(b))) {
|
||||
change = TRUE;
|
||||
Ccopy_set(newout, &L_OUT(b));
|
||||
Ccopy_set(newout, &L_IN(b));
|
||||
Csubtract(DEF(b), &L_IN(b));
|
||||
Cjoin(USE(b), &L_IN(b));
|
||||
}
|
||||
}
|
||||
}
|
||||
Cdeleteset(newout);
|
||||
}
|
||||
|
||||
|
||||
STATIC live_variables_analysis(p)
|
||||
proc_p p;
|
||||
{
|
||||
make_localtab(p);
|
||||
nrvars = nrglobals + nrlocals;
|
||||
def_use(p);
|
||||
solve_lv(p);
|
||||
}
|
||||
|
||||
|
||||
STATIC init_live_dead(b)
|
||||
bblock_p b;
|
||||
{
|
||||
/* For every register variable, see if it is
|
||||
* live or dead at the end of b.
|
||||
*/
|
||||
|
||||
register short v;
|
||||
local_p loc;
|
||||
|
||||
for (v = 1; v <= nrlocals; v++) {
|
||||
loc = locals[v];
|
||||
if (IS_REGVAR(loc) && Cis_elem(LOC_TO_VARNR(v),L_OUT(b))) {
|
||||
LIVE(loc);
|
||||
} else {
|
||||
DEAD(loc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC line_p make_mesg(mesg,loc)
|
||||
short mesg;
|
||||
local_p loc;
|
||||
{
|
||||
/* Create a line for a message stating that
|
||||
* local variable loc is live/dead. This message
|
||||
* looks like: "mes ms_liv,off,size" or
|
||||
* "mes ms_ded,off,size".
|
||||
*/
|
||||
|
||||
line_p l = newline(OPLIST);
|
||||
register arg_p ap;
|
||||
|
||||
l->l_instr = ps_mes;
|
||||
ap = ARG(l) = newarg(ARGOFF);
|
||||
ap->a_a.a_offset = mesg;
|
||||
ap = ap->a_next = newarg(ARGOFF);
|
||||
ap->a_a.a_offset = loc->lc_off;
|
||||
ap = ap->a_next = newarg(ARGOFF);
|
||||
ap->a_a.a_offset = loc->lc_size;
|
||||
return l;
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC block_entry(b,prev)
|
||||
bblock_p b,prev;
|
||||
{
|
||||
short v,vn;
|
||||
local_p loc;
|
||||
bool was_live, is_live;
|
||||
|
||||
/* Generate a live/dead message for every register variable that
|
||||
* was live at the end of prev, but dead at the beginning of b,
|
||||
* or v.v. If prev = 0 (i.e. begin of procedure), parameters were
|
||||
* live, normal local variables were dead.
|
||||
*/
|
||||
|
||||
for (v = 1; v <= nrlocals; v++) {
|
||||
loc = locals[v];
|
||||
vn = LOC_TO_VARNR(v);
|
||||
if (prev == (bblock_p) 0) {
|
||||
was_live = loc->lc_off >= 0;
|
||||
} else {
|
||||
was_live = Cis_elem(vn,L_OUT(prev));
|
||||
}
|
||||
is_live = Cis_elem(vn,L_IN(b));
|
||||
if (was_live != is_live) {
|
||||
app_block(make_mesg((is_live?ms_liv:ms_ded),loc),b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC app_block(l,b)
|
||||
line_p l;
|
||||
bblock_p b;
|
||||
{
|
||||
line_p x = b->b_start;
|
||||
|
||||
if (x != (line_p) 0 && INSTR(x) == ps_pro) {
|
||||
/* start of procedure; append after pro pseudo ! */
|
||||
if ((l->l_next = x->l_next) != (line_p) 0) {
|
||||
PREV(l->l_next) = l;
|
||||
}
|
||||
x->l_next = l;
|
||||
PREV(l) = x;
|
||||
} else {
|
||||
if ((l->l_next = x) != (line_p) 0) {
|
||||
PREV(l->l_next) = l;
|
||||
}
|
||||
b->b_start = l;
|
||||
PREV(l) = (line_p) 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC definition(l,useless_out,v_out,mesgflag)
|
||||
line_p l;
|
||||
bool *useless_out;
|
||||
short *v_out;
|
||||
bool mesgflag;
|
||||
{
|
||||
/* Process a definition. If the defined (register-) variable
|
||||
* is live after 'l', then create a live-message and put
|
||||
* it after 'l'.
|
||||
*/
|
||||
|
||||
short v;
|
||||
bool found;
|
||||
local_p loc;
|
||||
|
||||
*useless_out = FALSE;
|
||||
var_nr(l,&v,&found);
|
||||
if (found && IS_LOCAL(v)) {
|
||||
*v_out = v;
|
||||
loc = locals[TO_LOCAL(v)];
|
||||
if (IS_REGVAR(loc)) {
|
||||
if (IS_LIVE(loc)) {
|
||||
if (!mesgflag) {
|
||||
appnd_line(make_mesg(ms_liv,loc), l);
|
||||
}
|
||||
DEAD(loc);
|
||||
} else {
|
||||
*useless_out = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
STATIC use(l,mesgflag)
|
||||
line_p l;
|
||||
bool mesgflag;
|
||||
{
|
||||
/* Process a use. If the defined (register-) variable
|
||||
* is dead after 'l', then create a dead-message and put
|
||||
* it after 'l'.
|
||||
*/
|
||||
|
||||
short v;
|
||||
bool found;
|
||||
local_p loc;
|
||||
|
||||
var_nr(l,&v,&found);
|
||||
if (found && IS_LOCAL(v)) {
|
||||
loc = locals[TO_LOCAL(v)];
|
||||
if (IS_REGVAR(loc) && IS_DEAD(loc)) {
|
||||
if (!mesgflag) {
|
||||
appnd_line(make_mesg(ms_ded,loc), l);
|
||||
}
|
||||
LIVE(loc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC nothing() { } /* No action to be undertaken at level 0 of parser */
|
||||
|
||||
STATIC rem_code(l1,l2,b)
|
||||
line_p l1,l2;
|
||||
bblock_p b;
|
||||
{
|
||||
line_p l,x,y;
|
||||
|
||||
x = PREV(l1);
|
||||
y = l2->l_next;
|
||||
for (l = l1; l != l2; l = l->l_next) {
|
||||
oldline(l);
|
||||
}
|
||||
if (x == (line_p) 0) {
|
||||
b->b_start = y;
|
||||
} else {
|
||||
x->l_next = y;
|
||||
}
|
||||
if (y != (line_p) 0) {
|
||||
PREV(y) = x;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#define SIZE(v) ((offset) locals[TO_LOCAL(v)]->lc_size)
|
||||
|
||||
|
||||
|
||||
|
||||
lv_mesg(p,mesgflag)
|
||||
proc_p p;
|
||||
bool mesgflag;
|
||||
{
|
||||
/* Create live/dead messages for every possible register
|
||||
* variable of p. A dead-message is put after a "use" of
|
||||
* such a variable, if the variable becomes dead just
|
||||
* after the use (i.e. this was its last use).
|
||||
* A live message is put after a "definition" of such
|
||||
* a variable, if the variable becomes live just
|
||||
* after the definition (which will usually be the case).
|
||||
* We traverse every basic block b of p from the last
|
||||
* instruction of b backwards to the beginning of b.
|
||||
* Initially, all variables that are dead at the end
|
||||
* of b are marked dead. All others are marked live.
|
||||
* If we come accross a definition of a variable X that
|
||||
* was marked live, we put a live-message after the
|
||||
* definition and mark X dead.
|
||||
* If we come accross a use of a variable X that
|
||||
* was marked dead, we put a dead-message after the
|
||||
* use and mark X live.
|
||||
* So at any point, the mark of X tells whether X is
|
||||
* live or dead immediately before (!) that point.
|
||||
* We also generate a message at the start of a basic block
|
||||
* for every variable that was live at the end of the (textually)
|
||||
* previous block, but dead at the entry of this block, or v.v.
|
||||
* On the fly, useless assignments are removed.
|
||||
*/
|
||||
|
||||
register bblock_p b;
|
||||
register line_p l;
|
||||
line_p lnp, prev;
|
||||
bblock_p prevb = (bblock_p) 0;
|
||||
short v;
|
||||
bool useless;
|
||||
|
||||
for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
|
||||
block_entry(b,prevb); /* generate message at head of block */
|
||||
prevb = b;
|
||||
if (!mesgflag) {
|
||||
init_live_dead(b);
|
||||
}
|
||||
for (l = last_instr(b); l != (line_p) 0; l = prev) {
|
||||
/* traverse backwards! */
|
||||
prev = PREV(l);
|
||||
if (is_def(l)) {
|
||||
definition(l,&useless,&v,mesgflag);
|
||||
if (useless && /* assignment to dead var. */
|
||||
parse(prev,SIZE(v),&lnp,0,nothing)) {
|
||||
/* The code "VAR := expression" can
|
||||
* be removed. 'l' is the "STL VAR",
|
||||
* lnp is the beginning of the EM code
|
||||
* for the expression.
|
||||
*/
|
||||
prev = PREV(lnp);
|
||||
rem_code(lnp,l,b);
|
||||
OUTVERBOSE("useless assignment ,proc %d,local %d", curproc->p_id,
|
||||
(int) locals[TO_LOCAL(v)]->lc_off);
|
||||
Slv++;
|
||||
}
|
||||
} else {
|
||||
if (is_dir_use(l)) {
|
||||
use(l,mesgflag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
STATIC lv_extend(p)
|
||||
proc_p p;
|
||||
{
|
||||
/* Allocate extended data structures for Use Definition analysis */
|
||||
|
||||
register bblock_p b;
|
||||
|
||||
for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
|
||||
b->b_extend = newlvbx();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
STATIC lv_cleanup(p)
|
||||
proc_p p;
|
||||
{
|
||||
/* Deallocate extended data structures for Use Definition analysis */
|
||||
|
||||
register bblock_p b;
|
||||
|
||||
for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
|
||||
Cdeleteset(USE(b));
|
||||
Cdeleteset(DEF(b));
|
||||
Cdeleteset(L_IN(b));
|
||||
Cdeleteset(L_OUT(b));
|
||||
oldlvbx(b->b_extend);
|
||||
}
|
||||
}
|
||||
|
||||
lv_flags(p)
|
||||
char *p;
|
||||
{
|
||||
switch(*p) {
|
||||
case 'N':
|
||||
mesgflag = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
lv_optimize(p)
|
||||
proc_p p;
|
||||
{
|
||||
locals = (local_p *) 0;
|
||||
lv_extend(p);
|
||||
live_variables_analysis(p);
|
||||
lv_mesg(p,mesgflag);
|
||||
/* generate live-dead messages for regvars */
|
||||
lv_cleanup(p);
|
||||
clean_up();
|
||||
}
|
||||
|
||||
|
||||
|
||||
main(argc,argv)
|
||||
int argc;
|
||||
char *argv[];
|
||||
{
|
||||
go(argc,argv,init_globals,lv_optimize,no_action,lv_flags);
|
||||
report("useless assignments deleted",Slv);
|
||||
exit(0);
|
||||
}
|
42
util/ego/lv/lv.h
Normal file
42
util/ego/lv/lv.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
/* L I V E V A R I A B L E S A N A L Y S I S
|
||||
*
|
||||
* L V . H
|
||||
*/
|
||||
|
||||
|
||||
#define USE(b) (b)->b_extend->bx_lv.bx_use
|
||||
#define DEF(b) (b)->b_extend->bx_lv.bx_def
|
||||
#define L_IN(b) (b)->b_extend->bx_lv.bx_lin
|
||||
#define L_OUT(b) (b)->b_extend->bx_lv.bx_lout
|
||||
|
||||
extern short nrglobals; /* number of global variables for which
|
||||
* ud-info is maintained.
|
||||
*/
|
||||
extern short nrvars; /* total number of variables (global + local)
|
||||
* for which ud-info is maintained.
|
||||
*/
|
||||
|
||||
/* Every global variable for which ud-info is maintained has
|
||||
* a 'global variable number' (o_globnr). Every useful local
|
||||
* has a 'local variable number', which is its index in the
|
||||
* 'locals' table. All these variables also have a
|
||||
* 'variable number'. Conversions exist between these numbers.
|
||||
*/
|
||||
|
||||
#define TO_GLOBAL(v) (v)
|
||||
#define TO_LOCAL(v) (v - nrglobals)
|
||||
#define GLOB_TO_VARNR(v) (v)
|
||||
#define LOC_TO_VARNR(v) (v + nrglobals)
|
||||
#define IS_GLOBAL(v) (v <= nrglobals)
|
||||
#define IS_LOCAL(v) (v > nrglobals)
|
||||
|
||||
#define REGVAR(lc) lc->lc_flags |= LCF_REG
|
||||
#define IS_REGVAR(lc) (lc->lc_flags & LCF_REG)
|
||||
#define BADLC(lc) lc->lc_flags |= LCF_BAD
|
||||
#define IS_BADLC(lc) (lc->lc_flags & LCF_BAD)
|
||||
#define LIVE(lc) lc->lc_flags |= LCF_LIVE
|
||||
#define DEAD(lc) lc->lc_flags &= ~LCF_LIVE
|
||||
#define IS_LIVE(lc) (lc->lc_flags & LCF_LIVE)
|
||||
#define IS_DEAD(lc) (!(lc->lc_flags & LCF_LIVE))
|
||||
|
||||
|
108
util/ego/sr/Makefile
Normal file
108
util/ego/sr/Makefile
Normal file
|
@ -0,0 +1,108 @@
|
|||
|
||||
EMH=../../../h
|
||||
EML=../../../lib
|
||||
CFLAGS=-DVERBOSE
|
||||
SHARE=../share
|
||||
SR=.
|
||||
OBJECTS=sr.o sr_expr.o sr_reduce.o sr_iv.o sr_cand.o sr_xform.o sr_aux.o
|
||||
SHOBJECTS=$(SHARE)/get.o $(SHARE)/put.o $(SHARE)/alloc.o $(SHARE)/global.o $(SHARE)/debug.o $(SHARE)/files.o $(SHARE)/map.o $(SHARE)/lset.o $(SHARE)/cset.o $(SHARE)/aux.o $(SHARE)/go.o
|
||||
SRC=sr.h sr_iv.h sr_reduce.h sr_cand.h sr_xform.h sr_expr.h sr_aux.h sr.c sr_iv.c sr_reduce.c sr_cand.c sr_xform.c sr_expr.c sr_aux.c
|
||||
.c.o:
|
||||
cc $(CFLAGS) -c $<
|
||||
all: $(OBJECTS)
|
||||
sr: \
|
||||
$(OBJECTS) $(SHOBJECTS)
|
||||
cc -o sr -i $(OBJECTS) $(SHOBJECTS) $(EML)/em_data.a
|
||||
lpr:
|
||||
pr $(SRC) | lpr
|
||||
opr:
|
||||
pr $(SRC) | opr
|
||||
dumpflop:
|
||||
tar -uf /mnt/ego/sr/sr.tarf $(SRC) Makefile
|
||||
# the next lines are generated automatically
|
||||
# AUTOAUTOAUTOAUTOAUTOAUTO
|
||||
sr.o: ../share/alloc.h
|
||||
sr.o: ../share/debug.h
|
||||
sr.o: ../share/files.h
|
||||
sr.o: ../share/get.h
|
||||
sr.o: ../share/global.h
|
||||
sr.o: ../share/lset.h
|
||||
sr.o: ../share/map.h
|
||||
sr.o: ../share/put.h
|
||||
sr.o: ../share/types.h
|
||||
sr.o: sr.h
|
||||
sr.o: sr_aux.h
|
||||
sr.o: sr_iv.h
|
||||
sr_aux.o: ../../../h/em_mnem.h
|
||||
sr_aux.o: ../../../h/em_pseu.h
|
||||
sr_aux.o: ../share/aux.h
|
||||
sr_aux.o: ../share/debug.h
|
||||
sr_aux.o: ../share/global.h
|
||||
sr_aux.o: ../share/lset.h
|
||||
sr_aux.o: ../share/types.h
|
||||
sr_aux.o: sr.h
|
||||
sr_aux.o: sr_aux.h
|
||||
sr_aux.o: sr_xform.h
|
||||
sr_cand.o: ../../../h/em_mnem.h
|
||||
sr_cand.o: ../../../h/em_pseu.h
|
||||
sr_cand.o: ../share/aux.h
|
||||
sr_cand.o: ../share/cset.h
|
||||
sr_cand.o: ../share/debug.h
|
||||
sr_cand.o: ../share/global.h
|
||||
sr_cand.o: ../share/lset.h
|
||||
sr_cand.o: ../share/map.h
|
||||
sr_cand.o: ../share/types.h
|
||||
sr_cand.o: sr.h
|
||||
sr_cand.o: sr_aux.h
|
||||
sr_cand.o: sr_cand.h
|
||||
sr_expr.o: ../../../h/em_mnem.h
|
||||
sr_expr.o: ../share/aux.h
|
||||
sr_expr.o: ../share/debug.h
|
||||
sr_expr.o: ../share/global.h
|
||||
sr_expr.o: ../share/lset.h
|
||||
sr_expr.o: ../share/types.h
|
||||
sr_expr.o: sr.h
|
||||
sr_expr.o: sr_aux.h
|
||||
sr_expr.o: sr_iv.h
|
||||
sr_iv.o: ../../../h/em_mnem.h
|
||||
sr_iv.o: ../../../h/em_pseu.h
|
||||
sr_iv.o: ../share/alloc.h
|
||||
sr_iv.o: ../share/aux.h
|
||||
sr_iv.o: ../share/cset.h
|
||||
sr_iv.o: ../share/debug.h
|
||||
sr_iv.o: ../share/global.h
|
||||
sr_iv.o: ../share/lset.h
|
||||
sr_iv.o: ../share/types.h
|
||||
sr_iv.o: sr.h
|
||||
sr_iv.o: sr_aux.h
|
||||
sr_iv.o: sr_cand.h
|
||||
sr_iv.o: sr_iv.h
|
||||
sr_reduce.o: ../../../h/em_mes.h
|
||||
sr_reduce.o: ../../../h/em_mnem.h
|
||||
sr_reduce.o: ../../../h/em_pseu.h
|
||||
sr_reduce.o: ../../../h/em_reg.h
|
||||
sr_reduce.o: ../share/alloc.h
|
||||
sr_reduce.o: ../share/aux.h
|
||||
sr_reduce.o: ../share/debug.h
|
||||
sr_reduce.o: ../share/global.h
|
||||
sr_reduce.o: ../share/lset.h
|
||||
sr_reduce.o: ../share/types.h
|
||||
sr_reduce.o: sr.h
|
||||
sr_reduce.o: sr_aux.h
|
||||
sr_reduce.o: sr_expr.h
|
||||
sr_reduce.o: sr_reduce.h
|
||||
sr_reduce.o: sr_xform.h
|
||||
sr_xform.o: ../../../h/em_mnem.h
|
||||
sr_xform.o: ../../../h/em_pseu.h
|
||||
sr_xform.o: ../../../h/em_spec.h
|
||||
sr_xform.o: ../share/alloc.h
|
||||
sr_xform.o: ../share/aux.h
|
||||
sr_xform.o: ../share/debug.h
|
||||
sr_xform.o: ../share/def.h
|
||||
sr_xform.o: ../share/get.h
|
||||
sr_xform.o: ../share/global.h
|
||||
sr_xform.o: ../share/lset.h
|
||||
sr_xform.o: ../share/types.h
|
||||
sr_xform.o: sr.h
|
||||
sr_xform.o: sr_aux.h
|
||||
sr_xform.o: sr_xform.h
|
205
util/ego/sr/sr.c
Normal file
205
util/ego/sr/sr.c
Normal file
|
@ -0,0 +1,205 @@
|
|||
/* S T R E N G T H R E D U C T I O N */
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include "../share/types.h"
|
||||
#include "sr.h"
|
||||
#include "../share/debug.h"
|
||||
#include "../share/global.h"
|
||||
#include "../share/files.h"
|
||||
#include "../share/get.h"
|
||||
#include "../share/put.h"
|
||||
#include "../share/lset.h"
|
||||
#include "../share/map.h"
|
||||
#include "../share/alloc.h"
|
||||
#include "../share/go.h"
|
||||
#include "sr_aux.h"
|
||||
#include "sr_iv.h"
|
||||
|
||||
/* Strength reduction tries to change expensive operators occurring
|
||||
* in a loop into cheaper operators. The expensive operators considered
|
||||
* are multiplication and array referencing.
|
||||
* The transformations can be expressed in C as:
|
||||
*
|
||||
* [1]: for (i = e1; i <= e2; i++)
|
||||
* print(118*i);
|
||||
* becomes:
|
||||
* for (i = e1, t = 118*e1; i <= e2; i++, t += 118)
|
||||
* print(t);
|
||||
*
|
||||
* [2]: for (i = e1; i <= e2; i++)
|
||||
* print(a[i]);
|
||||
* becomes:
|
||||
* for (i = e1, p = &a[i]; i <= e2; i++, p++)
|
||||
* print(*p);
|
||||
* The latter optimization is suppressed if array bound checking
|
||||
* is required.
|
||||
*/
|
||||
|
||||
/* Machine and/or language dependent parameters: */
|
||||
|
||||
bool ovfl_harmful;
|
||||
bool arrbound_harmful;
|
||||
|
||||
int Ssr; /* #optimizations found */
|
||||
|
||||
sr_machinit(f)
|
||||
FILE *f;
|
||||
{
|
||||
/* Read target machine dependent information */
|
||||
char s[100];
|
||||
|
||||
|
||||
for (;;) {
|
||||
while(getc(f) != '\n');
|
||||
fscanf(f,"%s",s);
|
||||
if (strcmp(s,"%%SR") == 0)break;
|
||||
}
|
||||
fscanf(f,"%d",&ovfl_harmful);
|
||||
fscanf(f,"%d",&arrbound_harmful);
|
||||
}
|
||||
|
||||
STATIC del_ivs(ivs)
|
||||
lset ivs;
|
||||
{
|
||||
/* Delete the set of iv structs */
|
||||
|
||||
Lindex i;
|
||||
|
||||
for (i = Lfirst(ivs); i != (Lindex) 0; i = Lnext(i,ivs)) {
|
||||
oldiv(Lelem(i));
|
||||
}
|
||||
Ldeleteset(ivs);
|
||||
}
|
||||
|
||||
|
||||
STATIC do_loop(loop)
|
||||
loop_p loop;
|
||||
{
|
||||
lset ivs, vars;
|
||||
|
||||
OUTTRACE("going to process loop %d",loop->lp_id);
|
||||
induc_vars(loop,&ivs, &vars);
|
||||
/* Build a set of iv_structs, one for every induction
|
||||
* variable of the loop, i.e. a variable i that
|
||||
* is changed only by i := i + c, where c is a loop constant.
|
||||
* Also detects variables that are changed (including induction
|
||||
* variables!).
|
||||
*/
|
||||
OUTTRACE("loop has %d induction variables",Lnrelems(ivs));
|
||||
if (Lnrelems(ivs) > 0) {
|
||||
strength_reduction(loop,ivs,vars);
|
||||
/* Perform strength reduction. Reduce:
|
||||
* iv * c to addition
|
||||
* a[iv] to indirection (*p)
|
||||
* (unless array bound checking is required)
|
||||
*/
|
||||
}
|
||||
del_ivs(ivs);
|
||||
Ldeleteset(vars);
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC loopblocks(p)
|
||||
proc_p p;
|
||||
{
|
||||
/* Compute the LP_BLOCKS sets for all loops of p */
|
||||
|
||||
register bblock_p b;
|
||||
register Lindex i;
|
||||
|
||||
for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
|
||||
for (i = Lfirst(b->b_loops); i != (Lindex) 0;
|
||||
i = Lnext(i,b->b_loops)) {
|
||||
Ladd(b,&(((loop_p) Lelem(i))->LP_BLOCKS));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC opt_proc(p)
|
||||
proc_p p;
|
||||
{
|
||||
/* Optimize all loops of one procedure. We first do all
|
||||
* outer loops at the lowest nesting level and proceed
|
||||
* in the inwards direction.
|
||||
*/
|
||||
|
||||
Lindex i;
|
||||
loop_p lp,outermost;
|
||||
int min_level;
|
||||
|
||||
for (;;) {
|
||||
min_level = 1000;
|
||||
for (i = Lfirst(p->p_loops); i != (Lindex) 0;
|
||||
i = Lnext(i,p->p_loops)) {
|
||||
lp = (loop_p) Lelem(i);
|
||||
if (!lp->LP_DONE && lp->lp_level < min_level) {
|
||||
min_level = lp->lp_level;
|
||||
outermost = lp;
|
||||
}
|
||||
}
|
||||
if (min_level == 1000) break;
|
||||
do_loop(outermost);
|
||||
outermost->LP_DONE = TRUE;
|
||||
OUTTRACE("loop %d processed",outermost->lp_id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC sr_extproc(p)
|
||||
proc_p p;
|
||||
{
|
||||
/* Allocate the extended data structures for procedure p */
|
||||
|
||||
register loop_p lp;
|
||||
register Lindex pi;
|
||||
|
||||
for (pi = Lfirst(p->p_loops); pi != (Lindex) 0;
|
||||
pi = Lnext(pi,p->p_loops)) {
|
||||
lp = (loop_p) Lelem(pi);
|
||||
lp->lp_extend = newsrlpx();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC sr_cleanproc(p)
|
||||
proc_p p;
|
||||
{
|
||||
/* Remove the extended data structures for procedure p */
|
||||
|
||||
register loop_p lp;
|
||||
register Lindex pi;
|
||||
|
||||
|
||||
for (pi = Lfirst(p->p_loops); pi != (Lindex) 0;
|
||||
pi = Lnext(pi,p->p_loops)) {
|
||||
lp = (loop_p) Lelem(pi);
|
||||
oldsrlpx(lp->lp_extend);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sr_optimize(p)
|
||||
proc_p p;
|
||||
{
|
||||
sr_extproc(p);
|
||||
loopblocks(p);
|
||||
opt_proc(p);
|
||||
sr_cleanproc(p);
|
||||
}
|
||||
|
||||
|
||||
|
||||
main(argc,argv)
|
||||
int argc;
|
||||
char *argv[];
|
||||
{
|
||||
go(argc,argv,no_action,sr_optimize,sr_machinit,no_action);
|
||||
report("strength reductions",Ssr);
|
||||
exit(0);
|
||||
}
|
28
util/ego/sr/sr.h
Normal file
28
util/ego/sr/sr.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
/* I N T E R N A L D A T A S T R U C T U R E S O F
|
||||
*
|
||||
* S T R E N G T H R E D U C T I O N
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#define LOAD 0
|
||||
#define STORE 1
|
||||
|
||||
#define DLINK(l1,l2) l1->l_next=l2; l2->l_prev=l1
|
||||
|
||||
#define same_local(l1,l2) (off_set(l1) == off_set(l2))
|
||||
|
||||
#define LP_BLOCKS lp_extend->lpx_sr.lpx_blocks
|
||||
#define LP_DONE lp_extend->lpx_sr.lpx_done
|
||||
#define LP_HEADER lp_extend->lpx_sr.lpx_header
|
||||
#define LP_INSTR lp_extend->lpx_sr.lpx_instr
|
||||
|
||||
/* Parameters to be provided by environment: */
|
||||
|
||||
extern bool ovfl_harmful; /* Does overflow during multiplication
|
||||
* cause a trap ?
|
||||
*/
|
||||
extern bool arrbound_harmful; /* Is it harmful to take the address of
|
||||
* a non-existing array element ?
|
||||
*/
|
||||
extern int Ssr; /* #optimizations found */
|
115
util/ego/sr/sr_aux.c
Normal file
115
util/ego/sr/sr_aux.c
Normal file
|
@ -0,0 +1,115 @@
|
|||
/* S T R E N G T H R E D U C T I O N
|
||||
*
|
||||
* S R _ A U X . C
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "../share/types.h"
|
||||
#include "sr.h"
|
||||
#include "../share/debug.h"
|
||||
#include "../../../h/em_mnem.h"
|
||||
#include "../../../h/em_pseu.h"
|
||||
#include "../share/global.h"
|
||||
#include "../share/lset.h"
|
||||
#include "../share/aux.h"
|
||||
#include "sr_aux.h"
|
||||
#include "sr_xform.h"
|
||||
|
||||
#define INSIDE_LOOP(b,lp) Lis_elem(b,lp->LP_BLOCKS)
|
||||
|
||||
|
||||
bool is_loopconst(lnp,vars)
|
||||
line_p lnp;
|
||||
lset vars;
|
||||
{
|
||||
Lindex i;
|
||||
|
||||
assert(TYPE(lnp) == OPSHORT || TYPE(lnp) == OPOFFSET);
|
||||
if (!is_regvar(off_set(lnp))) return FALSE;
|
||||
for (i = Lfirst(vars); i != (Lindex) 0; i = Lnext(i,vars)) {
|
||||
if (same_local(Lelem(i),lnp)) {
|
||||
return FALSE; /* variable was changed */
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
bool is_caddress(lnp,vars)
|
||||
line_p lnp;
|
||||
lset vars; /* variables changed in loop */
|
||||
{
|
||||
/* See if lnp is a single instruction (i.e. without arguments)
|
||||
* that pushes a loop-invariant entity of size pointer-size (ps)
|
||||
* on the stack.
|
||||
*/
|
||||
|
||||
if (lnp == (line_p) 0) return FALSE;
|
||||
switch(INSTR(lnp)) {
|
||||
case op_lae:
|
||||
case op_lal:
|
||||
return TRUE;
|
||||
case op_lol:
|
||||
return ps == ws && is_loopconst(lnp,vars);
|
||||
case op_ldl:
|
||||
return ps == 2*ws && is_loopconst(lnp,vars);
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC arg_p find_arg(n,list)
|
||||
int n;
|
||||
arg_p list;
|
||||
{
|
||||
/* Find the n-th element of the list */
|
||||
|
||||
while (--n) {
|
||||
if (list == (arg_p) 0) break;
|
||||
list = list->a_next;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
int elemsize(lnp)
|
||||
line_p lnp;
|
||||
{
|
||||
/* lnp is an instruction that loads the address of an array
|
||||
* descriptor. Find the size of the elements of the array.
|
||||
* If this size cannot be determined (e.g. the descriptor may
|
||||
* not be in a rom) then return UNKNOWN_SIZE.
|
||||
*/
|
||||
|
||||
dblock_p d;
|
||||
arg_p v;
|
||||
|
||||
assert (lnp != (line_p) 0);
|
||||
if (INSTR(lnp) == op_lae) {
|
||||
d = OBJ(lnp)->o_dblock; /* datablock */
|
||||
if (d->d_pseudo == DROM &&
|
||||
(v = find_arg(3,d->d_values)) != (arg_p) 0 &&
|
||||
v->a_type == ARGOFF) {
|
||||
return (int) v->a_a.a_offset;
|
||||
}
|
||||
}
|
||||
return UNKNOWN_SIZE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
concatenate(list1,list2)
|
||||
line_p list1,list2;
|
||||
{
|
||||
/* Append list2 to the end of list1. list1 may not be empty. */
|
||||
|
||||
register line_p l;
|
||||
|
||||
assert(list1 != (line_p) 0);
|
||||
for (l =list1; l->l_next != (line_p) 0; l = l->l_next);
|
||||
l->l_next = list2;
|
||||
}
|
20
util/ego/sr/sr_aux.h
Normal file
20
util/ego/sr/sr_aux.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
/* S R _ A U X . H */
|
||||
|
||||
|
||||
extern bool is_loopconst(); /* (line_p l; lset vars)
|
||||
* See if l is a loop-constant. vars is the
|
||||
* set of variables changed in the loop.
|
||||
*/
|
||||
extern bool is_caddress(); /* (line_p l)
|
||||
* See if l loads a loop-invariant entity of
|
||||
* size pointer-size.
|
||||
*/
|
||||
extern int elemsize(); /* (line_p l)
|
||||
* l is an instruction that loads an array
|
||||
* descriptor. Try to determine the size
|
||||
* of the array elements.
|
||||
*/
|
||||
extern concatenate(); /* (line_p list1,list2)
|
||||
* Append list2 to the end of list1
|
||||
*/
|
||||
#define is_const(l) (INSTR(l) == op_loc)
|
187
util/ego/sr/sr_cand.c
Normal file
187
util/ego/sr/sr_cand.c
Normal file
|
@ -0,0 +1,187 @@
|
|||
/* S T R E N G T H R E D U C T I O N
|
||||
*
|
||||
* S R _ C A N D . C
|
||||
*/
|
||||
|
||||
|
||||
#include "../share/types.h"
|
||||
#include "../../../h/em_mnem.h"
|
||||
#include "../../../h/em_pseu.h"
|
||||
#include "../share/lset.h"
|
||||
#include "../share/cset.h"
|
||||
#include "../share/debug.h"
|
||||
#include "../share/global.h"
|
||||
#include "../share/map.h"
|
||||
#include "../share/aux.h"
|
||||
#include "sr.h"
|
||||
#include "sr_aux.h"
|
||||
#include "sr_cand.h"
|
||||
|
||||
|
||||
/* A candidate induction variable of a loop (hereafter called candidate) is a
|
||||
* local variable (of the current procedure) that is assigned a value
|
||||
* precisely once within the loop. Furthermore, this assignment must
|
||||
* take place in a firm block of the loop.
|
||||
* We determine those locals that are assigned precisely once, within
|
||||
* a firm block;
|
||||
*
|
||||
* We represent a local variable via an instruction that references it,
|
||||
* e.g. LOL -6 represents the local variable at offset -6 with size=wordsize.
|
||||
* We keep track of two sets:
|
||||
* cand - the set of all candidate variables
|
||||
* dismiss - a set of variables that may not be made a candidate
|
||||
* (because they are assigned more than once, or because
|
||||
* they are assigned outside a firm block).
|
||||
* Only local variables for which a register message is given are considered.
|
||||
*/
|
||||
|
||||
|
||||
STATIC lset cand, /* set of candidates */
|
||||
dism; /* set of dismissed variables */
|
||||
|
||||
|
||||
#define ALL_LINES(lnp,list) lnp = list; lnp != (line_p) 0; lnp = lnp->l_next
|
||||
|
||||
|
||||
|
||||
STATIC un_cand(lnp)
|
||||
line_p lnp;
|
||||
{
|
||||
/* remove the variable stored into by lnp from the list of
|
||||
* candidates (if it was there anyway).
|
||||
*/
|
||||
|
||||
Lindex i, next;
|
||||
|
||||
for (i = Lfirst(cand); i != (Lindex) 0; i = next) {
|
||||
next = Lnext(i,cand);
|
||||
if (same_local(lnp,Lelem(i))) {
|
||||
OUTTRACE("remove candidate",0);
|
||||
Lremove(Lelem(i), &cand);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
STATIC bool is_cand(lnp)
|
||||
line_p lnp;
|
||||
{
|
||||
/* see if the variable stored into by lnp is a candate */
|
||||
|
||||
Lindex i;
|
||||
|
||||
for (i = Lfirst(cand); i != (Lindex) 0; i = Lnext(i,cand)) {
|
||||
if (same_local(lnp,Lelem(i))) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
STATIC make_cand(lnp)
|
||||
line_p lnp;
|
||||
{
|
||||
/* make the variable stored into by lnp a candidate */
|
||||
|
||||
|
||||
OUTTRACE("add a new candidate",0);
|
||||
Ladd(lnp,&cand);
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC do_dismiss(lnp)
|
||||
line_p lnp;
|
||||
{
|
||||
Ladd(lnp,&dism);
|
||||
}
|
||||
|
||||
|
||||
STATIC dismiss(lnp)
|
||||
line_p lnp;
|
||||
{
|
||||
/* The variable referenced by lnp is turned definitely into
|
||||
* a non-candidate.
|
||||
*/
|
||||
|
||||
un_cand(lnp); /* remove it from the candidate set,
|
||||
* if it was there in the first place.
|
||||
*/
|
||||
do_dismiss(lnp); /* add it to the set of dismissed variables */
|
||||
}
|
||||
|
||||
|
||||
STATIC bool not_dismissed(lnp)
|
||||
line_p lnp;
|
||||
{
|
||||
Lindex i;
|
||||
|
||||
for (i = Lfirst(dism); i != (Lindex) 0; i = Lnext(i,dism)) {
|
||||
if (same_local(Lelem(i),lnp)) {
|
||||
return FALSE; /* variable was dismissed */
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
STATIC try_cand(lnp,b)
|
||||
line_p lnp;
|
||||
bblock_p b;
|
||||
{
|
||||
/* If the variable stored into by lnp was not already a candidate
|
||||
* and was not dismissed, then it is made a candidate
|
||||
* (unless the assignment takes places in a block that is not firm).
|
||||
*/
|
||||
|
||||
if (!is_regvar(off_set(lnp))) return;
|
||||
if (is_cand(lnp) || !IS_FIRM(b)) {
|
||||
dismiss(lnp);
|
||||
} else {
|
||||
if (not_dismissed(lnp)) {
|
||||
make_cand(lnp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
candidates(lp,cand_out,vars_out)
|
||||
loop_p lp;
|
||||
lset *cand_out, *vars_out;
|
||||
{
|
||||
/* Find the candidate induction variables.
|
||||
*/
|
||||
|
||||
bblock_p b;
|
||||
line_p lnp;
|
||||
Lindex i;
|
||||
|
||||
OUTTRACE("find candidates of loop %d",lp->lp_id);
|
||||
cand = Lempty_set();
|
||||
dism = Lempty_set();
|
||||
|
||||
for (i = Lfirst(lp->LP_BLOCKS); i != (Lindex) 0;
|
||||
i = Lnext(i,lp->LP_BLOCKS)) {
|
||||
b = (bblock_p) Lelem(i);
|
||||
for ( ALL_LINES(lnp, b->b_start)) {
|
||||
OUTTRACE("inspect instruction %d",INSTR(lnp));
|
||||
switch(INSTR(lnp)) {
|
||||
case op_stl:
|
||||
case op_inl:
|
||||
case op_del:
|
||||
OUTTRACE("it's a store local",0);
|
||||
try_cand(lnp,b);
|
||||
break;
|
||||
case op_zrl:
|
||||
OUTTRACE("it's a destroy local",0);
|
||||
if (is_regvar(off_set(lnp))) {
|
||||
dismiss(lnp);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
*cand_out = cand;
|
||||
*vars_out = dism;
|
||||
}
|
14
util/ego/sr/sr_cand.h
Normal file
14
util/ego/sr/sr_cand.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
/* S T R E N G T H R E D U C T I O N
|
||||
*
|
||||
* S R _ C A N D . H
|
||||
*/
|
||||
|
||||
|
||||
extern candidates(); /* (loop_p lp; lset *iv_cand, *vars)
|
||||
* Find candidate induction variables,
|
||||
* i.e. local variables that are assigned
|
||||
* a value precisely once within the loop,
|
||||
* within a strong block. Also find the
|
||||
* local variables that are changed within
|
||||
* the loop, but that are not a candidate.
|
||||
*/
|
199
util/ego/sr/sr_expr.c
Normal file
199
util/ego/sr/sr_expr.c
Normal file
|
@ -0,0 +1,199 @@
|
|||
/* S T R E N G T H R E D U C T I O N
|
||||
*
|
||||
* S R _ E X P R . C
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include "../share/types.h"
|
||||
#include "sr.h"
|
||||
#include "../share/debug.h"
|
||||
#include "../share/global.h"
|
||||
#include "../share/aux.h"
|
||||
#include "sr_aux.h"
|
||||
#include "../share/lset.h"
|
||||
#include "sr_iv.h"
|
||||
#include "../../../h/em_mnem.h"
|
||||
|
||||
|
||||
|
||||
#define ME_NONE 0
|
||||
#define ME_UNAIR 1
|
||||
#define ME_BINAIR 2
|
||||
#define ME_LOOPCONST 3
|
||||
#define ME_IV 4
|
||||
|
||||
|
||||
|
||||
STATIC iv_p last_iv;
|
||||
STATIC int iv_sign;
|
||||
STATIC lset ivars, loopvars;
|
||||
|
||||
STATIC bool is_loadiv(lnp)
|
||||
line_p lnp;
|
||||
{
|
||||
/* See if lnp is a LOL iv instruction, where iv is an
|
||||
* induction variable of the set ivars. If so, set the
|
||||
* the global variable last_iv to its descriptor.
|
||||
*/
|
||||
|
||||
Lindex i;
|
||||
iv_p iv;
|
||||
offset off;
|
||||
|
||||
if (INSTR(lnp) == op_lol) {
|
||||
off = off_set(lnp);
|
||||
for (i = Lfirst(ivars); i != (Lindex) 0; i = Lnext(i,ivars)) {
|
||||
iv = (iv_p) Lelem(i);
|
||||
if (iv->iv_off == off) {
|
||||
last_iv = iv;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#define size_ok(l) (TYPE(l) == OPSHORT && SHORT(l) == ws)
|
||||
|
||||
|
||||
STATIC int me_kind(l,sign_in,sign_out)
|
||||
line_p l;
|
||||
int sign_in, *sign_out;
|
||||
{
|
||||
if (l != (line_p) 0) {
|
||||
switch(INSTR(l)) {
|
||||
case op_adi:
|
||||
case op_adu:
|
||||
if (size_ok(l)) {
|
||||
*sign_out = sign_in;
|
||||
return ME_BINAIR;
|
||||
}
|
||||
break;
|
||||
case op_sbi:
|
||||
case op_sbu:
|
||||
if (size_ok(l)) {
|
||||
*sign_out = - sign_in;
|
||||
return ME_BINAIR;
|
||||
}
|
||||
break;
|
||||
case op_ngi:
|
||||
if (size_ok(l)) {
|
||||
*sign_out = - sign_in;
|
||||
return ME_UNAIR;
|
||||
}
|
||||
break;
|
||||
case op_inc:
|
||||
case op_dec:
|
||||
*sign_out = sign_in;
|
||||
return ME_UNAIR;
|
||||
case op_loc:
|
||||
return ME_LOOPCONST;
|
||||
case op_lol:
|
||||
if (is_loadiv(l)) {
|
||||
iv_sign = sign_in;
|
||||
return ME_IV;
|
||||
}
|
||||
if (is_loopconst(l,loopvars)) return ME_LOOPCONST;
|
||||
}
|
||||
}
|
||||
return ME_NONE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC bool match_expr(l,iv_allowed,lbegin,iv_seen,sign)
|
||||
line_p l,*lbegin;
|
||||
bool iv_allowed, *iv_seen;
|
||||
int sign;
|
||||
{
|
||||
/* This routine is a top down parser for simple
|
||||
* EM expressions. It recognizes expressions that
|
||||
* have as operators + and - (unary - is also allowed)
|
||||
* and that have as operands a number of loop constants
|
||||
* (either a constant or a variable that is not
|
||||
* changed within the loop) and at most one induction
|
||||
* variable.
|
||||
* The parameter iv_allowed is propagated downwards
|
||||
* in the expression tree, indicating whether the
|
||||
* subexpression may use an induction variable as
|
||||
* operand. The parameter iv_seen is propagated
|
||||
* upwards, indicating if the subexpression has used
|
||||
* an induction variable. The parameter sign is
|
||||
* propagated downwards; it indicates the sign of
|
||||
* the subexpression. lbegin will point to the
|
||||
* beginning of the recognized subexpression
|
||||
* (it is an out parameter). Note that we scan the
|
||||
* EM text from right to left (i.e. top down).
|
||||
*/
|
||||
|
||||
line_p l1;
|
||||
bool iv_insubexpr;
|
||||
int sign2;
|
||||
|
||||
switch(me_kind(l,sign,&sign2)) {
|
||||
case ME_UNAIR:
|
||||
/* unairy operator, match one subexpression */
|
||||
if (match_expr(PREV(l),iv_allowed,&l1,&iv_insubexpr,sign2)) {
|
||||
*lbegin = l1;
|
||||
*iv_seen = iv_insubexpr;
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
case ME_BINAIR:
|
||||
/* binairy operator, match two subexpressions */
|
||||
if (match_expr(PREV(l), iv_allowed, &l1, &iv_insubexpr,sign2)) {
|
||||
l = PREV(l1);
|
||||
iv_allowed = iv_allowed && !iv_insubexpr;
|
||||
if (match_expr(l,iv_allowed,&l1,
|
||||
&iv_insubexpr,sign)) {
|
||||
*lbegin = l1;
|
||||
*iv_seen = !iv_allowed || iv_insubexpr;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE; /* subexpression not recognized */
|
||||
case ME_LOOPCONST:
|
||||
*lbegin = l; /* expression is a loop constant */
|
||||
*iv_seen = FALSE;
|
||||
return TRUE;
|
||||
case ME_IV:
|
||||
if (iv_allowed) {
|
||||
*iv_seen = TRUE;
|
||||
*lbegin = l;
|
||||
return TRUE;
|
||||
}
|
||||
/* fall through ... */
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool is_ivexpr(l,ivs,vars,lbegin_out,iv_out,sign_out)
|
||||
line_p l, *lbegin_out;
|
||||
lset ivs,vars;
|
||||
iv_p *iv_out;
|
||||
int *sign_out;
|
||||
{
|
||||
line_p l2;
|
||||
bool iv_seen;
|
||||
|
||||
|
||||
loopvars = vars;
|
||||
ivars = ivs;
|
||||
if (match_expr(l,TRUE,&l2,&iv_seen,1)) {
|
||||
if (iv_seen) {
|
||||
/* recognized a correct expression */
|
||||
*lbegin_out = l2;
|
||||
*iv_out = last_iv;
|
||||
*sign_out = iv_sign;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
13
util/ego/sr/sr_expr.h
Normal file
13
util/ego/sr/sr_expr.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
/* S T R E N G T H R E D U C T I O N
|
||||
*
|
||||
* S R _ E X P R . H
|
||||
*
|
||||
*/
|
||||
|
||||
extern bool is_ivexpr();/* (line_p l; lset ivs,vars; line_p *lbegin; iv_p *iv;
|
||||
* int *out_sign)
|
||||
* Try to recognize an expression that is a linear
|
||||
* function of presicely one induction variable.
|
||||
* It may only use loop constants (besides the
|
||||
* induc. var.).
|
||||
*/
|
183
util/ego/sr/sr_iv.c
Normal file
183
util/ego/sr/sr_iv.c
Normal file
|
@ -0,0 +1,183 @@
|
|||
/* S T R E N G T H R E D U C T I O N
|
||||
*
|
||||
* S R _ I V . C
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "../share/types.h"
|
||||
#include "sr.h"
|
||||
#include "../../../h/em_mnem.h"
|
||||
#include "../../../h/em_pseu.h"
|
||||
#include "../share/lset.h"
|
||||
#include "../share/cset.h"
|
||||
#include "../share/debug.h"
|
||||
#include "../share/global.h"
|
||||
#include "../share/alloc.h"
|
||||
#include "../share/aux.h"
|
||||
#include "sr_aux.h"
|
||||
#include "sr_cand.h"
|
||||
#include "sr_iv.h"
|
||||
|
||||
|
||||
|
||||
STATIC lset ivars; /* set of induction variables */
|
||||
|
||||
STATIC short nature(lnp)
|
||||
line_p lnp;
|
||||
{
|
||||
/* Auxiliary routine used by inc_or_dec, is_add and plus_or_min.
|
||||
* Determine if lnp had INCREMENT/DECREMENT-nature (1),
|
||||
* ADD-nature (2), SUBTRACT-nature (3)
|
||||
* or Buddha-nature (0).
|
||||
*/
|
||||
|
||||
bool size_ok;
|
||||
|
||||
assert(lnp != (line_p) 0);
|
||||
size_ok = (TYPE(lnp) == OPSHORT && SHORT(lnp) == ws);
|
||||
switch(INSTR(lnp)) {
|
||||
case op_inc:
|
||||
case op_dec:
|
||||
return 1;
|
||||
case op_adi:
|
||||
case op_adu:
|
||||
return (size_ok? 2:0);
|
||||
case op_sbi:
|
||||
case op_sbu:
|
||||
return (size_ok? 3:0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#define is_add(l) (nature(l) == 2)
|
||||
#define plus_or_min(l) (nature(l) > 1)
|
||||
#define inc_or_dec(l) (nature(l) == 1)
|
||||
|
||||
|
||||
STATIC bool is_same(l,lnp)
|
||||
line_p l, lnp;
|
||||
{
|
||||
/* lnp is a STL x , where x is a candidate
|
||||
* induction variable. See if l is a LOL x
|
||||
* (with the same x as the store-instruction)
|
||||
*/
|
||||
|
||||
assert(INSTR(lnp) == op_stl);
|
||||
return l != (line_p) 0 && INSTR(l) == op_lol &&
|
||||
off_set(l) == off_set(lnp);
|
||||
}
|
||||
|
||||
|
||||
STATIC ivar(lnp,step)
|
||||
line_p lnp;
|
||||
int step;
|
||||
{
|
||||
/* Record the fact that we've found a new induction variable.
|
||||
* lnp points to the last instruction of the code that
|
||||
* increments the induction variable, i.e. a STL, DEL or INL.
|
||||
*/
|
||||
|
||||
iv_p i;
|
||||
|
||||
i = newiv();
|
||||
i->iv_off = (TYPE(lnp) == OPSHORT ? (offset) SHORT(lnp) : OFFSET(lnp));
|
||||
i->iv_incr = lnp; /* last instruction of increment code */
|
||||
i->iv_step = step; /* step value */
|
||||
Ladd(i,&ivars);
|
||||
}
|
||||
|
||||
|
||||
STATIC int sign(lnp)
|
||||
line_p lnp;
|
||||
{
|
||||
switch(INSTR(lnp)) {
|
||||
case op_inc:
|
||||
case op_inl:
|
||||
case op_adi:
|
||||
case op_adu:
|
||||
return 1;
|
||||
case op_dec:
|
||||
case op_del:
|
||||
case op_sbi:
|
||||
case op_sbu:
|
||||
return (-1);
|
||||
default:
|
||||
assert(FALSE);
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
|
||||
STATIC try_patterns(lnp)
|
||||
line_p lnp;
|
||||
{
|
||||
/* lnp is a STL x; try to recognize
|
||||
* one of the patterns:
|
||||
* 'LOAD const; LOAD x; ADD; STORE x'
|
||||
* or 'LOAD x; LOAD const; ADD or SUBTRACT;
|
||||
* STORE x'
|
||||
* or 'LOAD x; INCREMENT/DECREMENT; STORE x'
|
||||
*/
|
||||
|
||||
line_p l, l2;
|
||||
|
||||
l = PREV(lnp); /* instruction before lnp*/
|
||||
if (l == (line_p) 0) return; /* no match possible */
|
||||
l2 = PREV(l);
|
||||
if (inc_or_dec(l)) {
|
||||
if (is_same(l2,lnp)) {
|
||||
/* e.g. LOL iv ; INC ; STL iv */
|
||||
ivar(lnp,sign(l));
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (is_add(lnp)) {
|
||||
if(is_same(l2,lnp) && is_const(PREV(l2))) {
|
||||
ivar(lnp,SHORT(PREV(l2)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (plus_or_min(l)) {
|
||||
if (is_const(l2) && is_same(PREV(l2),lnp)) {
|
||||
ivar(lnp,sign(l) * SHORT(l2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
induc_vars(loop,ivar_out, vars_out)
|
||||
loop_p loop;
|
||||
lset *ivar_out, *vars_out;
|
||||
{
|
||||
/* Construct the set of induction variables. We use several
|
||||
* global variables computed by 'candidates'.
|
||||
*/
|
||||
|
||||
Lindex i;
|
||||
line_p lnp;
|
||||
lset cand_iv, vars;
|
||||
|
||||
ivars = Lempty_set();
|
||||
candidates(loop, &cand_iv, &vars);
|
||||
/* Find the set of all variables that are assigned precisely
|
||||
* once within the loop, within a firm block.
|
||||
* Also find all remaining local variables that are changed
|
||||
* within the loop.
|
||||
*/
|
||||
if (Lnrelems(cand_iv) > 0) {
|
||||
for (i = Lfirst(cand_iv); i != (Lindex) 0; i = Lnext(i,cand_iv)) {
|
||||
lnp = (line_p) Lelem(i);
|
||||
if (INSTR(lnp) == op_inl || INSTR(lnp) == op_del) {
|
||||
ivar(lnp,sign(lnp));
|
||||
} else {
|
||||
try_patterns(lnp);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ljoin(cand_iv, &vars);
|
||||
*ivar_out = ivars;
|
||||
*vars_out = vars;
|
||||
}
|
7
util/ego/sr/sr_iv.h
Normal file
7
util/ego/sr/sr_iv.h
Normal file
|
@ -0,0 +1,7 @@
|
|||
/* S R _ I V . H */
|
||||
|
||||
extern induc_vars(); /* (loop_p loop; lset *ivars, *vars)
|
||||
* Find the set of induction variables
|
||||
* of the loop. Also find the set of (local)
|
||||
* variables that are changed.
|
||||
*/
|
625
util/ego/sr/sr_reduce.c
Normal file
625
util/ego/sr/sr_reduce.c
Normal file
|
@ -0,0 +1,625 @@
|
|||
/* S T R E N G T H R E D U C T I O N
|
||||
*
|
||||
* S R _ R E D U C E . C
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "../share/types.h"
|
||||
#include "sr.h"
|
||||
#include "../../../h/em_mnem.h"
|
||||
#include "../share/debug.h"
|
||||
#include "../share/alloc.h"
|
||||
#include "../share/global.h"
|
||||
#include "../share/aux.h"
|
||||
#include "sr_aux.h"
|
||||
#include "../share/lset.h"
|
||||
#include "sr_xform.h"
|
||||
#include "sr_reduce.h"
|
||||
#include "sr_expr.h"
|
||||
#include "../../../h/em_pseu.h"
|
||||
#include "../../../h/em_reg.h"
|
||||
#include "../../../h/em_mes.h"
|
||||
#include "../../../h/em_mnem.h"
|
||||
|
||||
|
||||
|
||||
STATIC lset avail;
|
||||
/* If an expression such as "iv * const" or "A[iv]" is
|
||||
* used more than once in a loop, we only use one temporary
|
||||
* local for it and reuse this local each time.
|
||||
* After the first occurrence, the expression is said to
|
||||
* be available.
|
||||
*/
|
||||
|
||||
STATIC int regtyp(code)
|
||||
code_p code;
|
||||
{
|
||||
switch(code->co_instr) {
|
||||
case op_mli:
|
||||
case op_mlu:
|
||||
return reg_any;
|
||||
default:
|
||||
return reg_pointer;
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
|
||||
STATIC gen_regmes(tmp,score,code,p)
|
||||
offset tmp;
|
||||
int score;
|
||||
code_p code;
|
||||
proc_p p;
|
||||
{
|
||||
/* generate a register message for the temporary variable and
|
||||
* insert it at the start of the procedure.
|
||||
*/
|
||||
|
||||
line_p l,pro;
|
||||
|
||||
l = reg_mes(tmp,code->co_tmpsize,regtyp(code),score);
|
||||
pro = p->p_start->b_start; /* every proc. begins with a PRO pseudo */
|
||||
l->l_next = pro->l_next;
|
||||
PREV(l->l_next) = l;
|
||||
pro->l_next = l;
|
||||
PREV(l) = pro;
|
||||
}
|
||||
|
||||
|
||||
STATIC line_p newcode(code,tmp)
|
||||
code_p code;
|
||||
offset tmp;
|
||||
{
|
||||
/* Construct the EM code that will replace the reducible code,
|
||||
* e.g. iv * c -> tmp
|
||||
* a[iv] -> *tmp
|
||||
*/
|
||||
|
||||
line_p l;
|
||||
|
||||
switch(code->co_instr) {
|
||||
case op_mli:
|
||||
case op_mlu:
|
||||
/* new code is just a LOL tmp */
|
||||
l = int_line(tmp);
|
||||
l->l_instr = op_lol;
|
||||
break;
|
||||
case op_aar:
|
||||
/* New code is a LOAD tmp, where tmp is a
|
||||
* pointer variable, so the actual EM code
|
||||
* depends on the pointer size.
|
||||
*/
|
||||
l = move_pointer(tmp,LOAD);
|
||||
break;
|
||||
case op_lar:
|
||||
/* New code is a load-indirect */
|
||||
l = int_line(tmp);
|
||||
l->l_instr = op_lil;
|
||||
break;
|
||||
case op_sar:
|
||||
/* New code is a store-indirect */
|
||||
l = int_line(tmp);
|
||||
l->l_instr = op_sil;
|
||||
break;
|
||||
default:
|
||||
assert(FALSE);
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC replcode(code,text)
|
||||
code_p code;
|
||||
line_p text;
|
||||
{
|
||||
/* Replace old code (extending from code->co_lfirst to
|
||||
* code->co_llast) by new code (headed by 'text').
|
||||
*/
|
||||
|
||||
line_p l, l1, l2;
|
||||
|
||||
for (l = text; l->l_next != (line_p) 0; l = l->l_next);
|
||||
/* 'l' now points to last instruction of text */
|
||||
l1 = PREV(code->co_lfirst); /* instruction just before old code */
|
||||
l2 = code->co_llast->l_next; /* instruction just behind old code */
|
||||
if (l1 == (line_p) 0) {
|
||||
code->co_block->b_start = text;
|
||||
PREV(text) = (line_p) 0;
|
||||
} else {
|
||||
l1->l_next = text;
|
||||
PREV(text) = l1;
|
||||
}
|
||||
if (l2 != (line_p) 0) {
|
||||
PREV(l2) = l;
|
||||
}
|
||||
l->l_next = l2;
|
||||
code->co_llast->l_next = (line_p) 0;
|
||||
/* Note that the old code is still accessible via code->co_lfirst */
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC init_code(code,tmp)
|
||||
code_p code;
|
||||
offset tmp;
|
||||
{
|
||||
/* Generate code to set up the temporary local.
|
||||
* For multiplication, its initial value is const*iv_expr,
|
||||
* for array operations it is &a[iv_expr] (where iv_expr is
|
||||
* an expression that is a linear function of the induc. var.
|
||||
* This code is inserted immediately before the loop entry.
|
||||
* As the initializing code looks very much like the
|
||||
* reduced code, we reuse that (old) code.
|
||||
*/
|
||||
|
||||
line_p l, *p;
|
||||
|
||||
l = code->co_llast; /* the mli, lar etc. instruction */
|
||||
switch(INSTR(l)) {
|
||||
case op_mli:
|
||||
case op_mlu:
|
||||
/* reduced code is: iv_expr * lc (or lc * iv_expr)
|
||||
* init_code is: tmp = iv_expr * lc (or lc*iv_expr)
|
||||
* So we just insert a 'STL tmp'.
|
||||
*/
|
||||
l->l_next = int_line(tmp);
|
||||
l->l_next->l_instr = op_stl;
|
||||
break;
|
||||
case op_lar:
|
||||
case op_sar:
|
||||
/* reduced code is: ...= A[iv_expr] resp.
|
||||
* A[iv]_expr = ..
|
||||
* init_code is: tmp = &A[iv_expr].
|
||||
* So just change the lar or sar into a aar ...
|
||||
*/
|
||||
l->l_instr = (byte) op_aar;
|
||||
/* ... and fall through !! */
|
||||
case op_aar:
|
||||
/* append code to store a pointer in temp. local */
|
||||
l->l_next = move_pointer(tmp,STORE);
|
||||
break;
|
||||
default:
|
||||
assert(FALSE); /* non-reducible instruction */
|
||||
}
|
||||
PREV(l->l_next) = l;
|
||||
/* Now insert the code at the end of the header block */
|
||||
p = &code->co_loop->LP_INSTR;
|
||||
if (*p == (line_p) 0) {
|
||||
/* LP_INSTR points to last instruction of header block,
|
||||
* so if it is 0, the header block is empty yet.
|
||||
*/
|
||||
code->co_loop->LP_HEADER->b_start =
|
||||
code->co_lfirst;
|
||||
} else {
|
||||
(*p)->l_next = code->co_lfirst;
|
||||
PREV(code->co_lfirst) = *p;
|
||||
}
|
||||
*p = l->l_next; /* new last instruction */
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC incr_code(code,tmp)
|
||||
code_p code;
|
||||
offset tmp;
|
||||
{
|
||||
/* Generate code to increment the temporary local variable.
|
||||
* The variable is incremented by
|
||||
* 1) multiply --> step value of iv * loop constant
|
||||
* 2) array --> step value of iv * element size
|
||||
* This value can be determined statically.
|
||||
* If the induction variable is used in a linear
|
||||
* expression in which its sign is negative
|
||||
* (such as in: "5-(6-(-iv))" ), this value is negated.
|
||||
* The generated code looks like:
|
||||
* LOL tmp ; LOC incr ; ADI ws ; STL tmp
|
||||
* For pointer-increments we generate a "ADP c", rather than
|
||||
* a "LOC c; ADS ws".
|
||||
* This code is put just after the code that increments
|
||||
* the induction variable.
|
||||
*/
|
||||
|
||||
line_p load_tmp, loc, add, store_tmp, l;
|
||||
|
||||
add = newline(OPSHORT);
|
||||
SHORT(add) = ws; /* the add instruction, can be ADI,ADU or ADS */
|
||||
switch(code->co_instr) {
|
||||
case op_mli:
|
||||
case op_mlu:
|
||||
loc = int_line(
|
||||
code->co_sign *
|
||||
off_set(code->c_o.co_loadlc) *
|
||||
code->co_iv->iv_step);
|
||||
loc->l_instr = op_loc;
|
||||
add->l_instr = op_adi;
|
||||
load_tmp = int_line(tmp);
|
||||
load_tmp->l_instr = op_lol;
|
||||
store_tmp = int_line(tmp);
|
||||
store_tmp->l_instr = op_stl;
|
||||
break;
|
||||
case op_lar:
|
||||
case op_sar:
|
||||
case op_aar:
|
||||
loc = (line_p) 0;
|
||||
add = int_line(
|
||||
code->co_sign *
|
||||
code->co_iv->iv_step *
|
||||
elemsize(code->c_o.co_desc));
|
||||
add->l_instr = op_adp;
|
||||
load_tmp = move_pointer(tmp,LOAD);
|
||||
store_tmp = move_pointer(tmp,STORE);
|
||||
break;
|
||||
default:
|
||||
assert(FALSE);
|
||||
}
|
||||
/* Now we've got pieces of code to load the temp. local,
|
||||
* load the constant, add the two and store the result in
|
||||
* the local. This code will be put just after the code that
|
||||
* increments the induction variable.
|
||||
*/
|
||||
if (loc != (line_p) 0) concatenate(load_tmp,loc);
|
||||
concatenate(load_tmp,add);
|
||||
concatenate(load_tmp,store_tmp);
|
||||
/* Now load_tmp points to a list of EM instructions */
|
||||
l = code->co_iv->iv_incr;
|
||||
if (l->l_next != (line_p) 0) {
|
||||
DLINK(store_tmp,l->l_next);
|
||||
}
|
||||
DLINK(l,load_tmp); /* doubly link them */
|
||||
}
|
||||
|
||||
|
||||
STATIC remcode(c)
|
||||
code_p c;
|
||||
{
|
||||
line_p l, next;
|
||||
|
||||
for (l = c->co_lfirst; l != (line_p) 0; l = next) {
|
||||
next = l->l_next;
|
||||
oldline(l);
|
||||
}
|
||||
oldcinfo(c);
|
||||
}
|
||||
|
||||
|
||||
STATIC bool same_address(l1,l2,vars)
|
||||
line_p l1,l2;
|
||||
lset vars;
|
||||
{
|
||||
/* See if l1 and l2 load the same address */
|
||||
|
||||
if (INSTR(l1) != INSTR(l2)) return FALSE;
|
||||
switch(INSTR(l1)) {
|
||||
case op_lae:
|
||||
return OBJ(l1) == OBJ(l2);
|
||||
case op_lal:
|
||||
return off_set(l1) == off_set(l2);
|
||||
case op_lol:
|
||||
return ps == ws &&
|
||||
off_set(l1) == off_set(l2) &&
|
||||
is_loopconst(l1,vars);
|
||||
case op_ldl:
|
||||
return ps == 2*ws &&
|
||||
off_set(l1) == off_set(l2) &&
|
||||
is_loopconst(l1,vars);
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
STATIC bool same_expr(lb1,le1,lb2,le2)
|
||||
line_p lb1,le1,lb2,le2;
|
||||
{
|
||||
/* See if the code from lb1 to le1 is the same
|
||||
* expression as the code from lb2 to le2.
|
||||
*/
|
||||
|
||||
|
||||
register line_p l1,l2;
|
||||
|
||||
l1 = lb1;
|
||||
l2 = lb2;
|
||||
for (;;) {
|
||||
if (INSTR(l1) != INSTR(l2)) return FALSE;
|
||||
switch(TYPE(l1)) {
|
||||
case OPSHORT:
|
||||
if (TYPE(l2) != OPSHORT ||
|
||||
SHORT(l1) != SHORT(l2)) return FALSE;
|
||||
break;
|
||||
case OPOFFSET:
|
||||
if (TYPE(l2) != OPOFFSET ||
|
||||
OFFSET(l1) != OFFSET(l2)) return FALSE;
|
||||
break;
|
||||
case OPNO:
|
||||
break;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
if (l1 == le1 ) return l2 == le2;
|
||||
if (l2 == le2) return FALSE;
|
||||
l1 = l1->l_next;
|
||||
l2 = l2->l_next;
|
||||
}
|
||||
}
|
||||
|
||||
STATIC bool same_code(c1,c2,vars)
|
||||
code_p c1,c2;
|
||||
lset vars;
|
||||
{
|
||||
/* See if c1 and c2 compute the same expression. Two array
|
||||
* references can be the same even if one is e.g a fetch
|
||||
* and the other a store.
|
||||
*/
|
||||
|
||||
switch(c1->co_instr) {
|
||||
case op_mli:
|
||||
return c1->co_instr == c2->co_instr &&
|
||||
off_set(c1->c_o.co_loadlc) ==
|
||||
off_set(c2->c_o.co_loadlc) &&
|
||||
same_expr(c1->co_ivexpr,c1->co_endexpr,
|
||||
c2->co_ivexpr,c2->co_endexpr);
|
||||
case op_aar:
|
||||
case op_lar:
|
||||
case op_sar:
|
||||
return c2->co_instr != op_mli &&
|
||||
c2->co_instr != op_mlu &&
|
||||
same_expr(c1->co_ivexpr,c1->co_endexpr,
|
||||
c2->co_ivexpr,c2->co_endexpr) &&
|
||||
same_address(c1->c_o.co_desc,c2->c_o.co_desc,vars) &&
|
||||
same_address(c1->co_lfirst,c2->co_lfirst,vars);
|
||||
default:
|
||||
assert(FALSE);
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
|
||||
STATIC code_p available(c,vars)
|
||||
code_p c;
|
||||
lset vars;
|
||||
{
|
||||
/* See if the code is already available.
|
||||
* If so, return a pointer to the first occurrence
|
||||
* of the code.
|
||||
*/
|
||||
|
||||
Lindex i;
|
||||
code_p cp;
|
||||
|
||||
for (i = Lfirst(avail); i != (Lindex) 0; i = Lnext(i,avail)) {
|
||||
cp = (code_p) Lelem(i);
|
||||
if (same_code(c,cp,vars)) {
|
||||
return cp;
|
||||
}
|
||||
}
|
||||
return (code_p) 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC reduce(code,vars)
|
||||
code_p code;
|
||||
lset vars;
|
||||
{
|
||||
/* Perform the actual transformations. The code on the left
|
||||
* gets transformed into the code on the right. Note that
|
||||
* each piece of code is assigned a name, that will be
|
||||
* used to describe the whole process.
|
||||
*
|
||||
* t = iv * 118; (init_code)
|
||||
* do ---> do
|
||||
* .. iv * 118 .. .. t .. (new_code)
|
||||
* iv++; iv++;
|
||||
* t += 118; (incr_code)
|
||||
* od od
|
||||
*/
|
||||
|
||||
offset tmp;
|
||||
code_p ac;
|
||||
|
||||
OUTTRACE("succeeded!!",0);
|
||||
if ((ac = available(code,vars)) != (code_p) 0) {
|
||||
/* The expression is already available, so we
|
||||
* don't have to generate a new temporary local for it.
|
||||
*/
|
||||
OUTTRACE("expression was already available",0);
|
||||
replcode(code,newcode(code,ac->co_temp));
|
||||
remcode(code);
|
||||
} else {
|
||||
make_header(code->co_loop);
|
||||
/* make sure there's a header block */
|
||||
tmp = tmplocal(curproc,code->co_tmpsize);
|
||||
code->co_temp = tmp;
|
||||
/* create a new local variable in the stack frame
|
||||
* of current proc.
|
||||
*/
|
||||
gen_regmes(tmp,3,code,curproc); /* generate register message */
|
||||
/* score is set to 3, as TMP is used at least 3 times */
|
||||
replcode(code,newcode(code,tmp));
|
||||
OUTTRACE("replaced old code by new code",0);
|
||||
/* Construct the EM-code that will replace the reducible code
|
||||
* and replace the old code by the new code.
|
||||
*/
|
||||
init_code(code,tmp);
|
||||
OUTTRACE("emitted initializing code",0);
|
||||
/* Emit code to initialize the temporary local. This code is
|
||||
* put in the loop header block.
|
||||
*/
|
||||
incr_code(code,tmp); /* emit code to increment temp. local */
|
||||
OUTTRACE("emitted increment code",0);
|
||||
Ladd(code,&avail);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC try_multiply(lp,ivs,vars,b,mul)
|
||||
loop_p lp;
|
||||
lset ivs,vars;
|
||||
bblock_p b;
|
||||
line_p mul;
|
||||
{
|
||||
/* See if we can reduce the strength of the multiply
|
||||
* instruction. If so, then set up the global common
|
||||
* data structure 'c' (containing information about the
|
||||
* code to be reduced) and call 'reduce'.
|
||||
*/
|
||||
|
||||
line_p l2,lbegin;
|
||||
iv_p iv;
|
||||
code_p c;
|
||||
int sign;
|
||||
|
||||
VL(mul);
|
||||
OUTTRACE("trying multiply instruction on line %d",linecount);
|
||||
if (ovfl_harmful && !IS_STRONG(b)) return;
|
||||
/* If b is not a strong block, optimization may
|
||||
* introduce an overflow error in the initializing code.
|
||||
*/
|
||||
|
||||
l2 = PREV(mul); /* Instruction before the multiply */
|
||||
if ( (is_ivexpr(l2,ivs,vars,&lbegin,&iv,&sign)) &&
|
||||
is_const(PREV(lbegin)) ) {
|
||||
/* recognized expression "const * iv_expr" */
|
||||
c = newcinfo();
|
||||
c->c_o.co_loadlc = PREV(l2);
|
||||
c->co_endexpr = l2;
|
||||
} else {
|
||||
if (is_const(l2) &&
|
||||
(is_ivexpr(PREV(l2),ivs,vars,&lbegin,&iv,&sign))) {
|
||||
/* recognized "iv * const " */
|
||||
c = newcinfo();
|
||||
c->c_o.co_loadlc = l2;
|
||||
c->co_endexpr = PREV(l2);
|
||||
} else {
|
||||
OUTTRACE("failed",0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* common part for both patterns */
|
||||
c->co_iv = iv;
|
||||
c->co_loop = lp;
|
||||
c->co_block = b;
|
||||
c->co_lfirst = PREV(l2);
|
||||
c->co_llast = mul;
|
||||
c->co_ivexpr = lbegin;
|
||||
c->co_sign = sign;
|
||||
c->co_tmpsize = ws; /* temp. local is a word */
|
||||
c->co_instr = INSTR(mul);
|
||||
OUTVERBOSE("sr: multiply in proc %d loop %d",
|
||||
curproc->p_id, lp->lp_id);
|
||||
Ssr++;
|
||||
reduce(c,vars);
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC try_array(lp,ivs,vars,b,arr)
|
||||
loop_p lp;
|
||||
lset ivs,vars;
|
||||
bblock_p b;
|
||||
line_p arr;
|
||||
{
|
||||
/* See if we can reduce the strength of the array reference
|
||||
* instruction 'arr'.
|
||||
*/
|
||||
|
||||
line_p l2,l3,lbegin;
|
||||
iv_p iv;
|
||||
code_p c;
|
||||
int sign;
|
||||
|
||||
/* Try to recognize the pattern:
|
||||
* LOAD ADDRES OF A
|
||||
* LOAD IV
|
||||
* LOAD ADDRESS OF DESCRIPTOR
|
||||
*/
|
||||
VL(arr);
|
||||
OUTTRACE("trying array instruction on line %d",linecount);
|
||||
if (arrbound_harmful && !IS_STRONG(b)) return;
|
||||
/* If b is not a strong block, optimization may
|
||||
* introduce an array bound error in the initializing code.
|
||||
*/
|
||||
l2 = PREV(arr);
|
||||
if (is_caddress(l2,vars) &&
|
||||
(INSTR(arr) == op_aar || elemsize(l2) == ws) &&
|
||||
(is_ivexpr(PREV(l2),ivs,vars,&lbegin,&iv,&sign)) ) {
|
||||
l3 = PREV(lbegin);
|
||||
if (is_caddress(l3,vars)) {
|
||||
c = newcinfo();
|
||||
c->co_iv = iv;
|
||||
c->co_loop = lp;
|
||||
c->co_block = b;
|
||||
c->co_lfirst = l3;
|
||||
c->co_llast = arr;
|
||||
c->co_ivexpr = lbegin;
|
||||
c->co_endexpr = PREV(l2);
|
||||
c->co_sign = sign;
|
||||
c->co_tmpsize = ps; /* temp. local is pointer */
|
||||
c->co_instr = INSTR(arr);
|
||||
c->c_o.co_desc = l2;
|
||||
OUTVERBOSE("sr: array in proc %d loop %d",
|
||||
curproc->p_id,lp->lp_id);
|
||||
Ssr++;
|
||||
reduce(c,vars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC clean_avail()
|
||||
{
|
||||
Lindex i;
|
||||
|
||||
for (i = Lfirst(avail); i != (Lindex) 0; i = Lnext(i,avail)) {
|
||||
oldcinfo(Lelem(i));
|
||||
}
|
||||
Ldeleteset(avail);
|
||||
}
|
||||
|
||||
|
||||
|
||||
strength_reduction(lp,ivs,vars)
|
||||
loop_p lp; /* description of the loop */
|
||||
lset ivs; /* set of induction variables of the loop */
|
||||
lset vars; /* set of local variables changed in loop */
|
||||
{
|
||||
/* Find all expensive instructions (multiply, array) and see if
|
||||
* they can be reduced. We branch to several instruction-specific
|
||||
* routines (try_...) that check if reduction is possible,
|
||||
* and that set up a common data structure (code_info).
|
||||
* The actual transformations are done by 'reduce', that is
|
||||
* essentially instruction-independend.
|
||||
*/
|
||||
|
||||
bblock_p b;
|
||||
line_p l, next;
|
||||
Lindex i;
|
||||
|
||||
avail = Lempty_set();
|
||||
for (i = Lfirst(lp->LP_BLOCKS); i != (Lindex) 0;
|
||||
i = Lnext(i,lp->LP_BLOCKS)) {
|
||||
b = (bblock_p) Lelem(i);
|
||||
for (l = b->b_start; l != (line_p) 0; l = next) {
|
||||
next = l->l_next;
|
||||
if (TYPE(l) == OPSHORT && SHORT(l) == ws) {
|
||||
switch(INSTR(l)) {
|
||||
case op_mlu:
|
||||
case op_mli:
|
||||
try_multiply(lp,ivs,vars,b,l);
|
||||
break;
|
||||
case op_lar:
|
||||
case op_sar:
|
||||
case op_aar:
|
||||
try_array(lp,ivs,vars,b,l);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
clean_avail();
|
||||
}
|
5
util/ego/sr/sr_reduce.h
Normal file
5
util/ego/sr/sr_reduce.h
Normal file
|
@ -0,0 +1,5 @@
|
|||
/* S R _ R E D U C E . H */
|
||||
|
||||
extern strength_reduction(); /* (loop_p loop; lset ivs, vars)
|
||||
* Perform streength reduction.
|
||||
*/
|
178
util/ego/sr/sr_xform.c
Normal file
178
util/ego/sr/sr_xform.c
Normal file
|
@ -0,0 +1,178 @@
|
|||
/* S T R E N G T H R E D U C T I O N
|
||||
*
|
||||
* S R _ X F O R M . C
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include "../share/types.h"
|
||||
#include "sr.h"
|
||||
#include "../share/debug.h"
|
||||
#include "../share/global.h"
|
||||
#include "../share/alloc.h"
|
||||
#include "../share/def.h"
|
||||
#include "../share/get.h"
|
||||
#include "sr_aux.h"
|
||||
#include "../share/lset.h"
|
||||
#include "../share/aux.h"
|
||||
#include "../../../h/em_mnem.h"
|
||||
#include "../../../h/em_pseu.h"
|
||||
#include "../../../h/em_spec.h"
|
||||
#include "sr_xform.h"
|
||||
|
||||
/* Transformations on EM texts */
|
||||
|
||||
line_p move_pointer(tmp,dir)
|
||||
offset tmp;
|
||||
int dir;
|
||||
{
|
||||
/* Generate EM code to load/store a pointer variable
|
||||
* onto/from the stack, depending on dir(ection).
|
||||
* We accept all kinds of pointer sizes.
|
||||
*/
|
||||
|
||||
line_p l;
|
||||
|
||||
l = int_line(tmp);
|
||||
if (ps == ws) {
|
||||
/* pointer fits in a word */
|
||||
l->l_instr = (dir == LOAD ? op_lol : op_stl);
|
||||
} else {
|
||||
if (ps == 2 * ws) {
|
||||
/* pointer fits in a double word */
|
||||
l->l_instr = (dir == LOAD ? op_ldl : op_sdl);
|
||||
} else {
|
||||
/* very large pointer size, generate code:
|
||||
* LAL tmp ; LOI/STI ps */
|
||||
l->l_instr = op_lal;
|
||||
l->l_next = newline(OPSHORT);
|
||||
SHORT(l->l_next) = ps;
|
||||
l->l_next->l_instr =
|
||||
(dir == LOAD ? op_loi : op_sti);
|
||||
PREV(l->l_next) = l;
|
||||
}
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* make_header */
|
||||
|
||||
STATIC copy_loops(b1,b2,except)
|
||||
bblock_p b1,b2;
|
||||
loop_p except;
|
||||
{
|
||||
/* Copy the loopset of b2 to b1, except for 'except' */
|
||||
|
||||
Lindex i;
|
||||
loop_p lp;
|
||||
for (i = Lfirst(b2->b_loops); i != (Lindex) 0;
|
||||
i = Lnext(i,b2->b_loops)) {
|
||||
lp = (loop_p) Lelem(i);
|
||||
if (lp != except) {
|
||||
Ladd(lp,&b1->b_loops);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
STATIC lab_id label(b)
|
||||
bblock_p b;
|
||||
{
|
||||
/* Find the label at the head of block b. If there is
|
||||
* no such label yet, create one.
|
||||
*/
|
||||
|
||||
line_p l;
|
||||
|
||||
assert (b->b_start != (line_p) 0);
|
||||
if (INSTR(b->b_start) == op_lab) return INSTRLAB(b->b_start);
|
||||
/* The block has no label yet. */
|
||||
l = newline(OPINSTRLAB);
|
||||
INSTRLAB(l) = freshlabel();
|
||||
DLINK(l,b->b_start); /* doubly link them */
|
||||
return INSTRLAB(l);
|
||||
}
|
||||
|
||||
|
||||
STATIC adjust_jump(newtarg,oldtarg,c)
|
||||
bblock_p newtarg,oldtarg,c;
|
||||
{
|
||||
/* If the last instruction of c is a jump to the
|
||||
* old target, then change it into a jump to the
|
||||
* start of the new target.
|
||||
*/
|
||||
|
||||
line_p l;
|
||||
|
||||
if (INSTR(oldtarg->b_start) == op_lab) {
|
||||
/* If old target has no label, it cannot be jumped to */
|
||||
l = last_instr(c);
|
||||
assert(l != (line_p) 0);
|
||||
if (TYPE(l) == OPINSTRLAB &&
|
||||
INSTRLAB(l) == INSTRLAB(oldtarg->b_start)) {
|
||||
INSTRLAB(l) = label(newtarg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
make_header(lp)
|
||||
loop_p lp;
|
||||
{
|
||||
/* Make sure that the loop has a header block, i.e. a block
|
||||
* has the loop entry block as its only successor and
|
||||
* that dominates the loop entry block.
|
||||
* If there is no header yet, create one.
|
||||
*/
|
||||
|
||||
bblock_p b,c,entry;
|
||||
Lindex i,next;
|
||||
|
||||
if (lp->LP_HEADER != (bblock_p) 0) return;
|
||||
OUTTRACE("creating a new header block",0);
|
||||
/* The loop has no header yet. The main problem is to
|
||||
* keep all relations (SUCC, PRED, NEXT, IDOM, LOOPS)
|
||||
* up to date.
|
||||
*/
|
||||
b = freshblock(); /* new block with new b_id */
|
||||
entry = lp->lp_entry;
|
||||
|
||||
/* update succ/pred. Also take care that any jump from outside
|
||||
* the loop to the entry block now goes to b.
|
||||
*/
|
||||
|
||||
for (i = Lfirst(entry->b_pred); i != (Lindex) 0; i = next ) {
|
||||
next = Lnext(i,entry->b_pred);
|
||||
c = (bblock_p) Lelem(i);
|
||||
/* c is a predecessor of the entry block */
|
||||
if (!Lis_elem(c,lp->LP_BLOCKS)) {
|
||||
/* c is outside the loop */
|
||||
Lremove(c,&entry->b_pred);
|
||||
Lremove(entry,&c->b_succ);
|
||||
Ladd(b,&c->b_succ);
|
||||
adjust_jump(b,entry,c);
|
||||
}
|
||||
}
|
||||
Ladd(b,&entry->b_pred);
|
||||
b->b_succ = Lempty_set();
|
||||
b->b_pred = Lempty_set();
|
||||
Ladd(entry,&b->b_succ);
|
||||
if (curproc->p_start == entry) {
|
||||
/* entry was the first block of curproc */
|
||||
curproc->p_start = b;
|
||||
} else {
|
||||
/* find block before entry block */
|
||||
for (c = curproc->p_start; c->b_next != entry; c = c->b_next);
|
||||
c->b_next = b;
|
||||
Ladd(c,&b->b_pred);
|
||||
}
|
||||
b->b_next = entry;
|
||||
copy_loops(b,entry,lp);
|
||||
b->b_idom = entry->b_idom;
|
||||
entry->b_idom = b;
|
||||
lp->LP_HEADER = b;
|
||||
}
|
19
util/ego/sr/sr_xform.h
Normal file
19
util/ego/sr/sr_xform.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
/* S T R E N G T H R E D U C T I O N
|
||||
*
|
||||
* S R _ X F O R M . H
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
line_p move_pointer(); /* (offset tmp; int dir ) */
|
||||
/* Generate EM code to load/store a pointer variable
|
||||
* onto/from the stack, depending on dir(ection).
|
||||
* We accept all kinds of pointer sizes.
|
||||
*/
|
||||
make_header() ; /* (loop_p lp) */
|
||||
/* Make sure that the loop has a header block, i.e. a block
|
||||
* has the loop entry block as its only successor and
|
||||
* that dominates the loop entry block.
|
||||
* If there is no header yet, create one.
|
||||
*/
|
98
util/ego/ud/Makefile
Normal file
98
util/ego/ud/Makefile
Normal file
|
@ -0,0 +1,98 @@
|
|||
EMH=../../../h
|
||||
EML=../../../lib
|
||||
CFLAGS=
|
||||
SHARE=../share
|
||||
UD=.
|
||||
OBJECTS=ud.o ud_const.o ud_copy.o ud_aux.o ud_defs.o
|
||||
SHOBJECTS=$(SHARE)/get.o $(SHARE)/put.o $(SHARE)/map.o $(SHARE)/alloc.o $(SHARE)/global.o $(SHARE)/debug.o $(SHARE)/lset.o $(SHARE)/cset.o $(SHARE)/files.o $(SHARE)/aux.o $(SHARE)/locals.o $(SHARE)/init_glob.o $(SHARE)/go.o
|
||||
SRC=ud.h ud_defs.h ud_const.h ud_copy.h ud_aux.h ud.c ud_defs.c ud_const.c ud_copy.c ud_aux.c
|
||||
.c.o:
|
||||
cc $(CFLAGS) -c $<
|
||||
all: $(OBJECTS)
|
||||
ud: \
|
||||
$(OBJECTS) $(SHOBJECTS)
|
||||
cc -o ud -i $(OBJECTS) $(SHOBJECTS) $(EML)/em_data.a
|
||||
lpr:
|
||||
pr $(SRC) | lpr
|
||||
opr:
|
||||
pr $(SRC) | opr
|
||||
dumpflop:
|
||||
tar -uf /mnt/ego/ud/ud.tarf $(SRC)
|
||||
# the next lines are generated automatically
|
||||
# AUTOAUTOAUTOAUTOAUTOAUTO
|
||||
ud.o: ../../../h/em_pseu.h
|
||||
ud.o: ../../../h/em_spec.h
|
||||
ud.o: ../share/alloc.h
|
||||
ud.o: ../share/aux.h
|
||||
ud.o: ../share/cset.h
|
||||
ud.o: ../share/debug.h
|
||||
ud.o: ../share/def.h
|
||||
ud.o: ../share/files.h
|
||||
ud.o: ../share/get.h
|
||||
ud.o: ../share/global.h
|
||||
ud.o: ../share/locals.h
|
||||
ud.o: ../share/lset.h
|
||||
ud.o: ../share/map.h
|
||||
ud.o: ../share/put.h
|
||||
ud.o: ../share/types.h
|
||||
ud.o: ud.h
|
||||
ud.o: ud_const.h
|
||||
ud.o: ud_copy.h
|
||||
ud.o: ud_defs.h
|
||||
ud_aux.o: ../../../h/em_mnem.h
|
||||
ud_aux.o: ../../../h/em_pseu.h
|
||||
ud_aux.o: ../../../h/em_spec.h
|
||||
ud_aux.o: ../share/alloc.h
|
||||
ud_aux.o: ../share/cset.h
|
||||
ud_aux.o: ../share/debug.h
|
||||
ud_aux.o: ../share/def.h
|
||||
ud_aux.o: ../share/global.h
|
||||
ud_aux.o: ../share/locals.h
|
||||
ud_aux.o: ../share/lset.h
|
||||
ud_aux.o: ../share/types.h
|
||||
ud_aux.o: ../ud/ud.h
|
||||
ud_aux.o: ../ud/ud_defs.h
|
||||
ud_const.o: ../../../h/em_mnem.h
|
||||
ud_const.o: ../../../h/em_pseu.h
|
||||
ud_const.o: ../../../h/em_spec.h
|
||||
ud_const.o: ../share/alloc.h
|
||||
ud_const.o: ../share/aux.h
|
||||
ud_const.o: ../share/cset.h
|
||||
ud_const.o: ../share/debug.h
|
||||
ud_const.o: ../share/def.h
|
||||
ud_const.o: ../share/global.h
|
||||
ud_const.o: ../share/locals.h
|
||||
ud_const.o: ../share/lset.h
|
||||
ud_const.o: ../share/types.h
|
||||
ud_const.o: ../ud/ud.h
|
||||
ud_const.o: ../ud/ud_defs.h
|
||||
ud_const.o: ud_aux.h
|
||||
ud_const.o: ud_const.h
|
||||
ud_copy.o: ../../../h/em_mnem.h
|
||||
ud_copy.o: ../../../h/em_pseu.h
|
||||
ud_copy.o: ../../../h/em_spec.h
|
||||
ud_copy.o: ../share/alloc.h
|
||||
ud_copy.o: ../share/aux.h
|
||||
ud_copy.o: ../share/cset.h
|
||||
ud_copy.o: ../share/debug.h
|
||||
ud_copy.o: ../share/def.h
|
||||
ud_copy.o: ../share/global.h
|
||||
ud_copy.o: ../share/locals.h
|
||||
ud_copy.o: ../share/lset.h
|
||||
ud_copy.o: ../share/types.h
|
||||
ud_copy.o: ../ud/ud.h
|
||||
ud_copy.o: ../ud/ud_defs.h
|
||||
ud_copy.o: ud_aux.h
|
||||
ud_copy.o: ud_copy.h
|
||||
ud_defs.o: ../../../h/em_mnem.h
|
||||
ud_defs.o: ../share/alloc.h
|
||||
ud_defs.o: ../share/aux.h
|
||||
ud_defs.o: ../share/cset.h
|
||||
ud_defs.o: ../share/debug.h
|
||||
ud_defs.o: ../share/global.h
|
||||
ud_defs.o: ../share/locals.h
|
||||
ud_defs.o: ../share/lset.h
|
||||
ud_defs.o: ../share/map.h
|
||||
ud_defs.o: ../share/types.h
|
||||
ud_defs.o: ud.h
|
||||
ud_defs.o: ud_defs.h
|
554
util/ego/ud/ud.c
Normal file
554
util/ego/ud/ud.c
Normal file
|
@ -0,0 +1,554 @@
|
|||
/* U S E - D E F I N I T I O N A N A L Y S I S */
|
||||
|
||||
#include <stdio.h>
|
||||
#include "../share/types.h"
|
||||
#include "ud.h"
|
||||
#include "../share/debug.h"
|
||||
#include "../share/global.h"
|
||||
#include "../share/lset.h"
|
||||
#include "../share/cset.h"
|
||||
#include "../share/def.h"
|
||||
#include "../share/files.h"
|
||||
#include "../share/map.h"
|
||||
#include "../share/get.h"
|
||||
#include "../share/put.h"
|
||||
#include "../share/alloc.h"
|
||||
#include "../share/aux.h"
|
||||
#include "../share/init_glob.h"
|
||||
#include "../share/locals.h"
|
||||
#include "../share/go.h"
|
||||
#include "../../../h/em_pseu.h"
|
||||
#include "../../../h/em_spec.h"
|
||||
#include "ud_defs.h"
|
||||
#include "ud_const.h"
|
||||
#include "ud_copy.h"
|
||||
|
||||
short nrglobals;
|
||||
short nrvars;
|
||||
|
||||
int Svalue,Svariable;
|
||||
|
||||
cond_p globl_cond_tab,local_cond_tab;
|
||||
|
||||
STATIC cond_p getcondtab(f)
|
||||
FILE *f;
|
||||
{
|
||||
int l,i;
|
||||
cond_p tab;
|
||||
|
||||
fscanf(f,"%d",&l);
|
||||
tab = newcondtab(l);
|
||||
for (i = 0; i < l; i++) {
|
||||
fscanf(f,"%d %d %d",&tab[i].mc_cond,&tab[i].mc_tval,
|
||||
&tab[i].mc_sval);
|
||||
}
|
||||
assert(tab[l-1].mc_cond == DEFAULT);
|
||||
return tab;
|
||||
}
|
||||
|
||||
|
||||
STATIC ud_machinit(f)
|
||||
FILE *f;
|
||||
{
|
||||
char s[100];
|
||||
|
||||
for (;;) {
|
||||
while(getc(f) != '\n');
|
||||
fscanf(f,"%s",s);
|
||||
if (strcmp(s,"%%UD") == 0)break;
|
||||
}
|
||||
globl_cond_tab = getcondtab(f);
|
||||
local_cond_tab = getcondtab(f);
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC bool test_cond(cond,val)
|
||||
short cond;
|
||||
offset val;
|
||||
{
|
||||
switch(cond) {
|
||||
case DEFAULT:
|
||||
return TRUE;
|
||||
case FITBYTE:
|
||||
return val >= -128 && val < 128;
|
||||
}
|
||||
assert(FALSE);
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
|
||||
STATIC short map_value(tab,val,time)
|
||||
struct cond_tab tab[];
|
||||
offset val;
|
||||
bool time;
|
||||
{
|
||||
cond_p p;
|
||||
|
||||
for (p = &tab[0]; ; p++) {
|
||||
if (test_cond(p->mc_cond,val)) {
|
||||
return (time ? p->mc_tval : p->mc_sval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
STATIC init_root(root)
|
||||
bblock_p root;
|
||||
{
|
||||
/* Initialise the IN OUT sets of the entry block of the
|
||||
* current procedure. Global variables and parameters
|
||||
* already have a value at this point, although we do
|
||||
* not know which value. Therefor, implicit definitions
|
||||
* to all global variables and parameters are
|
||||
* put in IN.
|
||||
*/
|
||||
|
||||
short v;
|
||||
|
||||
for (v = 1; v <= nrglobals; v++) {
|
||||
Cadd(IMPLICIT_DEF(GLOB_TO_VARNR(v)), &IN(root));
|
||||
}
|
||||
for (v = 1; v <= nrlocals; v++) {
|
||||
if (locals[v]->lc_off >= 0) {
|
||||
Cadd(IMPLICIT_DEF(LOC_TO_VARNR(v)),&IN(root));
|
||||
}
|
||||
}
|
||||
/* OUT(root) = IN(root) - KILL(root) + GEN(root) */
|
||||
Ccopy_set(IN(root),&OUT(root));
|
||||
Csubtract(KILL(root),&OUT(root));
|
||||
Cjoin(GEN(root),&OUT(root));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
STATIC unite_outs(bbset,setp)
|
||||
lset bbset;
|
||||
cset *setp;
|
||||
{
|
||||
/* Take the union of OUT(b), for all b in bbset,
|
||||
* and put the result in setp.
|
||||
*/
|
||||
|
||||
Lindex i;
|
||||
|
||||
Cclear_set(setp);
|
||||
for (i = Lfirst(bbset); i != (Lindex) 0; i = Lnext(i,bbset)) {
|
||||
Cjoin(OUT((bblock_p) Lelem(i)), setp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC solve_equations(p)
|
||||
proc_p p;
|
||||
{
|
||||
/* Solve the data flow equations for reaching
|
||||
* definitions of procedure p.
|
||||
* These equations are:
|
||||
* (1) OUT(b) = IN(b) - KILL(b) + GEN(b)
|
||||
* (2) IN(b) = OUT(p1) + .. + OUT(pn) ;
|
||||
* where PRED(b) = {p1, .. , pn}
|
||||
* We use the iterative algorithm of Aho&Ullman to
|
||||
* solve the equations.
|
||||
*/
|
||||
|
||||
register bblock_p b;
|
||||
bool change;
|
||||
cset newin;
|
||||
|
||||
/* initializations */
|
||||
newin = Cempty_set(nrdefs);
|
||||
for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
|
||||
IN(b) = Cempty_set(nrdefs);
|
||||
OUT(b) = Cempty_set(nrdefs);
|
||||
Ccopy_set(GEN(b), &OUT(b));
|
||||
}
|
||||
init_root(p->p_start);
|
||||
/* Global variables and parameters have already a value
|
||||
* at the procedure entry block.
|
||||
*/
|
||||
change = TRUE;
|
||||
/* main loop */
|
||||
while (change) {
|
||||
change = FALSE;
|
||||
for (b = p->p_start->b_next; b != (bblock_p) 0; b = b->b_next) {
|
||||
unite_outs(b->b_pred, &newin);
|
||||
/* newin = OUT(p1) + .. + OUT(pn) */
|
||||
if (!Cequal(newin,IN(b))) {
|
||||
change = TRUE;
|
||||
Ccopy_set(newin, &IN(b));
|
||||
Ccopy_set(IN(b), &OUT(b));
|
||||
Csubtract(KILL(b), &OUT(b));
|
||||
Cjoin(GEN(b), &OUT(b));
|
||||
}
|
||||
}
|
||||
}
|
||||
for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
|
||||
Cdeleteset(KILL(b));
|
||||
Cdeleteset(OUT(b));
|
||||
}
|
||||
Cdeleteset(newin);
|
||||
}
|
||||
|
||||
|
||||
|
||||
short global_addr_cost()
|
||||
{
|
||||
return add_timespace(map_value(globl_cond_tab,(offset) 0,TRUE),
|
||||
map_value(globl_cond_tab,(offset) 0,FALSE));
|
||||
}
|
||||
|
||||
short local_addr_cost(off)
|
||||
offset off;
|
||||
{
|
||||
return add_timespace(map_value(local_cond_tab,off,TRUE),
|
||||
map_value(local_cond_tab,off,FALSE));
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC bool fold_is_desirable(old,new)
|
||||
line_p old,new;
|
||||
{
|
||||
/* See if it is desirable to replace the variable used by the
|
||||
* EM instruction 'old' by the variable used by 'new'.
|
||||
* We do not replace 'cheaply addressable variables' by 'expensively
|
||||
* addressable variables'. E.g. if we're optimizing object code size,
|
||||
* we do not replace a local variable by a global variable on a VAX,
|
||||
* because the former occupies 1 or 2 bytes and the latter occupies
|
||||
* 4 bytes.
|
||||
* If 2 local variables are equally expensive to address, we replace
|
||||
* the first one by the second only if the first one is used at
|
||||
* least as many times as the second one.
|
||||
*/
|
||||
|
||||
local_p oldloc,newloc;
|
||||
short old_cost,new_cost,nr;
|
||||
bool ok;
|
||||
|
||||
if (TYPE(old) == OPOBJECT) {
|
||||
/* old variable is a global variable */
|
||||
return TYPE(new) != OPOBJECT &&
|
||||
global_addr_cost() >=
|
||||
local_addr_cost(off_set(new));
|
||||
}
|
||||
find_local(off_set(old),&nr,&ok);
|
||||
assert(ok);
|
||||
oldloc = locals[nr];
|
||||
old_cost = local_addr_cost(off_set(old));
|
||||
if (TYPE(new) == OPOBJECT) {
|
||||
return oldloc->lc_score == 2 || /* old var. can be eliminated */
|
||||
old_cost > global_addr_cost();
|
||||
}
|
||||
find_local(off_set(new),&nr,&ok);
|
||||
assert(ok);
|
||||
newloc = locals[nr];
|
||||
new_cost = local_addr_cost(off_set(new));
|
||||
return old_cost > new_cost ||
|
||||
(old_cost == new_cost && oldloc->lc_score < newloc->lc_score);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef TRACE
|
||||
/*********** TRACING ROUTINES ***********/
|
||||
|
||||
pr_localtab() {
|
||||
short i;
|
||||
local_p lc;
|
||||
|
||||
printf("LOCAL-TABLE (%d)\n\n",nrlocals);
|
||||
for (i = 1; i <= nrlocals; i++) {
|
||||
lc = locals[i];
|
||||
printf("LOCAL %d\n",i);
|
||||
printf(" offset= %D\n",lc->lc_off);
|
||||
printf(" size= %d\n",lc->lc_size);
|
||||
printf(" flags= %d\n",lc->lc_flags);
|
||||
}
|
||||
}
|
||||
|
||||
pr_globals()
|
||||
{
|
||||
dblock_p d;
|
||||
obj_p obj;
|
||||
|
||||
printf("GLOBALS (%d)\n\n",nrglobals);
|
||||
printf("ID GLOBNR\n");
|
||||
for (d = fdblock; d != (dblock_p) 0; d = d->d_next) {
|
||||
for (obj = d->d_objlist; obj != (obj_p) 0; obj = obj->o_next) {
|
||||
if (obj->o_globnr != 0) {
|
||||
printf("%d %d\n", obj->o_id,obj->o_globnr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern char em_mnem[];
|
||||
|
||||
pr_defs()
|
||||
{
|
||||
short i;
|
||||
line_p l;
|
||||
|
||||
printf("DEF TABLE\n\n");
|
||||
for (i = 1; i <= nrexpldefs; i++) {
|
||||
l = defs[i];
|
||||
printf("%d %s ",EXPL_TO_DEFNR(i),
|
||||
&em_mnem[(INSTR(l)-sp_fmnem)*4]);
|
||||
switch(TYPE(l)) {
|
||||
case OPSHORT:
|
||||
printf("%d\n",SHORT(l));
|
||||
break;
|
||||
case OPOFFSET:
|
||||
printf("%D\n",OFFSET(l));
|
||||
break;
|
||||
case OPOBJECT:
|
||||
printf("%d\n",OBJ(l)->o_id);
|
||||
break;
|
||||
default:
|
||||
assert(FALSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pr_set(name,k,s,n)
|
||||
char *name;
|
||||
cset s;
|
||||
short k,n;
|
||||
{
|
||||
short i;
|
||||
|
||||
printf("%s(%d) = {",name,k);
|
||||
for (i = 1; i <= n; i++) {
|
||||
if (Cis_elem(i,s)) {
|
||||
printf("%d ",i);
|
||||
}
|
||||
}
|
||||
printf ("}\n");
|
||||
}
|
||||
|
||||
pr_blocks(p)
|
||||
proc_p p;
|
||||
{
|
||||
bblock_p b;
|
||||
short n;
|
||||
|
||||
for (b = p->p_start; b != 0; b = b->b_next) {
|
||||
printf ("\n");
|
||||
n = b->b_id;
|
||||
pr_set("GEN",n,GEN(b),nrdefs);
|
||||
pr_set("KILL",n,KILL(b),nrdefs);
|
||||
pr_set("IN ",n,IN(b),nrdefs);
|
||||
pr_set("OUT",n,OUT(b),nrdefs);
|
||||
pr_set("CHGVARS",n,CHGVARS(b),nrvars);
|
||||
}
|
||||
}
|
||||
|
||||
pr_copies()
|
||||
{
|
||||
short i;
|
||||
|
||||
printf("\nCOPY TABLE\n\n");
|
||||
for (i = 1; i <= nrdefs; i++) {
|
||||
if (def_to_copynr[i] != 0) {
|
||||
printf("%d %d\n",i,def_to_copynr[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pr_cblocks(p)
|
||||
proc_p p;
|
||||
{
|
||||
bblock_p b;
|
||||
short n;
|
||||
|
||||
for (b = p->p_start; b != 0; b = b->b_next) {
|
||||
printf ("\n");
|
||||
n = b->b_id;
|
||||
pr_set("CGEN",n,C_GEN(b),nrcopies);
|
||||
pr_set("CKILL",n,C_KILL(b),nrcopies);
|
||||
pr_set("CIN ",n,C_IN(b),nrcopies);
|
||||
pr_set("COUT",n,C_OUT(b),nrcopies);
|
||||
}
|
||||
}
|
||||
|
||||
/*********** END TRACING ********/
|
||||
|
||||
#endif
|
||||
|
||||
STATIC ud_analysis(p)
|
||||
proc_p p;
|
||||
{
|
||||
/* Perform use-definition analysis on procedure p */
|
||||
|
||||
make_localtab(p); /* See for which local we'll keep ud-info */
|
||||
#ifdef TRACE
|
||||
pr_localtab();
|
||||
#endif
|
||||
nrvars = nrglobals + nrlocals;
|
||||
make_defs(p); /* Make a table of all useful definitions in p */
|
||||
#ifdef TRACE
|
||||
pr_defs();
|
||||
#endif
|
||||
nrdefs = nrexpldefs + nrvars; /* number of definitions */
|
||||
gen_sets(p); /* compute GEN(b), for every basic block b */
|
||||
kill_sets(p); /* compute KILL(b), for every basic block b */
|
||||
solve_equations(p); /* solve data flow equations for p */
|
||||
#ifdef TRACE
|
||||
pr_blocks(p);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC clean_maps()
|
||||
{
|
||||
local_p *p;
|
||||
cset *v;
|
||||
|
||||
oldmap(defs,nrexpldefs);
|
||||
for (p = &locals[1]; p <= &locals[nrlocals]; p++) {
|
||||
oldlocal(*p);
|
||||
}
|
||||
oldmap(locals,nrlocals);
|
||||
for (v = &vardefs[1]; v <= &vardefs[nrvars]; v++) {
|
||||
Cdeleteset(*v);
|
||||
}
|
||||
oldmap(vardefs,nrvars);
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC bool try_optim(l,b)
|
||||
line_p l;
|
||||
bblock_p b;
|
||||
{
|
||||
/* Try copy propagation and constant propagation */
|
||||
|
||||
line_p def;
|
||||
offset val;
|
||||
short defnr;
|
||||
|
||||
|
||||
if (is_use(l) && (def = unique_def(l,b,&defnr)) != (line_p) 0) {
|
||||
if (is_copy(def)) {
|
||||
if (value_retained(def,defnr,l,b) &&
|
||||
fold_is_desirable(l,PREV(def))) {
|
||||
fold_var(l,PREV(def),b);
|
||||
OUTVERBOSE("vp:variable folded in proc %d",
|
||||
curproc->p_id,0);
|
||||
Svariable++;
|
||||
return TRUE;
|
||||
}
|
||||
} else {
|
||||
if (value_known(def,&val)) {
|
||||
fold_const(l,b,val);
|
||||
OUTVERBOSE("vp:value folded in proc %d",
|
||||
curproc->p_id,0);
|
||||
Svalue++;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
value_propagation(p)
|
||||
proc_p p;
|
||||
{
|
||||
/* Apply value propagation to procedure p */
|
||||
|
||||
bool changes;
|
||||
bblock_p b;
|
||||
line_p l, next;
|
||||
|
||||
changes = TRUE;
|
||||
/* If a statement like A := B is folded to A := constant,
|
||||
* new opportunities for constant folding may arise,
|
||||
* e.g. the value of A might be statically known too now.
|
||||
*/
|
||||
|
||||
while (changes) {
|
||||
changes = FALSE;
|
||||
for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
|
||||
for (l = b->b_start; l != (line_p) 0; l = next) {
|
||||
next = l->l_next;
|
||||
if (try_optim(l,b)) {
|
||||
changes = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
oldmap(copies,nrcopies);
|
||||
oldtable(def_to_copynr,nrdefs);
|
||||
}
|
||||
|
||||
|
||||
STATIC ud_extend(p)
|
||||
proc_p p;
|
||||
{
|
||||
/* Allocate extended data structures for Use Definition analysis */
|
||||
|
||||
register bblock_p b;
|
||||
|
||||
for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
|
||||
b->b_extend = newudbx();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
STATIC ud_cleanup(p)
|
||||
proc_p p;
|
||||
{
|
||||
/* Deallocate extended data structures for Use Definition analysis */
|
||||
|
||||
register bblock_p b;
|
||||
|
||||
for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
|
||||
Cdeleteset(GEN(b));
|
||||
Cdeleteset(IN(b));
|
||||
Cdeleteset(C_GEN(b));
|
||||
Cdeleteset(C_KILL(b));
|
||||
Cdeleteset(C_IN(b));
|
||||
Cdeleteset(C_OUT(b));
|
||||
Cdeleteset(CHGVARS(b));
|
||||
oldudbx(b->b_extend);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ud_optimize(p)
|
||||
proc_p p;
|
||||
{
|
||||
ud_extend(p);
|
||||
locals = (local_p *) 0;
|
||||
vardefs = (cset *) 0;
|
||||
defs = (line_p *) 0;
|
||||
ud_analysis(p);
|
||||
copy_analysis(p);
|
||||
#ifdef TRACE
|
||||
pr_copies();
|
||||
pr_cblocks(p);
|
||||
#endif
|
||||
value_propagation(p);
|
||||
ud_cleanup(p);
|
||||
clean_maps();
|
||||
}
|
||||
|
||||
main(argc,argv)
|
||||
int argc;
|
||||
char *argv[];
|
||||
{
|
||||
go(argc,argv,init_globals,ud_optimize,ud_machinit,no_action);
|
||||
report("values folded",Svalue);
|
||||
report("variables folded",Svariable);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in a new issue