Initial revision
authorbal <none@none>
Mon, 26 Nov 1984 14:51:59 +0000 (14:51 +0000)
committerbal <none@none>
Mon, 26 Nov 1984 14:51:59 +0000 (14:51 +0000)
20 files changed:
util/ego/lv/Makefile [new file with mode: 0644]
util/ego/lv/lv.c [new file with mode: 0644]
util/ego/lv/lv.h [new file with mode: 0644]
util/ego/sr/Makefile [new file with mode: 0644]
util/ego/sr/sr.c [new file with mode: 0644]
util/ego/sr/sr.h [new file with mode: 0644]
util/ego/sr/sr_aux.c [new file with mode: 0644]
util/ego/sr/sr_aux.h [new file with mode: 0644]
util/ego/sr/sr_cand.c [new file with mode: 0644]
util/ego/sr/sr_cand.h [new file with mode: 0644]
util/ego/sr/sr_expr.c [new file with mode: 0644]
util/ego/sr/sr_expr.h [new file with mode: 0644]
util/ego/sr/sr_iv.c [new file with mode: 0644]
util/ego/sr/sr_iv.h [new file with mode: 0644]
util/ego/sr/sr_reduce.c [new file with mode: 0644]
util/ego/sr/sr_reduce.h [new file with mode: 0644]
util/ego/sr/sr_xform.c [new file with mode: 0644]
util/ego/sr/sr_xform.h [new file with mode: 0644]
util/ego/ud/Makefile [new file with mode: 0644]
util/ego/ud/ud.c [new file with mode: 0644]

