Kaynağa Gözat

Initial revision

bal 40 yıl önce
ebeveyn
işleme
8e4156532d

+ 23 - 0
util/ego/cj/Makefile

@@ -0,0 +1,23 @@
+
+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
+

+ 352 - 0
util/ego/cj/cj.c

@@ -0,0 +1,352 @@
+/* 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);
+	}
+}

+ 475 - 0
util/ego/share/alloc.c

@@ -0,0 +1,475 @@
+/*  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);
+}

+ 87 - 0
util/ego/share/alloc.h

@@ -0,0 +1,87 @@
+/*  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();

+ 246 - 0
util/ego/share/aux.c

@@ -0,0 +1,246 @@
+/*  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);
+}

+ 66 - 0
util/ego/share/aux.h

@@ -0,0 +1,66 @@
+/*  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.
+				 */

+ 277 - 0
util/ego/share/cset.c

@@ -0,0 +1,277 @@
+/*  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;
+}

+ 21 - 0
util/ego/share/cset.h

@@ -0,0 +1,21 @@
+/*  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)			*/

+ 145 - 0
util/ego/share/debug.c

@@ -0,0 +1,145 @@
+/*  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);
+	}
+}

+ 56 - 0
util/ego/share/debug.h

@@ -0,0 +1,56 @@
+/* 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

+ 14 - 0
util/ego/share/def.h

@@ -0,0 +1,14 @@
+/*  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

+ 17 - 0
util/ego/share/files.c

@@ -0,0 +1,17 @@
+/*  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;
+}

+ 33 - 0
util/ego/share/files.h

@@ -0,0 +1,33 @@
+/* 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.
+				 */

+ 911 - 0
util/ego/share/get.c

@@ -0,0 +1,911 @@
+/* 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;
+}

+ 56 - 0
util/ego/share/get.h

@@ -0,0 +1,56 @@
+/*  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).
+					 */

+ 21 - 0
util/ego/share/global.c

@@ -0,0 +1,21 @@
+/* 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'.
+				 */

+ 51 - 0
util/ego/share/global.h

@@ -0,0 +1,51 @@
+/*  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'.
+				 */

+ 152 - 0
util/ego/share/go.c

@@ -0,0 +1,152 @@
+/*  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);
+	}
+}

+ 34 - 0
util/ego/share/go.h

@@ -0,0 +1,34 @@
+/*  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
+			 */

+ 57 - 0
util/ego/share/init_glob.c

@@ -0,0 +1,57 @@
+
+/*  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;
+}

+ 10 - 0
util/ego/share/init_glob.h

@@ -0,0 +1,10 @@
+
+/*  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.
+				  */

+ 240 - 0
util/ego/share/locals.c

@@ -0,0 +1,240 @@
+/*
+ *  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);
+	}
+}

+ 39 - 0
util/ego/share/locals.h

@@ -0,0 +1,39 @@
+
+/* 
+ *  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)
+
+

+ 208 - 0
util/ego/share/lset.c

@@ -0,0 +1,208 @@
+/*  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;
+}

+ 16 - 0
util/ego/share/lset.h

@@ -0,0 +1,16 @@
+/*  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)			*/

+ 83 - 0
util/ego/share/makecldef.c

@@ -0,0 +1,83 @@
+#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);
+}

+ 21 - 0
util/ego/share/map.c

@@ -0,0 +1,21 @@
+/* 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   */

+ 38 - 0
util/ego/share/map.h

@@ -0,0 +1,38 @@
+/* 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 */

+ 277 - 0
util/ego/share/parser.c

@@ -0,0 +1,277 @@
+
+#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;
+}

+ 13 - 0
util/ego/share/parser.h

@@ -0,0 +1,13 @@
+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.
+			 */

+ 528 - 0
util/ego/share/put.c

@@ -0,0 +1,528 @@
+/* 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;
+}

+ 42 - 0
util/ego/share/put.h

@@ -0,0 +1,42 @@
+ /*  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.
+				 */

+ 415 - 0
util/ego/share/show.c

@@ -0,0 +1,415 @@
+/*  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();
+				}
+			}
+		}
+	}
+}

+ 104 - 0
util/ego/share/stack_chg.c

@@ -0,0 +1,104 @@
+/* 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;
+}
+
+

+ 10 - 0
util/ego/share/stack_chg.h

@@ -0,0 +1,10 @@
+
+/* 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.
+			 */

+ 562 - 0
util/ego/share/types.h

@@ -0,0 +1,562 @@
+/* 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
+

+ 46 - 0
util/ego/sp/Makefile

@@ -0,0 +1,46 @@
+
+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

+ 240 - 0
util/ego/sp/sp.c

@@ -0,0 +1,240 @@
+/* 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);
+	}
+}
+
+*/