Initial revision

This commit is contained in:
bal 1984-11-26 15:04:22 +00:00
parent 6a9e49f683
commit 6d481ce4d6
38 changed files with 5986 additions and 0 deletions

23
util/ego/cj/Makefile Normal file
View file

@ -0,0 +1,23 @@
EMH=../../../h
EML=../../../lib
SHARE=../share
OBJECTS=cj.o
SHOBJECTS=$(SHARE)/get.o $(SHARE)/put.o $(SHARE)/alloc.o $(SHARE)/global.o $(SHARE)/debug.o $(SHARE)/files.o $(SHARE)/map.o $(SHARE)/lset.o $(SHARE)/cset.o $(SHARE)/aux.o $(SHARE)/stack_chg.o $(SHARE)/go.o
MSHOBJECTS=$(SHARE)/get.m $(SHARE)/put.m $(SHARE)/alloc.m $(SHARE)/global.m $(SHARE)/debug.m $(SHARE)/files.m $(SHARE)/map.m $(SHARE)/lset.m $(SHARE)/cset.m $(SHARE)/aux.m $(SHARE)/stack_chg.m
SRC=cj.c
.SUFFIXES: .m
.c.o:
cc $(CFLAGS) -c $<
.c.m:
ack -O -L -c.m $(CFLAGS) $<
all: $(OBJECTS)
cj: \
$(OBJECTS) $(SHOBJECTS)
cc -o cj -i $(OBJECTS) $(SHOBJECTS) $(EML)/em_data.a
lpr:
pr $(SRC) | lpr
# the next lines are generated automatically
# AUTOAUTOAUTOAUTOAUTOAUTO

352
util/ego/cj/cj.c Normal file
View file

@ -0,0 +1,352 @@
/* C R O S S J U M P I N G
*
* CJ.H
*
*/
#include <stdio.h>
#include "../share/types.h"
#include "../share/debug.h"
#include "../share/global.h"
#include "../share/files.h"
#include "../share/get.h"
#include "../share/put.h"
#include "../share/lset.h"
#include "../share/map.h"
#include "../share/alloc.h"
#include "../share/aux.h"
#include "../share/def.h"
#include "../share/stack_chg.h"
#include "../share/go.h"
#include "../../../h/em_mnem.h"
#include "../../../h/em_spec.h"
/* Cross jumping performs optimzations like:
*
* if cond then goto L1; if cond then goto L1
* S1; -----> S1;
* S2; goto L3;
* goto L2; L1:
* L1: S3;
* S3; L3:
* S2; S2;
* L2:
*
* CJ looks for two basic blocks b1 and b2 with the following properties:
* - there exists a basic block S such that SUCC(b1) = SUCC(b2) = {S}
* (so both have only 1 successor)
* - the last N (N > 0) instructions of b1 and b2, not counting a possible
* BRAnch instruction, are the same.
* As a result of the first condition, at least of the two blocks must end
* on an (unconditional) BRAnch instruction. If both end on a BRA, one block
* is chosen at random. Assume this block is b1. A new label L is put just
* before the N common instructions of block b2 (so this block is split
* into two). The BRA of b1 is changed into a BRA L. So dynamically the same
* instructions are executed in a slightly different order; yet the size of
* the code has become smaller.
*/
STATIC int Scj; /* number of optimizations found */
#define DLINK(l1,l2) l1->l_next=l2; l2->l_prev=l1
STATIC bool same_instr(l1,l2)
line_p l1,l2;
{
/* See if l1 and l2 are the same instruction */
if (l1 == 0 || l2 == 0 || TYPE(l1) != TYPE(l2)) return FALSE;
if (INSTR(l1) != INSTR(l2)) return FALSE;
switch(TYPE(l1)) {
case OPSHORT: return SHORT(l1) == SHORT(l2);
case OPOFFSET: return OFFSET(l1) == OFFSET(l2);
case OPPROC: return PROC(l1) == PROC(l2);
case OPOBJECT: return OBJ(l1) == OBJ(l2);
case OPINSTRLAB: return INSTRLAB(l1) == INSTRLAB(l2);
case OPNO: return TRUE;
default: return FALSE;
}
}
STATIC line_p last_mnem(b)
bblock_p b;
{
/* Determine the last line of a list */
register line_p l;
for (l = b->b_start; l->l_next != (line_p) 0; l = l->l_next);
while (INSTR(l) < sp_fmnem || INSTR(l) > sp_lmnem) l = PREV(l);
return l;
}
STATIC bool is_desirable(text)
line_p text;
{
/* We avoid to generate a BRAnch in the middle of some expression,
* as the code generator will write the contents of the fakestack
* to the real stack if it encounters a BRA. We do not avoid to
* split the parameter-pushing code of a subroutine call into two,
* as the parameters are pushed on the real stack anyway.
* So e.g. "LOL a ; LOL b; ADI" will not be split, but
* "LOL a; LOL b; CAL f" may be split.
*/
line_p l;
bool ok;
int stack_diff,pop,push;
stack_diff = 0;
for (l = text; l != (line_p) 0; l = l->l_next) {
switch(INSTR(l)) {
case op_cal:
case op_asp:
case op_bra:
return TRUE;
}
line_change(l,&ok,&pop,&push);
/* printf("instr %d, pop %d, push %d, ok %d\n",INSTR(l),pop,push,ok); */
if (!ok || (stack_diff -= pop) < 0) {
return FALSE;
} else {
stack_diff += push;
}
}
return TRUE;
}
STATIC cp_loops(b1,b2)
bblock_p b1,b2;
{
/* Copy the loopset of b2 to b1 */
Lindex i;
loop_p lp;
for (i = Lfirst(b2->b_loops); i != (Lindex) 0;
i = Lnext(i,b2->b_loops)) {
lp = (loop_p) Lelem(i);
Ladd(lp,&b1->b_loops);
}
}
STATIC jump_cross(l1,l2,b1,b2)
line_p l1,l2;
bblock_p b1,b2;
{
/* A cross-jump from block b2 to block b1 is found; the code in
* block b2 from line l2 up to the BRAnch is removed; block b1 is
* split into two; the second part consists of a new label
* followed by the code from l1 till the end of the block.
*/
line_p l;
bblock_p b;
bblock_p s;
/* First adjust the control flow graph */
b = freshblock(); /* create a new basic block */
b->b_succ = b1->b_succ;
/* SUCC(b1) = {b} */
b1->b_succ = Lempty_set(); Ladd(b,&b1->b_succ);
/* SUCC(b2) = {b} */
Ldeleteset(b2->b_succ); b2->b_succ = Lempty_set(); Ladd(b,&b2->b_succ);
/* PRED(b) = {b1,b2} */
b->b_pred = Lempty_set(); Ladd(b1,&b->b_pred); Ladd(b2,&b->b_pred);
/* PRED(SUCC(b)) := PRED(SUCC(b)) - {b1,b2} + {b} */
assert(Lnrelems(b->b_succ) == 1);
s = (bblock_p) Lelem(Lfirst(b->b_succ));
Lremove(b1,&s->b_pred); Lremove(b2,&s->b_pred); Ladd(b,&s->b_pred);
cp_loops(b,b1);
b->b_idom = common_dom(b1,b2);
b->b_flags = b1->b_flags;
b->b_next = b1->b_next;
b1->b_next = b;
/* Now adjust the EM text */
l = PREV(l1);
if (l == (line_p) 0) {
b1->b_start = (line_p) 0;
} else {
l->l_next = (line_p) 0;
}
l = newline(OPINSTRLAB);
l->l_instr = op_lab;
INSTRLAB(l) = freshlabel();
DLINK(l,l1);
b->b_start = l;
for (l = l2; INSTR(l) != op_bra; l = l->l_next) {
assert (l != (line_p) 0);
rm_line(l,b2);
}
INSTRLAB(l) = INSTRLAB(b->b_start);
}
STATIC bool try_tail(b1,b2)
bblock_p b1,b2;
{
/* See if b1 and b2 end on the same sequence of instructions */
line_p l1,l2;
bblock_p b = (bblock_p) 0;
int cnt = 0;
/* printf("try block %d and %d\n",b1->b_id,b2->b_id); */
if (b1->b_start == (line_p) 0 || b2->b_start == (line_p) 0) return FALSE;
l1 = last_mnem(b1);
l2 = last_mnem(b2);
/* printf("consider:\n"); showinstr(l1); showinstr(l2); */
if (INSTR(l1) == op_bra) {
b = b1;
l1 = PREV(l1);
}
if (INSTR(l2) == op_bra) {
b = b2;
l2 = PREV(l2);
}
assert(b != (bblock_p) 0);
while(same_instr(l1,l2)) {
cnt++;
l1 = PREV(l1);
l2 = PREV(l2);
/* printf("consider:\n"); showinstr(l1); showinstr(l2); */
}
if (cnt >= 1) {
l1 = (l1 == 0 ? b1->b_start : l1->l_next);
l2 = (l2 == 0 ? b2->b_start : l2->l_next);
if (is_desirable(l1)) {
if (b == b1) {
jump_cross(l2,l1,b2,b1);
Scj++;
} else {
jump_cross(l1,l2,b1,b2);
Scj++;
}
return TRUE;
}
}
return FALSE;
}
STATIC bool try_pred(b)
bblock_p b;
{
/* See if there is any pair (b1,b2), both in PRED(b) for
* which we can perform cross jumping.
*/
register bblock_p b1,b2;
register Lindex i,j;
lset s = b->b_pred;
for (i = Lfirst(s); i != (Lindex) 0; i = Lnext(i,s)) {
b1 = (bblock_p) Lelem(i);
if (Lnrelems(b1->b_succ) != 1) continue;
for (j = Lfirst(s); j != (Lindex) 0; j = Lnext(j,s)) {
b2 = (bblock_p) Lelem(j);
if (b1 != b2 && Lnrelems(b2->b_succ) == 1) {
if (try_tail(b1,b2)) return TRUE;
}
}
}
return FALSE;
}
cj_optimize(p)
proc_p p;
{
/* Perform cross jumping for procedure p.
* In case cases a cross-jumping optimization which give
* new opportunities for further cross-jumping optimizations.
* Hence we repeat the whole process for the entire procedure,
* untill we find no further optimizations.
*/
bblock_p b;
bool changes = TRUE;
while(changes) {
changes = FALSE;
b = p->p_start;
while (b != (bblock_p) 0) {
if (try_pred(b)) {
changes = TRUE;
} else {
b = b->b_next;
}
}
}
}
main(argc,argv)
int argc;
char *argv[];
{
go(argc,argv,no_action,cj_optimize,no_action,no_action);
report("cross jumps",Scj);
exit(0);
}
/******
* Debugging stuff
*/
extern char em_mnem[]; /* The mnemonics of the EM instructions. */
STATIC showinstr(lnp) line_p lnp; {
/* Makes the instruction in `lnp' human readable. Only lines that
* can occur in expressions that are going to be eliminated are
* properly handled.
*/
if (lnp == 0) return;
if (INSTR(lnp) < sp_fmnem || INSTR(lnp) > sp_lmnem) {
printf("\t*** ?\n");
return;
}
printf("\t%s", &em_mnem[4 * (INSTR(lnp)-sp_fmnem)]);
switch (TYPE(lnp)) {
case OPNO:
break;
case OPSHORT:
printf(" %d", SHORT(lnp)); break;
case OPOBJECT:
printf(" %d", OBJ(lnp)->o_id); break;
case OPOFFSET:
printf(" %D", OFFSET(lnp)); break;
default:
printf(" ?"); break;
}
printf("\n");
} /* showinstr */
STATIC print_list(list,b1,b2,p)
line_p list;
bblock_p b1,b2;
proc_p p;
{
line_p l;
printf("block %d and %d of proc %d:\n",b1->b_id,b2->b_id,p->p_id);
for (l = list; l != 0; l = l->l_next) {
showinstr(l);
}
}

475
util/ego/share/alloc.c Normal file
View file

@ -0,0 +1,475 @@
/* S H A R E D F I L E
*
* A L L O C . C
*/
#include <stdio.h>
#include "types.h"
#include "debug.h"
#include "alloc.h"
short * myalloc();
short * malloc();
#ifdef DEBUG
STATIC unsigned maxuse, curruse;
short *newcore(size)
int size;
{
if ((curruse += (unsigned) (size+2)) > maxuse) maxuse = curruse;
return myalloc(size);
}
oldcore(p,size)
short *p;
int size;
{
curruse -= (size+2);
free(p);
}
coreusage()
{
fprintf(stderr,"Maximal core usage (excl. buffers):%u\n",maxuse);
}
#endif
/*
* The following two sizetables contain the sizes of the various kinds
* of line and argument structures.
* The assumption when making the tables was that every non-byte object
* had to be aligned on an even boundary. On machines where alignment
* is worse ( for example a long has to be aligned on a longword bound )
* these tables should be revised.
* A wasteful but safe approach is to replace every line of them by
* sizeof(line_t)
* and
* sizeof(arg_t)
* respectively.
*/
#ifndef NOTCOMPACT
int lsizetab[] = {
2*sizeof(line_p)+2*sizeof(byte),
2*sizeof(line_p)+2*sizeof(byte)+sizeof(short),
2*sizeof(line_p)+2*sizeof(byte)+sizeof(offset),
2*sizeof(line_p)+2*sizeof(byte)+sizeof(lab_id),
2*sizeof(line_p)+2*sizeof(byte)+sizeof(obj_p),
2*sizeof(line_p)+2*sizeof(byte)+sizeof(proc_p),
2*sizeof(line_p)+2*sizeof(byte)+sizeof(arg_p),
};
int asizetab[] = {
sizeof(arg_p)+sizeof(short)+sizeof(offset),
sizeof(arg_p)+sizeof(short)+sizeof(lab_id),
sizeof(arg_p)+sizeof(short)+sizeof(obj_p),
sizeof(arg_p)+sizeof(short)+sizeof(proc_p),
sizeof(arg_p)+sizeof(short)+sizeof(argb_t),
sizeof(arg_p)+sizeof(short)+sizeof(short)+sizeof(argb_t),
sizeof(arg_p)+sizeof(short)+sizeof(short)+sizeof(argb_t),
sizeof(arg_p)+sizeof(short)+sizeof(short)+sizeof(argb_t)
};
#else
int lsizetab[] = {
sizeof(struct line),
sizeof(struct line),
sizeof(struct line),
sizeof(struct line),
sizeof(struct line),
sizeof(struct line),
sizeof(struct line)
};
int asizetab[] = {
sizeof (struct arg),
sizeof (struct arg),
sizeof (struct arg),
sizeof (struct arg),
sizeof (struct arg),
sizeof (struct arg),
sizeof (struct arg),
sizeof (struct arg)
};
#endif
/*
* alloc routines:
* Two parts:
* 1) typed alloc and free routines
* 2) untyped raw core allocation
*/
/*
* PART 1
*/
line_p newline(optyp) int optyp; {
register line_p lnp;
register kind=optyp;
lnp = (line_p) newcore(lsizetab[kind]);
TYPE(lnp) = optyp;
return(lnp);
}
oldline(lnp) register line_p lnp; {
register kind=TYPE(lnp)&BMASK;
if (kind == OPLIST)
oldargs(ARG(lnp));
oldcore((short *) lnp,lsizetab[kind]);
}
arg_p newarg(kind) int kind; {
register arg_p ap;
ap = (arg_p) newcore(asizetab[kind]);
ap->a_type = kind;
return(ap);
}
oldargs(ap) register arg_p ap; {
register arg_p next;
while (ap != (arg_p) 0) {
next = ap->a_next;
switch(ap->a_type) {
case ARGSTRING:
oldargb(ap->a_a.a_string.ab_next);
break;
case ARGICN:
case ARGUCN:
case ARGFCN:
oldargb(ap->a_a.a_con.ac_con.ab_next);
break;
}
oldcore((short *) ap,asizetab[ap->a_type]);
ap = next;
}
}
oldargb(abp) register argb_p abp; {
register argb_p next;
while (abp != (argb_p) 0) {
next = abp->ab_next;
oldcore((short *) abp,sizeof (argb_t));
abp = next;
}
}
num_p newnum() {
return((num_p) newcore(sizeof(struct num)));
}
oldnum(lp) num_p lp; {
oldcore((short *) lp,sizeof(struct num));
}
sym_p newsym() {
return((sym_p) newcore(sizeof(struct sym)));
}
oldsym(sp) sym_p sp; {
oldcore((short *) sp,sizeof(struct sym));
}
prc_p newprc() {
return((prc_p) newcore(sizeof(struct prc)));
}
oldprc(pp) prc_p pp; {
oldcore((short *) pp,sizeof(struct prc));
}
argb_p newargb() {
return((argb_p) newcore(sizeof(argb_t)));
}
obj_p newobject() {
return((obj_p) newcore(sizeof(struct obj)));
}
oldobjects(op) register obj_p op; {
register obj_p next;
while (op != (obj_p) 0) {
next = op->o_next;
oldcore((short *) op, sizeof(struct obj));
op = next;
}
}
proc_p newproc() {
return((proc_p) newcore(sizeof(struct proc)));
}
oldproc(p) proc_p p; {
oldcore((short *) p, sizeof(struct proc));
}
dblock_p newdblock() {
return((dblock_p) newcore(sizeof(struct dblock)));
}
olddblock(dbl) dblock_p dbl; {
oldobjects(dbl->d_objlist);
oldargs(dbl->d_values);
oldcore((short *) dbl, sizeof(struct dblock));
}
bblock_p newbblock() {
return((bblock_p) newcore(sizeof(struct bblock)));
}
oldbblock(b) bblock_p b; {
oldcore((short *) b, sizeof(struct bblock));
}
short **newmap(length) short length; {
return((short **) newcore((length+1) * sizeof(short *)));
}
oldmap(mp,length) short **mp, length; {
oldcore((short *) mp, (length+1) * sizeof(short *));
}
elem_p newelem() {
return((elem_p) newcore(sizeof(struct elemholder)));
}
oldelem(ep) elem_p ep; {
oldcore((short *) ep, sizeof(struct elemholder));
}
cset newbitvect(n) short n; {
return((cset) newcore((n-1)*sizeof(int) + sizeof(struct bitvector)));
/* sizeof(struct bitvector) equals to the size of a struct with
* one short, followed by one ALLIGNED int. So the above statement
* also works e.g. on a VAX.
*/
}
oldbitvect(s,n) cset s; short n; {
oldcore((short *) s, (n-1)*sizeof(int) + sizeof(struct bitvector));
}
loop_p newloop() {
return((loop_p) newcore(sizeof(struct loop)));
}
oldloop(lp) loop_p lp; {
oldcore((short *) lp, sizeof(struct loop));
}
use_p newuse() {
return((use_p) newcore(sizeof(struct use)));
}
olduse(u) use_p u; {
oldcore((short *) u, sizeof(struct use));
}
change_p newchange() {
return((change_p) newcore(sizeof(struct change)));
}
oldchange(c) change_p c; {
oldcore((short *) c, sizeof(struct change));
}
iv_p newiv() {
return((iv_p) newcore(sizeof(struct iv)));
}
oldiv(i) iv_p i; {
oldcore((short *) i, sizeof(struct iv));
}
code_p newcinfo() {
return((code_p) newcore(sizeof(struct code_info)));
}
oldcinfo(c) code_p c; {
oldcore((short *) c, sizeof(struct code_info));
}
call_p newcall() {
return((call_p) newcore(sizeof(struct call)));
}
oldcall(c) call_p c; {
oldcore((short *) c, sizeof(struct call));
}
actual_p newactual() {
return((actual_p) newcore(sizeof(struct actual)));
}
oldactual(a) actual_p a; {
oldcore((short *) a, sizeof(struct actual));
}
formal_p newformal() {
return((formal_p) newcore(sizeof(struct formal)));
}
oldformal(f) formal_p f; {
oldcore((short *) f, sizeof(struct formal));
}
calcnt_p newcalcnt() {
return ((calcnt_p) newcore(sizeof(struct calcnt)));
}
oldcalcnt(cc) calcnt_p cc; {
oldcore((short *) cc, sizeof(struct calcnt));
}
local_p newlocal() {
return ((local_p) newcore(sizeof(struct local)));
}
oldlocal(lc) local_p lc; {
oldcore((short *) lc, sizeof(struct local));
}
short *newtable(length) short length; {
return((short *) newcore((length+1) * sizeof(short)));
}
oldtable(mp,length) short **mp, length; {
oldcore((short *) mp, (length+1) * sizeof(short));
}
char **newnametab(tablen,namelen)
short tablen,namelen;
{
register char **np, **tab;
tab = (char **) newmap(tablen);
for (np = &tab[1]; np <= &tab[tablen]; np++) {
*np = (char *) newcore(namelen);
}
return tab;
}
bext_p newcfbx() {
return ((bext_p) newcore(sizeof(struct bext_cf)));
}
oldcfbx(bxp) bext_p bxp; {
oldcore((short *) bxp,sizeof(struct bext_cf));
}
lpext_p newcflpx() {
return ((lpext_p) newcore (sizeof(struct lpext_cf)));
}
oldcflpx(lxp) lpext_p lxp; {
oldcore((short *) lxp,sizeof(struct lpext_cf));
}
lpext_p newsrlpx() {
return ((lpext_p) newcore (sizeof(struct lpext_sr)));
}
oldsrlpx(lxp) lpext_p lxp; {
oldcore((short *) lxp,sizeof(struct lpext_sr));
}
pext_p newilpx() {
return ((pext_p) newcore(sizeof(struct pext_il)));
}
oldilpx(pxp) pext_p pxp; {
oldcore((short *) pxp,sizeof(struct pext_il));
}
bext_p newudbx() {
return ((bext_p) newcore(sizeof(struct bext_ud)));
}
oldudbx(bxp) bext_p bxp; {
oldcore((short *) bxp,sizeof(struct bext_ud));
}
bext_p newlvbx() {
return ((bext_p) newcore(sizeof(struct bext_lv)));
}
oldlvbx(bxp) bext_p bxp; {
oldcore((short *) bxp,sizeof(struct bext_lv));
}
lpext_p newralpx() {
return ((lpext_p) newcore (sizeof(struct lpext_ra)));
}
oldralpx(lxp) lpext_p lxp; {
oldcore((short *) lxp,sizeof(struct lpext_ra));
}
bext_p newrabx() {
return ((bext_p) newcore(sizeof(struct bext_ra)));
}
oldrabx(bxp) bext_p bxp; {
oldcore((short *) bxp,sizeof(struct bext_ra));
}
cond_p newcondtab(l) int l;
{
return (cond_p) newcore(l * (sizeof (struct cond_tab)));
}
oldcondtab(tab) cond_p tab;
{
int i;
for (i = 0; tab[i].mc_cond != DEFAULT; i++);
oldcore((short *) tab,((i+1) * sizeof (struct cond_tab)));
}
short *myalloc(size) register size; {
register short *p,*q;
p = malloc(size);
if (p == 0)
error("out of memory");
for(q=p;size>0;size -= sizeof(short))
*q++ = 0;
return(p);
}

