--- /dev/null
+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
--- /dev/null
+
+/* 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);
+}
--- /dev/null
+/* 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))
+
+
--- /dev/null
+
+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
--- /dev/null
+/* 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);
+}
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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)
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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.
+ */
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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.).
+ */
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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.
+ */
--- /dev/null
+/* 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();
+}
--- /dev/null
+/* S R _ R E D U C E . H */
+
+extern strength_reduction(); /* (loop_p loop; lset ivs, vars)
+ * Perform streength reduction.
+ */
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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.
+ */
--- /dev/null
+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
--- /dev/null
+/* 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);
+}
+
+
+