From: bal Date: Mon, 26 Nov 1984 15:04:22 +0000 (+0000) Subject: Initial revision X-Git-Tag: release-5-5~5969 X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=6d481ce4d616042f87c93c8a0111243545fecf62;p=ack.git Initial revision --- diff --git a/util/ego/cj/Makefile b/util/ego/cj/Makefile new file mode 100644 index 000000000..4a2f23d2e --- /dev/null +++ b/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 + diff --git a/util/ego/cj/cj.c b/util/ego/cj/cj.c new file mode 100644 index 000000000..3c27ebcec --- /dev/null +++ b/util/ego/cj/cj.c @@ -0,0 +1,352 @@ +/* C R O S S J U M P I N G + * + * CJ.H + * + */ + +#include +#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); + } +} diff --git a/util/ego/share/alloc.c b/util/ego/share/alloc.c new file mode 100644 index 000000000..c7cbc2d32 --- /dev/null +++ b/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 +#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); +} diff --git a/util/ego/share/alloc.h b/util/ego/share/alloc.h new file mode 100644 index 000000000..8fa5cd401 --- /dev/null +++ b/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(); diff --git a/util/ego/share/aux.c b/util/ego/share/aux.c new file mode 100644 index 000000000..d753ef5db --- /dev/null +++ b/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); +} diff --git a/util/ego/share/aux.h b/util/ego/share/aux.h new file mode 100644 index 000000000..451aa3966 --- /dev/null +++ b/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. + */ diff --git a/util/ego/share/cset.c b/util/ego/share/cset.c new file mode 100644 index 000000000..646fefed1 --- /dev/null +++ b/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; +} diff --git a/util/ego/share/cset.h b/util/ego/share/cset.h new file mode 100644 index 000000000..1c7199c08 --- /dev/null +++ b/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) */ diff --git a/util/ego/share/debug.c b/util/ego/share/debug.c new file mode 100644 index 000000000..104a30767 --- /dev/null +++ b/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 +#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); + } +} diff --git a/util/ego/share/debug.h b/util/ego/share/debug.h new file mode 100644 index 000000000..72da2c683 --- /dev/null +++ b/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 diff --git a/util/ego/share/def.h b/util/ego/share/def.h new file mode 100644 index 000000000..07db9076d --- /dev/null +++ b/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 diff --git a/util/ego/share/files.c b/util/ego/share/files.c new file mode 100644 index 000000000..062306ede --- /dev/null +++ b/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 + +FILE *openfile(name,mode) + char *name,*mode; +{ + FILE *f; + + if ((f = fopen(name,mode)) == NULL) { + error("cannot open %s",name); + } + return f; +} diff --git a/util/ego/share/files.h b/util/ego/share/files.h new file mode 100644 index 000000000..494893b9c --- /dev/null +++ b/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. + */ diff --git a/util/ego/share/get.c b/util/ego/share/get.c new file mode 100644 index 000000000..e09c4ee8b --- /dev/null +++ b/util/ego/share/get.c @@ -0,0 +1,911 @@ +/* S H A R E D F I L E + * + * G E T . C + */ + +#include +#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; +} diff --git a/util/ego/share/get.h b/util/ego/share/get.h new file mode 100644 index 000000000..c3185fb4e --- /dev/null +++ b/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). + */ diff --git a/util/ego/share/global.c b/util/ego/share/global.c new file mode 100644 index 000000000..37d8a00ff --- /dev/null +++ b/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'. + */ diff --git a/util/ego/share/global.h b/util/ego/share/global.h new file mode 100644 index 000000000..198e30fec --- /dev/null +++ b/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'. + */ diff --git a/util/ego/share/go.c b/util/ego/share/go.c new file mode 100644 index 000000000..5a5973581 --- /dev/null +++ b/util/ego/share/go.c @@ -0,0 +1,152 @@ +/* S H A R E D F I L E + * + * G O . C + * + */ + + +#include +#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); + } +} diff --git a/util/ego/share/go.h b/util/ego/share/go.h new file mode 100644 index 000000000..3c3bff1aa --- /dev/null +++ b/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 + */ diff --git a/util/ego/share/init_glob.c b/util/ego/share/init_glob.c new file mode 100644 index 000000000..088d679b1 --- /dev/null +++ b/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; +} diff --git a/util/ego/share/init_glob.h b/util/ego/share/init_glob.h new file mode 100644 index 000000000..984b847d9 --- /dev/null +++ b/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. + */ diff --git a/util/ego/share/locals.c b/util/ego/share/locals.c new file mode 100644 index 000000000..e71c0a985 --- /dev/null +++ b/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); + } +} diff --git a/util/ego/share/locals.h b/util/ego/share/locals.h new file mode 100644 index 000000000..3b101fda5 --- /dev/null +++ b/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) + + diff --git a/util/ego/share/lset.c b/util/ego/share/lset.c new file mode 100644 index 000000000..85348dca3 --- /dev/null +++ b/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; +} diff --git a/util/ego/share/lset.h b/util/ego/share/lset.h new file mode 100644 index 000000000..826b89444 --- /dev/null +++ b/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) */ diff --git a/util/ego/share/makecldef.c b/util/ego/share/makecldef.c new file mode 100644 index 000000000..f656b09be --- /dev/null +++ b/util/ego/share/makecldef.c @@ -0,0 +1,83 @@ +#include + +/* 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); +} diff --git a/util/ego/share/map.c b/util/ego/share/map.c new file mode 100644 index 000000000..654588265 --- /dev/null +++ b/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 */ diff --git a/util/ego/share/map.h b/util/ego/share/map.h new file mode 100644 index 000000000..9c3a4655a --- /dev/null +++ b/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 */ diff --git a/util/ego/share/parser.c b/util/ego/share/parser.c new file mode 100644 index 000000000..94ddb51c0 --- /dev/null +++ b/util/ego/share/parser.c @@ -0,0 +1,277 @@ + +#include +#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; +} diff --git a/util/ego/share/parser.h b/util/ego/share/parser.h new file mode 100644 index 000000000..b09c5f5a0 --- /dev/null +++ b/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. + */ diff --git a/util/ego/share/put.c b/util/ego/share/put.c new file mode 100644 index 000000000..ba26b07f6 --- /dev/null +++ b/util/ego/share/put.c @@ -0,0 +1,528 @@ +/* P U T . C */ + +#include +#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;lengthab_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; +} diff --git a/util/ego/share/put.h b/util/ego/share/put.h new file mode 100644 index 000000000..73c0a47cc --- /dev/null +++ b/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. + */ diff --git a/util/ego/share/show.c b/util/ego/share/show.c new file mode 100644 index 000000000..b7e3d1a21 --- /dev/null +++ b/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 +#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(); + } + } + } + } +} diff --git a/util/ego/share/stack_chg.c b/util/ego/share/stack_chg.c new file mode 100644 index 000000000..89a8056db --- /dev/null +++ b/util/ego/share/stack_chg.c @@ -0,0 +1,104 @@ +/* S T A C K _ C H A N G E . C */ + + +#include +#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; +} + + diff --git a/util/ego/share/stack_chg.h b/util/ego/share/stack_chg.h new file mode 100644 index 000000000..52576264e --- /dev/null +++ b/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. + */ diff --git a/util/ego/share/types.h b/util/ego/share/types.h new file mode 100644 index 000000000..d002ca26b --- /dev/null +++ b/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 + diff --git a/util/ego/sp/Makefile b/util/ego/sp/Makefile new file mode 100644 index 000000000..e33dc9fd7 --- /dev/null +++ b/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 diff --git a/util/ego/sp/sp.c b/util/ego/sp/sp.c new file mode 100644 index 000000000..001c2022c --- /dev/null +++ b/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 +#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); + } +} + +*/