87
util/ego/share/alloc.h Normal file
View file

@ -0,0 +1,87 @@
/* I N T E R M E D I A T E C O D E
*
* C O R E A L L O C A T I O N A N D D E A L L O C A T I O N
*/
#ifdef DEBUG
extern short *newcore();
extern oldcore();
#else
extern short *myalloc();
#define newcore(size) myalloc(size)
#define oldcore(p,size) free(p)
#endif
#define newstruct(t) (newcore (sizeof (struct t)))
#define oldstruct(t,p) oldcore((short *) p,sizeof (struct t))
extern line_p newline(); /* (byte optype) */
extern dblock_p newdblock();
extern obj_p newobject();
extern proc_p newproc();
extern arg_p newarg(); /* (byte argtype) */
extern argb_p newargb();
extern bblock_p newbblock();
extern short **newmap(); /* (short length) */
extern elem_p newelem();
extern cset newbitvect(); /* (short nrbytes) */
extern loop_p newloop();
extern use_p newuse();
extern change_p newchange();
extern cond_p newcondtab();
extern oldline() ;
extern oldargs() ;
extern oldargb() ;
extern oldobjects() ;
extern oldproc() ;
extern olddblock() ;
extern oldbblock();
extern oldmap();
extern oldelem();
extern oldbitvect(); /* (cset s, short nrbytes) */
extern oldloop();
extern olduse();
extern oldchange();
extern oldcondtab();
extern sym_p newsym();
extern prc_p newprc();
extern num_p newnum();
extern oldnum() ;
extern oldsym();
extern oldprc();
extern iv_p newiv();
extern oldiv();
extern code_p newcinfo();
extern oldcinfo();
extern call_p newcall();
extern oldcall();
extern actual_p newactual();
extern oldactual();
extern formal_p newformal();
extern oldformal();
extern calcnt_p newcalcnt();
extern oldcalcnt();
extern local_p newlocal();
extern oldlocal();
extern short *newtable();
extern oldtable();
extern char **newnametab();
extern bext_p newcfbx();
extern oldcfbx();
extern lpext_p newcflpx();
extern oldcflpx();
extern lpext_p newsrlpx();
extern oldsrlpx();
extern pext_p newilpx();
extern oldilpx();
extern bext_p newudbx();
extern oldudbx();
extern bext_p newlvbx();
extern oldlvbx();
extern bext_p newrabx();
extern oldrabx();
extern lpext_p newralpx();
extern oldralpx();

246
util/ego/share/aux.c Normal file
View file

@ -0,0 +1,246 @@
/* S H A R E D F I L E
*
* A U X I L I A R Y R O U T I N E S
*
*/
#include "../share/types.h"
#include "../share/debug.h"
#include "../share/global.h"
#include "../share/alloc.h"
#include "../share/aux.h"
#include "../share/map.h"
#include "../share/lset.h"
#include "../../../h/em_mes.h"
#include "../../../h/em_pseu.h"
offset off_set(lnp)
line_p lnp;
{
switch(lnp->l_optype) {
case OPSHORT:
return (offset) SHORT(lnp);
case OPOFFSET:
return OFFSET(lnp);
default:
assert(FALSE);
}
/* NOTREACHED */
}
offset aoff(ap,n)
register arg_p ap;
{
while (n>0) {
if (ap != (arg_p) 0)
ap = ap->a_next;
n--;
}
if (ap == (arg_p) 0)
error("too few parameters");
if (ap->a_type != ARGOFF)
error("offset expected");
return(ap->a_a.a_offset);
}
offset tmplocal(p,size)
proc_p p;
int size;
{
/* Allocate a new local variable in the stack frame of p */
p->p_localbytes += (offset) size;
return -(p->p_localbytes);
}
line_p int_line(off)
offset off;
{
/* Allocate a line struct of type OPSHORT or OPOFFSET,
* whichever one fits best.
*/
line_p lnp;
if ((short) off == off) {
/* fits in a short */
lnp = newline(OPSHORT);
SHORT(lnp) = (short) off;
} else {
lnp = newline(OPOFFSET);
OFFSET(lnp) = off;
}
return lnp;
}
line_p reg_mes(tmp,size,typ,score)
offset tmp;
short size;
int typ,score;
{
/* Generate a register message */
line_p l;
arg_p a;
#define NEXTARG(a,val) a->a_next = newarg(ARGOFF); a = a->a_next; \
a->a_a.a_offset = val
l = newline(OPLIST);
l->l_instr = ps_mes;
a = ARG(l) = newarg(ARGOFF);
a->a_a.a_offset = ms_reg;
NEXTARG(a,tmp);
NEXTARG(a,size);
NEXTARG(a,typ);
NEXTARG(a,score);
return l;
}
bool dom(b1,b2)
bblock_p b1,b2;
{
/* See if b1 dominates b2. Note that a block always
* dominates itself.
*/
register bblock_p b;
for (b = b2; b != (bblock_p) 0; b = b->b_idom) {
/* See if b1 is a (not necessarily proper) ancestor
* of b2 in the immediate dominator tree.
*/
if (b == b1) return TRUE;
}
return FALSE;
}
bblock_p common_dom(a,b)
bblock_p a,b;
{
/* find a basic block that dominates a as well as b;
* note that a basic block also dominates itself.
*/
assert (a != (bblock_p) 0);
assert (b != (bblock_p) 0);
if (dom(a,b)) {
return a;
} else {
if (dom(b,a)) {
return b;
} else {
return common_dom(a->b_idom,b->b_idom);
}
}
}
#define R time_space_ratio
short add_timespace(time,space)
short time,space;
{
/* Add together a time and space, using the time_space_ratio
* parameter that may be set by the user, indicating the need
* to optimize for time, space or something in between.
*/
return (R * time + (100 - R) * space) / 100;
}
rm_line(l,b)
line_p l;
bblock_p b;
{
if (b->b_start == l) {
b->b_start = l->l_next;
} else {
PREV(l)->l_next = l->l_next;
}
if (l->l_next != (line_p) 0) {
PREV(l->l_next) = PREV(l);
}
oldline(l);
}
appnd_line(l1,l2)
line_p l1,l2;
{
/* Put l1 after l2 */
PREV(l1) = l2;
l1->l_next = l2->l_next;
l2->l_next = l1;
if (l1->l_next != (line_p) 0) {
PREV(l1->l_next) = l1;
}
}
line_p last_instr(b)
bblock_p b;
{
/* Determine the last line of a list */
register line_p l = b->b_start;
if (l == (line_p) 0) return (line_p) 0;
while (l->l_next != (line_p) 0) l = l->l_next;
return l;
}
line_p find_mesreg(off)
offset off;
{
/* Find the register message for the local with the given offset */
Lindex li;
line_p l;
for (li = Lfirst(mesregs); li != (Lindex) 0; li = Lnext(li,mesregs)) {
l = (line_p) Lelem(li);
if (aoff(ARG(l),1) == off) return l;
}
return (line_p) 0;
}
bool is_regvar(off)
offset off;
{
return find_mesreg(off) != (line_p) 0;
}
offset regv_arg(off,n)
offset off;
int n;
{
/* fetch the n'th argument of the register message of the
* local variable at offset off;
*/
line_p x = find_mesreg(off);
assert (x != (line_p) 0);
return aoff(ARG(x),n);
}

66
util/ego/share/aux.h Normal file
View file

@ -0,0 +1,66 @@
/* S H A R E D
*
* A U X I L I A R Y R O U T I N E S
*
*/
extern offset off_set(); /* (line_p lnp)
* lnp has a SHORT or OFFSET operand. Return
* the value of this operand as an offset.
*/
extern offset aoff(); /* (arg_p list; int n)
* Determine the offset field of the
* n'th argument in the list (this argument
* must have type ARGOFF). Start counting at 0.
*/
extern offset tmplocal(); /* (proc_p p, int size)
* Allocate a new local variable in the
* stack frame of p.
*/
line_p int_line(); /* (offset off)
* Allocate a line struct of type OPSHORT
* or OPOFFSET, whichever one fits best.
*/
extern line_p reg_mes(); /* (offset tmp; short size; int typ,score)
* Generate a register message with the
* given arguments.
*/
extern bool dom(); /* (bblock_p b1,b2)
/* See if b1 dominates b2. Note that a
* block always * dominates itself.
*/
extern bblock_p common_dom(); /* (bblock_p a,b)
* find a basic block that dominates a as
* well as b; note that a basic block also
* dominates itself.
*/
extern short add_timespace(); /* (short time,space)
* Add together a time and space, using
* the time_space_ratio parameter that
* may be set by the user.
*/
extern rm_line(); /* ( line_p l; bblock_p b)
* Remove line l from b basic block b.
*/
extern appnd_line(); /* ( line_p l1,l2)
* Put line l1 after l2.
*/
extern line_p last_instr(); /* ( bblock_p b)
* Determine the last line of a basic block.
*/
extern line_p find_mesreg(); /* (offset off)
* Find the register message for the local
* with the given offset.
*/
extern bool is_regvar(); /* (offset off)
* See if there is a 'register message'
* for the local variable with the
* given offset.
*/
extern offset regv_arg(); /* (offset off; int n)
* Fetch the n'th argument of the
* register message of the local with
* the given offset.
*/

277
util/ego/share/cset.c Normal file
View file

@ -0,0 +1,277 @@
/* S H A R E D F I L E
*
* C S E T . C
*/
#include "types.h"
#include "cset.h"
#include "alloc.h"
#include "debug.h"
#include "global.h"
/* A set over a range of integers from 1 to N may be represented
* as a 'compact' set. Such a set is represented as a 'bitvector'
* record, containing the size of the set (i.e. N) and a row
* of words (the bitvector itself). An integer J (1 <= J <= N) is
* an element of the set iff the J-th bit of the vector is a '1'.
* Any redundant bits in the last word are garanteed to be zero bits.
* This package implements the usual operations on sets.
* The name of every operation is preceede by a 'C' to
* distinguish it from the operation on 'long' (list)
* sets whth a similar name.
*/
/* The two arithmetic operations 'divide by wordlength' and
* 'modulo wordlength' can be performed very efficiently
* if the word length (of the source machine) is 16.
*/
cset Cempty_set(n)
short n;
{
cset s;
s = newbitvect(DIVWL(n-1) + 1);
s->v_size = n;
return s;
}
bool Cis_elem(x,s)
Celem_t x;
cset s;
{
short n;
int mask;
assert(x>0 && x <= s->v_size);
n = DIVWL(x-1);
mask = (1 << MODWL(x-1));
if ((s->v_bits[n] & mask) == 0) {
return FALSE;
} else {
return TRUE;
}
}
Cadd(x,s_p)
Celem_t x;
cset *s_p;
{
cset s;
short n;
int mask;
s = *s_p;
assert(x>0 && x <= s->v_size);
n = DIVWL(x-1);
mask = (1 << MODWL(x-1));
s->v_bits[n] |= mask;
}
Cremove(x,s_p)
Celem_t x;
cset *s_p;
{
cset s;
short n;
int mask;
s = *s_p;
assert(x>0 && x <= s->v_size);
n = DIVWL(x-1);
mask = (1 << MODWL(x-1));
s->v_bits[n] &= ~mask;
}
/* The operations first, next and elem can be used to iterate
* over a set. For example:
* for (i = Cfirst(s); i != (Cindex) 0; i = Cnext(i,s) {
* x = Celem(i);
* use x
* }
* which is like:
* 'for all elements x of s do'
* use x
*
* The implementation of first and next is not very fast.
* It could be made much more efficient (at the price of a
* higher complexity) by not using 'is_elem'.
* Iteration over a bitvector, however, is not supposed to
* be used very often.
*/
Cindex Cfirst(s)
cset s;
{
return Cnext((Cindex) 0,s);
}
Cindex Cnext(i,s)
Cindex i;
cset s;
{
register short n;
for (n = i+1; n <= s->v_size; n++) {
if (Cis_elem(n,s)) {
return (Cindex) n;
}
}
return (Cindex) 0;
}
Celem_t Celem(i)
Cindex i;
{
return (Celem_t) i;
}
Cjoin(s1,s2_p)
cset s1, *s2_p;
{
/* Two sets are joined by or-ing their bitvectors,
* word by word.
*/
cset s2;
short n;
register short i;
s2 = *s2_p;
assert(s1->v_size == s2->v_size);
n = DIVWL(s1->v_size -1); /* #words -1 */
for (i = 0; i <= n; i++) {
s2->v_bits[i] |= s1->v_bits[i];
}
}
Cintersect(s1,s2_p)
cset s1, *s2_p;
{
/* Two sets are intersected by and-ing their bitvectors,
* word by word.
*/
cset s2;
short n;
register short i;
s2 = *s2_p;
assert(s1->v_size == s2->v_size);
n = DIVWL(s1->v_size -1); /* #words -1 */
for (i = 0; i <= n; i++) {
s2->v_bits[i] &= s1->v_bits[i];
}
}
Cdeleteset(s)
cset s;
{
oldbitvect(s,DIVWL(s->v_size - 1) + 1);
}
bool Cis_subset(s1,s2)
cset s1,s2;
{
/* See if s1 is a subset of s2 */
register short i;
assert(s1->v_size == s2->v_size);
if (s1->v_size == 0) return TRUE;
for (i = 0; i <= DIVWL(s1->v_size-1); i++) {
if ((s1->v_bits[i] & ~(s2->v_bits[i])) != 0) {
return FALSE;
}
}
return TRUE;
}
Cclear_set(s_p)
cset *s_p;
{
cset s;
register short i;
s = *s_p;
assert (s != (cset) 0);
for (i = 0; i <= DIVWL(s->v_size-1); i++) {
s->v_bits[i] = 0;
}
}
Ccopy_set(s1,s2_p)
cset s1, *s2_p;
{
cset s2;
register short i;
s2 = *s2_p;
assert (s1->v_size == s2->v_size);
for (i = 0; i <= DIVWL(s1->v_size-1); i++) {
s2->v_bits[i] = s1->v_bits[i];
}
}
Csubtract(s1,s2_p)
cset s1, *s2_p;
{
cset s2;
register short i;
s2 = *s2_p;
assert (s1->v_size == s2->v_size);
for (i = 0; i <= DIVWL(s1->v_size-1); i++) {
s2->v_bits[i] &= ~(s1->v_bits[i]);
}
}
bool Cequal(s1,s2)
cset s1, s2;
{
register short i;
assert (s1->v_size == s2->v_size);
for (i = 0; i <= DIVWL(s1->v_size-1); i++) {
if (s1->v_bits[i] != s2->v_bits[i]) return FALSE;
}
return TRUE;
}
short Cnrelems(s)
cset s;
{
register short n, cnt;
cnt = 0;
for (n = 1; n <= s->v_size; n++) {
if (Cis_elem(n,s)) {
cnt++;
}
}
return cnt;
}

