--- /dev/null
+
+EMH=../../../h
+EML=../../../lib
+SHARE=../share
+OBJECTS=cj.o
+SHOBJECTS=$(SHARE)/get.o $(SHARE)/put.o $(SHARE)/alloc.o $(SHARE)/global.o $(SHARE)/debug.o $(SHARE)/files.o $(SHARE)/map.o $(SHARE)/lset.o $(SHARE)/cset.o $(SHARE)/aux.o $(SHARE)/stack_chg.o $(SHARE)/go.o
+MSHOBJECTS=$(SHARE)/get.m $(SHARE)/put.m $(SHARE)/alloc.m $(SHARE)/global.m $(SHARE)/debug.m $(SHARE)/files.m $(SHARE)/map.m $(SHARE)/lset.m $(SHARE)/cset.m $(SHARE)/aux.m $(SHARE)/stack_chg.m
+SRC=cj.c
+.SUFFIXES: .m
+
+.c.o:
+ cc $(CFLAGS) -c $<
+.c.m:
+ ack -O -L -c.m $(CFLAGS) $<
+all: $(OBJECTS)
+cj: \
+ $(OBJECTS) $(SHOBJECTS)
+ cc -o cj -i $(OBJECTS) $(SHOBJECTS) $(EML)/em_data.a
+lpr:
+ pr $(SRC) | lpr
+# the next lines are generated automatically
+# AUTOAUTOAUTOAUTOAUTOAUTO
+
--- /dev/null
+/* C R O S S J U M P I N G
+ *
+ * CJ.H
+ *
+ */
+
+#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/aux.h"
+#include "../share/def.h"
+#include "../share/stack_chg.h"
+#include "../share/go.h"
+#include "../../../h/em_mnem.h"
+#include "../../../h/em_spec.h"
+
+
+/* Cross jumping performs optimzations like:
+ *
+ * if cond then goto L1; if cond then goto L1
+ * S1; -----> S1;
+ * S2; goto L3;
+ * goto L2; L1:
+ * L1: S3;
+ * S3; L3:
+ * S2; S2;
+ * L2:
+ *
+ * CJ looks for two basic blocks b1 and b2 with the following properties:
+ * - there exists a basic block S such that SUCC(b1) = SUCC(b2) = {S}
+ * (so both have only 1 successor)
+ * - the last N (N > 0) instructions of b1 and b2, not counting a possible
+ * BRAnch instruction, are the same.
+ * As a result of the first condition, at least of the two blocks must end
+ * on an (unconditional) BRAnch instruction. If both end on a BRA, one block
+ * is chosen at random. Assume this block is b1. A new label L is put just
+ * before the N common instructions of block b2 (so this block is split
+ * into two). The BRA of b1 is changed into a BRA L. So dynamically the same
+ * instructions are executed in a slightly different order; yet the size of
+ * the code has become smaller.
+ */
+
+
+STATIC int Scj; /* number of optimizations found */
+
+
+
+#define DLINK(l1,l2) l1->l_next=l2; l2->l_prev=l1
+
+
+STATIC bool same_instr(l1,l2)
+ line_p l1,l2;
+{
+ /* See if l1 and l2 are the same instruction */
+
+ if (l1 == 0 || l2 == 0 || TYPE(l1) != TYPE(l2)) return FALSE;
+ if (INSTR(l1) != INSTR(l2)) return FALSE;
+ switch(TYPE(l1)) {
+ case OPSHORT: return SHORT(l1) == SHORT(l2);
+ case OPOFFSET: return OFFSET(l1) == OFFSET(l2);
+ case OPPROC: return PROC(l1) == PROC(l2);
+ case OPOBJECT: return OBJ(l1) == OBJ(l2);
+ case OPINSTRLAB: return INSTRLAB(l1) == INSTRLAB(l2);
+ case OPNO: return TRUE;
+ default: return FALSE;
+ }
+}
+
+
+
+STATIC line_p last_mnem(b)
+ bblock_p b;
+{
+ /* Determine the last line of a list */
+
+ register line_p l;
+
+ for (l = b->b_start; l->l_next != (line_p) 0; l = l->l_next);
+ while (INSTR(l) < sp_fmnem || INSTR(l) > sp_lmnem) l = PREV(l);
+ return l;
+}
+
+
+STATIC bool is_desirable(text)
+ line_p text;
+{
+ /* We avoid to generate a BRAnch in the middle of some expression,
+ * as the code generator will write the contents of the fakestack
+ * to the real stack if it encounters a BRA. We do not avoid to
+ * split the parameter-pushing code of a subroutine call into two,
+ * as the parameters are pushed on the real stack anyway.
+ * So e.g. "LOL a ; LOL b; ADI" will not be split, but
+ * "LOL a; LOL b; CAL f" may be split.
+ */
+
+ line_p l;
+ bool ok;
+ int stack_diff,pop,push;
+
+ stack_diff = 0;
+ for (l = text; l != (line_p) 0; l = l->l_next) {
+ switch(INSTR(l)) {
+ case op_cal:
+ case op_asp:
+ case op_bra:
+ return TRUE;
+ }
+ line_change(l,&ok,&pop,&push);
+ /* printf("instr %d, pop %d, push %d, ok %d\n",INSTR(l),pop,push,ok); */
+ if (!ok || (stack_diff -= pop) < 0) {
+ return FALSE;
+ } else {
+ stack_diff += push;
+ }
+ }
+ return TRUE;
+}
+
+
+STATIC cp_loops(b1,b2)
+ bblock_p b1,b2;
+{
+ /* Copy the loopset of b2 to b1 */
+
+ Lindex i;
+ loop_p lp;
+ for (i = Lfirst(b2->b_loops); i != (Lindex) 0;
+ i = Lnext(i,b2->b_loops)) {
+ lp = (loop_p) Lelem(i);
+ Ladd(lp,&b1->b_loops);
+ }
+}
+
+
+STATIC jump_cross(l1,l2,b1,b2)
+ line_p l1,l2;
+ bblock_p b1,b2;
+{
+ /* A cross-jump from block b2 to block b1 is found; the code in
+ * block b2 from line l2 up to the BRAnch is removed; block b1 is
+ * split into two; the second part consists of a new label
+ * followed by the code from l1 till the end of the block.
+ */
+
+ line_p l;
+ bblock_p b;
+ bblock_p s;
+
+ /* First adjust the control flow graph */
+ b = freshblock(); /* create a new basic block */
+ b->b_succ = b1->b_succ;
+ /* SUCC(b1) = {b} */
+ b1->b_succ = Lempty_set(); Ladd(b,&b1->b_succ);
+ /* SUCC(b2) = {b} */
+ Ldeleteset(b2->b_succ); b2->b_succ = Lempty_set(); Ladd(b,&b2->b_succ);
+ /* PRED(b) = {b1,b2} */
+ b->b_pred = Lempty_set(); Ladd(b1,&b->b_pred); Ladd(b2,&b->b_pred);
+ /* PRED(SUCC(b)) := PRED(SUCC(b)) - {b1,b2} + {b} */
+ assert(Lnrelems(b->b_succ) == 1);
+ s = (bblock_p) Lelem(Lfirst(b->b_succ));
+ Lremove(b1,&s->b_pred); Lremove(b2,&s->b_pred); Ladd(b,&s->b_pred);
+ cp_loops(b,b1);
+ b->b_idom = common_dom(b1,b2);
+ b->b_flags = b1->b_flags;
+ b->b_next = b1->b_next;
+ b1->b_next = b;
+
+ /* Now adjust the EM text */
+ l = PREV(l1);
+ if (l == (line_p) 0) {
+ b1->b_start = (line_p) 0;
+ } else {
+ l->l_next = (line_p) 0;
+ }
+ l = newline(OPINSTRLAB);
+ l->l_instr = op_lab;
+ INSTRLAB(l) = freshlabel();
+ DLINK(l,l1);
+ b->b_start = l;
+ for (l = l2; INSTR(l) != op_bra; l = l->l_next) {
+ assert (l != (line_p) 0);
+ rm_line(l,b2);
+ }
+ INSTRLAB(l) = INSTRLAB(b->b_start);
+}
+
+
+STATIC bool try_tail(b1,b2)
+ bblock_p b1,b2;
+{
+ /* See if b1 and b2 end on the same sequence of instructions */
+
+ line_p l1,l2;
+ bblock_p b = (bblock_p) 0;
+ int cnt = 0;
+ /* printf("try block %d and %d\n",b1->b_id,b2->b_id); */
+
+ if (b1->b_start == (line_p) 0 || b2->b_start == (line_p) 0) return FALSE;
+ l1 = last_mnem(b1);
+ l2 = last_mnem(b2);
+ /* printf("consider:\n"); showinstr(l1); showinstr(l2); */
+ if (INSTR(l1) == op_bra) {
+ b = b1;
+ l1 = PREV(l1);
+ }
+ if (INSTR(l2) == op_bra) {
+ b = b2;
+ l2 = PREV(l2);
+ }
+ assert(b != (bblock_p) 0);
+ while(same_instr(l1,l2)) {
+ cnt++;
+ l1 = PREV(l1);
+ l2 = PREV(l2);
+ /* printf("consider:\n"); showinstr(l1); showinstr(l2); */
+ }
+ if (cnt >= 1) {
+ l1 = (l1 == 0 ? b1->b_start : l1->l_next);
+ l2 = (l2 == 0 ? b2->b_start : l2->l_next);
+ if (is_desirable(l1)) {
+ if (b == b1) {
+ jump_cross(l2,l1,b2,b1);
+ Scj++;
+ } else {
+ jump_cross(l1,l2,b1,b2);
+ Scj++;
+ }
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+
+STATIC bool try_pred(b)
+ bblock_p b;
+{
+ /* See if there is any pair (b1,b2), both in PRED(b) for
+ * which we can perform cross jumping.
+ */
+
+ register bblock_p b1,b2;
+ register Lindex i,j;
+ lset s = b->b_pred;
+
+ for (i = Lfirst(s); i != (Lindex) 0; i = Lnext(i,s)) {
+ b1 = (bblock_p) Lelem(i);
+ if (Lnrelems(b1->b_succ) != 1) continue;
+ for (j = Lfirst(s); j != (Lindex) 0; j = Lnext(j,s)) {
+ b2 = (bblock_p) Lelem(j);
+ if (b1 != b2 && Lnrelems(b2->b_succ) == 1) {
+ if (try_tail(b1,b2)) return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+
+
+cj_optimize(p)
+ proc_p p;
+{
+ /* Perform cross jumping for procedure p.
+ * In case cases a cross-jumping optimization which give
+ * new opportunities for further cross-jumping optimizations.
+ * Hence we repeat the whole process for the entire procedure,
+ * untill we find no further optimizations.
+ */
+
+ bblock_p b;
+ bool changes = TRUE;
+
+ while(changes) {
+ changes = FALSE;
+ b = p->p_start;
+ while (b != (bblock_p) 0) {
+ if (try_pred(b)) {
+ changes = TRUE;
+ } else {
+ b = b->b_next;
+ }
+ }
+ }
+}
+
+
+main(argc,argv)
+ int argc;
+ char *argv[];
+{
+ go(argc,argv,no_action,cj_optimize,no_action,no_action);
+ report("cross jumps",Scj);
+ exit(0);
+}
+
+
+
+/******
+ * Debugging stuff
+ */
+
+extern char em_mnem[]; /* The mnemonics of the EM instructions. */
+
+STATIC showinstr(lnp) line_p lnp; {
+
+ /* Makes the instruction in `lnp' human readable. Only lines that
+ * can occur in expressions that are going to be eliminated are
+ * properly handled.
+ */
+ if (lnp == 0) return;
+ if (INSTR(lnp) < sp_fmnem || INSTR(lnp) > sp_lmnem) {
+ printf("\t*** ?\n");
+ return;
+ }
+
+ printf("\t%s", &em_mnem[4 * (INSTR(lnp)-sp_fmnem)]);
+ switch (TYPE(lnp)) {
+ case OPNO:
+ break;
+ case OPSHORT:
+ printf(" %d", SHORT(lnp)); break;
+ case OPOBJECT:
+ printf(" %d", OBJ(lnp)->o_id); break;
+ case OPOFFSET:
+ printf(" %D", OFFSET(lnp)); break;
+ default:
+ printf(" ?"); break;
+ }
+ printf("\n");
+} /* showinstr */
+
+
+STATIC print_list(list,b1,b2,p)
+ line_p list;
+ bblock_p b1,b2;
+ proc_p p;
+{
+ line_p l;
+ printf("block %d and %d of proc %d:\n",b1->b_id,b2->b_id,p->p_id);
+ for (l = list; l != 0; l = l->l_next) {
+ showinstr(l);
+ }
+}
--- /dev/null
+/* S H A R E D F I L E
+ *
+ * A L L O C . C
+ */
+
+
+
+#include <stdio.h>
+#include "types.h"
+#include "debug.h"
+#include "alloc.h"
+
+
+short * myalloc();
+short * malloc();
+
+#ifdef DEBUG
+
+STATIC unsigned maxuse, curruse;
+
+short *newcore(size)
+ int size;
+{
+ if ((curruse += (unsigned) (size+2)) > maxuse) maxuse = curruse;
+ return myalloc(size);
+}
+
+oldcore(p,size)
+ short *p;
+ int size;
+{
+ curruse -= (size+2);
+ free(p);
+}
+
+coreusage()
+{
+ fprintf(stderr,"Maximal core usage (excl. buffers):%u\n",maxuse);
+}
+
+#endif
+
+
+/*
+ * The following two sizetables contain the sizes of the various kinds
+ * of line and argument structures.
+ * The assumption when making the tables was that every non-byte object
+ * had to be aligned on an even boundary. On machines where alignment
+ * is worse ( for example a long has to be aligned on a longword bound )
+ * these tables should be revised.
+ * A wasteful but safe approach is to replace every line of them by
+ * sizeof(line_t)
+ * and
+ * sizeof(arg_t)
+ * respectively.
+ */
+
+#ifndef NOTCOMPACT
+int lsizetab[] = {
+ 2*sizeof(line_p)+2*sizeof(byte),
+ 2*sizeof(line_p)+2*sizeof(byte)+sizeof(short),
+ 2*sizeof(line_p)+2*sizeof(byte)+sizeof(offset),
+ 2*sizeof(line_p)+2*sizeof(byte)+sizeof(lab_id),
+ 2*sizeof(line_p)+2*sizeof(byte)+sizeof(obj_p),
+ 2*sizeof(line_p)+2*sizeof(byte)+sizeof(proc_p),
+ 2*sizeof(line_p)+2*sizeof(byte)+sizeof(arg_p),
+};
+
+int asizetab[] = {
+ sizeof(arg_p)+sizeof(short)+sizeof(offset),
+ sizeof(arg_p)+sizeof(short)+sizeof(lab_id),
+ sizeof(arg_p)+sizeof(short)+sizeof(obj_p),
+ sizeof(arg_p)+sizeof(short)+sizeof(proc_p),
+ sizeof(arg_p)+sizeof(short)+sizeof(argb_t),
+ sizeof(arg_p)+sizeof(short)+sizeof(short)+sizeof(argb_t),
+ sizeof(arg_p)+sizeof(short)+sizeof(short)+sizeof(argb_t),
+ sizeof(arg_p)+sizeof(short)+sizeof(short)+sizeof(argb_t)
+};
+#else
+int lsizetab[] = {
+ sizeof(struct line),
+ sizeof(struct line),
+ sizeof(struct line),
+ sizeof(struct line),
+ sizeof(struct line),
+ sizeof(struct line),
+ sizeof(struct line)
+};
+
+int asizetab[] = {
+ sizeof (struct arg),
+ sizeof (struct arg),
+ sizeof (struct arg),
+ sizeof (struct arg),
+ sizeof (struct arg),
+ sizeof (struct arg),
+ sizeof (struct arg),
+ sizeof (struct arg)
+};
+#endif
+
+/*
+ * alloc routines:
+ * Two parts:
+ * 1) typed alloc and free routines
+ * 2) untyped raw core allocation
+ */
+
+/*
+ * PART 1
+ */
+
+line_p newline(optyp) int optyp; {
+ register line_p lnp;
+ register kind=optyp;
+
+ lnp = (line_p) newcore(lsizetab[kind]);
+ TYPE(lnp) = optyp;
+ return(lnp);
+}
+
+oldline(lnp) register line_p lnp; {
+ register kind=TYPE(lnp)&BMASK;
+
+ if (kind == OPLIST)
+ oldargs(ARG(lnp));
+ oldcore((short *) lnp,lsizetab[kind]);
+}
+
+arg_p newarg(kind) int kind; {
+ register arg_p ap;
+
+ ap = (arg_p) newcore(asizetab[kind]);
+ ap->a_type = kind;
+ return(ap);
+}
+
+oldargs(ap) register arg_p ap; {
+ register arg_p next;
+
+ while (ap != (arg_p) 0) {
+ next = ap->a_next;
+ switch(ap->a_type) {
+ case ARGSTRING:
+ oldargb(ap->a_a.a_string.ab_next);
+ break;
+ case ARGICN:
+ case ARGUCN:
+ case ARGFCN:
+ oldargb(ap->a_a.a_con.ac_con.ab_next);
+ break;
+ }
+ oldcore((short *) ap,asizetab[ap->a_type]);
+ ap = next;
+ }
+}
+
+oldargb(abp) register argb_p abp; {
+ register argb_p next;
+
+ while (abp != (argb_p) 0) {
+ next = abp->ab_next;
+ oldcore((short *) abp,sizeof (argb_t));
+ abp = next;
+ }
+}
+
+num_p newnum() {
+
+ return((num_p) newcore(sizeof(struct num)));
+}
+
+oldnum(lp) num_p lp; {
+
+ oldcore((short *) lp,sizeof(struct num));
+}
+
+
+sym_p newsym() {
+
+ return((sym_p) newcore(sizeof(struct sym)));
+}
+
+oldsym(sp) sym_p sp; {
+ oldcore((short *) sp,sizeof(struct sym));
+}
+
+
+prc_p newprc() {
+ return((prc_p) newcore(sizeof(struct prc)));
+}
+
+
+oldprc(pp) prc_p pp; {
+ oldcore((short *) pp,sizeof(struct prc));
+}
+
+
+argb_p newargb() {
+
+ return((argb_p) newcore(sizeof(argb_t)));
+}
+
+obj_p newobject() {
+ return((obj_p) newcore(sizeof(struct obj)));
+}
+
+oldobjects(op) register obj_p op; {
+ register obj_p next;
+
+ while (op != (obj_p) 0) {
+ next = op->o_next;
+ oldcore((short *) op, sizeof(struct obj));
+ op = next;
+ }
+}
+
+proc_p newproc() {
+ return((proc_p) newcore(sizeof(struct proc)));
+}
+
+oldproc(p) proc_p p; {
+ oldcore((short *) p, sizeof(struct proc));
+}
+
+dblock_p newdblock() {
+ return((dblock_p) newcore(sizeof(struct dblock)));
+}
+
+olddblock(dbl) dblock_p dbl; {
+ oldobjects(dbl->d_objlist);
+ oldargs(dbl->d_values);
+ oldcore((short *) dbl, sizeof(struct dblock));
+}
+
+
+bblock_p newbblock() {
+ return((bblock_p) newcore(sizeof(struct bblock)));
+}
+
+oldbblock(b) bblock_p b; {
+ oldcore((short *) b, sizeof(struct bblock));
+}
+
+
+short **newmap(length) short length; {
+ return((short **) newcore((length+1) * sizeof(short *)));
+}
+
+oldmap(mp,length) short **mp, length; {
+ oldcore((short *) mp, (length+1) * sizeof(short *));
+}
+
+
+elem_p newelem() {
+ return((elem_p) newcore(sizeof(struct elemholder)));
+}
+
+
+oldelem(ep) elem_p ep; {
+ oldcore((short *) ep, sizeof(struct elemholder));
+}
+
+
+cset newbitvect(n) short n; {
+ return((cset) newcore((n-1)*sizeof(int) + sizeof(struct bitvector)));
+ /* sizeof(struct bitvector) equals to the size of a struct with
+ * one short, followed by one ALLIGNED int. So the above statement
+ * also works e.g. on a VAX.
+ */
+}
+
+oldbitvect(s,n) cset s; short n; {
+ oldcore((short *) s, (n-1)*sizeof(int) + sizeof(struct bitvector));
+}
+
+
+loop_p newloop() {
+ return((loop_p) newcore(sizeof(struct loop)));
+}
+
+
+oldloop(lp) loop_p lp; {
+ oldcore((short *) lp, sizeof(struct loop));
+}
+
+use_p newuse() {
+ return((use_p) newcore(sizeof(struct use)));
+}
+
+
+olduse(u) use_p u; {
+ oldcore((short *) u, sizeof(struct use));
+}
+
+
+change_p newchange() {
+ return((change_p) newcore(sizeof(struct change)));
+}
+
+
+oldchange(c) change_p c; {
+ oldcore((short *) c, sizeof(struct change));
+}
+
+
+iv_p newiv() {
+ return((iv_p) newcore(sizeof(struct iv)));
+}
+
+oldiv(i) iv_p i; {
+ oldcore((short *) i, sizeof(struct iv));
+}
+
+
+code_p newcinfo() {
+ return((code_p) newcore(sizeof(struct code_info)));
+}
+
+oldcinfo(c) code_p c; {
+ oldcore((short *) c, sizeof(struct code_info));
+}
+
+
+call_p newcall() {
+ return((call_p) newcore(sizeof(struct call)));
+}
+
+oldcall(c) call_p c; {
+ oldcore((short *) c, sizeof(struct call));
+}
+
+
+actual_p newactual() {
+ return((actual_p) newcore(sizeof(struct actual)));
+}
+
+oldactual(a) actual_p a; {
+ oldcore((short *) a, sizeof(struct actual));
+}
+
+formal_p newformal() {
+ return((formal_p) newcore(sizeof(struct formal)));
+}
+
+oldformal(f) formal_p f; {
+ oldcore((short *) f, sizeof(struct formal));
+}
+
+calcnt_p newcalcnt() {
+ return ((calcnt_p) newcore(sizeof(struct calcnt)));
+}
+
+oldcalcnt(cc) calcnt_p cc; {
+ oldcore((short *) cc, sizeof(struct calcnt));
+}
+
+local_p newlocal() {
+ return ((local_p) newcore(sizeof(struct local)));
+}
+
+oldlocal(lc) local_p lc; {
+ oldcore((short *) lc, sizeof(struct local));
+}
+
+
+short *newtable(length) short length; {
+ return((short *) newcore((length+1) * sizeof(short)));
+}
+
+oldtable(mp,length) short **mp, length; {
+ oldcore((short *) mp, (length+1) * sizeof(short));
+}
+
+char **newnametab(tablen,namelen)
+ short tablen,namelen;
+{
+ register char **np, **tab;
+
+ tab = (char **) newmap(tablen);
+ for (np = &tab[1]; np <= &tab[tablen]; np++) {
+ *np = (char *) newcore(namelen);
+ }
+ return tab;
+}
+
+bext_p newcfbx() {
+ return ((bext_p) newcore(sizeof(struct bext_cf)));
+}
+
+oldcfbx(bxp) bext_p bxp; {
+ oldcore((short *) bxp,sizeof(struct bext_cf));
+}
+
+lpext_p newcflpx() {
+ return ((lpext_p) newcore (sizeof(struct lpext_cf)));
+}
+
+oldcflpx(lxp) lpext_p lxp; {
+ oldcore((short *) lxp,sizeof(struct lpext_cf));
+}
+
+lpext_p newsrlpx() {
+ return ((lpext_p) newcore (sizeof(struct lpext_sr)));
+}
+
+oldsrlpx(lxp) lpext_p lxp; {
+ oldcore((short *) lxp,sizeof(struct lpext_sr));
+}
+
+pext_p newilpx() {
+ return ((pext_p) newcore(sizeof(struct pext_il)));
+}
+
+oldilpx(pxp) pext_p pxp; {
+ oldcore((short *) pxp,sizeof(struct pext_il));
+}
+
+
+bext_p newudbx() {
+ return ((bext_p) newcore(sizeof(struct bext_ud)));
+}
+
+oldudbx(bxp) bext_p bxp; {
+ oldcore((short *) bxp,sizeof(struct bext_ud));
+}
+
+bext_p newlvbx() {
+ return ((bext_p) newcore(sizeof(struct bext_lv)));
+}
+
+oldlvbx(bxp) bext_p bxp; {
+ oldcore((short *) bxp,sizeof(struct bext_lv));
+}
+
+lpext_p newralpx() {
+ return ((lpext_p) newcore (sizeof(struct lpext_ra)));
+}
+
+oldralpx(lxp) lpext_p lxp; {
+ oldcore((short *) lxp,sizeof(struct lpext_ra));
+}
+
+bext_p newrabx() {
+ return ((bext_p) newcore(sizeof(struct bext_ra)));
+}
+
+oldrabx(bxp) bext_p bxp; {
+ oldcore((short *) bxp,sizeof(struct bext_ra));
+}
+
+
+cond_p newcondtab(l) int l;
+{
+ return (cond_p) newcore(l * (sizeof (struct cond_tab)));
+}
+
+oldcondtab(tab) cond_p tab;
+{
+ int i;
+ for (i = 0; tab[i].mc_cond != DEFAULT; i++);
+ oldcore((short *) tab,((i+1) * sizeof (struct cond_tab)));
+}
+
+
+short *myalloc(size) register size; {
+ register short *p,*q;
+
+ p = malloc(size);
+ if (p == 0)
+ error("out of memory");
+ for(q=p;size>0;size -= sizeof(short))
+ *q++ = 0;
+ return(p);
+}
--- /dev/null
+/* I N T E R M E D I A T E C O D E
+ *
+ * C O R E A L L O C A T I O N A N D D E A L L O C A T I O N
+ */
+
+#ifdef DEBUG
+extern short *newcore();
+extern oldcore();
+#else
+extern short *myalloc();
+#define newcore(size) myalloc(size)
+#define oldcore(p,size) free(p)
+#endif
+
+#define newstruct(t) (newcore (sizeof (struct t)))
+#define oldstruct(t,p) oldcore((short *) p,sizeof (struct t))
+
+extern line_p newline(); /* (byte optype) */
+extern dblock_p newdblock();
+extern obj_p newobject();
+extern proc_p newproc();
+extern arg_p newarg(); /* (byte argtype) */
+extern argb_p newargb();
+extern bblock_p newbblock();
+extern short **newmap(); /* (short length) */
+extern elem_p newelem();
+extern cset newbitvect(); /* (short nrbytes) */
+extern loop_p newloop();
+extern use_p newuse();
+extern change_p newchange();
+extern cond_p newcondtab();
+
+
+extern oldline() ;
+extern oldargs() ;
+extern oldargb() ;
+extern oldobjects() ;
+extern oldproc() ;
+extern olddblock() ;
+extern oldbblock();
+extern oldmap();
+extern oldelem();
+extern oldbitvect(); /* (cset s, short nrbytes) */
+extern oldloop();
+extern olduse();
+extern oldchange();
+extern oldcondtab();
+
+extern sym_p newsym();
+extern prc_p newprc();
+extern num_p newnum();
+extern oldnum() ;
+extern oldsym();
+extern oldprc();
+extern iv_p newiv();
+extern oldiv();
+extern code_p newcinfo();
+extern oldcinfo();
+extern call_p newcall();
+extern oldcall();
+extern actual_p newactual();
+extern oldactual();
+extern formal_p newformal();
+extern oldformal();
+extern calcnt_p newcalcnt();
+extern oldcalcnt();
+extern local_p newlocal();
+extern oldlocal();
+extern short *newtable();
+extern oldtable();
+extern char **newnametab();
+extern bext_p newcfbx();
+extern oldcfbx();
+extern lpext_p newcflpx();
+extern oldcflpx();
+extern lpext_p newsrlpx();
+extern oldsrlpx();
+extern pext_p newilpx();
+extern oldilpx();
+extern bext_p newudbx();
+extern oldudbx();
+extern bext_p newlvbx();
+extern oldlvbx();
+extern bext_p newrabx();
+extern oldrabx();
+extern lpext_p newralpx();
+extern oldralpx();
--- /dev/null
+/* S H A R E D F I L E
+ *
+ * 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/global.h"
+#include "../share/alloc.h"
+#include "../share/aux.h"
+#include "../share/map.h"
+#include "../share/lset.h"
+#include "../../../h/em_mes.h"
+#include "../../../h/em_pseu.h"
+
+offset off_set(lnp)
+ line_p lnp;
+{
+ switch(lnp->l_optype) {
+ case OPSHORT:
+ return (offset) SHORT(lnp);
+ case OPOFFSET:
+ return OFFSET(lnp);
+ default:
+ assert(FALSE);
+ }
+ /* NOTREACHED */
+}
+
+
+
+
+offset aoff(ap,n)
+ register arg_p ap;
+{
+ while (n>0) {
+ if (ap != (arg_p) 0)
+ ap = ap->a_next;
+ n--;
+ }
+ if (ap == (arg_p) 0)
+ error("too few parameters");
+ if (ap->a_type != ARGOFF)
+ error("offset expected");
+ return(ap->a_a.a_offset);
+}
+
+
+offset tmplocal(p,size)
+ proc_p p;
+ int size;
+{
+ /* Allocate a new local variable in the stack frame of p */
+
+ p->p_localbytes += (offset) size;
+ return -(p->p_localbytes);
+}
+
+
+
+
+line_p int_line(off)
+ offset off;
+{
+ /* Allocate a line struct of type OPSHORT or OPOFFSET,
+ * whichever one fits best.
+ */
+
+ line_p lnp;
+
+ if ((short) off == off) {
+ /* fits in a short */
+ lnp = newline(OPSHORT);
+ SHORT(lnp) = (short) off;
+ } else {
+ lnp = newline(OPOFFSET);
+ OFFSET(lnp) = off;
+ }
+ return lnp;
+}
+
+
+
+line_p reg_mes(tmp,size,typ,score)
+ offset tmp;
+ short size;
+ int typ,score;
+{
+ /* Generate a register message */
+
+ line_p l;
+ arg_p a;
+
+#define NEXTARG(a,val) a->a_next = newarg(ARGOFF); a = a->a_next; \
+ a->a_a.a_offset = val
+ l = newline(OPLIST);
+ l->l_instr = ps_mes;
+ a = ARG(l) = newarg(ARGOFF);
+ a->a_a.a_offset = ms_reg;
+ NEXTARG(a,tmp);
+ NEXTARG(a,size);
+ NEXTARG(a,typ);
+ NEXTARG(a,score);
+ return l;
+}
+
+
+bool dom(b1,b2)
+ bblock_p b1,b2;
+{
+ /* See if b1 dominates b2. Note that a block always
+ * dominates itself.
+ */
+
+ register bblock_p b;
+
+ for (b = b2; b != (bblock_p) 0; b = b->b_idom) {
+ /* See if b1 is a (not necessarily proper) ancestor
+ * of b2 in the immediate dominator tree.
+ */
+ if (b == b1) return TRUE;
+ }
+ return FALSE;
+}
+
+
+bblock_p common_dom(a,b)
+ bblock_p a,b;
+{
+ /* find a basic block that dominates a as well as b;
+ * note that a basic block also dominates itself.
+ */
+
+ assert (a != (bblock_p) 0);
+ assert (b != (bblock_p) 0);
+ if (dom(a,b)) {
+ return a;
+ } else {
+ if (dom(b,a)) {
+ return b;
+ } else {
+ return common_dom(a->b_idom,b->b_idom);
+ }
+ }
+}
+
+#define R time_space_ratio
+
+short add_timespace(time,space)
+ short time,space;
+{
+ /* Add together a time and space, using the time_space_ratio
+ * parameter that may be set by the user, indicating the need
+ * to optimize for time, space or something in between.
+ */
+
+ return (R * time + (100 - R) * space) / 100;
+}
+
+
+
+rm_line(l,b)
+ line_p l;
+ bblock_p b;
+{
+ if (b->b_start == l) {
+ b->b_start = l->l_next;
+ } else {
+ PREV(l)->l_next = l->l_next;
+ }
+ if (l->l_next != (line_p) 0) {
+ PREV(l->l_next) = PREV(l);
+ }
+ oldline(l);
+}
+
+
+
+
+appnd_line(l1,l2)
+ line_p l1,l2;
+{
+ /* Put l1 after l2 */
+
+ PREV(l1) = l2;
+ l1->l_next = l2->l_next;
+ l2->l_next = l1;
+ if (l1->l_next != (line_p) 0) {
+ PREV(l1->l_next) = l1;
+ }
+}
+
+
+
+line_p last_instr(b)
+ bblock_p b;
+{
+ /* Determine the last line of a list */
+
+ register line_p l = b->b_start;
+
+ if (l == (line_p) 0) return (line_p) 0;
+ while (l->l_next != (line_p) 0) l = l->l_next;
+ return l;
+}
+
+
+
+
+line_p find_mesreg(off)
+ offset off;
+{
+ /* Find the register message for the local with the given offset */
+
+ Lindex li;
+ line_p l;
+
+ for (li = Lfirst(mesregs); li != (Lindex) 0; li = Lnext(li,mesregs)) {
+ l = (line_p) Lelem(li);
+ if (aoff(ARG(l),1) == off) return l;
+ }
+ return (line_p) 0;
+}
+
+
+bool is_regvar(off)
+ offset off;
+{
+ return find_mesreg(off) != (line_p) 0;
+}
+
+
+
+offset regv_arg(off,n)
+ offset off;
+ int n;
+{
+ /* fetch the n'th argument of the register message of the
+ * local variable at offset off;
+ */
+
+ line_p x = find_mesreg(off);
+ assert (x != (line_p) 0);
+ return aoff(ARG(x),n);
+}
--- /dev/null
+/* S H A R E D
+ *
+ * A U X I L I A R Y R O U T I N E S
+ *
+ */
+
+
+extern offset off_set(); /* (line_p lnp)
+ * lnp has a SHORT or OFFSET operand. Return
+ * the value of this operand as an offset.
+ */
+extern offset aoff(); /* (arg_p list; int n)
+ * Determine the offset field of the
+ * n'th argument in the list (this argument
+ * must have type ARGOFF). Start counting at 0.
+ */
+extern offset tmplocal(); /* (proc_p p, int size)
+ * Allocate a new local variable in the
+ * stack frame of p.
+ */
+line_p int_line(); /* (offset off)
+ * Allocate a line struct of type OPSHORT
+ * or OPOFFSET, whichever one fits best.
+ */
+extern line_p reg_mes(); /* (offset tmp; short size; int typ,score)
+ * Generate a register message with the
+ * given arguments.
+ */
+extern bool dom(); /* (bblock_p b1,b2)
+ /* See if b1 dominates b2. Note that a
+ * block always * dominates itself.
+ */
+extern bblock_p common_dom(); /* (bblock_p a,b)
+ * find a basic block that dominates a as
+ * well as b; note that a basic block also
+ * dominates itself.
+ */
+extern short add_timespace(); /* (short time,space)
+ * Add together a time and space, using
+ * the time_space_ratio parameter that
+ * may be set by the user.
+ */
+extern rm_line(); /* ( line_p l; bblock_p b)
+ * Remove line l from b basic block b.
+ */
+
+extern appnd_line(); /* ( line_p l1,l2)
+ * Put line l1 after l2.
+ */
+extern line_p last_instr(); /* ( bblock_p b)
+ * Determine the last line of a basic block.
+ */
+extern line_p find_mesreg(); /* (offset off)
+ * Find the register message for the local
+ * with the given offset.
+ */
+extern bool is_regvar(); /* (offset off)
+ * See if there is a 'register message'
+ * for the local variable with the
+ * given offset.
+ */
+extern offset regv_arg(); /* (offset off; int n)
+ * Fetch the n'th argument of the
+ * register message of the local with
+ * the given offset.
+ */
--- /dev/null
+/* S H A R E D F I L E
+ *
+ * C S E T . C
+ */
+
+
+#include "types.h"
+#include "cset.h"
+#include "alloc.h"
+#include "debug.h"
+#include "global.h"
+
+
+/* A set over a range of integers from 1 to N may be represented
+ * as a 'compact' set. Such a set is represented as a 'bitvector'
+ * record, containing the size of the set (i.e. N) and a row
+ * of words (the bitvector itself). An integer J (1 <= J <= N) is
+ * an element of the set iff the J-th bit of the vector is a '1'.
+ * Any redundant bits in the last word are garanteed to be zero bits.
+ * This package implements the usual operations on sets.
+ * The name of every operation is preceede by a 'C' to
+ * distinguish it from the operation on 'long' (list)
+ * sets whth a similar name.
+ */
+
+
+/* The two arithmetic operations 'divide by wordlength' and
+ * 'modulo wordlength' can be performed very efficiently
+ * if the word length (of the source machine) is 16.
+ */
+
+
+
+
+cset Cempty_set(n)
+ short n;
+{
+ cset s;
+
+ s = newbitvect(DIVWL(n-1) + 1);
+ s->v_size = n;
+ return s;
+}
+
+
+bool Cis_elem(x,s)
+ Celem_t x;
+ cset s;
+{
+ short n;
+ int mask;
+
+ assert(x>0 && x <= s->v_size);
+ n = DIVWL(x-1);
+ mask = (1 << MODWL(x-1));
+ if ((s->v_bits[n] & mask) == 0) {
+ return FALSE;
+ } else {
+ return TRUE;
+ }
+}
+
+
+
+Cadd(x,s_p)
+ Celem_t x;
+ cset *s_p;
+{
+ cset s;
+ short n;
+ int mask;
+
+ s = *s_p;
+ assert(x>0 && x <= s->v_size);
+ n = DIVWL(x-1);
+ mask = (1 << MODWL(x-1));
+ s->v_bits[n] |= mask;
+}
+
+
+Cremove(x,s_p)
+ Celem_t x;
+ cset *s_p;
+{
+ cset s;
+ short n;
+ int mask;
+
+ s = *s_p;
+ assert(x>0 && x <= s->v_size);
+ n = DIVWL(x-1);
+ mask = (1 << MODWL(x-1));
+ s->v_bits[n] &= ~mask;
+}
+
+
+
+/* The operations first, next and elem can be used to iterate
+ * over a set. For example:
+ * for (i = Cfirst(s); i != (Cindex) 0; i = Cnext(i,s) {
+ * x = Celem(i);
+ * use x
+ * }
+ * which is like:
+ * 'for all elements x of s do'
+ * use x
+ *
+ * The implementation of first and next is not very fast.
+ * It could be made much more efficient (at the price of a
+ * higher complexity) by not using 'is_elem'.
+ * Iteration over a bitvector, however, is not supposed to
+ * be used very often.
+ */
+
+Cindex Cfirst(s)
+ cset s;
+{
+ return Cnext((Cindex) 0,s);
+}
+
+
+Cindex Cnext(i,s)
+ Cindex i;
+ cset s;
+{
+ register short n;
+
+ for (n = i+1; n <= s->v_size; n++) {
+ if (Cis_elem(n,s)) {
+ return (Cindex) n;
+ }
+ }
+ return (Cindex) 0;
+}
+
+
+Celem_t Celem(i)
+ Cindex i;
+{
+ return (Celem_t) i;
+}
+
+
+
+Cjoin(s1,s2_p)
+ cset s1, *s2_p;
+{
+ /* Two sets are joined by or-ing their bitvectors,
+ * word by word.
+ */
+
+ cset s2;
+ short n;
+ register short i;
+
+ s2 = *s2_p;
+ assert(s1->v_size == s2->v_size);
+ n = DIVWL(s1->v_size -1); /* #words -1 */
+ for (i = 0; i <= n; i++) {
+ s2->v_bits[i] |= s1->v_bits[i];
+ }
+}
+
+
+
+Cintersect(s1,s2_p)
+ cset s1, *s2_p;
+{
+ /* Two sets are intersected by and-ing their bitvectors,
+ * word by word.
+ */
+
+ cset s2;
+ short n;
+ register short i;
+
+ s2 = *s2_p;
+ assert(s1->v_size == s2->v_size);
+ n = DIVWL(s1->v_size -1); /* #words -1 */
+ for (i = 0; i <= n; i++) {
+ s2->v_bits[i] &= s1->v_bits[i];
+ }
+}
+
+
+Cdeleteset(s)
+ cset s;
+{
+ oldbitvect(s,DIVWL(s->v_size - 1) + 1);
+}
+
+
+bool Cis_subset(s1,s2)
+ cset s1,s2;
+{
+ /* See if s1 is a subset of s2 */
+
+ register short i;
+
+ assert(s1->v_size == s2->v_size);
+ if (s1->v_size == 0) return TRUE;
+ for (i = 0; i <= DIVWL(s1->v_size-1); i++) {
+ if ((s1->v_bits[i] & ~(s2->v_bits[i])) != 0) {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+
+Cclear_set(s_p)
+ cset *s_p;
+{
+ cset s;
+ register short i;
+
+ s = *s_p;
+ assert (s != (cset) 0);
+ for (i = 0; i <= DIVWL(s->v_size-1); i++) {
+ s->v_bits[i] = 0;
+ }
+}
+
+
+Ccopy_set(s1,s2_p)
+ cset s1, *s2_p;
+{
+ cset s2;
+ register short i;
+
+ s2 = *s2_p;
+ assert (s1->v_size == s2->v_size);
+ for (i = 0; i <= DIVWL(s1->v_size-1); i++) {
+ s2->v_bits[i] = s1->v_bits[i];
+ }
+}
+
+
+Csubtract(s1,s2_p)
+ cset s1, *s2_p;
+{
+ cset s2;
+ register short i;
+
+ s2 = *s2_p;
+ assert (s1->v_size == s2->v_size);
+ for (i = 0; i <= DIVWL(s1->v_size-1); i++) {
+ s2->v_bits[i] &= ~(s1->v_bits[i]);
+ }
+}
+
+
+bool Cequal(s1,s2)
+ cset s1, s2;
+{
+ register short i;
+
+ assert (s1->v_size == s2->v_size);
+ for (i = 0; i <= DIVWL(s1->v_size-1); i++) {
+ if (s1->v_bits[i] != s2->v_bits[i]) return FALSE;
+ }
+ return TRUE;
+}
+
+short Cnrelems(s)
+ cset s;
+{
+ register short n, cnt;
+
+ cnt = 0;
+ for (n = 1; n <= s->v_size; n++) {
+ if (Cis_elem(n,s)) {
+ cnt++;
+ }
+ }
+ return cnt;
+}
--- /dev/null
+/* O P E R A T I O N S F O R
+ * C O M P A C T S E T S
+ */
+
+
+extern cset Cempty_set(); /* (short) */
+extern bool Cis_elem(); /* (Celem, cset) */
+extern Cadd(); /* (Celem, *cset) */
+extern Cremove(); /* (Celem, *cset) */
+extern Cindex Cfirst(); /* (cset) */
+extern Cindex Cnext(); /* (Cindex, cset) */
+extern Celem_t Celem(); /* (Cindex) */
+extern Cjoin(); /* (cset, *cset) */
+extern Cintersect(); /* (cset, *cset) */
+extern Cdeleteset(); /* (cset) */
+extern bool Cis_subset(); /* (cset, cset) */
+extern Cclearset(); /* (cset, *cset) */
+extern Ccopy_set(); /* (cset, *cset) */
+extern Csubtract(); /* (cset, *cset) */
+extern bool Cequal(); /* (cset, cset) */
+extern short Cnrelems(); /* (cset) */
--- /dev/null
+/* S H A R E D F I L E
+ *
+ * D E B U G . C
+ */
+
+
+#include <stdio.h>
+#include "types.h"
+#include "def.h"
+#include "debug.h"
+#include "../../../h/em_spec.h"
+#include "global.h"
+
+
+
+int linecount; /* # lines in this file */
+bool verbose_flag = FALSE; /* generate verbose output ? */
+
+/* VARARGS1 */
+error(s,a) char *s,*a; {
+
+ fprintf(stderr,"error on line %u",linecount);
+ if (filename != (char *) 0) {
+ fprintf(stderr," file %s",filename);
+ }
+ fprintf(stderr,": ");
+ fprintf(stderr,s,a);
+ fprintf(stderr,"\n");
+ _cleanup();
+ abort();
+ exit(-1);
+}
+
+#ifdef TRACE
+/* VARARGS1 */
+OUTTRACE(s,n)
+ char *s;
+ int n;
+{
+ fprintf(stderr,"> ");
+ fprintf(stderr,s,n);
+ fprintf(stderr,"\n");
+}
+#endif
+
+#ifdef VERBOSE
+/* VARARGS1 */
+OUTVERBOSE(s,n1,n2)
+ char *s;
+ int n1,n2;
+{
+ if (verbose_flag) {
+ fprintf(stderr,"optimization: ");
+ fprintf(stderr,s,n1,n2);
+ fprintf(stderr,"\n");
+ }
+}
+#endif
+
+
+
+#ifdef DEBUG
+badassertion(file,line) char *file; unsigned line; {
+
+ fprintf(stderr,"assertion failed file %s, line %u\n",file,line);
+ error("assertion");
+}
+#endif
+/* Valid Address */
+
+VA(a) short *a; {
+ if (a == (short *) 0) error("VA: 0 argument");
+ if ( ((unsigned) a & 01) == 01) {
+ /* MACHINE DEPENDENT TEST */
+ error("VA: odd argument");
+ }
+}
+
+
+/* Valid Instruction code */
+
+VI(i) short i; {
+ if (i > ps_last) error("VI: illegal instr: %d", i);
+}
+
+
+/* Valid Line */
+
+VL(l) line_p l; {
+ byte instr, optype;
+
+ VA((short *) l);
+ instr = l->l_instr;
+ VI(instr);
+ optype = TYPE(l);
+ if (optype < OP_FIRST || optype > OP_LAST) {
+ error("VL: illegal optype: %d", optype);
+ }
+}
+
+
+
+/* Valid Data block */
+
+VD(d) dblock_p d; {
+ byte pseudo;
+
+ VA((short *) d);
+ pseudo = d->d_pseudo;
+ if (pseudo < D_FIRST || pseudo > D_LAST) {
+ error("VD: illegal pseudo: %d",pseudo);
+ }
+}
+
+
+/* Valid Object */
+
+VO(o) obj_p o; {
+ offset off;
+
+ VA((short *) o);
+ off = o->o_off;
+ if (off < 0 || off > 10000) {
+ error("VO: unlikely offset: %d", off);
+ }
+}
+
+
+
+/* Valid Proc */
+
+VP(p) proc_p p; {
+ proc_id pid;
+ int nrlabs;
+
+ VA((short *) p);
+ pid = p->p_id;
+ if (pid <0 || pid > 1000) {
+ error("VP: unlikely proc_id: %d", (int) pid);
+ }
+ nrlabs = p->p_nrlabels;
+ if (nrlabs < 0 || nrlabs > 500) {
+ error("VP: unlikely p_nrlabels: %d", nrlabs);
+ }
+}
--- /dev/null
+/* D E B U G G I N G T O O L S */
+
+/* TEMPORARY: */
+#define DEBUG
+
+extern int linecount; /* # lines in this file */
+extern bool verbose_flag; /* generate verbose output ? */
+
+/* VARARGS 1 */
+error();
+
+
+#ifdef TRACE
+extern OUTTRACE();
+#else
+#define OUTTRACE(s,n)
+#endif
+#ifdef VERBOSE
+extern OUTVERBOSE();
+#else
+#define OUTVERBOSE(s,n1,n2)
+#endif
+#ifdef DEBUG
+
+/* Some (all?) Unix debuggers don't particularly like
+ * static procedures and variables. Therefor we make everything
+ * global when debugging.
+ */
+
+#define STATIC
+
+#define assert(x) if(!(x)) badassertion(__FILE__,__LINE__)
+
+extern VI();
+extern VL();
+extern VD();
+extern VA();
+extern VO();
+extern VP();
+
+
+
+#else /*DEBUG*/
+
+#define assert(b)
+
+#define VI(i)
+#define VL(l)
+#define VD(d)
+#define VA(a)
+#define VO(o)
+#define VP(p)
+
+
+#define STATIC static
+#endif
--- /dev/null
+/* G L O B A L M A C R O D E F I N I T I O N S
+ *
+ * F O R A L L O P T I M I Z E R P A S S E S
+ */
+
+#define MARK_DBLOCK 0
+#define MARK_OBJ 1
+#define MARK_ARG 2
+
+
+#define op_lab (sp_lmnem+1)
+#define op_last op_lab
+#define ps_sym (sp_lpseu+1)
+#define ps_last ps_sym
--- /dev/null
+/* S H A R E D F I L E
+ *
+ * F I L E S . C
+ */
+
+#include <stdio.h>
+
+FILE *openfile(name,mode)
+ char *name,*mode;
+{
+ FILE *f;
+
+ if ((f = fopen(name,mode)) == NULL) {
+ error("cannot open %s",name);
+ }
+ return f;
+}
--- /dev/null
+/* F I L E N A M E S */
+
+/* The names of the input files of every phase are passed as
+ * arguments to the phase. First come the input file names,
+ * then the output file names. We use a one-letter convention
+ * to denote the type of file:
+ * p: procedure table file
+ * d: data table file
+ * l: EM text file (lines of EM instructions)
+ * b: basic block file (Control Flow Graph file)
+ */
+
+/* The input file names */
+
+#define pname argv[1]
+#define dname argv[2]
+#define lname argv[3]
+#define bname argv[4]
+
+/* The output file names */
+
+#define pname2 argv[5]
+#define dname2 argv[6]
+#define lname2 argv[7]
+#define bname2 argv[8]
+
+#define ARGSTART 9
+
+extern FILE *openfile(); /* (char *name, *mode)
+ * Open a file with the given name
+ * and mode; aborts if the file
+ * cannot be opened.
+ */
--- /dev/null
+/* S H A R E D F I L E
+ *
+ * G E T . C
+ */
+
+#include <stdio.h>
+#include "types.h"
+#include "def.h"
+#include "debug.h"
+#include "global.h"
+#include "lset.h"
+#include "cset.h"
+#include "get.h"
+#include "alloc.h"
+#include "map.h"
+#include "aux.h"
+#include "../../../h/em_spec.h"
+#include "../../../h/em_mnem.h"
+#include "../../../h/em_pseu.h"
+#include "../../../h/em_mes.h"
+#include "../../../h/em_flag.h"
+
+extern char em_flag[];
+
+
+/* global variables */
+
+static FILE *f;
+STATIC block_id lastbid; /* block identifying number */
+STATIC lab_id lastlabid; /* last label identifier */
+
+/* creating new identifying numbers, i.e. numbers that did not
+ * appear in the input.
+ */
+
+bblock_p freshblock()
+{
+ bblock_p b;
+ b = newbblock();
+ b->b_id = ++lastbid;
+ return b;
+}
+
+
+lab_id freshlabel()
+{
+ curproc->p_nrlabels++;
+ return ++lastlabid;
+}
+
+
+/* local routines */
+
+#define getbyte() getc(f)
+
+#define getmark() getbyte()
+
+STATIC short getshort() {
+ register int l_byte, h_byte;
+
+ l_byte = getbyte();
+ h_byte = getbyte();
+ if ( h_byte>=128 ) h_byte -= 256 ;
+ return l_byte | (h_byte*256) ;
+}
+
+
+STATIC offset getoff() {
+ register long l;
+ register int h_byte;
+
+ l = getbyte();
+ l |= ((unsigned) getbyte())*256 ;
+ l |= getbyte()*256L*256L ;
+ h_byte = getbyte() ;
+ if ( h_byte>=128 ) h_byte -= 256 ;
+ return l | (h_byte*256L*256*256L) ;
+}
+
+STATIC int getint()
+{
+ /* Read an integer from the input file. This routine is
+ * only used when reading a bitvector-set. We expect an
+ * integer to be either a short or a long.
+ */
+
+ if (sizeof(int) == sizeof(short)) {
+ return getshort();
+ } else {
+ assert (sizeof(int) == sizeof(offset));
+ return getoff();
+ }
+}
+
+/* getptable */
+
+loop_p getloop(id)
+ loop_id id;
+{
+ /* Map a loop identifier onto a loop struct.
+ * If no struct was alocated yet for this identifier then
+ * allocate one now and update the loop-map table.
+ */
+
+
+ assert (id > 0 && id <=lplength);
+ if (lpmap[id] == (loop_p) 0) {
+ lpmap[id] = newloop();
+ lpmap[id]->lp_id = id;
+ }
+ return (lpmap[id]);
+}
+
+bblock_p getblock(id)
+ block_id id;
+{
+ /* Map a basic block identifier onto a block struct
+ * If no struct was alocated yet for this identifier then
+ * allocate one now and update the block-map table.
+ */
+
+
+ assert (id >= 0 && id <=blength);
+ if (id == 0) return (bblock_p) 0;
+ if (bmap[id] == (bblock_p) 0) {
+ bmap[id] = newbblock();
+ bmap[id]->b_id = id;
+ }
+ return (bmap[id]);
+}
+
+
+lset getlset(p)
+ char *((*p) ());
+{
+ /* Read a 'long' set. Such a set is represented externally
+ * as a sequence of identifying numbers terminated by a 0.
+ * The procedural parameter p maps such a number onto a
+ * pointer to a struct (bblock_p, loop_p etc.).
+ */
+
+ lset s;
+ int id;
+
+ s = Lempty_set();
+ while (id = getshort()) {
+ Ladd( (*p) (id), &s);
+ }
+ return s;
+}
+
+
+cset getcset()
+{
+ /* Read a 'compact' set. Such a set is represented externally
+ * a row of bytes (its bitvector) preceded by its length.
+ */
+
+ cset s;
+ register short i;
+
+ s = Cempty_set(getshort());
+ for (i = 0; i <= DIVWL(s->v_size-1);i++) {
+ s->v_bits[i] = getint();
+ }
+ return s;
+}
+
+
+proc_p getptable(pname)
+ char *pname;
+{
+ short i;
+ proc_p head, p, *pp;
+ short all;
+
+ if ((f = fopen(pname,"r")) == NULL) {
+ error("cannot open %s",pname);
+ }
+
+ plength = getshort(); /* table is preceded by its length */
+ assert(plength >= 0);
+ assert(plength < 1000); /* See if its a reasonable number */
+ pmap = (proc_p *) newmap(plength); /* allocate the pmap table */
+
+ all = getshort();
+ head = (proc_p) 0;
+ pp = &head;
+ for (i = 0; i < plength; i++) {
+ if (feof(f)) {
+ error("unexpected eof %s", pname);
+ }
+ p = newproc();
+ p->p_id = getshort();
+ assert(p->p_id > 0 && p->p_id <= plength);
+ pmap[p->p_id] = p;
+ p->p_flags1 = getbyte();
+ if (p->p_flags1 & PF_BODYSEEN) {
+ p->p_nrlabels = getshort();
+ p->p_localbytes = getoff();
+ p->p_nrformals = getoff();
+ if (all) {
+ p->p_change = newchange();
+ p->p_change->c_ext = getcset();
+ p->p_change->c_flags = getshort();
+ p->p_use = newuse();
+ p->p_use->u_flags = getshort();
+ p->p_calling = getcset();
+ }
+ }
+ *pp = p;
+ pp = &(p->p_next);
+ }
+ fclose(f);
+ OUTTRACE("have read proc table of length %d",plength);
+ return head; /* pointer to first structure of list */
+}
+
+
+
+/* getdtable */
+
+dblock_p getdtable(dname)
+ char *dname;
+{
+ /* Read the data block table. Every data block may
+ * have a list of objects and a list of values (arguments),
+ * each of which is also represented by a structure.
+ * So the input file contains a mixture of dblock,
+ * obj and arg records, each one having its own
+ * attributes. A mark indicates which one comes next.
+ * We assume that the syntactic structure of the input
+ * is correct.
+ */
+
+ dblock_p head, d, *dp;
+ obj_p obj, *op;
+ arg_p arg, *ap;
+ /* dp, op an ap tell how the next dblock/obj/arg
+ * has to be linked.
+ */
+ int n;
+
+ head = (dblock_p) 0;
+ dp = &head;
+ if ((f = fopen(dname,"r")) == NULL) {
+ error("cannot open %s", dname);
+ }
+ olength = getshort();
+ assert(olength >= 0);
+ assert(olength < 5000); /* See if its a reasonable number */
+ /* total number of objects */
+ omap = (obj_p *) newmap(olength); /* allocate omap table */
+
+ while (TRUE) {
+ n = getmark();
+ if (feof(f)) break;
+ switch(n) {
+ case MARK_DBLOCK:
+ d = *dp = newdblock();
+ op = &d->d_objlist;
+ ap = &d->d_values;
+ dp = &d->d_next;
+ d->d_id = getshort();
+ d->d_pseudo = getbyte();
+ d->d_size = getoff();
+ d->d_fragmnr = getshort();
+ d->d_flags1 = getbyte();
+ break;
+ case MARK_OBJ:
+ obj = *op = newobject();
+ op = &obj->o_next;
+ obj->o_dblock = d;
+ obj->o_id = getshort();
+ assert(obj->o_id >0);
+ assert(obj->o_id <= olength);
+ omap[obj->o_id] = obj;
+ obj->o_size = getoff();
+ obj->o_off = getoff();
+ break;
+ case MARK_ARG:
+ arg = *ap = newarg(ARGOFF);
+ ap = &arg->a_next;
+ arg->a_a.a_offset = getoff();
+ break;
+ default:
+ assert(FALSE);
+ }
+ }
+ OUTTRACE("have read data table, %d objects",olength);
+ return head;
+}
+
+
+
+/* getbblocks */
+
+STATIC argstring(length,abp)
+ short length;
+ register argb_p abp;
+{
+
+ while (length--) {
+ if (abp->ab_index == NARGBYTES)
+ abp = abp->ab_next = newargb();
+ abp->ab_contents[abp->ab_index++] = getbyte();
+ }
+}
+
+
+
+STATIC arg_p readargs()
+{
+ /* Read a list of arguments and allocate structures
+ * for them. Return a pointer to the head of the list.
+ */
+
+ arg_p head, arg, *ap;
+ byte t;
+ short length;
+
+ ap = &head;
+ for (;;) {
+ /* every argument list is terminated by an
+ * ARGCEND byte in Intermediate Code.
+ */
+ t = getbyte();
+ if (t == (byte) ARGCEND) {
+ return head;
+ }
+ arg = *ap = newarg(t);
+ ap = &arg->a_next;
+ switch((short) t) {
+ case ARGOFF:
+ arg->a_a.a_offset = getoff();
+ break;
+ case ARGINSTRLAB:
+ arg->a_a.a_instrlab = getshort();
+ break;
+ case ARGOBJECT:
+ arg->a_a.a_obj = omap[getshort()];
+ /* Read an object identifier (o_id)
+ * and use the omap table to obtain
+ * a pointer to the rigth obj struct.
+ */
+ break;
+ case ARGPROC:
+ arg->a_a.a_proc = pmap[getshort()];
+ /* Read a procedure identifier (p_id) */
+ break;
+ case ARGSTRING:
+ length = getshort();
+ argstring(length, &arg->a_a.a_string);
+ break;
+ case ARGICN:
+ case ARGUCN:
+ case ARGFCN:
+ length = getshort();
+ arg->a_a.a_con.ac_length = length;
+ /* size of the constant */
+ argstring(getshort(),
+ &arg->a_a.a_con.ac_con);
+ break;
+ default:
+ assert(FALSE);
+ }
+ }
+}
+
+
+STATIC line_p read_line(p_out)
+ proc_p *p_out;
+{
+ /* Read a line of EM code (i.e. one instruction)
+ * and its arguments (if any).
+ * In Intermediate Code, the first byte is the
+ * instruction code and the second byte denotes the kind
+ * of operand(s) that follow.
+ */
+
+ line_p lnp;
+ byte instr;
+
+ instr = getbyte();
+ if (feof(f)) return (line_p) 0;
+ lnp = newline(getbyte());
+ linecount++;
+ lnp->l_instr = instr;
+ switch(TYPE(lnp)) {
+ /* read the operand(s) */
+ case OPSHORT:
+ SHORT(lnp) = getshort();
+ break;
+ case OPOFFSET:
+ OFFSET(lnp) = getoff();
+ break;
+ case OPINSTRLAB:
+ INSTRLAB(lnp) = getshort();
+ if (instr == op_lab) {
+ /* defining occurrence of an
+ * instruction label.
+ */
+ lmap[INSTRLAB(lnp)] = lnp;
+ }
+ break;
+ case OPOBJECT:
+ OBJ(lnp) = omap[getshort()];
+ break;
+ case OPPROC:
+ PROC(lnp) = pmap[getshort()];
+ if ((instr & BMASK) == ps_pro) {
+ /* enter new procedure: allocate a
+ * label map and a label-block map table.
+ */
+ *p_out = PROC(lnp);
+ llength = (*p_out)->p_nrlabels;
+ lmap = (line_p *) newmap(llength);
+ /* maps lab_id to line structure */
+ lbmap = (bblock_p *) newmap(llength);
+ /* maps lab_id to bblock structure */
+ lastlabid = llength;
+ }
+ break;
+ case OPLIST:
+ ARG(lnp) = readargs();
+ break;
+ default:
+ assert(TYPE(lnp) == OPNO);
+ }
+ return lnp;
+}
+
+
+STATIC message(lnp)
+ line_p lnp;
+{
+ /* See if lnp is some useful message.
+ * (e.g. a message telling that a certain local variable
+ * will never be referenced indirectly, so it may be put
+ * in a register. If so, add it to the mesregs set.)
+ */
+
+ assert(ARG(lnp)->a_type == ARGOFF);
+ switch((int) aoff(ARG(lnp),0)) {
+ case ms_reg:
+ if (ARG(lnp)->a_next != (arg_p) 0) {
+ /* take only "mes 3" with further arguments */
+ Ladd(lnp,&mesregs);
+ }
+ break;
+ case ms_err:
+ error("ms_err encountered");
+ case ms_opt:
+ error("ms_opt encountered");
+ case ms_emx:
+ ws = aoff(ARG(lnp),1);
+ ps = aoff(ARG(lnp),2);
+ break;
+ }
+}
+
+
+
+STATIC line_p getlines(lf,n,p_out,collect_mes)
+ FILE *lf;
+ int n;
+ proc_p *p_out;
+ bool collect_mes;
+{
+ /* Read n lines of EM text and doubly link them.
+ * Also process messages.
+ */
+
+ line_p head, *pp, l, lprev;
+
+ f = lf; /* EM input file */
+ pp = &head;
+ lprev = (line_p) 0;
+ while (n--) {
+ l = *pp = read_line(p_out);
+ PREV(l) = lprev;
+ pp = &l->l_next;
+ lprev = l;
+ if (collect_mes && INSTR(l) == ps_mes) {
+ message(l);
+ }
+ }
+ *pp = (line_p) 0;
+ return head;
+}
+
+
+
+bool getunit(gf,lf,kind_out,g_out,l_out,p_out,collect_mes)
+ FILE *gf,*lf;
+ short *kind_out;
+ bblock_p *g_out;
+ line_p *l_out;
+ proc_p *p_out;
+ bool collect_mes;
+{
+ /* Read control flow graph (gf) and EM text (lf) of the next procedure.
+ * A pointer to the proctable entry of the read procedure is
+ * returned via p_out.
+ * This routine also constructs the bmap and lpmap tables.
+ * Note that we allocate structs for basic blocks and loops
+ * at their first reference rather than at when we read them.
+ */
+
+ int n,i;
+ bblock_p head, *pp, b;
+ loop_p lp;
+
+ f = gf;
+ blength = getshort(); /* # basic blocks in this procedure */
+ if (feof(f)) return FALSE;
+ if (blength == 0) {
+ /* data unit */
+ *kind_out = LDATA;
+ n = getshort();
+ *l_out = getlines(lf,n,p_out,collect_mes);
+ return TRUE;
+ }
+ *kind_out = LTEXT;
+ bmap = (bblock_p *) newmap(blength); /* maps block_id on bblock_p */
+ lplength = getshort(); /* # loops in this procedure */
+ lpmap = (loop_p *) newmap(lplength); /* maps loop_id on loop_p */
+
+ /* Read the basic blocks and the EM text */
+ pp = &head; /* we use a pointer-to-a-pointer to link the structs */
+ for (i = 0; i < blength; i++) {
+ b = getblock(getshort());
+ n = getshort(); /* #instructions in the block */
+ b->b_succ = getlset(getblock);
+ b->b_pred = getlset(getblock);
+ b->b_idom = getblock(getshort());
+ b->b_loops = getlset(getloop);
+ b->b_flags = getshort();
+ b->b_start = getlines(lf,n,p_out,collect_mes); /* read EM text */
+ *pp = b;
+ pp = &b->b_next;
+ f = gf;
+ }
+ lastbid = blength; /* last block_id */
+
+ /* read the information about loops */
+ curproc->p_loops = Lempty_set();
+ for (i = 0; i < lplength; i++) {
+ lp = getloop(getshort());
+ lp->lp_level = getshort(); /* nesting level */
+ lp->lp_entry = getblock(getshort()); /* entry block of the loop */
+ lp->lp_end = getblock(getshort()); /* tail of back edge of loop */
+ Ladd(lp,&curproc->p_loops);
+ }
+ *g_out = head;
+ return TRUE;
+}
+
+
+/* The procedure getbblocks is used only by the Control Flow phase.
+ * It reads the EM textfile and partitions every procedure into
+ * a number of basic blocks.
+ */
+
+
+#define LABEL0 0
+#define LABEL 1
+#define NORMAL 2
+#define JUMP 3
+#define END 4
+#define AFTERPRO 5
+#define INIT 6
+
+
+/* These global variables are used by getbblocks and nextblock. */
+
+STATIC bblock_p b, *bp; /* b is the current basic block, bp is
+ * the address where the next block has
+ * to be linked.
+ */
+STATIC line_p lnp, *lp; /* lnp is the current line, lp is
+ * the address where the next line
+ * has to be linked.
+ */
+STATIC short state; /* We use a finite state machine with the
+ * following states:
+ * LABEL0: after the first (successive)
+ * instruction label.
+ * LABEL1: after at least two successive
+ * instruction labels.
+ * NORMAL: after a normal instruction.
+ * JUMP: after a branch (conditional,
+ * unconditional or CSA/CSB).
+ * END: after an END pseudo
+ * AFTERPRO: after we've read a PRO pseudo
+ * INIT: initial state
+ */
+
+
+STATIC nextblock()
+{
+ /* allocate a new basic block structure and
+ * set b, bp and lp.
+ */
+
+ b = *bp = freshblock();
+ bp = &b->b_next;
+ b->b_start = lnp;
+ b->b_succ = Lempty_set();
+ b->b_pred = Lempty_set();
+ b->b_extend = newcfbx(); /* basic block extension for CF */
+ b->b_extend->bx_cf.bx_bucket = Lempty_set();
+ b->b_extend->bx_cf.bx_semi = 0;
+ lp = &lnp->l_next;
+#ifdef TRACE
+ fprintf(stderr,"new basic block, id = %d\n",lastbid);
+#endif
+}
+
+
+STATIC short kind(lnp)
+ line_p lnp;
+{
+ /* determine if lnp is a label, branch, end or otherwise */
+
+ short instr;
+ byte flow;
+
+ if ((instr = INSTR(lnp)) == op_lab) return (short) LABEL;
+ if (instr == ps_end) return (short) END;
+ if (instr > sp_lmnem) return (short) NORMAL; /* pseudo */
+ if ((flow = (em_flag[instr-sp_fmnem] & EM_FLO)) == FLO_C ||
+ flow == FLO_T) return (short) JUMP; /* conditional/uncond. jump */
+ return (short) NORMAL;
+}
+
+
+
+bool getbblocks(fp,kind_out,n_out,g_out,l_out)
+ FILE *fp;
+ short *kind_out;
+ short *n_out;
+ bblock_p *g_out;
+ line_p *l_out;
+{
+ bblock_p head = (bblock_p) 0;
+ line_p headl = (line_p) 0;
+
+ curproc = (proc_p) 0;
+ /* curproc will get a value when we encounter a PRO pseudo.
+ * If there is no such pseudo, we're reading only data
+ * declarations or messages (outside any proc.).
+ */
+ f = fp;
+ lastbid = (block_id) 0; /* block identier */
+ state = INIT; /* initial state */
+ bp = &head;
+
+ for (;;) {
+#ifdef TRACE
+ fprintf(stderr,"state = %d\n",state);
+#endif
+ switch(state) {
+ case LABEL0:
+ nextblock();
+ /* Fall through !! */
+ case LABEL:
+ lbmap[INSTRLAB(lnp)] = b;
+ /* The lbmap table contains for each
+ * label_id the basic block of that label.
+ */
+ lnp = read_line(&curproc);
+ state = kind(lnp);
+ if (state != END) {
+ *lp = lnp;
+ lp = &lnp->l_next;
+ }
+ break;
+ case NORMAL:
+ lnp = read_line(&curproc);
+ if ( (state = kind(lnp)) == LABEL) {
+ /* If we come accross a label
+ * here, it must be the beginning
+ * of a new basic block.
+ */
+ state = LABEL0;
+ } else {
+ if (state != END) {
+ *lp = lnp;
+ lp = &lnp->l_next;
+ }
+ }
+ break;
+ case JUMP:
+ lnp = read_line(&curproc);
+ /* fall through ... */
+ case AFTERPRO:
+ switch(state = kind(lnp)) {
+ case LABEL:
+ state = LABEL0;
+ break;
+ case JUMP:
+ case NORMAL:
+ nextblock();
+ break;
+ }
+ break;
+ case END:
+ *lp = lnp;
+#ifdef TRACE
+ fprintf(stderr,"at end of proc, %d blocks\n",lastbid);
+#endif
+ if (head == (bblock_p) 0) {
+ *kind_out = LDATA;
+ *l_out = headl;
+ } else {
+ *kind_out = LTEXT;
+ *g_out = head;
+ *n_out = (short) lastbid;
+ /* number of basic blocks */
+ }
+ return TRUE;
+ case INIT:
+ lnp = read_line(&curproc);
+ if (feof(f)) return FALSE;
+ if (INSTR(lnp) == ps_pro) {
+ state = AFTERPRO;
+ } else {
+ state = NORMAL;
+ headl = lnp;
+ lp = &lnp->l_next;
+ }
+ break;
+ }
+ }
+}
+
+/* The following routines are only used by the Inline Substitution phase */
+
+call_p getcall(cf)
+ FILE *cf;
+{
+ /* read a call from the call-file */
+
+ call_p c;
+ proc_p voided;
+ actual_p act,*app;
+ short n,m;
+
+ f = cf;
+ c = newcall();
+ n = getshort(); /* void nesting level */
+ if (feof(f)) return (call_p) 0;
+ c->cl_caller = pmap[getshort()];
+ c->cl_id = getshort();
+ c->cl_proc = pmap[getshort()];
+ c->cl_looplevel = getbyte();
+ c->cl_flags = getbyte();
+ c->cl_ratio = getshort();
+ app = &c->cl_actuals;
+ n = getshort();
+ while(n--) {
+ act = newactual();
+ m = getshort();
+ act->ac_size = getoff();
+ act->ac_inl = getbyte();
+ act->ac_exp = getlines(cf,m,&voided);
+ *app = act;
+ app = &act->ac_next;
+ }
+ *app = (actual_p) 0;
+ return c;
+}
+
+
+
+line_p get_text(lf,p_out)
+ FILE *lf;
+ proc_p *p_out;
+{
+ /* Read the EM text of one unit
+ * If it is a procedure, set p_out to
+ * the proc. just read. Else set p_out
+ * to 0.
+ */
+
+ line_p dumhead, l, lprev;
+ loop_p *oldlpmap = lpmap;
+ line_p *oldlmap = lmap;
+ short oldllength = llength;
+ short oldlastlabid = lastlabid;
+
+ f = lf;
+ *p_out = (proc_p) 0;
+ dumhead = newline(OPNO);
+ /* The list of instructions is preceeded by a dummy
+ * line, to simplify list manipulation
+ */
+ dumhead->l_instr = op_nop; /* just for fun */
+ lprev = dumhead;
+ for (;;) {
+ l = read_line(p_out);
+ if (feof(f)) return (line_p) 0;
+ lprev->l_next = l;
+ PREV(l) = lprev;
+ if (INSTR(l) == ps_end) break;
+ if (INSTR(l) == ps_mes) {
+ message(l);
+ }
+ lprev = l;
+ }
+ /* The tables that map labels to instructions
+ * and labels to basic blocks are not used.
+ */
+ if (*p_out != (proc_p) 0) {
+ oldmap(lmap,llength);
+ oldmap(lbmap,llength);
+ lmap = oldlmap;
+ lpmap = oldlpmap;
+ }
+ llength = oldllength;
+ lastlabid = oldlastlabid;
+ return dumhead;
+}
+
+
+
+calcnt_p getcc(ccf,p)
+ FILE *ccf;
+ proc_p p;
+{
+ /* Get call-count info of procedure p */
+
+ calcnt_p head,cc,*ccp;
+ short i;
+
+ fseek(ccf,p->p_extend->px_il.p_ccaddr,0);
+ f = ccf;
+ head = (calcnt_p) 0;
+ ccp = &head;
+ for (i = getshort(); i != (short) 0; i--) {
+ cc = *ccp = newcalcnt();
+ cc->cc_proc = pmap[getshort()];
+ cc->cc_count = getshort();
+ ccp = &cc->cc_next;
+ }
+ return head;
+}
+
+
+/* The following routine is only used by the Compact Assembly generation phase,
+ * which does not read basic blocks.
+ */
+
+line_p get_ca_lines(lf,p_out)
+ FILE *lf;
+ proc_p *p_out;
+{
+ /* Read lines of EM text and link them.
+ * Register messages are outputted immediately after the PRO.
+ */
+
+ line_p head, *pp, l;
+ line_p headm, *mp;
+ arg_p a;
+
+ f = lf; /* EM input file */
+ pp = &head;
+ mp = &headm;
+ headm = (line_p) 0;
+ while (TRUE) {
+ l = read_line(p_out);
+ if (feof(f)) break;
+ assert (l != (line_p) 0);
+ if (INSTR(l) == ps_end && INSTR(head) != ps_pro) {
+ /* Delete end pseudo after data-unit */
+ oldline(l);
+ break;
+ }
+ if (INSTR(l) == ps_mes && l->l_a.la_arg->a_a.a_offset == ms_reg) {
+ /* l is a register message */
+ if (l->l_a.la_arg->a_next == (arg_p) 0) {
+ /* register message without arguments */
+ oldline(l);
+ } else {
+ *mp = l;
+ mp = &l->l_next;
+ }
+ } else {
+ *pp = l;
+ pp = &l->l_next;
+ }
+ if (INSTR(l) == ps_end) {
+ break;
+ }
+ }
+ *pp = (line_p) 0;
+ if (INSTR(head) == ps_pro) {
+ /* append register message without arguments to list */
+ l = newline(OPLIST);
+ l->l_instr = ps_mes;
+ a = ARG(l) = newarg(ARGOFF);
+ a->a_a.a_offset = ms_reg;
+ *mp = l;
+ l->l_next = head->l_next;
+ head->l_next = headm;
+ } else {
+ assert(headm == (line_p) 0);
+ }
+ return head;
+}
--- /dev/null
+/* I N P U T R O U T I N E S */
+
+extern bblock_p freshblock(); /* ()
+ * Allocate a bblock struct and assign
+ * it a brand new block_id.
+ */
+extern lab_id freshlabel(); /* ()
+ * Get a brand new lab_id.
+ */
+extern dblock_p getdtable(); /* (char *dname)
+ * Read the data block table from
+ * the file with the given name.
+ */
+extern proc_p getptable(); /* (char *pname)
+ * Read the proc table from
+ * the file with the given name.
+ */
+extern bool getunit(); /* (FILE *gf,*lf; short kind_out;
+ * bblock_p g_out; line_p l_out;
+ * proc_p *p_out; bool collect_mes)
+ * Read the control flow graph
+ * (from file gf) and the EM text
+ * (from lf). If collect_mes is TRUE,
+ * all register messages will be
+ * collected and put in the global
+ * variable 'mesregs'. The proc read
+ * is returned in p_out.
+ */
+extern bool getbblocks(); /* (FILE *f,short kind_out,
+ * short *n_out, bblock_p *g_out,
+ * line_p *l_out)
+ * Read the EM text of a single
+ * unit from the given file.
+ * This unit can be either a procedure
+ * or a umber of data declarations and
+ * messages. If it is a proc., then
+ * partition the text into
+ * basic blocks. Return the
+ * number of basic blocks in n_out.
+ */
+extern call_p getcall(); /* (FILE *cf)
+ * Read a call from the call-file
+ */
+extern line_p get_text(); /* (FILE *lf; proc_p *p_out)
+ * Read the EM text of one procedure.
+ * The procedure read is returned via
+ * p_out.
+ */
+extern calcnt_p getcc(); /* (FILE *ccf; proc_p p)
+ * Read the call-count information
+ * of procedure p.
+ */
+extern line_p get_ca_lines(); /* (FILE *lf; proc_p *p_out)
+ * Read em lines till end pseudo is met.
+ * (Used only by CA phase).
+ */
--- /dev/null
+/* S H A R E D F I L E
+ *
+ * G L O B A L . C
+ */
+
+#include "types.h"
+
+int ps = 0;
+int ws = 0;
+
+proc_p curproc; /* current procedure */
+
+char *filename; /* name of current input file */
+
+lset mesregs; /* set of MES ms_reg pseudos */
+
+short time_space_ratio = 50;
+ /* 0 if optimizing for space only,
+ * 100 if optimizing for time only,
+ * else something 'in between'.
+ */
--- /dev/null
+/* G L O B A L V A R I A B L E S */
+
+/* sizes of TARGET machine */
+
+extern int ps; /* pointer size */
+extern int ws; /* word size */
+
+/* sizes of SOURCE machine (i.e. machine on which
+ * the optimizer runs)
+ */
+
+/* number of bits in a byte */
+#define BYTELENGTH 8
+
+/* number of bits in a word */
+#define WORDLENGTH 32
+
+#if BYTELENGTH==8
+#define DIVBL(a) ((a) >> 3)
+#define MODBL(a) ((a) & 07)
+#else
+#define DIVBL(a) (a/BYTELENGTH)
+#define MODBL(a) (a%BYTELENGTH)
+#endif
+
+#if WORDLENGTH==16
+#define DIVWL(a) ((a) >> 4)
+#define MODWL(a) ((a) & 017)
+#else
+#if WORDLENGTH==32
+#define DIVWL(a) ((a) >> 5)
+#define MODWL(a) ((a) & 037)
+#else
+#define DIVWL(a) (a/WORDLENGTH)
+#define MODWL(a) (a%WORDLENGTH)
+#endif
+#endif
+
+
+#define UNKNOWN_SIZE (-1)
+
+extern proc_p curproc; /* current procedure */
+
+extern char *filename; /* name of current input file */
+
+extern lset mesregs; /* set of MES ms_reg pseudos */
+
+extern short time_space_ratio; /* 0 if optimizing for space only,
+ * 100 if optimizing for time only,
+ * else something 'in between'.
+ */
--- /dev/null
+/* S H A R E D F I L E
+ *
+ * G O . C
+ *
+ */
+
+
+#include <stdio.h>
+#include "types.h"
+#include "debug.h"
+#include "global.h"
+#include "files.h"
+#include "get.h"
+#include "put.h"
+#include "lset.h"
+#include "map.h"
+#include "alloc.h"
+#include "go.h"
+
+
+STATIC bool report_flag = FALSE; /* report #optimizations found? */
+STATIC bool core_flag = FALSE; /* report core usage? */
+
+
+STATIC mach_init(machfile,phase_machinit)
+ char *machfile;
+ int (*phase_machinit)();
+{
+ /* Read target machine dependent information */
+
+ FILE *f;
+
+ f = openfile(machfile,"r");
+ fscanf(f,"%d",&ws);
+ fscanf(f,"%d",&ps);
+ if (ws != ps && ps != 2*ws) error("illegal pointer size");
+ phase_machinit(f);
+ fclose(f);
+}
+
+
+
+go(argc,argv,initialize,optimize,phase_machinit,proc_flag)
+ int argc;
+ char *argv[];
+ int (*initialize)();
+ int (*optimize)();
+ int (*phase_machinit)();
+ int (*proc_flag)();
+{
+ FILE *f, *gf, *f2, *gf2; /* The EM input and output and
+ * the basic block graphs input and output
+ */
+ bblock_p g;
+ line_p l;
+ short kind;
+ int i;
+ char *p;
+ bool time_opt = FALSE;
+
+ linecount = 0;
+ for (i = ARGSTART; i < argc; i++) {
+ p = argv[i];
+ if (*p++ != '-') error("illegal argument");
+ switch(*p) {
+ case 'S':
+ time_opt = FALSE;
+ break;
+ case 'T':
+ time_opt = TRUE;
+ break;
+ case 'M':
+ p++;
+ mach_init(p,phase_machinit);
+ break;
+ case 'C':
+ core_flag = TRUE;
+ break;
+ case 'Q':
+ report_flag = TRUE;
+ break;
+ case 'V':
+ verbose_flag = TRUE;
+ break;
+ default:
+ proc_flag(p);
+ break;
+ }
+ }
+ time_space_ratio = (time_opt ? 100 : 0);
+ fproc = getptable(pname); /* proc table */
+ fdblock = getdtable(dname); /* data block table */
+ initialize();
+ if (optimize == no_action) return;
+ f = openfile(lname,"r");
+ gf = openfile(bname,"r");
+ f2 = openfile(lname2,"w");
+ gf2 = openfile(bname2,"w");
+ mesregs = Lempty_set();
+ while (getunit(gf,f,&kind,&g,&l,&curproc,TRUE)) {
+ /* Read the control flow graph and EM text of
+ * one procedure and optimize it.
+ */
+ if (kind == LDATA) {
+ putunit(LDATA, (proc_p) 0, l, gf2, f2);
+ continue;
+ }
+ OUTTRACE("flow graph of proc %d read",curproc->p_id);
+ curproc->p_start = g;
+ /* The global variable curproc points to the
+ * current procedure. It is set by getgraph
+ */
+ optimize(curproc);
+ putunit(LTEXT,curproc,(line_p) 0,gf2,f2);
+ /* output control flow graph + text */
+ OUTTRACE("graph of proc %d outputted",curproc->p_id);
+ Ldeleteset(mesregs);
+ mesregs = Lempty_set();
+ }
+ fclose(f);
+ fclose(f2);
+ fclose(gf);
+ fclose(gf2);
+ f = openfile(dname2,"w");
+ putdtable(fdblock,f);
+ fclose(f);
+ f = openfile(pname2,"w");
+ putptable(fproc,f,TRUE);
+ fclose(f);
+ core_usage();
+}
+
+
+no_action() { }
+
+core_usage()
+{
+ if (core_flag) {
+ coreusage();
+ }
+}
+
+report(s,n)
+ char *s;
+ int n;
+{
+ /* Report number of optimizations found, if report_flag is set */
+
+ if (report_flag) {
+ fprintf(stderr,"%s: %d\n",s,n);
+ }
+}
--- /dev/null
+/* S H A R E D F I L E
+ *
+ * G O . H
+ *
+ */
+
+
+extern go(); /* ( int argc; char *argv[];
+ * int (*initialize)(); int (*optimize)();
+ * int (*phase_machinit)(); int (*proc_flag)() )
+ * This is the main driving routine of the optimizer.
+ * It first processes the flags given as argument;
+ * for every flag it does not recognize itself, it
+ * calls 'proc_flag'; as soon as the -M flag is seen,
+ * it opens the machine descriptor file and
+ * reads phase-independend information (notably the
+ * wordsize and pointersize of the target machine);
+ * next it calls 'phase_machinit' with this file as
+ * parameter. Subsequently it calls 'initialize'.
+ * Finally, all procedures are read, one at a time,
+ * and 'optimize' is called with the current procedure
+ * as parameter.
+ */
+extern no_action(); /* ()
+ * Parameter to be supplied for e.g. 'initialize' if
+ * no action is required.
+ */
+extern core_usage(); /* ()
+ * Report core usage, if core_flag is set.
+ */
+extern report(); /* ( char *s; int n)
+ * Report number of optimizations found, if
+ * report_flag is set
+ */
--- /dev/null
+
+/* S H A R E D F I L E
+ *
+ * I N I T _ G L O B L S
+ *
+ */
+
+#include "../share/types.h"
+#include "../share/debug.h"
+#include "../share/global.h"
+#include "../share/alloc.h"
+#include "../share/map.h"
+
+
+extern short nrglobals;
+
+init_globals()
+{
+ /* Assign a 'global variable number (o_globnr) to
+ * every global variable for which we want to
+ * maintain ud-info. We do not maintain ud-info
+ * for a global variable if:
+ * - it is part of a ROM data block (so it will never be changed)
+ * - it's size is not known
+ * - it overlaps another variable (e.g. LOE X+2 ; LDE X)
+ */
+
+ dblock_p d;
+ obj_p obj, prev;
+ short nr = 1;
+ offset ill_zone, x;
+
+ for (d = fdblock; d != (dblock_p) 0; d = d->d_next) {
+ ill_zone = (offset) 0;
+ for (obj = d->d_objlist; obj != (obj_p) 0; obj = obj->o_next) {
+ if (d->d_pseudo == DROM ||
+ obj->o_size == UNKNOWN_SIZE) {
+ obj->o_globnr = 0; /* var. not considered */
+ continue;
+ }
+ if (obj->o_off < ill_zone) {
+ obj->o_globnr = 0; /* var. not considered */
+ if (prev != (obj_p) 0 && prev->o_globnr != 0) {
+ prev->o_globnr = 0;
+ nr--;
+ }
+ } else {
+ obj->o_globnr = nr++;
+ }
+ if ((x = obj->o_off + obj->o_size) > ill_zone) {
+ ill_zone = x;
+ }
+ prev = obj;
+ }
+ }
+ nrglobals = nr -1;
+}
--- /dev/null
+
+/* S H A R E D
+ *
+ * I N I T _ G L O B L S
+ *
+ */
+
+extern init_globals(); /* Assign a 'global variable number (o_globnr)
+ * to every global variable.
+ */
--- /dev/null
+/*
+ * L O C A L S . C
+ */
+
+#include "types.h"
+#include "debug.h"
+#include "global.h"
+#include "lset.h"
+#include "cset.h"
+#include "def.h"
+#include "get.h"
+#include "aux.h"
+#include "alloc.h"
+#include "../../../h/em_mnem.h"
+#include "../../../h/em_spec.h"
+#include "../../../h/em_pseu.h"
+#include "../../../h/em_mes.h"
+#include "locals.h"
+
+extern short nrglobals;
+
+short nrlocals;
+local_p *locals; /* dynamic array */
+
+STATIC localvar(off,size,locs,reg,score)
+ offset off;
+ short size;
+ local_p *locs;
+ bool reg;
+ offset score;
+{
+ /* process a reference to a local variable.
+ * A local is characterized by a (offset,size) pair.
+ * We first collect all locals in a list, sorted
+ * by offset. Later we will construct a table
+ * out of this list.
+ */
+
+ local_p lc, x, *prevp;
+
+ prevp = locs;
+ for (lc = *locs; lc != (local_p) 0; lc = lc->lc_next) {
+ if (lc->lc_off == off && lc->lc_size == size) {
+ if (reg) {
+ REGVAR(lc); /* register variable */
+ lc->lc_score = score;
+ }
+ return; /* local already present */
+ }
+ if (lc->lc_off > off) break;
+ prevp = &lc->lc_next;
+ }
+ /* the local was not seen before; create an entry
+ * for it in the list.
+ */
+ x = *prevp = newlocal();
+ x->lc_off = off;
+ x->lc_size = size;
+ x->lc_next = lc;
+ if (reg) {
+ REGVAR(x);
+ x->lc_score = score;
+ }
+}
+
+
+
+STATIC check_message(l,locs)
+ line_p l;
+ local_p *locs;
+{
+ /* See if l is a register message */
+
+ arg_p arg;
+
+ arg = ARG(l);
+ if (aoff(arg,0) == ms_reg && arg->a_next != (arg_p) 0) {
+ localvar(aoff(arg,1), (short) aoff(arg,2), locs, TRUE,
+ aoff(arg,4));
+ }
+}
+
+
+
+
+STATIC check_local_use(l,locs)
+ line_p l;
+ local_p *locs;
+{
+ short sz;
+
+ switch(INSTR(l)) {
+ case op_lol:
+ case op_stl:
+ case op_inl:
+ case op_del:
+ case op_zrl:
+ sz = ws;
+ break;
+ case op_ldl:
+ case op_sdl:
+ sz = 2 * ws;
+ break;
+ case op_lil:
+ case op_sil:
+ sz = ps;
+ break;
+ case ps_mes:
+ check_message(l,locs);
+ /* fall through .. */
+ default:
+ return;
+ }
+ localvar(off_set(l),sz,locs,FALSE,(offset) 0);
+}
+
+
+make_localtab(p)
+ proc_p p;
+{
+ /* Make a table of local variables.
+ * This table is used to associate a
+ * unique number with a local. If two
+ * locals overlap (e.g. LDL 4 and LDL 2)
+ * none of them is considered any further,
+ * i.e. we don't compute ud-info for them.
+ */
+
+ local_p prev, next, lc;
+ local_p locallist = (local_p) 0;
+ short cnt = 0;
+ offset x, ill_zone = 0;
+ register bblock_p b;
+ register line_p l;
+
+ /* first make a list of all locals used */
+ 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) {
+ check_local_use(l,&locallist);
+ }
+ }
+ /* Now remove overlapping locals, count useful ones on the fly */
+ for (lc = locallist; lc != (local_p) 0; lc = lc->lc_next) {
+ if (ill_zone != 0 && lc->lc_off < ill_zone) {
+ /* this local overlaps with a previous one */
+ BADLC(lc);
+ if (!IS_BADLC(prev)) {
+ BADLC(prev);
+ cnt--;
+ }
+ } else {
+ cnt++;
+ }
+ x = lc->lc_off + lc->lc_size;
+ if (ill_zone == 0 || x > ill_zone) {
+ ill_zone = x;
+ }
+ prev = lc;
+ }
+ /* Now we know how many local variables there are */
+ nrlocals = cnt;
+ locals = (local_p *) newmap(cnt);
+ cnt = 1;
+ for (lc = locallist; lc != (local_p) 0; lc = next) {
+ next = lc->lc_next;
+ if (IS_BADLC(lc)) {
+ oldlocal(lc);
+ } else {
+ locals[cnt++] = lc;
+ lc->lc_next = (local_p) 0;
+ }
+ }
+ assert (cnt == nrlocals+1);
+}
+
+
+
+STATIC find_local(off,nr_out,found_out)
+ offset off;
+ short *nr_out;
+ bool *found_out;
+{
+ /* Try to find the local variable at the given
+ * offset. Return its local-number.
+ */
+
+ short v;
+
+ for (v = 1; v <= nrlocals; v++) {
+ if (locals[v]->lc_off > off) break;
+ if (locals[v]->lc_off == off) {
+ *found_out = TRUE;
+ *nr_out = v;
+ return;
+ }
+ }
+ *found_out = FALSE;
+}
+
+
+
+
+var_nr(l,nr_out,found_out)
+ line_p l;
+ short *nr_out;
+ bool *found_out;
+{
+ /* Determine the number of the variable referenced
+ * by EM instruction l.
+ */
+
+ offset off;
+ short nr;
+
+ switch(TYPE(l)) {
+ case OPOBJECT:
+ /* global variable */
+ if (OBJ(l)->o_globnr == 0) {
+ /* We don't maintain ud-info for this var */
+ *found_out = FALSE;
+ } else {
+ *nr_out = GLOB_TO_VARNR(OBJ(l)->o_globnr);
+ *found_out = TRUE;
+ }
+ return;
+ case OPSHORT:
+ off = (offset) SHORT(l);
+ break;
+ case OPOFFSET:
+ off = OFFSET(l);
+ break;
+ default:
+ assert(FALSE);
+ }
+ /* Its's a local variable */
+ find_local(off,&nr,found_out);
+ if (*found_out) {
+ *nr_out = LOC_TO_VARNR(nr);
+ }
+}
--- /dev/null
+
+/*
+ * 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.
+ */
+
+/* Every global variable for which ud-info is maintained has
+ * a 'global variable number' (o_globnr). Every useful local
+ * has a 'local variable number', which is its index in the
+ * 'locals' table. All these variables also have a
+ * 'variable number'. Conversions exist between these numbers.
+ */
+
+#define TO_GLOBAL(v) (v)
+#define TO_LOCAL(v) (v - nrglobals)
+#define GLOB_TO_VARNR(v) (v)
+#define LOC_TO_VARNR(v) (v + nrglobals)
+#define IS_GLOBAL(v) (v <= nrglobals)
+#define IS_LOCAL(v) (v > nrglobals)
+
+#define REGVAR(lc) lc->lc_flags |= LCF_REG
+#define IS_REGVAR(lc) (lc->lc_flags & LCF_REG)
+#define BADLC(lc) lc->lc_flags |= LCF_BAD
+#define IS_BADLC(lc) (lc->lc_flags & LCF_BAD)
+
+
--- /dev/null
+/* L O N G S E T S
+ *
+ * L S E T . C
+ */
+
+
+#include "types.h"
+#include "lset.h"
+#include "alloc.h"
+#include "debug.h"
+
+
+/* A 'long' set is represented as a linear list of 'elemholder'
+ * records. Every such record contains a pointer to an element
+ * of the set and to the next elemholder. An empty set is
+ * represented as a null pointer.
+ * An element of a long set must be of some pointer type or,
+ * in any case, must have the size of a pointer. Note that
+ * the strict typing rules are not obeyed here.
+ * This package implements the usual operations on sets.
+ * The name of every operation is preceeded by a 'L' to
+ * distinguish it from the operation on 'compact' (bitvector)
+ * sets with a similar name.
+ */
+
+
+lset Lempty_set()
+{
+ return ((lset) 0);
+}
+
+
+bool Lis_elem(x,s)
+ register Lelem_t x;
+ register lset s;
+{
+
+ /* Search the list to see if x is an element of s */
+ while (s != (elem_p) 0) {
+ if (s->e_elem == x) {
+ return TRUE;
+ }
+ s = s->e_next;
+ }
+ return FALSE;
+}
+
+
+Ladd(x,s_p)
+ Lelem_t x;
+ lset *s_p;
+{
+ /* add x to a set. Note that the set is given as in-out
+ * parameter, because it may be changed.
+ */
+
+ elem_p t;
+
+ if (!Lis_elem(x,*s_p)) {
+ t = newelem(); /* allocate a new elemholder */
+ t->e_elem = x;
+ t->e_next = *s_p; /* insert it at the head of the list */
+ *s_p = t;
+ }
+}
+
+
+Lremove(x,s_p)
+ Lelem_t x;
+ lset *s_p;
+{
+ /* Remove x from a set. If x was not an element of
+ * the set, nothing happens.
+ */
+
+ register elem_p *epp, ep;
+ lset s;
+
+ s = *s_p;
+ epp = &s;
+ while ((ep = *epp) != (elem_p) 0) {
+ if (ep->e_elem == x) {
+ *epp = ep->e_next;
+ oldelem(ep);
+ break;
+ } else {
+ epp = &ep->e_next;
+ }
+ }
+ *s_p = s;
+}
+
+
+/* The operations first, next and elem can be used to iterate
+ * over a set. For example:
+ * for (i = Lfirst(s); i != (Lindex) 0; i = Lnext(i,s) {
+ * x = Lelem(i);
+ * use x
+ * }
+ * which is like:
+ * 'for all elements x of s do'
+ * use x
+ */
+
+
+Lindex Lfirst(s)
+ lset s;
+{
+ return ((Lindex) s);
+ /* Note that an index for long sets is just
+ * a pointer to an elemholder.
+ */
+}
+
+
+Lindex Lnext(i,s)
+ Lindex i;
+ lset s;
+{
+ assert(i != (Lindex) 0);
+ return (i->e_next);
+}
+
+
+Lelem_t Lelem(i)
+ Lindex i;
+{
+ return (i->e_elem);
+}
+
+
+
+Ljoin(s1,s2_p)
+ lset s1,*s2_p;
+{
+ /* Join two sets, assign the result to the second set
+ * and delete the first set (i.e. the value of the
+ * first set becomes undefined).
+ */
+
+ register elem_p *epp, ep;
+ lset s2;
+
+ /* First all elements of s1 that are also an element of s2
+ * are removed from the s1 list. The two resulting lists
+ * (for s1 and s2) are linked (s1 first).
+ * Note the usage of epp, which points to a pointer that
+ * points to the next elemholder record of the list.
+ */
+
+ s2 = *s2_p;
+ epp = &s1;
+ while ((ep = *epp) != (elem_p) 0) {
+ if (Lis_elem(ep->e_elem,s2)) {
+ /* remove an element */
+ *epp = ep->e_next;
+ oldelem(ep);
+ } else {
+ epp = &ep->e_next;
+ }
+ }
+ *epp = s2; /* last record of s1 (or s1 itself) now points
+ * to first record of s2.
+ */
+ *s2_p = s1;
+}
+
+
+Ldeleteset(s)
+ lset s;
+{
+ register elem_p ep, next;
+
+ for (ep = s; ep != (elem_p) 0; ep = next) {
+ next = ep->e_next;
+ oldelem(ep);
+ }
+}
+
+
+bool Lis_subset(s1,s2)
+ lset s1,s2;
+{
+ /* See if s1 is a subset of s2 */
+
+ register Lindex i;
+
+ for (i = Lfirst(s1); i != (Lindex) 0; i = Lnext(i,s1)) {
+ if (!Lis_elem(Lelem(i),s2)) return FALSE;
+ }
+ return TRUE;
+}
+
+
+short Lnrelems(s)
+ lset s;
+{
+ /* Compute the number of elements of a set */
+
+ register elem_p ep;
+ register short cnt;
+
+ cnt = 0;
+ for (ep = s; ep != (elem_p) 0; ep = ep->e_next) {
+ cnt++;
+ }
+ return cnt;
+}
--- /dev/null
+/* O P E R A T I O N S F O R
+ * L O N G S E T S
+ */
+
+
+extern lset Lempty_set(); /* () */
+extern bool Lis_elem(); /* (Lelem_t, lset) */
+extern Ladd(); /* (Lelem_t, *lset) */
+extern Lremove(); /* (Lelem_t, *lset) */
+extern Lindex Lfirst(); /* (lset) */
+extern Lindex Lnext(); /* (Lindex, lset) */
+extern Lelem_t Lelem(); /* (Lindex) */
+extern Ljoin(); /* (lset, *lset) */
+extern Ldeleteset(); /* (lset) */
+extern bool Lis_subset(); /* (lset, lset) */
+extern short Lnrelems(); /* (lset) */
--- /dev/null
+#include <stdio.h>
+
+/* MAKECLASSDEF
+ *
+ * This program is used by several phases of the optimizer
+ * to make the file classdefs.h. It reads two files:
+ * - the em_mnem,h file, containing the definitions of the
+ * EM mnemonics
+ * - the class-file, containing tuples:
+ * (mnemonic, src_class, res_class)
+ * where src_class and res_class are integers telling how
+ * to compute the number of bytes popped and pushed
+ * by the instruction.
+ * The output (standard output) is a C array.
+ */
+
+
+#define TRUE 1
+#define FALSE 0
+
+convert(mnemfile,classfile)
+ FILE *mnemfile, *classfile;
+{
+ char mnem1[10], mnem2[10],def[10];
+ int src,res,newcl,opc;
+
+ newcl = TRUE;
+ printf("struct class classtab[] = {\n");
+ printf("\tNOCLASS,\tNOCLASS,\n");
+ /* EM mnemonics start at 1, arrays in C at 0 */
+ 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(classfile,"%s%d%d",mnem2,&src,&res);
+ /* read a line like "op_loc 8 1" */
+ }
+ if (feof(classfile) || strcmp(mnem1,mnem2) != 0) {
+ /* there is no line for this mnemonic, so
+ * it has no class.
+ */
+ printf("\tNOCLASS,\tNOCLASS,\n");
+ newcl = FALSE;
+ } else {
+ printf("\tCLASS%d,\t\tCLASS%d,\n",src,res);
+ /* print a line like "CLASS8, CLASS1," */
+ 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: makeclassdef mnemfile classfile");
+ }
+ if ((f1 = fopen(argv[1],"r")) == NULL) {
+ error("cannot open mnemonic file");
+ }
+ if ((f2 = fopen(argv[2],"r")) == NULL) {
+ error("cannot open class file");
+ }
+ convert(f1,f2);
+}
--- /dev/null
+/* M A P . C */
+
+#include "types.h"
+#include "map.h"
+
+short plength;
+short olength;
+short llength;
+short blength;
+short lplength;
+line_p *lmap;
+bblock_p *lbmap;
+proc_p *pmap ; /* dynamically allocated array that maps
+ * every proc_id to a proc_p.
+ */
+obj_p *omap; /* maps obj_id to obj_p */
+loop_p *lpmap; /* maps loop_id to loop_p */
+bblock_p *bmap; /* maps block_id to bblock_p */
+
+dblock_p fdblock; /* first dblock */
+proc_p fproc; /* first proc */
--- /dev/null
+/* M A P . H */
+
+extern short plength; /* length of pmap, i.e. number of procs */
+extern short olength; /* length of omap, i.e. number of objects */
+extern short llength; /* length of lmap and lbmap, i.e.
+ * # instruction labels in current proc.
+ */
+extern short lplength; /* length of lpmap, i.e. number of loops
+ * in current procedure.
+ */
+extern short blength; /* length of bmap, i.e. number of basic blocks
+ * in current procedure.
+ */
+
+
+extern line_p *lmap; /* contains for every label_id its
+ * defining occurrence (line structure)
+ * label_id --> line_p
+ */
+extern bblock_p *lbmap; /* contains for every label_id its
+ * basic block.
+ * label_id --> bblock_p
+ */
+extern proc_p *pmap; /* contains for every proc_id its proc structure
+ * proc_id --> proc_p
+ */
+extern obj_p *omap; /* contains for every obj_id its object struct
+ * obj_id --> obj_p
+ */
+extern loop_p *lpmap; /* contains for every loop_id its loop struct
+ * loop_id --> loop_p
+ */
+extern bblock_p *bmap; /* contains for every block_id its bblock struct
+ * block_id --> bblock_p
+ */
+
+extern dblock_p fdblock;/* first dblock, heads dblock list */
+extern proc_p fproc; /* first proc, heads proc table */
--- /dev/null
+
+#include <stdio.h>
+#include "types.h"
+#include "debug.h"
+#include "alloc.h"
+#include "global.h"
+#include "lset.h"
+#include "aux.h"
+#include "../../../h/em_spec.h"
+#include "../../../h/em_mnem.h"
+
+struct class {
+ byte src_class;
+ byte res_class;
+};
+
+typedef struct class *class_p;
+
+
+#define NOCLASS 0
+#define CLASS1 1
+#define CLASS2 2
+#define CLASS3 3
+#define CLASS4 4
+#define CLASS5 5
+#define CLASS6 6
+#define CLASS7 7
+#define CLASS8 8
+#define CLASS9 9
+#define CLASS10 10
+#define CLASS11 11
+
+#include "classdefs.h"
+/* The file classdefs.h contains the table classtab. It is
+ * generated automatically from the file classdefs.src.
+ */
+
+STATIC bool classes(instr,src_out,res_out)
+ int instr;
+ int *src_out, *res_out;
+{
+ /* Determine the classes of the given instruction */
+
+ class_p c;
+
+ if (instr < sp_fmnem || instr > sp_lmnem) return FALSE;
+ c = &classtab[instr];
+ if (c->src_class == NOCLASS) return FALSE;
+ *src_out = c->src_class;
+ *res_out = c->res_class;
+ return TRUE;
+}
+
+
+
+STATIC bool uses_arg(class)
+ int class;
+{
+ /* See if a member of the given class uses
+ * an argument.
+ */
+
+ switch(class) {
+ case CLASS1:
+ case CLASS2:
+ case CLASS3:
+ case CLASS4:
+ case CLASS11:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+ /* NOTREACHED */
+}
+
+
+
+STATIC bool uses_2args(class)
+ int class;
+{
+ /* See if a member of the given class uses
+ * 2 arguments.
+ */
+
+ return class == CLASS10;
+}
+
+
+STATIC bool parse_locs(l,c1_out,c2_out)
+ line_p l;
+ offset *c1_out, *c2_out;
+{
+ if (INSTR(l) == op_loc && INSTR(PREV(l)) == op_loc) {
+ *c1_out = off_set(l);
+ *c2_out = off_set(PREV(l));
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+
+STATIC bool check_args(l,src_class,res_class,arg1_out,arg2_out)
+ line_p l;
+ int src_class,res_class;
+ offset *arg1_out, *arg2_out;
+{
+ /* Several EM instructions have an argument
+ * giving the size of the operand(s) of
+ * the instruction. E.g. a 'adi 4' is a 4-byte
+ * addition. The size may also be put on the
+ * stack. In this case we give up our
+ * efforts to recognize the parameter expression.
+ * Some instructions (e.g. CIU) use 2 arguments
+ * that are both on the stack. In this case we
+ * check if both arguments are LOCs (the usual case),
+ * else we give up.
+ */
+
+ if (uses_2args(src_class) || uses_2args(res_class)) {
+ return parse_locs(PREV(l),arg1_out,arg2_out);
+ }
+ if (uses_arg(src_class) || uses_arg(res_class)) {
+ if (TYPE(l) == OPSHORT) {
+ *arg1_out = (offset) SHORT(l);
+ return TRUE;
+ } else {
+ if (TYPE(l) == OPOFFSET) {
+ *arg1_out = OFFSET(l);
+ } else {
+ return FALSE;
+ }
+ }
+ }
+ return TRUE; /* no argument needed */
+}
+
+
+
+STATIC offset nrbytes(class,arg1,arg2)
+ int class;
+ offset arg1,arg2;
+{
+ /* Determine the number of bytes of the given
+ * arguments and class.
+ */
+
+ offset n;
+
+ switch(class) {
+ case CLASS1:
+ n = arg1;
+ break;
+ case CLASS2:
+ n = 2 * arg1;
+ break;
+ case CLASS3:
+ n = arg1 + ws;
+ break;
+ case CLASS4:
+ n = arg1 + ps;
+ break;
+ case CLASS5:
+ n = ws;
+ break;
+ case CLASS6:
+ n = 2 * ws;
+ break;
+ case CLASS7:
+ n = ps;
+ break;
+ case CLASS8:
+ n = 2 * ps;
+ break;
+ case CLASS9:
+ n = 0;
+ break;
+ case CLASS10:
+ n = arg2 + 2*ws;
+ break;
+ case CLASS11:
+ n = arg1 + 2*ps;
+ break;
+ default:
+ assert(FALSE);
+ }
+ return n;
+}
+
+
+
+STATIC attrib(l,expect_out,srcb_out,resb_out)
+ line_p l;
+ offset *expect_out, *srcb_out, *resb_out;
+{
+ /* Determine a number of attributes of an EM
+ * instruction appearing in an expression.
+ * If it is something we don't
+ * expect in such expression (e.g. a store)
+ * expect_out is set to FALSE. Else we
+ * determine the number of bytes popped from
+ * the stack by the instruction and the
+ * number of bytes pushed on the stack as
+ * result.
+ */
+
+ int src_class,res_class;
+ offset arg1, arg2;
+
+ if (l == (line_p) 0 || !classes(INSTR(l),&src_class,&res_class) ||
+ !check_args(l,src_class,res_class,&arg1,&arg2)) {
+ *expect_out = FALSE;
+ } else {
+ *expect_out = TRUE;
+ *srcb_out = nrbytes(src_class,arg1,arg2);
+ *resb_out = nrbytes(res_class,arg1,arg2);
+ }
+}
+
+
+
+bool parse(l,nbytes,l_out,level,action0)
+ line_p l, *l_out;
+ offset nbytes;
+ int level;
+ int (*action0) ();
+{
+ /* This is a recursive descent parser for
+ * EM expressions.
+ * It tries to recognize EM code that loads exactly
+ * 'nbytes' bytes on the stack.
+ * 'l' is the last instruction of this code.
+ * As EM is essentially postfix, this instruction
+ * can be regarded as the root node of an expression
+ * tree. The EM code is traversed from right to left,
+ * i.e. top down. On success, TRUE is returned and
+ * 'l_out' will point to the first instruction
+ * of the recognized code. On toplevel, when an
+ * expression has been recognized, the procedure-parameter
+ * 'action0' is called, with parameters: the first and
+ * last instruction of the expression and the number of
+ * bytes recognized.
+ */
+
+ offset more, expected, sourcebytes,resultbytes;
+ line_p lnp;
+
+ more = nbytes; /* #bytes to be recognized */
+ while (more > 0) {
+ attrib(l,&expected,&sourcebytes,&resultbytes);
+ /* Get the attributes of EM instruction 'l'.
+ * 'expected' denotes if it is something we can use;
+ * 'sourcebytes' and 'resultbytes' are the number of
+ * bytes popped resp. pushed by the instruction
+ * (e.g. 'adi 2' pops 4 bytes and pushes 2 bytes).
+ */
+ if (!expected || (more -= resultbytes) < 0) return FALSE;
+ if (sourcebytes == 0) {
+ /* a leaf of the expression tree */
+ lnp = l;
+ } else {
+ if (!parse(PREV(l),sourcebytes,&lnp,level+1,action0)) {
+ return FALSE;
+ }
+ }
+ if (level == 0) {
+ /* at toplevel */
+ (*action0) (lnp,l,resultbytes);
+ }
+ l = PREV(lnp);
+ }
+ /* Now we've recognized a number of expressions that
+ * together push nbytes on the stack.
+ */
+ *l_out = lnp;
+ return TRUE;
+}
--- /dev/null
+bool parse(); /* (line_p l, *l_out; offset nbytes;
+ * int level; int (*action0) ())
+ * This is a recursive descent parser for
+ * EM expressions.
+ * It tries to recognize EM code that loads exactly
+ * 'nbytes' bytes on the stack.
+ * 'l' is the last instruction of this code.
+ * On toplevel, when an expression has been
+ * recognized, the procedure-parameter
+ * 'action0' is called, with parameters: the first and
+ * last instruction of the expression and the number of
+ * bytes recognized.
+ */
--- /dev/null
+/* P U T . C */
+
+#include <stdio.h>
+#include "types.h"
+#include "global.h"
+#include "debug.h"
+#include "def.h"
+#include "map.h"
+#include "../../../h/em_pseu.h"
+#include "../../../h/em_spec.h"
+#include "lset.h"
+#include "alloc.h"
+#include "put.h"
+
+
+/* the output file */
+
+static FILE *f; /* current output file, can be EM text file,
+ * basic block file, data block file or proc table file.
+ */
+
+
+#define outbyte(b) putc(b,f)
+
+
+/* The output can be either 'typed' or 'untyped'. Typed data
+ * consists of a value preceded by a byte specifying what kind
+ * of value it is (e.g. 2 bytes constant, 4 bytes constant,
+ * proc-id, lab-id, string etc.). Untyped data consists
+ * of the value only. We use typed data for the EM text and
+ * untyped data for all other files.
+ */
+
+/* putlines */
+
+STATIC putargs(ap)
+ register arg_p ap;
+{
+ while (ap != (arg_p) 0) {
+ outbyte((byte) ap->a_type & BMASK);
+ switch(ap->a_type) {
+ case ARGOFF:
+ outoff(ap->a_a.a_offset);
+ break;
+ case ARGINSTRLAB:
+ outlab(ap->a_a.a_instrlab);
+ break;
+ case ARGOBJECT:
+ outobject(ap->a_a.a_obj);
+ break;
+ case ARGPROC:
+ outproc(ap->a_a.a_proc);
+ break;
+ case ARGSTRING:
+ putstr(&ap->a_a.a_string);
+ break;
+ case ARGICN:
+ case ARGUCN:
+ case ARGFCN:
+ outshort(ap->a_a.a_con.ac_length);
+ putstr(&ap->a_a.a_con.ac_con);
+ break;
+ }
+ ap = ap->a_next;
+ }
+ outbyte((byte) ARGCEND);
+}
+
+
+
+STATIC putstr(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;
+ }
+ outshort(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 outoff(off) offset off; {
+
+ outshort( (short) (off&0177777L) );
+ outshort( (short) (off>>16) );
+}
+
+
+STATIC outshort(i) short i; {
+
+ outbyte( (byte) (i&BMASK) );
+ outbyte( (byte) (i>>8) );
+}
+
+
+STATIC outint(i)
+ int i;
+{
+ /* Write an integer to the output file. This routine is
+ * only used when outputting a bitvector-set. We expect an
+ * integer to be either a short or a long.
+ */
+
+ if (sizeof(int) == sizeof(short)) {
+ outshort(i);
+ } else {
+ assert (sizeof(int) == sizeof(offset));
+ outoff(i);
+ }
+}
+
+STATIC outlab(lid) lab_id lid; {
+ outshort((short) lid);
+}
+
+
+STATIC outobject(obj) obj_p obj; {
+ outshort((short) obj->o_id);
+}
+
+
+STATIC outproc(p) proc_p p; {
+ outshort((short) p->p_id);
+}
+
+
+short putlines(l,lf)
+ line_p l;
+ FILE *lf;
+{
+ /* Output the list of em instructions headed by l.
+ * Return the number of instruction written.
+ */
+
+ register line_p lnp;
+ line_p next;
+ short instr;
+ short count= 0;
+
+ f = lf; /* Set f to the EM-text output file */
+ for (lnp = l; lnp != (line_p) 0; lnp = next) {
+ VL(lnp);
+ count++;
+ next = lnp->l_next;
+ instr = INSTR(lnp);
+ outbyte((byte) instr);
+ outbyte((byte) TYPE(lnp));
+ switch(TYPE(lnp)) {
+ case OPSHORT:
+ outshort(SHORT(lnp));
+ break;
+ case OPOFFSET:
+ outoff(OFFSET(lnp));
+ break;
+ case OPINSTRLAB:
+ outlab(INSTRLAB(lnp));
+ break;
+ case OPOBJECT:
+ outobject(OBJ(lnp));
+ break;
+ case OPPROC:
+ outproc(PROC(lnp));
+ break;
+ case OPLIST:
+ putargs(ARG(lnp));
+ break;
+ }
+ oldline(lnp);
+ }
+ return count;
+}
+
+
+
+
+
+/* putdtable */
+
+#define outmark(m) outbyte((byte) m)
+
+
+STATIC putobjects(obj)
+ register obj_p obj;
+{
+ while (obj != (obj_p) 0) {
+ outmark(MARK_OBJ);
+ outshort(obj->o_id);
+ outoff(obj->o_size);
+ outoff(obj->o_off);
+ obj = obj->o_next;
+ }
+}
+
+
+
+STATIC putvalues(arg)
+ register arg_p arg;
+{
+ while (arg != (arg_p) 0) {
+ assert(arg->a_type == ARGOFF);
+ outmark(MARK_ARG);
+ outoff(arg->a_a.a_offset);
+ arg = arg->a_next;
+ }
+}
+putdtable(head,df)
+ dblock_p head;
+ FILE *df;
+{
+ /* Write the datablock table to the data block file df. */
+
+ register dblock_p dbl;
+ register obj_p obj;
+ dblock_p next;
+ register short n = 0;
+
+ f = df; /* set f to the data block output file */
+ /* Count the number of objects */
+ for (dbl = head; dbl != (dblock_p) 0; dbl = dbl->d_next) {
+ for (obj = dbl->d_objlist; obj != (obj_p) 0;
+ obj = obj->o_next) {
+ n++;
+ }
+ }
+ outshort(n); /* The table is preceded by #objects . */
+ for (dbl = head; dbl != (dblock_p) 0; dbl = next) {
+ next = dbl->d_next;
+ outmark(MARK_DBLOCK);
+ outshort(dbl->d_id);
+ outbyte(dbl->d_pseudo);
+ outoff(dbl->d_size);
+ outshort(dbl->d_fragmnr);
+ outbyte(dbl->d_flags1);
+ putobjects(dbl->d_objlist);
+ putvalues(dbl->d_values);
+ olddblock(dbl);
+ }
+ fclose(f);
+ if (omap != (obj_p *) 0) {
+ oldmap(omap,olength); /* release memory for omap */
+ }
+}
+
+
+
+/* putptable */
+
+
+
+STATIC outcset(s)
+ cset s;
+{
+ /* A 'compact' set is represented externally as a row of words
+ * (its bitvector) preceded by its length.
+ */
+
+ register short i;
+
+ outshort(s->v_size);
+ for (i = 0; i <= DIVWL(s->v_size - 1); i++) {
+ outint(s->v_bits[i]);
+ }
+}
+
+
+
+putptable(head,pf,all)
+ proc_p head;
+ FILE *pf;
+ bool all;
+{
+ register proc_p p;
+ proc_p next;
+ register short n = 0;
+ /* Write the proc table */
+
+ f = pf;
+ /* Determine the number of procs */
+ for (p = head; p != (proc_p) 0; p = p->p_next) {
+ n++;
+ }
+ outshort(n); /* The table is preceded by its length. */
+ outshort ((all?1:0)); /* if all=false, only some of the attributes
+ are written. */
+ for (p = head; p != (proc_p) 0; p = next) {
+ next = p->p_next;
+ outshort(p->p_id);
+ outbyte(p->p_flags1);
+ if (p->p_flags1 & PF_BODYSEEN) {
+ /* If we have no access to the EM text of the
+ * body of a procedure, we have no information
+ * about it whatsoever, so there is nothing
+ * to output in that case.
+ */
+ outshort(p->p_nrlabels);
+ outoff(p->p_localbytes);
+ outoff(p->p_nrformals);
+ if (all) {
+ outcset(p->p_change->c_ext);
+ outshort(p->p_change->c_flags);
+ outshort(p->p_use->u_flags);
+ outcset(p->p_calling);
+ Cdeleteset(p->p_change->c_ext);
+ oldchange(p->p_change);
+ olduse(p->p_use);
+ Cdeleteset(p->p_calling);
+ }
+ }
+ oldproc(p);
+ }
+ fclose(f);
+ if (pmap != (proc_p *) 0) {
+ oldmap(pmap,plength); /* release memory for pmap */
+ }
+}
+
+
+
+/* putunit */
+
+STATIC outloop(l)
+ loop_p l;
+{
+ outshort((short) l->lp_id);
+}
+
+
+STATIC outblock(b)
+ bblock_p b;
+{
+ if (b == (bblock_p) 0) {
+ outshort((short) 0);
+ } else {
+ outshort((short) b->b_id);
+ }
+}
+
+
+STATIC outid(e,p)
+ Lelem_t e;
+ int (*p) ();
+{
+ /* Auxiliary routine used by outlset. */
+
+ /* NOSTRICT */
+ (*p) (e);
+}
+
+
+STATIC outlset(s,p)
+ lset s;
+ int (*p) ();
+{
+ /* A 'long' set is represented externally as a
+ * a sequence of elements terminated by a 0 word.
+ * The procedural parameter p is a routine that
+ * prints an id (proc_id, obj_id etc.).
+ */
+
+ register Lindex i;
+
+ for (i = Lfirst(s); i != (Lindex) 0; i = Lnext(i,s)) {
+ outid(Lelem(i),p);
+ }
+ outshort((short) 0);
+}
+
+
+
+putunit(kind,p,l,gf,lf)
+ short kind;
+ proc_p p;
+ line_p l;
+ FILE *gf, *lf;
+{
+ register bblock_p b;
+ register short n = 0;
+ Lindex pi;
+ loop_p lp;
+
+ f = gf;
+ if (kind == LDATA) {
+ outshort(0); /* No basic blocks */
+ n = putlines(l,lf);
+ f = gf;
+ outshort(n);
+ return;
+ }
+ /* Determine the number of basic blocks */
+ for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
+ n++;
+ }
+ outshort(n); /* # basic blocks */
+ outshort(Lnrelems(p->p_loops)); /* # loops */
+ for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
+ n = putlines(b->b_start,lf);
+ f = gf;
+ outblock(b); /* put its block_id */
+ outshort(n); /* #instructions of the block */
+ outlset(b->b_succ, outblock); /* put succ set */
+ outlset(b->b_pred, outblock); /* put pred set */
+ outblock(b->b_idom); /* put id of immediate dominator */
+ outlset(b->b_loops, outloop); /* put loop set */
+ outshort(b->b_flags);
+ }
+ /* The Control Flow Graph of every procedure is followed
+ * by a description of the loops of the procedure.
+ * Every loop contains an id, an entry block and a level.
+ */
+ for (pi = Lfirst(p->p_loops); pi != (Lindex) 0;
+ pi = Lnext(pi,p->p_loops)) {
+ lp = (loop_p) Lelem(pi);
+ outloop(lp); /* id */
+ outshort(lp->lp_level); /* nesting level */
+ outblock(lp->lp_entry); /* loop entry block */
+ outblock(lp->lp_end);
+ oldloop(lp);
+ }
+ Ldeleteset(p->p_loops);
+ /* We will now release the memory of the basic blocks.
+ * Note that it would be incorrect to release a basic block
+ * after it has been written, because there may be references
+ * to it from other (later) blocks.
+ */
+ for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
+ Ldeleteset(b->b_loops);
+ Ldeleteset(b->b_succ);
+ Ldeleteset(b->b_pred);
+ oldbblock(b);
+ }
+ /* Release the memory for the lmap, lbmap, bmap, lpmap tables */
+ if (lmap != (line_p *) 0) oldmap(lmap,llength);
+ if (lbmap != (bblock_p *) 0) oldmap(lbmap,llength);
+ if (bmap != (bblock_p *) 0) oldmap(bmap,blength);
+ if (lpmap != (loop_p *) 0) oldmap(lpmap,lplength);
+ f = lf;
+}
+
+
+/* The following routines are only used by the Inline Substitution phase */
+
+
+STATIC putactuals(alist,cfile)
+ actual_p alist;
+ FILE *cfile;
+{
+ /* output a list of actual parameters */
+
+ actual_p a,next;
+ line_p l;
+ int count;
+
+ count = 0;
+ for (a = alist; a != (actual_p) 0; a = a->ac_next) count++;
+ outshort(count); /* number of actuals */
+ for (a = alist; a != (actual_p) 0; a = next) {
+ next = a->ac_next;
+ count = 0;
+ for (l = a->ac_exp; l != (line_p) 0; l= l->l_next) count++;
+ outshort(count); /* length of actual */
+ outoff(a->ac_size);
+ outbyte(a->ac_inl);
+ count = putlines(a->ac_exp,cfile);
+ oldactual(a);
+ }
+}
+
+
+
+putcall(c,cfile,level)
+ call_p c;
+ FILE *cfile;
+ short level;
+{
+ /* output a call */
+
+ call_p nc,nextc;
+
+
+ f = cfile;
+ outshort(level); /* nesting level */
+ outshort(c->cl_caller->p_id); /* calling proc */
+ outshort(c->cl_id);
+ outshort(c->cl_proc->p_id); /* called proc */
+ outbyte(c->cl_looplevel);
+ outbyte(c->cl_flags);
+ outshort(c->cl_ratio);
+ putactuals(c->cl_actuals,cfile);
+ nc = c->cl_car;
+ oldcall(c);
+ for (; nc != (call_p) 0; nc = nextc) {
+ /* take care of nested calls */
+ nextc = nc->cl_cdr;
+ putcall(nc,cfile,level+1);
+ }
+}
+
+long putcc(head,ccf)
+ calcnt_p head;
+ FILE *ccf;
+{
+ /* Write call-count information to file ccf.
+ * Return the disk address of the info written.
+ */
+
+ calcnt_p cc;
+ long addr;
+ short cnt;
+
+ addr = ftell(ccf);
+ f = ccf;
+ cnt = 0;
+ for (cc = head; cc != (calcnt_p) 0;cc = cc->cc_next) cnt++;
+ outshort(cnt);
+ for (cc = head; cc != (calcnt_p) 0; cc = cc->cc_next) {
+ outproc(cc->cc_proc);
+ outshort(cc->cc_count);
+ }
+ return addr;
+}
--- /dev/null
+ /* O U T P U T R O U T I N E S */
+
+
+
+extern putdtable(); /* (dblock_p head, FILE *df)
+ * Write the data block table to file df,
+ * preceded by its length.
+ */
+extern putptable(); /* (proc_p head, FILE *pf, bool all)
+ * Write the proc table to file pf,
+ * preceded by its length. If all=false,
+ * the fields computed by CF will not be
+ * written (used by the IC phase).
+ */
+extern putunit(); /* (short kind; proc_p p; line_p l;
+ * FILE *gf, *lf)
+ * If kind = LTEXT, then write
+ * the control flow graph to file gf,
+ * preceded by its length (#basic blocks);
+ * write the EM code of every basic block
+ * in the graph to file lf, preceded by
+ * the number of instructions in the block.
+ * Else, (kind = LDATA) just write the
+ * list of instructions (data declarations)
+ * to lf.
+ */
+extern short putlines(); /* (line_p l; FILE *lf)
+ * Output the list of em instructions
+ * headed by l. Return the number of
+ * instructions written.
+ */
+extern putcall(); /* (call_p call; FILE *cfile; short level)
+ * Write the call
+ * with the given id to the given file.
+ * The level is the nesting level, used by
+ * putcall when it calls itself recurively.
+ * It should be 0 on outer levels.
+ */
+extern long putcc(); /* (calcnt_p head; FILE *ccf)
+ * Write call-count information to
+ * file ccf.
+ */
--- /dev/null
+/* S H O W . C */
+
+/* This program can be used to make the output of the 'cf' pass
+ * human readable. It will display either the procedure table,
+ * the datablock table, the basic block table or the EM text,
+ * depending on the flag that is passed as first argument.
+ */
+
+
+
+#include <stdio.h>
+#include "../../../h/em_spec.h"
+#include "../../../h/em_flag.h"
+#include "../../../h/em_pseu.h"
+#include "../share/types.h"
+#include "../share/def.h"
+#include "../share/global.h"
+
+
+#define BMASK 0377
+
+
+
+
+
+
+extern byte em_flag[];
+
+#define space1() printf(" ")
+char format[] = " %-11s%d\n";
+char lformat[] = " %-11s%D\n";
+char sformat[] = " %-10s%s\n";
+char dformat[] = " %-11s%d\n";
+char oformat[] = " %-11s%D\n";
+
+
+
+FILE *f; /* input file */
+
+
+#define getbyte() getc(f)
+
+short getshort()
+{
+ register n;
+
+ n = getbyte();
+ n |= getbyte() << 8;
+ return n;
+}
+
+int getint()
+{
+ /* Read an integer from the input file. This routine is
+ * only used when reading a bitvector-set. We expect an
+ * integer to be either a short or a long.
+ */
+
+ if (sizeof(int) == sizeof(short)) {
+ return getshort();
+ } else {
+ return getoff();
+ }
+}
+
+
+offset getoff()
+{
+ register offset n;
+
+ n = (unsigned) getshort();
+ n |= ((offset) getshort() ) << 16;
+ return n;
+}
+
+
+/* VARARGS 1 */
+error(s,a) char *s,*a; {
+
+ fprintf(stderr,"error");
+ fprintf(stderr,": ");
+ fprintf(stderr,s,a);
+ fprintf(stderr,"\n");
+ abort();
+ exit(-1);
+}
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ if (argc != 3 || argv[1][0] != '-') {
+ error("usage: %s -[ldpbc] filename",argv[0]);
+ }
+ if ((f = fopen(argv[2], "r")) == NULL) {
+ error("cannot open %s", argv[2]);
+ }
+ switch(argv[1][1]) {
+ case 'l':
+ showl();
+ break;
+ case 'd':
+ showd();
+ break;
+ case 'p':
+ showp();
+ break;
+ case 'b':
+ showb();
+ break;
+ case 'c':
+ showc();
+ break;
+ default:
+ error("bad flag");
+ }
+
+ fclose(f);
+}
+
+
+showcset()
+{
+ /* print a compact (bitvector) set */
+
+ short size;
+ register short i,j;
+ int w, mask;
+
+ size = getshort();
+ /* # significant bits in bitvector */
+ i = 1;
+ printf(" { ");
+ if (size == 0) {
+ printf("}\n");
+ return;
+ }
+ for (;;) {
+ w = getint();
+ mask = 1 ;
+ for (j = 1; j <= WORDLENGTH; j++) {
+ if (w & mask) {
+ printf("%d ",i);
+ }
+ if (i++ == size) {
+ printf ("}\n");
+ return;
+ }
+ mask <<= 1;
+ }
+ }
+}
+
+
+
+showp()
+{
+ byte b;
+ short n;
+ short all;
+ printf("total number of procs: %d\n\n",getshort());
+ all = getshort();
+ while (TRUE) {
+ n = getshort();
+ if (feof(f)) break;
+ printf("PROC\n");
+ printf(format,"id =",n);
+ printf(format,"flags1 =",b = getbyte());
+ if (b & PF_BODYSEEN) {
+ printf(format,"# labels =",getshort());
+ printf(lformat,"# locals =",getoff());
+ printf(lformat,"# formals =",getoff());
+ if (all == 1) {
+ printf(" changed ="); showcset();
+ printf(format,"c_flags =",getshort());
+ printf(" used ="); showcset();
+ printf(format,"u_flags =",getshort());
+ printf(" calling ="); showcset();
+ }
+ } else {
+ printf(" body not available\n");
+ }
+ }
+}
+
+
+char *pseudo[] = {"hol", "bss", "rom", "con", "unknown" };
+
+showd()
+{
+ short n;
+ printf("total number of objects: %d\n\n",getshort());
+ while (TRUE) {
+ n = getbyte();
+ if (feof(f)) break;
+ switch(n) {
+ case MARK_DBLOCK:
+ printf("DBLOCK\n");
+ printf(format,"id =",getshort());
+ printf(sformat,"pseudo =",
+ pseudo[(short) getbyte()]);
+ printf(lformat,"size =",getoff());
+ printf(format,"fragment =",getshort());
+ printf(format,"flags1 =",
+ (short) getbyte());
+ break;
+ case MARK_OBJ:
+ printf(" OBJ\n");
+ space1();
+ printf(format,"id =",getshort());
+ space1();
+ printf(lformat,"size =",getoff());
+ space1();
+ printf(lformat,"offset =",getoff());
+ break;
+ case MARK_ARG:
+ printf(" VALUE\n");
+ space1();
+ printf(lformat,"offset =",getoff());
+ break;
+ }
+ }
+}
+
+
+/* The mnemonics of the EM instructions and pseudos */
+
+
+extern char em_mnem[];
+extern char em_pseu[];
+char lab_mnem[] = "instrlab";
+char sym_mnem[] = "datalab";
+
+showinstr()
+{
+ short instr;
+ char *s;
+
+ instr = (short) getbyte();
+ if (feof(f)) return FALSE;
+ if (instr >= sp_fmnem && instr <= sp_lmnem) {
+ s = &(em_mnem[(instr-sp_fmnem) *4]);
+ } else {
+ if (instr == op_lab) {
+ s = lab_mnem;
+ } else {
+ if (instr == ps_sym) {
+ s = sym_mnem;
+ } else {
+ s = &(em_pseu[(instr-sp_fpseu)*4]);
+ }
+ }
+ }
+ printf("%s",s);
+ switch((short) getbyte()) {
+ case OPSHORT:
+ case OPOBJECT:
+ printf(" %d", getshort());
+ break;
+ case OPPROC:
+ printf(" $%d",getshort());
+ break;
+ case OPINSTRLAB:
+ printf(" *%d",getshort());
+ break;
+ case OPOFFSET:
+ printf(" %D", getoff());
+ break;
+ case OPLIST:
+ arglist();
+ break;
+ }
+ printf("\n");
+ return TRUE;
+}
+
+
+showl()
+{
+ while (showinstr());
+}
+
+
+
+arglist()
+{
+ short length;
+ for (;;) {
+ switch((short) getbyte()) {
+ case ARGOBJECT:
+ printf(" %d", getshort());
+ break;
+ case ARGPROC:
+ printf(" $%d",getshort());
+ break;
+ case ARGINSTRLAB:
+ printf(" *%d",getshort());
+ break;
+ case ARGOFF:
+ printf(" %D", getoff());
+ break;
+ case ARGICN:
+ case ARGUCN:
+ case ARGFCN:
+ printf(" %d",getshort());
+ /* Fall through !! */
+ case ARGSTRING:
+ length = getshort();
+ putchar(' ');
+ putchar('"');
+ while (length--) {
+ putchar(getbyte());
+ }
+ putchar('"');
+ break;
+ case ARGCEND:
+ return;
+ }
+ }
+}
+
+
+
+showlset()
+{
+ register short x;
+
+ printf("{ ");
+ while (x = getshort()) {
+ printf("%d ",x);
+ }
+ printf("}\n");
+}
+
+
+
+
+showb()
+{
+ /* basic block file */
+
+ short n,m;
+
+ while (TRUE) {
+ n = getshort();
+ if (feof(f)) break;
+ if (n == 0) {
+ printf("Declaration Unit:\n");
+ printf(dformat,"#instrs =",getshort());
+ printf("\n");
+ continue;
+ }
+ printf("Control Flow Graph:\n");
+ printf("number of basic blocks: %d\n",n);
+ m = getshort(); /* #loops */
+ while (n--) {
+ printf(" BASIC BLOCK\n");
+ printf(dformat,"id =",getshort());
+ printf(dformat,"# instrs =",getshort());
+ printf(" succ =");
+ showlset();
+ printf(" pred =");
+ showlset();
+ printf(dformat,"idom =",getshort());
+ printf(" loops =");
+ showlset();
+ printf(dformat,"flags =",getshort());
+ }
+ printf("number of loops: %d\n",m);
+ while (m--) {
+ printf(" LOOP\n");
+ printf(dformat,"id =",getshort());
+ printf(dformat,"level =",getshort());
+ printf(dformat,"entry =",getshort());
+ printf(dformat,"end =",getshort());
+ }
+ printf("\n");
+ }
+}
+
+
+showc()
+{
+ int n,m,cnt,t;
+
+ cnt = 1;
+ while(TRUE) {
+ t = getshort();
+ if (feof(f)) break;
+ printf("CALL %d\n",cnt++);
+ printf(format,"nestlevel =",t);
+ printf(format,"calling p. =",getshort());
+ printf(format,"call_id =",getshort());
+ printf(format,"called p. =",getshort());
+ printf(format,"looplevel =",getbyte());
+ printf(format,"flags =",getbyte());
+ printf(format,"ratio =",getshort());
+ printf(" actuals:");
+ n = getshort();
+ if (n == 0) {
+ printf(" ---\n");
+ } else {
+ while (n--) {
+ printf("\n");
+ m = getshort();
+ printf(oformat,"size =",getoff());
+ printf(dformat,"inl =",getbyte());
+ while (m--) {
+ printf(" ");
+ showinstr();
+ }
+ }
+ }
+ }
+}
--- /dev/null
+/* S T A C K _ C H A N G E . C */
+
+
+#include <stdio.h>
+#include "../share/types.h"
+#include "../share/debug.h"
+#include "../share/global.h"
+#include "../../../h/em_spec.h"
+#include "../../../h/em_mnem.h"
+
+#include "pop_push.h"
+
+#define IS_LOC(l) (l!=(line_p) 0 && INSTR(l)==op_loc && TYPE(l)==OPSHORT)
+
+int stack_change(l,sign)
+ line_p l;
+ char sign;
+{
+ /* Interpret the string in the third column of the em_table file */
+
+ char *s;
+ bool argdef;
+ short arg;
+ int sum = 0;
+ line_p p = PREV(l);
+ line_p pp = (p == (line_p) 0 ? (line_p) 0 : PREV(p));
+ short i = INSTR(l);
+
+ if (i < sp_fmnem || i > sp_lmnem) {
+ return 0;
+ } else {
+ if (TYPE(l) == OPSHORT) {
+ arg = SHORT(l);
+ if (arg < ws) {
+ /* E.g. a LOI 1 loads word-size bytes,
+ * not 1 byte!
+ */
+ arg = ws;
+ }
+ argdef = TRUE;
+ } else {
+ argdef = FALSE;
+ }
+ }
+ s = pop_push[i];
+ if (*s == '0') return 0;
+ while (*s != '\0') {
+ if (*s++ == sign) {
+ switch(*s) {
+ case 'w':
+ sum += ws;
+ break;
+ case 'd':
+ sum += 2 * ws;
+ break;
+ case 'p':
+ sum += ps;
+ break;
+ case 'a':
+ if (!argdef) return -1;
+ sum += arg;
+ break;
+ case 'x':
+ if (IS_LOC(p)) {
+ sum += SHORT(p);
+ break;
+ } else {
+ return -1;
+ }
+ case 'y':
+ if (IS_LOC(pp)) {
+ sum += SHORT(pp);
+ break;
+ } else {
+ return -1;
+ }
+ case '?':
+ return -1;
+ default:
+ assert(FALSE);
+ }
+ }
+ s++;
+ }
+ return sum;
+}
+
+
+
+line_change(l,ok_out,pop_out,push_out)
+ line_p l;
+ bool *ok_out;
+ int *pop_out,*push_out;
+{
+ short pop,push;
+
+ pop = stack_change(l,'-');
+ push = stack_change(l,'+');
+ *ok_out = (pop != -1 && push != -1);
+ *pop_out = pop;
+ *push_out = push;
+}
+
+
--- /dev/null
+
+/* S T A C K _ C H A N G E . H */
+
+extern line_change(); /* ( line_p l; bool *ok_out; int *pop_out,*push_out)
+ * Try to determine how the stack-height will be
+ * affected by the EM instruction l. 'ok_out' is set
+ * to false if we fail to do so. pop_out and
+ * push_out are set to the number of bytes popped
+ * and pushed. E.g. for an "ADI 2" 4 and 2 are returned.
+ */
--- /dev/null
+/* I N T E R N A L D A T A S T R U C T U R E S O F E G O */
+
+
+/* This file contains the definitions of the global data types.
+ */
+
+
+/* TEMPORARY: */
+#define LONGOFF
+
+
+#define IDL 8 /* identifier length */
+#define DYNAMIC 1
+#define NARGBYTES 14
+#define BMASK 0377
+
+typedef struct argbytes argb_t;
+typedef char byte;
+typedef byte bool;
+typedef long offset;
+typedef short obj_id;
+typedef short proc_id;
+typedef short dblock_id;
+typedef short block_id;
+typedef short loop_id;
+typedef short lab_id;
+
+
+typedef struct dblock *dblock_p;
+typedef struct obj *obj_p;
+typedef struct proc *proc_p;
+typedef struct loop *loop_p;
+typedef struct change *change_p;
+typedef struct use *use_p;
+typedef struct bblock *bblock_p;
+typedef struct line *line_p;
+typedef struct arg *arg_p;
+typedef struct argbytes *argb_p;
+typedef struct elemholder *elem_p;
+typedef struct elemholder *lset;
+typedef struct bitvector *cset;
+typedef elem_p Lindex;
+typedef short Cindex;
+typedef char *Lelem_t;
+typedef short Celem_t;
+
+typedef union pext_t *pext_p;
+typedef union bext_t *bext_p;
+typedef union lpext_t *lpext_p;
+
+/* Intermediate Code generation */
+
+typedef struct sym *sym_p;
+typedef struct prc *prc_p;
+typedef struct num *num_p;
+
+/* Inline Substitution */
+typedef struct call *call_p;
+typedef struct actual *actual_p;
+typedef struct formal *formal_p;
+typedef struct calcnt *calcnt_p;
+typedef short call_id;
+
+/* Strength Reduction */
+typedef struct iv *iv_p;
+typedef struct code_info *code_p;
+
+/* Used-Definition Analysis */
+typedef struct local *local_p;
+
+typedef struct cond_tab *cond_p;
+
+#define TRUE 1
+#define FALSE 0
+
+/* DATABLOCKS */
+
+/* A datablock is a block of global data, declared by means of
+ * a hol, bss, con or rom pseudo. The declaration may be in a file
+ * that is inaccessible to EGO, in which case the pseudo is unknown.
+ * Successive rom or con pseudos that are garanteed to be in the
+ * same fragment (according to the EM definition) share the
+ * same fragment number.
+ */
+
+#define DHOL 0
+#define DBSS 1
+#define DROM 2
+#define DCON 3
+#define DUNKNOWN 4
+
+
+/* The following constants are used by the debugging tools: */
+#define D_FIRST DHOL
+#define D_LAST DUNKNOWN
+
+
+struct dblock {
+ dblock_id d_id; /* unique integer */
+ byte d_pseudo; /* one of DHOL,DBSS,DROM,DCON,DUNKNOWN */
+ offset d_size; /* # bytes, -1 if unknown */
+ obj_p d_objlist; /* list of objects of the data block */
+ byte d_flags1; /* see below */
+ byte d_flags2; /* free to be used by phases */
+ arg_p d_values; /* values, in case of ROM */
+ short d_fragmnr; /* fragment number */
+ dblock_p d_next; /* link to next block */
+};
+
+
+#define DF_EXTERNAL 01 /* Is name visible outside its module? */
+
+/* OBJECTS */
+
+/* An object is a row of successive bytes in one datablock
+ * that are considered to be a whole. E.g. scalar variables,
+ * arrays, I/O buffers etc. are objects.
+ */
+
+struct obj {
+ offset o_off; /* offset within the block */
+ offset o_size; /* size of the object, 0 if not known */
+ obj_id o_id; /* unique integer */
+ dblock_p o_dblock; /* backlink to data block */
+ short o_globnr; /* global variable number */
+ obj_p o_next; /* link */
+};
+
+
+/* PROCEDURES */
+
+struct proc {
+ proc_id p_id; /* unique integer */
+ short p_nrlabels; /* #instruction labels in the proc */
+ offset p_localbytes; /* #bytes for locals */
+ offset p_nrformals; /* #bytes for formals */
+ byte p_flags1; /* see below */
+ byte p_flags2; /* free to be used by phases */
+ bblock_p p_start; /* pointer to first basic block */
+ cset p_calling; /* set of all procs called by this one */
+ lset p_loops; /* information about loops */
+ change_p p_change; /* variables changed by this proc */
+ use_p p_use; /* variables used by this proc */
+ pext_p p_extend; /* pointer to any further information */
+ proc_p p_next; /* link */
+};
+
+
+union pext_t {
+ struct pext_il {
+ call_p p_cals; /* candidate calls for in line expansion */
+ short p_size; /* length of proc (EM-instrs or bytes) */
+ formal_p p_formals; /* description of formals */
+ short p_nrcalled; /* # times proc is called (varying) */
+ long p_ccaddr; /* address of calcnt info on disk */
+ long p_laddr; /* address in EM-text file on disk */
+ short p_orglabels; /* original #labels before substitution */
+ offset p_orglocals; /* original #bytes for locals */
+ } px_il;
+} ;
+
+#define PF_EXTERNAL 01 /* proc is externally visible */
+#define PF_BODYSEEN 02 /* body of proc is available as EM text */
+#define PF_CALUNKNOWN 04 /* proc calls an unavailable procedure */
+#define PF_ENVIRON 010 /* proc does a lxa or lxl */
+#define PF_LPI 020 /* proc may be called indirect */
+#define PF_CALINLOOP 040 /* proc ever called in a loop? (transitively) */
+
+#define CALLED_IN_LOOP(p) p->p_flags1 |= PF_CALINLOOP
+#define IS_CALLED_IN_LOOP(p) (p->p_flags1 & PF_CALINLOOP)
+
+
+/* LOOPS */
+
+ struct loop {
+ loop_id lp_id; /* unique integer */
+ short lp_level; /* nesting level, 0=outermost loop,
+ * 1=loop within loop etc. */
+ bblock_p lp_entry; /* unique entry block of loop */
+ bblock_p lp_end; /* tail of back edge of natural loop */
+ lpext_p lp_extend; /* pointer to any further information */
+};
+
+
+
+union lpext_t {
+ struct lpext_cf {
+ lset lpx_blocks;
+ short lpx_count;
+ bool lpx_messy;
+ } lpx_cf;
+ struct lpext_sr {
+ lset lpx_blocks; /* basic blocks constituting the loop */
+ bblock_p lpx_header; /* header block, 0 if no one allocated yet */
+ bool lpx_done; /* TRUE if we've processed this loop */
+ line_p lpx_instr; /* current last instruction in header block*/
+ } lpx_sr;
+ struct lpext_ra {
+ lset lpx_blocks; /* basic blocks constituting the loop */
+ bblock_p lpx_header; /* header block, 0 if no one allocated yet */
+ } lpx_ra;
+} ;
+
+/* CHANGED/USED VARIABLES INFORMATION */
+
+
+struct change {
+ cset c_ext; /* external variables changed */
+ short c_flags; /* see below */
+};
+
+struct use {
+ short u_flags; /* see below */
+};
+
+
+#define CF_INDIR 01
+#define UF_INDIR 01
+
+
+/* SETS */
+
+
+/* There are 2 set representations:
+ * - long (lset), which is essentially a list
+ * - compact (cset), which is essentially a bitvector
+ */
+
+
+ struct elemholder {
+ char *e_elem; /* pointer to the element */
+ elem_p e_next; /* link */
+};
+
+struct bitvector {
+ short v_size; /* # significant bits */
+ int v_bits[DYNAMIC];/* a row of bits */
+};
+
+
+
+/* BASIC BLOCKS */
+
+
+/* Note that the b_succ and b_pred fields constitute the
+ * Control Flow Graph
+ */
+
+
+ struct bblock {
+ block_id b_id; /* unique integer */
+ line_p b_start; /* pointer to first instruction */
+ lset b_succ; /* set of successor blocks */
+ lset b_pred; /* set of predecessor blocks */
+ bblock_p b_idom; /* immediate dominator */
+ lset b_loops; /* set of loops it is in */
+ short b_flags; /* see below */
+ bext_p b_extend; /* pointer to any further information */
+ bblock_p b_next; /* link to textually next block */
+};
+
+
+union bext_t {
+ struct bext_cf {
+ short bx_semi; /* dfs number of semi-dominator */
+ bblock_p bx_parent; /* parent in dfs spanning tree */
+ lset bx_bucket; /* set of vertices whose sdom is b */
+ bblock_p bx_ancestor; /* ancestor of b in forest, */
+ bblock_p bx_label; /* used by link/eval */
+ } bx_cf;
+ struct bext_ud {
+ cset bx_gen; /* definition generated in b */
+ cset bx_kill; /* defs. outside b killed by b */
+ cset bx_in; /* defs. reaching beginning of b */
+ cset bx_out; /* defs. reaching end of b */
+ cset bx_cgen; /* generated copies */
+ cset bx_ckill; /* killed copies */
+ cset bx_cin; /* copies reaching begin of b */
+ cset bx_cout; /* copies reaching end of b */
+ cset bx_chgvars; /* variables changed by b */
+ } bx_ud;
+ struct bext_lv {
+ cset bx_use; /* variables used before being defined */
+ cset bx_def; /* variables defined before being used */
+ cset bx_lin; /* variables live at entry of b */
+ cset bx_lout; /* variables live at exit of b */
+ } bx_lv;
+ struct bext_ra {
+ short bx_begin; /* number of first instruction of block */
+ short bx_end; /* number of last instruction of block */
+ short bx_usecnt; /* used by minimal_score() */
+ short bx_dist; /* ,, */
+ bool bx_mark; /* ,, */
+ } bx_ra;
+} ;
+
+
+#define BF_STRONG 01
+#define BF_FIRM 02
+
+#define IS_STRONG(b) (b->b_flags&BF_STRONG)
+#define IS_FIRM(b) (b->b_flags&BF_FIRM)
+
+
+/* EM INSTRUCTIONS */
+
+/* Kinds of operand types (l_optype field) */
+
+#define OPNO 0
+#define OPSHORT 1
+#define OPOFFSET 2
+#define OPINSTRLAB 3
+#define OPOBJECT 4
+#define OPPROC 5
+#define OPLIST 6
+
+
+/* The following constants are used by the debugging tools: */
+#define OP_FIRST OPNO
+#define OP_LAST OPLIST
+
+#define LDATA 0
+#define LTEXT 01
+
+struct line {
+ line_p l_next; /* link */
+ byte l_instr; /* instruction */
+ byte l_optype; /* kind of operand, used as tag */
+ line_p l_prev; /* backlink to previous instruction */
+ union {
+ short la_short; /* short: LOC 5 */
+ offset la_offset; /* offset: LDC 20 */
+ lab_id la_instrlab; /* label: BRA *10 */
+ obj_p la_obj; /* object: LOE X+2 */
+ proc_p la_proc; /* proc: CAL F3 */
+ arg_p la_arg; /* arguments: HOL 10,0,0 */
+ } l_a;
+};
+
+
+/* ARGUMENTS */
+
+
+/* String representation of a constant, partitioned into
+ * pieces of NARGBYTES bytes.
+ */
+
+#define ARGOFF 0
+#define ARGINSTRLAB 1
+#define ARGOBJECT 2
+#define ARGPROC 3
+#define ARGSTRING 4
+#define ARGICN 5
+#define ARGUCN 6
+#define ARGFCN 7
+#define ARGCEND 8
+
+
+struct argbytes {
+ argb_p ab_next;
+ short ab_index;
+ char ab_contents[NARGBYTES];
+};
+
+
+struct arg {
+ arg_p a_next; /* link */
+ short a_type; /* kind of argument */
+ union {
+ offset a_offset; /* offset */
+ lab_id a_instrlab; /* instruction label */
+ proc_p a_proc; /* procedure */
+ obj_p a_obj; /* object */
+ argb_t a_string; /* string */
+ struct { /* int/unsigned/float constant */
+ short ac_length; /* size in bytes */
+ argb_t ac_con; /* its string repres. */
+ } a_con;
+ } a_a;
+};
+
+
+
+/* Macros to increase readability: */
+
+#define INSTR(lnp) (lnp->l_instr & BMASK)
+#define TYPE(lnp) lnp->l_optype
+#define PREV(lnp) lnp->l_prev
+#define SHORT(lnp) lnp->l_a.la_short
+#define OFFSET(lnp) lnp->l_a.la_offset
+#define INSTRLAB(lnp) lnp->l_a.la_instrlab
+#define OBJ(lnp) lnp->l_a.la_obj
+#define PROC(lnp) lnp->l_a.la_proc
+#define ARG(lnp) lnp->l_a.la_arg
+
+
+/* Data structures for Intermediate Code generation */
+
+
+struct sym {
+ sym_p sy_next; /* link */
+ char sy_name[IDL]; /* name of the symbol */
+ dblock_p sy_dblock; /* pointer to dblock struct */
+};
+struct prc {
+ prc_p pr_next; /* link */
+ char pr_name[IDL]; /* name of the procedure */
+ proc_p pr_proc; /* pointer tto proc struct */
+};
+
+
+struct num {
+ num_p n_next; /* link */
+ unsigned n_number; /* EM repr. e.g. 120 in 'BRA *120' */
+ lab_id n_labid; /* sequential integer repr. of IC */
+};
+
+
+/* Data structures for Inline Substitution */
+
+struct call {
+ proc_p cl_caller; /* calling procedure */
+ call_id cl_id; /* uniquely denotes a CAL instruction */
+ proc_p cl_proc; /* the called procedure */
+ byte cl_looplevel; /* loop nesting level of the CAL */
+ bool cl_flags; /* flag bits */
+ short cl_ratio; /* indicates 'speed gain / size lost' */
+ call_p cl_cdr; /* link to next call */
+ call_p cl_car; /* link to nested calls */
+ actual_p cl_actuals; /* actual parameter expr. trees */
+};
+
+#define CLF_INLPARS 017 /* min(15,nr. of inline parameters) */
+#define CLF_SELECTED 020 /* is call selected for expansion? */
+#define CLF_EVER_EXPANDED 040 /* ever expanded? e.g. in a nested call. */
+#define CLF_FIRM 0100 /* indicates if the call takes place in a
+ * firm block of a loop (i.e. one that
+ * is always executed, except
+ * -perhaps- at the last iteration).
+ * Used for heuristics only.
+ */
+
+struct actual {
+ line_p ac_exp; /* copy of EM text */
+ /* 0 for actuals that are not inline */
+ offset ac_size; /* number of bytes of parameter */
+ bool ac_inl; /* TRUE if it may be expanded in line */
+ actual_p ac_next; /* link */
+};
+
+
+struct formal {
+ offset f_offset; /* offsetin bytes */
+ byte f_flags; /* flags FF_BAD etc. */
+ byte f_type; /* SINGLE, DOUBLE,POINTER,UNKNOWN */
+ formal_p f_next; /* link */
+};
+
+
+/* flags of formal: */
+
+#define FF_BAD 01
+#define FF_REG 02
+#define FF_ONCEUSED 04
+#define FF_OFTENUSED 06
+#define USEMASK 014
+
+/* types of formals: */
+
+#define SINGLE 1
+#define DOUBLE 2
+#define POINTER 3
+#define UNKNOWN 4
+
+/* 'call-count' information keeps track of the number
+ * of times one procedure calls another. Conceptually,
+ * it may be regarded as a two dimensional array, where
+ * calcnt[p,q] is the number of times p calls q. As this
+ * matrix would be very dense, we use a more efficient
+ * list representation. Every procedure has a list
+ * of calcnt structs.
+ */
+
+struct calcnt {
+ proc_p cc_proc; /* the called procedure */
+ short cc_count; /* # times proc. is called in the
+ * original text of the caller.
+ */
+ calcnt_p cc_next; /* link */
+};
+
+
+/* Data structures for Strength Reduction */
+
+/* An induction variable */
+
+struct iv {
+ offset iv_off; /* offset of the induction variable */
+ line_p iv_incr; /* pointer to last instr. of EM-code that
+ * increments the induction variable */
+ offset iv_step; /* step value */
+};
+
+
+/* All information about a reducible piece of code is collected in
+ * a single structure.
+ */
+
+struct code_info {
+ loop_p co_loop; /* the loop the code is in */
+ bblock_p co_block; /* the basic block the code is in */
+ line_p co_lfirst; /* first instruction of the code */
+ line_p co_llast; /* last instruction of the code */
+ line_p co_ivexpr; /* start of linear expr. using iv */
+ line_p co_endexpr; /* end of the expression */
+ int co_sign; /* sign of iv in above expr */
+ iv_p co_iv; /* the induction variable */
+ offset co_temp; /* temporary variable */
+ int co_tmpsize; /* size of the temp. variable (ws or ps)*/
+ int co_instr; /* the expensive instruction (mli,lar..)*/
+ union {
+ line_p co_loadlc; /* LOC lc instruction (for mult.)*/
+ line_p co_desc; /* load address of descriptor
+ * (for lar etc.) */
+ } c_o;
+};
+
+
+/* Data structures for Use-Definition and Live-Dead Analysis */
+
+struct local {
+ offset lc_off; /* offset of local in stackframe */
+ short lc_size; /* size of local in bytes */
+ short lc_flags; /* see below */
+ offset lc_score; /* score in register message, if regvar */
+ local_p lc_next; /* link, only used when building the list */
+};
+
+/* values of lc_flags */
+
+#define LCF_BAD 01
+/* Set when no ud-info for this local is maintained, e.g. when it is
+ * overlapped by another local.
+ */
+#define LCF_REG 02 /* register variable */
+#define LCF_LIVE 04 /* use by live-dead message generation */
+
+
+struct cond_tab {
+ short mc_cond; /* Denotes a condition e.g. FITBYTE */
+ short mc_tval; /* value for time optimization */
+ short mc_sval; /* value for space optimization */
+ short mc_dummy; /* allignment */
+};
+
+/* conditions: */
+
+#define DEFAULT 0
+#define FITBYTE 1
+#define IN_0_63 2
+#define IN_0_8 3
+
--- /dev/null
+
+EMH=../../../h
+EML=../../../lib
+SHARE=../share
+OBJECTS=sp.o
+MOBJECTS=sp.m
+SHOBJECTS=$(SHARE)/get.o $(SHARE)/put.o $(SHARE)/alloc.o $(SHARE)/global.o $(SHARE)/debug.o $(SHARE)/files.o $(SHARE)/map.o $(SHARE)/lset.o $(SHARE)/cset.o $(SHARE)/aux.o $(SHARE)/stack_chg.o $(SHARE)/go.o
+MSHOBJECTS=$(SHARE)/get.m $(SHARE)/put.m $(SHARE)/alloc.m $(SHARE)/global.m $(SHARE)/debug.m $(SHARE)/files.m $(SHARE)/map.m $(SHARE)/lset.m $(SHARE)/cset.m $(SHARE)/aux.m $(SHARE)/stack_chg.m
+SRC=sp.c
+.SUFFIXES: .m
+
+.c.o:
+ cc $(CFLAGS) -c $<
+.c.m:
+ ack -O -L -c.m $(CFLAGS) $<
+all: $(OBJECTS)
+sp: \
+ $(OBJECTS) $(SHOBJECTS)
+ cc -o sp -i $(OBJECTS) $(SHOBJECTS) $(EML)/em_data.a
+optim: $(MOBJECTS) $(MSHOBJECTS)
+ ego IC CF $(F) CA $(MOBJECTS) $(MSHOBJECTS)
+ ack -O -o sp.ego -.c lfile.m $(EML)/em_data.a
+lpr:
+ pr $(SRC) | lpr
+# the next lines are generated automatically
+# AUTOAUTOAUTOAUTOAUTOAUTO
+sp.o: ../share/alloc.h
+sp.o: ../share/aux.h
+sp.o: ../share/debug.h
+sp.o: ../share/files.h
+sp.o: ../share/get.h
+sp.o: ../share/global.h
+sp.o: ../share/go.h
+sp.o: ../share/lset.h
+sp.o: ../share/map.h
+sp.o: ../share/put.h
+sp.o: ../share/stack_chg.h
+sp.o: ../share/types.h
+sp.o: ../../../h/em_mnem.h
+sp.o: ../../../h/em_spec.h
+stack_chg.o: ../share/debug.h
+stack_chg.o: ../share/global.h
+stack_chg.o: ../share/types.h
+stack_chg.o: ../../../h/em_mnem.h
+stack_chg.o: ../../../h/em_spec.h
+stack_chg.o: pop_push.h
--- /dev/null
+/* S T A C K P O L L U T I O N
+ *
+ * S P . C
+ */
+
+
+#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/aux.h"
+#include "../share/go.h"
+#include "../share/stack_chg.h"
+#include "../../../h/em_mnem.h"
+#include "../../../h/em_spec.h"
+
+
+/* Stack pollution throws away the ASP instructions after a procedure call.
+ * This saves a lot of code, at the cost of some extra stack space.
+ * ASPs that are part of a loop are not removed.
+ */
+
+#define BF_MARK 04
+#define MARK(b) b->b_flags |= BF_MARK
+#define NOT_MARKED(b) (!(b->b_flags&BF_MARK))
+#define IN_LOOP(b) (Lnrelems(b->b_loops) > 0)
+
+STATIC int Ssp; /* number of optimizations */
+
+/* According to the EM definition, the stack must be cleaned up
+ * before any return. However, for some backends it causes no harm
+ * if the stack is not cleaned up. If so, we can do Stack Pollution
+ * more globally.
+ */
+
+STATIC int globl_sp_allowed;
+
+
+#define IS_ASP(l) (INSTR(l) == op_asp && TYPE(l) == OPSHORT && SHORT(l) > 0)
+
+
+STATIC sp_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,"%%SP") == 0)break;
+ }
+ fscanf(f,"%d",&globl_sp_allowed);
+}
+comb_asps(l1,l2,b)
+ line_p l1,l2;
+ bblock_p b;
+{
+ assert(INSTR(l1) == op_asp);
+ assert(INSTR(l2) == op_asp);
+ assert(TYPE(l1) == OPSHORT);
+ assert(TYPE(l2) == OPSHORT);
+
+ SHORT(l2) += SHORT(l1);
+ rm_line(l1,b);
+}
+
+
+
+
+stack_pollution(b)
+ bblock_p b;
+{
+ /* For every pair of successive ASP instructions in basic
+ * block b, try to combine the two into one ASP.
+ */
+
+ register line_p l;
+ line_p asp,next = b->b_start;
+ bool asp_seen = FALSE;
+ int stack_diff,pop,push;
+ bool ok;
+
+ do {
+ stack_diff = 0;
+ for (l = next; l != (line_p) 0; l = next) {
+ next = l->l_next;
+ if (IS_ASP(l)) break;
+ if (asp_seen) {
+ if (INSTR(l) == op_ret) {
+ stack_diff -= SHORT(l);
+ } else {
+ line_change(l,&ok,&pop,&push);
+ if (!ok || (stack_diff -= pop) < 0) {
+ /* can't eliminate last ASP */
+ asp_seen = FALSE;
+ } else {
+ stack_diff += push;
+ }
+ }
+ }
+ }
+ if (asp_seen) {
+ if (l == (line_p) 0) {
+ /* last asp of basic block */
+ if (globl_sp_allowed &&
+ NOT_MARKED(b) && !IN_LOOP(b)) {
+ Ssp++;
+ rm_line(asp,b);
+ }
+ } else {
+ /* try to combine with previous asp */
+ if (SHORT(l) == stack_diff) {
+ Ssp++;
+ comb_asps(asp,l,b);
+ }
+ }
+ }
+ asp = l;
+ asp_seen = TRUE; /* use new ASP for next try! */
+ } while (asp != (line_p) 0);
+}
+
+STATIC bool block_save(b)
+ bblock_p b;
+{
+
+ register line_p l;
+ int stack_diff,pop,push;
+ bool ok;
+
+ stack_diff = 0;
+ for (l = b->b_start; l != (line_p) 0; l = l->l_next) {
+ if (INSTR(l) == op_ret) {
+ stack_diff -= SHORT(l);
+ break;
+ }
+ line_change(l,&ok,&pop,&push);
+ /* printf("instr %d, pop %d,push %d,ok %d\n",INSTR(l),pop,push,ok); */
+ if (!ok || (stack_diff -= pop) < 0) {
+ return FALSE;
+ } else {
+ stack_diff += push;
+ }
+ }
+ return stack_diff >= 0;
+}
+
+
+
+STATIC mark_pred(b)
+ bblock_p b;
+{
+ Lindex i;
+ bblock_p x;
+
+ for (i = Lfirst(b->b_pred); i != (Lindex) 0; i = Lnext(i,b->b_pred)) {
+ x = (bblock_p) Lelem(i);
+ if (NOT_MARKED(x)) {
+ MARK(x);
+ mark_pred(x);
+ }
+ }
+}
+
+
+
+
+
+STATIC mark_unsave_blocks(p)
+ proc_p p;
+{
+ register bblock_p b;
+
+ for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
+ if (NOT_MARKED(b) && !block_save(b)) {
+ MARK(b);
+ mark_pred(b);
+ }
+ }
+}
+
+
+sp_optimize(p)
+ proc_p p;
+{
+ register bblock_p b;
+
+ mark_unsave_blocks(p);
+ for (b = p->p_start; b != 0; b = b->b_next) {
+ stack_pollution(b);
+ }
+}
+
+
+
+
+main(argc,argv)
+ int argc;
+ char *argv[];
+{
+ go(argc,argv,no_action,sp_optimize,sp_machinit,no_action);
+ report("stack adjustments deleted",Ssp);
+ exit(0);
+}
+
+
+
+
+/***** DEBUGGING:
+
+debug_stack_pollution(p)
+ proc_p p;
+{
+ register bblock_p b;
+ register line_p l;
+ int lcnt,aspcnt,instr;
+
+ for (b = p->p_start; b != 0; b = b->b_next) {
+ lcnt = 0; aspcnt = 0;
+ for (l = b->b_start; l != 0; l= l->l_next) {
+ instr = INSTR(l);
+ if (instr >= sp_fmnem && instr <= sp_lmnem) {
+ lcnt++;
+ if (instr == op_asp && off_set(l) > 0) {
+ aspcnt++;
+ }
+ }
+ }
+ printf("%d\t%d\n",aspcnt,lcnt);
+ }
+}
+
+*/