File:  [Research Unix] / researchv9 / cmd / sun / as / sdi.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 17:21:59 2018 UTC (8 years, 1 month ago) by root
Branches: belllabs, MAIN
CVS tags: researchv9-SUN3_old, researchv9-SUN3, HEAD
researchv9-SUN3(old)

#ifndef lint
static	char sccsid[] = "@(#)sdi.c 1.1 86/02/03 Copyr 1985 Sun Micro";
#endif

/*
 * Copyright (c) 1985 by Sun Microsystems, Inc.
 */

#include "as.h"

/*
 * module to handle span-dependent instructions, e.g. jbr
 * see CACM Vol 21 No 4 April 1978 for a description of the
 * algorithm for resolving sdi's between the 1st and 2nd pass
 */

#define SQUID( x ) ( ((x)==C_TEXT)||(rflag && ((x) != C_BSS)) )
#define INFINITY	0x7fffffff	/* larger than any address */

struct sdi {	/* information for span dependent instructions (sdi's) */
	struct sdi 	*sdi_next;      /* next sdi in list		   */
	long int 	sdi_loc;	/* location of the sdi		   */
	union{
	    struct sym_bkt 	*sdi_Sym;/* symbol part of the sdi address  */
	    int			*sdi_Add;
	}su;
#define	sdi_sym	su.sdi_Sym
#define sdi_address su.sdi_Add
	long int 	sdi_off;	/* offset part of the sdi address  */
	short 		sdi_leng;	/* actual length of the sdi	   */
	long int 	sdi_base;	/* origin of the span of the sdi   */
	short		sdi_flags;	/* bits: is/was it a ddi? 	   */
# if EBUG
	unsigned	sdi_lineno;
# endif EBUG
} *sdi_list;				/* linked list of descriptors */

struct sdi *ddi_list;

/* sdi_flag bit */
#define SDI_TYPE 7 /* SDI4, SDI6, SDI8, SDIP */
#define SDI_DDI       010
#define SDI_EXTERN 020

		  /* SDI4, SDI6, SDI8, SDIP, SDIX, SDIL */
static int glen[] = {  4,    6,    8,    4,    6,    4 };


/* grab a new sdi descriptor off of the free list; allocate space
 * for a new free list if necessary
 * stolen from the symbol-table manager
 * We keep the sdi and ddi lists separate because:
 * 1) we want to keep locality in the sdi list
 * 2) we want to give back the ddi list
 */

#define SEGALLOC 	100
static
struct seglist { int allocsize, nalloced; struct sdi *nextfree, *head; char *segname;} 
	sdilist = { SEGALLOC, 0, NULL, NULL, "sdi" },
        ddilist = { SEGALLOC, 0, NULL, NULL, "ddi" };

static long total_delta = 0;

struct sdi *
sdi_alloc(s)
    register struct seglist *s;
{	
    register struct sdi	*sdp, *sdi_free;
    register int i;

    if ((sdp = s->nextfree) != NULL) 
	s->nextfree = sdp->sdi_next;
    else {
        sdp = (struct sdi *)calloc(s->allocsize,sizeof(struct sdi));
        if (sdp == NULL) sys_error("%s storage exceeded\n", s->segname);
	sdp->sdi_next = s->head;
	s->head = sdp++;
	sdi_free = NULL;
        for (i = s->allocsize-2; i--;) {
	    sdp->sdi_next = sdi_free;
	    sdi_free = sdp++;
        }
	s->nextfree = sdi_free;
	s->allocsize *= 2;
    }
    s->nalloced++;
    return(sdp);
}

void
sdi_free(s)
    register struct seglist *s;
{
    register struct sdi *t, *u;
    u = s->head;
    while (u != NULL){
	t = u->sdi_next;
	free(u);
	u = t;
    }
    s->head = NULL;
    s->nextfree = NULL;
    s->allocsize = SEGALLOC;
    s->nalloced = 0;
}

/*
 * short-lived data structure
 * used just for sorting the destination addresses, and keeping pointers
 * to the corresponding sdi's.
 */