21
util/ego/share/cset.h Normal file
View file

@ -0,0 +1,21 @@
/* O P E R A T I O N S F O R
* C O M P A C T S E T S
*/
extern cset Cempty_set(); /* (short) */
extern bool Cis_elem(); /* (Celem, cset) */
extern Cadd(); /* (Celem, *cset) */
extern Cremove(); /* (Celem, *cset) */
extern Cindex Cfirst(); /* (cset) */
extern Cindex Cnext(); /* (Cindex, cset) */
extern Celem_t Celem(); /* (Cindex) */
extern Cjoin(); /* (cset, *cset) */
extern Cintersect(); /* (cset, *cset) */
extern Cdeleteset(); /* (cset) */
extern bool Cis_subset(); /* (cset, cset) */
extern Cclearset(); /* (cset, *cset) */
extern Ccopy_set(); /* (cset, *cset) */
extern Csubtract(); /* (cset, *cset) */
extern bool Cequal(); /* (cset, cset) */
extern short Cnrelems(); /* (cset) */

145
util/ego/share/debug.c Normal file
View file

@ -0,0 +1,145 @@
/* S H A R E D F I L E
*
* D E B U G . C
*/
#include <stdio.h>
#include "types.h"
#include "def.h"
#include "debug.h"
#include "../../../h/em_spec.h"
#include "global.h"
int linecount; /* # lines in this file */
bool verbose_flag = FALSE; /* generate verbose output ? */
/* VARARGS1 */
error(s,a) char *s,*a; {
fprintf(stderr,"error on line %u",linecount);
if (filename != (char *) 0) {
fprintf(stderr," file %s",filename);
}
fprintf(stderr,": ");
fprintf(stderr,s,a);
fprintf(stderr,"\n");
_cleanup();
abort();
exit(-1);
}
#ifdef TRACE
/* VARARGS1 */
OUTTRACE(s,n)
char *s;
int n;
{
fprintf(stderr,"> ");
fprintf(stderr,s,n);
fprintf(stderr,"\n");
}
#endif
#ifdef VERBOSE
/* VARARGS1 */
OUTVERBOSE(s,n1,n2)
char *s;
int n1,n2;
{
if (verbose_flag) {
fprintf(stderr,"optimization: ");
fprintf(stderr,s,n1,n2);
fprintf(stderr,"\n");
}
}
#endif
#ifdef DEBUG
badassertion(file,line) char *file; unsigned line; {
fprintf(stderr,"assertion failed file %s, line %u\n",file,line);
error("assertion");
}
#endif
/* Valid Address */
VA(a) short *a; {
if (a == (short *) 0) error("VA: 0 argument");
if ( ((unsigned) a & 01) == 01) {
/* MACHINE DEPENDENT TEST */
error("VA: odd argument");
}
}
/* Valid Instruction code */
VI(i) short i; {
if (i > ps_last) error("VI: illegal instr: %d", i);
}
/* Valid Line */
VL(l) line_p l; {
byte instr, optype;
VA((short *) l);
instr = l->l_instr;
VI(instr);
optype = TYPE(l);
if (optype < OP_FIRST || optype > OP_LAST) {
error("VL: illegal optype: %d", optype);
}
}
/* Valid Data block */
VD(d) dblock_p d; {
byte pseudo;
VA((short *) d);
pseudo = d->d_pseudo;
if (pseudo < D_FIRST || pseudo > D_LAST) {
error("VD: illegal pseudo: %d",pseudo);
}
}
/* Valid Object */
VO(o) obj_p o; {
offset off;
VA((short *) o);
off = o->o_off;
if (off < 0 || off > 10000) {
error("VO: unlikely offset: %d", off);
}
}
/* Valid Proc */
VP(p) proc_p p; {
proc_id pid;
int nrlabs;
VA((short *) p);
pid = p->p_id;
if (pid <0 || pid > 1000) {
error("VP: unlikely proc_id: %d", (int) pid);
}
nrlabs = p->p_nrlabels;
if (nrlabs < 0 || nrlabs > 500) {
error("VP: unlikely p_nrlabels: %d", nrlabs);
}
}

56
util/ego/share/debug.h Normal file
View file

@ -0,0 +1,56 @@
/* D E B U G G I N G T O O L S */
/* TEMPORARY: */
#define DEBUG
extern int linecount; /* # lines in this file */
extern bool verbose_flag; /* generate verbose output ? */
/* VARARGS 1 */
error();
#ifdef TRACE
extern OUTTRACE();
#else
#define OUTTRACE(s,n)
#endif
#ifdef VERBOSE
extern OUTVERBOSE();
#else
#define OUTVERBOSE(s,n1,n2)
#endif
#ifdef DEBUG
/* Some (all?) Unix debuggers don't particularly like
* static procedures and variables. Therefor we make everything
* global when debugging.
*/
#define STATIC
#define assert(x) if(!(x)) badassertion(__FILE__,__LINE__)
extern VI();
extern VL();
extern VD();
extern VA();
extern VO();
extern VP();
#else /*DEBUG*/
#define assert(b)
#define VI(i)
#define VL(l)
#define VD(d)
#define VA(a)
#define VO(o)
#define VP(p)
#define STATIC static
#endif

14
util/ego/share/def.h Normal file
View file

@ -0,0 +1,14 @@
/* G L O B A L M A C R O D E F I N I T I O N S
*
* F O R A L L O P T I M I Z E R P A S S E S
*/
#define MARK_DBLOCK 0
#define MARK_OBJ 1
#define MARK_ARG 2
#define op_lab (sp_lmnem+1)
#define op_last op_lab
#define ps_sym (sp_lpseu+1)
#define ps_last ps_sym

17
util/ego/share/files.c Normal file
View file

@ -0,0 +1,17 @@
/* S H A R E D F I L E
*
* F I L E S . C
*/
#include <stdio.h>
FILE *openfile(name,mode)
char *name,*mode;
{
FILE *f;
if ((f = fopen(name,mode)) == NULL) {
error("cannot open %s",name);
}
return f;
}

33
util/ego/share/files.h Normal file
View file

@ -0,0 +1,33 @@
/* F I L E N A M E S */
/* The names of the input files of every phase are passed as
* arguments to the phase. First come the input file names,
* then the output file names. We use a one-letter convention
* to denote the type of file:
* p: procedure table file
* d: data table file
* l: EM text file (lines of EM instructions)
* b: basic block file (Control Flow Graph file)
*/
/* The input file names */
#define pname argv[1]
#define dname argv[2]
#define lname argv[3]
#define bname argv[4]
/* The output file names */
#define pname2 argv[5]
#define dname2 argv[6]
#define lname2 argv[7]
#define bname2 argv[8]
#define ARGSTART 9
extern FILE *openfile(); /* (char *name, *mode)
* Open a file with the given name
* and mode; aborts if the file
* cannot be opened.
*/

911
util/ego/share/get.c Normal file
View file