diff --git a/util/ego/lv/Makefile b/util/ego/lv/Makefile
new file mode 100644 (file)
index 0000000..e6e828b
--- /dev/null
@@ -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 (file)
index 0000000..a842e26
--- /dev/null
@@ -0,0 +1,584 @@
+
+/* L I V E   V A R I A B L E S   A N A L Y S I S */
+
+#include <stdio.h>
+#include "../share/types.h"
+#include "lv.h"
+#include "../share/debug.h"
+#include "../share/global.h"
+#include "../share/lset.h"
+#include "../share/cset.h"
+#include "../share/def.h"
+#include "../share/files.h"
+#include "../share/alloc.h"
+#include "../share/map.h"
+#include "../share/get.h"
+#include "../share/put.h"
+#include "../share/aux.h"
+#include "../share/init_glob.h"
+#include "../share/locals.h"
+#include "../share/go.h"
+#include "../../../h/em_mnem.h"
+#include "../../../h/em_pseu.h"
+#include "../../../h/em_spec.h"
+#include "../share/parser.h"
+
+/* TEMPORARY: should be put in ../../../h/em_mes.h: */
+#define ms_liv 9
+#define ms_ded 10
+
+short nrglobals;
+short nrvars;
+
+STATIC int Slv;
+STATIC bool mesgflag = FALSE;  /* Suppress generation of live/dead info */
+
+
+STATIC clean_up()
+{
+       local_p *p;
+
+       for (p = &locals[1]; p <= &locals[nrlocals]; p++) {
+               oldlocal(*p);
+       }
+       oldmap(locals,nrlocals);
+}
+
+
+
+STATIC bool is_dir_use(l)
+       line_p l;
+{
+       /* See if l is a direct use of some variable
+        * (i.e. not through a pointer). A LIL is a
+        * direct use of some pointer variable
+        * (and an indirect use of some other variable).
+        * A SIL is also a direct use.
+        * A LOI, however, is not an direct use of a variable.
+        * An an increment/decrement instruction is regarded
+        * as a use here, and not as a definition, as the
+        * variable is first used and than defined.
+        */
+
+       switch(INSTR(l)) {
+               case op_dee:
+               case op_del:
+               case op_ine:
+               case op_inl:
+               case op_lde:
+               case op_ldl:
+               case op_lil:
+               case op_loe:
+               case op_lol:
+               case op_sil:
+                       return TRUE;
+               default:
+                       return FALSE;
+       }
+       /* NOTREACHED */
+}
+
+
+
+STATIC bool is_indir_use(l)
+       line_p l;
+{
+       /* See if instruction l uses some variable(s) indirectly,
+        * i.e. through a pointer or via a procedure call.
+        */
+
+       switch(INSTR(l)) {
+               case op_blm:
+               case op_bls:
+               case op_cai:
+               case op_cal:
+               case op_lar:
+               case op_ldf:
+               case op_lil:
+               case op_lof:
+               case op_loi:
+               case op_los:
+               case op_mon:
+                       return TRUE;
+               default:
+                       return FALSE;
+       }
+       /* NOTREACHED */
+}
+
+
+
+STATIC bool is_def(l)
+       line_p l;
+{
+       /* See if l does a direct definition */
+
+       switch(INSTR(l)) {
+               case op_sde:
+               case op_sdl:
+               case op_ste:
+               case op_stl:
+               case op_zre:
+               case op_zrl:
+                       return TRUE;
+               default:
+                       return FALSE;
+       }
+       /* NOTREACHED */
+}
+
+
+STATIC def_use(p)
+       proc_p p;
+{
+       /* Compute DEF(b) and USE(b), for every basic block b
+        * of procedure p. DEF(b) contains the variables that
+        * are certain to be defined (assigned) in b
+        * before being used. USE(b) contains the variables
+        * that may be used in b, before being defined.
+        * (Note that uncertainty arises in the presence of
+        *  pointers and procedure calls).
+        * We compute these sets, by scanning the text of
+        * the basic block from beginning till end.
+        */
+
+       register bblock_p b;
+       register line_p l;
+       short v;
+       bool found;
+       cset all_ind_uses;
+
+       all_ind_uses = Cempty_set(nrvars);
+       for (v = 1; v < nrlocals; v++) {
+               if (!IS_REGVAR(locals[v])) {
+                       Cadd(LOC_TO_VARNR(v),&all_ind_uses);
+               }
+       }
+       for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
+               USE(b) = Cempty_set(nrvars);
+               DEF(b) = Cempty_set(nrvars);
+               for (l = b->b_start; l != (line_p) 0; l = l->l_next) {
+                       if (is_def(l)) {
+                               /* An direct definition (i.e. not
+                                * through a pointer).
+                                */
+                               var_nr(l,&v,&found);
+                               if (found && !Cis_elem(v,USE(b))) {
+                                       /* We do maintain live-dead info
+                                        * for this variable, and it was
+                                        * not used earlier in b.
+                                        */
+                                       Cadd(v, &DEF(b));
+                               }
+                       } else {
+                               if (is_dir_use(l)) {
+                                       var_nr(l,&v,&found);
+                                       if (found && !Cis_elem(v,DEF(b))) {
+                                               Cadd(v, &USE(b));
+                                       }
+                               }
+                               if (is_indir_use(l)) {
+                                       /* Add variable that may be used
+                                        * by l to USE(b).
+                                        */
+                                       Cjoin(all_ind_uses,&USE(b));
+                               }
+                       }
+               }
+       }
+       Cdeleteset(all_ind_uses);
+}
+
+
+
+STATIC unite_ins(bbset,setp)
+       lset bbset;
+       cset *setp;
+{
+       /* Take the union of L_IN(b), for all b in bbset,
+        * and put the result in setp.
+        */
+
+       Lindex i;
+
+       Cclear_set(setp);
+       for (i = Lfirst(bbset); i != (Lindex) 0; i = Lnext(i,bbset)) {
+               Cjoin(L_IN((bblock_p) Lelem(i)), setp);
+       }
+}
+
+
+
+STATIC solve_lv(p)
+       proc_p p;
+{
+       /* Solve the data flow equations for Live Variables,
+        * for procedure p. These equations are:
+        *  (1)   IN[b] = OUT[b] - DEF[b] + USE[b]
+        *  (2)   OUT(b) = IN(s1) + ... + IN(sn) ;
+        *        where SUCC(b) = {s1, ... , sn}
+        */
+
+       register bblock_p b;
+       cset newout = Cempty_set(nrvars);
+       bool change = TRUE;
+
+       for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
+               L_IN(b) = Cempty_set(nrvars);
+               Ccopy_set(USE(b), &L_IN(b));
+               L_OUT(b) = Cempty_set(nrvars);
+       }
+       while (change) {
+               change = FALSE;
+               for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
+                       unite_ins(b->b_succ,&newout);
+                       if (!Cequal(newout,L_OUT(b))) {
+                               change = TRUE;
+                               Ccopy_set(newout, &L_OUT(b));
+                               Ccopy_set(newout, &L_IN(b));
+                               Csubtract(DEF(b), &L_IN(b));
+                               Cjoin(USE(b), &L_IN(b));
+                       }
+               }
+       }
+       Cdeleteset(newout);
+}
+
+
+STATIC live_variables_analysis(p)
+       proc_p p;
+{
+       make_localtab(p);
+       nrvars = nrglobals + nrlocals;
+       def_use(p);
+       solve_lv(p);
+}
+
+
+STATIC init_live_dead(b)
+       bblock_p b;
+{
+       /* For every register variable, see if it is
+        * live or dead at the end of b.
+        */
+
+       register short v;
+       local_p loc;
+
+       for (v = 1; v <= nrlocals; v++) {
+               loc = locals[v];
+               if (IS_REGVAR(loc) && Cis_elem(LOC_TO_VARNR(v),L_OUT(b))) {
+                       LIVE(loc);
+               } else {
+                       DEAD(loc);
+               }
+       }
+}
+
+
+
+STATIC line_p make_mesg(mesg,loc)
+       short mesg;
+       local_p loc;
+{
+       /* Create a line for a message stating that
+        * local variable loc is live/dead. This message
+        * looks like: "mes ms_liv,off,size" or
+        * "mes ms_ded,off,size".
+        */
+
+       line_p l = newline(OPLIST);
+       register arg_p ap;
+
+       l->l_instr = ps_mes;
+       ap = ARG(l) = newarg(ARGOFF);
+       ap->a_a.a_offset = mesg;
+       ap = ap->a_next = newarg(ARGOFF);
+       ap->a_a.a_offset = loc->lc_off;
+       ap = ap->a_next = newarg(ARGOFF);
+       ap->a_a.a_offset = loc->lc_size;
+       return l;
+}
+
+
+
+STATIC block_entry(b,prev)
+       bblock_p b,prev;
+{
+       short v,vn;
+       local_p loc;
+       bool was_live, is_live;
+
+       /* Generate a live/dead message for every register variable that
+        * was live at the end of prev, but dead at the beginning of b,
+        * or v.v. If prev = 0 (i.e. begin of procedure), parameters were
+        * live, normal local variables were dead.
+        */
+
+       for (v = 1; v <= nrlocals; v++) {
+               loc = locals[v];
+               vn = LOC_TO_VARNR(v);
+               if (prev == (bblock_p) 0) {
+                       was_live = loc->lc_off >= 0;
+               } else {
+                       was_live = Cis_elem(vn,L_OUT(prev));
+               }
+               is_live = Cis_elem(vn,L_IN(b));
+               if (was_live != is_live) {
+                       app_block(make_mesg((is_live?ms_liv:ms_ded),loc),b);
+               }
+       }
+}
+
+
+
+STATIC app_block(l,b)
+       line_p l;
+       bblock_p b;
+{
+       line_p x = b->b_start;
+
+       if (x != (line_p) 0 && INSTR(x) == ps_pro) {
+               /* start of procedure; append after pro pseudo ! */
+               if ((l->l_next = x->l_next) != (line_p) 0) {
+                       PREV(l->l_next) = l;
+               }
+               x->l_next = l;
+               PREV(l) = x;
+       } else {
+               if ((l->l_next = x) != (line_p) 0) {
+                       PREV(l->l_next) = l;
+               }
+               b->b_start = l;
+               PREV(l) = (line_p) 0;
+       }
+}
+
+
+
+STATIC definition(l,useless_out,v_out,mesgflag)
+       line_p l;
+       bool *useless_out;
+       short *v_out;
+       bool mesgflag;
+{
+       /* Process a definition. If the defined (register-) variable
+        * is live after 'l', then create a live-message and put
+        * it after 'l'.
+        */
+
+       short v;
+       bool found;
+       local_p loc;
+
+       *useless_out = FALSE;
+       var_nr(l,&v,&found);
+       if (found && IS_LOCAL(v)) {
+               *v_out = v;
+               loc = locals[TO_LOCAL(v)];
+               if (IS_REGVAR(loc)) {
+                       if (IS_LIVE(loc)) {
+                               if (!mesgflag) {
+                                       appnd_line(make_mesg(ms_liv,loc), l);
+                               }
+                               DEAD(loc);
+                       } else {
+                               *useless_out = TRUE;
+                       }
+               }
+       }
+}
+
+
+
+
+STATIC use(l,mesgflag)
+       line_p l;
+       bool mesgflag;
+{
+       /* Process a use. If the defined (register-) variable
+        * is dead after 'l', then create a dead-message and put
+        * it after 'l'.
+        */
+
+       short v;
+       bool found;
+       local_p loc;
+
+       var_nr(l,&v,&found);
+       if (found && IS_LOCAL(v)) {
+               loc = locals[TO_LOCAL(v)];
+               if (IS_REGVAR(loc) && IS_DEAD(loc)) {
+                       if (!mesgflag) {
+                               appnd_line(make_mesg(ms_ded,loc), l);
+                       }
+                       LIVE(loc);
+               }
+       }
+}
+
+
+
+STATIC nothing() { }  /* No action to be undertaken at level 0 of parser */
+
+STATIC rem_code(l1,l2,b)
+       line_p l1,l2;
+       bblock_p b;
+{
+       line_p l,x,y;
+
+       x = PREV(l1);
+       y = l2->l_next;
+       for (l = l1; l != l2; l = l->l_next) {
+               oldline(l);
+       }
+       if (x == (line_p) 0) {
+               b->b_start = y;
+       } else {
+               x->l_next = y;
+       }
+       if (y != (line_p) 0) {
+               PREV(y) = x;
+       }
+}
+
+
+
+
+#define SIZE(v)        ((offset) locals[TO_LOCAL(v)]->lc_size)
+
+
+
+
+lv_mesg(p,mesgflag)
+       proc_p p;
+       bool mesgflag;
+{
+       /* Create live/dead messages for every possible register
+        * variable of p. A dead-message is put after a "use" of
+        * such a variable, if the variable becomes dead just
+        * after the use (i.e. this was its last use).
+        * A live message is put after a "definition" of such
+        * a variable, if the variable becomes live just
+        * after the definition (which will usually be the case).
+        * We traverse every basic block b of p from the last
+        * instruction of b backwards to the beginning of b.
+        * Initially, all variables that are dead at the end
+        * of b are marked dead. All others are marked live.
+        * If we come accross a definition of a variable X that
+        * was marked live, we put a live-message after the
+        * definition and mark X dead.
+        * If we come accross a use of a variable X that
+        * was marked dead, we put a dead-message after the
+        * use and mark X live.
+        * So at any point, the mark of X tells whether X is
+        * live or dead immediately before (!) that point.
+        * We also generate a message at the start of a basic block
+        * for every variable that was live at the end of the (textually)
+        * previous block, but dead at the entry of this block, or v.v.
+        * On the fly, useless assignments are removed.
+        */
+
+       register bblock_p b;
+       register line_p l;
+       line_p lnp, prev;
+       bblock_p prevb = (bblock_p) 0;
+       short v;
+       bool useless;
+
+       for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
+               block_entry(b,prevb); /* generate message at head of block */
+               prevb = b;
+               if (!mesgflag) {
+                       init_live_dead(b);
+               }
+               for (l = last_instr(b); l != (line_p) 0; l = prev) {
+                       /* traverse backwards! */
+                       prev = PREV(l);
+                       if (is_def(l)) {
+                               definition(l,&useless,&v,mesgflag);
+                               if (useless &&   /* assignment to dead var. */
+                                   parse(prev,SIZE(v),&lnp,0,nothing)) {
+                                       /* The code "VAR := expression" can
+                                        * be removed. 'l' is the "STL VAR",
+                                        * lnp is the beginning of the EM code
+                                        * for the expression.
+                                        */
+                                       prev = PREV(lnp);
+                                       rem_code(lnp,l,b);
+OUTVERBOSE("useless assignment ,proc %d,local %d", curproc->p_id,
+  (int) locals[TO_LOCAL(v)]->lc_off);
+                                       Slv++;
+                               }
+                       } else {
+                               if (is_dir_use(l))  {
+                                       use(l,mesgflag);
+                               }
+                       }
+               }
+       }
+}
+
+
+STATIC lv_extend(p)
+       proc_p p;
+{
+       /* Allocate extended data structures for Use Definition analysis */
+
+       register bblock_p b;
+
+       for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
+               b->b_extend = newlvbx();
+       }
+}
+
+
+STATIC lv_cleanup(p)
+       proc_p p;
+{
+       /* Deallocate extended data structures for Use Definition analysis */
+
+       register bblock_p b;
+
+       for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
+               Cdeleteset(USE(b));
+               Cdeleteset(DEF(b));
+               Cdeleteset(L_IN(b));
+               Cdeleteset(L_OUT(b));
+               oldlvbx(b->b_extend);
+       }
+}
+
+lv_flags(p)
+       char *p;
+{
+       switch(*p) {
+               case 'N':
+                       mesgflag = TRUE;
+                       break;
+       }
+}
+
+
+lv_optimize(p)
+       proc_p p;
+{
+       locals = (local_p *) 0;
+       lv_extend(p);
+       live_variables_analysis(p);
+       lv_mesg(p,mesgflag);
+       /* generate live-dead messages for regvars */
+       lv_cleanup(p);
+       clean_up();
+}
+
+
+
+main(argc,argv)
+       int argc;
+       char *argv[];
+{
+       go(argc,argv,init_globals,lv_optimize,no_action,lv_flags);
+       report("useless assignments deleted",Slv);
+       exit(0);
+}
diff --git a/util/ego/lv/lv.h b/util/ego/lv/lv.h
new file mode 100644 (file)
index 0000000..27fc135
--- /dev/null
@@ -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 (file)
index 0000000..6e34e95
--- /dev/null
@@ -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 (file)
index 0000000..ce91d9a
--- /dev/null
@@ -0,0 +1,205 @@
+/* S T R E N G T H   R E D U C T I O N  */
+
+
+#include <stdio.h>
+#include "../share/types.h"
+#include "sr.h"
+#include "../share/debug.h"
+#include "../share/global.h"
+#include "../share/files.h"
+#include "../share/get.h"
+#include "../share/put.h"
+#include "../share/lset.h"
+#include "../share/map.h"
+#include "../share/alloc.h"
+#include "../share/go.h"
+#include "sr_aux.h"
+#include "sr_iv.h"
+
+/* Strength reduction tries to change expensive operators occurring
+ * in a loop into cheaper operators. The expensive operators considered
+ * are multiplication and array referencing.
+ * The transformations can be expressed in C as:
+ *
+ * [1]:                for (i = e1; i <= e2; i++)
+ *                     print(118*i);
+ *   becomes:
+ *             for (i = e1, t = 118*e1; i <= e2; i++, t += 118)
+ *                     print(t);
+ *
+ * [2]:                for (i = e1; i <= e2; i++)
+ *                     print(a[i]);
+ *   becomes:
+ *             for (i = e1, p = &a[i]; i <= e2; i++, p++)
+ *                     print(*p);
+ * The latter optimization is suppressed if array bound checking
+ * is required.
+ */
+
+/* Machine and/or language dependent parameters: */
+
+bool ovfl_harmful;
+bool arrbound_harmful;
+
+int Ssr;  /* #optimizations found */
+
+sr_machinit(f)
+       FILE *f;
+{
+       /* Read target machine dependent information */
+       char s[100];
+
+
+       for (;;) {
+               while(getc(f) != '\n');
+               fscanf(f,"%s",s);
+               if (strcmp(s,"%%SR") == 0)break;
+       }
+       fscanf(f,"%d",&ovfl_harmful);
+       fscanf(f,"%d",&arrbound_harmful);
+}
+
+STATIC del_ivs(ivs)
+       lset ivs;
+{
+       /* Delete the set of iv structs */
+
+       Lindex i;
+
+       for (i = Lfirst(ivs); i != (Lindex) 0; i = Lnext(i,ivs)) {
+               oldiv(Lelem(i));
+       }
+       Ldeleteset(ivs);
+}
+
+
+STATIC do_loop(loop)
+       loop_p loop;
+{
+       lset ivs, vars;
+
+       OUTTRACE("going to process loop %d",loop->lp_id);
+       induc_vars(loop,&ivs, &vars);
+       /* Build a set of iv_structs, one for every induction
+        * variable of the loop, i.e. a variable i that
+        * is changed only by  i := i + c, where c is a loop constant.
+        * Also detects variables that are changed (including induction
+        * variables!).
+        */
+       OUTTRACE("loop has %d induction variables",Lnrelems(ivs));
+       if (Lnrelems(ivs) > 0) {
+               strength_reduction(loop,ivs,vars);
+               /* Perform strength reduction. Reduce:
+                *    iv * c    to addition
+                *    a[iv]     to indirection (*p)
+                *     (unless array bound checking is required)
+                */
+       }
+       del_ivs(ivs);
+       Ldeleteset(vars);
+}
+
+
+
+STATIC loopblocks(p)
+       proc_p p;
+{
+       /* Compute the LP_BLOCKS sets for all loops of p */
+
+       register bblock_p b;
+       register Lindex i;
+
+       for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
+               for (i = Lfirst(b->b_loops); i != (Lindex) 0;
+                  i = Lnext(i,b->b_loops)) {
+                       Ladd(b,&(((loop_p) Lelem(i))->LP_BLOCKS));
+               }
+       }
+}
+
+
+
+STATIC opt_proc(p)
+       proc_p p;
+{
+       /* Optimize all loops of one procedure. We first do all
+        * outer loops at the lowest nesting level and proceed
+        * in the inwards direction.
+        */
+
+       Lindex i;
+       loop_p lp,outermost;
+       int min_level;
+
+       for (;;) {
+               min_level = 1000;
+               for (i = Lfirst(p->p_loops); i != (Lindex) 0;
+                    i = Lnext(i,p->p_loops)) {
+                       lp = (loop_p) Lelem(i);
+                       if (!lp->LP_DONE && lp->lp_level < min_level) {
+                               min_level = lp->lp_level;
+                               outermost = lp;
+                       }
+               }
+               if (min_level == 1000) break;
+               do_loop(outermost);
+               outermost->LP_DONE = TRUE;
+               OUTTRACE("loop %d processed",outermost->lp_id);
+       }
+}
+
+
+
+STATIC sr_extproc(p)
+       proc_p p;
+{
+       /* Allocate the extended data structures for procedure p */
+
+       register loop_p lp;
+       register Lindex pi;
+
+       for (pi = Lfirst(p->p_loops); pi != (Lindex) 0;
+          pi = Lnext(pi,p->p_loops)) {
+               lp = (loop_p) Lelem(pi);
+               lp->lp_extend = newsrlpx();
+       }
+}
+
+
+
+STATIC sr_cleanproc(p)
+       proc_p p;
+{
+       /* Remove the extended data structures for procedure p */
+
+       register loop_p lp;
+       register Lindex pi;
+
+
+       for (pi = Lfirst(p->p_loops); pi != (Lindex) 0;
+          pi = Lnext(pi,p->p_loops)) {
+               lp = (loop_p) Lelem(pi);
+               oldsrlpx(lp->lp_extend);
+       }
+}
+
+
+sr_optimize(p)
+       proc_p p;
+{
+       sr_extproc(p);
+       loopblocks(p);
+       opt_proc(p);
+       sr_cleanproc(p);
+}
+
+
+
+main(argc,argv)
+       int argc;
+       char *argv[];
+{
+       go(argc,argv,no_action,sr_optimize,sr_machinit,no_action);
+       report("strength reductions",Ssr);
+       exit(0);
+}
diff --git a/util/ego/sr/sr.h b/util/ego/sr/sr.h
new file mode 100644 (file)
index 0000000..987473c
--- /dev/null
@@ -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 (file)
index 0000000..9b6d5b0
--- /dev/null
@@ -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 (file)
index 0000000..a58dfbd
--- /dev/null
@@ -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 (file)
index 0000000..4dcd4ea
--- /dev/null
@@ -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 (file)
index 0000000..578fb58
--- /dev/null
@@ -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 (file)
index 0000000..d15ea83
--- /dev/null
@@ -0,0 +1,199 @@
+/* S T R E N G T H   R E D U C T I O N 
+ *
+ * S R _ E X P R . C
+ *
+ */
+
+
+#include <stdio.h>
+#include "../share/types.h"
+#include "sr.h"
+#include "../share/debug.h"
+#include "../share/global.h"
+#include "../share/aux.h"
+#include "sr_aux.h"
+#include "../share/lset.h"
+#include "sr_iv.h"
+#include "../../../h/em_mnem.h"
+
+
+
+#define ME_NONE                0
+#define ME_UNAIR       1
+#define ME_BINAIR      2
+#define ME_LOOPCONST   3
+#define ME_IV          4
+
+
+
+STATIC iv_p last_iv;
+STATIC int  iv_sign;
+STATIC lset ivars, loopvars;
+
+STATIC bool is_loadiv(lnp)
+       line_p lnp;
+{
+       /* See if lnp is a LOL iv instruction, where iv is an
+        * induction variable of the set ivars. If so, set the
+        * the global variable last_iv to its descriptor.
+        */
+
+       Lindex i;
+       iv_p iv;
+       offset off;
+
+       if (INSTR(lnp) == op_lol) {
+               off = off_set(lnp);
+               for (i = Lfirst(ivars); i != (Lindex) 0; i = Lnext(i,ivars)) {
+                       iv = (iv_p) Lelem(i);
+                       if (iv->iv_off == off) {
+                               last_iv = iv;
+                               return TRUE;
+                       }
+               }
+       }
+       return FALSE;
+}
+
+
+
+
+#define size_ok(l)     (TYPE(l) == OPSHORT && SHORT(l) == ws)
+
+
+STATIC int me_kind(l,sign_in,sign_out)
+       line_p l;
+       int    sign_in, *sign_out;
+{
+       if (l != (line_p) 0) {
+               switch(INSTR(l)) {
+                       case op_adi:
+                       case op_adu:
+                               if (size_ok(l)) {
+                                       *sign_out = sign_in;
+                                       return ME_BINAIR;
+                               }
+                               break;
+                       case op_sbi:
+                       case op_sbu:
+                               if (size_ok(l)) {
+                                       *sign_out = - sign_in;
+                                       return ME_BINAIR;
+                               }
+                               break;
+                       case op_ngi:
+                               if (size_ok(l)) {
+                                       *sign_out = - sign_in;
+                                       return ME_UNAIR;
+                               }
+                               break;
+                       case op_inc:
+                       case op_dec:
+                               *sign_out = sign_in;
+                               return ME_UNAIR;
+                       case op_loc:
+                               return ME_LOOPCONST;
+                       case op_lol:
+                               if (is_loadiv(l)) {
+                                       iv_sign = sign_in;
+                                       return ME_IV;
+                               }
+                               if (is_loopconst(l,loopvars)) return ME_LOOPCONST;
+               }
+       }
+       return ME_NONE;
+}
+
+
+
+STATIC bool match_expr(l,iv_allowed,lbegin,iv_seen,sign)
+       line_p l,*lbegin;
+       bool   iv_allowed, *iv_seen;
+       int    sign;
+{
+       /* This routine is a top down parser for simple
+        * EM expressions. It recognizes expressions that
+        * have as operators + and - (unary - is also allowed)
+        * and that have as operands a number of loop constants
+        * (either a constant or a variable that is not
+        * changed within the loop) and at most one induction
+        * variable.
+        * The parameter iv_allowed is propagated downwards
+        * in the expression tree, indicating whether the
+        * subexpression may use an induction variable as 
+        * operand. The parameter iv_seen is propagated
+        * upwards, indicating if the subexpression has used
+        * an induction variable. The parameter sign is
+        * propagated downwards; it indicates the sign of
+        * the subexpression. lbegin will point to the
+        * beginning of the recognized subexpression
+        * (it is an out parameter). Note that we scan the
+        * EM text from right to left (i.e. top down).
+        */
+
+       line_p l1;
+       bool   iv_insubexpr;
+       int    sign2;
+
+       switch(me_kind(l,sign,&sign2)) {
+          case ME_UNAIR:
+               /* unairy operator, match one subexpression */
+               if (match_expr(PREV(l),iv_allowed,&l1,&iv_insubexpr,sign2)) {
+                       *lbegin = l1;
+                       *iv_seen = iv_insubexpr;
+                       return TRUE;
+               }
+               return FALSE;
+          case ME_BINAIR:
+               /* binairy operator, match two subexpressions */
+               if (match_expr(PREV(l), iv_allowed, &l1, &iv_insubexpr,sign2)) {
+                       l = PREV(l1);
+                       iv_allowed = iv_allowed && !iv_insubexpr;
+                       if (match_expr(l,iv_allowed,&l1,
+                               &iv_insubexpr,sign)) {
+                               *lbegin = l1;
+                               *iv_seen = !iv_allowed || iv_insubexpr;
+                               return TRUE;
+                       }
+               }
+               return FALSE; /* subexpression not recognized */
+          case ME_LOOPCONST:
+               *lbegin = l;  /* expression is a loop constant */
+               *iv_seen = FALSE;
+               return TRUE;
+          case ME_IV:
+               if (iv_allowed) {
+                       *iv_seen = TRUE;
+                       *lbegin = l;
+                       return TRUE;
+               }
+               /* fall through ... */
+          default:
+               return FALSE;
+       }
+}
+
+
+bool is_ivexpr(l,ivs,vars,lbegin_out,iv_out,sign_out)
+       line_p l, *lbegin_out;
+       lset   ivs,vars;
+       iv_p   *iv_out;
+       int    *sign_out;
+{
+       line_p l2;
+       bool iv_seen;
+
+
+       loopvars = vars;
+       ivars = ivs;
+       if (match_expr(l,TRUE,&l2,&iv_seen,1)) {
+               if (iv_seen) {
+                       /* recognized a correct expression */
+                       *lbegin_out = l2;
+                       *iv_out     = last_iv;
+                       *sign_out   = iv_sign;
+                       return TRUE;
+               }
+       }
+       return FALSE;
+}
diff --git a/util/ego/sr/sr_expr.h b/util/ego/sr/sr_expr.h
new file mode 100644 (file)
index 0000000..dae187c
--- /dev/null
@@ -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 (file)
index 0000000..f228c2b
--- /dev/null
@@ -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 (file)
index 0000000..a157e25
--- /dev/null
@@ -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 (file)
index 0000000..42ca2ee
--- /dev/null
@@ -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 (file)
index 0000000..794f68f
--- /dev/null
@@ -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 (file)
index 0000000..e36f95e
--- /dev/null
@@ -0,0 +1,178 @@
+/* S T R E N G T H   R E D U C T I O N
+ *
+ * S R _ X F O R M . C
+ *
+ */
+
+
+
+#include <stdio.h>
+#include "../share/types.h"
+#include "sr.h"
+#include "../share/debug.h"
+#include "../share/global.h"
+#include "../share/alloc.h"
+#include "../share/def.h"
+#include "../share/get.h"
+#include "sr_aux.h"
+#include "../share/lset.h"
+#include "../share/aux.h"
+#include "../../../h/em_mnem.h"
+#include "../../../h/em_pseu.h"
+#include "../../../h/em_spec.h"
+#include "sr_xform.h"
+
+/* Transformations on EM texts */
+
+line_p move_pointer(tmp,dir)
+       offset tmp;
+       int    dir;
+{
+       /* Generate EM code to load/store a pointer variable
+        * onto/from the stack, depending on dir(ection).
+        * We accept all kinds of pointer sizes.
+        */
+
+       line_p l;
+
+       l = int_line(tmp);
+       if (ps == ws) {
+               /* pointer fits in a word */
+               l->l_instr = (dir == LOAD ? op_lol : op_stl);
+       } else {
+               if (ps == 2 * ws) {
+                       /* pointer fits in a double word */
+                       l->l_instr = (dir == LOAD ? op_ldl : op_sdl);
+               } else {
+                       /* very large pointer size, generate code:
+                        * LAL tmp ; LOI/STI ps */
+                       l->l_instr = op_lal;
+                       l->l_next = newline(OPSHORT);
+                       SHORT(l->l_next) = ps;
+                       l->l_next->l_instr =
+                          (dir == LOAD ? op_loi : op_sti);
+                       PREV(l->l_next) = l;
+               }
+       }
+       return l;
+}
+
+
+
+/* make_header */
+
+STATIC copy_loops(b1,b2,except)
+       bblock_p b1,b2;
+       loop_p except;
+{
+       /* Copy the loopset of b2 to b1, except for 'except' */
+
+       Lindex i;
+       loop_p lp;
+       for (i = Lfirst(b2->b_loops); i != (Lindex) 0;
+            i = Lnext(i,b2->b_loops)) {
+               lp = (loop_p) Lelem(i);
+               if (lp != except) {
+                       Ladd(lp,&b1->b_loops);
+               }
+       }
+}
+
+
+STATIC lab_id label(b)
+       bblock_p b;
+{
+       /* Find the label at the head of block b. If there is
+        * no such label yet, create one.
+        */
+
+       line_p l;
+
+       assert (b->b_start != (line_p) 0);
+       if (INSTR(b->b_start) == op_lab) return INSTRLAB(b->b_start);
+       /* The block has no label yet. */
+       l = newline(OPINSTRLAB);
+       INSTRLAB(l) = freshlabel();
+       DLINK(l,b->b_start); /* doubly link them */
+       return INSTRLAB(l);
+}
+
+
+STATIC adjust_jump(newtarg,oldtarg,c)
+       bblock_p newtarg,oldtarg,c;
+{
+       /* If the last instruction of c is a jump to the
+        * old target, then change it into a jump to the
+        * start of the new target.
+        */
+
+       line_p l;
+
+       if (INSTR(oldtarg->b_start) == op_lab) {
+               /* If old target has no label, it cannot be jumped to */
+               l = last_instr(c);
+               assert(l != (line_p) 0);
+               if (TYPE(l) == OPINSTRLAB &&
+                   INSTRLAB(l) == INSTRLAB(oldtarg->b_start)) {
+                       INSTRLAB(l) = label(newtarg);
+               }
+       }
+}
+
+
+make_header(lp)
+       loop_p lp;
+{
+       /* Make sure that the loop has a header block, i.e. a block
+        * has the loop entry block as its only successor and
+        * that dominates the loop entry block.
+        * If there is no header yet, create one.
+        */
+
+       bblock_p b,c,entry;
+       Lindex i,next;
+
+       if (lp->LP_HEADER != (bblock_p) 0) return;
+       OUTTRACE("creating a new header block",0);
+       /* The loop has no header yet. The main problem is to
+        * keep all relations (SUCC, PRED, NEXT, IDOM, LOOPS)
+        * up to date.
+        */
+       b = freshblock(); /* new block with new b_id */
+       entry = lp->lp_entry;
+
+       /* update succ/pred. Also take care that any jump from outside
+        * the loop to the entry block now goes to b.
+        */
+
+       for (i = Lfirst(entry->b_pred); i != (Lindex) 0; i = next ) {
+               next = Lnext(i,entry->b_pred);
+               c = (bblock_p) Lelem(i);
+               /* c is a predecessor of the entry block */
+               if (!Lis_elem(c,lp->LP_BLOCKS)) {
+                       /* c is outside the loop */
+                       Lremove(c,&entry->b_pred);
+                       Lremove(entry,&c->b_succ);
+                       Ladd(b,&c->b_succ);
+                       adjust_jump(b,entry,c);
+               }
+       }
+       Ladd(b,&entry->b_pred);
+       b->b_succ = Lempty_set();
+       b->b_pred = Lempty_set();
+       Ladd(entry,&b->b_succ);
+       if (curproc->p_start == entry) {
+               /* entry was the first block of curproc */
+               curproc->p_start = b;
+       } else {
+               /* find block before entry block */
+               for (c = curproc->p_start; c->b_next != entry; c = c->b_next);
+               c->b_next = b;
+               Ladd(c,&b->b_pred);
+       }
+       b->b_next = entry;
+       copy_loops(b,entry,lp);
+       b->b_idom = entry->b_idom;
+       entry->b_idom = b;
+       lp->LP_HEADER = b;
+}
diff --git a/util/ego/sr/sr_xform.h b/util/ego/sr/sr_xform.h
new file mode 100644 (file)
index 0000000..8ea103b
--- /dev/null
@@ -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 (file)
index 0000000..e899198
--- /dev/null
@@ -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 (file)
index 0000000..37a39ad
--- /dev/null
@@ -0,0 +1,554 @@
+/*  U S E  -  D E F I N I T I O N   A N A L Y S I S */
+
+#include <stdio.h>
+#include "../share/types.h"
+#include "ud.h"
+#include "../share/debug.h"
+#include "../share/global.h"
+#include "../share/lset.h"
+#include "../share/cset.h"
+#include "../share/def.h"
+#include "../share/files.h"
+#include "../share/map.h"
+#include "../share/get.h"
+#include "../share/put.h"
+#include "../share/alloc.h"
+#include "../share/aux.h"
+#include "../share/init_glob.h"
+#include "../share/locals.h"
+#include "../share/go.h"
+#include "../../../h/em_pseu.h"
+#include "../../../h/em_spec.h"
+#include "ud_defs.h"
+#include "ud_const.h"
+#include "ud_copy.h"
+
+short nrglobals;
+short nrvars;
+
+int Svalue,Svariable;
+
+cond_p globl_cond_tab,local_cond_tab;
+
+STATIC cond_p getcondtab(f)
+       FILE *f;
+{
+       int l,i;
+       cond_p tab;
+
+       fscanf(f,"%d",&l);
+       tab = newcondtab(l);
+       for (i = 0; i < l; i++) {
+               fscanf(f,"%d %d %d",&tab[i].mc_cond,&tab[i].mc_tval,
+                        &tab[i].mc_sval);
+       }
+       assert(tab[l-1].mc_cond == DEFAULT);
+       return tab;
+}
+
+
+STATIC ud_machinit(f)
+       FILE *f;
+{
+       char s[100];
+
+       for (;;) {
+               while(getc(f) != '\n');
+               fscanf(f,"%s",s);
+               if (strcmp(s,"%%UD") == 0)break;
+       }
+       globl_cond_tab = getcondtab(f);
+       local_cond_tab = getcondtab(f);
+}
+
+
+
+STATIC bool test_cond(cond,val)
+       short cond;
+       offset val;
+{
+       switch(cond) {
+               case DEFAULT:
+                       return TRUE;
+               case FITBYTE:
+                       return val >= -128 && val < 128;
+       }
+       assert(FALSE);
+       /* NOTREACHED */
+}
+
+
+STATIC short map_value(tab,val,time)
+       struct cond_tab tab[];
+       offset val;
+       bool time;
+{
+       cond_p p;
+
+       for (p = &tab[0]; ; p++) {
+               if (test_cond(p->mc_cond,val)) {
+                       return (time ? p->mc_tval : p->mc_sval);
+               }
+       }
+}
+
+
+STATIC init_root(root)
+       bblock_p root;
+{
+       /* Initialise the IN OUT sets of the entry block of the
+        * current procedure. Global variables and parameters
+        * already have a value at this point, although we do
+        * not know which value. Therefor, implicit definitions
+        * to all global variables and parameters are
+        * put in IN.
+        */
+
+       short v;
+
+       for (v = 1; v <= nrglobals; v++) {
+               Cadd(IMPLICIT_DEF(GLOB_TO_VARNR(v)), &IN(root));
+       }
+       for (v = 1; v <= nrlocals; v++) {
+               if (locals[v]->lc_off >= 0) {
+                       Cadd(IMPLICIT_DEF(LOC_TO_VARNR(v)),&IN(root));
+               }
+       }
+       /* OUT(root) = IN(root) - KILL(root) + GEN(root) */
+       Ccopy_set(IN(root),&OUT(root));
+       Csubtract(KILL(root),&OUT(root));
+       Cjoin(GEN(root),&OUT(root));
+}
+
+
+
+
+STATIC unite_outs(bbset,setp)
+       lset bbset;
+       cset *setp;
+{
+       /* Take the union of OUT(b), for all b in bbset,
+        * and put the result in setp.
+        */
+
+       Lindex i;
+
+       Cclear_set(setp);
+       for (i = Lfirst(bbset); i != (Lindex) 0; i = Lnext(i,bbset)) {
+               Cjoin(OUT((bblock_p) Lelem(i)), setp);
+       }
+}
+
+
+
+STATIC solve_equations(p)
+       proc_p p;
+{
+       /* Solve the data flow equations for reaching
+        * definitions of procedure p.
+        * These equations are:
+        *  (1)  OUT(b) = IN(b) - KILL(b) + GEN(b)
+        *  (2)  IN(b)  = OUT(p1) + .. + OUT(pn) ; 
+        *       where PRED(b) = {p1, .. , pn}
+        * We use the iterative algorithm of Aho&Ullman to
+        * solve the equations.
+        */
+
+       register bblock_p b;
+       bool     change;
+       cset     newin;
+
+       /* initializations */
+       newin = Cempty_set(nrdefs);
+       for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
+               IN(b) = Cempty_set(nrdefs);
+               OUT(b) = Cempty_set(nrdefs);
+               Ccopy_set(GEN(b), &OUT(b));
+       }
+       init_root(p->p_start);
+       /* Global variables and parameters have already a value
+        * at the procedure entry block.
+        */
+       change = TRUE;
+       /* main loop */
+       while (change) {
+               change = FALSE;
+               for (b = p->p_start->b_next; b != (bblock_p) 0; b = b->b_next) {
+                       unite_outs(b->b_pred, &newin);
+                       /* newin = OUT(p1) + .. + OUT(pn) */
+                       if (!Cequal(newin,IN(b))) {
+                               change = TRUE;
+                               Ccopy_set(newin, &IN(b));
+                               Ccopy_set(IN(b),   &OUT(b));
+                               Csubtract(KILL(b), &OUT(b));
+                               Cjoin(GEN(b),      &OUT(b));
+                       }
+               }
+       }
+       for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
+               Cdeleteset(KILL(b));
+               Cdeleteset(OUT(b));
+       }
+       Cdeleteset(newin);
+}
+
+
+
+short global_addr_cost()
+{
+       return add_timespace(map_value(globl_cond_tab,(offset) 0,TRUE),
+                            map_value(globl_cond_tab,(offset) 0,FALSE));
+}
+
+short local_addr_cost(off)
+       offset off;
+{
+       return add_timespace(map_value(local_cond_tab,off,TRUE),
+                            map_value(local_cond_tab,off,FALSE));
+}
+
+
+
+STATIC bool fold_is_desirable(old,new)
+       line_p old,new;
+{
+       /* See if it is desirable to replace the variable used by the
+        * EM instruction 'old' by the variable used by 'new'.
+        * We do not replace 'cheaply addressable variables' by 'expensively
+        * addressable variables'. E.g. if we're optimizing object code size,
+        * we do not replace a local variable by a global variable on a VAX,
+        * because the former occupies 1 or 2 bytes and the latter occupies
+        * 4 bytes.
+        * If 2 local variables are equally expensive to address, we replace
+        * the first one by the second only if the first one is used at
+        * least as many times as the second one.
+        */
+
+       local_p oldloc,newloc;
+       short old_cost,new_cost,nr;
+       bool ok;
+
+       if (TYPE(old) == OPOBJECT) {
+               /* old variable is a global variable */
+               return TYPE(new) != OPOBJECT && 
+                      global_addr_cost() >=
+                      local_addr_cost(off_set(new));
+       }
+       find_local(off_set(old),&nr,&ok);
+       assert(ok);
+       oldloc = locals[nr];
+       old_cost = local_addr_cost(off_set(old));
+       if (TYPE(new) == OPOBJECT) {
+               return oldloc->lc_score == 2 || /* old var. can be eliminated */
+                      old_cost > global_addr_cost();
+       }
+       find_local(off_set(new),&nr,&ok);
+       assert(ok);
+       newloc = locals[nr];
+       new_cost = local_addr_cost(off_set(new));
+       return old_cost > new_cost ||
+              (old_cost == new_cost && oldloc->lc_score < newloc->lc_score);
+}
+
+
+
+#ifdef TRACE
+/*********** TRACING ROUTINES ***********/
+
+pr_localtab() {
+       short i;
+       local_p lc;
+
+       printf("LOCAL-TABLE (%d)\n\n",nrlocals);
+       for (i = 1; i <= nrlocals; i++) {
+               lc = locals[i];
+               printf("LOCAL %d\n",i);
+               printf("        offset= %D\n",lc->lc_off);
+               printf("        size=   %d\n",lc->lc_size);
+               printf("        flags=  %d\n",lc->lc_flags);
+       }
+}
+
+pr_globals()
+{
+       dblock_p d;
+       obj_p obj;
+
+       printf("GLOBALS (%d)\n\n",nrglobals);
+       printf("ID      GLOBNR\n");
+       for (d = fdblock; d != (dblock_p) 0; d = d->d_next) {
+               for (obj = d->d_objlist; obj != (obj_p) 0; obj = obj->o_next) {
+                       if (obj->o_globnr != 0) {
+                          printf("%d   %d\n", obj->o_id,obj->o_globnr);
+                       }
+               }
+       }
+}
+
+extern char em_mnem[];
+
+pr_defs()
+{
+       short i;
+       line_p l;
+
+       printf("DEF TABLE\n\n");
+       for (i = 1; i <= nrexpldefs; i++) {
+               l = defs[i];
+               printf("%d      %s ",EXPL_TO_DEFNR(i),
+                       &em_mnem[(INSTR(l)-sp_fmnem)*4]);
+               switch(TYPE(l)) {
+                       case OPSHORT:
+                               printf("%d\n",SHORT(l));
+                               break;
+                       case OPOFFSET:
+                               printf("%D\n",OFFSET(l));
+                               break;
+                       case OPOBJECT:
+                               printf("%d\n",OBJ(l)->o_id);
+                               break;
+                       default:
+                               assert(FALSE);
+               }
+       }
+}
+
+
+pr_set(name,k,s,n)
+       char *name;
+       cset s;
+       short k,n;
+{
+       short i;
+
+       printf("%s(%d) =        {",name,k);
+       for (i = 1; i <= n; i++) {
+               if (Cis_elem(i,s)) {
+                       printf("%d ",i);
+               }
+       }
+       printf ("}\n");
+}
+
+pr_blocks(p)
+       proc_p p;
+{
+       bblock_p b;
+       short n;
+
+       for (b = p->p_start; b != 0; b = b->b_next) {
+               printf ("\n");
+               n = b->b_id;
+               pr_set("GEN",n,GEN(b),nrdefs);
+               pr_set("KILL",n,KILL(b),nrdefs);
+               pr_set("IN ",n,IN(b),nrdefs);
+               pr_set("OUT",n,OUT(b),nrdefs);
+               pr_set("CHGVARS",n,CHGVARS(b),nrvars);
+       }
+}
+
+pr_copies()
+{
+       short i;
+
+       printf("\nCOPY TABLE\n\n");
+       for (i = 1; i <= nrdefs; i++) {
+               if (def_to_copynr[i] != 0) {
+                       printf("%d      %d\n",i,def_to_copynr[i]);
+               }
+       }
+}
+
+pr_cblocks(p)
+       proc_p p;
+{
+       bblock_p b;
+       short n;
+
+       for (b = p->p_start; b != 0; b = b->b_next) {
+               printf ("\n");
+               n = b->b_id;
+               pr_set("CGEN",n,C_GEN(b),nrcopies);
+               pr_set("CKILL",n,C_KILL(b),nrcopies);
+               pr_set("CIN ",n,C_IN(b),nrcopies);
+               pr_set("COUT",n,C_OUT(b),nrcopies);
+       }
+}
+
+/*********** END TRACING ********/
+
+#endif
+
+STATIC ud_analysis(p)
+       proc_p p;
+{
+       /* Perform use-definition analysis on procedure p */
+
+       make_localtab(p);  /* See for which local we'll keep ud-info */
+#ifdef TRACE
+       pr_localtab();
+#endif
+       nrvars = nrglobals + nrlocals;
+       make_defs(p);  /* Make a table of all useful definitions in p */
+#ifdef TRACE
+       pr_defs();
+#endif
+       nrdefs = nrexpldefs + nrvars; /* number of definitions */
+       gen_sets(p); /* compute GEN(b), for every basic block b */
+       kill_sets(p); /* compute KILL(b), for every basic block b */
+       solve_equations(p); /* solve data flow equations for p */
+#ifdef TRACE
+       pr_blocks(p);
+#endif
+}
+
+
+
+STATIC clean_maps()
+{
+       local_p *p;
+       cset *v;
+
+       oldmap(defs,nrexpldefs);
+       for (p = &locals[1]; p <= &locals[nrlocals]; p++) {
+               oldlocal(*p);
+       }
+       oldmap(locals,nrlocals);
+       for (v = &vardefs[1]; v <= &vardefs[nrvars]; v++) {
+               Cdeleteset(*v);
+       }
+       oldmap(vardefs,nrvars);
+}
+
+
+
+STATIC bool try_optim(l,b)
+       line_p l;
+       bblock_p b;
+{
+       /* Try copy propagation and constant propagation */
+
+       line_p def;
+       offset val;
+       short defnr;
+
+
+       if (is_use(l) && (def = unique_def(l,b,&defnr)) != (line_p) 0) {
+               if (is_copy(def)) {
+                       if (value_retained(def,defnr,l,b) &&
+                           fold_is_desirable(l,PREV(def))) {
+                               fold_var(l,PREV(def),b);
+                               OUTVERBOSE("vp:variable folded in proc %d",
+                                           curproc->p_id,0);
+                               Svariable++;
+                               return TRUE;
+                       }
+               } else {
+                       if (value_known(def,&val)) {
+                               fold_const(l,b,val);
+                               OUTVERBOSE("vp:value folded in proc %d",
+                                  curproc->p_id,0);
+                               Svalue++;
+                               return TRUE;
+                       }
+               }
+       }
+       return FALSE;
+}
+
+
+
+value_propagation(p)
+       proc_p p;
+{
+       /* Apply value propagation to procedure p */
+
+       bool    changes;
+       bblock_p b;
+       line_p  l, next;
+
+       changes = TRUE;
+       /* If a statement like A := B is folded to A := constant,
+        * new opportunities for constant folding may arise,
+        * e.g. the value of A might be statically known too now.
+        */
+
+        while (changes) {
+               changes = FALSE;
+               for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
+                       for (l = b->b_start; l != (line_p) 0; l = next) {
+                               next = l->l_next;
+                               if (try_optim(l,b)) {
+                                       changes = TRUE;
+                               }
+                       }
+               }
+       }
+       oldmap(copies,nrcopies);
+       oldtable(def_to_copynr,nrdefs);
+}
+
+
+STATIC ud_extend(p)
+       proc_p p;
+{
+       /* Allocate extended data structures for Use Definition analysis */
+
+       register bblock_p b;
+
+       for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
+               b->b_extend = newudbx();
+       }
+}
+
+
+STATIC ud_cleanup(p)
+       proc_p p;
+{
+       /* Deallocate extended data structures for Use Definition analysis */
+
+       register bblock_p b;
+
+       for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
+               Cdeleteset(GEN(b));
+               Cdeleteset(IN(b));
+               Cdeleteset(C_GEN(b));
+               Cdeleteset(C_KILL(b));
+               Cdeleteset(C_IN(b));
+               Cdeleteset(C_OUT(b));
+               Cdeleteset(CHGVARS(b));
+               oldudbx(b->b_extend);
+       }
+}
+
+
+ud_optimize(p)
+       proc_p p;
+{
+       ud_extend(p);
+       locals = (local_p *) 0;
+       vardefs = (cset *) 0;
+       defs = (line_p *) 0;
+       ud_analysis(p);
+       copy_analysis(p);
+#ifdef TRACE
+       pr_copies();
+       pr_cblocks(p);
+#endif
+       value_propagation(p);
+       ud_cleanup(p);
+       clean_maps();
+}
+
+main(argc,argv)
+       int argc;
+       char *argv[];
+{
+       go(argc,argv,init_globals,ud_optimize,ud_machinit,no_action);
+       report("values folded",Svalue);
+       report("variables folded",Svariable);
+       exit(0);
+}
+
+
+