From 6a9e49f6838e8ccb6fcba57e4d9695922d58dfa0 Mon Sep 17 00:00:00 2001 From: bal Date: Mon, 26 Nov 1984 14:51:59 +0000 Subject: [PATCH] Initial revision --- util/ego/lv/Makefile | 40 +++ util/ego/lv/lv.c | 584 +++++++++++++++++++++++++++++++++++++ util/ego/lv/lv.h | 42 +++ util/ego/sr/Makefile | 108 +++++++ util/ego/sr/sr.c | 205 +++++++++++++ util/ego/sr/sr.h | 28 ++ util/ego/sr/sr_aux.c | 115 ++++++++ util/ego/sr/sr_aux.h | 20 ++ util/ego/sr/sr_cand.c | 187 ++++++++++++ util/ego/sr/sr_cand.h | 14 + util/ego/sr/sr_expr.c | 199 +++++++++++++ util/ego/sr/sr_expr.h | 13 + util/ego/sr/sr_iv.c | 183 ++++++++++++ util/ego/sr/sr_iv.h | 7 + util/ego/sr/sr_reduce.c | 625 ++++++++++++++++++++++++++++++++++++++++ util/ego/sr/sr_reduce.h | 5 + util/ego/sr/sr_xform.c | 178 ++++++++++++ util/ego/sr/sr_xform.h | 19 ++ util/ego/ud/Makefile | 98 +++++++ util/ego/ud/ud.c | 554 +++++++++++++++++++++++++++++++++++ 20 files changed, 3224 insertions(+) create mode 100644 util/ego/lv/Makefile create mode 100644 util/ego/lv/lv.c create mode 100644 util/ego/lv/lv.h create mode 100644 util/ego/sr/Makefile create mode 100644 util/ego/sr/sr.c create mode 100644 util/ego/sr/sr.h create mode 100644 util/ego/sr/sr_aux.c create mode 100644 util/ego/sr/sr_aux.h create mode 100644 util/ego/sr/sr_cand.c create mode 100644 util/ego/sr/sr_cand.h create mode 100644 util/ego/sr/sr_expr.c create mode 100644 util/ego/sr/sr_expr.h create mode 100644 util/ego/sr/sr_iv.c create mode 100644 util/ego/sr/sr_iv.h create mode 100644 util/ego/sr/sr_reduce.c create mode 100644 util/ego/sr/sr_reduce.h create mode 100644 util/ego/sr/sr_xform.c create mode 100644 util/ego/sr/sr_xform.h create mode 100644 util/ego/ud/Makefile create mode 100644 util/ego/ud/ud.c diff --git a/util/ego/lv/Makefile b/util/ego/lv/Makefile new file mode 100644 index 000000000..e6e828b96 --- /dev/null +++ b/util/ego/lv/Makefile @@ -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 diff --git a/util/ego/lv/lv.c b/util/ego/lv/lv.c new file mode 100644 index 000000000..a842e26ab --- /dev/null +++ b/util/ego/lv/lv.c @@ -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 +#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); +} diff --git a/util/ego/lv/lv.h b/util/ego/lv/lv.h new file mode 100644 index 000000000..27fc13597 --- /dev/null +++ b/util/ego/lv/lv.h @@ -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)) + + diff --git a/util/ego/sr/Makefile b/util/ego/sr/Makefile new file mode 100644 index 000000000..6e34e95b2 --- /dev/null +++ b/util/ego/sr/Makefile @@ -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 diff --git a/util/ego/sr/sr.c b/util/ego/sr/sr.c new file mode 100644 index 000000000..ce91d9ace --- /dev/null +++ b/util/ego/sr/sr.c @@ -0,0 +1,205 @@ +/* S T R E N G T H R E D U C T I O N */ + + +#include +#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); +} diff --git a/util/ego/sr/sr.h b/util/ego/sr/sr.h new file mode 100644 index 000000000..987473ca2 --- /dev/null +++ b/util/ego/sr/sr.h @@ -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 */ diff --git a/util/ego/sr/sr_aux.c b/util/ego/sr/sr_aux.c new file mode 100644 index 000000000..9b6d5b082 --- /dev/null +++ b/util/ego/sr/sr_aux.c @@ -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; +} diff --git a/util/ego/sr/sr_aux.h b/util/ego/sr/sr_aux.h new file mode 100644 index 000000000..a58dfbd54 --- /dev/null +++ b/util/ego/sr/sr_aux.h @@ -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) diff --git a/util/ego/sr/sr_cand.c b/util/ego/sr/sr_cand.c new file mode 100644 index 000000000..4dcd4ea0a --- /dev/null +++ b/util/ego/sr/sr_cand.c @@ -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; +} diff --git a/util/ego/sr/sr_cand.h b/util/ego/sr/sr_cand.h new file mode 100644 index 000000000..578fb58a4 --- /dev/null +++ b/util/ego/sr/sr_cand.h @@ -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. + */ diff --git a/util/ego/sr/sr_expr.c b/util/ego/sr/sr_expr.c new file mode 100644 index 000000000..d15ea8341 --- /dev/null +++ b/util/ego/sr/sr_expr.c @@ -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 +#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; +} diff --git a/util/ego/sr/sr_expr.h b/util/ego/sr/sr_expr.h new file mode 100644 index 000000000..dae187ccb --- /dev/null +++ b/util/ego/sr/sr_expr.h @@ -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.). + */ diff --git a/util/ego/sr/sr_iv.c b/util/ego/sr/sr_iv.c new file mode 100644 index 000000000..f228c2bf4 --- /dev/null +++ b/util/ego/sr/sr_iv.c @@ -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; +} diff --git a/util/ego/sr/sr_iv.h b/util/ego/sr/sr_iv.h new file mode 100644 index 000000000..a157e25fe --- /dev/null +++ b/util/ego/sr/sr_iv.h @@ -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. + */ diff --git a/util/ego/sr/sr_reduce.c b/util/ego/sr/sr_reduce.c new file mode 100644 index 000000000..42ca2eee3 --- /dev/null +++ b/util/ego/sr/sr_reduce.c @@ -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(); +} diff --git a/util/ego/sr/sr_reduce.h b/util/ego/sr/sr_reduce.h new file mode 100644 index 000000000..794f68f11 --- /dev/null +++ b/util/ego/sr/sr_reduce.h @@ -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. + */ diff --git a/util/ego/sr/sr_xform.c b/util/ego/sr/sr_xform.c new file mode 100644 index 000000000..e36f95ed2 --- /dev/null +++ b/util/ego/sr/sr_xform.c @@ -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 +#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; +} diff --git a/util/ego/sr/sr_xform.h b/util/ego/sr/sr_xform.h new file mode 100644 index 000000000..8ea103be8 --- /dev/null +++ b/util/ego/sr/sr_xform.h @@ -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. + */ diff --git a/util/ego/ud/Makefile b/util/ego/ud/Makefile new file mode 100644 index 000000000..e8991980f --- /dev/null +++ b/util/ego/ud/Makefile @@ -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 diff --git a/util/ego/ud/ud.c b/util/ego/ud/ud.c new file mode 100644 index 000000000..37a39ad16 --- /dev/null +++ b/util/ego/ud/ud.c @@ -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 +#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); +} + + +