@ -0,0 +1,911 @@
/* S H A R E D F I L E
*
* G E T . C
*/
#include <stdio.h>
#include "types.h"
#include "def.h"
#include "debug.h"
#include "global.h"
#include "lset.h"
#include "cset.h"
#include "get.h"
#include "alloc.h"
#include "map.h"
#include "aux.h"
#include "../../../h/em_spec.h"
#include "../../../h/em_mnem.h"
#include "../../../h/em_pseu.h"
#include "../../../h/em_mes.h"
#include "../../../h/em_flag.h"
extern char em_flag[];
/* global variables */
static FILE *f;
STATIC block_id lastbid; /* block identifying number */
STATIC lab_id lastlabid; /* last label identifier */
/* creating new identifying numbers, i.e. numbers that did not
* appear in the input.
*/
bblock_p freshblock()
{
bblock_p b;
b = newbblock();
b->b_id = ++lastbid;
return b;
}
lab_id freshlabel()
{
curproc->p_nrlabels++;
return ++lastlabid;
}
/* local routines */
#define getbyte() getc(f)
#define getmark() getbyte()
STATIC short getshort() {
register int l_byte, h_byte;
l_byte = getbyte();
h_byte = getbyte();
if ( h_byte>=128 ) h_byte -= 256 ;
return l_byte | (h_byte*256) ;
}
STATIC offset getoff() {
register long l;
register int h_byte;
l = getbyte();
l |= ((unsigned) getbyte())*256 ;
l |= getbyte()*256L*256L ;
h_byte = getbyte() ;
if ( h_byte>=128 ) h_byte -= 256 ;
return l | (h_byte*256L*256*256L) ;
}
STATIC int getint()
{
/* Read an integer from the input file. This routine is
* only used when reading a bitvector-set. We expect an
* integer to be either a short or a long.
*/
if (sizeof(int) == sizeof(short)) {
return getshort();
} else {
assert (sizeof(int) == sizeof(offset));
return getoff();
}
}
/* getptable */
loop_p getloop(id)
loop_id id;
{
/* Map a loop identifier onto a loop struct.
* If no struct was alocated yet for this identifier then
* allocate one now and update the loop-map table.
*/
assert (id > 0 && id <=lplength);
if (lpmap[id] == (loop_p) 0) {
lpmap[id] = newloop();
lpmap[id]->lp_id = id;
}
return (lpmap[id]);
}
bblock_p getblock(id)
block_id id;
{
/* Map a basic block identifier onto a block struct
* If no struct was alocated yet for this identifier then
* allocate one now and update the block-map table.
*/
assert (id >= 0 && id <=blength);
if (id == 0) return (bblock_p) 0;
if (bmap[id] == (bblock_p) 0) {
bmap[id] = newbblock();
bmap[id]->b_id = id;
}
return (bmap[id]);
}
lset getlset(p)
char *((*p) ());
{
/* Read a 'long' set. Such a set is represented externally
* as a sequence of identifying numbers terminated by a 0.
* The procedural parameter p maps such a number onto a
* pointer to a struct (bblock_p, loop_p etc.).
*/
lset s;
int id;
s = Lempty_set();
while (id = getshort()) {
Ladd( (*p) (id), &s);
}
return s;
}
cset getcset()
{
/* Read a 'compact' set. Such a set is represented externally
* a row of bytes (its bitvector) preceded by its length.
*/
cset s;
register short i;
s = Cempty_set(getshort());
for (i = 0; i <= DIVWL(s->v_size-1);i++) {
s->v_bits[i] = getint();
}
return s;
}
proc_p getptable(pname)
char *pname;
{
short i;
proc_p head, p, *pp;
short all;
if ((f = fopen(pname,"r")) == NULL) {
error("cannot open %s",pname);
}
plength = getshort(); /* table is preceded by its length */
assert(plength >= 0);
assert(plength < 1000); /* See if its a reasonable number */
pmap = (proc_p *) newmap(plength); /* allocate the pmap table */
all = getshort();
head = (proc_p) 0;
pp = &head;
for (i = 0; i < plength; i++) {
if (feof(f)) {
error("unexpected eof %s", pname);
}
p = newproc();
p->p_id = getshort();
assert(p->p_id > 0 && p->p_id <= plength);
pmap[p->p_id] = p;
p->p_flags1 = getbyte();
if (p->p_flags1 & PF_BODYSEEN) {
p->p_nrlabels = getshort();
p->p_localbytes = getoff();
p->p_nrformals = getoff();
if (all) {
p->p_change = newchange();
p->p_change->c_ext = getcset();
p->p_change->c_flags = getshort();
p->p_use = newuse();
p->p_use->u_flags = getshort();
p->p_calling = getcset();
}
}
*pp = p;
pp = &(p->p_next);
}
fclose(f);
OUTTRACE("have read proc table of length %d",plength);
return head; /* pointer to first structure of list */
}
/* getdtable */
dblock_p getdtable(dname)
char *dname;
{
/* Read the data block table. Every data block may
* have a list of objects and a list of values (arguments),
* each of which is also represented by a structure.
* So the input file contains a mixture of dblock,
* obj and arg records, each one having its own
* attributes. A mark indicates which one comes next.
* We assume that the syntactic structure of the input
* is correct.
*/
dblock_p head, d, *dp;
obj_p obj, *op;
arg_p arg, *ap;
/* dp, op an ap tell how the next dblock/obj/arg
* has to be linked.
*/
int n;
head = (dblock_p) 0;
dp = &head;
if ((f = fopen(dname,"r")) == NULL) {
error("cannot open %s", dname);
}
olength = getshort();
assert(olength >= 0);
assert(olength < 5000); /* See if its a reasonable number */
/* total number of objects */
omap = (obj_p *) newmap(olength); /* allocate omap table */
while (TRUE) {
n = getmark();
if (feof(f)) break;
switch(n) {
case MARK_DBLOCK:
d = *dp = newdblock();
op = &d->d_objlist;
ap = &d->d_values;
dp = &d->d_next;
d->d_id = getshort();
d->d_pseudo = getbyte();
d->d_size = getoff();
d->d_fragmnr = getshort();
d->d_flags1 = getbyte();
break;
case MARK_OBJ:
obj = *op = newobject();
op = &obj->o_next;
obj->o_dblock = d;
obj->o_id = getshort();
assert(obj->o_id >0);
assert(obj->o_id <= olength);
omap[obj->o_id] = obj;
obj->o_size = getoff();
obj->o_off = getoff();
break;
case MARK_ARG:
arg = *ap = newarg(ARGOFF);
ap = &arg->a_next;
arg->a_a.a_offset = getoff();
break;
default:
assert(FALSE);
}
}
OUTTRACE("have read data table, %d objects",olength);
return head;
}
/* getbblocks */
STATIC argstring(length,abp)
short length;
register argb_p abp;
{
while (length--) {
if (abp->ab_index == NARGBYTES)
abp = abp->ab_next = newargb();
abp->ab_contents[abp->ab_index++] = getbyte();
}
}
STATIC arg_p readargs()
{
/* Read a list of arguments and allocate structures
* for them. Return a pointer to the head of the list.
*/
arg_p head, arg, *ap;
byte t;
short length;
ap = &head;
for (;;) {
/* every argument list is terminated by an
* ARGCEND byte in Intermediate Code.
*/
t = getbyte();
if (t == (byte) ARGCEND) {
return head;
}
arg = *ap = newarg(t);
ap = &arg->a_next;
switch((short) t) {
case ARGOFF:
arg->a_a.a_offset = getoff();
break;
case ARGINSTRLAB:
arg->a_a.a_instrlab = getshort();
break;
case ARGOBJECT:
arg->a_a.a_obj = omap[getshort()];
/* Read an object identifier (o_id)
* and use the omap table to obtain
* a pointer to the rigth obj struct.
*/
break;
case ARGPROC:
arg->a_a.a_proc = pmap[getshort()];
/* Read a procedure identifier (p_id) */
break;
case ARGSTRING:
length = getshort();
argstring(length, &arg->a_a.a_string);
break;
case ARGICN:
case ARGUCN:
case ARGFCN:
length = getshort();
arg->a_a.a_con.ac_length = length;
/* size of the constant */
argstring(getshort(),
&arg->a_a.a_con.ac_con);
break;
default:
assert(FALSE);
}
}
}
STATIC line_p read_line(p_out)
proc_p *p_out;
{
/* Read a line of EM code (i.e. one instruction)
* and its arguments (if any).
* In Intermediate Code, the first byte is the
* instruction code and the second byte denotes the kind
* of operand(s) that follow.
*/
line_p lnp;
byte instr;
instr = getbyte();
if (feof(f)) return (line_p) 0;
lnp = newline(getbyte());
linecount++;
lnp->l_instr = instr;
switch(TYPE(lnp)) {
/* read the operand(s) */
case OPSHORT:
SHORT(lnp) = getshort();
break;
case OPOFFSET:
OFFSET(lnp) = getoff();
break;
case OPINSTRLAB:
INSTRLAB(lnp) = getshort();
if (instr == op_lab) {
/* defining occurrence of an
* instruction label.
*/
lmap[INSTRLAB(lnp)] = lnp;
}
break;
case OPOBJECT:
OBJ(lnp) = omap[getshort()];
break;
case OPPROC:
PROC(lnp) = pmap[getshort()];
if ((instr & BMASK) == ps_pro) {
/* enter new procedure: allocate a
* label map and a label-block map table.
*/
*p_out = PROC(lnp);
llength = (*p_out)->p_nrlabels;
lmap = (line_p *) newmap(llength);
/* maps lab_id to line structure */
lbmap = (bblock_p *) newmap(llength);
/* maps lab_id to bblock structure */
lastlabid = llength;
}
break;
case OPLIST:
ARG(lnp) = readargs();
break;
default:
assert(TYPE(lnp) == OPNO);
}
return lnp;
}
STATIC message(lnp)
line_p lnp;
{
/* See if lnp is some useful message.
* (e.g. a message telling that a certain local variable
* will never be referenced indirectly, so it may be put
* in a register. If so, add it to the mesregs set.)
*/
assert(ARG(lnp)->a_type == ARGOFF);
switch((int) aoff(ARG(lnp),0)) {
case ms_reg:
if (ARG(lnp)->a_next != (arg_p) 0) {
/* take only "mes 3" with further arguments */
Ladd(lnp,&mesregs);
}
break;
case ms_err:
error("ms_err encountered");
case ms_opt:
error("ms_opt encountered");
case ms_emx:
ws = aoff(ARG(lnp),1);
ps = aoff(ARG(lnp),2);
break;
}
}
STATIC line_p getlines(lf,n,p_out,collect_mes)
FILE *lf;
int n;
proc_p *p_out;
bool collect_mes;
{
/* Read n lines of EM text and doubly link them.
* Also process messages.
*/
line_p head, *pp, l, lprev;
f = lf; /* EM input file */
pp = &head;
lprev = (line_p) 0;
while (n--) {
l = *pp = read_line(p_out);
PREV(l) = lprev;
pp = &l->l_next;
lprev = l;
if (collect_mes && INSTR(l) == ps_mes) {
message(l);
}
}
*pp = (line_p) 0;
return head;
}
bool getunit(gf,lf,kind_out,g_out,l_out,p_out,collect_mes)
FILE *gf,*lf;
short *kind_out;
bblock_p *g_out;
line_p *l_out;
proc_p *p_out;
bool collect_mes;
{
/* Read control flow graph (gf) and EM text (lf) of the next procedure.
* A pointer to the proctable entry of the read procedure is
* returned via p_out.
* This routine also constructs the bmap and lpmap tables.
* Note that we allocate structs for basic blocks and loops
* at their first reference rather than at when we read them.
*/
int n,i;
bblock_p head, *pp, b;
loop_p lp;
f = gf;
blength = getshort(); /* # basic blocks in this procedure */
if (feof(f)) return FALSE;
if (blength == 0) {
/* data unit */
*kind_out = LDATA;
n = getshort();
*l_out = getlines(lf,n,p_out,collect_mes);
return TRUE;
}
*kind_out = LTEXT;
bmap = (bblock_p *) newmap(blength); /* maps block_id on bblock_p */
lplength = getshort(); /* # loops in this procedure */
lpmap = (loop_p *) newmap(lplength); /* maps loop_id on loop_p */
/* Read the basic blocks and the EM text */
pp = &head; /* we use a pointer-to-a-pointer to link the structs */
for (i = 0; i < blength; i++) {
b = getblock(getshort());
n = getshort(); /* #instructions in the block */
b->b_succ = getlset(getblock);
b->b_pred = getlset(getblock);
b->b_idom = getblock(getshort());
b->b_loops = getlset(getloop);
b->b_flags = getshort();
b->b_start = getlines(lf,n,p_out,collect_mes); /* read EM text */
*pp = b;
pp = &b->b_next;
f = gf;
}
lastbid = blength; /* last block_id */
/* read the information about loops */
curproc->p_loops = Lempty_set();
for (i = 0; i < lplength; i++) {
lp = getloop(getshort());
lp->lp_level = getshort(); /* nesting level */
lp->lp_entry = getblock(getshort()); /* entry block of the loop */
lp->lp_end = getblock(getshort()); /* tail of back edge of loop */
Ladd(lp,&curproc->p_loops);
}
*g_out = head;
return TRUE;
}
/* The procedure getbblocks is used only by the Control Flow phase.
* It reads the EM textfile and partitions every procedure into
* a number of basic blocks.
*/
#define LABEL0 0
#define LABEL 1
#define NORMAL 2
#define JUMP 3
#define END 4
#define AFTERPRO 5
#define INIT 6
/* These global variables are used by getbblocks and nextblock. */
STATIC bblock_p b, *bp; /* b is the current basic block, bp is
* the address where the next block has
* to be linked.
*/
STATIC line_p lnp, *lp; /* lnp is the current line, lp is
* the address where the next line
* has to be linked.
*/
STATIC short state; /* We use a finite state machine with the
* following states:
* LABEL0: after the first (successive)
* instruction label.
* LABEL1: after at least two successive
* instruction labels.
* NORMAL: after a normal instruction.
* JUMP: after a branch (conditional,
* unconditional or CSA/CSB).
* END: after an END pseudo
* AFTERPRO: after we've read a PRO pseudo
* INIT: initial state
*/
STATIC nextblock()
{
/* allocate a new basic block structure and
* set b, bp and lp.
*/
b = *bp = freshblock();
bp = &b->b_next;
b->b_start = lnp;
b->b_succ = Lempty_set();
b->b_pred = Lempty_set();
b->b_extend = newcfbx(); /* basic block extension for CF */
b->b_extend->bx_cf.bx_bucket = Lempty_set();
b->b_extend->bx_cf.bx_semi = 0;
lp = &lnp->l_next;
#ifdef TRACE
fprintf(stderr,"new basic block, id = %d\n",lastbid);
#endif
}
STATIC short kind(lnp)
line_p lnp;
{
/* determine if lnp is a label, branch, end or otherwise */
short instr;
byte flow;
if ((instr = INSTR(lnp)) == op_lab) return (short) LABEL;
if (instr == ps_end) return (short) END;
if (instr > sp_lmnem) return (short) NORMAL; /* pseudo */
if ((flow = (em_flag[instr-sp_fmnem] & EM_FLO)) == FLO_C ||
flow == FLO_T) return (short) JUMP; /* conditional/uncond. jump */
return (short) NORMAL;
}
bool getbblocks(fp,kind_out,n_out,g_out,l_out)
FILE *fp;
short *kind_out;
short *n_out;
bblock_p *g_out;
line_p *l_out;
{
bblock_p head = (bblock_p) 0;
line_p headl = (line_p) 0;
curproc = (proc_p) 0;
/* curproc will get a value when we encounter a PRO pseudo.
* If there is no such pseudo, we're reading only data
* declarations or messages (outside any proc.).
*/
f = fp;
lastbid = (block_id) 0; /* block identier */
state = INIT; /* initial state */
bp = &head;
for (;;) {
#ifdef TRACE
fprintf(stderr,"state = %d\n",state);
#endif
switch(state) {
case LABEL0:
nextblock();
/* Fall through !! */
case LABEL:
lbmap[INSTRLAB(lnp)] = b;
/* The lbmap table contains for each
* label_id the basic block of that label.
*/
lnp = read_line(&curproc);
state = kind(lnp);
if (state != END) {
*lp = lnp;
lp = &lnp->l_next;
}
break;
case NORMAL:
lnp = read_line(&curproc);
if ( (state = kind(lnp)) == LABEL) {
/* If we come accross a label
* here, it must be the beginning
* of a new basic block.
*/
state = LABEL0;
} else {
if (state != END) {
*lp = lnp;
lp = &lnp->l_next;
}
}
break;
case JUMP:
lnp = read_line(&curproc);
/* fall through ... */
case AFTERPRO:
switch(state = kind(lnp)) {
case LABEL:
state = LABEL0;
break;
case JUMP:
case NORMAL:
nextblock();
break;
}
break;
case END:
*lp = lnp;
#ifdef TRACE
fprintf(stderr,"at end of proc, %d blocks\n",lastbid);
#endif
if (head == (bblock_p) 0) {
*kind_out = LDATA;
*l_out = headl;
} else {
*kind_out = LTEXT;
*g_out = head;
*n_out = (short) lastbid;
/* number of basic blocks */
}
return TRUE;
case INIT:
lnp = read_line(&curproc);
if (feof(f)) return FALSE;
if (INSTR(lnp) == ps_pro) {
state = AFTERPRO;
} else {
state = NORMAL;
headl = lnp;
lp = &lnp->l_next;
}
break;
}
}
}
/* The following routines are only used by the Inline Substitution phase */
call_p getcall(cf)
FILE *cf;
{
/* read a call from the call-file */
call_p c;
proc_p voided;
actual_p act,*app;
short n,m;
f = cf;
c = newcall();
n = getshort(); /* void nesting level */
if (feof(f)) return (call_p) 0;
c->cl_caller = pmap[getshort()];
c->cl_id = getshort();
c->cl_proc = pmap[getshort()];
c->cl_looplevel = getbyte();
c->cl_flags = getbyte();
c->cl_ratio = getshort();
app = &c->cl_actuals;
n = getshort();
while(n--) {
act = newactual();
m = getshort();
act->ac_size = getoff();
act->ac_inl = getbyte();
act->ac_exp = getlines(cf,m,&voided);
*app = act;
app = &act->ac_next;
}
*app = (actual_p) 0;
return c;
}
line_p get_text(lf,p_out)
FILE *lf;
proc_p *p_out;
{
/* Read the EM text of one unit
* If it is a procedure, set p_out to
* the proc. just read. Else set p_out
* to 0.
*/
line_p dumhead, l, lprev;
loop_p *oldlpmap = lpmap;
line_p *oldlmap = lmap;
short oldllength = llength;
short oldlastlabid = lastlabid;
f = lf;
*p_out = (proc_p) 0;
dumhead = newline(OPNO);
/* The list of instructions is preceeded by a dummy
* line, to simplify list manipulation
*/
dumhead->l_instr = op_nop; /* just for fun */
lprev = dumhead;
for (;;) {
l = read_line(p_out);
if (feof(f)) return (line_p) 0;
lprev->l_next = l;
PREV(l) = lprev;
if (INSTR(l) == ps_end) break;
if (INSTR(l) == ps_mes) {
message(l);
}
lprev = l;
}
/* The tables that map labels to instructions
* and labels to basic blocks are not used.
*/
if (*p_out != (proc_p) 0) {
oldmap(lmap,llength);
oldmap(lbmap,llength);
lmap = oldlmap;
lpmap = oldlpmap;
}
llength = oldllength;
lastlabid = oldlastlabid;
return dumhead;
}
calcnt_p getcc(ccf,p)
FILE *ccf;
proc_p p;
{
/* Get call-count info of procedure p */
calcnt_p head,cc,*ccp;
short i;
fseek(ccf,p->p_extend->px_il.p_ccaddr,0);
f = ccf;
head = (calcnt_p) 0;
ccp = &head;
for (i = getshort(); i != (short) 0; i--) {
cc = *ccp = newcalcnt();
cc->cc_proc = pmap[getshort()];
cc->cc_count = getshort();
ccp = &cc->cc_next;
}
return head;
}
/* The following routine is only used by the Compact Assembly generation phase,
* which does not read basic blocks.
*/
line_p get_ca_lines(lf,p_out)
FILE *lf;
proc_p *p_out;
{
/* Read lines of EM text and link them.
* Register messages are outputted immediately after the PRO.
*/
line_p head, *pp, l;
line_p headm, *mp;
arg_p a;
f = lf; /* EM input file */
pp = &head;
mp = &headm;
headm = (line_p) 0;
while (TRUE) {
l = read_line(p_out);
if (feof(f)) break;
assert (l != (line_p) 0);
if (INSTR(l) == ps_end && INSTR(head) != ps_pro) {
/* Delete end pseudo after data-unit */
oldline(l);
break;
}
if (INSTR(l) == ps_mes && l->l_a.la_arg->a_a.a_offset == ms_reg) {
/* l is a register message */
if (l->l_a.la_arg->a_next == (arg_p) 0) {
/* register message without arguments */
oldline(l);
} else {
*mp = l;
mp = &l->l_next;
}
} else {
*pp = l;
pp = &l->l_next;
}
if (INSTR(l) == ps_end) {
break;
}
}
*pp = (line_p) 0;
if (INSTR(head) == ps_pro) {
/* append register message without arguments to list */
l = newline(OPLIST);
l->l_instr = ps_mes;
a = ARG(l) = newarg(ARGOFF);
a->a_a.a_offset = ms_reg;
*mp = l;
l->l_next = head->l_next;
head->l_next = headm;
} else {
assert(headm == (line_p) 0);
}
return head;
}

56
util/ego/share/get.h Normal file
View file

@ -0,0 +1,56 @@
/* I N P U T R O U T I N E S */
extern bblock_p freshblock(); /* ()
* Allocate a bblock struct and assign
* it a brand new block_id.
*/
extern lab_id freshlabel(); /* ()
* Get a brand new lab_id.
*/
extern dblock_p getdtable(); /* (char *dname)
* Read the data block table from
* the file with the given name.
*/
extern proc_p getptable(); /* (char *pname)
* Read the proc table from
* the file with the given name.
*/
extern bool getunit(); /* (FILE *gf,*lf; short kind_out;
* bblock_p g_out; line_p l_out;
* proc_p *p_out; bool collect_mes)
* Read the control flow graph
* (from file gf) and the EM text
* (from lf). If collect_mes is TRUE,
* all register messages will be
* collected and put in the global
* variable 'mesregs'. The proc read
* is returned in p_out.
*/
extern bool getbblocks(); /* (FILE *f,short kind_out,
* short *n_out, bblock_p *g_out,
* line_p *l_out)
* Read the EM text of a single
* unit from the given file.
* This unit can be either a procedure
* or a umber of data declarations and
* messages. If it is a proc., then
* partition the text into
* basic blocks. Return the
* number of basic blocks in n_out.
*/
extern call_p getcall(); /* (FILE *cf)
* Read a call from the call-file
*/
extern line_p get_text(); /* (FILE *lf; proc_p *p_out)
* Read the EM text of one procedure.
* The procedure read is returned via
* p_out.
*/
extern calcnt_p getcc(); /* (FILE *ccf; proc_p p)
* Read the call-count information
* of procedure p.
*/
extern line_p get_ca_lines(); /* (FILE *lf; proc_p *p_out)
* Read em lines till end pseudo is met.
* (Used only by CA phase).
*/

21
util/ego/share/global.c Normal file
View file

@ -0,0 +1,21 @@
/* S H A R E D F I L E
*
* G L O B A L . C
*/
#include "types.h"
int ps = 0;
int ws = 0;
proc_p curproc; /* current procedure */
char *filename; /* name of current input file */
lset mesregs; /* set of MES ms_reg pseudos */
short time_space_ratio = 50;
/* 0 if optimizing for space only,
* 100 if optimizing for time only,
* else something 'in between'.
*/

51
util/ego/share/global.h Normal file
View file

@ -0,0 +1,51 @@
/* G L O B A L V A R I A B L E S */
/* sizes of TARGET machine */
extern int ps; /* pointer size */
extern int ws; /* word size */
/* sizes of SOURCE machine (i.e. machine on which
* the optimizer runs)
*/
/* number of bits in a byte */
#define BYTELENGTH 8
/* number of bits in a word */
#define WORDLENGTH 32
#if BYTELENGTH==8
#define DIVBL(a) ((a) >> 3)
#define MODBL(a) ((a) & 07)
#else
#define DIVBL(a) (a/BYTELENGTH)
#define MODBL(a) (a%BYTELENGTH)
#endif
#if WORDLENGTH==16
#define DIVWL(a) ((a) >> 4)
#define MODWL(a) ((a) & 017)
#else
#if WORDLENGTH==32
#define DIVWL(a) ((a) >> 5)
#define MODWL(a) ((a) & 037)
#else
#define DIVWL(a) (a/WORDLENGTH)
#define MODWL(a) (a%WORDLENGTH)
#endif
#endif
#define UNKNOWN_SIZE (-1)
extern proc_p curproc; /* current procedure */
extern char *filename; /* name of current input file */
extern lset mesregs; /* set of MES ms_reg pseudos */
extern short time_space_ratio; /* 0 if optimizing for space only,
* 100 if optimizing for time only,
* else something 'in between'.
*/

152
util/ego/share/go.c Normal file
View file

