/* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. */ #ifndef lint static char sccsid[] = "@(#)rec.c 5.1 (Berkeley) 6/5/85"; #endif not lint #include "whoami.h" #include "0.h" #include "tree.h" #include "opcode.h" #include "align.h" #include "tree_ty.h" /* * set this to TRUE with adb to turn on record alignment/offset debugging. */ bool debug_records = FALSE; #define DEBUG_RECORDS(x) if (debug_records) { x ; } else /* * Build a record namelist entry. * Some of the processing here is somewhat involved. * The basic structure we are building is as follows. * * Each record has a main RECORD entry, * with an attached chain of fields as ->chain; * these enclude all the fields in all the variants of this record. * Fields are cons'ed to the front of the ->chain list as they are discovered. * This is for reclook(), but not for sizing and aligning offsets. * * If there are variants to the record, NL_TAG points to the field which * is the tag. If its name is NIL, the tag field is unnamed, and is not * allocated any space in the record. * Attached to NL_VARNT is a chain of VARNT structures * describing each of the variants. These are further linked * through ->chain. Each VARNT has, in ->range[0] the value of * the associated constant, and each points at a RECORD describing * the subrecord through NL_VTOREC. These pointers are not unique, * more than one VARNT may reference the same RECORD. * * On the first pass, we traverse the parse tree and construct the namelist * entries. This pass fills in the alignment of each record (including * subrecords (the alignment of a record is the maximum of the alignments * of any of its fields). * A second pass over the namelist entries fills in the offsets of each field * based on the alignments required. This second pass uses the NL_FIELDLIST * chaining of fields, and the NL_TAG pointer and the NL_VARNT pointer to get * to fields in the order in which they were declared. * This second pass can not be folded into the first pass, * as the starting offset of all variants is the same, * so we must see all the variants (and especially must know their alignments) * before assigning offsets. With the alignments calculated (by the first * pass) this can be done in one top down pass, max'ing over the alignment of * variants before assigning offsets to any of them. */ /* * P0 points to the outermost RECORD for name searches. */ struct nl *P0; struct nl * tyrec(r, off) struct tnode *r; int off; { struct nl *recp; DEBUG_RECORDS(fprintf(stderr,"[tyrec] off=%d\n", off)); /* * build namelist structure for the outermost record type. * then calculate offsets (starting at 0) of the fields * in this record and its variant subrecords. */ recp = tyrec1(r, TRUE); rec_offsets(recp, (long) 0); return recp; } /* * Define a record namelist entry. * r is the tree for the record to be built. * first is a boolean indicating whether this is an outermost record, * for name lookups. * p is the record we define here. * P0was is a local which stacks the enclosing value of P0 in the stack frame, * since tyrec1() is recursive. */ struct nl * tyrec1(r, first) register struct tnode *r; /* T_FLDLST */ bool first; { register struct nl *p, *P0was; DEBUG_RECORDS(fprintf(stderr,"[tyrec1] first=%d\n", first)); p = defnl((char *) 0, RECORD, NLNIL, 0); P0was = P0; if (first) P0 = p; #ifndef PI0 p->align_info = A_MIN; #endif if (r != TR_NIL) { fields(p, r->fldlst.fix_list); variants(p, r->fldlst.variant); } P0 = P0was; return (p); } /* * Define the fixed part fields for p. * hang them, in order, from the record entry, through ->ptr[NL_FIELDLIST]. * the fieldlist is a tconc structure, and is manipulated * just like newlist(), addlist(), fixlist() in the parser. */ fields(p, r) struct nl *p; struct tnode *r; /* T_LISTPP */ { register struct tnode *fp, *tp, *ip; struct nl *jp; struct nl *fieldnlp; DEBUG_RECORDS(fprintf(stderr,"[fields]\n")); for (fp = r; fp != TR_NIL; fp = fp->list_node.next) { tp = fp->list_node.list; if (tp == TR_NIL) continue; jp = gtype(tp->rfield.type); line = tp->rfield.line_no; for (ip = tp->rfield.id_list; ip != TR_NIL; ip = ip->list_node.next) { fieldnlp = deffld(p, (char *) ip->list_node.list, jp); if ( p->ptr[NL_FIELDLIST] == NIL ) { /* newlist */ p->ptr[NL_FIELDLIST] = fieldnlp; fieldnlp->ptr[NL_FIELDLIST] = fieldnlp; } else { /* addlist */ fieldnlp->ptr[NL_FIELDLIST] = p->ptr[NL_FIELDLIST]->ptr[NL_FIELDLIST]; p->ptr[NL_FIELDLIST]->ptr[NL_FIELDLIST] = fieldnlp; p->ptr[NL_FIELDLIST] = fieldnlp; } } } if ( p->ptr[NL_FIELDLIST] != NIL ) { /* fixlist */ fieldnlp = p->ptr[NL_FIELDLIST]->ptr[NL_FIELDLIST]; p->ptr[NL_FIELDLIST]->ptr[NL_FIELDLIST] = NIL; p->ptr[NL_FIELDLIST] = fieldnlp; } } /* * Define the variants for RECORD p. */ variants(p, r) struct nl *p; register struct tnode *r; /* T_TYVARPT */ { register struct tnode *vc, *v; struct nl *vr; struct nl *ct; DEBUG_RECORDS(fprintf(stderr,"[variants]\n")); if (r == TR_NIL) return; ct = gtype(r->varpt.type_id); if ( ( ct != NLNIL ) && ( isnta( ct , "bcsi" ) ) ) { error("Tag fields cannot be %ss" , nameof( ct ) ); } line = r->varpt.line_no; /* * Want it even if r[2] is NIL so * we check its type in "new" and "dispose" * calls -- link it to NL_TAG. */ p->ptr[NL_TAG] = deffld(p, r->varpt.cptr, ct); for (vc = r->varpt.var_list; vc != TR_NIL; vc = vc->list_node.next) { v = vc->list_node.list; if (v == TR_NIL) continue; vr = tyrec1(v->tyvarnt.fld_list, FALSE); #ifndef PI0 DEBUG_RECORDS( fprintf(stderr, "[variants] p->align_info %d vr->align_info %d\n", p->align_info, vr->align_info)); if (vr->align_info > p->align_info) { p->align_info = vr->align_info; } #endif line = v->tyvarnt.line_no; for (v = v->tyvarnt.const_list; v != TR_NIL; v = v->list_node.next) (void) defvnt(p, v->list_node.list, vr, ct); } } /* * Define a field in subrecord p of record P0 * with name s and type t. */ struct nl * deffld(p, s, t) struct nl *p; register char *s; register struct nl *t; { register struct nl *fp; DEBUG_RECORDS(fprintf(stderr,"[deffld] s=<%s>\n", s)); if (reclook(P0, s) != NIL) { #ifndef PI1 error("%s is a duplicate field name in this record", s); #endif s = NIL; } /* * enter the field with its type */ fp = enter(defnl(s, FIELD, t, 0)); /* * if no name, then this is an unnamed tag, * so don't link it into reclook()'s chain. */ if (s != NIL) { fp->chain = P0->chain; P0->chain = fp; #ifndef PI0 /* * and the alignment is propagated back. */ fp->align_info = align(t); DEBUG_RECORDS( fprintf(stderr, "[deffld] fp->align_info %d p->align_info %d \n", fp->align_info, p->align_info)); if (fp->align_info > p->align_info) { p->align_info = fp->align_info; } #endif if (t != NIL) { P0->nl_flags |= t->nl_flags & NFILES; p->nl_flags |= t->nl_flags & NFILES; } } return (fp); } /* * Define a variant from the constant tree of t * in subrecord p of record P0 where the casetype * is ct and the variant record to be associated is vr. */ struct nl * defvnt(p, t, vr, ct) struct nl *p, *vr; struct tnode *t; /* CHAR_CONST or SIGN_CONST */ register struct nl *ct; { register struct nl *av; gconst(t); if (ct != NIL && incompat(con.ctype, ct , t )) { #ifndef PI1 cerror("Variant label type incompatible with selector type"); #endif ct = NIL; } av = defnl((char *) 0, VARNT, ct, 0); #ifndef PI1 if (ct != NIL) uniqv(p); #endif not PI1 av->chain = p->ptr[NL_VARNT]; p->ptr[NL_VARNT] = av; av->ptr[NL_VTOREC] = vr; av->range[0] = con.crval; return (av); } #ifndef PI1 /* * Check that the constant label value * is unique among the labels in this variant. */ uniqv(p) struct nl *p; { register struct nl *vt; for (vt = p->ptr[NL_VARNT]; vt != NIL; vt = vt->chain) if (vt->range[0] == con.crval) { error("Duplicate variant case label in record"); return; } } #endif /* * See if the field name s is defined * in the record p, returning a pointer * to it namelist entry if it is. */ struct nl * reclook(p, s) register struct nl *p; char *s; { if (p == NIL || s == NIL) return (NIL); for (p = p->chain; p != NIL; p = p->chain) if (p->symbol == s) return (p); return (NIL); } /* * descend namelist entry for a record and assign offsets. * fields go at the next higher offset that suits their alignment. * all variants of a record start at the same offset, which is suitable * for the alignment of their worst aligned field. thus the size of a * record is independent of whether or not it is a variant * (a desirable property). * records come to us in the namelist, where they have been annotated * with the maximum alignment their fields require. * the starting offset is passed to us, and is passed recursively for * variant records within records. * the final maximum size of each record is recorded in the namelist * in the value[NL_OFFS] field of the namelist for the record. * * this is supposed to match the offsets used by the c compiler * so people can share records between modules in both languages. */ rec_offsets(recp, offset) struct nl *recp; /* pointer to the namelist record */ long offset; /* starting offset for this record/field */ { long origin; /* offset of next field */ struct nl *fieldnlp; /* the current field */ struct nl *varntnlp; /* the current variant */ struct nl *vrecnlp; /* record for the current variant */ if ( recp == NIL ) { return; } origin = roundup((int) offset,(long) recp->align_info); if (origin != offset) { fprintf(stderr, "[rec_offsets] offset=%d recp->align_info=%d origin=%d\n", offset, recp->align_info, origin); panic("rec_offsets"); } DEBUG_RECORDS( fprintf(stderr, "[rec_offsets] offset %d recp->align %d origin %d\n", offset, recp->align_info, origin)); /* * fixed fields are forward linked though ->ptr[NL_FIELDLIST] * give them all suitable offsets. */ for ( fieldnlp = recp->ptr[NL_FIELDLIST]; fieldnlp != NIL; fieldnlp = fieldnlp->ptr[NL_FIELDLIST] ) { origin = roundup((int) origin,(long) align(fieldnlp->type)); fieldnlp->value[NL_OFFS] = origin; DEBUG_RECORDS( fprintf(stderr,"[rec_offsets] symbol %s origin %d\n", fieldnlp->symbol, origin)); origin += lwidth(fieldnlp->type); } /* * this is the extent of the record, so far */ recp->value[NL_OFFS] = origin; /* * if we have a tag field, we have variants to deal with */ if ( recp->ptr[NL_TAG] ) { /* * if tag field is unnamed, then don't allocate space for it. */ fieldnlp = recp->ptr[NL_TAG]; if ( fieldnlp->symbol != NIL ) { origin = roundup((int) origin,(long) align(fieldnlp->type)); fieldnlp->value[NL_OFFS] = origin; DEBUG_RECORDS(fprintf(stderr,"[rec_offsets] tag %s origin\n", fieldnlp->symbol, origin)); origin += lwidth(fieldnlp->type); } /* * find maximum alignment of records of variants */ for ( varntnlp = recp->ptr[NL_VARNT]; varntnlp != NIL; varntnlp = varntnlp -> chain ) { vrecnlp = varntnlp->ptr[NL_VTOREC]; DEBUG_RECORDS( fprintf(stderr, "[rec_offsets] maxing variant %d align_info %d\n", varntnlp->value[0], vrecnlp->align_info)); origin = roundup((int) origin,(long) vrecnlp->align_info); } DEBUG_RECORDS( fprintf(stderr, "[rec_offsets] origin of variants %d\n", origin)); /* * assign offsets to fields of records of the variants * keep maximum length of the current record. */ for ( varntnlp = recp->ptr[NL_VARNT]; varntnlp != NIL; varntnlp = varntnlp -> chain ) { vrecnlp = varntnlp->ptr[NL_VTOREC]; /* * assign offsets to fields of the variant. * recursive call on rec_offsets. */ rec_offsets(vrecnlp,origin); /* * extent of the record is the * maximum extent of all variants */ if ( vrecnlp->value[NL_OFFS] > recp->value[NL_OFFS] ) { recp->value[NL_OFFS] = vrecnlp->value[NL_OFFS]; } } } /* * roundup the size of the record to its alignment */ DEBUG_RECORDS( fprintf(stderr, "[rec_offsets] recp->value[NL_OFFS] %d ->align_info %d\n", recp->value[NL_OFFS], recp->align_info)); recp->value[NL_OFFS] = roundup(recp->value[NL_OFFS],(long) recp->align_info); }