* Copyright (c) 1980 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
static char sccsid
[] = "@(#)rec.c 5.1 (Berkeley) 6/5/85";
* 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
* 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.
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.
rec_offsets(recp
, (long) 0);
* 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,
* 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.
register struct tnode
*r
; /* T_FLDLST */
register struct nl
*p
, *P0was
;
DEBUG_RECORDS(fprintf(stderr
,"[tyrec1] first=%d\n", first
));
p
= defnl((char *) 0, RECORD
, NLNIL
, 0);
fields(p
, r
->fldlst
.fix_list
);
variants(p
, r
->fldlst
.variant
);
* 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.
struct tnode
*r
; /* T_LISTPP */
register struct tnode
*fp
, *tp
, *ip
;
DEBUG_RECORDS(fprintf(stderr
,"[fields]\n"));
for (fp
= r
; fp
!= TR_NIL
; fp
= fp
->list_node
.next
) {
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
) {
p
->ptr
[NL_FIELDLIST
] = fieldnlp
;
fieldnlp
->ptr
[NL_FIELDLIST
] = fieldnlp
;
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
) {
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.
register struct tnode
*r
; /* T_TYVARPT */
register struct tnode
*vc
, *v
;
DEBUG_RECORDS(fprintf(stderr
,"[variants]\n"));
ct
= gtype(r
->varpt
.type_id
);
if ( ( ct
!= NLNIL
) && ( isnta( ct
, "bcsi" ) ) ) {
error("Tag fields cannot be %ss" , nameof( ct
) );
* 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
) {
vr
= tyrec1(v
->tyvarnt
.fld_list
, FALSE
);
"[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
;
line
= v
->tyvarnt
.line_no
;
for (v
= v
->tyvarnt
.const_list
; v
!= TR_NIL
;
(void) defvnt(p
, v
->list_node
.list
, vr
, ct
);
* Define a field in subrecord p of record P0
* with name s and type t.
DEBUG_RECORDS(fprintf(stderr
,"[deffld] s=<%s>\n", s
));
if (reclook(P0
, s
) != NIL
) {
error("%s is a duplicate field name in this record", s
);
* 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.
* and the alignment is propagated back.
fp
->align_info
= align(t
);
"[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
;
P0
->nl_flags
|= t
->nl_flags
& NFILES
;
p
->nl_flags
|= t
->nl_flags
& NFILES
;
* 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 tnode
*t
; /* CHAR_CONST or SIGN_CONST */
if (ct
!= NIL
&& incompat(con
.ctype
, ct
, t
)) {
cerror("Variant label type incompatible with selector type");
av
= defnl((char *) 0, VARNT
, ct
, 0);
av
->chain
= p
->ptr
[NL_VARNT
];
av
->range
[0] = con
.crval
;
* Check that the constant label value
* is unique among the labels in this variant.
for (vt
= p
->ptr
[NL_VARNT
]; vt
!= NIL
; vt
= vt
->chain
)
if (vt
->range
[0] == con
.crval
) {
error("Duplicate variant case label in record");
* See if the field name s is defined
* in the record p, returning a pointer
* to it namelist entry if it is.
if (p
== NIL
|| s
== NIL
)
for (p
= p
->chain
; p
!= NIL
; p
= p
->chain
)
* 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 */
origin
= roundup((int) offset
,(long) recp
->align_info
);
"[rec_offsets] offset=%d recp->align_info=%d origin=%d\n",
offset
, recp
->align_info
, origin
);
"[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
= fieldnlp
->ptr
[NL_FIELDLIST
] ) {
origin
= roundup((int) origin
,(long) align(fieldnlp
->type
));
fieldnlp
->value
[NL_OFFS
] = origin
;
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
= varntnlp
-> chain
) {
vrecnlp
= varntnlp
->ptr
[NL_VTOREC
];
"[rec_offsets] maxing variant %d align_info %d\n",
varntnlp
->value
[0], vrecnlp
->align_info
));
origin
= roundup((int) origin
,(long) vrecnlp
->align_info
);
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
= 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
"[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
);