@ -0,0 +1,152 @@
/* S H A R E D F I L E
*
* G O . C
*
*/
#include <stdio.h>
#include "types.h"
#include "debug.h"
#include "global.h"
#include "files.h"
#include "get.h"
#include "put.h"
#include "lset.h"
#include "map.h"
#include "alloc.h"
#include "go.h"
STATIC bool report_flag = FALSE; /* report #optimizations found? */
STATIC bool core_flag = FALSE; /* report core usage? */
STATIC mach_init(machfile,phase_machinit)
char *machfile;
int (*phase_machinit)();
{
/* Read target machine dependent information */
FILE *f;
f = openfile(machfile,"r");
fscanf(f,"%d",&ws);
fscanf(f,"%d",&ps);
if (ws != ps && ps != 2*ws) error("illegal pointer size");
phase_machinit(f);
fclose(f);
}
go(argc,argv,initialize,optimize,phase_machinit,proc_flag)
int argc;
char *argv[];
int (*initialize)();
int (*optimize)();
int (*phase_machinit)();
int (*proc_flag)();
{
FILE *f, *gf, *f2, *gf2; /* The EM input and output and
* the basic block graphs input and output
*/
bblock_p g;
line_p l;
short kind;
int i;
char *p;
bool time_opt = FALSE;
linecount = 0;
for (i = ARGSTART; i < argc; i++) {
p = argv[i];
if (*p++ != '-') error("illegal argument");
switch(*p) {
case 'S':
time_opt = FALSE;
break;
case 'T':
time_opt = TRUE;
break;
case 'M':
p++;
mach_init(p,phase_machinit);
break;
case 'C':
core_flag = TRUE;
break;
case 'Q':
report_flag = TRUE;
break;
case 'V':
verbose_flag = TRUE;
break;
default:
proc_flag(p);
break;
}
}
time_space_ratio = (time_opt ? 100 : 0);
fproc = getptable(pname); /* proc table */
fdblock = getdtable(dname); /* data block table */
initialize();
if (optimize == no_action) return;
f = openfile(lname,"r");
gf = openfile(bname,"r");
f2 = openfile(lname2,"w");
gf2 = openfile(bname2,"w");
mesregs = Lempty_set();
while (getunit(gf,f,&kind,&g,&l,&curproc,TRUE)) {
/* Read the control flow graph and EM text of
* one procedure and optimize it.
*/
if (kind == LDATA) {
putunit(LDATA, (proc_p) 0, l, gf2, f2);
continue;
}
OUTTRACE("flow graph of proc %d read",curproc->p_id);
curproc->p_start = g;
/* The global variable curproc points to the
* current procedure. It is set by getgraph
*/
optimize(curproc);
putunit(LTEXT,curproc,(line_p) 0,gf2,f2);
/* output control flow graph + text */
OUTTRACE("graph of proc %d outputted",curproc->p_id);
Ldeleteset(mesregs);
mesregs = Lempty_set();
}
fclose(f);
fclose(f2);
fclose(gf);
fclose(gf2);
f = openfile(dname2,"w");
putdtable(fdblock,f);
fclose(f);
f = openfile(pname2,"w");
putptable(fproc,f,TRUE);
fclose(f);
core_usage();
}
no_action() { }
core_usage()
{
if (core_flag) {
coreusage();
}
}
report(s,n)
char *s;
int n;
{
/* Report number of optimizations found, if report_flag is set */
if (report_flag) {
fprintf(stderr,"%s: %d\n",s,n);
}
}

34
util/ego/share/go.h Normal file
View file

@ -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
*/

View file

@ -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;
}

View file

@ -0,0 +1,10 @@
/* S H A R E D
*
* I N I T _ G L O B L S
*
*/
extern init_globals(); /* Assign a 'global variable number (o_globnr)
* to every global variable.
*/

240
util/ego/share/locals.c Normal file
View file

@ -0,0 +1,240 @@
/*
* L O C A L S . C
*/
#include "types.h"
#include "debug.h"
#include "global.h"
#include "lset.h"
#include "cset.h"
#include "def.h"
#include "get.h"
#include "aux.h"
#include "alloc.h"
#include "../../../h/em_mnem.h"
#include "../../../h/em_spec.h"
#include "../../../h/em_pseu.h"
#include "../../../h/em_mes.h"
#include "locals.h"
extern short nrglobals;
short nrlocals;
local_p *locals; /* dynamic array */
STATIC localvar(off,size,locs,reg,score)
offset off;
short size;
local_p *locs;
bool reg;
offset score;
{
/* process a reference to a local variable.
* A local is characterized by a (offset,size) pair.
* We first collect all locals in a list, sorted
* by offset. Later we will construct a table
* out of this list.
*/
local_p lc, x, *prevp;
prevp = locs;
for (lc = *locs; lc != (local_p) 0; lc = lc->lc_next) {
if (lc->lc_off == off && lc->lc_size == size) {
if (reg) {
REGVAR(lc); /* register variable */
lc->lc_score = score;
}
return; /* local already present */
}
if (lc->lc_off > off) break;
prevp = &lc->lc_next;
}
/* the local was not seen before; create an entry
* for it in the list.
*/
x = *prevp = newlocal();
x->lc_off = off;
x->lc_size = size;
x->lc_next = lc;
if (reg) {
REGVAR(x);
x->lc_score = score;
}
}
STATIC check_message(l,locs)
line_p l;
local_p *locs;
{
/* See if l is a register message */
arg_p arg;
arg = ARG(l);
if (aoff(arg,0) == ms_reg && arg->a_next != (arg_p) 0) {
localvar(aoff(arg,1), (short) aoff(arg,2), locs, TRUE,
aoff(arg,4));
}
}
STATIC check_local_use(l,locs)
line_p l;
local_p *locs;
{
short sz;
switch(INSTR(l)) {
case op_lol:
case op_stl:
case op_inl:
case op_del:
case op_zrl:
sz = ws;
break;
case op_ldl:
case op_sdl:
sz = 2 * ws;
break;
case op_lil:
case op_sil:
sz = ps;
break;
case ps_mes:
check_message(l,locs);
/* fall through .. */
default:
return;
}
localvar(off_set(l),sz,locs,FALSE,(offset) 0);
}
make_localtab(p)
proc_p p;
{
/* Make a table of local variables.
* This table is used to associate a
* unique number with a local. If two
* locals overlap (e.g. LDL 4 and LDL 2)
* none of them is considered any further,
* i.e. we don't compute ud-info for them.
*/
local_p prev, next, lc;
local_p locallist = (local_p) 0;
short cnt = 0;
offset x, ill_zone = 0;
register bblock_p b;
register line_p l;
/* first make a list of all locals used */
for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
for (l = b->b_start; l != (line_p) 0; l = l->l_next) {
check_local_use(l,&locallist);
}
}
/* Now remove overlapping locals, count useful ones on the fly */
for (lc = locallist; lc != (local_p) 0; lc = lc->lc_next) {
if (ill_zone != 0 && lc->lc_off < ill_zone) {
/* this local overlaps with a previous one */
BADLC(lc);
if (!IS_BADLC(prev)) {
BADLC(prev);
cnt--;
}
} else {
cnt++;
}
x = lc->lc_off + lc->lc_size;
if (ill_zone == 0 || x > ill_zone) {
ill_zone = x;
}
prev = lc;
}
/* Now we know how many local variables there are */
nrlocals = cnt;
locals = (local_p *) newmap(cnt);
cnt = 1;
for (lc = locallist; lc != (local_p) 0; lc = next) {
next = lc->lc_next;
if (IS_BADLC(lc)) {
oldlocal(lc);
} else {
locals[cnt++] = lc;
lc->lc_next = (local_p) 0;
}
}
assert (cnt == nrlocals+1);
}
STATIC find_local(off,nr_out,found_out)
offset off;
short *nr_out;
bool *found_out;
{
/* Try to find the local variable at the given
* offset. Return its local-number.
*/
short v;
for (v = 1; v <= nrlocals; v++) {
if (locals[v]->lc_off > off) break;
if (locals[v]->lc_off == off) {
*found_out = TRUE;
*nr_out = v;
return;
}
}
*found_out = FALSE;
}
var_nr(l,nr_out,found_out)
line_p l;
short *nr_out;
bool *found_out;
{
/* Determine the number of the variable referenced
* by EM instruction l.
*/
offset off;
short nr;
switch(TYPE(l)) {
case OPOBJECT:
/* global variable */
if (OBJ(l)->o_globnr == 0) {
/* We don't maintain ud-info for this var */
*found_out = FALSE;
} else {
*nr_out = GLOB_TO_VARNR(OBJ(l)->o_globnr);
*found_out = TRUE;
}
return;
case OPSHORT:
off = (offset) SHORT(l);
break;
case OPOFFSET:
off = OFFSET(l);
break;
default:
assert(FALSE);
}
/* Its's a local variable */
find_local(off,&nr,found_out);
if (*found_out) {
*nr_out = LOC_TO_VARNR(nr);
}
}

39
util/ego/share/locals.h Normal file
View file

@ -0,0 +1,39 @@
/*
* L O C A L S . H
*/
extern local_p *locals; /* table of locals, index is local-number */
extern short nrlocals; /* number of locals for which we keep ud-info */
extern make_localtab(); /* (proc_p p)
* Analyse the text of procedure p to determine
* which local variable p has. Make a table of
* these variables ('locals') and count them
* ('nrlocals'). Also collect register messages.
*/
extern var_nr(); /* (line_p l; short *nr_out;bool *found_out)
* Compute the 'variable number' of the
* variable referenced by EM instruction l.
*/
/* Every global variable for which ud-info is maintained has
* a 'global variable number' (o_globnr). Every useful local
* has a 'local variable number', which is its index in the
* 'locals' table. All these variables also have a
* 'variable number'. Conversions exist between these numbers.
*/
#define TO_GLOBAL(v) (v)
#define TO_LOCAL(v) (v - nrglobals)
#define GLOB_TO_VARNR(v) (v)
#define LOC_TO_VARNR(v) (v + nrglobals)
#define IS_GLOBAL(v) (v <= nrglobals)
#define IS_LOCAL(v) (v > nrglobals)
#define REGVAR(lc) lc->lc_flags |= LCF_REG
#define IS_REGVAR(lc) (lc->lc_flags & LCF_REG)
#define BADLC(lc) lc->lc_flags |= LCF_BAD
#define IS_BADLC(lc) (lc->lc_flags & LCF_BAD)

208
util/ego/share/lset.c Normal file
View file

@ -0,0 +1,208 @@
/* L O N G S E T S
*
* L S E T . C
*/
#include "types.h"
#include "lset.h"
#include "alloc.h"
#include "debug.h"
/* A 'long' set is represented as a linear list of 'elemholder'
* records. Every such record contains a pointer to an element
* of the set and to the next elemholder. An empty set is
* represented as a null pointer.
* An element of a long set must be of some pointer type or,
* in any case, must have the size of a pointer. Note that
* the strict typing rules are not obeyed here.
* This package implements the usual operations on sets.
* The name of every operation is preceeded by a 'L' to
* distinguish it from the operation on 'compact' (bitvector)
* sets with a similar name.
*/
lset Lempty_set()
{
return ((lset) 0);
}
bool Lis_elem(x,s)
register Lelem_t x;
register lset s;
{
/* Search the list to see if x is an element of s */
while (s != (elem_p) 0) {
if (s->e_elem == x) {
return TRUE;
}
s = s->e_next;
}
return FALSE;
}
Ladd(x,s_p)
Lelem_t x;
lset *s_p;
{
/* add x to a set. Note that the set is given as in-out
* parameter, because it may be changed.
*/
elem_p t;
if (!Lis_elem(x,*s_p)) {
t = newelem(); /* allocate a new elemholder */
t->e_elem = x;
t->e_next = *s_p; /* insert it at the head of the list */
*s_p = t;
}
}
Lremove(x,s_p)
Lelem_t x;
lset *s_p;
{
/* Remove x from a set. If x was not an element of
* the set, nothing happens.
*/
register elem_p *epp, ep;
lset s;
s = *s_p;
epp = &s;
while ((ep = *epp) != (elem_p) 0) {
if (ep->e_elem == x) {
*epp = ep->e_next;
oldelem(ep);
break;
} else {
epp = &ep->e_next;
}
}
*s_p = s;
}
/* The operations first, next and elem can be used to iterate
* over a set. For example:
* for (i = Lfirst(s); i != (Lindex) 0; i = Lnext(i,s) {
* x = Lelem(i);
* use x
* }
* which is like:
* 'for all elements x of s do'
* use x
*/
Lindex Lfirst(s)
lset s;
{
return ((Lindex) s);
/* Note that an index for long sets is just
* a pointer to an elemholder.
*/
}
Lindex Lnext(i,s)
Lindex i;
lset s;
{
assert(i != (Lindex) 0);
return (i->e_next);
}
Lelem_t Lelem(i)
Lindex i;
{
return (i->e_elem);
}
Ljoin(s1,s2_p)
lset s1,*s2_p;
{
/* Join two sets, assign the result to the second set
* and delete the first set (i.e. the value of the
* first set becomes undefined).
*/
register elem_p *epp, ep;
lset s2;
/* First all elements of s1 that are also an element of s2
* are removed from the s1 list. The two resulting lists
* (for s1 and s2) are linked (s1 first).
* Note the usage of epp, which points to a pointer that
* points to the next elemholder record of the list.
*/
s2 = *s2_p;
epp = &s1;
while ((ep = *epp) != (elem_p) 0) {
if (Lis_elem(ep->e_elem,s2)) {
/* remove an element */
*epp = ep->e_next;
oldelem(ep);
} else {
epp = &ep->e_next;
}
}
*epp = s2; /* last record of s1 (or s1 itself) now points
* to first record of s2.
*/
*s2_p = s1;
}
Ldeleteset(s)
lset s;
{
register elem_p ep, next;
for (ep = s; ep != (elem_p) 0; ep = next) {
next = ep->e_next;
oldelem(ep);
}
}
bool Lis_subset(s1,s2)
lset s1,s2;
{
/* See if s1 is a subset of s2 */
register Lindex i;
for (i = Lfirst(s1); i != (Lindex) 0; i = Lnext(i,s1)) {
if (!Lis_elem(Lelem(i),s2)) return FALSE;
}
return TRUE;
}
short Lnrelems(s)
lset s;
{
/* Compute the number of elements of a set */
register elem_p ep;
register short cnt;
cnt = 0;
for (ep = s; ep != (elem_p) 0; ep = ep->e_next) {
cnt++;
}
return cnt;
}

16
util/ego/share/lset.h Normal file
View file

@ -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) */

View file

@ -0,0 +1,83 @@
#include <stdio.h>
/* MAKECLASSDEF
*
* This program is used by several phases of the optimizer
* to make the file classdefs.h. It reads two files:
* - the em_mnem,h file, containing the definitions of the
* EM mnemonics
* - the class-file, containing tuples:
* (mnemonic, src_class, res_class)
* where src_class and res_class are integers telling how
* to compute the number of bytes popped and pushed
* by the instruction.
* The output (standard output) is a C array.
*/
#define TRUE 1
#define FALSE 0
convert(mnemfile,classfile)
FILE *mnemfile, *classfile;
{
char mnem1[10], mnem2[10],def[10];
int src,res,newcl,opc;
newcl = TRUE;
printf("struct class classtab[] = {\n");
printf("\tNOCLASS,\tNOCLASS,\n");
/* EM mnemonics start at 1, arrays in C at 0 */
for (;;) {
fscanf(mnemfile,"%s%s%d",def,mnem1,&opc);
/* read a line like "#define op_aar 1" */
if (feof(mnemfile)) break;
if (strcmp(def,"#define") != 0) {
error("bad mnemonic file, #define expected");
}
if (newcl) {
fscanf(classfile,"%s%d%d",mnem2,&src,&res);
/* read a line like "op_loc 8 1" */
}
if (feof(classfile) || strcmp(mnem1,mnem2) != 0) {
/* there is no line for this mnemonic, so
* it has no class.
*/
printf("\tNOCLASS,\tNOCLASS,\n");
newcl = FALSE;
} else {
printf("\tCLASS%d,\t\tCLASS%d,\n",src,res);
/* print a line like "CLASS8, CLASS1," */
newcl = TRUE;
}
}
printf("};\n");
}
error(s)
char *s;
{
fprintf(stderr,"%s\n",s);
exit(-1);
}
main(argc,argv)
int argc;
char *argv[];
{
FILE *f1,*f2;
if (argc != 3) {
error("usage: makeclassdef mnemfile classfile");
}
if ((f1 = fopen(argv[1],"r")) == NULL) {
error("cannot open mnemonic file");
}
if ((f2 = fopen(argv[2],"r")) == NULL) {
error("cannot open class file");
}
convert(f1,f2);
}

21
util/ego/share/map.c Normal file
View file

@ -0,0 +1,21 @@
/* M A P . C */
#include "types.h"
#include "map.h"
short plength;
short olength;
short llength;
short blength;
short lplength;
line_p *lmap;
bblock_p *lbmap;
proc_p *pmap ; /* dynamically allocated array that maps
* every proc_id to a proc_p.
*/
obj_p *omap; /* maps obj_id to obj_p */
loop_p *lpmap; /* maps loop_id to loop_p */
bblock_p *bmap; /* maps block_id to bblock_p */
dblock_p fdblock; /* first dblock */
proc_p fproc; /* first proc */

38
util/ego/share/map.h Normal file
View file

@ -0,0 +1,38 @@
/* M A P . H */
extern short plength; /* length of pmap, i.e. number of procs */
extern short olength; /* length of omap, i.e. number of objects */
extern short llength; /* length of lmap and lbmap, i.e.
* # instruction labels in current proc.
*/
extern short lplength; /* length of lpmap, i.e. number of loops
* in current procedure.
*/
extern short blength; /* length of bmap, i.e. number of basic blocks
* in current procedure.
*/
extern line_p *lmap; /* contains for every label_id its
* defining occurrence (line structure)
* label_id --> line_p
*/
extern bblock_p *lbmap; /* contains for every label_id its
* basic block.
* label_id --> bblock_p
*/
extern proc_p *pmap; /* contains for every proc_id its proc structure
* proc_id --> proc_p
*/
extern obj_p *omap; /* contains for every obj_id its object struct
* obj_id --> obj_p
*/
extern loop_p *lpmap; /* contains for every loop_id its loop struct
* loop_id --> loop_p
*/
extern bblock_p *bmap; /* contains for every block_id its bblock struct
* block_id --> bblock_p
*/
extern dblock_p fdblock;/* first dblock, heads dblock list */
extern proc_p fproc; /* first proc, heads proc table */

277
util/ego/share/parser.c Normal file
View file

