ack/util/ego/share/get.c

912 lines
19 KiB
C
Raw Normal View History

1984-11-26 15:04:22 +00:00
/* 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;
}