struct sdi_destpair { struct sdi *user; int address; };

int * destlist;
int destsize;

sort_destpair( a, b )
    struct sdi_destpair *a, *b;
{
	return a->address - b->address;
}

/*
 * reformulate the base/span representation as follows:
 * for each sdi, form a pair: a pointer to the sdi structure and
 * the destination address (relative to the text base).
 * Sort on destination address, then re-form as an integer array
 * into which point the sdi structures. The array can be segmented
 * to speed up processing it.
 */
make_destlist()
{
	struct sdi_destpair *darray;
	register struct sdi_destpair *dp;
	register struct sdi *s, *d;
	register int *a;
	register ndest = 0;
	register lastaddr, newaddr;
        int ddidelta;

	if ( sdilist.nalloced==0 ) return; /* empty list */
	if ((darray = (struct sdi_destpair *)calloc( sdilist.nalloced , sizeof( struct sdi_destpair))) == NULL)
		sys_error("cannot allocate intermediate structure");
	for (s=sdi_list, dp = darray; s; s = s->sdi_next){
	    if (s->sdi_loc == -1 ) continue;
	    if (s->sdi_sym == NULL){
		dp->address = s->sdi_off;
		dp++ -> user = s;
		ndest++;
	    }
	}
	if (destsize=ndest){
	    qsort( darray, ndest, sizeof (struct sdi_destpair), sort_destpair );
	    destlist = a = (int *)calloc( ndest, sizeof a[0] );
	    if (a==NULL)
		    sys_error("cannot allocate intermediate structure");
	    dp = darray;
	    *a = lastaddr = dp->address;
	    dp++ ->user->sdi_address = a++;
	    while (--ndest > 0){
		if (lastaddr == (newaddr = dp->address)){
		    /* pick up the occasional duplicate destination address */
		    a--;
		    destsize--;
		}else
		    *a = lastaddr = newaddr;
		dp++ ->user->sdi_address = a++;
	    }
	}
	free( darray );
        /*
         * this is the part I hate.
         * sweep through the sdi list and the dest list in parallel.
         * for each sdi that was a ddi, subtract 2 from all destinations
         * above the base address of the s(d)di. At the same time, 
         * sweep through the ddi list and subtract the shrinkage size
         * from all destinations above the base address of the ddi. 
         * These are corrective factors we must apply because of initially
         * assuming that all ddi's would be long. The base addresses of
         * the sdi's have already been fixed by the routine resolve_ddi.
         */
        if (destsize){
            a = destlist;
            ndest = destsize;
            s = sdi_list;
            d = ddi_list;
            ddidelta = 0;
            while(ndest--){
                newaddr = *a;
                while( s != NULL && s->sdi_loc < newaddr ){
                    if (s->sdi_flags & SDI_DDI)
                        ddidelta += 2;
                    s = s->sdi_next;
                }
                while( d != NULL && d->sdi_loc < newaddr ){
                    ddidelta += d->sdi_flags;
                    d = d->sdi_next;
                }
                *a++ -= ddidelta;
	    }
	}
}

/*
 * routine to create a sdi descriptor and insert it into the list
 */