@ -0,0 +1,277 @@
#include <stdio.h>
#include "types.h"
#include "debug.h"
#include "alloc.h"
#include "global.h"
#include "lset.h"
#include "aux.h"
#include "../../../h/em_spec.h"
#include "../../../h/em_mnem.h"
struct class {
byte src_class;
byte res_class;
};
typedef struct class *class_p;
#define NOCLASS 0
#define CLASS1 1
#define CLASS2 2
#define CLASS3 3
#define CLASS4 4
#define CLASS5 5
#define CLASS6 6
#define CLASS7 7
#define CLASS8 8
#define CLASS9 9
#define CLASS10 10
#define CLASS11 11
#include "classdefs.h"
/* The file classdefs.h contains the table classtab. It is
* generated automatically from the file classdefs.src.
*/
STATIC bool classes(instr,src_out,res_out)
int instr;
int *src_out, *res_out;
{
/* Determine the classes of the given instruction */
class_p c;
if (instr < sp_fmnem || instr > sp_lmnem) return FALSE;
c = &classtab[instr];
if (c->src_class == NOCLASS) return FALSE;
*src_out = c->src_class;
*res_out = c->res_class;
return TRUE;
}
STATIC bool uses_arg(class)
int class;
{
/* See if a member of the given class uses
* an argument.
*/
switch(class) {
case CLASS1:
case CLASS2:
case CLASS3:
case CLASS4:
case CLASS11:
return TRUE;
default:
return FALSE;
}
/* NOTREACHED */
}
STATIC bool uses_2args(class)
int class;
{
/* See if a member of the given class uses
* 2 arguments.
*/
return class == CLASS10;
}
STATIC bool parse_locs(l,c1_out,c2_out)
line_p l;
offset *c1_out, *c2_out;
{
if (INSTR(l) == op_loc && INSTR(PREV(l)) == op_loc) {
*c1_out = off_set(l);
*c2_out = off_set(PREV(l));
return TRUE;
}
return FALSE;
}
STATIC bool check_args(l,src_class,res_class,arg1_out,arg2_out)
line_p l;
int src_class,res_class;
offset *arg1_out, *arg2_out;
{
/* Several EM instructions have an argument
* giving the size of the operand(s) of
* the instruction. E.g. a 'adi 4' is a 4-byte
* addition. The size may also be put on the
* stack. In this case we give up our
* efforts to recognize the parameter expression.
* Some instructions (e.g. CIU) use 2 arguments
* that are both on the stack. In this case we
* check if both arguments are LOCs (the usual case),
* else we give up.
*/
if (uses_2args(src_class) || uses_2args(res_class)) {
return parse_locs(PREV(l),arg1_out,arg2_out);
}
if (uses_arg(src_class) || uses_arg(res_class)) {
if (TYPE(l) == OPSHORT) {
*arg1_out = (offset) SHORT(l);
return TRUE;
} else {
if (TYPE(l) == OPOFFSET) {
*arg1_out = OFFSET(l);
} else {
return FALSE;
}
}
}
return TRUE; /* no argument needed */
}
STATIC offset nrbytes(class,arg1,arg2)
int class;
offset arg1,arg2;
{
/* Determine the number of bytes of the given
* arguments and class.
*/
offset n;
switch(class) {
case CLASS1:
n = arg1;
break;
case CLASS2:
n = 2 * arg1;
break;
case CLASS3:
n = arg1 + ws;
break;
case CLASS4:
n = arg1 + ps;
break;
case CLASS5:
n = ws;
break;
case CLASS6:
n = 2 * ws;
break;
case CLASS7:
n = ps;
break;
case CLASS8:
n = 2 * ps;
break;
case CLASS9:
n = 0;
break;
case CLASS10:
n = arg2 + 2*ws;
break;
case CLASS11:
n = arg1 + 2*ps;
break;
default:
assert(FALSE);
}
return n;
}
STATIC attrib(l,expect_out,srcb_out,resb_out)
line_p l;
offset *expect_out, *srcb_out, *resb_out;
{
/* Determine a number of attributes of an EM
* instruction appearing in an expression.
* If it is something we don't
* expect in such expression (e.g. a store)
* expect_out is set to FALSE. Else we
* determine the number of bytes popped from
* the stack by the instruction and the
* number of bytes pushed on the stack as
* result.
*/
int src_class,res_class;
offset arg1, arg2;
if (l == (line_p) 0 || !classes(INSTR(l),&src_class,&res_class) ||
!check_args(l,src_class,res_class,&arg1,&arg2)) {
*expect_out = FALSE;
} else {
*expect_out = TRUE;
*srcb_out = nrbytes(src_class,arg1,arg2);
*resb_out = nrbytes(res_class,arg1,arg2);
}
}
bool parse(l,nbytes,l_out,level,action0)
line_p l, *l_out;
offset nbytes;
int level;
int (*action0) ();
{
/* This is a recursive descent parser for
* EM expressions.
* It tries to recognize EM code that loads exactly
* 'nbytes' bytes on the stack.
* 'l' is the last instruction of this code.
* As EM is essentially postfix, this instruction
* can be regarded as the root node of an expression
* tree. The EM code is traversed from right to left,
* i.e. top down. On success, TRUE is returned and
* 'l_out' will point to the first instruction
* of the recognized code. On toplevel, when an
* expression has been recognized, the procedure-parameter
* 'action0' is called, with parameters: the first and
* last instruction of the expression and the number of
* bytes recognized.
*/
offset more, expected, sourcebytes,resultbytes;
line_p lnp;
more = nbytes; /* #bytes to be recognized */
while (more > 0) {
attrib(l,&expected,&sourcebytes,&resultbytes);
/* Get the attributes of EM instruction 'l'.
* 'expected' denotes if it is something we can use;
* 'sourcebytes' and 'resultbytes' are the number of
* bytes popped resp. pushed by the instruction
* (e.g. 'adi 2' pops 4 bytes and pushes 2 bytes).
*/
if (!expected || (more -= resultbytes) < 0) return FALSE;
if (sourcebytes == 0) {
/* a leaf of the expression tree */
lnp = l;
} else {
if (!parse(PREV(l),sourcebytes,&lnp,level+1,action0)) {
return FALSE;
}
}
if (level == 0) {
/* at toplevel */
(*action0) (lnp,l,resultbytes);
}
l = PREV(lnp);
}
/* Now we've recognized a number of expressions that
* together push nbytes on the stack.
*/
*l_out = lnp;
return TRUE;
}

13
util/ego/share/parser.h Normal file
View file

@ -0,0 +1,13 @@
bool parse(); /* (line_p l, *l_out; offset nbytes;
* int level; int (*action0) ())
* This is a recursive descent parser for
* EM expressions.
* It tries to recognize EM code that loads exactly
* 'nbytes' bytes on the stack.
* 'l' is the last instruction of this code.
* On toplevel, when an expression has been
* recognized, the procedure-parameter
* 'action0' is called, with parameters: the first and
* last instruction of the expression and the number of
* bytes recognized.
*/

528
util/ego/share/put.c Normal file
View file

@ -0,0 +1,528 @@
/* P U T . C */
#include <stdio.h>
#include "types.h"
#include "global.h"
#include "debug.h"
#include "def.h"
#include "map.h"
#include "../../../h/em_pseu.h"
#include "../../../h/em_spec.h"
#include "lset.h"
#include "alloc.h"
#include "put.h"
/* the output file */
static FILE *f; /* current output file, can be EM text file,
* basic block file, data block file or proc table file.
*/
#define outbyte(b) putc(b,f)
/* The output can be either 'typed' or 'untyped'. Typed data
* consists of a value preceded by a byte specifying what kind
* of value it is (e.g. 2 bytes constant, 4 bytes constant,
* proc-id, lab-id, string etc.). Untyped data consists
* of the value only. We use typed data for the EM text and
* untyped data for all other files.
*/
/* putlines */
STATIC putargs(ap)
register arg_p ap;
{
while (ap != (arg_p) 0) {
outbyte((byte) ap->a_type & BMASK);
switch(ap->a_type) {
case ARGOFF:
outoff(ap->a_a.a_offset);
break;
case ARGINSTRLAB:
outlab(ap->a_a.a_instrlab);
break;
case ARGOBJECT:
outobject(ap->a_a.a_obj);
break;
case ARGPROC:
outproc(ap->a_a.a_proc);
break;
case ARGSTRING:
putstr(&ap->a_a.a_string);
break;
case ARGICN:
case ARGUCN:
case ARGFCN:
outshort(ap->a_a.a_con.ac_length);
putstr(&ap->a_a.a_con.ac_con);
break;
}
ap = ap->a_next;
}
outbyte((byte) ARGCEND);
}
STATIC putstr(abp) register argb_p abp; {
register argb_p tbp;
register length;
length = 0;
tbp = abp;
while (tbp!= (argb_p) 0) {
length += tbp->ab_index;
tbp = tbp->ab_next;
}
outshort(length);
while (abp != (argb_p) 0) {
for (length=0;length<abp->ab_index;length++)
outbyte( (byte) abp->ab_contents[length] );
abp = abp->ab_next;
}
}
STATIC outoff(off) offset off; {
outshort( (short) (off&0177777L) );
outshort( (short) (off>>16) );
}
STATIC outshort(i) short i; {
outbyte( (byte) (i&BMASK) );
outbyte( (byte) (i>>8) );
}
STATIC outint(i)
int i;
{
/* Write an integer to the output file. This routine is
* only used when outputting a bitvector-set. We expect an
* integer to be either a short or a long.
*/
if (sizeof(int) == sizeof(short)) {
outshort(i);
} else {
assert (sizeof(int) == sizeof(offset));
outoff(i);
}
}
STATIC outlab(lid) lab_id lid; {
outshort((short) lid);
}
STATIC outobject(obj) obj_p obj; {
outshort((short) obj->o_id);
}
STATIC outproc(p) proc_p p; {
outshort((short) p->p_id);
}
short putlines(l,lf)
line_p l;
FILE *lf;
{
/* Output the list of em instructions headed by l.
* Return the number of instruction written.
*/
register line_p lnp;
line_p next;
short instr;
short count= 0;
f = lf; /* Set f to the EM-text output file */
for (lnp = l; lnp != (line_p) 0; lnp = next) {
VL(lnp);
count++;
next = lnp->l_next;
instr = INSTR(lnp);
outbyte((byte) instr);
outbyte((byte) TYPE(lnp));
switch(TYPE(lnp)) {
case OPSHORT:
outshort(SHORT(lnp));
break;
case OPOFFSET:
outoff(OFFSET(lnp));
break;
case OPINSTRLAB:
outlab(INSTRLAB(lnp));
break;
case OPOBJECT:
outobject(OBJ(lnp));
break;
case OPPROC:
outproc(PROC(lnp));
break;
case OPLIST:
putargs(ARG(lnp));
break;
}
oldline(lnp);
}
return count;
}
/* putdtable */
#define outmark(m) outbyte((byte) m)
STATIC putobjects(obj)
register obj_p obj;
{
while (obj != (obj_p) 0) {
outmark(MARK_OBJ);
outshort(obj->o_id);
outoff(obj->o_size);
outoff(obj->o_off);
obj = obj->o_next;
}
}
STATIC putvalues(arg)
register arg_p arg;
{
while (arg != (arg_p) 0) {
assert(arg->a_type == ARGOFF);
outmark(MARK_ARG);
outoff(arg->a_a.a_offset);
arg = arg->a_next;
}
}
putdtable(head,df)
dblock_p head;
FILE *df;
{
/* Write the datablock table to the data block file df. */
register dblock_p dbl;
register obj_p obj;
dblock_p next;
register short n = 0;
f = df; /* set f to the data block output file */
/* Count the number of objects */
for (dbl = head; dbl != (dblock_p) 0; dbl = dbl->d_next) {
for (obj = dbl->d_objlist; obj != (obj_p) 0;
obj = obj->o_next) {
n++;
}
}
outshort(n); /* The table is preceded by #objects . */
for (dbl = head; dbl != (dblock_p) 0; dbl = next) {
next = dbl->d_next;
outmark(MARK_DBLOCK);
outshort(dbl->d_id);
outbyte(dbl->d_pseudo);
outoff(dbl->d_size);
outshort(dbl->d_fragmnr);
outbyte(dbl->d_flags1);
putobjects(dbl->d_objlist);
putvalues(dbl->d_values);
olddblock(dbl);
}
fclose(f);
if (omap != (obj_p *) 0) {
oldmap(omap,olength); /* release memory for omap */
}
}
/* putptable */
STATIC outcset(s)
cset s;
{
/* A 'compact' set is represented externally as a row of words
* (its bitvector) preceded by its length.
*/
register short i;
outshort(s->v_size);
for (i = 0; i <= DIVWL(s->v_size - 1); i++) {
outint(s->v_bits[i]);
}
}
putptable(head,pf,all)
proc_p head;
FILE *pf;
bool all;
{
register proc_p p;
proc_p next;
register short n = 0;
/* Write the proc table */
f = pf;
/* Determine the number of procs */
for (p = head; p != (proc_p) 0; p = p->p_next) {
n++;
}
outshort(n); /* The table is preceded by its length. */
outshort ((all?1:0)); /* if all=false, only some of the attributes
are written. */
for (p = head; p != (proc_p) 0; p = next) {
next = p->p_next;
outshort(p->p_id);
outbyte(p->p_flags1);
if (p->p_flags1 & PF_BODYSEEN) {
/* If we have no access to the EM text of the
* body of a procedure, we have no information
* about it whatsoever, so there is nothing
* to output in that case.
*/
outshort(p->p_nrlabels);
outoff(p->p_localbytes);
outoff(p->p_nrformals);
if (all) {
outcset(p->p_change->c_ext);
outshort(p->p_change->c_flags);
outshort(p->p_use->u_flags);
outcset(p->p_calling);
Cdeleteset(p->p_change->c_ext);
oldchange(p->p_change);
olduse(p->p_use);
Cdeleteset(p->p_calling);
}
}
oldproc(p);
}
fclose(f);
if (pmap != (proc_p *) 0) {
oldmap(pmap,plength); /* release memory for pmap */
}
}
/* putunit */
STATIC outloop(l)
loop_p l;
{
outshort((short) l->lp_id);
}
STATIC outblock(b)
bblock_p b;
{
if (b == (bblock_p) 0) {
outshort((short) 0);
} else {
outshort((short) b->b_id);
}
}
STATIC outid(e,p)
Lelem_t e;
int (*p) ();
{
/* Auxiliary routine used by outlset. */
/* NOSTRICT */
(*p) (e);
}
STATIC outlset(s,p)
lset s;
int (*p) ();
{
/* A 'long' set is represented externally as a
* a sequence of elements terminated by a 0 word.
* The procedural parameter p is a routine that
* prints an id (proc_id, obj_id etc.).
*/
register Lindex i;
for (i = Lfirst(s); i != (Lindex) 0; i = Lnext(i,s)) {
outid(Lelem(i),p);
}
outshort((short) 0);
}
putunit(kind,p,l,gf,lf)
short kind;
proc_p p;
line_p l;
FILE *gf, *lf;
{
register bblock_p b;
register short n = 0;
Lindex pi;
loop_p lp;
f = gf;
if (kind == LDATA) {
outshort(0); /* No basic blocks */
n = putlines(l,lf);
f = gf;
outshort(n);
return;
}
/* Determine the number of basic blocks */
for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
n++;
}
outshort(n); /* # basic blocks */
outshort(Lnrelems(p->p_loops)); /* # loops */
for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
n = putlines(b->b_start,lf);
f = gf;
outblock(b); /* put its block_id */
outshort(n); /* #instructions of the block */
outlset(b->b_succ, outblock); /* put succ set */
outlset(b->b_pred, outblock); /* put pred set */
outblock(b->b_idom); /* put id of immediate dominator */
outlset(b->b_loops, outloop); /* put loop set */
outshort(b->b_flags);
}
/* The Control Flow Graph of every procedure is followed
* by a description of the loops of the procedure.
* Every loop contains an id, an entry block and a level.
*/
for (pi = Lfirst(p->p_loops); pi != (Lindex) 0;
pi = Lnext(pi,p->p_loops)) {
lp = (loop_p) Lelem(pi);
outloop(lp); /* id */
outshort(lp->lp_level); /* nesting level */
outblock(lp->lp_entry); /* loop entry block */
outblock(lp->lp_end);
oldloop(lp);
}
Ldeleteset(p->p_loops);
/* We will now release the memory of the basic blocks.
* Note that it would be incorrect to release a basic block
* after it has been written, because there may be references
* to it from other (later) blocks.
*/
for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
Ldeleteset(b->b_loops);
Ldeleteset(b->b_succ);
Ldeleteset(b->b_pred);
oldbblock(b);
}
/* Release the memory for the lmap, lbmap, bmap, lpmap tables */
if (lmap != (line_p *) 0) oldmap(lmap,llength);
if (lbmap != (bblock_p *) 0) oldmap(lbmap,llength);
if (bmap != (bblock_p *) 0) oldmap(bmap,blength);
if (lpmap != (loop_p *) 0) oldmap(lpmap,lplength);
f = lf;
}
/* The following routines are only used by the Inline Substitution phase */
STATIC putactuals(alist,cfile)
actual_p alist;
FILE *cfile;
{
/* output a list of actual parameters */
actual_p a,next;
line_p l;
int count;
count = 0;
for (a = alist; a != (actual_p) 0; a = a->ac_next) count++;
outshort(count); /* number of actuals */
for (a = alist; a != (actual_p) 0; a = next) {
next = a->ac_next;
count = 0;
for (l = a->ac_exp; l != (line_p) 0; l= l->l_next) count++;
outshort(count); /* length of actual */
outoff(a->ac_size);
outbyte(a->ac_inl);
count = putlines(a->ac_exp,cfile);
oldactual(a);
}
}
putcall(c,cfile,level)
call_p c;
FILE *cfile;
short level;
{
/* output a call */
call_p nc,nextc;
f = cfile;
outshort(level); /* nesting level */
outshort(c->cl_caller->p_id); /* calling proc */
outshort(c->cl_id);
outshort(c->cl_proc->p_id); /* called proc */
outbyte(c->cl_looplevel);
outbyte(c->cl_flags);
outshort(c->cl_ratio);
putactuals(c->cl_actuals,cfile);
nc = c->cl_car;
oldcall(c);
for (; nc != (call_p) 0; nc = nextc) {
/* take care of nested calls */
nextc = nc->cl_cdr;
putcall(nc,cfile,level+1);
}
}
long putcc(head,ccf)
calcnt_p head;
FILE *ccf;
{
/* Write call-count information to file ccf.
* Return the disk address of the info written.
*/
calcnt_p cc;
long addr;
short cnt;
addr = ftell(ccf);
f = ccf;
cnt = 0;
for (cc = head; cc != (calcnt_p) 0;cc = cc->cc_next) cnt++;
outshort(cnt);
for (cc = head; cc != (calcnt_p) 0; cc = cc->cc_next) {
outproc(cc->cc_proc);
outshort(cc->cc_count);
}
return addr;
}

