xref: /original-bsd/usr.bin/pascal/src/rec.c (revision 0f30d223)
1 /* Copyright (c) 1979 Regents of the University of California */
2 
3 #ifndef lint
4 static char sccsid[] = "@(#)rec.c 2.1 02/08/84";
5 #endif
6 
7 #include "whoami.h"
8 #include "0.h"
9 #include "tree.h"
10 #include "opcode.h"
11 #include "align.h"
12 #include "tree_ty.h"
13 
14     /*
15      *	set this to TRUE with adb to turn on record alignment/offset debugging.
16      */
17 bool	debug_records = FALSE;
18 #define	DEBUG_RECORDS(x)	if (debug_records) { x ; } else
19 
20 /*
21  * Build a record namelist entry.
22  * Some of the processing here is somewhat involved.
23  * The basic structure we are building is as follows.
24  *
25  * Each record has a main RECORD entry,
26  * with an attached chain of fields as ->chain;
27  * these enclude all the fields in all the variants of this record.
28  * Fields are cons'ed to the front of the ->chain list as they are discovered.
29  * This is for reclook(), but not for sizing and aligning offsets.
30  *
31  * If there are variants to the record, NL_TAG points to the field which
32  * is the tag.  If its name is NIL, the tag field is unnamed, and is not
33  * allocated any space in the record.
34  * Attached to NL_VARNT is a chain of VARNT structures
35  * describing each of the variants.  These are further linked
36  * through ->chain.  Each VARNT has, in ->range[0] the value of
37  * the associated constant, and each points at a RECORD describing
38  * the subrecord through NL_VTOREC.  These pointers are not unique,
39  * more than one VARNT may reference the same RECORD.
40  *
41  * On the first pass, we traverse the parse tree and construct the namelist
42  * entries.  This pass fills in the alignment of each record (including
43  * subrecords (the alignment of a record is the maximum of the alignments
44  * of any of its fields).
45  * A second pass over the namelist entries fills in the offsets of each field
46  * based on the alignments required.  This second pass uses the NL_FIELDLIST
47  * chaining of fields, and the NL_TAG pointer and the NL_VARNT pointer to get
48  * to fields in the order in which they were declared.
49  * This second pass can not be folded into the first pass,
50  * as the starting offset of all variants is the same,
51  * so we must see all the variants (and especially must know their alignments)
52  * before assigning offsets.  With the alignments calculated (by the first
53  * pass) this can be done in one top down pass, max'ing over the alignment of
54  * variants before assigning offsets to any of them.
55  */
56 
57 /*
58  * P0 points to the outermost RECORD for name searches.
59  */
60 struct	nl *P0;
61 
62 struct nl *
63 tyrec(r, off)
64 	struct tnode *r;
65 	int	      off;
66 {
67 	struct nl	*recp;
68 
69 	DEBUG_RECORDS(fprintf(stderr,"[tyrec] off=%d\n", off));
70 	    /*
71 	     *	build namelist structure for the outermost record type.
72 	     *	then calculate offsets (starting at 0) of the fields
73 	     *	in this record and its variant subrecords.
74 	     */
75 	recp = tyrec1(r, TRUE);
76 	rec_offsets(recp, (long) 0);
77 	return recp;
78 }
79 
80 /*
81  * Define a record namelist entry.
82  * r is the tree for the record to be built.
83  * first is a boolean indicating whether this is an outermost record,
84  * for name lookups.
85  * p is the record we define here.
86  * P0was is a local which stacks the enclosing value of P0 in the stack frame,
87  * since tyrec1() is recursive.
88  */
89 struct nl *
90 tyrec1(r, first)
91 	register struct tnode *r;	/* T_FLDLST */
92 	bool first;
93 {
94 	register struct nl *p, *P0was;
95 
96 	DEBUG_RECORDS(fprintf(stderr,"[tyrec1] first=%d\n", first));
97 	p = defnl((char *) 0, RECORD, NLNIL, 0);
98 	P0was = P0;
99 	if (first)
100 		P0 = p;
101 #ifndef PI0
102 	p->align_info = A_MIN;
103 #endif
104 	if (r != TR_NIL) {
105 		fields(p, r->fldlst.fix_list);
106 		variants(p, r->fldlst.variant);
107 	}
108 	P0 = P0was;
109 	return (p);
110 }
111 
112 /*
113  * Define the fixed part fields for p.
114  * hang them, in order, from the record entry, through ->ptr[NL_FIELDLIST].
115  * the fieldlist is a tconc structure, and is manipulated
116  * just like newlist(), addlist(), fixlist() in the parser.
117  */
118 fields(p, r)
119 	struct nl *p;
120 	struct tnode *r;	/* T_LISTPP */
121 {
122 	register struct tnode	*fp, *tp, *ip;
123 	struct nl	*jp;
124 	struct nl	*fieldnlp;
125 
126 	DEBUG_RECORDS(fprintf(stderr,"[fields]\n"));
127 	for (fp = r; fp != TR_NIL; fp = fp->list_node.next) {
128 		tp = fp->list_node.list;
129 		if (tp == TR_NIL)
130 			continue;
131 		jp = gtype(tp->rfield.type);
132 		line = tp->rfield.line_no;
133 		for (ip = tp->rfield.id_list; ip != TR_NIL;
134 				    ip = ip->list_node.next) {
135 		    fieldnlp = deffld(p, (char *) ip->list_node.list, jp);
136 		    if ( p->ptr[NL_FIELDLIST] == NIL ) {
137 			    /* newlist */
138 			p->ptr[NL_FIELDLIST] = fieldnlp;
139 			fieldnlp->ptr[NL_FIELDLIST] = fieldnlp;
140 		    } else {
141 			    /* addlist */
142 			fieldnlp->ptr[NL_FIELDLIST] =
143 				p->ptr[NL_FIELDLIST]->ptr[NL_FIELDLIST];
144 			p->ptr[NL_FIELDLIST]->ptr[NL_FIELDLIST] = fieldnlp;
145 			p->ptr[NL_FIELDLIST] = fieldnlp;
146 		    }
147 		}
148 	}
149 	if ( p->ptr[NL_FIELDLIST] != NIL ) {
150 		/* fixlist */
151 	    fieldnlp = p->ptr[NL_FIELDLIST]->ptr[NL_FIELDLIST];
152 	    p->ptr[NL_FIELDLIST]->ptr[NL_FIELDLIST] = NIL;
153 	    p->ptr[NL_FIELDLIST] = fieldnlp;
154 	}
155 }
156 
157 /*
158  * Define the variants for RECORD p.
159  */
160 variants(p, r)
161 	struct nl *p;
162 	register struct tnode *r;	/* T_TYVARPT */
163 {
164 	register struct tnode *vc, *v;
165 	struct nl *vr;
166 	struct nl *ct;
167 
168 	DEBUG_RECORDS(fprintf(stderr,"[variants]\n"));
169 	if (r == TR_NIL)
170 		return;
171 	ct = gtype(r->varpt.type_id);
172 	if ( ( ct != NLNIL ) && ( isnta( ct , "bcsi" ) ) ) {
173 	    error("Tag fields cannot be %ss" , nameof( ct ) );
174 	}
175 	line = r->varpt.line_no;
176 	/*
177 	 * Want it even if r[2] is NIL so
178 	 * we check its type in "new" and "dispose"
179 	 * calls -- link it to NL_TAG.
180 	 */
181 	p->ptr[NL_TAG] = deffld(p, r->varpt.cptr, ct);
182 	for (vc = r->varpt.var_list; vc != TR_NIL; vc = vc->list_node.next) {
183 		v = vc->list_node.list;
184 		if (v == TR_NIL)
185 			continue;
186 		vr = tyrec1(v->tyvarnt.fld_list, FALSE);
187 #ifndef PI0
188 		DEBUG_RECORDS(
189 		    fprintf(stderr,
190 			"[variants] p->align_info %d vr->align_info %d\n",
191 			p->align_info, vr->align_info));
192 		if (vr->align_info > p->align_info) {
193 		    p->align_info = vr->align_info;
194 		}
195 #endif
196 		line = v->tyvarnt.line_no;
197 		for (v = v->tyvarnt.const_list; v != TR_NIL;
198 				v = v->list_node.next)
199 			(void) defvnt(p, v->list_node.list, vr, ct);
200 	}
201 }
202 
203 /*
204  * Define a field in subrecord p of record P0
205  * with name s and type t.
206  */
207 struct nl *
208 deffld(p, s, t)
209 	struct nl *p;
210 	register char *s;
211 	register struct nl *t;
212 {
213 	register struct nl *fp;
214 
215 	DEBUG_RECORDS(fprintf(stderr,"[deffld] s=<%s>\n", s));
216 	if (reclook(P0, s) != NIL) {
217 #ifndef PI1
218 		error("%s is a duplicate field name in this record", s);
219 #endif
220 		s = NIL;
221 	}
222 	    /*
223 	     *	enter the field with its type
224 	     */
225 	fp = enter(defnl(s, FIELD, t, 0));
226 	    /*
227 	     *	if no name, then this is an unnamed tag,
228 	     *	so don't link it into reclook()'s chain.
229 	     */
230 	if (s != NIL) {
231 		fp->chain = P0->chain;
232 		P0->chain = fp;
233 #ifndef PI0
234 		    /*
235 		     * and the alignment is propagated back.
236 		     */
237 		fp->align_info = align(t);
238 		DEBUG_RECORDS(
239 		    fprintf(stderr,
240 			"[deffld] fp->align_info %d p->align_info %d \n",
241 			fp->align_info, p->align_info));
242 		if (fp->align_info > p->align_info) {
243 		    p->align_info = fp->align_info;
244 		}
245 #endif
246 		if (t != NIL) {
247 			P0->nl_flags |= t->nl_flags & NFILES;
248 			p->nl_flags |= t->nl_flags & NFILES;
249 		}
250 	}
251 	return (fp);
252 }
253 
254 /*
255  * Define a variant from the constant tree of t
256  * in subrecord p of record P0 where the casetype
257  * is ct and the variant record to be associated is vr.
258  */
259 struct nl *
260 defvnt(p, t, vr, ct)
261 	struct nl *p, *vr;
262 	struct tnode *t;	/* CHAR_CONST or SIGN_CONST */
263 	register struct nl *ct;
264 {
265 	register struct nl *av;
266 
267 	gconst(t);
268 	if (ct != NIL && incompat(con.ctype, ct , t )) {
269 #ifndef PI1
270 		cerror("Variant label type incompatible with selector type");
271 #endif
272 		ct = NIL;
273 	}
274 	av = defnl((char *) 0, VARNT, ct, 0);
275 #ifndef PI1
276 	if (ct != NIL)
277 		uniqv(p);
278 #endif not PI1
279 	av->chain = p->ptr[NL_VARNT];
280 	p->ptr[NL_VARNT] = av;
281 	av->ptr[NL_VTOREC] = vr;
282 	av->range[0] = con.crval;
283 	return (av);
284 }
285 
286 #ifndef PI1
287 /*
288  * Check that the constant label value
289  * is unique among the labels in this variant.
290  */
291 uniqv(p)
292 	struct nl *p;
293 {
294 	register struct nl *vt;
295 
296 	for (vt = p->ptr[NL_VARNT]; vt != NIL; vt = vt->chain)
297 		if (vt->range[0] == con.crval) {
298 			error("Duplicate variant case label in record");
299 			return;
300 		}
301 }
302 #endif
303 
304 /*
305  * See if the field name s is defined
306  * in the record p, returning a pointer
307  * to it namelist entry if it is.
308  */
309 struct nl *
310 reclook(p, s)
311 	register struct nl *p;
312 	char *s;
313 {
314 
315 	if (p == NIL || s == NIL)
316 		return (NIL);
317 	for (p = p->chain; p != NIL; p = p->chain)
318 		if (p->symbol == s)
319 			return (p);
320 	return (NIL);
321 }
322 
323     /*
324      *	descend namelist entry for a record and assign offsets.
325      *	fields go at the next higher offset that suits their alignment.
326      *	all variants of a record start at the same offset, which is suitable
327      *	for the alignment of their worst aligned field.  thus the size of a
328      *	record is independent of whether or not it is a variant
329      *	(a desirable property).
330      *	records come to us in the namelist, where they have been annotated
331      *	with the maximum alignment their fields require.
332      *	the starting offset is passed to us, and is passed recursively for
333      *	variant records within records.
334      *	the final maximum size of each record is recorded in the namelist
335      *	in the value[NL_OFFS] field of the namelist for the record.
336      *
337      *	this is supposed to match the offsets used by the c compiler
338      *	so people can share records between modules in both languages.
339      */
340 rec_offsets(recp, offset)
341     struct nl	*recp;		/* pointer to the namelist record */
342     long	offset;		/* starting offset for this record/field */
343 {
344     long	origin;		/* offset of next field */
345     struct nl	*fieldnlp;	/* the current field */
346     struct nl	*varntnlp;	/* the current variant */
347     struct nl	*vrecnlp;	/* record for the current variant */
348 
349     if ( recp == NIL ) {
350 	return;
351     }
352     origin = roundup((int) offset,(long) recp->align_info);
353     if (origin != offset) {
354 	fprintf(stderr,
355 		"[rec_offsets] offset=%d recp->align_info=%d origin=%d\n",
356 		offset, recp->align_info, origin);
357 	panic("rec_offsets");
358     }
359     DEBUG_RECORDS(
360 	fprintf(stderr,
361 	    "[rec_offsets] offset %d recp->align %d origin %d\n",
362 	    offset, recp->align_info, origin));
363 	/*
364 	 *	fixed fields are forward linked though ->ptr[NL_FIELDLIST]
365 	 *	give them all suitable offsets.
366 	 */
367     for (   fieldnlp = recp->ptr[NL_FIELDLIST];
368 	    fieldnlp != NIL;
369 	    fieldnlp = fieldnlp->ptr[NL_FIELDLIST] ) {
370 	origin = roundup((int) origin,(long) align(fieldnlp->type));
371 	fieldnlp->value[NL_OFFS] = origin;
372 	DEBUG_RECORDS(
373 	    fprintf(stderr,"[rec_offsets] symbol %s origin %d\n",
374 		    fieldnlp->symbol, origin));
375 	origin += lwidth(fieldnlp->type);
376     }
377 	/*
378 	 *	this is the extent of the record, so far
379 	 */
380     recp->value[NL_OFFS] = origin;
381 	/*
382 	 *	if we have a tag field, we have variants to deal with
383 	 */
384     if ( recp->ptr[NL_TAG] ) {
385 	    /*
386 	     *	if tag field is unnamed, then don't allocate space for it.
387 	     */
388 	fieldnlp = recp->ptr[NL_TAG];
389 	if ( fieldnlp->symbol != NIL ) {
390 	    origin = roundup((int) origin,(long) align(fieldnlp->type));
391 	    fieldnlp->value[NL_OFFS] = origin;
392 	    DEBUG_RECORDS(fprintf(stderr,"[rec_offsets] tag %s origin\n",
393 				    fieldnlp->symbol, origin));
394 	    origin += lwidth(fieldnlp->type);
395 	}
396 	    /*
397 	     *	find maximum alignment of records of variants
398 	     */
399 	for (	varntnlp = recp->ptr[NL_VARNT];
400 		varntnlp != NIL;
401 		varntnlp = varntnlp -> chain ) {
402 	    vrecnlp = varntnlp->ptr[NL_VTOREC];
403 	    DEBUG_RECORDS(
404 		fprintf(stderr,
405 			"[rec_offsets] maxing variant %d align_info %d\n",
406 			varntnlp->value[0], vrecnlp->align_info));
407 	    origin = roundup((int) origin,(long) vrecnlp->align_info);
408 	}
409 	DEBUG_RECORDS(
410 	    fprintf(stderr, "[rec_offsets] origin of variants %d\n", origin));
411 	    /*
412 	     *	assign offsets to fields of records of the variants
413 	     *	keep maximum length of the current record.
414 	     */
415 	for (	varntnlp = recp->ptr[NL_VARNT];
416 		varntnlp != NIL;
417 		varntnlp = varntnlp -> chain ) {
418 	    vrecnlp = varntnlp->ptr[NL_VTOREC];
419 		/*
420 		 *	assign offsets to fields of the variant.
421 		 *	recursive call on rec_offsets.
422 		 */
423 	    rec_offsets(vrecnlp,origin);
424 		/*
425 		 *	extent of the record is the
426 		 *	maximum extent of all variants
427 		 */
428 	    if ( vrecnlp->value[NL_OFFS] > recp->value[NL_OFFS] ) {
429 		recp->value[NL_OFFS] = vrecnlp->value[NL_OFFS];
430 	    }
431 	}
432     }
433 	/*
434 	 *	roundup the size of the record to its alignment
435 	 */
436     DEBUG_RECORDS(
437 	fprintf(stderr,
438 		"[rec_offsets] recp->value[NL_OFFS] %d ->align_info %d\n",
439 		recp->value[NL_OFFS], recp->align_info));
440     recp->value[NL_OFFS] = roundup(recp->value[NL_OFFS],(long) recp->align_info);
441 }
442