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