makesdi(op, base, flavor)
	struct oper *op;/* the operand of the sdi */
	long int base;	/* origin of the the initial span of the sdi, to be */
			/* corrected between pass1 and pass2 by subtracting */
			/* from the value of the symbol of the operand */
			/* i.e. 0 for resolving short absolute address modes */
			/* Dot[+increment] for PC relative addresses */
{
	register struct sdi *s, **p;
	register int loc;
	static long lastloc = INFINITY; /* you wanna see hackery? */
	static struct sdi * lastsdi;	 /* we got hackery         */

	if (!op ){                 /* put the procedure boundary: .proc */
		if (Oflag) return;
		s = sdi_alloc(&sdilist);
		s->sdi_loc = -1;
		for ( p = lastsdi ? &(lastsdi->sdi_next) : &sdi_list;
		     *p;
		      p = &(*p)->sdi_next );
		s->sdi_next = *p;
		*p = s;
		return;
	}
	if (op->flags_o&O_COMPLEX)	/* not a simple address */
		return(0);
	s = sdi_alloc(&sdilist);
	s->sdi_loc = dot; /* future bug here */
	s->sdi_flags = flavor;
	s->sdi_sym = op->sym_o;
        s->sdi_off = op->value_o-op->sym_o->value_s; /* collect offset-from-label */
	s->sdi_base = base;
	s->sdi_leng = 2;	/* shortest length */
# if EBUG
	s->sdi_lineno = line_no;
# endif EBUG
	loc = dot;
	for ( p = (loc < lastloc) ? &sdi_list : &(lastsdi->sdi_next);
	     *p; 
	      p = &(*p)->sdi_next)
		if (loc < (*p)->sdi_loc)
			break;
	s->sdi_next = *p;
	*p = s;
	lastloc = loc;
	lastsdi = s;
	return(2);	/* return the current length */
}

/*
 * resolve sdi's between pass1 and pass2
 * basic algorithm is to repeatedly look for sdi that must use the
 * long form, and update the span of other sdi's.
 * When this terminates, all remaining sdi's can use the short form
 */
sdi_resolve()
{
	register struct sdi *s;
	register struct sym_bkt *p;
	register int t;
	struct sdi *first, *end, *q;
	long loc, big_delta = 0;
	int *dest, *start;
	int changed;
	int csect_offsets[5];

	csect_offsets[C_UNDEF] = 0;
	csect_offsets[C_TEXT ] = 0;
	csect_offsets[C_DATA ] = tsize;
	csect_offsets[C_DATA1] = tsize + dsize;
	csect_offsets[C_DATA2] = tsize + dsize + d1size;

	for (s = sdi_list; s && s->sdi_loc == -1; s = s->sdi_next );
	sdi_list = s;             /* get rid of .proc at the beginning */

	for (s = sdi_list; s; s = s->sdi_next) {
		if (s->sdi_loc == -1) continue;
		p = s->sdi_sym;
		if (SQUID(p->csect_s) && (p->attr_s & S_DEF)){
			s->sdi_off += p->value_s + csect_offsets[p->csect_s];
			s->sdi_sym = NULL;
		} else
			s->sdi_flags |= SDI_EXTERN;
	}
        ddi_resolve();
	make_destlist();

	for (first = sdi_list; first; ) {
		for (s=first; s && s->sdi_loc != -1; s=s->sdi_next )
			end = s;         /* find last sdi in this procedure */
		for (s=end->sdi_next; s && s->sdi_loc == -1; s=s->sdi_next );
		if (s)
			loc = s->sdi_loc + total_delta;
		else
			loc = INFINITY;
		if (destsize) {
		    for (dest = destlist;
		        *dest <= loc && dest < destlist + destsize;
		         dest++ );
		    start = --dest;     /* last label before 'end' */ 
		}
		do {
			changed = 0;
			for (s = first; s && s->sdi_loc!=-1 ; s = s->sdi_next) {
				if ((t = sdi_len(s) - s->sdi_leng) > 0) {
					big_delta += t;
					longsdi(s, t, first, start);
					changed = 1;
				} else if (t < 0) {
					sys_error("Pathological sdi\n");
				}
			}
		} while (changed);

		for (s=end->sdi_next; s && s->sdi_loc == -1; s=s->sdi_next );
		first = s;
		end->sdi_next = first;   /* get rid of boundary .proc */
		for (q = first; q; q = q->sdi_next )
			q->sdi_base += big_delta;
		if (destsize)
			for ( dest = destlist + destsize - 1; dest > start; dest-- )
				*dest += big_delta;
		total_delta += big_delta;
		big_delta = 0;
	}
}

/*
 * update sdi list assuming the specified sdi must be long
 */