42
util/ego/share/put.h Normal file
View file

@ -0,0 +1,42 @@
/* O U T P U T R O U T I N E S */
extern putdtable(); /* (dblock_p head, FILE *df)
* Write the data block table to file df,
* preceded by its length.
*/
extern putptable(); /* (proc_p head, FILE *pf, bool all)
* Write the proc table to file pf,
* preceded by its length. If all=false,
* the fields computed by CF will not be
* written (used by the IC phase).
*/
extern putunit(); /* (short kind; proc_p p; line_p l;
* FILE *gf, *lf)
* If kind = LTEXT, then write
* the control flow graph to file gf,
* preceded by its length (#basic blocks);
* write the EM code of every basic block
* in the graph to file lf, preceded by
* the number of instructions in the block.
* Else, (kind = LDATA) just write the
* list of instructions (data declarations)
* to lf.
*/
extern short putlines(); /* (line_p l; FILE *lf)
* Output the list of em instructions
* headed by l. Return the number of
* instructions written.
*/
extern putcall(); /* (call_p call; FILE *cfile; short level)
* Write the call
* with the given id to the given file.
* The level is the nesting level, used by
* putcall when it calls itself recurively.
* It should be 0 on outer levels.
*/
extern long putcc(); /* (calcnt_p head; FILE *ccf)
* Write call-count information to
* file ccf.
*/

415
util/ego/share/show.c Normal file
View file

@ -0,0 +1,415 @@
/* S H O W . C */
/* This program can be used to make the output of the 'cf' pass
* human readable. It will display either the procedure table,
* the datablock table, the basic block table or the EM text,
* depending on the flag that is passed as first argument.
*/
#include <stdio.h>
#include "../../../h/em_spec.h"
#include "../../../h/em_flag.h"
#include "../../../h/em_pseu.h"
#include "../share/types.h"
#include "../share/def.h"
#include "../share/global.h"
#define BMASK 0377
extern byte em_flag[];
#define space1() printf(" ")
char format[] = " %-11s%d\n";
char lformat[] = " %-11s%D\n";
char sformat[] = " %-10s%s\n";
char dformat[] = " %-11s%d\n";
char oformat[] = " %-11s%D\n";
FILE *f; /* input file */
#define getbyte() getc(f)
short getshort()
{
register n;
n = getbyte();
n |= getbyte() << 8;
return n;
}
int getint()
{
/* Read an integer from the input file. This routine is
* only used when reading a bitvector-set. We expect an
* integer to be either a short or a long.
*/
if (sizeof(int) == sizeof(short)) {
return getshort();
} else {
return getoff();
}
}
offset getoff()
{
register offset n;
n = (unsigned) getshort();
n |= ((offset) getshort() ) << 16;
return n;
}
/* VARARGS 1 */
error(s,a) char *s,*a; {
fprintf(stderr,"error");
fprintf(stderr,": ");
fprintf(stderr,s,a);
fprintf(stderr,"\n");
abort();
exit(-1);
}
main(argc, argv)
int argc;
char *argv[];
{
if (argc != 3 || argv[1][0] != '-') {
error("usage: %s -[ldpbc] filename",argv[0]);
}
if ((f = fopen(argv[2], "r")) == NULL) {
error("cannot open %s", argv[2]);
}
switch(argv[1][1]) {
case 'l':
showl();
break;
case 'd':
showd();
break;
case 'p':
showp();
break;
case 'b':
showb();
break;
case 'c':
showc();
break;
default:
error("bad flag");
}
fclose(f);
}
showcset()
{
/* print a compact (bitvector) set */
short size;
register short i,j;
int w, mask;
size = getshort();
/* # significant bits in bitvector */
i = 1;
printf(" { ");
if (size == 0) {
printf("}\n");
return;
}
for (;;) {
w = getint();
mask = 1 ;
for (j = 1; j <= WORDLENGTH; j++) {
if (w & mask) {
printf("%d ",i);
}
if (i++ == size) {
printf ("}\n");
return;
}
mask <<= 1;
}
}
}
showp()
{
byte b;
short n;
short all;
printf("total number of procs: %d\n\n",getshort());
all = getshort();
while (TRUE) {
n = getshort();
if (feof(f)) break;
printf("PROC\n");
printf(format,"id =",n);
printf(format,"flags1 =",b = getbyte());
if (b & PF_BODYSEEN) {
printf(format,"# labels =",getshort());
printf(lformat,"# locals =",getoff());
printf(lformat,"# formals =",getoff());
if (all == 1) {
printf(" changed ="); showcset();
printf(format,"c_flags =",getshort());
printf(" used ="); showcset();
printf(format,"u_flags =",getshort());
printf(" calling ="); showcset();
}
} else {
printf(" body not available\n");
}
}
}
char *pseudo[] = {"hol", "bss", "rom", "con", "unknown" };
showd()
{
short n;
printf("total number of objects: %d\n\n",getshort());
while (TRUE) {
n = getbyte();
if (feof(f)) break;
switch(n) {
case MARK_DBLOCK:
printf("DBLOCK\n");
printf(format,"id =",getshort());
printf(sformat,"pseudo =",
pseudo[(short) getbyte()]);
printf(lformat,"size =",getoff());
printf(format,"fragment =",getshort());
printf(format,"flags1 =",
(short) getbyte());
break;
case MARK_OBJ:
printf(" OBJ\n");
space1();
printf(format,"id =",getshort());
space1();
printf(lformat,"size =",getoff());
space1();
printf(lformat,"offset =",getoff());
break;
case MARK_ARG:
printf(" VALUE\n");
space1();
printf(lformat,"offset =",getoff());
break;
}
}
}
/* The mnemonics of the EM instructions and pseudos */
extern char em_mnem[];
extern char em_pseu[];
char lab_mnem[] = "instrlab";
char sym_mnem[] = "datalab";
showinstr()
{
short instr;
char *s;
instr = (short) getbyte();
if (feof(f)) return FALSE;
if (instr >= sp_fmnem && instr <= sp_lmnem) {
s = &(em_mnem[(instr-sp_fmnem) *4]);
} else {
if (instr == op_lab) {
s = lab_mnem;
} else {
if (instr == ps_sym) {
s = sym_mnem;
} else {
s = &(em_pseu[(instr-sp_fpseu)*4]);
}
}
}
printf("%s",s);
switch((short) getbyte()) {
case OPSHORT:
case OPOBJECT:
printf(" %d", getshort());
break;
case OPPROC:
printf(" $%d",getshort());
break;
case OPINSTRLAB:
printf(" *%d",getshort());
break;
case OPOFFSET:
printf(" %D", getoff());
break;
case OPLIST:
arglist();
break;
}
printf("\n");
return TRUE;
}
showl()
{
while (showinstr());
}
arglist()
{
short length;
for (;;) {
switch((short) getbyte()) {
case ARGOBJECT:
printf(" %d", getshort());
break;
case ARGPROC:
printf(" $%d",getshort());
break;
case ARGINSTRLAB:
printf(" *%d",getshort());
break;
case ARGOFF:
printf(" %D", getoff());
break;
case ARGICN:
case ARGUCN:
case ARGFCN:
printf(" %d",getshort());
/* Fall through !! */
case ARGSTRING:
length = getshort();
putchar(' ');
putchar('"');
while (length--) {
putchar(getbyte());
}
putchar('"');
break;
case ARGCEND:
return;
}
}
}
showlset()
{
register short x;
printf("{ ");
while (x = getshort()) {
printf("%d ",x);
}
printf("}\n");
}
showb()
{
/* basic block file */
short n,m;
while (TRUE) {
n = getshort();
if (feof(f)) break;
if (n == 0) {
printf("Declaration Unit:\n");
printf(dformat,"#instrs =",getshort());
printf("\n");
continue;
}
printf("Control Flow Graph:\n");
printf("number of basic blocks: %d\n",n);
m = getshort(); /* #loops */
while (n--) {
printf(" BASIC BLOCK\n");
printf(dformat,"id =",getshort());
printf(dformat,"# instrs =",getshort());
printf(" succ =");
showlset();
printf(" pred =");
showlset();
printf(dformat,"idom =",getshort());
printf(" loops =");
showlset();
printf(dformat,"flags =",getshort());
}
printf("number of loops: %d\n",m);
while (m--) {
printf(" LOOP\n");
printf(dformat,"id =",getshort());
printf(dformat,"level =",getshort());
printf(dformat,"entry =",getshort());
printf(dformat,"end =",getshort());
}
printf("\n");
}
}
showc()
{
int n,m,cnt,t;
cnt = 1;
while(TRUE) {
t = getshort();
if (feof(f)) break;
printf("CALL %d\n",cnt++);
printf(format,"nestlevel =",t);
printf(format,"calling p. =",getshort());
printf(format,"call_id =",getshort());
printf(format,"called p. =",getshort());
printf(format,"looplevel =",getbyte());
printf(format,"flags =",getbyte());
printf(format,"ratio =",getshort());
printf(" actuals:");
n = getshort();
if (n == 0) {
printf(" ---\n");
} else {
while (n--) {
printf("\n");
m = getshort();
printf(oformat,"size =",getoff());
printf(dformat,"inl =",getbyte());
while (m--) {
printf(" ");
showinstr();
}
}
}
}
}

104
util/ego/share/stack_chg.c Normal file
View file

@ -0,0 +1,104 @@
/* S T A C K _ C H A N G E . C */
#include <stdio.h>
#include "../share/types.h"
#include "../share/debug.h"
#include "../share/global.h"
#include "../../../h/em_spec.h"
#include "../../../h/em_mnem.h"
#include "pop_push.h"
#define IS_LOC(l) (l!=(line_p) 0 && INSTR(l)==op_loc && TYPE(l)==OPSHORT)
int stack_change(l,sign)
line_p l;
char sign;
{
/* Interpret the string in the third column of the em_table file */
char *s;
bool argdef;
short arg;
int sum = 0;
line_p p = PREV(l);
line_p pp = (p == (line_p) 0 ? (line_p) 0 : PREV(p));
short i = INSTR(l);
if (i < sp_fmnem || i > sp_lmnem) {
return 0;
} else {
if (TYPE(l) == OPSHORT) {
arg = SHORT(l);
if (arg < ws) {
/* E.g. a LOI 1 loads word-size bytes,
* not 1 byte!
*/
arg = ws;
}
argdef = TRUE;
} else {
argdef = FALSE;
}
}
s = pop_push[i];
if (*s == '0') return 0;
while (*s != '\0') {
if (*s++ == sign) {
switch(*s) {
case 'w':
sum += ws;
break;
case 'd':
sum += 2 * ws;
break;
case 'p':
sum += ps;
break;
case 'a':
if (!argdef) return -1;
sum += arg;
break;
case 'x':
if (IS_LOC(p)) {
sum += SHORT(p);
break;
} else {
return -1;
}
case 'y':
if (IS_LOC(pp)) {
sum += SHORT(pp);
break;
} else {
return -1;
}
case '?':
return -1;
default:
assert(FALSE);
}
}
s++;
}
return sum;
}
line_change(l,ok_out,pop_out,push_out)
line_p l;
bool *ok_out;
int *pop_out,*push_out;
{
short pop,push;
pop = stack_change(l,'-');
push = stack_change(l,'+');
*ok_out = (pop != -1 && push != -1);
*pop_out = pop;
*push_out = push;
}

View file

@ -0,0 +1,10 @@
/* S T A C K _ C H A N G E . H */
extern line_change(); /* ( line_p l; bool *ok_out; int *pop_out,*push_out)
* Try to determine how the stack-height will be
* affected by the EM instruction l. 'ok_out' is set
* to false if we fail to do so. pop_out and
* push_out are set to the number of bytes popped
* and pushed. E.g. for an "ADI 2" 4 and 2 are returned.
*/

562
util/ego/share/types.h Normal file
View file

