Initial revision

This commit is contained in:
bal 1984-11-26 14:51:59 +00:00
parent 1833451151
commit 6a9e49f683
20 changed files with 3224 additions and 0 deletions

40
util/ego/lv/Makefile Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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);
}