longsdi(s, delta, first, start )
register struct sdi *s, *first;
register int delta;
int * start;     
{
	register struct sdi *t;
	register long loc;
	register int *dest;

	s->sdi_leng += delta;	/* update the length of this sdi */
	/* get the real location of the sdi */
	loc = s->sdi_loc + sdi_inc( s->sdi_loc, first);
	for (t = s->sdi_next; t && t->sdi_loc != -1; t = t->sdi_next)
		t->sdi_base += delta;
	if (destsize){
	    for ( dest = start; *dest > loc && dest >= destlist; dest-- )
		*dest += delta;
	}
}

/*
 * compute the length of the specified sdi by looking at the possible choices
 */
sdi_len(s)
register struct sdi *s;
{
	struct bchoice { int len; int lbound, ubound; };
	static struct bchoice bsdi68[] = {
		{ 2, -128, 127 },
		{ 4, -32768, 32767 },
		{ 0,      0,     0 },
	};
	static struct bchoice bsdi4[] = {
		{ 2, -128, 127 },
		{ 0,    0,   0 },
	};
	static struct bchoice bsdip[] = {
		{ 2, -32768, 32767 },
		{ 0,      0,     0 },
	};
					  /* SDI4,   SDI6,   SDI8,  SDIP, */
	static struct bchoice *blists[] = { bsdi4, bsdi68, bsdi68, bsdip, };
	/* SDIX and SDIL are never span-dependent, so are not in this table! */

	register struct bchoice *b;
	register span;

	span = *s->sdi_address - s->sdi_base;
	if (!(s->sdi_flags&SDI_EXTERN)){
		for (b = blists[ s->sdi_flags&SDI_TYPE ]; b->len; b++ )
		    if ( b->lbound <= span && span <= b->ubound)
			return(b->len);
	}
	/* only the most general case will do */
	return(glen[s->sdi_flags&SDI_TYPE]);
}

/*
 * return the total number of extra bytes due to long sdi's before
 * the specified offset
 */
sdi_inc(offset, first)
	struct sdi *first;
	register long offset;
{
	register struct sdi *s;
	register int total;

	if (first) {
		s = first;
		total = total_delta;
	} else {
		s = sdi_list;
		total = 0;
	}
	for (; s; s = s->sdi_next) {
	    if (s->sdi_loc == -1 ) continue;
	    if (offset <= s->sdi_loc)
		    break;
	    total += s->sdi_leng - 2;
            if (s->sdi_flags&SDI_DDI){
                total -= 2;
            }
	}
       for (s = ddi_list ; s; s = s->sdi_next) {
            if (offset <= s->sdi_loc)
                    break;
            total -= s->sdi_flags; /* if its still on the list, it shrank */
        }
	return(total);
}

/* 
 * We are given an array of pointers to symbol-table entries for
 * text labels, sorted by location. Update these by the changes in the
 * sdi's. This used to be done by repeated calls to sdi_inc(), but this
 * was obscenely expensive. Now that we have the lists sorted the same way,
 * we should be able to compute this incrementally. Of course, there was
 * the extra overhead of sorting the symbol list in the first place.
 */
sdi_sym_update( symp, nsyms )
    register struct sym_bkt **symp;
    register int nsyms;
{
    register struct sdi      *sdi;
    register struct sdi      *ddi;
    register curloc, curdelta;
    register symloc, curddi;
    curdelta = 0;

    if (nsyms==0) return;
    if ((sdi = sdi_list) == NULL)
	curloc = INFINITY;
    else
	curloc = sdi->sdi_loc;
    if ((ddi = ddi_list) == NULL)
        curddi = INFINITY;
    else
        curddi = ddi->sdi_loc;
    while (nsyms--){
	symloc = (*symp)->value_s;
	while (symloc > curloc){
	    curdelta += sdi->sdi_leng - 2;
            if (sdi->sdi_flags&SDI_DDI){
                curdelta -= 2;
            }
	    if ((sdi = sdi->sdi_next) == NULL){
		curloc = INFINITY;
	    } else {
		curloc = sdi->sdi_loc;
	    }
	}
        while (symloc > curddi){
            curdelta -= ddi->sdi_flags ; /* if on ddi list, it shrank */
            if ((ddi = ddi->sdi_next) == NULL){
                curddi = INFINITY;
            } else {
                curddi = ddi->sdi_loc;
            }
        }
	(*symp++)->value_s = symloc + curdelta;
    }
}