@ -0,0 +1,562 @@
/* I N T E R N A L D A T A S T R U C T U R E S O F E G O */
/* This file contains the definitions of the global data types.
*/
/* TEMPORARY: */
#define LONGOFF
#define IDL 8 /* identifier length */
#define DYNAMIC 1
#define NARGBYTES 14
#define BMASK 0377
typedef struct argbytes argb_t;
typedef char byte;
typedef byte bool;
typedef long offset;
typedef short obj_id;
typedef short proc_id;
typedef short dblock_id;
typedef short block_id;
typedef short loop_id;
typedef short lab_id;
typedef struct dblock *dblock_p;
typedef struct obj *obj_p;
typedef struct proc *proc_p;
typedef struct loop *loop_p;
typedef struct change *change_p;
typedef struct use *use_p;
typedef struct bblock *bblock_p;
typedef struct line *line_p;
typedef struct arg *arg_p;
typedef struct argbytes *argb_p;
typedef struct elemholder *elem_p;
typedef struct elemholder *lset;
typedef struct bitvector *cset;
typedef elem_p Lindex;
typedef short Cindex;
typedef char *Lelem_t;
typedef short Celem_t;
typedef union pext_t *pext_p;
typedef union bext_t *bext_p;
typedef union lpext_t *lpext_p;
/* Intermediate Code generation */
typedef struct sym *sym_p;
typedef struct prc *prc_p;
typedef struct num *num_p;
/* Inline Substitution */
typedef struct call *call_p;
typedef struct actual *actual_p;
typedef struct formal *formal_p;
typedef struct calcnt *calcnt_p;
typedef short call_id;
/* Strength Reduction */
typedef struct iv *iv_p;
typedef struct code_info *code_p;
/* Used-Definition Analysis */
typedef struct local *local_p;
typedef struct cond_tab *cond_p;
#define TRUE 1
#define FALSE 0
/* DATABLOCKS */
/* A datablock is a block of global data, declared by means of
* a hol, bss, con or rom pseudo. The declaration may be in a file
* that is inaccessible to EGO, in which case the pseudo is unknown.
* Successive rom or con pseudos that are garanteed to be in the
* same fragment (according to the EM definition) share the
* same fragment number.
*/
#define DHOL 0
#define DBSS 1
#define DROM 2
#define DCON 3
#define DUNKNOWN 4
/* The following constants are used by the debugging tools: */
#define D_FIRST DHOL
#define D_LAST DUNKNOWN
struct dblock {
dblock_id d_id; /* unique integer */
byte d_pseudo; /* one of DHOL,DBSS,DROM,DCON,DUNKNOWN */
offset d_size; /* # bytes, -1 if unknown */
obj_p d_objlist; /* list of objects of the data block */
byte d_flags1; /* see below */
byte d_flags2; /* free to be used by phases */
arg_p d_values; /* values, in case of ROM */
short d_fragmnr; /* fragment number */
dblock_p d_next; /* link to next block */
};
#define DF_EXTERNAL 01 /* Is name visible outside its module? */
/* OBJECTS */
/* An object is a row of successive bytes in one datablock
* that are considered to be a whole. E.g. scalar variables,
* arrays, I/O buffers etc. are objects.
*/
struct obj {
offset o_off; /* offset within the block */
offset o_size; /* size of the object, 0 if not known */
obj_id o_id; /* unique integer */
dblock_p o_dblock; /* backlink to data block */
short o_globnr; /* global variable number */
obj_p o_next; /* link */
};
/* PROCEDURES */
struct proc {
proc_id p_id; /* unique integer */
short p_nrlabels; /* #instruction labels in the proc */
offset p_localbytes; /* #bytes for locals */
offset p_nrformals; /* #bytes for formals */
byte p_flags1; /* see below */
byte p_flags2; /* free to be used by phases */
bblock_p p_start; /* pointer to first basic block */
cset p_calling; /* set of all procs called by this one */
lset p_loops; /* information about loops */
change_p p_change; /* variables changed by this proc */
use_p p_use; /* variables used by this proc */
pext_p p_extend; /* pointer to any further information */
proc_p p_next; /* link */
};
union pext_t {
struct pext_il {
call_p p_cals; /* candidate calls for in line expansion */
short p_size; /* length of proc (EM-instrs or bytes) */
formal_p p_formals; /* description of formals */
short p_nrcalled; /* # times proc is called (varying) */
long p_ccaddr; /* address of calcnt info on disk */
long p_laddr; /* address in EM-text file on disk */
short p_orglabels; /* original #labels before substitution */
offset p_orglocals; /* original #bytes for locals */
} px_il;
} ;
#define PF_EXTERNAL 01 /* proc is externally visible */
#define PF_BODYSEEN 02 /* body of proc is available as EM text */
#define PF_CALUNKNOWN 04 /* proc calls an unavailable procedure */
#define PF_ENVIRON 010 /* proc does a lxa or lxl */
#define PF_LPI 020 /* proc may be called indirect */
#define PF_CALINLOOP 040 /* proc ever called in a loop? (transitively) */
#define CALLED_IN_LOOP(p) p->p_flags1 |= PF_CALINLOOP
#define IS_CALLED_IN_LOOP(p) (p->p_flags1 & PF_CALINLOOP)
/* LOOPS */
struct loop {
loop_id lp_id; /* unique integer */
short lp_level; /* nesting level, 0=outermost loop,
* 1=loop within loop etc. */
bblock_p lp_entry; /* unique entry block of loop */
bblock_p lp_end; /* tail of back edge of natural loop */
lpext_p lp_extend; /* pointer to any further information */
};
union lpext_t {
struct lpext_cf {
lset lpx_blocks;
short lpx_count;
bool lpx_messy;
} lpx_cf;
struct lpext_sr {
lset lpx_blocks; /* basic blocks constituting the loop */
bblock_p lpx_header; /* header block, 0 if no one allocated yet */
bool lpx_done; /* TRUE if we've processed this loop */
line_p lpx_instr; /* current last instruction in header block*/
} lpx_sr;
struct lpext_ra {
lset lpx_blocks; /* basic blocks constituting the loop */
bblock_p lpx_header; /* header block, 0 if no one allocated yet */
} lpx_ra;
} ;
/* CHANGED/USED VARIABLES INFORMATION */
struct change {
cset c_ext; /* external variables changed */
short c_flags; /* see below */
};
struct use {
short u_flags; /* see below */
};
#define CF_INDIR 01
#define UF_INDIR 01
/* SETS */
/* There are 2 set representations:
* - long (lset), which is essentially a list
* - compact (cset), which is essentially a bitvector
*/
struct elemholder {
char *e_elem; /* pointer to the element */
elem_p e_next; /* link */
};
struct bitvector {
short v_size; /* # significant bits */
int v_bits[DYNAMIC];/* a row of bits */
};
/* BASIC BLOCKS */
/* Note that the b_succ and b_pred fields constitute the
* Control Flow Graph
*/
struct bblock {
block_id b_id; /* unique integer */
line_p b_start; /* pointer to first instruction */
lset b_succ; /* set of successor blocks */
lset b_pred; /* set of predecessor blocks */
bblock_p b_idom; /* immediate dominator */
lset b_loops; /* set of loops it is in */
short b_flags; /* see below */
bext_p b_extend; /* pointer to any further information */
bblock_p b_next; /* link to textually next block */
};
union bext_t {
struct bext_cf {
short bx_semi; /* dfs number of semi-dominator */
bblock_p bx_parent; /* parent in dfs spanning tree */
lset bx_bucket; /* set of vertices whose sdom is b */
bblock_p bx_ancestor; /* ancestor of b in forest, */
bblock_p bx_label; /* used by link/eval */
} bx_cf;
struct bext_ud {
cset bx_gen; /* definition generated in b */
cset bx_kill; /* defs. outside b killed by b */
cset bx_in; /* defs. reaching beginning of b */
cset bx_out; /* defs. reaching end of b */
cset bx_cgen; /* generated copies */
cset bx_ckill; /* killed copies */
cset bx_cin; /* copies reaching begin of b */
cset bx_cout; /* copies reaching end of b */
cset bx_chgvars; /* variables changed by b */
} bx_ud;
struct bext_lv {
cset bx_use; /* variables used before being defined */
cset bx_def; /* variables defined before being used */
cset bx_lin; /* variables live at entry of b */
cset bx_lout; /* variables live at exit of b */
} bx_lv;
struct bext_ra {
short bx_begin; /* number of first instruction of block */
short bx_end; /* number of last instruction of block */
short bx_usecnt; /* used by minimal_score() */
short bx_dist; /* ,, */
bool bx_mark; /* ,, */
} bx_ra;
} ;
#define BF_STRONG 01
#define BF_FIRM 02
#define IS_STRONG(b) (b->b_flags&BF_STRONG)
#define IS_FIRM(b) (b->b_flags&BF_FIRM)
/* EM INSTRUCTIONS */
/* Kinds of operand types (l_optype field) */
#define OPNO 0
#define OPSHORT 1
#define OPOFFSET 2
#define OPINSTRLAB 3
#define OPOBJECT 4
#define OPPROC 5
#define OPLIST 6
/* The following constants are used by the debugging tools: */
#define OP_FIRST OPNO
#define OP_LAST OPLIST
#define LDATA 0
#define LTEXT 01
struct line {
line_p l_next; /* link */
byte l_instr; /* instruction */
byte l_optype; /* kind of operand, used as tag */
line_p l_prev; /* backlink to previous instruction */
union {
short la_short; /* short: LOC 5 */
offset la_offset; /* offset: LDC 20 */
lab_id la_instrlab; /* label: BRA *10 */
obj_p la_obj; /* object: LOE X+2 */
proc_p la_proc; /* proc: CAL F3 */
arg_p la_arg; /* arguments: HOL 10,0,0 */
} l_a;
};
/* ARGUMENTS */
/* String representation of a constant, partitioned into
* pieces of NARGBYTES bytes.
*/
#define ARGOFF 0
#define ARGINSTRLAB 1
#define ARGOBJECT 2
#define ARGPROC 3
#define ARGSTRING 4
#define ARGICN 5
#define ARGUCN 6
#define ARGFCN 7
#define ARGCEND 8
struct argbytes {
argb_p ab_next;
short ab_index;
char ab_contents[NARGBYTES];
};
struct arg {
arg_p a_next; /* link */
short a_type; /* kind of argument */
union {
offset a_offset; /* offset */
lab_id a_instrlab; /* instruction label */
proc_p a_proc; /* procedure */
obj_p a_obj; /* object */
argb_t a_string; /* string */
struct { /* int/unsigned/float constant */
short ac_length; /* size in bytes */
argb_t ac_con; /* its string repres. */
} a_con;
} a_a;
};
/* Macros to increase readability: */
#define INSTR(lnp) (lnp->l_instr & BMASK)
#define TYPE(lnp) lnp->l_optype
#define PREV(lnp) lnp->l_prev
#define SHORT(lnp) lnp->l_a.la_short
#define OFFSET(lnp) lnp->l_a.la_offset
#define INSTRLAB(lnp) lnp->l_a.la_instrlab
#define OBJ(lnp) lnp->l_a.la_obj
#define PROC(lnp) lnp->l_a.la_proc
#define ARG(lnp) lnp->l_a.la_arg
/* Data structures for Intermediate Code generation */
struct sym {
sym_p sy_next; /* link */
char sy_name[IDL]; /* name of the symbol */
dblock_p sy_dblock; /* pointer to dblock struct */
};
struct prc {
prc_p pr_next; /* link */
char pr_name[IDL]; /* name of the procedure */
proc_p pr_proc; /* pointer tto proc struct */
};
struct num {
num_p n_next; /* link */
unsigned n_number; /* EM repr. e.g. 120 in 'BRA *120' */
lab_id n_labid; /* sequential integer repr. of IC */
};
/* Data structures for Inline Substitution */
struct call {
proc_p cl_caller; /* calling procedure */
call_id cl_id; /* uniquely denotes a CAL instruction */
proc_p cl_proc; /* the called procedure */
byte cl_looplevel; /* loop nesting level of the CAL */
bool cl_flags; /* flag bits */
short cl_ratio; /* indicates 'speed gain / size lost' */
call_p cl_cdr; /* link to next call */
call_p cl_car; /* link to nested calls */
actual_p cl_actuals; /* actual parameter expr. trees */
};
#define CLF_INLPARS 017 /* min(15,nr. of inline parameters) */
#define CLF_SELECTED 020 /* is call selected for expansion? */
#define CLF_EVER_EXPANDED 040 /* ever expanded? e.g. in a nested call. */
#define CLF_FIRM 0100 /* indicates if the call takes place in a
* firm block of a loop (i.e. one that
* is always executed, except
* -perhaps- at the last iteration).
* Used for heuristics only.
*/
struct actual {
line_p ac_exp; /* copy of EM text */
/* 0 for actuals that are not inline */
offset ac_size; /* number of bytes of parameter */
bool ac_inl; /* TRUE if it may be expanded in line */
actual_p ac_next; /* link */
};
struct formal {
offset f_offset; /* offsetin bytes */
byte f_flags; /* flags FF_BAD etc. */
byte f_type; /* SINGLE, DOUBLE,POINTER,UNKNOWN */
formal_p f_next; /* link */
};
/* flags of formal: */
#define FF_BAD 01
#define FF_REG 02
#define FF_ONCEUSED 04
#define FF_OFTENUSED 06
#define USEMASK 014
/* types of formals: */
#define SINGLE 1
#define DOUBLE 2
#define POINTER 3
#define UNKNOWN 4
/* 'call-count' information keeps track of the number
* of times one procedure calls another. Conceptually,
* it may be regarded as a two dimensional array, where
* calcnt[p,q] is the number of times p calls q. As this
* matrix would be very dense, we use a more efficient
* list representation. Every procedure has a list
* of calcnt structs.
*/
struct calcnt {
proc_p cc_proc; /* the called procedure */
short cc_count; /* # times proc. is called in the
* original text of the caller.
*/
calcnt_p cc_next; /* link */
};
/* Data structures for Strength Reduction */
/* An induction variable */
struct iv {
offset iv_off; /* offset of the induction variable */
line_p iv_incr; /* pointer to last instr. of EM-code that
* increments the induction variable */
offset iv_step; /* step value */
};
/* All information about a reducible piece of code is collected in
* a single structure.
*/
struct code_info {
loop_p co_loop; /* the loop the code is in */
bblock_p co_block; /* the basic block the code is in */
line_p co_lfirst; /* first instruction of the code */
line_p co_llast; /* last instruction of the code */
line_p co_ivexpr; /* start of linear expr. using iv */
line_p co_endexpr; /* end of the expression */
int co_sign; /* sign of iv in above expr */
iv_p co_iv; /* the induction variable */
offset co_temp; /* temporary variable */
int co_tmpsize; /* size of the temp. variable (ws or ps)*/
int co_instr; /* the expensive instruction (mli,lar..)*/
union {
line_p co_loadlc; /* LOC lc instruction (for mult.)*/
line_p co_desc; /* load address of descriptor
* (for lar etc.) */
} c_o;
};
/* Data structures for Use-Definition and Live-Dead Analysis */
struct local {
offset lc_off; /* offset of local in stackframe */
short lc_size; /* size of local in bytes */
short lc_flags; /* see below */
offset lc_score; /* score in register message, if regvar */
local_p lc_next; /* link, only used when building the list */
};
/* values of lc_flags */
#define LCF_BAD 01
/* Set when no ud-info for this local is maintained, e.g. when it is
* overlapped by another local.
*/
#define LCF_REG 02 /* register variable */
#define LCF_LIVE 04 /* use by live-dead message generation */
struct cond_tab {
short mc_cond; /* Denotes a condition e.g. FITBYTE */
short mc_tval; /* value for time optimization */
short mc_sval; /* value for space optimization */
short mc_dummy; /* allignment */
};
/* conditions: */
#define DEFAULT 0
#define FITBYTE 1
#define IN_0_63 2
#define IN_0_8 3

46
util/ego/sp/Makefile Normal file
View file

@ -0,0 +1,46 @@
EMH=../../../h
EML=../../../lib
SHARE=../share
OBJECTS=sp.o
MOBJECTS=sp.m
SHOBJECTS=$(SHARE)/get.o $(SHARE)/put.o $(SHARE)/alloc.o $(SHARE)/global.o $(SHARE)/debug.o $(SHARE)/files.o $(SHARE)/map.o $(SHARE)/lset.o $(SHARE)/cset.o $(SHARE)/aux.o $(SHARE)/stack_chg.o $(SHARE)/go.o
MSHOBJECTS=$(SHARE)/get.m $(SHARE)/put.m $(SHARE)/alloc.m $(SHARE)/global.m $(SHARE)/debug.m $(SHARE)/files.m $(SHARE)/map.m $(SHARE)/lset.m $(SHARE)/cset.m $(SHARE)/aux.m $(SHARE)/stack_chg.m
SRC=sp.c
.SUFFIXES: .m
.c.o:
cc $(CFLAGS) -c $<
.c.m:
ack -O -L -c.m $(CFLAGS) $<
all: $(OBJECTS)
sp: \
$(OBJECTS) $(SHOBJECTS)
cc -o sp -i $(OBJECTS) $(SHOBJECTS) $(EML)/em_data.a
optim: $(MOBJECTS) $(MSHOBJECTS)
ego IC CF $(F) CA $(MOBJECTS) $(MSHOBJECTS)
ack -O -o sp.ego -.c lfile.m $(EML)/em_data.a
lpr:
pr $(SRC) | lpr
# the next lines are generated automatically
# AUTOAUTOAUTOAUTOAUTOAUTO
sp.o: ../share/alloc.h
sp.o: ../share/aux.h
sp.o: ../share/debug.h
sp.o: ../share/files.h
sp.o: ../share/get.h
sp.o: ../share/global.h
sp.o: ../share/go.h
sp.o: ../share/lset.h
sp.o: ../share/map.h
sp.o: ../share/put.h
sp.o: ../share/stack_chg.h
sp.o: ../share/types.h
sp.o: ../../../h/em_mnem.h
sp.o: ../../../h/em_spec.h
stack_chg.o: ../share/debug.h
stack_chg.o: ../share/global.h
stack_chg.o: ../share/types.h
stack_chg.o: ../../../h/em_mnem.h
stack_chg.o: ../../../h/em_spec.h
stack_chg.o: pop_push.h

240
util/ego/sp/sp.c Normal file
View file

@ -0,0 +1,240 @@
/* S T A C K P O L L U T I O N
*
* S P . C
*/
#include <stdio.h>
#include "../share/types.h"
#include "../share/debug.h"
#include "../share/global.h"
#include "../share/files.h"
#include "../share/get.h"
#include "../share/put.h"
#include "../share/lset.h"
#include "../share/map.h"
#include "../share/alloc.h"
#include "../share/aux.h"
#include "../share/go.h"
#include "../share/stack_chg.h"
#include "../../../h/em_mnem.h"
#include "../../../h/em_spec.h"
/* Stack pollution throws away the ASP instructions after a procedure call.
* This saves a lot of code, at the cost of some extra stack space.
* ASPs that are part of a loop are not removed.
*/
#define BF_MARK 04
#define MARK(b) b->b_flags |= BF_MARK
#define NOT_MARKED(b) (!(b->b_flags&BF_MARK))
#define IN_LOOP(b) (Lnrelems(b->b_loops) > 0)
STATIC int Ssp; /* number of optimizations */
/* According to the EM definition, the stack must be cleaned up
* before any return. However, for some backends it causes no harm
* if the stack is not cleaned up. If so, we can do Stack Pollution
* more globally.
*/
STATIC int globl_sp_allowed;
#define IS_ASP(l) (INSTR(l) == op_asp && TYPE(l) == OPSHORT && SHORT(l) > 0)
STATIC sp_machinit(f)
FILE *f;
{
/* Read target machine dependent information for this phase */
char s[100];
for (;;) {
while(getc(f) != '\n');
fscanf(f,"%s",s);
if (strcmp(s,"%%SP") == 0)break;
}
fscanf(f,"%d",&globl_sp_allowed);
}
comb_asps(l1,l2,b)
line_p l1,l2;
bblock_p b;
{
assert(INSTR(l1) == op_asp);
assert(INSTR(l2) == op_asp);
assert(TYPE(l1) == OPSHORT);
assert(TYPE(l2) == OPSHORT);
SHORT(l2) += SHORT(l1);
rm_line(l1,b);
}
stack_pollution(b)
bblock_p b;
{
/* For every pair of successive ASP instructions in basic
* block b, try to combine the two into one ASP.
*/
register line_p l;
line_p asp,next = b->b_start;
bool asp_seen = FALSE;
int stack_diff,pop,push;
bool ok;
do {
stack_diff = 0;
for (l = next; l != (line_p) 0; l = next) {
next = l->l_next;
if (IS_ASP(l)) break;
if (asp_seen) {
if (INSTR(l) == op_ret) {
stack_diff -= SHORT(l);
} else {
line_change(l,&ok,&pop,&push);
if (!ok || (stack_diff -= pop) < 0) {
/* can't eliminate last ASP */
asp_seen = FALSE;
} else {
stack_diff += push;
}
}
}
}
if (asp_seen) {
if (l == (line_p) 0) {
/* last asp of basic block */
if (globl_sp_allowed &&
NOT_MARKED(b) && !IN_LOOP(b)) {
Ssp++;
rm_line(asp,b);
}
} else {
/* try to combine with previous asp */
if (SHORT(l) == stack_diff) {
Ssp++;
comb_asps(asp,l,b);
}
}
}
asp = l;
asp_seen = TRUE; /* use new ASP for next try! */
} while (asp != (line_p) 0);
}
STATIC bool block_save(b)
bblock_p b;
{
register line_p l;
int stack_diff,pop,push;
bool ok;
stack_diff = 0;
for (l = b->b_start; l != (line_p) 0; l = l->l_next) {
if (INSTR(l) == op_ret) {
stack_diff -= SHORT(l);
break;
}
line_change(l,&ok,&pop,&push);
/* printf("instr %d, pop %d,push %d,ok %d\n",INSTR(l),pop,push,ok); */
if (!ok || (stack_diff -= pop) < 0) {
return FALSE;
} else {
stack_diff += push;
}
}
return stack_diff >= 0;
}
STATIC mark_pred(b)
bblock_p b;
{
Lindex i;
bblock_p x;
for (i = Lfirst(b->b_pred); i != (Lindex) 0; i = Lnext(i,b->b_pred)) {
x = (bblock_p) Lelem(i);
if (NOT_MARKED(x)) {
MARK(x);
mark_pred(x);
}
}
}
STATIC mark_unsave_blocks(p)
proc_p p;
{
register bblock_p b;
for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
if (NOT_MARKED(b) && !block_save(b)) {
MARK(b);
mark_pred(b);
}
}
}
sp_optimize(p)
proc_p p;
{
register bblock_p b;
mark_unsave_blocks(p);
for (b = p->p_start; b != 0; b = b->b_next) {
stack_pollution(b);
}
}
main(argc,argv)
int argc;
char *argv[];
{
go(argc,argv,no_action,sp_optimize,sp_machinit,no_action);
report("stack adjustments deleted",Ssp);
exit(0);
}
/***** DEBUGGING:
debug_stack_pollution(p)
proc_p p;
{
register bblock_p b;
register line_p l;
int lcnt,aspcnt,instr;
for (b = p->p_start; b != 0; b = b->b_next) {
lcnt = 0; aspcnt = 0;
for (l = b->b_start; l != 0; l= l->l_next) {
instr = INSTR(l);
if (instr >= sp_fmnem && instr <= sp_lmnem) {
lcnt++;
if (instr == op_asp && off_set(l) > 0) {
aspcnt++;
}
}
}
printf("%d\t%d\n",aspcnt,lcnt);
}
}
*/