--- /dev/null
+EMH=../../../h
+EML=../../../lib
+CFLAGS=
+SHARE=../share
+CA=.
+OBJECTS=ca.o ca_put.o
+SHOBJECTS=$(SHARE)/get.o $(SHARE)/alloc.o $(SHARE)/global.o $(SHARE)/aux.o $(SHARE)/debug.o $(SHARE)/lset.o $(SHARE)/cset.o $(SHARE)/files.o $(SHARE)/map.o
+SRC=ca.h ca_put.h ca.c ca_put.c
+
+.c.o:
+ cc $(CFLAGS) -c $<
+all: $(OBJECTS)
+ca: \
+ $(OBJECTS) $(SHOBJECTS)
+ cc -o ca -i $(OBJECTS) $(SHOBJECTS) $(EML)/em_data.a
+lpr:
+ pr $(SRC) | lpr
+dumpflop:
+ tar -uf /mnt/ego/ca/ca.tarf $(SRC) Makefile
+# the next lines are generated automatically
+# AUTOAUTOAUTOAUTOAUTOAUTO
+ca.o: ../share/alloc.h
+ca.o: ../share/debug.h
+ca.o: ../share/files.h
+ca.o: ../share/get.h
+ca.o: ../share/global.h
+ca.o: ../share/lset.h
+ca.o: ../share/map.h
+ca.o: ../share/types.h
+ca.o: ca.h
+ca.o: ca_put.h
+ca_put.o: ../../../h/em_flag.h
+ca_put.o: ../../../h/em_mes.h
+ca_put.o: ../../../h/em_mnem.h
+ca_put.o: ../../../h/em_pseu.h
+ca_put.o: ../../../h/em_spec.h
+ca_put.o: ../share/alloc.h
+ca_put.o: ../share/debug.h
+ca_put.o: ../share/def.h
+ca_put.o: ../share/map.h
+ca_put.o: ../share/types.h
+ca_put.o: ca.h
--- /dev/null
+/*
+ * C O M P A C T A S S E M B L Y L A N G U A G E G E N E R A T I O N
+ *
+ */
+
+
+#include <stdio.h>
+#include "../share/types.h"
+#include "ca.h"
+#include "../share/debug.h"
+#include "../share/global.h"
+#include "../share/lset.h"
+#include "../share/files.h"
+#include "../share/map.h"
+#include "../share/alloc.h"
+#include "../share/get.h"
+#include "ca_put.h"
+
+
+/* This phase transforms the Intermediate Code of the global optimizer
+ * to 'standard' compact assembly language, which will be processed
+ * by the code generator.
+ */
+
+
+short dlength;
+dblock_p *dmap;
+
+char **dnames, **pnames; /* Dynamically allocated arrays of strings.
+ * pnames[i] contains a pointer to the name
+ * of the procedure with proc_id i.
+ */
+
+
+
+STATIC int makedmap(dbl)
+ dblock_p dbl;
+{
+ /* construct the dmap table */
+
+ dblock_p d;
+ int cnt;
+
+ /* determine the length of the table */
+
+ cnt = 0;
+ for (d = dbl; d != (dblock_p) 0; d = d->d_next) cnt++;
+ dmap = (dblock_p *) newmap(cnt);
+ for (d = dbl; d != (dblock_p) 0; d = d->d_next) {
+ assert(d->d_id) <= cnt;
+ dmap[d->d_id] = d;
+ }
+ return cnt;
+}
+
+
+
+STATIC getdnames(dumpd)
+ FILE *dumpd;
+{
+ /* Read the names of the datalabels from
+ * the dump file.
+ */
+
+ char str[IDL+1];
+ char *s;
+ int id;
+ register int i;
+
+ dnames = (char **) newnametab(dlength,IDL);
+ for (;;) {
+ if (fscanf(dumpd,"%d %s",&id,str) == EOF) return;
+ assert(id <= dlength);
+ s = dnames[id];
+ for (i = 0; i < IDL; i++) {
+ *s++ = str[i];
+ }
+ }
+}
+
+STATIC getpnames(dumpp)
+ FILE *dumpp;
+{
+ /* Read the names of the procedures from
+ * the dump file.
+ */
+
+ char str[IDL+1];
+ char *s;
+ int id;
+ register int i;
+
+ pnames = (char **) newnametab(plength,IDL);
+ for (;;) {
+ if (fscanf(dumpp,"%d %s",&id,str) == EOF) return;
+ assert(id <= plength);
+ s = pnames[id];
+ for (i = 0; i < IDL; i++) {
+ *s++ = str[i];
+ }
+ }
+}
+
+
+STATIC bool name_exists(name,endp,endd)
+ char *name;
+ proc_p endp;
+ dblock_p endd;
+{
+ /* Search the proctable (from fproc to endp)
+ * and the data block table (from fdblock to endd)
+ * to see if the name is already in use.
+ */
+
+ proc_p p;
+ dblock_p d;
+
+ for (p = fproc; p != endp; p = p->p_next) {
+ if (strncmp(name,pnames[p->p_id],IDL) == 0) return TRUE;
+ }
+ for (d = fdblock; d != endd; d = d->d_next) {
+ if (strncmp(name,dnames[d->d_id],IDL) == 0) return TRUE;
+ }
+ return FALSE;
+}
+
+
+
+static int nn = 0;
+
+STATIC new_name(s)
+ char *s;
+{
+ s[0] = '_';
+ s[1] = 'I';
+ s[2] = 'I';
+ sprintf(&s[3],"%d",nn);
+ nn++;
+}
+
+
+
+STATIC uniq_names()
+{
+ /* The names of all internal procedures and data blocks
+ * are made different. As the optimizer combines several
+ * modules into one, there may be name conflicts between
+ * procedures or data blocks that were internal in
+ * different source modules.
+ */
+
+ proc_p p;
+ dblock_p d;
+
+ for (p = fproc; p != (proc_p) 0; p = p->p_next) {
+ if (!(p->p_flags1 & PF_EXTERNAL) &&
+ name_exists(pnames[p->p_id],p,fdblock)) {
+ new_name(pnames[p->p_id]);
+ }
+ }
+ for (d = fdblock; d != (dblock_p) 0; d = d->d_next) {
+ if (!(d->d_flags1 & DF_EXTERNAL) &&
+ name_exists(dnames[d->d_id],(proc_p) 0,d) ) {
+ new_name(dnames[d->d_id]);
+ }
+ }
+}
+main(argc,argv)
+ int argc;
+ char *argv[];
+{
+ /* CA does not output proctable etc. files. Instead, its
+ * pname2 and dname2 arguments contain the names of the
+ * dump files created by IC.
+ */
+ FILE *f, *f2; /* The EM input and output. */
+ FILE *df, *pf; /* The dump files */
+ line_p lnp;
+
+ fproc = getptable(pname); /* proc table */
+ fdblock = getdtable(dname); /* data block table */
+ dlength = makedmap(fdblock); /* allocate dmap table */
+ df = openfile(dname2,"r");
+ getdnames(df);
+ fclose(df);
+ pf = openfile(pname2,"r");
+ getpnames(pf);
+ fclose(pf);
+ uniq_names();
+ f = openfile(lname,"r");
+ f2 = stdout;
+ cputmagic(f2); /* write magic number */
+ while ((lnp = get_ca_lines(f,&curproc)) != (line_p) 0) {
+ cputlines(lnp,f2);
+ }
+ fclose(f);
+ fclose(f2);
+ exit(0);
+}
--- /dev/null
+/*
+ * C O M P A C T A S S E M B L Y L A N G U A G E G E N E R A T I O N
+ *
+ */
+
+
+#define PF_SYMOUT 01
+#define DF_SYMOUT 01
+
+extern dblock_p *dmap;
+
+extern char **dnames;
+extern char **pnames;
+
+extern byte em_flag[];
--- /dev/null
+#include <stdio.h>
+#include "../share/types.h"
+#include "ca.h"
+#include "../share/debug.h"
+#include "../share/def.h"
+#include "../share/map.h"
+#include "../../../h/em_spec.h"
+#include "../../../h/em_pseu.h"
+#include "../../../h/em_mnem.h"
+#include "../../../h/em_flag.h"
+#include "../../../h/em_mes.h"
+#include "../share/alloc.h"
+
+#define outbyte(b) putc(b,outfile)
+
+FILE *outfile;
+
+STATIC proc_p thispro;
+
+STATIC outinst(m) {
+
+ outbyte( (byte) m );
+}
+
+STATIC coutshort(i) short i; {
+
+ outbyte( (byte) (i&BMASK) );
+ outbyte( (byte) (i>>8) );
+}
+
+STATIC coutint(i) short i; {
+
+ if (i>= -sp_zcst0 && i< sp_ncst0-sp_zcst0)
+ outbyte( (byte) (i+sp_zcst0+sp_fcst0) );
+ else {
+ outbyte( (byte) sp_cst2) ;
+ coutshort(i);
+ }
+}
+
+STATIC coutoff(off) offset off; {
+
+ if ((short) off == off)
+ coutint((short) off);
+ else {
+ outbyte( (byte) sp_cst4) ;
+ coutshort( (short) (off&0177777L) );
+ coutshort( (short) (off>>16) );
+ }
+}
+
+
+STATIC outsym(s,t)
+ char *s;
+ int t;
+{
+ register byte *p;
+ register unsigned num;
+
+ if (s[0] == '.') {
+ num = atoi(&s[1]);
+ if (num < 256) {
+ outbyte( (byte) sp_dlb1) ;
+ outbyte( (byte) (num) );
+ } else {
+ outbyte( (byte) sp_dlb2) ;
+ coutshort((short) num);
+ }
+ } else {
+ p= s;
+ while (*p && p < &s[IDL])
+ p++;
+ num = p - s;
+ outbyte( (byte) t);
+ coutint((short) num);
+ p = s;
+ while (num--)
+ outbyte( (byte) *p++ );
+ }
+}
+
+
+STATIC outdsym(dbl)
+ dblock_p dbl;
+{
+ outsym(dnames[dbl->d_id],sp_dnam);
+}
+
+
+STATIC outpsym(p)
+ proc_p p;
+{
+ outsym(pnames[p->p_id],sp_pnam);
+}
+
+
+STATIC outddef(id) short id; {
+
+ dblock_p dbl;
+
+ dbl = dmap[id];
+ dbl->d_flags2 |= DF_SYMOUT;
+ if (dbl->d_flags1 & DF_EXTERNAL) {
+ outinst(ps_exa);
+ outdsym(dbl);
+ }
+}
+
+STATIC outpdef(p) proc_p p; {
+ p->p_flags2 |= PF_SYMOUT;
+ if (p->p_flags1 & PF_EXTERNAL) {
+ outinst(ps_exp);
+ outpsym(p);
+ }
+}
+
+
+STATIC outdocc(obj) obj_p obj; {
+ dblock_p dbl;
+
+ dbl = obj->o_dblock;
+ if ((dbl->d_flags2 & DF_SYMOUT) == 0) {
+ dbl->d_flags2 |= DF_SYMOUT;
+ if ((dbl->d_flags1 & DF_EXTERNAL) == 0) {
+ outinst(ps_ina);
+ outdsym(dbl);
+ }
+ }
+}
+
+
+STATIC outpocc(p) proc_p p; {
+ if ((p->p_flags2 & PF_SYMOUT) == 0) {
+ p->p_flags2 |= PF_SYMOUT;
+ if ((p->p_flags1 & PF_EXTERNAL) == 0) {
+ outinst(ps_inp);
+ outpsym(p);
+ }
+ }
+}
+
+
+STATIC coutobject(obj)
+ obj_p obj;
+{
+ /* In general, an object is defined by a global data
+ * label and an offset. There are two special cases:
+ * the label is omitted if the object is part of the current
+ * hol block; the offset is omitted if it is 0 and the label
+ * was not omitted.
+ */
+ if (dnames[obj->o_dblock->d_id][0] == '\0') {
+ coutoff(obj->o_off);
+ } else {
+ if (obj->o_off == 0) {
+ outdsym(obj->o_dblock);
+ } else {
+ outbyte((byte) sp_doff);
+ outdsym(obj->o_dblock);
+ coutoff(obj->o_off);
+ }
+ }
+}
+
+
+STATIC cputstr(abp) register argb_p abp; {
+ register argb_p tbp;
+ register length;
+
+ length = 0;
+ tbp = abp;
+ while (tbp!= (argb_p) 0) {
+ length += tbp->ab_index;
+ tbp = tbp->ab_next;
+ }
+ coutint(length);
+ while (abp != (argb_p) 0) {
+ for (length=0;length<abp->ab_index;length++)
+ outbyte( (byte) abp->ab_contents[length] );
+ abp = abp->ab_next;
+ }
+}
+
+
+STATIC outnum(n)
+ int n;
+{
+ if (n < 256) {
+ outbyte((byte) sp_ilb1);
+ outbyte((byte) n);
+ } else {
+ outbyte((byte) sp_ilb2);
+ coutshort((short) n);
+ }
+}
+
+
+STATIC numlab(n)
+ int n;
+{
+ if (n < sp_nilb0) {
+ outbyte((byte) (n + sp_filb0));
+ } else {
+ outnum(n);
+ }
+}
+
+
+STATIC cputargs(lnp)
+ line_p lnp;
+{
+ register arg_p ap;
+ int cnt = 0;
+ ap = ARG(lnp);
+ while (ap != (arg_p) 0) {
+ switch(ap->a_type) {
+ case ARGOFF:
+ coutoff(ap->a_a.a_offset);
+ break;
+ case ARGOBJECT:
+ coutobject(ap->a_a.a_obj);
+ break;
+ case ARGPROC:
+ outpsym(ap->a_a.a_proc);
+ break;
+ case ARGINSTRLAB:
+ outnum(ap->a_a.a_instrlab);
+ break;
+ case ARGSTRING:
+ outbyte((byte) sp_scon);
+ cputstr(&ap->a_a.a_string);
+ break;
+ case ARGICN:
+ outbyte((byte) sp_icon);
+ goto casecon;
+ case ARGUCN:
+ outbyte((byte) sp_ucon);
+ goto casecon;
+ case ARGFCN:
+ outbyte((byte) sp_fcon);
+ casecon:
+ coutint(ap->a_a.a_con.ac_length);
+ cputstr(&ap->a_a.a_con.ac_con);
+ break;
+ default:
+ assert(FALSE);
+ }
+ ap = ap->a_next;
+ /* Avoid generating extremely long CON or ROM statements */
+ if (cnt++ > 10 && ap != (arg_p) 0 &&
+ (INSTR(lnp) == ps_con || INSTR(lnp) == ps_rom)) {
+ cnt = 0;
+ outbyte((byte) sp_cend);
+ outinst(INSTR(lnp));
+ }
+ }
+}
+
+
+
+STATIC outoperand(lnp)
+ line_p lnp;
+{
+ /* Output the operand of instruction lnp */
+
+ switch(TYPE(lnp)) {
+ case OPNO:
+ if ((em_flag[INSTR(lnp)-sp_fmnem]&EM_PAR) != PAR_NO) {
+ outbyte((byte) sp_cend);
+ }
+ break;
+ case OPSHORT:
+ if (INSTR(lnp) == ps_sym) {
+ outsym(dnames[SHORT(lnp)],sp_dnam);
+ } else {
+ coutint(SHORT(lnp));
+ }
+ break;
+ case OPOFFSET:
+ coutoff(OFFSET(lnp));
+ break;
+ case OPINSTRLAB:
+ if (INSTR(lnp) == op_lab) {
+ numlab(INSTRLAB(lnp));
+ } else {
+ if (INSTR(lnp) < sp_fpseu) {
+ coutint(INSTRLAB(lnp));
+ } else {
+ numlab(INSTRLAB(lnp));
+ }
+ }
+ break;
+ case OPOBJECT:
+ coutobject(OBJ(lnp));
+ break;
+ case OPPROC:
+ outpsym(PROC(lnp));
+ break;
+ case OPLIST:
+ cputargs(lnp);
+ switch(INSTR(lnp)) {
+ case ps_con:
+ case ps_rom:
+ case ps_mes:
+ outbyte((byte) sp_cend);
+ /* list terminator */
+ break;
+ }
+ break;
+ default:
+ assert(FALSE);
+ }
+}
+
+
+STATIC outvisibility(lnp)
+ line_p lnp;
+{
+ /* In EM names of datalabels and procedures can be made
+ * externally visible, so they can be used in other files.
+ * There are special EM pseudo-instructions to state
+ * explicitly that a certain identifier is externally
+ * visible (ps_exa,ps_exp) or invisible (ps_ina,ps_inp).
+ * If there is no such pseudo for a certain identifier,
+ * the identifier is external only if its first use
+ * in the current file is an applied occurrence.
+ * Unfortunately the global optimizer may change the
+ * order of defining and applied occurrences.
+ * In the first optimizer pass (ic) we record for each identifier
+ * whether it is external or not. If necessary we generate
+ * pseudo instructions here.
+ */
+
+ arg_p ap;
+ short instr;
+
+ instr = INSTR(lnp);
+ switch(TYPE(lnp)) {
+ case OPOBJECT:
+ outdocc(OBJ(lnp));
+ /* applied occurrence of a data label */
+ break;
+ case OPSHORT:
+ if (instr == ps_sym) {
+ outddef(SHORT(lnp));
+ /* defining occ. data label */
+ }
+ break;
+ case OPPROC:
+ if (instr == ps_pro) {
+ outpdef(PROC(lnp));
+ /* defining occ. procedure */
+ } else {
+ outpocc(PROC(lnp));
+ }
+ break;
+ case OPLIST:
+ for (ap = ARG(lnp); ap != (arg_p) 0; ap = ap->a_next) {
+ switch(ap->a_type) {
+ case ARGOBJECT:
+ outdocc(ap->a_a.a_obj);
+ break;
+ case ARGPROC:
+ outpocc(ap->a_a.a_proc);
+ break;
+ }
+ }
+ break;
+ }
+}
+
+
+cputlines(l,lf)
+ line_p l;
+ FILE *lf;
+{
+ /* Output the lines in Campact assembly language
+ * format.
+ */
+
+ line_p next,lnp;
+
+ outfile = lf;
+ for (lnp = l; lnp != (line_p) 0; lnp = next) {
+ next = lnp->l_next;
+ outvisibility(lnp); /* take care of visibiltity rules */
+ if (INSTR(lnp) != ps_sym && INSTR(lnp) != op_lab) {
+ outinst(INSTR(lnp));
+ }
+ outoperand(lnp);
+ switch(INSTR(lnp)) {
+ case ps_pro:
+ thispro = PROC(lnp);
+ /* fall through ... */
+ case ps_end:
+ coutoff(thispro->p_localbytes);
+ }
+ oldline(lnp);
+ }
+ if (thispro != (proc_p) 0) {
+ oldmap(lmap,llength);
+ }
+}
+
+cputmagic(lf)
+ FILE *lf;
+{
+ /* write the magic number */
+
+ outfile = lf;
+ coutshort(sp_magic);
+}
--- /dev/null
+/* C O M P A C T A S S E M B L Y G E N E R A T I O N
+ *
+ * C A _ P U T . C
+ *
+ */
+
+
+extern cputlines();
+extern cputmagic();
--- /dev/null
+
+EMH=../../../h
+EML=../../../lib
+CFLAGS=-DVERBOSE -O
+SHARE=../share
+RA=.
+OBJECTS=ra.o ra_items.o ra_lifet.o ra_allocl.o ra_profits.o ra_interv.o ra_pack.o ra_xform.o ra_aux.o
+SHOBJECTS=$(SHARE)/aux.o $(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)/go.o
+SRC=ra.h ra_items.h ra_lifet.h ra_allocl.h ra_profits.h ra_interv.h ra_pack.h ra_xform.h ra_aux.h ra.c ra_items.c ra_lifet.c ra_allocl.c ra_profits.c ra_interv.c ra_pack.c ra_xform.c ra_aux.c
+.c.o:
+ cc $(CFLAGS) -c $<
+all: $(OBJECTS)
+itemtab.h: \
+ makeitems \
+ itemtab.src
+ makeitems $(EMH)/em_mnem.h itemtab.src > itemtab.h
+makeitems: \
+ makeitems.c
+ cc -o makeitems makeitems.c
+ra: \
+ $(OBJECTS) $(SHOBJECTS)
+ cc -o ra -i $(OBJECTS) $(SHOBJECTS) $(EML)/em_data.a
+opr:
+ pr $(SRC) | opr
+lpr:
+ pr $(SRC) | lpr
+# the next lines are generated automatically
+# AUTOAUTOAUTOAUTOAUTOAUTO
+ra.o: ../../../h/em_reg.h
+ra.o: ../share/alloc.h
+ra.o: ../share/debug.h
+ra.o: ../share/files.h
+ra.o: ../share/get.h
+ra.o: ../share/global.h
+ra.o: ../share/go.h
+ra.o: ../share/lset.h
+ra.o: ../share/map.h
+ra.o: ../share/put.h
+ra.o: ../share/types.h
+ra.o: ra.h
+ra.o: ra_allocl.h
+ra.o: ra_items.h
+ra.o: ra_pack.h
+ra.o: ra_profits.h
+ra.o: ra_xform.h
+ra_allocl.o: ../../../h/em_mnem.h
+ra_allocl.o: ../../../h/em_pseu.h
+ra_allocl.o: ../../../h/em_reg.h
+ra_allocl.o: ../../../h/em_spec.h
+ra_allocl.o: ../share/alloc.h
+ra_allocl.o: ../share/aux.h
+ra_allocl.o: ../share/cset.h
+ra_allocl.o: ../share/debug.h
+ra_allocl.o: ../share/def.h
+ra_allocl.o: ../share/global.h
+ra_allocl.o: ../share/lset.h
+ra_allocl.o: ../share/map.h
+ra_allocl.o: ../share/types.h
+ra_allocl.o: ra.h
+ra_allocl.o: ra_allocl.h
+ra_allocl.o: ra_aux.h
+ra_allocl.o: ra_interv.h
+ra_allocl.o: ra_items.h
+ra_aux.o: ../../../h/em_mnem.h
+ra_aux.o: ../../../h/em_pseu.h
+ra_aux.o: ../../../h/em_reg.h
+ra_aux.o: ../../../h/em_spec.h
+ra_aux.o: ../share/alloc.h
+ra_aux.o: ../share/debug.h
+ra_aux.o: ../share/def.h
+ra_aux.o: ../share/global.h
+ra_aux.o: ../share/lset.h
+ra_aux.o: ../share/types.h
+ra_aux.o: ra.h
+ra_aux.o: ra_aux.h
+ra_interv.o: ../share/alloc.h
+ra_interv.o: ../share/debug.h
+ra_interv.o: ../share/global.h
+ra_interv.o: ../share/lset.h
+ra_interv.o: ../share/types.h
+ra_interv.o: ../../../h/em_reg.h
+ra_interv.o: ra.h
+ra_interv.o: ra_interv.h
+ra_items.o: ../../../h/em_mnem.h
+ra_items.o: ../../../h/em_pseu.h
+ra_items.o: ../../../h/em_reg.h
+ra_items.o: ../../../h/em_spec.h
+ra_items.o: ../share/alloc.h
+ra_items.o: ../share/aux.h
+ra_items.o: ../share/debug.h
+ra_items.o: ../share/def.h
+ra_items.o: ../share/global.h
+ra_items.o: ../share/lset.h
+ra_items.o: ../share/types.h
+ra_items.o: itemtab.h
+ra_items.o: ra.h
+ra_items.o: ra_aux.h
+ra_items.o: ra_items.h
+ra_lifet.o: ../../../h/em_mnem.h
+ra_lifet.o: ../../../h/em_pseu.h
+ra_lifet.o: ../../../h/em_reg.h
+ra_lifet.o: ../../../h/em_spec.h
+ra_lifet.o: ../share/alloc.h
+ra_lifet.o: ../share/aux.h
+ra_lifet.o: ../share/debug.h
+ra_lifet.o: ../share/def.h
+ra_lifet.o: ../share/global.h
+ra_lifet.o: ../share/lset.h
+ra_lifet.o: ../share/types.h
+ra_lifet.o: ra.h
+ra_lifet.o: ra_aux.h
+ra_lifet.o: ra_items.h
+ra_lifet.o: ra_lifet.h
+ra_pack.o: ../../../h/em_reg.h
+ra_pack.o: ../share/alloc.h
+ra_pack.o: ../share/aux.h
+ra_pack.o: ../share/cset.h
+ra_pack.o: ../share/debug.h
+ra_pack.o: ../share/def.h
+ra_pack.o: ../share/global.h
+ra_pack.o: ../share/lset.h
+ra_pack.o: ../share/types.h
+ra_pack.o: ra.h
+ra_pack.o: ra_aux.h
+ra_pack.o: ra_interv.h
+ra_profits.o: ../../../h/em_reg.h
+ra_profits.o: ../share/debug.h
+ra_profits.o: ../share/global.h
+ra_profits.o: ../share/lset.h
+ra_profits.o: ../share/types.h
+ra_profits.o: ra.h
+ra_profits.o: ra_aux.h
+ra_profits.o: ra_profits.h
+ra_xform.o: ../../../h/em_mes.h
+ra_xform.o: ../../../h/em_mnem.h
+ra_xform.o: ../../../h/em_pseu.h
+ra_xform.o: ../../../h/em_reg.h
+ra_xform.o: ../../../h/em_spec.h
+ra_xform.o: ../share/alloc.h
+ra_xform.o: ../share/aux.h
+ra_xform.o: ../share/debug.h
+ra_xform.o: ../share/def.h
+ra_xform.o: ../share/global.h
+ra_xform.o: ../share/lset.h
+ra_xform.o: ../share/types.h
+ra_xform.o: ra.h
+ra_xform.o: ra_interv.h
+ra_xform.o: ra_items.h
+ra_xform.o: ra_xform.h
--- /dev/null
+#include <stdio.h>
+
+/* MAKE ITEMS TABLE
+ *
+ * This program is used by the register allocation phase of the optimizer
+ * to make the file itemtab.h. It reads two files:
+ * - the em_mnem.h file, containing the definitions of the
+ * EM mnemonics
+ * - the item-file, containing tuples:
+ * (mnemonic, item_type)
+ * The output (standard output) is a C array.
+ */
+
+
+#define TRUE 1
+#define FALSE 0
+
+convert(mnemfile,itemfile)
+ FILE *mnemfile, *itemfile;
+{
+ char mnem1[20], mnem2[20],def[20],itemtype[20];
+ int newcl,opc,index;
+
+ newcl = TRUE;
+ printf("struct item_descr itemtab[] = {\n");
+ for (;;) {
+ fscanf(mnemfile,"%s%s%d",def,mnem1,&opc);
+ /* read a line like "#define op_aar 1" */
+ if (feof(mnemfile)) break;
+ if (strcmp(def,"#define") != 0) {
+ error("bad mnemonic file, #define expected");
+ }
+ if (newcl) {
+ fscanf(itemfile,"%s%s%d",mnem2,itemtype,&index);
+ /* read a line like "op_loc CONST 4" */
+ }
+ if (feof(itemfile) || strcmp(mnem1,mnem2) != 0) {
+ /* there is no line for this mnemonic, so
+ * it has no type.
+ */
+ printf("{NO_ITEM,0},\n");
+ newcl = FALSE;
+ } else {
+ printf("{%s,%d},\n",itemtype,index);
+ newcl = TRUE;
+ }
+ }
+ printf("};\n");
+}
+
+
+
+error(s)
+ char *s;
+{
+ fprintf(stderr,"%s\n",s);
+ exit(-1);
+}
+
+
+main(argc,argv)
+ int argc;
+ char *argv[];
+{
+ FILE *f1,*f2;
+
+ if (argc != 3) {
+ error("usage: makeitems mnemfile itemfile");
+ }
+ if ((f1 = fopen(argv[1],"r")) == NULL) {
+ error("cannot open mnemonic file");
+ }
+ if ((f2 = fopen(argv[2],"r")) == NULL) {
+ error("cannot open item file");
+ }
+ convert(f1,f2);
+}
--- /dev/null
+/*
+ * R E G I S T E R A L L O C A T I O N
+ *
+ */
+
+#include <stdio.h>
+#include "../share/types.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 "../../../h/em_reg.h"
+#include "ra.h"
+#include "ra_items.h"
+#include "ra_allocl.h"
+#include "ra_profits.h"
+#include "ra_pack.h"
+#include "ra_xform.h"
+
+
+short alloc_id;
+item_p items[NRITEMTYPES];
+int nrinstrs;
+line_p *instrmap;
+
+cond_p alocaltab[NRREGTYPES][NRREGTYPES],alocaddrtab[NRREGTYPES][NRREGTYPES],
+ aconsttab,adconsttab,aglobaltab,aproctab;
+cond_p olocaltab[NRREGTYPES],olocaddrtab[NRREGTYPES],
+ oconsttab,odconsttab,oglobaltab,oproctab;
+cond_p regsav_cost;
+
+short regs_available[] = {
+ /* Actually machine dependent; this is for vax2 */
+ 3, /* reg_any i.e. data regs */
+ 0, /* reg_loop */
+ 3, /* reg_pointer i.e. address reg. */
+ 0 /* reg_float */
+} ;
+
+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;
+}
+
+get_atab(f,tab)
+ FILE *f;
+ cond_p tab[NRREGTYPES][NRREGTYPES];
+{
+ int i,cnt,totyp,regtyp;
+
+ fscanf(f,"%d",&cnt);
+ for (i = 0; i < cnt; i++) {
+ fscanf(f,"%d %d",®typ,&totyp);
+ assert(regtyp >= 0 && regtyp < NRREGTYPES);
+ assert(totyp >= 0 && totyp < NRREGTYPES);
+ tab[regtyp][totyp] = getcondtab(f);
+ }
+}
+
+
+get_otab(f,tab)
+ FILE *f;
+ cond_p tab[NRREGTYPES];
+{
+ int i,cnt,regtyp;
+
+ fscanf(f,"%d",&cnt);
+ for (i = 0; i < cnt; i++) {
+ fscanf(f,"%d",®typ);
+ assert(regtyp >= 0 && regtyp < NRREGTYPES);
+ tab[regtyp] = getcondtab(f);
+ }
+}
+
+
+
+STATIC ra_machinit(f)
+ FILE *f;
+{
+ /* Read target machine dependent information for this phase */
+ char s[100];
+
+ for (;;) {
+ while(getc(f) != '\n');
+ fscanf(f,"%s",s);
+ if (strcmp(s,"%%RA") == 0)break;
+ }
+ fscanf(f,"%d",®s_available[reg_any]);
+ fscanf(f,"%d",®s_available[reg_pointer]);
+ fscanf(f,"%d",®s_available[reg_float]);
+ get_atab(f,alocaltab);
+ get_atab(f,alocaddrtab);
+ aconsttab = getcondtab(f);
+ adconsttab = getcondtab(f);
+ aglobaltab = getcondtab(f);
+ aproctab = getcondtab(f);
+ get_otab(f,olocaltab);
+ get_otab(f,olocaddrtab);
+ oconsttab = getcondtab(f);
+ odconsttab = getcondtab(f);
+ oglobaltab = getcondtab(f);
+ oproctab = getcondtab(f);
+ regsav_cost = getcondtab(f);
+}
+
+
+STATIC bblock_p header(lp)
+ loop_p lp;
+{
+ /* Try to determine the 'header' block of loop lp.
+ * If 'e' is the entry block of loop L, then block 'b' is
+ * called the header block of L, iff:
+ * SUCC(b) = {e} & PRED(e) = {b}
+ * If lp has no header block, 0 is returned.
+ */
+
+ bblock_p x = lp->lp_entry->b_idom;
+
+ if (x != (bblock_p) 0 && Lnrelems(x->b_succ) == 1 &&
+ (bblock_p) Lelem(Lfirst(x->b_succ)) == lp->lp_entry) {
+ return x;
+ }
+ return (bblock_p) 0;
+}
+
+
+STATIC ra_extproc(p)
+ proc_p p;
+{
+ /* Allocate the extended data structures for procedure p */
+
+ register loop_p lp;
+ register Lindex pi;
+ register bblock_p b;
+
+ for (pi = Lfirst(p->p_loops); pi != (Lindex) 0;
+ pi = Lnext(pi,p->p_loops)) {
+ lp = (loop_p) Lelem(pi);
+ lp->lp_extend = newralpx();
+ lp->LP_HEADER = header(lp);
+ }
+ for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
+ b->b_extend = newrabx();
+ }
+}
+
+
+
+
+STATIC ra_cleanproc(p)
+ proc_p p;
+{
+ /* Allocate the extended data structures for procedure p */
+
+ register loop_p lp;
+ register Lindex pi;
+ register bblock_p b;
+
+ for (pi = Lfirst(p->p_loops); pi != (Lindex) 0;
+ pi = Lnext(pi,p->p_loops)) {
+ lp = (loop_p) Lelem(pi);
+ oldralpx(lp->lp_extend);
+ }
+ for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
+ oldrabx(b->b_extend);
+ }
+}
+
+
+
+STATIC loop_blocks(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 make_instrmap(p,map)
+ proc_p p;
+ line_p map[];
+{
+ /* make the instructions map of procedure p */
+
+ register bblock_p b;
+ register line_p l;
+ register int i = 0;
+
+ for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
+ b->B_BEGIN = i; /* number of first instruction */
+ for (l = b->b_start; l != (line_p) 0; l = l->l_next) {
+ map[i++] = l;
+ }
+ b->B_END = i-1; /* number of last instruction */
+ }
+}
+
+
+
+STATIC bool useful_item(item)
+ item_p item;
+{
+ /* See if it may be useful to put the item in a register.
+ * A local variable that is not a parameter may always be put
+ * in a register (as it need not be initialized).
+ * Other items must be used at least twice.
+ */
+
+ int nruses = Lnrelems(item->it_usage);
+ assert (nruses > 0); /* otherwise it would not be an item! */
+ return nruses > 1 || (item->it_type == LOCALVAR &&
+ item->i_t.it_off < 0);
+}
+
+
+STATIC item_p cat_items(items)
+ item_p items[];
+{
+ /* Make one item list out of an array of itemlists.
+ * Remove items that are used only once.
+ */
+
+ register item_p it;
+ item_p *ip,head,next;
+ int t;
+
+
+ ip = &head;
+ for (t = 0; t < NRITEMTYPES;t++) {
+ for ( it = items[t]; it != (item_p) 0; it = next) {
+ next = it->it_next;
+ if (!it->it_desirable || !useful_item(it)) {
+ clean_timeset(it->it_usage);
+ olditem(it);
+ } else {
+ *ip = it;
+ ip = &it->it_next;
+ }
+ }
+ }
+ *ip = (item_p) 0;
+ return head;
+}
+
+
+
+
+STATIC clean_interval(list)
+ interv_p list;
+{
+ register interv_p x,next;
+
+ for (x = list; x != (interv_p) 0; x = next) {
+ next = x->i_next;
+ oldinterval(x);
+ }
+}
+
+
+
+STATIC clean_timeset(s)
+ lset s;
+{
+ register Lindex i;
+ register time_p t;
+
+ for (i = Lfirst(s); i != (Lindex) 0; i = Lnext(i,s)) {
+ t = (time_p) Lelem(i);
+ oldtime(t);
+ }
+ Ldeleteset(s);
+}
+
+
+
+STATIC clean_allocs(list)
+ alloc_p list;
+{
+ register alloc_p x,next;
+
+ for (x = list; x != (alloc_p) 0; x = next) {
+ next = x->al_next;
+ clean_interval(x->al_timespan);
+ Cdeleteset(x->al_rivals);
+ Ldeleteset(x->al_inits);
+ clean_interval(x->al_busy);
+ clean_allocs(x->al_mates);
+ oldalloc(x);
+ }
+}
+
+
+
+STATIC clean_items(list)
+ item_p list;
+{
+ register item_p x,next;
+
+ for (x = list; x != (item_p) 0; x = next ) {
+ next = x->it_next;
+ clean_timeset(x->it_usage);
+ olditem(x);
+ }
+}
+
+
+ra_initialize()
+{
+ init_replacements(ps,ws);
+}
+
+
+ra_optimize(p)
+ proc_p p;
+{
+ item_p itemlist;
+ alloc_p alloclist,packed,unpacked;
+ offset locls;
+ bool time_opt = (time_space_ratio == 100);
+
+ ra_extproc(p);
+ loop_blocks(p);
+ alloc_id =0;
+ locls = p->p_localbytes;
+ build_itemlist(p,items,&nrinstrs);
+ instrmap = (line_p *) newmap(nrinstrs-1); /* map starts counting at 0 */
+ make_instrmap(p,instrmap);
+ build_lifetimes(items);
+ /* print_items(items,p); */
+ /* statistics(items); */
+ itemlist = cat_items(items); /* make one list */
+ alloclist = build_alloc_list(p,Lnrelems(p->p_loops),
+ itemlist);
+ build_rivals_graph(alloclist);
+ compute_profits(alloclist,time_opt);
+ /* print_allocs(alloclist); */
+ pack(alloclist,time_opt,&packed,&unpacked,p);
+ stat_regusage(packed);
+ xform_proc(p,packed,nrinstrs,instrmap);
+ /* print_allocs(packed); */
+ p->p_localbytes = locls;
+ /* don't really allocate dummy local variables! */
+ rem_locals(p,packed);
+ rem_formals(p,packed);
+ /* remove storage for real locals that
+ *are always put in register .
+ */
+ clean_allocs(unpacked);
+ clean_allocs(packed);
+ clean_items(itemlist);
+ oldmap(instrmap,nrinstrs-1);
+ ra_cleanproc(p);
+}
+
+
+
+main(argc,argv)
+ int argc;
+ char *argv[];
+{
+ go(argc,argv,ra_initialize,ra_optimize,ra_machinit,no_action);
+ exit(0);
+}
+
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+/* debugging stuff */
+
+
+
+char *str_types[] = {
+ "local variable",
+ "addr. of local",
+ "addr. of external",
+ "addr. of procedure",
+ "constant",
+ "double constant"
+};
+
+char *str_regtypes[] = {
+ "any",
+ "loop",
+ "pointer",
+ "float"
+};
+
+
+print_items(items,p)
+ item_p items[];
+ proc_p p;
+{
+ int t;
+ item_p item;
+ interv_p iv;
+
+ printf("BEGIN PROCEDURE %d\n",p->p_id);
+ for (t = 0; t < NRITEMTYPES;t++) {
+ for (item = items[t]; item != (item_p) 0;item = item->it_next) {
+ printf("\nitemtype = %s\n",str_types[t]);
+ if (t == GLOBL_ADDR) {
+ printf("id of external = %d\n",
+ item->i_t.it_obj->o_id);
+ } else {
+ printf("offset = %D\n",
+ item->i_t.it_off);
+ }
+ printf("regtype = %s\n",str_regtypes[item->it_regtype]);
+ printf("size = %d\n",item->it_size);
+ printf("#usages = %d\n", Lnrelems(item->it_usage));
+ printf("lifetime = {");
+ for (iv = item->it_lives; iv != (interv_p) 0;
+ iv = iv->i_next) {
+ printf("(%d,%d) ",iv->i_start,iv->i_stop);
+ }
+ printf("} \n");
+ }
+ }
+ printf("END PROCEDURE %d\n\n",p->p_id);
+}
+
+
+print_allocs(list)
+ alloc_p list;
+{
+ alloc_p al,m;
+ item_p item;
+ short t;
+ interv_p iv;
+
+ printf("BEGIN ALLOCLIST of proc %d\n",curproc->p_id);
+ for (m = list ; m != (alloc_p) 0; m = m->al_next) {
+ for (al = m; al != (alloc_p) 0; al = al->al_mates) {
+ item = al->al_item;
+ t = item->it_type;
+ printf("\nitem: [type = %s, ",str_types[t]);
+ switch(t) {
+ case GLOBL_ADDR:
+ printf("id = %d]\n", item->i_t.it_obj->o_id);
+ break;
+ case PROC_ADDR:
+ printf("id = %d]\n", item->i_t.it_proc->p_id);
+ break;
+ default:
+ printf("offset = %D]\n", item->i_t.it_off);
+ }
+ printf("#usages(static) = %d\n",al->al_susecount);
+ printf("#usages(dyn) = %d\n",al->al_dusecount);
+ printf("#inits = %d\n",Lnrelems(al->al_inits));
+ printf("timespan = {");
+ for (iv = al->al_timespan; iv != (interv_p) 0;
+ iv = iv->i_next) {
+ printf("(%d,%d) ",iv->i_start,iv->i_stop);
+ }
+ printf("} \n");
+ printf("busy = {");
+ for (iv = al->al_busy; iv != (interv_p) 0;
+ iv = iv->i_next) {
+ printf("(%d,%d) ",iv->i_start,iv->i_stop);
+ }
+ printf("} \n");
+ printf("profits = %d\n",al->al_profits);
+ printf("dummy local = %D\n",al->al_dummy);
+ printf("regnr = %d\n",al->al_regnr);
+ }
+ }
+}
+
+
+short regs_needed[4];
+stat_regusage(list)
+ alloc_p list;
+{
+ int i;
+ alloc_p x;
+
+ for (i = 0; i < 4; i++) {
+ regs_needed[i] = 0;
+ }
+ for (x = list; x != (alloc_p) 0; x = x->al_next) {
+ regs_needed[x->al_regtype]++;
+ }
+ /* printf("data regs:%d\n",regs_needed[reg_any]); */
+ /* printf("address regs:%d\n",regs_needed[reg_pointer]); */
+}
+
+
+
+int cnt_regtypes[reg_float+1];
+
+statistics(items)
+ item_p items[];
+{
+ register item_p item,next;
+ int t,r;
+ int cnt;
+
+ printf("\nSTATISTICS\n");
+ for (r = 0; r <= reg_float; r++) cnt_regtypes[r] = 0;
+ for (t = 0; t < NRITEMTYPES;t++) {
+ cnt = 0;
+ for (item = items[t]; item != (item_p) 0;item = next) {
+ if (useful_item(item)) {
+ cnt++;
+ cnt_regtypes[item->it_regtype]++;
+ }
+ next = item->it_next;
+ }
+ printf("#%s = %d\n",str_types[t],cnt);
+ }
+ for (r = 0; r <= reg_float; r++) {
+ printf("#%s = %d\n",str_regtypes[r],cnt_regtypes[r]);
+ }
+}
--- /dev/null
+/*
+ * R E G I S T E R A L L O C A T I O N
+ *
+ */
+
+/* TEMPORARY: should be put in ../../../h/em_mes.h: */
+#define ms_liv 9
+#define ms_ded 10
+
+#define INFINITE 10000
+#define NRREGTYPES (reg_float+1)
+
+int nrinstrs; /* number of instructions of current procedure */
+line_p *instrmap; /* Dynamic array: instrmap[i] points to i'th instruction */
+
+extern cond_p alocaltab[NRREGTYPES][NRREGTYPES],
+ alocaddrtab[NRREGTYPES][NRREGTYPES], aconsttab,
+ adconsttab,aglobaltab,aproctab;
+extern cond_p olocaltab[NRREGTYPES],olocaddrtab[NRREGTYPES],
+ oconsttab,odconsttab,oglobaltab,oproctab;
+extern cond_p regsav_cost;
+
+/* Register Allocation */
+typedef struct item *item_p;
+typedef struct allocation *alloc_p;
+typedef struct interval *interv_p;
+typedef struct time *time_p;
+
+
+
+
+extern short regs_available[]; /* contains #registers of every type */
+
+
+/* A thing that can be put in a register is called an "item". The are several
+ * types of items: a local variable, the address of a local variable,
+ * the address of a global variable, the address of a procedure,
+ * a word-size constant and a doubleword- size constant.
+ */
+
+#define LOCALVAR 0
+#define LOCAL_ADDR 1
+#define GLOBL_ADDR 2
+#define PROC_ADDR 3
+#define CONST 4
+#define DCONST 5
+
+#define NO_ITEM 6
+#define NRITEMTYPES 6
+
+struct item {
+ item_p it_next; /* link to next item is list */
+ short it_type; /* its type; see above */
+ short it_regtype; /* preferred type of register */
+ short it_size; /* its size (in bytes) */
+ short it_lastlive; /* temporary, used to build livetime */
+ lset it_usage; /* all points in text where item is used*/
+ interv_p it_lives; /* intervals during which item is live */
+ bool it_desirable; /* should this item be put in reg.? */
+ union {
+ obj_p it_obj; /* for GLOBL_ADDR */
+ proc_p it_proc; /* for PROC_ADDR */
+ offset it_off; /* for others */
+ } i_t;
+};
+
+
+/* A 'point in time' is defined by a (line,basic block) pair */
+
+struct time {
+ line_p t_line; /* point in EM text */
+ bblock_p t_bblock; /* its basic block */
+};
+
+
+struct interval {
+ short i_start; /* number of first instruction */
+ short i_stop; /* number of last instruction */
+ interv_p i_next;
+};
+
+
+/* An item may be put in a register for the duration of a whole procedure
+ * or part of a procedure (e.g. a loop). So a possible "allocation" looks
+ * like: put item X in a register during the timespan T (which is a subset
+ * of the timespan of the entire procedure). The packing process deals
+ * with allocations, rather than items. One item may be part of several
+ * possible allocations.
+ */
+
+struct allocation {
+ item_p al_item; /* the item to be put in a register */
+ short al_id; /* unique identifying number */
+ short al_regtype; /* the register type to be used */
+ interv_p al_timespan; /* timespan during which item is in reg. */
+ short al_profits; /* gains of putting item in register */
+ cset al_rivals; /* set of allocations competing with it */
+ short al_susecount; /* #usages during timespan (statically) */
+ short al_dusecount; /* #usages (dynamically, estimate) */
+ lset al_inits; /* points where reg. must be initialized */
+ interv_p al_busy; /* used to compute rivals */
+ short al_regnr; /* register nr.,if it is granted a reg. */
+ offset al_dummy; /* dummy local variable,if granted a reg */
+ alloc_p al_mates; /* link to allocations packed in same reg */
+ alloc_p al_wholeproc; /* alloc. for whole proc as timespan */
+ short al_cntrivals; /* # unpacked rivals ; used for cost estim. */
+ bool al_isloop; /* true if timespan consists of loop */
+ bool al_iswholeproc;/*true if timespan consists of whole proc*/
+ alloc_p al_next; /* link to next one in a list */
+};
+
+extern short alloc_id; /* last al_id used for current procedure */
+
+#define LP_BLOCKS lp_extend->lpx_ra.lpx_blocks
+#define LP_HEADER lp_extend->lpx_ra.lpx_header
+#define B_BEGIN b_extend->bx_ra.bx_begin
+#define B_END b_extend->bx_ra.bx_end
+#define B_DIST b_extend->bx_ra.bx_dist
+#define B_USECNT b_extend->bx_ra.bx_usecnt
+#define B_MARK b_extend->bx_ra.bx_mark
+
+#define DLINK(l1,l2) l1->l_next=l2; l2->l_prev=l1
+
+struct item_descr {
+ int id_type;
+ int id_replindex;
+} ;
+
+extern struct item_descr itemtab[];
+
+#define newalloc() (alloc_p) newstruct(allocation)
+#define oldalloc(a) oldstruct(allocation,a)
+#define newitem() (item_p) newstruct(item)
+#define olditem(i) oldstruct(item,i)
+#define newtime() (time_p) newstruct(time)
+#define oldtime(t) oldstruct(time,t)
+#define newinterval() (interv_p) newstruct(interval)
+#define oldinterval(i) oldstruct(interval,i)
--- /dev/null
+/* R E G I S T E R A L L O C A T I O N
+ *
+ * R A _ A L L O C L I S T . C
+ */
+
+#include "../share/types.h"
+#include "../share/debug.h"
+#include "../share/def.h"
+#include "../share/global.h"
+#include "../share/lset.h"
+#include "../share/cset.h"
+#include "../share/aux.h"
+#include "../share/alloc.h"
+#include "../share/map.h"
+#include "../../../h/em_mnem.h"
+#include "../../../h/em_spec.h"
+#include "../../../h/em_pseu.h"
+#include "../../../h/em_reg.h"
+#include "ra.h"
+#include "ra_aux.h"
+#include "ra_items.h"
+#include "ra_allocl.h"
+#include "ra_interv.h"
+
+STATIC count_usage(p,item,nrloops,sloopcnt,dloopcnt)
+ proc_p p;
+ item_p item;
+ short nrloops, sloopcnt[], dloopcnt[];
+{
+ /* Determine how many times the item is used in every loop.
+ * We maintain a 'static' count and a 'dynamic' count. The dynamic
+ * count estimates the number of times the item is used during
+ * execution, i.e. it gives a higher mark to items used inside
+ * a loop.
+ */
+
+ lset loops;
+ loop_p l;
+ int i;
+ short lev;
+ Lindex ui,li;
+ time_p u;
+
+ for (i = 0; i <= nrloops; i++) {
+ sloopcnt[i] = 0;
+ dloopcnt[i] = 0;
+ }
+ for (ui = Lfirst(item->it_usage); ui != (Lindex) 0;
+ ui = Lnext(ui,item->it_usage)) {
+ u = (time_p) Lelem(ui);
+ loops = u->t_bblock->b_loops;
+ lev = Lnrelems(loops);
+ /* set of loops in which this usage of item occurs */
+ for (li = Lfirst(loops); li != (Lindex) 0; li=Lnext(li,loops)) {
+ l = (loop_p) Lelem(li);
+ sloopcnt[l->lp_id]++;
+ dloopcnt[l->lp_id] +=
+ (IS_FIRM(u->t_bblock) ? loop_scale(lev) : 1);
+ }
+ }
+}
+
+
+
+STATIC alloc_p cons_alloc(item,timespan,stat_usecount,
+ dyn_usecount,inits,wholeproc,isloop,iswholeproc)
+ item_p item;
+ interv_p timespan;
+ short stat_usecount,dyn_usecount;
+ lset inits;
+ alloc_p wholeproc;
+ bool isloop,iswholeproc;
+{
+ alloc_p x;
+
+ x = newalloc();
+ x->al_id = ++alloc_id;
+ x->al_item = item;
+ x->al_timespan = timespan;
+ x->al_susecount = stat_usecount;
+ x->al_dusecount = dyn_usecount;
+ x->al_inits = inits;
+ x->al_wholeproc = wholeproc;
+ x->al_isloop = isloop;
+ x->al_iswholeproc = iswholeproc;
+ return x;
+}
+
+
+STATIC insert_alloc(alloc,list_p)
+ alloc_p alloc, *list_p;
+{
+ alloc->al_next = *list_p;
+ *list_p = alloc;
+}
+
+
+
+#define MUST_INIT(i,b) (i->it_type!=LOCALVAR ||contains(b->B_BEGIN,i->it_lives))
+#define MUST_UPDATE(i,b) (i->it_type==LOCALVAR &&contains(b->B_BEGIN,i->it_lives))
+
+STATIC lset loop_inits(lp,item,header)
+ loop_p lp;
+ item_p item;
+ bblock_p header;
+{
+ /* Build the set of entry points to loop lp where item
+ * must be initialized
+ */
+
+ lset s = Lempty_set();
+ if (header != (bblock_p) 0 && MUST_INIT(item,header)) {
+ Ladd(header,&s);
+ }
+ return s;
+}
+
+
+
+#define IN_LOOP(b) (Lnrelems(b->b_loops) > 0)
+
+STATIC bblock_p init_point(item)
+ item_p item;
+{
+ /* Find the most appropriate point to initialize any register
+ * containing the item. We want to do the initialization as
+ * late as possible, to allow other items to be put in the
+ * same register, before this initialization. Yet, as we want
+ * to do the initialization only once, it must be done in a
+ * basic block that is a dominator of all points where the
+ * item is used (ultimately in the first block of the procedure).
+ * This basic block should not be part of loop.
+ */
+
+ bblock_p b,dom = 0;
+ Lindex ti;
+ time_p t;
+
+ for (ti = Lfirst(item->it_usage); ti != (Lindex) 0;
+ ti = Lnext(ti,item->it_usage)) {
+ t = (time_p) Lelem(ti);
+ b = t->t_bblock;
+ dom = (dom == (bblock_p) 0 ? b : common_dom(dom,b));
+ }
+ while (IN_LOOP(dom)) {
+ /* Find a dominator of dom (possibly
+ * dom itself) that is outside any loop.
+ */
+ dom = dom->b_idom;
+ }
+ return dom;
+}
+
+
+STATIC add_blocks(b,s,span)
+ bblock_p b;
+ cset *s;
+ interv_p *span;
+{
+ Lindex pi;
+
+ if (!Cis_elem(b->b_id,*s)) {
+ Cadd(b->b_id,s);
+ add_interval(b->B_BEGIN,b->B_END,span);
+ for (pi = Lfirst(b->b_pred); pi != (Lindex) 0;
+ pi = Lnext(pi,b->b_pred)) {
+ add_blocks((bblock_p) Lelem(pi),s,span);
+ }
+ }
+}
+
+
+
+STATIC whole_lifetime(item,ini_out,span_out)
+ item_p item;
+ bblock_p *ini_out;
+ interv_p *span_out;
+{
+ /* Find the initialization point and the time_span of the item, if
+ * we put the item in a register during all its uses.
+ */
+
+ bblock_p b, ini = init_point(item);
+ cset s = Cempty_set(blength);
+ Lindex ti;
+ time_p t;
+ interv_p span = (interv_p) 0;
+
+ for (ti = Lfirst(item->it_usage); ti != (Lindex) 0;
+ ti = Lnext(ti,item->it_usage)) {
+ t = (time_p) Lelem(ti);
+ b = t->t_bblock;
+ add_blocks(b,&s,&span);
+ }
+ if (!Cis_elem(ini->b_id,s)) {
+ add_interval(ini->B_BEGIN,ini->B_END,&span);
+ }
+ Cdeleteset(s);
+ *ini_out = ini;
+ *span_out = span;
+}
+
+
+
+
+STATIC lset proc_inits(p,item,ini)
+ proc_p p;
+ item_p item;
+ bblock_p ini;
+{
+ lset s = Lempty_set();
+
+ if (item->it_type != LOCALVAR || item->i_t.it_off >= 0) {
+ /* only local variables need not be initialized */
+ Ladd(ini, &s);
+ }
+ return s;
+}
+
+
+STATIC bool updates_needed(lp,item)
+ loop_p lp;
+ item_p item;
+{
+ /* See if the value of item is live after the loop has
+ * been exited, i.e. must the item be updated after the loop?
+ */
+
+ Lindex bi,si;
+ bblock_p b,s;
+
+ for (bi = Lfirst(lp->LP_BLOCKS); bi != (Lindex) 0;
+ bi = Lnext(bi,lp->LP_BLOCKS)) {
+ b = (bblock_p) Lelem(bi);
+ for (si = Lfirst(b->b_succ); si != (Lindex) 0;
+ si = Lnext(si,b->b_succ)) {
+ s = (bblock_p) Lelem(si);
+ if (!Lis_elem(s,lp->LP_BLOCKS) && MUST_UPDATE(item,s)) {
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+
+
+STATIC short countuses(usage,b)
+ lset usage;
+ bblock_p b;
+{
+ short cnt = 0;
+ Lindex ti;
+ time_p t;
+
+ for (ti = Lfirst(usage); ti != (Lindex) 0; ti = Lnext(ti,usage)) {
+ t = (time_p) Lelem(ti);
+ if (t->t_bblock == b) cnt++;
+ }
+ return cnt;
+}
+
+
+
+STATIC allocs_of_item(p,item,loops,sloopcnt,dloopcnt,alloc_list_p)
+ proc_p p;
+ item_p item;
+ lset loops;
+ short *sloopcnt,*dloopcnt; /* dynamic arrays */
+ alloc_p *alloc_list_p;
+{
+ register Lindex li;
+ loop_p lp;
+ bblock_p header,ini;
+ short susecount,dusecount;
+ interv_p lt;
+ alloc_p wholeproc;
+
+ /* The whole procedure may be used as timespan.
+ The dynamic usecount of a procedure is taken to be the same
+ as its static usecount; this number is not very important, as
+ time-optimziation chooses loops first.
+ */
+ whole_lifetime(item,&ini,<);
+ wholeproc = cons_alloc(item,lt,Lnrelems(item->it_usage),
+ Lnrelems(item->it_usage), proc_inits(p,item,ini),
+ (alloc_p) 0,FALSE,TRUE);
+ insert_alloc(wholeproc, alloc_list_p);
+ for (li = Lfirst(loops); li != (Lindex) 0; li = Lnext(li,loops)) {
+ lp = (loop_p) Lelem(li);
+ if (sloopcnt[lp->lp_id] != 0 && !updates_needed(lp,item)) {
+ /* Item is used within loop, so consider loop
+ * as a timespan during which item may be put in
+ * a register.
+ */
+ if ((header = lp->LP_HEADER) == (bblock_p) 0 &&
+ MUST_INIT(item,lp->lp_entry)) continue;
+ lt = loop_lifetime(lp);
+ susecount = sloopcnt[lp->lp_id];
+ dusecount = dloopcnt[lp->lp_id];
+ if (MUST_INIT(item,lp->lp_entry)) {
+ /* include header block in timespan */
+ add_interval(header->B_BEGIN,header->B_END,<);
+ susecount += countuses(item->it_usage,header);
+ } else {
+ header = (bblock_p) 0;
+ }
+ insert_alloc(cons_alloc(item,lt,susecount,dusecount,
+ loop_inits(lp,item,header),wholeproc,
+ TRUE,FALSE),
+ alloc_list_p);
+ }
+ }
+}
+
+
+
+alloc_p build_alloc_list(p,nrloops,itemlist)
+ proc_p p;
+ short nrloops;
+ item_p itemlist;
+{
+ short *sloopcnt,*dloopcnt; /* dynamic arrays */
+ register item_p item;
+ alloc_p alloc_list = (alloc_p) 0;
+
+ sloopcnt = (short *) newtable(nrloops);
+ dloopcnt = (short *) newtable(nrloops);
+ for (item = itemlist; item != (item_p) 0; item = item->it_next) {
+ count_usage(p,item,nrloops,sloopcnt,dloopcnt);
+ allocs_of_item(p,item,p->p_loops,sloopcnt,dloopcnt,
+ &alloc_list);
+ }
+ oldtable(sloopcnt,nrloops);
+ oldtable(dloopcnt,nrloops);
+ return alloc_list;
+}
+
+
+
+build_rivals_graph(alloclist)
+ alloc_p alloclist;
+{
+ /* See which allocations in the list are rivals of each other,
+ * i.e. there is some point of time, falling in both
+ * timespans, at which the items of both allocations are live.
+ * Allocations with the same item (but different timespans) are
+ * not considered to be rivals.
+ * We use an auxiliary data structure "busy" for each allocation,
+ * indicating when the item is live during the timespan of the
+ * allocation.
+ */
+
+ register alloc_p alloc,x;
+
+ for (alloc = alloclist; alloc != (alloc_p) 0; alloc = alloc->al_next) {
+ alloc->al_rivals = Cempty_set(alloc_id);
+ }
+ for (alloc = alloclist; alloc != (alloc_p) 0; alloc = alloc->al_next) {
+ alloc->al_busy =
+ (alloc->al_item->it_type == LOCALVAR ?
+ intersect(alloc->al_timespan,alloc->al_item->it_lives) :
+ copy_timespan(alloc->al_timespan));
+ for (x = alloclist; x != alloc; x = x->al_next) {
+ if (x->al_item != alloc->al_item &&
+ not_disjoint(alloc->al_busy,x->al_busy)) {
+ Cadd(x->al_id,&alloc->al_rivals);
+ Cadd(alloc->al_id,&x->al_rivals);
+ if (alloc->al_regtype == x->al_regtype) {
+ alloc->al_cntrivals++;
+ x->al_cntrivals++;
+ }
+ }
+ }
+ }
+}
--- /dev/null
+
+/* R E G I S T E R A L L O C A T I O N
+ *
+ * R A _ A L L O C L I S T . H
+ */
+
+extern alloc_p build_alloc_list(); /* (proc_p p; short nrloops;
+ * item_p itemlist)
+ * Build a list of possible allocations
+ * for procedure p. An allocation
+ * essentially is a pair (item,timespan)
+ */
+extern build_rivals_graph(); /* (alloc_p alloclist)
+ /* See which allocations in the list are
+ * rivals of each other, i.e. there is
+ * some point of time, falling in both
+ * timespans, at which the items of
+ * both allocations are live.
+ */
--- /dev/null
+/* R E G I S T E R A L L O C A T I O N
+ *
+ * A U X I L I A R Y R O U T I N E S
+ */
+
+#include "../share/types.h"
+#include "../share/debug.h"
+#include "../share/def.h"
+#include "../share/global.h"
+#include "../share/lset.h"
+#include "../share/alloc.h"
+#include "../../../h/em_mnem.h"
+#include "../../../h/em_spec.h"
+#include "../../../h/em_pseu.h"
+#include "../../../h/em_reg.h"
+#include "ra.h"
+#include "ra_aux.h"
+
+
+time_p cons_time(l,b)
+ line_p l;
+ bblock_p b;
+{
+ /* Construct a time */
+
+ time_p t = newtime();
+
+ t->t_line = l;
+ t->t_bblock = b;
+ return t;
+}
+
+
+
+
+short loop_scale(lev)
+ short lev;
+{
+ return (lev == 0 ? 1 : (lev > 3 ? 20 : 5 * lev));
+}
--- /dev/null
+/* R E G I S T E R A L L O C A T I O N
+ *
+ * A U X I L I A R Y R O U T I N E S
+ */
+
+#define regv_size(off) regv_arg(off,2)
+ /* Fetch the size argument of the
+ * register message of the local with
+ * the given offset.
+ */
+#define regv_type(off) regv_arg(off,3)
+ /* Fetch the type argument of the
+ * register message of the local with
+ * the given offset.
+ */
+extern time_p cons_time(); /* (line_p l; bblock_p b)
+ * Construct a 'time' record with
+ * fields 'l' and 'b'.
+ */
+extern short loop_scale(); /* (short lev)
+ * Estimate how many times an item
+ * appearing in a loop of nesting
+ * level 'lev' will be used dynamically.
+ */
--- /dev/null
+/* R E G I S T E R A L L O C A T I O N
+ *
+ * R A _ I N T E R V A L . C
+ */
+
+
+#include "../share/types.h"
+#include "../share/debug.h"
+#include "../share/global.h"
+#include "../share/alloc.h"
+#include "../share/lset.h"
+#include "../../../h/em_reg.h"
+#include "ra.h"
+#include "ra_interv.h"
+
+interv_p cons_interval(t_start,t_stop)
+ short t_start,t_stop;
+{
+ interv_p x;
+
+ x = newinterval();
+ x->i_start = t_start;
+ x->i_stop = t_stop;
+ return x;
+}
+
+
+
+add_interval(t1,t2,list)
+ short t1,t2;
+ interv_p *list;
+{
+ /* Add interval (t1,t2) to the list of intervals (which is
+ * an in-out parameter!). The list is sorted in 'chronological'
+ * order. We attempt to keep the list as small as possible, by
+ * putting adjacent intervals in one interval.
+ */
+
+ register interv_p x1, x2, *q;
+ int adjacent = 0;
+ interv_p x;
+
+ q = list;
+ x1 = (interv_p) 0;
+ for (x2 = *list; x2 != (interv_p) 0; x2 = x2->i_next) {
+ if (t2 < x2->i_start) break;
+ x1 = x2;
+ q = &x2->i_next;
+ }
+ /* Now interval (t1,t2) should be inserted somewhere in between
+ * x1 and x2.
+ */
+ if (x1 != (interv_p) 0 && t1 == x1->i_stop + 1) {
+ /* join x1 and (t1,t2) */
+ x1->i_stop = t2;
+ adjacent++;
+ }
+ if (x2 != (interv_p) 0 && t2 + 1 == x2->i_start) {
+ /* join (t1,t2) and x2 */
+ x2->i_start = t1;
+ adjacent++;
+ }
+ if (adjacent == 0) {
+ /* no adjacents, allocate a new intervalfor (t1,t2) */
+ x = cons_interval(t1,t2);
+ x->i_next = x2;
+ *q = x;
+ } else {
+ if (adjacent == 2) {
+ /* x1, (t1,t2) and x2 can be put in one interval */
+ x1->i_stop = x2->i_stop;
+ x1->i_next = x2->i_next;
+ oldinterval(x2);
+ }
+ }
+}
+
+
+
+interv_p loop_lifetime(lp)
+ loop_p lp;
+{
+ /* Determine the timespan of the loop, expressed as a list
+ * of intervals.
+ */
+
+ interv_p lt = 0;
+ register bblock_p b;
+ register Lindex bi;
+
+ for (bi = Lfirst(lp->LP_BLOCKS); bi != (Lindex) 0;
+ bi = Lnext(bi,lp->LP_BLOCKS)) {
+ b = (bblock_p) Lelem(bi);
+ add_interval(b->B_BEGIN,b->B_END,<);
+ }
+ return lt;
+}
+
+
+interv_p proc_lifetime(p)
+ proc_p p;
+{
+ /* Determine the lifetime of an entire procedure */
+
+ register bblock_p b;
+
+ for (b = p->p_start; b->b_next != (bblock_p) 0; b = b->b_next) ;
+ return cons_interval(0,b->B_END);
+}
+
+
+
+STATIC set_min_max(iv1,iv2)
+ interv_p *iv1,*iv2;
+{
+ /* Auxiliary routine of intersect */
+
+ interv_p i1 = *iv1, i2 = *iv2;
+
+ if (i1->i_start < i2->i_start) {
+ *iv1 = i1;
+ *iv2 = i2;
+ } else {
+ *iv1 = i2;
+ *iv2 = i1;
+ }
+}
+
+
+
+interv_p intersect(list1,list2)
+ interv_p list1,list2;
+{
+ /* Intersect two lifetimes, each denoted by a list of intervals.
+ * We maintain two pointers, pmin and pmax, pointing to the
+ * next interval of each list. At any time, pmin points to the
+ * interval of which i_start is lowest; pmax points to the
+ * other interval (i.e. the next interval of the other list).
+ */
+
+ interv_p lt = 0;
+ interv_p pmin,pmax;
+
+#define BUMP(p) p = p->i_next
+#define EMIT(t1,t2) add_interval(t1,t2,<)
+
+ pmin = list1;
+ pmax = list2;
+ while (pmin != (interv_p) 0 && pmax != (interv_p) 0) {
+ set_min_max(&pmin,&pmax);
+ if (pmax->i_start > pmin->i_stop) {
+ /* e.g. (5,7) and (9,13) */
+ BUMP(pmin);
+ } else {
+ if (pmax->i_stop < pmin->i_stop) {
+ /* e.g. (5,12) and (7,10) */
+ EMIT(pmax->i_start,pmax->i_stop);
+ BUMP(pmax);
+ } else {
+ /* e.g. (5,8) and (7,12) */
+ EMIT(pmax->i_start,pmin->i_stop);
+ if (pmax->i_stop == pmin->i_stop) {
+ /* e.g. (5,12) and (7,12) */
+ BUMP(pmax);
+ }
+ BUMP(pmin);
+ }
+ }
+ }
+ return lt;
+}
+
+
+
+bool not_disjoint(list1,list2)
+ interv_p list1,list2;
+{
+ /* See if list1 and list2 do overlap somewhere */
+
+ interv_p pmin,pmax;
+
+ pmin = list1;
+ pmax = list2;
+ while (pmin != (interv_p) 0 && pmax != (interv_p) 0) {
+ set_min_max(&pmin,&pmax);
+ if (pmax->i_start > pmin->i_stop) {
+ /* e.g. (5,7) and (9,13) */
+ BUMP(pmin);
+ } else {
+ return TRUE; /* not disjoint */
+ }
+ }
+ return FALSE; /* disjoint */
+}
+
+
+
+bool contains(t,timespan)
+ short t;
+ interv_p timespan;
+{
+ register interv_p iv;
+
+ for (iv = timespan; iv != (interv_p) 0; iv = iv->i_next) {
+ if (t <= iv->i_stop) return (t >= iv->i_start);
+ }
+ return FALSE;
+}
+
+
+
+interv_p copy_timespan(list)
+ interv_p list;
+{
+ /* copy the time span */
+
+ interv_p x,y,head,*p;
+
+ head = (interv_p) 0;
+ p = &head;
+
+ for (x = list; x != (interv_p) 0; x = x->i_next) {
+ y = cons_interval(x->i_start,x->i_stop);
+ *p = y;
+ p = &y->i_next;
+ }
+ return head;
+}
--- /dev/null
+
+/* R E G I S T E R A L L O C A T I O N
+ *
+ * R A _ I N T E R V A L . H
+ */
+
+
+extern interv_p cons_interval();/* (short t_start,t_stop)
+ * construct an interval
+ */
+extern add_interval(); /* (short t1,t2; interv_p *list)
+ * Add interval (t1,t2) to the list of
+ * intervals (which is an in-out parameter!).
+ */
+extern interv_p loop_lifetime();/* (loop_p lp)
+ * Determine the timespan of the loop,
+ * expressed as a list of intervals.
+ */
+extern interv_p proc_lifetime();/* (proc_p p)
+ * Determine the timespan of a procedure,
+ * expressed as an interval.
+ */
+extern interv_p intersect(); /* (interv_p list1,list2)
+ * Intersect two lifetimes, each denoted
+ * by a list of intervals.
+ */
+extern bool not_disjoint(); /* (interv_p list1,list2)
+ * See if list1 and list2 do overlap somewhere.
+ */
+extern bool contains(); /* (short t;interv_p timespan)
+ * See if t is part of the timespan.
+ */
+extern interv_p copy_timespan();/* (interv_p list)
+ * Make a copy of the timespan.
+ */
--- /dev/null
+/* R E G I S T E R A L L O C A T I O N
+ *
+ * R A _ I T E M S . C
+ */
+
+#include "../share/types.h"
+#include "../share/debug.h"
+#include "../share/def.h"
+#include "../share/global.h"
+#include "../share/lset.h"
+#include "../share/aux.h"
+#include "../share/alloc.h"
+#include "../../../h/em_mnem.h"
+#include "../../../h/em_spec.h"
+#include "../../../h/em_pseu.h"
+#include "../../../h/em_reg.h"
+#include "ra.h"
+#include "ra_aux.h"
+#include "ra_items.h"
+
+
+#include "itemtab.h"
+/* Maps EM mnemonics onto item types, e.g. op_lol -> LOCALVAR, op_ldc->DCONST,
+ * generated from em_mmen.h and itemtab.src files.
+ */
+
+#define SMALL_CONSTANT(c) (c >= 0 && c <= 8)
+/* prevent small constants from being put in a register */
+
+
+clean_tab(items)
+ item_p items[];
+{
+ int t;
+
+ for (t = 0; t < NRITEMTYPES;t++) {
+ items[t] = (item_p) 0;
+ }
+}
+
+
+
+
+short item_type(l)
+ line_p l;
+{
+ int instr = INSTR(l);
+ int t;
+
+ if (instr < sp_fmnem || instr > sp_lmnem) return NO_ITEM;
+ t = itemtab[instr - sp_fmnem].id_type;
+ if (t == CONST && SMALL_CONSTANT(off_set(l))) return NO_ITEM;
+ return t;
+}
+
+
+
+bool is_item(l)
+ line_p l;
+{
+ return item_type(l) != NO_ITEM;
+}
+
+
+item_p item_of(off,items)
+ offset off;
+ item_p items[];
+{
+ register item_p x;
+
+ for (x = items[LOCALVAR]; x != (item_p) 0; x = x->it_next) {
+ if (off == x->i_t.it_off) {
+ if (!x->it_desirable) break;
+ /* don't put this item in reg */
+ return x;
+ }
+ }
+ return (item_p) 0;
+}
+
+
+
+fill_item(item,l)
+ item_p item;
+ line_p l;
+{
+ item->it_type = item_type(l);
+ switch(item->it_type) {
+ case GLOBL_ADDR:
+ item->i_t.it_obj = OBJ(l);
+ break;
+ case PROC_ADDR:
+ item->i_t.it_proc = PROC(l);
+ break;
+ default:
+ item->i_t.it_off = off_set(l);
+ }
+}
+
+
+
+STATIC bool desirable(l)
+ line_p l;
+{
+ /* See if it is really desirable to put the item of line l
+ * in a register. We do not put an item in a register if it
+ * is used as 'address of array descriptor' of an array
+ * instruction.
+ */
+
+ if (l->l_next != (line_p) 0) {
+ switch(INSTR(l->l_next)) {
+ case op_aar:
+ case op_lar:
+ case op_sar:
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+
+
+STATIC int cmp_items(a,b)
+ item_p a,b;
+{
+ /* This routine defines the <, = and > relations between items,
+ * used to sort them for fast lookup.
+ */
+
+ offset n1,n2;
+
+ switch(a->it_type) {
+ case GLOBL_ADDR:
+ assert(b->it_type == GLOBL_ADDR);
+ n1 = (offset) a->i_t.it_obj->o_id;
+ n2 = (offset) b->i_t.it_obj->o_id;
+ break;
+ case PROC_ADDR:
+ assert(b->it_type == PROC_ADDR);
+ n1 = (offset) a->i_t.it_proc->p_id;
+ n2 = (offset) b->i_t.it_proc->p_id;
+ break;
+ default:
+ n1 = a->i_t.it_off;
+ n2 = b->i_t.it_off;
+ }
+ return (n1 == n2 ? 0 : (n1 > n2 ? 1 : -1));
+}
+
+
+
+bool same_item(a,b)
+ item_p a,b;
+{
+ return cmp_items(a,b) == 0;
+}
+
+
+STATIC bool lt_item(a,b)
+ item_p a,b;
+{
+ return cmp_items(a,b) == -1;
+}
+
+
+
+/* build_itemlist()
+ *
+ * Build a list of all items used in the current procedure. An item
+ * is anything that can be put in a register (a local variable, a constant,
+ * the address of a local or global variable).
+ * For each type of item we use a sorted list containing all items of
+ * that type found so far.
+ * A local variable is only considered to be an item if there is a
+ * register message for it (indicating it is never accessed indirectly).
+ * For each item, we keep track of all places where it is used
+ * (either fetched or stored into). The usage of a local variable is also
+ * considered to be a usage of its address.
+ */
+
+
+
+STATIC item_p items[NRITEMTYPES]; /* items[i] points to the list of type i */
+
+
+
+STATIC short reg_type(item)
+ item_p item;
+{
+ /* See which type of register the item should best be assigned to */
+
+ switch(item->it_type) {
+ case LOCALVAR:
+ return regv_type(item->i_t.it_off);
+ /* use type mentioned in reg. message for local */
+ case LOCAL_ADDR:
+ case GLOBL_ADDR:
+ case PROC_ADDR:
+ return reg_pointer;
+ case CONST:
+ case DCONST:
+ return reg_any;
+ default: assert(FALSE);
+ }
+ /* NOTREACHED */
+}
+
+
+
+STATIC short item_size(item)
+ item_p item;
+{
+ /* Determine the size of the item (in bytes) */
+
+ switch(item->it_type) {
+ case LOCALVAR:
+ return regv_size(item->i_t.it_off);
+ /* use size mentioned in reg. message for local */
+ case LOCAL_ADDR:
+ case GLOBL_ADDR:
+ case PROC_ADDR:
+ return ps; /* pointer size */
+ case CONST:
+ return ws; /* word size */
+ case DCONST:
+ return 2 * ws; /* 2 * word size */
+ default: assert(FALSE);
+ }
+ /* NOTREACHED */
+}
+
+
+
+STATIC init_item(a,b)
+ item_p a,b;
+{
+ a->it_type = b->it_type;
+ switch(a->it_type) {
+ case GLOBL_ADDR:
+ a->i_t.it_obj = b->i_t.it_obj;
+ break;
+ case PROC_ADDR:
+ a->i_t.it_proc = b->i_t.it_proc;
+ break;
+ default:
+ a->i_t.it_off = b->i_t.it_off;
+ }
+ a->it_usage = Lempty_set();
+ a->it_regtype = reg_type(b);
+ a->it_size = item_size(b);
+ a->it_desirable = b->it_desirable;
+}
+
+
+
+STATIC add_item(item,t,items)
+ item_p item;
+ time_p t;
+ item_p items[];
+{
+ /* See if there was already a list element for item. In any
+ * case record the fact that item is used at 't'.
+ */
+
+ register item_p x, *q;
+
+ q = &items[item->it_type]; /* each type has its own list */
+ for (x = *q; x != (item_p) 0; x = *q) {
+ if (same_item(x,item)) {
+ /* found */
+ if (!item->it_desirable) {
+ x->it_desirable = FALSE;
+ }
+ Ladd(t,&x->it_usage);
+ return; /* done */
+ }
+ if (lt_item(item,x)) break;
+ q = &x->it_next;
+ }
+ /* not found, allocate new item; q points to it_next field of
+ * the item after which the new item should be put.
+ */
+ x = newitem();
+ x->it_next = *q;
+ *q = x;
+ init_item(x,item);
+ Ladd(t,&x->it_usage);
+}
+
+
+
+STATIC add_usage(l,b,items)
+ line_p l;
+ bblock_p b;
+ item_p items[];
+{
+ /* An item is used at line l. Add it to the list of items.
+ * A local variable is only considered to be an item, if
+ * there is a register message for it; else its address
+ * is also considered to be an item.
+ */
+
+ struct item thisitem;
+
+ fill_item(&thisitem,l); /* fill in some fields */
+ if (!desirable(l)) {
+ thisitem.it_desirable = FALSE; /* don't put item in reg. */
+ }
+ if (thisitem.it_type == LOCALVAR && !is_regvar(thisitem.i_t.it_off)) {
+ /* Use address of local instead of local itself */
+ thisitem.it_type = LOCAL_ADDR;
+ thisitem.it_regtype = reg_pointer;
+ }
+ add_item(&thisitem,cons_time(l,b),items);
+}
+
+
+
+build_itemlist(p,items,nrinstr_out)
+ proc_p p;
+ item_p items[];
+ int *nrinstr_out;
+{
+ /* Make a list of all items used in procedure p.
+ * An item is anything that can be put in a register,
+ * such as a local variable, a constant etc.
+ * As a side effect, determine the number of instructions of p.
+ */
+
+ register line_p l;
+ register bblock_p b;
+ register cnt= 0;
+
+ clean_tab(items);
+ for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
+ for (l = b->b_start; l != (line_p) 0; l = l->l_next) {
+ if (is_item(l)) {
+ add_usage(l,b,items);
+ }
+ cnt++;
+ }
+ }
+ *nrinstr_out = cnt;
+}
--- /dev/null
+/* R E G I S T E R A L L O C A T I O N
+ *
+ * R A _ I T E M S . H
+ */
+
+extern short item_type(); /* (line_p l)
+ * Determine the type of item (constant,local
+ * variable etc.) accessed by l.
+ */
+extern bool is_item(); /* (line_p l)
+ * See if l accesses an item
+ */
+extern item_p item_of(); /* (offset off;item_p items)
+ * Determine the descriptor of the item
+ * accessed by l; return 0 if not found
+ */
+extern fill_item(); /* (item_p item;line_p l)
+ * Compute the type and obj/off attributes
+ * of the item accessed by l and put them
+ * in the given item descriptor.
+ */
+extern bool same_item(); /* (item_p a,b)
+ * See if a and b are the same items.
+ */
+extern build_itemlist(); /* (proc_p p;item_p items[]; int *nrinstr_out)
+ * Determine all items accessed by procedure p
+ * and put them in the items lists. All items
+ * of type T must be put in list items[T].
+ * Also determine the number of instructions
+ * of p.
+ */
--- /dev/null
+/* R E G I S T E R A L L O C A T I O N
+ *
+ * R A _ L I F E T I M E . C
+ */
+
+#include "../share/types.h"
+#include "../share/debug.h"
+#include "../share/def.h"
+#include "../share/global.h"
+#include "../share/lset.h"
+#include "../share/aux.h"
+#include "../share/alloc.h"
+#include "../../../h/em_mnem.h"
+#include "../../../h/em_spec.h"
+#include "../../../h/em_pseu.h"
+#include "../../../h/em_reg.h"
+#include "ra.h"
+#include "ra_aux.h"
+#include "ra_items.h"
+#include "ra_lifet.h"
+
+
+#define MSG_OFF(l) aoff(ARG(l),1)
+#define is_livemsg(l) (INSTR(l) == ps_mes && aoff(ARG(l),0) == ms_liv)
+#define is_deadmsg(l) (INSTR(l) == ps_mes && aoff(ARG(l),0) == ms_ded)
+
+build_lifetimes(items)
+ item_p items[];
+{
+ /* compute the it_lives attribute of every item; this is
+ * a list of intervals during which the item is live,
+ * i.e. its current value may be used.
+ * We traverse the EM text of the current procedure in
+ * lexical order. If we encounter a live-message, we store
+ * the number ('time') of the current instruction in the
+ * it_lastlive attribute of the concerning item. If we see
+ * a dead-message for that item, we know that the item is
+ * live in between these two pseudo's. If the first message
+ * appearing in the procedure is a dead-message, the item
+ * is live from time 0 (start of procedure) till now. (Note
+ * that it_lastlive is initially 0!).
+ * The lifetime ends on the last instruction before the
+ * dead-message that is not a live -or dead message.
+ */
+
+ register line_p l;
+ register short now;
+ item_p item;
+ short last_code;
+
+ last_code = 0;
+ for (now = 0; now < nrinstrs; now++) {
+ l = instrmap[now];
+ if (is_livemsg(l)) {
+ item = item_of(MSG_OFF(l),items);
+ /* A local variable that is never used is NOT an
+ * item; yet, there may be a register message for it...
+ */
+ if(item != (item_p) 0) {
+ item->it_lastlive = now;
+ }
+ } else {
+ if (is_deadmsg(l)) {
+ item = item_of(MSG_OFF(l),items);
+ if (item != (item_p) 0) {
+ add_interval(item->it_lastlive,
+ last_code, &item->it_lives);
+ }
+ } else {
+ last_code = now;
+ }
+ }
+ }
+}
--- /dev/null
+/* R E G I S T E R A L L O C A T I O N
+ *
+ * R A _ L I F E T I M E . H
+ */
+
+
+extern build_lifetimes(); /* item_p items[];
+ * compute the it_lives attribute of every
+ * item; this is a list of intervals
+ * during which the item is live,
+ * i.e. its current value may be used.
+ */
--- /dev/null
+/* R E G I S T E R A L L O C A T I O N
+ *
+ * R A _ P A C K . C
+ */
+
+#include "../share/types.h"
+#include "../share/debug.h"
+#include "../share/def.h"
+#include "../share/global.h"
+#include "../share/lset.h"
+#include "../share/cset.h"
+#include "../share/alloc.h"
+#include "../share/aux.h"
+#include "../../../h/em_reg.h"
+#include "ra.h"
+#include "ra_aux.h"
+#include "ra_interv.h"
+
+
+short regs_occupied[NRREGTYPES]; /* #occupied registers for reg_pointer,
+ * reg_any etc.
+ */
+#define reg_available(t) (regs_available[t] > regs_occupied[t])
+
+STATIC init_regcount()
+{
+ int t;
+
+ for (t = 0; t < NRREGTYPES; t++) {
+ regs_occupied[t] = 0;
+ }
+}
+
+STATIC alloc_p make_dummy()
+{
+ alloc_p x;
+
+ x = newalloc();
+ /* x->al_profits = 0; */
+ return x;
+}
+
+
+STATIC bool fits_in(a,b,cont_item)
+ alloc_p a,b;
+ bool *cont_item;
+{
+ /* See if allocation a can be assigned the same register as b.
+ * Both allocations should be of the same register-type.
+ * Note that there may be several other allocations (mates) assigned to
+ * the same register as b. A new candidate (i.e. 'a') is only
+ * allowed to join them if it is not the rival of any resident
+ * allocation.
+ */
+
+ *cont_item = FALSE;
+ if (a->al_regtype == b->al_regtype) {
+ while (b != (alloc_p) 0) {
+ if (Cis_elem(a->al_id,b->al_rivals)) break;
+ b = b->al_mates;
+ if (a->al_item == b->al_item) {
+ *cont_item = TRUE;
+ }
+ }
+ }
+ return b == (alloc_p) 0;
+}
+
+
+STATIC alloc_p find_fitting_alloc(alloc,packed)
+ alloc_p alloc,packed;
+{
+ /* Try to find and already packed allocation that is assigned
+ * a register that may also be used for alloc.
+ * We prefer allocations that have the same item as alloc.
+ */
+
+ register alloc_p x;
+ alloc_p cand = (alloc_p) 0;
+ bool cont_item;
+
+ for (x = packed->al_next; x != (alloc_p) 0; x = x->al_next) {
+ if (fits_in(alloc,x,&cont_item)) {
+ cand = x;
+ if (cont_item) break;
+ }
+ }
+ return cand;
+}
+
+
+STATIC bool room_for(alloc,packed)
+ alloc_p alloc,packed;
+{
+ /* See if there is any register available for alloc */
+
+ return reg_available(alloc->al_regtype) ||
+ (find_fitting_alloc(alloc,packed) != (alloc_p) 0);
+}
+
+
+
+STATIC alloc_p best_alloc(unpacked,packed,time_opt)
+ alloc_p unpacked,packed;
+ bool time_opt;
+{
+ /* Find the next best candidate */
+
+ register alloc_p x,best;
+ bool loops_only;
+
+ for (loops_only = time_opt; ; loops_only = FALSE) {
+ /* If we're optimizing execution time, we first
+ * consider loops.
+ */
+ best = unpacked; /* dummy */
+ for (x = unpacked->al_next; x != (alloc_p) 0; x = x->al_next) {
+ if ((!loops_only || x->al_isloop) &&
+ x->al_profits > best->al_profits &&
+ room_for(x,packed)) {
+ best = x;
+ }
+ }
+ if (best != unpacked || !loops_only) break;
+ }
+ return (best == unpacked ? (alloc_p) 0 : best);
+}
+
+
+
+
+STATIC alloc_p choose_location(alloc,packed,p)
+ alloc_p alloc,packed;
+ proc_p p;
+{
+ /* Decide in which register to put alloc */
+
+ alloc_p fit;
+ offset dum;
+
+ fit = find_fitting_alloc(alloc,packed);
+ if (fit == (alloc_p) 0) {
+ /* Take a brand new register; allocate a dummy local for it */
+ alloc->al_regnr = regs_occupied[alloc->al_regtype]++;
+ dum = tmplocal(p,alloc->al_item->it_size);
+ alloc->al_dummy = dum;
+ } else {
+ alloc->al_regnr = fit->al_regnr;
+ alloc->al_dummy = fit->al_dummy;
+ }
+ return fit;
+}
+
+
+
+STATIC update_lists(alloc,unpacked,packed,fit)
+ alloc_p alloc,unpacked,packed,fit;
+{
+ /* 'alloc' has been granted a register; move it from the 'unpacked'
+ * list to the 'packed' list. Also remove any allocation from 'unpacked'
+ * having:
+ * 1. the same item as 'alloc' and
+ * 2. a timespan that overlaps the timespan of alloc.
+ */
+
+ register alloc_p x,q,next;
+
+ q = unpacked; /* dummy element at head of list */
+ for (x = unpacked->al_next; x != (alloc_p) 0; x = next) {
+ next = x->al_next;
+ if (x->al_item == alloc->al_item &&
+ not_disjoint(x->al_timespan, alloc->al_timespan)) {
+ /* this code kills two birds with one stone;
+ * x is either an overlapping allocation or
+ * alloc itself!
+ */
+ q->al_next = x->al_next;
+ if (x == alloc) {
+ if (fit == (alloc_p) 0) {
+ x->al_next = packed->al_next;
+ packed->al_next = x;
+ } else {
+ x->al_mates = fit->al_mates;
+ fit->al_mates = x;
+ x->al_next = (alloc_p) 0;
+ }
+ }
+ } else {
+ q = x;
+ }
+ }
+}
+
+
+
+STATIC short cum_profits(alloc)
+ alloc_p alloc;
+{
+ /* Add the profits of all allocations packed in the same
+ * register as alloc (i.e. alloc and all its 'mates').
+ */
+
+ alloc_p m;
+ short sum = 0;
+
+ for (m = alloc; m != (alloc_p) 0; m = m->al_mates) {
+ sum += m->al_profits;
+ }
+ return sum;
+}
+
+
+
+STATIC alloc_p best_cumprofits(list,x_out,prev_out)
+ alloc_p list, *x_out, *prev_out;
+{
+ /* Find the allocation with the best cummulative profits */
+
+ register alloc_p x,prev,best_prev;
+ short best = 0, cum;
+
+ prev = list;
+ for (x = list->al_next; x != (alloc_p) 0; x = x->al_next) {
+ cum = cum_profits(x);
+ if (cum > best) {
+ best = cum;
+ best_prev = prev;
+ }
+ prev = x;
+ }
+ if (best == 0) {
+ *x_out = (alloc_p) 0;
+ } else {
+ *x_out = best_prev->al_next;
+ *prev_out = best_prev;
+ }
+}
+
+
+
+STATIC account_regsave(packed,unpacked)
+ alloc_p packed,unpacked;
+{
+ /* After all packing has been done, we check for every allocated
+ * register whether it is really advantageous to use this
+ * register. It may be possible that the cost of saving
+ * and restoring the register are higher than the profits of all
+ * allocations packed in the register. If so, we simply remove
+ * all these allocations.
+ * The cost of saving/restoring one extra register may depend on
+ * the number of registers already saved.
+ */
+
+ alloc_p x,prev,checked;
+ short time,space;
+ short tot_cost = 0,diff;
+
+ init_regcount();
+ checked = make_dummy();
+ while (TRUE) {
+ best_cumprofits(packed,&x,&prev);
+ if (x == (alloc_p) 0) break;
+ regs_occupied[x->al_regtype]++;
+ regsave_cost(regs_occupied,&time,&space);
+ diff = add_timespace(time,space) - tot_cost;
+ if (diff < cum_profits(x)) {
+ /* x is o.k. */
+ prev->al_next = x->al_next;
+ x->al_next = checked->al_next;
+ checked->al_next = x;
+ tot_cost += diff;
+ } else {
+ break;
+ }
+ }
+ /* Now every allocation in 'packed' does not pay off, so
+ * it is moved to unpacked, indicating it will not be assigned
+ * a register.
+ */
+ for (x = unpacked; x->al_next != (alloc_p) 0; x = x->al_next);
+ x->al_next = packed->al_next;
+ packed->al_next = checked->al_next;
+ oldalloc(checked);
+}
+
+
+
+STATIC bool in_single_reg(item,packed)
+ item_p item;
+ alloc_p packed;
+{
+ /* See if item is allocated in only one register (i.e. not in
+ * several different registers during several parts of its lifetime.
+ */
+
+ register alloc_p x,m;
+ bool seen = FALSE;
+
+ for (x = packed->al_next; x != (alloc_p) 0; x = x->al_next) {
+ for ( m = x; m != (alloc_p) 0; m = m->al_mates) {
+ if (m->al_item == item) {
+ if (seen) return FALSE;
+ seen = TRUE;
+ break;
+ }
+ }
+ }
+ return TRUE;
+}
+
+
+
+STATIC alloc_p find_prev(alloc,list)
+ alloc_p alloc,list;
+{
+ register alloc_p x;
+
+ assert ( alloc != (alloc_p) 0);
+ for (x = list; x->al_next != alloc ; x = x->al_next)
+ assert(x != (alloc_p) 0);
+ return x;
+}
+
+
+
+STATIC repl_allocs(new,old,packed)
+ alloc_p new,old,packed;
+{
+ alloc_p x,next,prev,*p;
+ new->al_regnr = old->al_regnr;
+ new->al_dummy = old->al_dummy;
+ prev = find_prev(old,packed);
+ new->al_next = old->al_next;
+ old->al_next = (alloc_p) 0;
+ prev->al_next = new;
+ new->al_mates = old;
+ p = &new->al_mates;
+ for (x = old; x != (alloc_p) 0; x = next) {
+ next = x->al_mates;
+ if (x->al_item == new->al_item) {
+ *p = next;
+ oldalloc(x);
+ } else {
+ p = &x->al_mates;
+ }
+ }
+}
+
+
+
+STATIC assemble_allocs(packed)
+ alloc_p packed;
+{
+ register alloc_p x,m,next;
+ alloc_p e;
+ bool voidb;
+
+ for (x = packed->al_next; x != (alloc_p) 0; x = next) {
+ next = x->al_next;
+ for ( m = x; m != (alloc_p) 0; m = m->al_mates) {
+ if (in_single_reg(m->al_item,packed) &&
+ (e = m->al_wholeproc) != (alloc_p) 0 &&
+ e->al_profits > 0 &&
+ fits_in(e,x,&voidb)) {
+ repl_allocs(e,x,packed);
+ break;
+ }
+ }
+ }
+}
+
+pack(alloclist,time_opt,packed_out,not_packed_out,p)
+ alloc_p alloclist, *packed_out,*not_packed_out;
+ bool time_opt;
+ proc_p p;
+{
+ /* This is the packing system. It decides which allations
+ * to grant a register.
+ * We use two lists: packed (for allocations that are assigned a
+ * register) and unpacked (allocations not yet assigned a register).
+ * The packed list is in fact '2-dimensional': the al_next field is
+ * used to link allations that are assigned different registers;
+ * the al_mates field links allocations that are assigned to
+ * the same registers (i.e. these allocations fit together).
+ */
+
+ register alloc_p x;
+ alloc_p packed,unpacked,fit;
+
+ init_regcount();
+ packed = make_dummy();
+ unpacked = make_dummy();
+ unpacked->al_next = alloclist;
+ while ((x = best_alloc(unpacked,packed,time_opt)) != (alloc_p) 0) {
+ fit = choose_location(x,packed,p);
+ update_lists(x,unpacked,packed,fit);
+ }
+ assemble_allocs(packed);
+ account_regsave(packed,unpacked);
+ /* remove allocations that don't pay off against register
+ * save/restore costs.
+ */
+ *packed_out = packed->al_next;
+ *not_packed_out = unpacked->al_next;
+ oldalloc(packed);
+ oldalloc(unpacked);
+}
--- /dev/null
+
+/* R E G I S T E R A L L O C A T I O N
+ *
+ * R A _ P A C K . H
+ */
+
+extern pack(); /* ( alloc_p alloclist, *packed_out,*not_packed_out;
+ * bool time_opt; proc_p p)
+ * This is the packing system. It decides which
+ * allations to grant a register.
+ */
--- /dev/null
+/* R E G I S T E R A L L O C A T I O N
+ *
+ * R A _ P R O F I T S . C
+ */
+
+#include "../share/types.h"
+#include "../share/debug.h"
+#include "../share/lset.h"
+#include "../share/global.h"
+#include "../../../h/em_reg.h"
+#include "ra.h"
+#include "ra_aux.h"
+#include "ra_profits.h"
+
+STATIC bool test_cond(cond,val)
+ short cond;
+ offset val;
+{
+ switch(cond) {
+ case DEFAULT:
+ return TRUE;
+ case FITBYTE:
+ return val >= -128 && val < 128;
+ case IN_0_63:
+ return val >= 0 && val <= 63;
+ case IN_0_8:
+ return val >= 0 && val <= 8;
+ }
+}
+
+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 short index_value(tab,n,time)
+ struct cond_tab tab[];
+ short n;
+ bool time;
+{
+ cond_p p;
+
+ p = &tab[n];
+ return (time ? p->mc_tval : p->mc_sval);
+}
+
+
+allocscore(itemtyp,localtyp,size,off,totyp,time_out,space_out)
+ short itemtyp, localtyp,totyp,size;
+ offset off;
+ short *time_out, *space_out;
+{
+ cond_p m;
+
+ if (localtyp == reg_loop) localtyp = reg_any;
+ if (size == ws || size ==ps && totyp == reg_pointer) {
+ switch(itemtyp) {
+ case LOCALVAR:
+ m = alocaltab[localtyp][totyp];
+ break;
+ case LOCAL_ADDR:
+ m = alocaddrtab[localtyp][totyp];
+ break;
+ case CONST:
+ m = aconsttab;
+ break;
+ case DCONST:
+ m = aconsttab;
+ break;
+ case GLOBL_ADDR:
+ m = aglobaltab;
+ break;
+ case PROC_ADDR:
+ m = aproctab;
+ break;
+ }
+ } else {
+ m = (cond_p) 0;
+ }
+ *time_out = (m == (cond_p) 0 ? -1 : map_value(m,off,TRUE));
+ *space_out = (m == (cond_p) 0 ? -1 : map_value(m,off,FALSE));
+ /*
+ printf("itemtyp = %d, localtyp = %d off = %D\n",itemtyp,localtyp,off);
+ printf("ALLOCSCORE = (%d,%d)\n",*time_out,*space_out);
+ */
+}
+
+opening_cost(itemtyp,localtyp,off,time_out,space_out)
+ short itemtyp, localtyp;
+ offset off;
+ short *time_out, *space_out;
+{
+ cond_p m;
+
+ if (localtyp == reg_loop) localtyp = reg_any;
+ switch(itemtyp) {
+ case LOCALVAR:
+ m = olocaltab[localtyp];
+ break;
+ case LOCAL_ADDR:
+ m = olocaddrtab[localtyp];
+ break;
+ case CONST:
+ m = oconsttab;
+ break;
+ case DCONST:
+ m = oconsttab;
+ break;
+ case GLOBL_ADDR:
+ m = oglobaltab;
+ break;
+ case PROC_ADDR:
+ m = oproctab;
+ break;
+ }
+ *time_out = (m == (cond_p) 0 ? 1000 : map_value(m,off,TRUE));
+ *space_out = (m == (cond_p) 0 ? 1000 : map_value(m,off,FALSE));
+ /*
+ printf("itemtyp = %d, localtyp = %d off = %D\n",itemtyp,localtyp,off);
+ printf("OPEN_COST = (%d,%d)\n",*time_out,*space_out);
+ */
+}
+
+
+
+
+short regsave_cost(regs,time_out,space_out)
+ short regs[], *time_out, *space_out;
+{
+ /* Estimate the costs of saving and restoring the registers
+ * The array regs contains the number of registers of every
+ * possible type.
+ */
+
+ short n = regs[reg_any] + regs[reg_pointer] + regs[reg_float];
+ /* #registers */
+
+ *time_out = index_value(regsav_cost,n,TRUE);
+ *space_out = index_value(regsav_cost,n,FALSE);
+ /*
+ printf("REGSAVE COST, n=%d, (%d,%d)\n",n,*time_out,*space_out);
+ */
+}
+
+
+
+STATIC short dyn_inits(inits)
+ lset inits;
+{
+ Lindex i;
+ short sum = 0;
+ bblock_p b;
+
+ for (i = Lfirst(inits); i != (Lindex) 0; i = Lnext(i,inits)) {
+ b = (bblock_p) Lelem(i);
+ sum += loop_scale(Lnrelems(b->b_loops));
+ }
+ return sum;
+}
+
+
+
+compute_profits(alloclist,time_opt)
+ alloc_p alloclist;
+ bool time_opt;
+{
+ /* Compute the profits attribute of every allocation.
+ * If the item of an allocation may be put in several types
+ * of register, we choose only the most advanteagous one.
+ */
+
+ register alloc_p alloc;
+ short s,t,rtyp,maxsc;
+ item_p item;
+ short time,space,sc;
+ short otime,ospace;
+ offset off;
+ short cnt,nr_inits;
+
+ for (alloc = alloclist; alloc != (alloc_p) 0; alloc = alloc->al_next) {
+ maxsc = 0;
+ item = alloc->al_item;
+ switch(item->it_type) {
+ case LOCALVAR:
+ case LOCAL_ADDR:
+ case CONST:
+ case DCONST:
+ off = item->i_t.it_off;
+ break;
+ default:
+ off = 0;
+ }
+ for (rtyp = item->it_regtype; ; rtyp = reg_any) {
+ allocscore( item->it_type,
+ item->it_regtype,
+ item->it_size,
+ off,
+ rtyp,
+ &time,
+ &space);
+ opening_cost( item->it_type,
+ item->it_regtype,
+ off,
+ &otime,
+ &ospace);
+ nr_inits = Lnrelems(alloc->al_inits);
+ s = alloc->al_susecount * space -
+ nr_inits*ospace;
+ if (!alloc->al_isloop && nr_inits > 0) {
+ /* might lead to increase of execution time */
+ cnt = 0;
+ } else {
+ cnt = alloc->al_dusecount;
+ }
+ t = cnt * time - dyn_inits(alloc->al_inits) * otime;
+ sc = (time_opt ? t : s);
+ if (sc >= maxsc) {
+ maxsc = sc;
+ alloc->al_regtype = rtyp;
+ alloc->al_profits = sc;
+ }
+ if (rtyp == reg_any) break;
+ }
+ }
+}
--- /dev/null
+
+/* R E G I S T E R A L L O C A T I O N
+ *
+ * R A _ P R O F I T S . H
+ */
+
+extern compute_profits();/* (alloc_p alloclist)
+ * Compute the profits attribute of every allocation.
+ */
+short regsave_cost(); /* (short regs[], *time_out, *space_out)
+ */
--- /dev/null
+/* R E G I S T E R A L L O C A T I O N
+ *
+ * R A _ X F O R M . C
+ */
+
+#include "../share/types.h"
+#include "../share/debug.h"
+#include "../share/def.h"
+#include "../share/global.h"
+#include "../share/lset.h"
+#include "../share/aux.h"
+#include "../share/alloc.h"
+#include "../../../h/em_mnem.h"
+#include "../../../h/em_spec.h"
+#include "../../../h/em_pseu.h"
+#include "../../../h/em_mes.h"
+#include "../../../h/em_reg.h"
+#include "ra.h"
+#include "ra_interv.h"
+#include "ra_xform.h"
+#include "ra_items.h"
+
+
+/* The replacement table is used to transform instructions that reference
+ * items other than local variables (i.e. the address of a local or global
+ * variable or a single/double constant; the transformation of an instruction
+ * that references a local variable is very simple).
+ * The generated code depends on the word and pointer size of the target
+ * machine.
+ */
+
+
+struct repl {
+ short r_instr; /* instruction */
+ short r_op; /* operand */
+};
+
+/* REGNR,NO and STOP should not equal the wordsize or pointer size
+ * of any machine.
+ */
+#define REGNR -3
+#define NO -2
+#define STOP -1
+#define PS 0
+#define PS2 1
+#define WS 2
+#define WS2 3
+
+#define LOAD_POINTER op_nop
+#define BLANK {0, STOP}
+
+#define NRREPLACEMENTS 13
+#define REPL_LENGTH 3
+
+struct repl repl_tab[NRREPLACEMENTS][REPL_LENGTH] = {
+ /* 0 */ {{op_lil, REGNR}, BLANK, BLANK},
+ /* 1 */ {{LOAD_POINTER,REGNR}, {op_loi,PS}, {op_loi,WS}},
+ /* 2 */ {{LOAD_POINTER,REGNR}, BLANK, BLANK},
+ /* 3 */ {{LOAD_POINTER,REGNR}, {op_loi,WS2}, BLANK},
+ /* 4 */ {{op_sil,REGNR}, BLANK, BLANK},
+ /* 5 */ {{LOAD_POINTER,REGNR}, {op_loi,PS}, {op_sti,WS}},
+ /* 6 */ {{LOAD_POINTER,REGNR}, {op_sti,WS2}, BLANK},
+ /* 7 */ {{op_lil,REGNR}, {op_inc,NO}, {op_sil,REGNR}},
+ /* 8 */ {{op_lil,REGNR}, {op_dec,NO}, {op_sil,REGNR}},
+ /* 9 */ {{op_zer,WS}, {op_sil,REGNR}, BLANK},
+ /*10 */ {{op_lol,REGNR}, BLANK, BLANK},
+ /*11 */ {{op_ldl,REGNR}, BLANK, BLANK},
+ /*12 */ {{LOAD_POINTER,REGNR}, {op_cai,NO}, BLANK},
+};
+
+
+
+
+init_replacements(psize,wsize)
+ short psize,wsize;
+{
+ /* The replacement code to be generated depends on the
+ * wordsize and pointer size of the target machine.
+ * The replacement table is initialized with a description
+ * of which sizes to use. This routine inserts the real sizes.
+ * It also inserts the actual EM instruction to be used
+ * as a 'Load pointer' instruction.
+ */
+
+ register int i,j;
+ short load_pointer;
+ struct repl *r;
+
+ assert (psize == wsize || psize == 2*wsize);
+ load_pointer = (psize == wsize ? op_lol : op_ldl);
+ for (i = 0; i < NRREPLACEMENTS; i++) {
+ for (j = 0; j < REPL_LENGTH; j++) {
+ r = &repl_tab[i][j];
+ if (r->r_op == STOP) break;
+ if (r->r_instr == LOAD_POINTER) {
+ r->r_instr = load_pointer;
+ }
+ switch (r->r_op) {
+ /* initially r_op describes how to compute
+ * the real operand of the instruction. */
+ case PS2:
+ r->r_op = 2*psize;
+ break;
+ case PS:
+ r->r_op = psize;
+ break;
+ case WS2:
+ r->r_op = 2*wsize;
+ break;
+ case WS:
+ r->r_op = wsize;
+ break;
+ case NO:
+ case REGNR: /* use offset of dummy local,
+ * will be filled in later.
+ */
+ break;
+ default: assert(FALSE);
+ }
+ }
+ }
+}
+
+
+
+STATIC int repl_index(l)
+ line_p l;
+{
+ return itemtab[INSTR(l) - sp_fmnem].id_replindex;
+}
+
+
+
+STATIC bool is_current(alloc,t)
+ alloc_p alloc;
+ short t;
+{
+ /* Is time t part of alloc's timespan? */
+
+ return contains(t,alloc->al_timespan);
+}
+
+
+STATIC match_item(item,l)
+ item_p item;
+ line_p l;
+{
+ /* See if the item used by l is the same one as 'item' */
+ struct item thisitem;
+
+ fill_item(&thisitem,l);
+ if (item->it_type == LOCAL_ADDR && thisitem.it_type == LOCALVAR) {
+ /* The usage of a local variable is also considered to
+ * be the usage of the address of that variable.
+ */
+ thisitem.it_type = LOCAL_ADDR;
+ }
+ return item->it_type == thisitem.it_type && same_item(item,&thisitem);
+}
+
+
+
+STATIC alloc_p find_alloc(alloclist,l,t)
+ alloc_p alloclist;
+ line_p l;
+ short t;
+{
+ /* See if any of the allocations of the list applies to instruction
+ * l at time t.
+ */
+
+ register alloc_p alloc,m;
+
+ for (alloc = alloclist; alloc != (alloc_p) 0; alloc = alloc->al_next) {
+ for (m = alloc; m != (alloc_p) 0; m = m->al_mates) {
+ if (is_current(m,t) && match_item(m->al_item,l)) {
+ return m;
+ }
+ }
+ }
+ return (alloc_p) 0;
+}
+
+
+STATIC replace_line(l,b,list)
+ line_p l,list;
+ bblock_p b;
+{
+ if (b->b_start == l) {
+ b->b_start = list;
+ } else {
+ PREV(l)->l_next = list;
+ }
+ PREV(list) = PREV(l);
+ while (list->l_next != (line_p) 0) {
+ list = list->l_next;
+ }
+ list->l_next = l->l_next;
+ if (l->l_next != (line_p) 0) {
+ PREV(l->l_next) = list;
+ }
+ oldline(l);
+}
+
+
+STATIC line_p repl_code(lnp,regnr)
+ line_p lnp;
+ offset regnr;
+{
+ line_p head,*q,l,prev = (line_p) 0;
+ int i,index;
+ struct repl *r;
+
+ q = &head;
+ index = repl_index(lnp);
+ for (i = 0; i < REPL_LENGTH; i++) {
+ r = &repl_tab[index][i];
+ if (r->r_op == STOP) break; /* replacement < REPL_LENGTH */
+ switch(r->r_op) {
+ case REGNR:
+ l = int_line(regnr);
+ break;
+ case NO:
+ l = newline(OPNO);
+ break;
+ default:
+ l = newline(OPSHORT);
+ SHORT(l) = r->r_op;
+ break;
+ }
+ *q = l;
+ l->l_instr = r->r_instr;
+ PREV(l) = prev;
+ prev = l;
+ q = &l->l_next;
+ }
+ return head;
+}
+
+
+
+STATIC apply_alloc(b,l,alloc)
+ bblock_p b;
+ line_p l;
+ alloc_p alloc;
+{
+ /* 'l' is an EM instruction using an item that will be put in
+ * a register. Generate new code that uses the register instead
+ * of the item.
+ * If the item is a local variable the new code is the same as
+ * the old code, except for the fact that the offset of the
+ * local is changed (it now uses the dummy local that will be
+ * put in a register by the code generator).
+ * If the item is a constant, the new code is a LOL or LDL.
+ * If the item is the address of a local or global variable, things
+ * get more complicated. The new code depends on the instruction
+ * that uses the item (i.e. l). The new code, which may consist of
+ * several instructions, is obtained by consulting a replacement
+ * table.
+ */
+
+ line_p newcode;
+
+ if (alloc->al_item->it_type == LOCALVAR) {
+ SHORT(l) = alloc->al_dummy;
+ } else {
+ newcode = repl_code(l,alloc->al_dummy);
+ replace_line(l,b,newcode);
+ }
+}
+
+
+
+STATIC int loaditem_tab[NRITEMTYPES][2] =
+{ /* WS 2 * WS */
+ /*LOCALVAR*/ op_lol, op_ldl,
+ /*LOCAL_ADDR*/ op_lal, op_lal,
+ /*GLOBL_ADDR*/ op_lae, op_lae,
+ /*PROC_ADDR*/ op_lpi, op_lpi,
+ /*CONST*/ op_loc, op_nop,
+ /*DCONST*/ op_nop, op_ldc
+};
+
+
+STATIC line_p load_item(item)
+ item_p item;
+{
+ /* Generate an EM instruction that loads the item on the stack */
+
+ line_p l;
+
+ switch (item->it_type) {
+ case GLOBL_ADDR:
+ l = newline(OPOBJECT);
+ OBJ(l) = item->i_t.it_obj;
+ break;
+ case PROC_ADDR:
+ l = newline(OPPROC);
+ PROC(l) = item->i_t.it_proc;
+ break;
+ default:
+ l = int_line(item->i_t.it_off);
+ }
+ l->l_instr = loaditem_tab[item->it_type][item->it_size == ws ? 0 : 1];
+ assert(l->l_instr != op_nop);
+ return l;
+}
+
+
+STATIC line_p store_local(size,off)
+ short size;
+ offset off;
+{
+ line_p l = int_line(off);
+
+ l->l_instr = (size == ws ? op_stl : op_sdl);
+ return l;
+}
+
+
+
+STATIC line_p init_place(b)
+ bblock_p b;
+{
+
+ register line_p l,prev;
+
+ prev = (line_p) 0;
+ for (l = b->b_start; l != (line_p) 0; l = l->l_next) {
+ switch(INSTR(l)) {
+ case ps_mes:
+ case ps_pro:
+ case op_lab:
+ break;
+ default:
+ return prev;
+ }
+ prev =l;
+ }
+ return prev;
+}
+
+
+
+STATIC append_code(l1,l2,b)
+ line_p l1,l2;
+ bblock_p b;
+{
+ /* Append instruction l1 and l2 at begin of block b */
+
+ line_p l;
+
+ DLINK(l1,l2);
+ l = init_place(b);
+ if (l == (line_p) 0) {
+ l2->l_next = b->b_start;
+ b->b_start = l1;
+ PREV(l1) = (line_p) 0;
+ } else {
+ l2->l_next = l->l_next;
+ DLINK(l,l1);
+ }
+ if (l2->l_next != (line_p) 0) {
+ PREV(l2->l_next) = l2;
+ }
+}
+
+
+
+STATIC emit_init_code(list)
+ alloc_p list;
+{
+ /* Emit initialization code for all packed allocations.
+ * This code looks like "dummy_local := item", e.g.
+ * "LOC 25 ; STL -10" in EM terminology.
+ */
+
+ register alloc_p alloc,m;
+ Lindex bi;
+ bblock_p b;
+
+ for (alloc = list; alloc != (alloc_p) 0; alloc = alloc->al_next) {
+ for (m = alloc; m != (alloc_p) 0; m = m->al_mates) {
+ for (bi = Lfirst(m->al_inits); bi != (Lindex) 0;
+ bi = Lnext(bi,m->al_inits)) {
+ /* "inits" contains all initialization points */
+ b = (bblock_p) Lelem(bi);
+ append_code(load_item(m->al_item),
+ store_local(m->al_item->it_size,
+ m->al_dummy),
+ b);
+ }
+ }
+ }
+}
+
+
+
+STATIC emit_mesregs(p,alloclist)
+ proc_p p;
+ alloc_p alloclist;
+{
+ line_p l,m,x;
+ alloc_p alloc;
+
+
+ l = p->p_start->b_start;
+ x = l->l_next;
+ for (alloc = alloclist; alloc != (alloc_p) 0; alloc = alloc->al_next) {
+ m = reg_mes(alloc->al_dummy,alloc->al_item->it_size,
+ alloc->al_regtype,INFINITE);
+ DLINK(l,m);
+ l = m;
+ }
+ if (x != (line_p) 0) DLINK(l,x);
+}
+
+#define is_mesreg(l) (INSTR(l) == ps_mes && aoff(ARG(l),0) == ms_reg)
+
+
+
+rem_mes(p)
+ proc_p p;
+{
+ register bblock_p b;
+ register line_p l,next;
+ offset m;
+
+ 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 ( INSTR(l) == ps_mes &&
+ ((m = aoff(ARG(l),0)) == ms_liv || m == ms_ded)) {
+ /* remove live/dead messages */
+ rm_line(l,b);
+ }
+ }
+ }
+}
+
+
+
+xform_proc(p,alloclist,nrinstrs,instrmap)
+ proc_p p;
+ alloc_p alloclist;
+ short nrinstrs;
+ line_p instrmap[];
+{
+ /* Transform every instruction of procedure p that uses an item
+ * at a point where the item is kept in a register.
+ */
+
+ register short now = 0;
+ register line_p l,next;
+ register bblock_p b;
+ alloc_p alloc;
+
+ 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 (is_mesreg(l) && ARG(l)->a_next != (arg_p) 0 &&
+ aoff(ARG(l),4) != INFINITE) {
+ /* All register messages for local variables
+ * that were not assigned a register get
+ * their 'count' fields* set to 0.
+ */
+ ARG(l)->a_next->a_next->a_next
+ ->a_next->a_a.a_offset = 0;
+ }
+ if (is_item(l) &&
+ (alloc = find_alloc(alloclist,l,now))
+ != (alloc_p) 0 ) {
+ apply_alloc(b,l,alloc);
+ }
+ now++;
+ }
+ }
+ emit_init_code(alloclist);
+ emit_mesregs(p,alloclist);
+ rem_mes(p);
+}
+
+
+
+
+STATIC bool always_in_reg(off,allocs,size_out)
+ offset off;
+ alloc_p allocs;
+ short *size_out;
+{
+ /* See if the local variable with the given offset is stored
+ * in a register during its entire lifetime. As a side effect,
+ * return the size of the local.
+ */
+
+ alloc_p alloc,m;
+ item_p item;
+
+ for (alloc = allocs; alloc != (alloc_p) 0; alloc = alloc->al_next) {
+ for (m = alloc; m != (alloc_p) 0; m = m->al_mates) {
+ item = m->al_item;
+ if (m->al_iswholeproc &&
+ item->it_type == LOCALVAR &&
+ item->i_t.it_off == off) {
+ *size_out = item->it_size;
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+
+rem_locals(p,allocs)
+ proc_p p;
+ alloc_p allocs;
+{
+ /* Try to decrease the number of locals of procedure p, by
+ * looking at which locals are always stored in a register.
+ */
+
+ offset nrlocals = p->p_localbytes;
+ short size;
+
+ while (nrlocals > 0) {
+ /* A local can only be removed if all locals with
+ * higher offsets are removed too.
+ */
+ if (always_in_reg(-nrlocals,allocs,&size)) {
+ OUTVERBOSE("local %d removed from proc %d\n",
+ nrlocals,p->p_id);
+ nrlocals -= size;
+ } else {
+ break;
+ }
+ }
+ p->p_localbytes = nrlocals;
+}
+rem_formals(p,allocs)
+ proc_p p;
+ alloc_p allocs;
+{
+ /* Try to decrease the number of formals of procedure p, by
+ * looking at which formals are always stored in a register.
+ */
+
+ offset nrformals = p->p_nrformals;
+ offset off = 0;
+ short size;
+
+ if (nrformals == UNKNOWN_SIZE) return;
+ while (off < nrformals) {
+ if (always_in_reg(off,allocs,&size)) {
+ OUTVERBOSE("formal %d removed from proc %d\n",
+ off,p->p_id);
+ off += size;
+ } else {
+ break;
+ }
+ }
+ if (nrformals == off) {
+ OUTVERBOSE("all formals of procedure %d removed\n",p->p_id,0);
+ p->p_nrformals = 0;
+ }
+}
--- /dev/null
+
+/* R E G I S T E R A L L O C A T I O N
+ *
+ * R A _ X F O R M . H
+ */
+
+extern init_replacements(); /* (short psize,wsize)
+ * This routine must be called once, before
+ * any call to xform_proc. It initializes
+ * a machine dependent table.
+ */
+extern xform_proc(); /* (proc_p p; alloc_p alloclist;
+ * short nrinstrs; line_p instrmap[])
+ * Transform a procedure. Alloclist must
+ * contain the packed allocations (i.e. those
+ * allocations that are assigned a register).
+ */
+bool always_in_reg(); /* ( offset off; alloc_p allocs;
+ * short *size_out;)
+ * See if the local variable with the given
+ * offset is stored in a register during its
+ * entire lifetime. As a side effect,
+ * return the size of the local.
+ */
--- /dev/null
+/* U S E - D E F I N I T I O N A N A L Y S I S
+ *
+ * U D . H
+ */
+
+#define GEN(b) (b)->b_extend->bx_ud.bx_gen
+#define KILL(b) (b)->b_extend->bx_ud.bx_kill
+#define IN(b) (b)->b_extend->bx_ud.bx_in
+#define OUT(b) (b)->b_extend->bx_ud.bx_out
+#define C_GEN(b) (b)->b_extend->bx_ud.bx_cgen
+#define C_KILL(b) (b)->b_extend->bx_ud.bx_ckill
+#define C_IN(b) (b)->b_extend->bx_ud.bx_cin
+#define C_OUT(b) (b)->b_extend->bx_ud.bx_cout
+#define CHGVARS(b) (b)->b_extend->bx_ud.bx_chgvars
+
+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.
+ */
--- /dev/null
+/* C O P Y P R O P A G A T I O N
+ *
+ * A U X I L I A R Y R O U T I N E S
+ */
+
+
+#include "../share/types.h"
+#include "../ud/ud.h"
+#include "../share/debug.h"
+#include "../share/global.h"
+#include "../share/alloc.h"
+#include "../share/lset.h"
+#include "../share/cset.h"
+#include "../share/def.h"
+#include "../share/locals.h"
+#include "../share/aux.h"
+#include "../../../h/em_mnem.h"
+#include "../../../h/em_pseu.h"
+#include "../../../h/em_spec.h"
+#include "../ud/ud_defs.h"
+
+repl_line(old,new,b)
+ line_p old,new;
+ bblock_p b;
+{
+ /* Replace 'old' by 'new' */
+
+ if (PREV(old) == (line_p) 0) {
+ b->b_start = new;
+ } else {
+ PREV(old)->l_next = new;
+ }
+ PREV(new) = PREV(old);
+ if ((new->l_next = old->l_next) != (line_p) 0) {
+ PREV(new->l_next) = new;
+ }
+ oldline(old);
+}
+
+
+
+bool same_var(use,def)
+ line_p use,def;
+{
+ /* 'use' is an instruction that uses a variable
+ * for which we maintain ud-info (e.g. a LOL).
+ * See if 'def' references the same variable.
+ */
+
+ if (TYPE(use) == OPOBJECT) {
+ return TYPE(def) == OPOBJECT && OBJ(use) == OBJ(def);
+ } else {
+ return TYPE(def) != OPOBJECT && off_set(use) == off_set(def);
+ }
+}
--- /dev/null
+
+/* C O P Y P R O P A G A T I O N
+ *
+ * A U X I L I A R Y R O U T I N E S
+ */
+
+
+extern repl_line(); /* (line_p old,new; bblock_p b)
+ * Replace EM instruction 'old' by a
+ * copy of 'new'. Update doubly-linked
+ * list.
+ */
+extern bool same_var(); /* (line_p use,def)
+ * 'use' is an instruction that uses a variable
+ * for which we maintain ud-info (e.g. a LOL).
+ * See if 'def' references the same variable.
+ */
--- /dev/null
+/* C O N S T A N T P R O P A G A T I O N */
+
+#include "../share/types.h"
+#include "../ud/ud.h"
+#include "../share/debug.h"
+#include "../share/global.h"
+#include "../share/alloc.h"
+#include "../share/lset.h"
+#include "../share/cset.h"
+#include "../share/def.h"
+#include "../share/aux.h"
+#include "../share/locals.h"
+#include "../../../h/em_mnem.h"
+#include "../../../h/em_pseu.h"
+#include "../../../h/em_spec.h"
+#include "../ud/ud_defs.h"
+#include "ud_const.h"
+#include "ud_aux.h"
+
+
+#define CHANGE_INDIR(p) (p->p_change->c_flags & CF_INDIR)
+#define IS_REG(v) (locals[TO_LOCAL(v)]->lc_flags & LCF_REG)
+#define BODY_KNOWN(p) (p->p_flags1 & (byte) PF_BODYSEEN)
+#define CALLS_UNKNOWN(p) (p->p_flags1 & (byte) PF_CALUNKNOWN)
+
+
+bool is_use(l)
+ line_p l;
+{
+ /* See if 'l' is a use of a variable */
+
+ switch(INSTR(l)) {
+ case op_lde:
+ case op_ldl:
+ case op_loe:
+ case op_lol:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+ /* NOTREACHED */
+}
+
+
+
+
+bool value_known(def,val_out)
+ line_p def;
+ offset *val_out;
+{
+ /* See if the value stored by definition 'def'
+ * is known statically (i.e. is a constant).
+ */
+
+ short sz1, sz2;
+ offset v;
+ line_p l;
+
+ sz1 = ws;
+ switch(INSTR(def)) {
+ case op_inl:
+ case op_ine:
+ case op_del:
+ case op_dee:
+ return FALSE;
+ case op_zrl:
+ case op_zre:
+ v = (offset) 0;
+ break;
+ case op_sdl:
+ case op_sde:
+ sz1 += ws;
+ /* fall through ... */
+ case op_stl:
+ case op_ste:
+ l = PREV(def);
+ if (l == (line_p) 0) return FALSE;
+ sz2 = ws;
+ switch(INSTR(l)) {
+ case op_zer:
+ if (SHORT(l) >= sz1) {
+ v = (offset) 0;
+ break;
+ }
+ return FALSE;
+ case op_ldc:
+ sz2 += ws;
+ /* fall through ...*/
+ case op_loc:
+ if (sz1 == sz2) {
+ v = off_set(l);
+ break;
+ }
+ /* fall through ... */
+ default:
+ return FALSE;
+ }
+ break;
+ default:
+ assert(FALSE);
+ }
+ *val_out = v;
+ return TRUE;
+}
+
+
+
+
+bool affected(use,v,l)
+ line_p use,l;
+ short v;
+{
+ /* See if the variable referenced by 'use' may be
+ * changed by instruction l, which is either a cal, cai or
+ * an indirect assignment.
+ */
+
+ if (INSTR(l) == op_cal &&
+ TYPE(use) == OPOBJECT &&
+ BODY_KNOWN(PROC(l)) &&
+ !CALLS_UNKNOWN(PROC(l)) &&
+ !CHANGE_INDIR(PROC(l))) {
+ return Cis_elem(OBJ(use)->o_id,PROC(l)->p_change->c_ext);
+ }
+ return TYPE(use) == OPOBJECT || !IS_REG(v);
+}
+
+
+
+
+STATIC search_backwards(use,v,found,def)
+ line_p use, *def;
+ short v;
+ bool *found;
+{
+ /* Search backwards in the current basic block,
+ * starting at 'use', trying to find a definition
+ * of the variable referenced by 'use', whose variable
+ * number is v. If the definition found is an
+ * implicit one, return 0 as def.
+ */
+
+ register line_p l;
+
+ for (l = PREV(use); l != (line_p) 0; l = PREV(l)) {
+ if (does_expl_def(l) && same_var(use,l)) {
+ *found = TRUE;
+ *def = l;
+ return;
+ }
+ if (does_impl_def(l) && affected(use,v,l)) {
+ *found = TRUE;
+ *def = (line_p) 0;
+ return;
+ }
+ }
+ *found = FALSE;
+}
+
+
+
+
+STATIC short outer_def(vdefs,in)
+ cset vdefs, in;
+{
+ /* See if there is a unique definition of variable
+ * v reaching the beginning of block b.
+ * 'vdefs' is vardefs[v], 'in' is IN(b).
+ */
+
+ short n,defnr = 0;
+ Cindex i;
+
+ for (i = Cfirst(vdefs); i != (Cindex) 0; i = Cnext(i,vdefs)) {
+ n = Celem(i);
+ if (Cis_elem(EXPL_TO_DEFNR(n),in)) {
+ if (defnr != 0) return 0;
+ /* If there was already a def., there's no unique one */
+ defnr = n;
+ }
+ }
+ return defnr;
+}
+
+
+
+
+line_p unique_def(use,b,defnr_out)
+ line_p use;
+ bblock_p b;
+ short *defnr_out;
+{
+ /* See if there is one unique explicit definition
+ * of the variable used by 'use', that reaches 'use'.
+ */
+
+ short v;
+ bool found;
+ line_p def = (line_p) 0;
+
+ *defnr_out = 0;
+ var_nr(use,&v,&found);
+ if (found) {
+ /* We do maintain ud-info for this variable.
+ * See if there is a previous explicit definition
+ * in the current basic block.
+ */
+ search_backwards(use,v,&found,&def);
+ if (!found && !Cis_elem(IMPLICIT_DEF(v),IN(b))) {
+ /* See if there is a unique explicit definition
+ * outside the current block, reaching the
+ * beginning of the current block.
+ */
+ *defnr_out = outer_def(vardefs[v],IN(b));
+ def = (*defnr_out == 0 ? (line_p) 0 : defs[*defnr_out]);
+ }
+ }
+ return def;
+}
+
+
+
+fold_const(l,b,val)
+ line_p l;
+ bblock_p b;
+ offset val;
+{
+ /* Perform the substitutions required for constant folding */
+
+ line_p n;
+
+ n = int_line(val);
+ switch(INSTR(l)) {
+ case op_lol:
+ case op_loe:
+ n->l_instr = op_loc;
+ break;
+ case op_ldl:
+ case op_lde:
+ n->l_instr = op_ldc;
+ break;
+ default:
+ assert (FALSE);
+ }
+ repl_line(l,n,b);
+}
--- /dev/null
+
+/* C O N S T A N T P R O P A G A T I O N */
+
+extern line_p unique_def(); /* ( line_p use; bblock_p b; short *defnr_out;)
+ * See if there is a unique explicit definition
+ * of the variable used by 'use' that
+ * reaches 'use'.
+ */
+extern bool value_known(); /* (line_p def; offset *val_out)
+ * See if the value stored by definition 'def'
+ * is known statically (i.e. is a constant).
+ */
+extern fold_const(); /* (line_p l; bblock_p b; offset val)
+ * Perform the substitutions required for
+ * constant folding.
+ */
+extern bool is_use(); /* (line_p l)
+ * See if 'l' is a use of a variable.
+ */
+extern bool affected(); /* (line_p use,l; short v)
+ * See if the variable referenced by 'use' may
+ * be changed by instruction l, which is
+ * either a cal, cai or an indirect assignment.
+ */
--- /dev/null
+/* C O P Y P R O P A G A T I O N */
+
+#include "../share/types.h"
+#include "../ud/ud.h"
+#include "../share/debug.h"
+#include "../share/global.h"
+#include "../share/alloc.h"
+#include "../share/lset.h"
+#include "../share/cset.h"
+#include "../share/def.h"
+#include "../share/aux.h"
+#include "../share/locals.h"
+#include "../../../h/em_mnem.h"
+#include "../../../h/em_pseu.h"
+#include "../../../h/em_spec.h"
+#include "../ud/ud_defs.h"
+#include "ud_copy.h"
+#include "ud_const.h"
+#include "ud_aux.h"
+
+
+
+line_p *copies; /* table of copies; every entry points to the
+ * store-instruction.
+ */
+short *def_to_copynr; /* table that maps a 'definition'-number to a
+ * 'copy' number.
+ */
+short nrcopies; /* number of copies in the current procedure
+ * (length of copies-table)
+ */
+
+#define COPY_NR(c) def_to_copynr[c]
+#define CHANGED(v,b) (Cis_elem(v,CHGVARS(b)) || Cis_elem(IMPLICIT_DEF(v),GEN(b)))
+
+
+#define COUNT 0
+#define MAP 1
+
+STATIC traverse_defs(p,action)
+ proc_p p;
+ int action;
+{
+ bblock_p b;
+ line_p l;
+ bool found;
+ short defcnt,v,cnt;
+
+ defcnt = 1;
+ if (action == COUNT) {
+ nrcopies = 0;
+ } else {
+ copies = (line_p *) newmap(nrcopies);
+ def_to_copynr = newtable(nrdefs);
+ cnt = 1;
+ }
+ if (defcnt > nrdefs) return;
+ for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
+ for (l = b->b_start; l != (line_p) 0; l = l->l_next) {
+ if (defs[defcnt] == l) {
+ if (is_copy(l)) {
+ var_nr(PREV(l),&v,&found);
+ if (found) {
+ if (action == COUNT) {
+ nrcopies++;
+ } else {
+ copies[cnt] = l;
+ def_to_copynr[defcnt] =
+ cnt++;
+ }
+ }
+ }
+ if (++defcnt > nrdefs) return;
+ }
+ }
+ }
+}
+
+
+
+STATIC make_copytab(p)
+ proc_p p;
+{
+ /* Make a table of all copies appearing in procedure p.
+ * We first count how many there are, because we
+ * have to allocate a dynamic array of the correct size.
+ */
+
+ traverse_defs(p,COUNT);
+ traverse_defs(p,MAP);
+}
+
+
+
+STATIC bool is_changed(varl,start,stop)
+ line_p varl, start, stop;
+{
+ /* See if the variable used by instruction varl
+ * is changed anywhere between 'start' and 'stop'
+ */
+
+ register line_p l;
+ short v;
+ bool found;
+
+ var_nr(varl,&v,&found);
+ if (!found) {
+ return TRUE; /* We don't maintain ud-info for this variable */
+ }
+ for (l = start; l != (line_p) 0 && l != stop; l = l->l_next) {
+ if (does_expl_def(l) && same_var(varl,l)) return TRUE;
+ if (does_impl_def(l) && affected(varl,v,l)) return TRUE;
+ }
+ return FALSE;
+}
+
+
+
+STATIC gen_kill_copies(p)
+ proc_p p;
+{
+ /* Compute C_GEN and C_KILL for every basic block
+ * of p.
+ */
+
+ register line_p l;
+ register bblock_p b,n;
+ short v;
+ bool found;
+ short copycnt = 1, defcnt = 1;
+
+ for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
+ C_GEN(b) = Cempty_set(nrcopies);
+ C_KILL(b) = Cempty_set(nrcopies);
+ }
+ if (nrcopies == 0) return;
+ for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
+ for (l = b->b_start; l != (line_p) 0; l = l->l_next) {
+ if (copies[copycnt] == l) {
+ var_nr(PREV(l),&v,&found);
+ assert(found);
+ for (n = p->p_start; n != (bblock_p) 0;
+ n = n->b_next) {
+ if (n != b && CHANGED(v,n) &&
+ Cis_elem(EXPL_TO_DEFNR(defcnt),IN(n))) {
+ Cadd(copycnt,&C_KILL(n));
+ }
+ }
+ if (is_changed(PREV(l),l,(line_p) 0)) {
+ Cadd(copycnt,&C_KILL(b));
+ } else {
+ Cadd(copycnt,&C_GEN(b));
+ }
+ if (++copycnt > nrcopies) return;
+ }
+ if (defs[defcnt] == l) defcnt++;
+ }
+ }
+}
+
+
+
+STATIC intersect_outs(bbset,setp,full_set)
+ lset bbset;
+ cset *setp,full_set;
+{
+ /* Take the intersection of C_OUT(b), for all b in bbset,
+ * and put the result in setp.
+ */
+
+ Lindex i;
+
+ Ccopy_set(full_set,setp);
+ for (i = Lfirst(bbset); i != (Lindex) 0; i = Lnext(i,bbset)) {
+ Cintersect(C_OUT((bblock_p) Lelem(i)), setp);
+ }
+}
+
+
+
+STATIC init_cin(p,full_set)
+ proc_p p;
+ cset full_set;
+{
+ /* Initialize C_IN(b) and C_OUT(b), for every basic block b.
+ * C_IN of the root of the CFG (i.e. the procedure entry block)
+ * will contain every copy, as it trivially holds that for
+ * every copy "s: A := B" there is no assignment to B on any
+ * path from s to the beginning of the root (because PRED(root)=empty).
+ * C_IN and C_OUT of the root will never be changed.
+ * For all remaining blocks b, C_IN(b) is initialized to the set of
+ * all copies, and C_OUT is set to all copies but those killed in b.
+ */
+
+ bblock_p b;
+ bblock_p root = p->p_start;
+
+ C_IN(root) = Cempty_set(nrcopies);
+ Ccopy_set(full_set,&C_IN(root)); /* full_set is the set of all copies */
+ /* C_OUT(root) = {all copies} - C_KILL(root) + C_GEN(root) */
+ C_OUT(root) = Cempty_set(nrcopies);
+ Ccopy_set(full_set,&C_OUT(root));
+ Csubtract(C_KILL(root),&C_OUT(root));
+ Cjoin(C_GEN(root),&C_OUT(root));
+ for (b = root->b_next; b != (bblock_p) 0; b = b->b_next) {
+ C_IN(b) = Cempty_set(nrcopies);
+ Ccopy_set(full_set,&C_IN(b));
+ C_OUT(b) = Cempty_set(nrcopies);
+ Ccopy_set(full_set,&C_OUT(b));
+ Csubtract(C_KILL(b),&C_OUT(b));
+ }
+}
+
+
+
+STATIC solve_cin(p)
+ proc_p p;
+{
+ /* Solve the data flow equations for reaching
+ * definitions of procedure p.
+ * These equations are:
+ * (1) C_OUT(b) = C_IN(b) - C_KILL(b) + C_GEN(b)
+ * (2) C_IN(b) = C_OUT(p1) * .. * C_OUT(pn)
+ * (3) C_IN(root) = {all copies} ;
+ * where PRED(b) = {p1, .. , pn}
+ * and '*' denotes set intersection.
+ * We use the iterative algorithm of Aho&Ullman to
+ * solve the equations.
+ */
+
+ register bblock_p b;
+ bool change;
+ cset newin,full_set;
+ short n;
+
+ /* initializations */
+ full_set = Cempty_set(nrcopies);
+ for (n = 1; n <= nrcopies; n++) {
+ Cadd(n,&full_set);
+ }
+ newin = Cempty_set(nrcopies);
+ init_cin(p,full_set);
+ change = TRUE;
+ /* main loop */
+ while (change) {
+ change = FALSE;
+ for (b = p->p_start->b_next; b != (bblock_p) 0; b = b->b_next) {
+ intersect_outs(b->b_pred, &newin,full_set);
+ /* newin = C_OUT(p1) * .. * C_OUT(pn) */
+ if (!Cequal(newin,C_IN(b))) {
+ change = TRUE;
+ Ccopy_set(newin, &C_IN(b));
+ Ccopy_set(C_IN(b), &C_OUT(b));
+ Csubtract(C_KILL(b), &C_OUT(b));
+ Cjoin(C_GEN(b), &C_OUT(b));
+ }
+ }
+ }
+ Cdeleteset(newin);
+ Cdeleteset(full_set);
+}
+
+
+
+copy_analysis(p)
+ proc_p p;
+{
+ /* Determine which copies procedure p has. Compute C_IN(b),
+ * for every basic block b.
+ */
+
+ make_copytab(p); /* Make a table of all copies */
+ gen_kill_copies(p); /* Compute C_GEN(b) and C_KILL(b), for every b */
+ solve_cin(p); /* Solve equations for C_IN(b) */
+}
+
+
+
+bool is_copy(def)
+ line_p def;
+{
+ /* See if the definition def is also a 'copy', i.e. an
+ * statement of the form 'A := B' (or, in EM terminology:
+ * a sequence 'Load Variable; Store Variable').
+ */
+
+
+ line_p lhs;
+ int instr;
+
+ lhs = PREV(def);
+ if (lhs == (line_p) 0) return FALSE;
+ instr = INSTR(def);
+ switch(INSTR(lhs)) {
+ case op_lol:
+ case op_loe:
+ return instr == op_stl || instr == op_ste;
+ case op_ldl:
+ case op_lde:
+ return instr == op_sdl || instr == op_sde;
+ default:
+ return FALSE;
+ }
+ /* NOTREACHED */
+}
+
+
+
+fold_var(old,new,b)
+ line_p old, new;
+ bblock_p b;
+{
+ /* The variable referenced by the EM instruction 'old'
+ * must be replaced by the variable referenced by 'new'.
+ */
+
+ line_p l;
+
+/* DEBUGGING:
+ local_p loc;
+ short nr;
+ bool ok;
+ if (TYPE(old) == OPOBJECT) {
+ printf("global var.");
+ } else {
+ printf("local var. with off. %D",off_set(old));
+ find_local(off_set(old),&nr,&ok);
+ assert(ok);
+ loc = locals[nr];
+ printf(",score %D",loc->lc_score);
+ }
+ printf(" replaced by ");
+ if (TYPE(new) == OPOBJECT) {
+ printf("global var.");
+ } else {
+ printf("local var. with off. %D",off_set(new));
+ find_local(off_set(new),&nr,&ok);
+ assert(ok);
+ loc = locals[nr];
+ printf(",score %D",loc->lc_score);
+ }
+ printf("\n");
+END DEBUG */
+ l = old;
+ if (TYPE(l) != TYPE(new)) {
+ l = newline(TYPE(new));
+ l->l_instr = INSTR(new);
+ repl_line(old,l,b);
+ }
+ switch(TYPE(new)) {
+ case OPOBJECT:
+ OBJ(l) = OBJ(new);
+ break;
+ case OPSHORT:
+ SHORT(l) = SHORT(new);
+ break;
+ case OPOFFSET:
+ OFFSET(l) = OFFSET(new);
+ break;
+ default:
+ assert(FALSE);
+ }
+}
+
+
+
+bool value_retained(copy,defnr,use,b)
+ line_p copy,use;
+ short defnr;
+ bblock_p b;
+{
+ /* See if the right hand side variable of the
+ * copy still has the same value at 'use'.
+ * If the copy and the use are in the same
+ * basic block (defnr = 0), search from the
+ * copy to the use, to see if the rhs variable
+ * is changed. If the copy is in another block,
+ * defnr is the definition-number of the copy.
+ * Search from the beginning of the block to
+ * the use, to see if the rhs is changed; if not,
+ * check that the copy is in C_IN(b).
+ */
+
+ line_p rhs, start;
+
+ rhs = PREV(copy);
+ start = (defnr == 0 ? copy : b->b_start);
+ return !is_changed(rhs,start,use) &&
+ (defnr == 0 || Cis_elem(COPY_NR(defnr), C_IN(b)));
+}
--- /dev/null
+
+/* C O P Y P R O P A G A T I O N */
+
+extern line_p *copies; /* table of copies; every entry points to the
+ * store-instruction.
+ */
+extern short *def_to_copynr; /* Table that maps a 'definition'-number to a
+ * 'copy' number.
+ */
+extern short nrcopies; /* number of copies in the current procedure
+ * (length of copies-table)
+ */
+
+extern copy_analysis(); /* (proc_p p)
+ * Determine which copies procedure p has.
+ * Compute C_IN(b), for every basic block b.
+ */
+extern bool is_copy(); /* (line_p def)
+ * See if the definition def is also a 'copy',
+ * i.e. an statement of the form
+ * 'A := B' (or, in EM terminology:
+ * a sequence 'Load Variable; Store Variable').
+ */
+extern fold_var(); /* (line_p old,new; bblock_p b)
+ * The variable referenced by the
+ * EM instruction 'old' must be replaced
+ * by the variable referenced by 'new'.
+ */
+extern bool value_retained(); /* (line_p copy; short defnr; line_p use;
+ * bblock_p b)
+ * See if the right hand side variable of the
+ * copy still has the same value at 'use'.
+ * If the copy and the use are in the same
+ * basic block (defnr = 0), search from the
+ * copy to the use, to see if the rhs variable
+ * is changed. If the copy is in another block,
+ * defnr is the definition-number of the copy.
+ * Search from the beginning of the block to
+ * the use, to see if the rhs is changed;
+ * if not, check that the copy is in C_IN(b).
+ */
--- /dev/null
+
+/* U S E - D E F I N I T I O N A N A L Y S I S
+ *
+ * U D _ D E F S . C
+ */
+
+#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/map.h"
+#include "../share/locals.h"
+#include "../../../h/em_mnem.h"
+#include "ud_defs.h"
+#include "../share/alloc.h"
+#include "../share/aux.h"
+
+#define BODY_KNOWN(p) (p->p_flags1 & (byte) PF_BODYSEEN)
+#define CHANGE_INDIR(p) (p->p_change->c_flags & CF_INDIR)
+
+short nrdefs; /* total number of definitions */
+short nrexpldefs; /* number of explicit definitions */
+line_p *defs;
+cset *vardefs;
+
+STATIC cset all_globl_defs, all_indir_defs;
+/* auxiliary sets, used by gen_sets */
+
+
+bool does_expl_def(l)
+ line_p l;
+{
+ /* See if instruction l does an explicit definition */
+
+ switch(INSTR(l)) {
+ case op_stl:
+ case op_sdl:
+ case op_ste:
+ case op_sde:
+ case op_inl:
+ case op_del:
+ case op_ine:
+ case op_dee:
+ case op_zrl:
+ case op_zre:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+ /* NOTREACHED */
+}
+
+
+
+bool does_impl_def(l)
+ line_p l;
+{
+ /* See if instruction l does an implicit definition */
+
+ switch(INSTR(l)) {
+ case op_cal:
+ case op_cai:
+ case op_sil:
+ case op_stf:
+ case op_sti:
+ case op_sts:
+ case op_sdf:
+ case op_sar:
+ case op_blm:
+ case op_bls:
+ case op_zrf:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+
+make_defs(p)
+ proc_p p;
+{
+ /* Make a map of all explicit definitions
+ * occurring in p.
+ * Determine the set of explicit definitions
+ * of variable v (i.e. vardefs[v]), for all
+ * v from 1 to nrvars.
+ * For every basic block b, compute CHGVARS(b),
+ * i.e. the set of variables changed in b by an
+ * explicit definition.
+ */
+
+ register bblock_p b;
+ register line_p l;
+ short v, i, cnt = 0;
+ bool found;
+
+ /* first count the number of definitions */
+ for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
+ for (l = b->b_start; l != (line_p) 0 ; l = l->l_next) {
+ if (does_expl_def(l)) {
+ var_nr(l,&v,&found);
+ if (!found) continue; /* no ud for this var */
+ cnt++;
+ }
+ }
+ }
+ nrexpldefs = cnt;
+ /* now allocate the defs table and the vardefs table*/
+ defs = (line_p *) newmap(nrexpldefs);
+ vardefs = (cset *) newmap(nrvars);
+ for (i = 1; i <= nrvars; i++) {
+ vardefs[i] = Cempty_set(nrexpldefs);
+ }
+ cnt = 1;
+ for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
+ CHGVARS(b) =Cempty_set(nrvars);
+ for (l = b->b_start; l != (line_p) 0 ; l = l->l_next) {
+ if (does_expl_def(l)) {
+ var_nr(l,&v,&found);
+ if (!found) continue;
+ assert (v <= nrvars);
+ Cadd(v,&CHGVARS(b));
+ defs[cnt] = l;
+ Cadd(cnt,&vardefs[v]);
+ cnt++;
+ }
+ }
+ }
+}
+
+
+
+STATIC init_gen(nrdefs)
+ short nrdefs;
+{
+ /* Initializing routine of gen_sets. Compute the set
+ * of all implicit definitions to global variables
+ * (all_globl_defs) and the set of all implicit
+ * definition generated by an indirect assignment
+ * through a pointer (all_indir_defs).
+ */
+
+ short v;
+
+ all_globl_defs = Cempty_set(nrdefs);
+ all_indir_defs = Cempty_set(nrdefs);
+ for (v = 1; v <= nrglobals; v++) {
+ Cadd(IMPLICIT_DEF(GLOB_TO_VARNR(v)), &all_globl_defs);
+ Cadd(IMPLICIT_DEF(GLOB_TO_VARNR(v)), &all_indir_defs);
+ }
+ for (v = 1; v <= nrlocals; v++) {
+ if (!IS_REGVAR(locals[v])) {
+ Cadd(IMPLICIT_DEF(LOC_TO_VARNR(v)), &all_indir_defs);
+ }
+ }
+}
+
+
+
+STATIC clean_gen()
+{
+ Cdeleteset(all_globl_defs);
+ Cdeleteset(all_indir_defs);
+}
+
+
+
+STATIC bool same_target(l,defnr)
+ line_p l;
+ short defnr;
+{
+ /* See if l defines the same variable as def */
+
+ line_p def;
+ short v;
+
+ if (IS_IMPL_DEF(defnr)) {
+ /* An implicitly generated definition */
+ v = IMPL_VAR(TO_IMPLICIT(defnr));
+ if (IS_GLOBAL(v)) {
+ return TYPE(l) == OPOBJECT &&
+ OBJ(l)->o_globnr == TO_GLOBAL(v);
+ } else {
+ return TYPE(l) != OPOBJECT &&
+ locals[TO_LOCAL(v)]->lc_off == off_set(l);
+ }
+ }
+ /* explicit definition */
+ def = defs[TO_EXPLICIT(defnr)];
+ if (TYPE(l) == OPOBJECT) {
+ return TYPE(def) == OPOBJECT && OBJ(def) == OBJ(l);
+ } else {
+ return TYPE(def) != OPOBJECT && off_set(def) == off_set(l);
+ }
+}
+
+
+
+STATIC rem_prev_defs(l,gen_p)
+ line_p l;
+ cset *gen_p;
+{
+ /* Remove all definitions in gen that define the
+ * same variable as l.
+ */
+
+ cset gen;
+ Cindex i,next;
+
+ gen = *gen_p;
+ for (i = Cfirst(gen); i != (Cindex) 0; i = next) {
+ next = Cnext(i,gen);
+ if (same_target(l,Celem(i))) {
+ Cremove(Celem(i),gen_p);
+ }
+ }
+}
+
+
+
+
+STATIC impl_globl_defs(p,gen_p)
+ proc_p p;
+ cset *gen_p;
+{
+ /* Add all definitions of global variables
+ * that are generated implicitly by a call
+ * to p to the set gen_p.
+ */
+
+ Cindex i;
+ short v;
+ cset ext = p->p_change->c_ext;
+
+ for (i = Cfirst(ext); i != (Cindex) 0; i = Cnext(i,ext)) {
+ if (( v = omap[Celem(i)]->o_globnr) != (short) 0) {
+ /* the global variable v, for which we do
+ * maintain ud-info is changed by p, so a
+ * definition of v is generated implicitly.
+ */
+ Cadd(IMPLICIT_DEF(GLOB_TO_VARNR(v)),gen_p);
+ }
+ }
+}
+
+
+
+STATIC impl_gen_defs(l,gen_p)
+ line_p l;
+ cset *gen_p;
+{
+ /* Add all definitions generated implicitly by instruction l
+ * to gen_p. l may be a call or some kind of indirect
+ * assignment.
+ */
+
+ proc_p p;
+
+ switch(INSTR(l)) {
+ case op_cal:
+ p = PROC(l);
+ if (BODY_KNOWN(p)) {
+ impl_globl_defs(p,gen_p);
+ if (!CHANGE_INDIR(p)) return;
+ break;
+ }
+ /* else fall through ... */
+ case op_cai:
+ /* Indirect subroutine call or call to
+ * a subroutine whose body is not available.
+ * Assume worst case; all global
+ * variables are changed and
+ * the called proc. does a store-
+ * indirect.
+ */
+ Cjoin(all_globl_defs,gen_p);
+ break;
+ /* default: indir. assignment */
+ }
+ Cjoin(all_indir_defs,gen_p);
+}
+
+
+
+
+gen_sets(p)
+ proc_p p;
+{
+ /* Compute for every basic block b of p the
+ * set GEN(b) of definitions in b (explicit as
+ * well as implicit) that reach the end of b.
+ */
+
+ register bblock_p b;
+ register line_p l;
+ short defnr = 1;
+
+ init_gen(nrdefs); /* compute all_globl_defs and all_indir_defs */
+ for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
+ GEN(b) = Cempty_set(nrdefs);
+ for (l = b->b_start; l != (line_p) 0; l = l->l_next) {
+ if (does_impl_def(l)) {
+ impl_gen_defs(l,&GEN(b));
+ /* add definitions implicitly
+ * generated by subroutine call
+ * or indir. pointer assignment.
+ */
+ } else {
+ if (does_expl_def(l)) {
+ if (defnr <= nrdefs && defs[defnr] == l) {
+ rem_prev_defs(l,&GEN(b));
+ /* previous defs. of same var
+ * don't reach the end of b.
+ */
+ Cadd(EXPL_TO_DEFNR(defnr),&GEN(b));
+ defnr++;
+ }
+ }
+ }
+ }
+ }
+ clean_gen(); /* clean up */
+}
+
+
+
+
+STATIC killed_defs(v,b)
+ short v;
+ bblock_p b;
+{
+ /* Put all definitions of v occurring outside b
+ * in KILL(b). In fact, we also put explicit
+ * definitions occurring in b, but not reaching the
+ * end of b, in KILL(b). This causes no harm.
+ */
+
+ Cindex i;
+ short d;
+
+ for (i = Cfirst(vardefs[v]); i != (Cindex) 0; i = Cnext(i,vardefs[v])) {
+ d = Celem(i); /* d is an explicit definition of v */
+ if (!Cis_elem(EXPL_TO_DEFNR(d),GEN(b))) {
+ Cadd(EXPL_TO_DEFNR(d),&KILL(b));
+ }
+ }
+ /* Also add implicit definition of v to KILL(b) */
+ Cadd(IMPLICIT_DEF(v),&KILL(b));
+}
+
+
+
+
+kill_sets(p)
+ proc_p p;
+{
+ /* For every basic block b of p compute the set
+ * KILL(b) of definitions outside b that define
+ * variables redefined by b.
+ * KILL(b) contains explicit as well as implicit
+ * definitions.
+ */
+
+ register bblock_p b;
+ Cindex i;
+ short v;
+
+ for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
+ KILL(b) = Cempty_set(nrdefs);
+ for (i = Cfirst(CHGVARS(b)); i != (Cindex) 0;
+ i = Cnext(i,CHGVARS(b))) {
+ v = Celem(i); /* v is a variable changed in b */
+ killed_defs(v,b);
+ }
+ }
+}
--- /dev/null
+/* U S E - D E F I N I T I O N A N A L Y S I S
+ *
+ * U D _ D E F S . H
+ */
+
+extern short nrdefs; /* total number of definitions */
+extern short nrexpldefs; /* number of explicit definitions */
+extern line_p *defs; /* map of explicit definitions */
+extern cset *vardefs; /* set of explicit defs. of all variables */
+
+extern make_defs(); /* (proc_p p)
+ * Compute defs[], vardefs[]
+ * and CHGVARS(b) (for every b).
+ */
+extern gen_sets(); /* (proc_p p)
+ * Compute GEN(b) (for every b).
+ */
+extern kill_sets(); /* (proc_p p)
+ *Compute KILL(b) (for every b).
+ */
+extern bool does_expl_def(); /* (line_p l)
+ * See if instruction l does an explicit
+ * definition (e.g. a STL).
+ */
+extern bool does_impl_def(); /* (line_p l)
+ * See if instruction l does an implicit
+ * definition (e.g. a CAL).
+ */
+
+
+/* Two kinds of definitions exist:
+ * - an explicit definition is an assignment to a single
+ * variable (e.g. a STL, STE, INE).
+ * - an implicit definition is an assignment to a variable
+ * performed via a subroutine call or an
+ * indirect assignment (through a pointer).
+ * Every explicit definition has an 'explicit definition number',
+ * which is its index in the 'defs' table.
+ * Every implicit definition has an 'implicit definition number',
+ * which is the 'variable number' of the changed variable.
+ * Every such definition also has a 'definition number'.
+ * Conversions exist between these numbers.
+ */
+
+#define TO_EXPLICIT(defnr) (defnr - nrvars)
+#define TO_IMPLICIT(defnr) (defnr)
+#define EXPL_TO_DEFNR(explnr) (explnr + nrvars)
+#define IMPL_TO_DEFNR(implnr) (implnr)
+#define IMPLICIT_DEF(v) (v)
+#define IMPL_VAR(defnr) (defnr)
+#define IS_IMPL_DEF(defnr) (defnr <= nrvars)
--- /dev/null
+/* U S E - D E F I N I T I O N A N A L Y S I S
+ *
+ * U D _ L O C A L S . H
+ */
+
+extern local_p *locals; /* table of locals, index is local-number */
+extern short nrlocals; /* number of locals for which we keep ud-info */
+
+extern make_localtab(); /* (proc_p p)
+ * Analyse the text of procedure p to determine
+ * which local variable p has. Make a table of
+ * these variables ('locals') and count them
+ * ('nrlocals'). Also collect register messages.
+ */
+extern var_nr(); /* (line_p l; short *nr_out;bool *found_out)
+ * Compute the 'variable number' of the
+ * variable referenced by EM instruction l.
+ */