/*
 * Data-dependent instructions 
 * are those whose length we may not be able to ascertain at first pass,
 * but which we would like to address in the most effecient manner.
 * Determining their length could, in full generality, be an iterative
 * process, but I think we can make some useful simplifications that
 * will do most of what we desire in one pass over our intermediate
 * data structure. The gross structure will mimic the sdi code above.
 * The instructions we are dealing with here have three forms:
 * long absolute, word absolute, and PC-relative. Thus, there are
 * two lengths of addressing extensions: two bytes and four bytes.
 */

/*
 * Statistically, ALL data references are long. Thus it makes little
 * sense to follow the sdi strategy of assuming the shortest form until
 * proven otherwise. Since the ddi list is only looked at once, we
 * don't have to maintain the data structures required by the sdi algorithm.
 * In fact, once we've decided that a reference is long, we can take it 
 * entirely off the list, if we make this assumption.
 */
makeddi(op, base, flavor, wrtflg )
        struct oper *op;/* the operand of the sdi */
        long int base;  /* origin of the the initial span of the sdi, to be */
                        /* corrected between pass1 and pass2 by subtracting */
                        /* from the value of the symbol of the operand */
                        /* i.e. 0 for resolving short absolute address modes */
                        /* Dot[+increment] for PC relative addresses */
        int flavor;     /* form of ddi: usually SDIP, but for index SDIX, or SDIL for link*/
        int wrtflg;     /* 0 => not memory write destination: PC-relative ok */
                        /* 1 => memory write destination: PC-relative NOT ok */
{
        register struct sdi *s, **p;
        register int loc;
        static long lastloc = INFINITY;  /* you wanna see hackery? */
        static struct sdi * lastddi;     /* we got hackery         */
  
        if (op->flags_o&O_COMPLEX)      /* not a simple address */
                return(0);
        s = sdi_alloc( &ddilist );
        s->sdi_loc = dot; /* soon to be a bug */
        s->sdi_sym = op->sym_o;
        s->sdi_off = op->value_o;
        s->sdi_base = base;
        s->sdi_leng = 4;        /* assumed length */
        s->sdi_flags =  flavor;
# if EBUG
        s->sdi_lineno = line_no;
# endif EBUG
        if (wrtflg) s->sdi_flags |= SDI_DDI;
        loc = dot;

        for ( p = (loc < lastloc) ? &ddi_list : &(lastddi->sdi_next);
             *p;
             p = &(*p)->sdi_next)
                if (loc < (*p)->sdi_loc)
                        break;
        s->sdi_next = *p;
        lastloc = loc;
        lastddi = s;
        *p = s;
        return(flavor==SDI6?6:4);       /* return the current length */
}


ddi_resolve()
{
        register struct sdi *s, **spp, *q;
        register struct sym_bkt *p;
        register int t;

        spp = &ddi_list;
        while (s = *spp){
                p = s->sdi_sym;
                switch (s->sdi_flags){
                case SDIP:
                    if (SQUID(p->csect_s ) && (p->attr_s & S_DEF) ){
                            /* this looks like a job for PC@()  */
                            /* fill out the sdi structure, and 
                                insert it in the sdi list */
                            register struct sdi **pp;
                            struct sdi *newsdi;

                            s->sdi_off += p->value_s;
                            s->sdi_sym = NULL;
                            /* take ourselves off of ddi list */
                            *spp = s->sdi_next;
                            /* 
                             * here's the tricky part:
                             * all the addresses in our lists assume this
                             * address has length four. We will now change
                             * them so it appears to have length two.
                             */
                            shortddi( s, 2 );
                            s->sdi_flags  = SDI_DDI|SDIP;
                            /* now put ourselves in the sdi list */
                            for (pp = &sdi_list; *pp; pp = &(*pp)->sdi_next)
                                    if (s->sdi_loc < (*pp)->sdi_loc)
                                            break;
                            newsdi = sdi_alloc(&sdilist);
                            *newsdi = *s;
                            newsdi->sdi_next = *pp;
                            *pp = newsdi;
                            /* fix addresses on sdi list, too */
			    newsdi->sdi_leng -= 2;
			    for ( q= newsdi->sdi_next; q; q = q->sdi_next )
				    q->sdi_base -= 2;
                            /* sdi routines will fix it up later */
                            continue;
                    } 
                    /* else fall through */
                case SDIP|SDI_DDI:
  		case SDI4:
  		case SDI4|SDI_DDI:
  		    if ((p->attr_s & S_DEF) && p->csect_s == C_UNDEF) {
  			    /* try on a short absolute */
  			    t = p->value_s + s->sdi_off;
  			    if (t >= -32768 && t <= 32767 ){
  				    shortddi( s, 2 );
  				    spp = &s->sdi_next;
  				    continue;
  			    }
  
  		    }
  		    /* else it does not shrink -- take off list */
  		    break;
  		case SDI6:
  		case SDI6|SDI_DDI:
  		    /*
  		     * Index mode. 
  		     */
  		    /* Write bit only confuses us: delete it */
  		    s->sdi_flags = SDI6;
  		    if ((p->attr_s & S_DEF) && p->csect_s == C_UNDEF){
  			/* try very short, then just plain short */
  			t = p->value_s + s->sdi_off;
  			if ( t >= -128 && t <= 127 ){
  			     /* shrinks much */
  			    shortddi( s, 4 );
  			    spp = &s->sdi_next;
  			    continue;
  			} else if (t >= -32768 && t <= 32767 ){
  			    /* shrinks some */
  			    shortddi( s, 2 );
  			    spp = &s->sdi_next;
  			    continue;
  			} 
  		    }
  		    /* else it does not shrink -- take off list */
  		    break;
  		case SDIX:
  		case SDIX|SDI_DDI:
  		    /*
  		     * should be displacement mode, but will be
  		     * index mode with supressed indexing if the
  		     * displacement is too big.
  		     */
  		    /* FALL THROUGH */
  		case SDIL:
  		    /* 
  		     * link instruction: looks like SDIP, but is not
  		     * PC-relative.
  		     */
  		    /* Write bit only confuses us: delete it */
  		    s->sdi_flags &= SDI_TYPE;
  		    if ((p->attr_s & S_DEF) && p->csect_s == C_UNDEF){
  			/* try short */
  			t = p->value_s + s->sdi_off;
  			if (t >= -32768 && t <= 32767 ){
  			    /* shrinks */
  			    shortddi( s, glen[s->sdi_flags&SDI_TYPE] - 2 );
  			    spp = &s->sdi_next;
  			    continue;
  			}
  		    }
  		    /* else it does not shrink -- take off list */
  		    break;
  		}
  		/* take ddi off the list */
  		*spp = s->sdi_next;
  	}
  	if (ddi_list == NULL){
  	    sdi_free(&ddilist); /* all gone */
  	} else {
  	    register int cursdi;
  	    /*
  	     * Sweep through ddi list and sdi list in parallel,
  	     * fixing up sdi base-addresses.
  	     */
  	    s = ddi_list;
  	    t = 0;
  	    q = sdi_list;
  	    while (q != NULL){
  		cursdi = q->sdi_base;
  		while ( s != NULL && s->sdi_base < cursdi ){
  		    t += s->sdi_flags;
  		    s = s->sdi_next;
  		}
  		q->sdi_base -= t;
  		q = q->sdi_next;
  	    }
  	}
  }

shortddi( s, d )
    register struct sdi *s;
    register d;
{
    /*
     * A ddi shrank! Adjust all lists accordingly.
     */
    register struct sdi *t;
    register loc = s->sdi_loc+sdi_inc( s->sdi_loc, NULL);

    s->sdi_flags = d; /* how much I shrank */
    for (t = ddi_list; t; t = t->sdi_next)
	    if (t != s && t->sdi_base > loc)
		    t->sdi_base -= d;
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.