{ MODULE 13 Data Representations Data representations are specified by the Representation field of the TypeRecord. This itself is a record of type TypeRepresenta- tion whose fields are used as follows: (1) WordSize is a word-measure of the storage requirement for the type. BitSize is an equivalent bit-measure for types that can be packed. (2) Kind provides an alternative data classification, relevant to various needs of the code-generator. (3) Min and Max define the minimum and maximum values of an ordi- nal type and are used to generate range-checking code. (4) CheckValue contains a representation of the 'undefined value' for scalar types and optimisable structured types. For other structured types, the entry points of internally compiled procedures performing type-specific initialisation, finalisa- tion, and value inspection are recorded in the fields PresetCode, PostSetCode, and CheckCode. (5) Selector holds the field-offset of the selector associated with a record variant-part, and is used in the generation of checking code for variant-field access and variant (re-) selection. The procedures in this chapter are grouped into those dealing with basic data representations, augmented representations and standard representations. The following utilities are used by the first of these groups. } program DataReps; #include "globals.x" #include "source.pf" #include "semantics.pf" #include "generator.pf" #include "objvalues.pf" procedure Validate(var DataSize: Scalar); begin if DataSize > MCMaxMemWord then begin SystemError(2); DataSize := 0 end end { validate }; function WordsFor(Units, UnitsPerWord: Scalar): Scalar; visible; var Words: Scalar; begin Words := Units div UnitsPerWord; if (Units mod UnitsPerWord) <> 0 then WordsFor := Words + 1 else WordsFor := Words end { wordsfor }; function BitsFor(Decimal: MCScalar): MCBitRange; var Bits: MCBitRange; begin Bits := 0; repeat Bits := Bits + 1; Decimal := Decimal div 2 until Decimal = 0; BitsFor := Bits end { bitsfor }; {function PowerOf2(Index: MCBitIndex): Scalar; var Decimal: Scalar; begin Decimal := 1; while Index > 0 do begin Decimal := 2 * Decimal; Index := Index - 1 end; PowerOf2 := Decimal end ; } { powerof2 } procedure SetCheckValue(var Representation: TypeRepresentation); visible; begin { with Representation, CheckValue do begin Multiple := WordSize; Defined := false; if (Kind = ForScalar) and (Checks in Requested) then if BitSize < MCBitsPerWord then begin if BitSize < MCBitsPerWord - 1 then begin Defined := true; Magnitude := PowerOf2(BitSize) end; BitSize := BitSize + 1 end end } end { setcheckvalue }; function Cardinality(OrdinalType: TypEntry): integer; visible; var TypeMin, TypeMax: ObjectValue; begin GetBounds(OrdinalType, TypeMin, TypeMax); Cardinality := Range(TypeMin, TypeMax) end { cardinality }; function VariantFiles(VariantList: TypEntry): Boolean; var AnyFiles: Boolean; ThisVariant: TypEntry; begin AnyFiles := false; ThisVariant := VariantList; while (ThisVariant <> nil) and (not AnyFiles) do if EmbeddedFile(ThisVariant) then AnyFiles := true else ThisVariant := ThisVariant^.NextVariant; VariantFiles := AnyFiles end { variantfiles }; { 12.1 Basic Representations The representation of each user-defined type is set by a call to the procedure SetRepresentationFor made once the type has been fully-defined. The settings for each of the representation fields depend upon a number of criteria which may be summarised as fol- lows: (1) Scalar Types The Min and Max fields are set directly from the bounding values associated with the type. A scalar whose Min is non-negative is potentially packable into a bits field whose width is set to the minimum required to store the value Max. If runtime checks are being generated the CheckValue field is set to the smallest power of 2 larger than Max, and BitSize is increased to accommodate this value. Thus the type primary = (violet,indigo,blue,green,yellow,orange,red) has a representation denoted by: WordSize = 1; BitSize = 4; CheckValue = 8; Min = 0; Max = 6. When runtime checks are suppressed, BitSize would be 3 and CheckValue would not be required. (2) Pointer Types If checks are being generated the wordsize is set to 2, and to 1 otherwise. All other fields are irrelevant. BitSize is set to MCBitsPerWord to ensure pointers are never packed. (3) Set Types For a given base-type whose bounds are BaseMin and BaseMax, sets are allocated as if their actual base-type was the subrange 0..BaseMax. Sets are implemented as bit-maps using (MCBitsPerWord-1) bits for member storage in each word. This leaves the most significant bit free to denote an 'undefined' set word. The special instructions of the P-machine require that a set must never be packed, and accordingly BitSize is set to MCBitsPerWord to achieve that effect. To handle set constructors where the base-type is integer, an actual basetype defined by the subrange 0..MCMaxSet is used. Base types whose minimum is nega- tive, or whose maximum exceeds MCMaxSet are always rejected. The set type: set of 'a'..'z' has a representation: WordSize = 4; BitSize = MCBitsPerWord; Min = 98; Max = 123 where the Min and Max are the ASCII display codes derived from the base-type and used to generate the set-assignment check. (4) Array Types Storage for unpacked arrays is calculated according to the number of elements and the size of each. For packed arrays the WordSize is reduced according to the number of elements that can be packed per word. Thus, given the subrange: range = 0..63 whose representation is: WordSize = 1; BitSize = 7; CheckValue = 64; Min = 0; Max = 63 the array type: packed array [1..8] of range occupies 2 words which are indexed by the P-machine as follows: word w w+1 ._____._____._____._____.._____._____._____._____. | | | | || | | | | element | 4 | 3 | 2 | 1 || 8 | 7 | 6 | 5 | !_____!_____!_____!_____!!_____!_____!_____!_____! bit 24 16 8 0 24 16 8 0 Notice the actual indexed element size is taken to be (MCBitsPer- Word div ElementsPerWord) which is 8 rather than 7. This has the dual effect of avoiding unused word portions and spreading each element into a more efficiently accessed machine byte without loss of packing density. Packed arrays may occupy partwords if the number of elements is less than ElementsPerWord. The representation of: packed array [1..2] of range is WordSize = 1; BitSize = 16 The criteria for augmenting the array representation with specific preset, postset and value-checking procedures are examined for each array type by a call to procedure AugmentRepresentationFor. A more detailed discussion of these criteria is deferred to Sec- tion 12.2. (5) Conformant Array Types By definition, the static store requirement of a conformant array type cannot be known at compile-time. However, a call is made to SetRepresentationFor to set the storage requirement for each bound-pair block in such a way that the block for the outer-most dimension effectively encloses blocks for the inner dimensions. Individually each block occupies 3 machine words arranged as fol- lows: |-------------| | lower bound | w |-------------| | upper bound | w+1 |-------------| | size | w+2 |-------------| For an unpacked schema, the third word holds the size of an actual array element. For a packed schema the third word holds the size of the actual array itself. The array schema: array [ l1..u1 : integer] of array [l2..u2 : integer] of real is represented by two adjacent bound-pair blocks utilised as fol- lows: |-------------| | l1 | w |-------------| | u1 | w+1 |-------------| | size1 | w+2 |-------------| | l2 | w+3 |-------------| | u2 | w+4 |-------------| | size2 | w+5 |-------------| At runtime size2=1 and size1 = u2-l2+1. The inner block has a representation WordSize of 3 and the outer block a WordSize of 6. (6) Record Types The aggregate representation for a record is computed from the sum of its fixed-part representation, and its variant part representa- tion. For an unpacked record fixed fields are assigned consecu- tive offsets from the record base. Thus: record i,j : integer; a : array [1..2] of real end has a representation WordSize = 4 with the fields assigned word offsets 0 for i, 1 for j, and 2 for a. Storage for variant records is overlaid so that the fraction of storage corresponding to the variant-part as a whole reduces to that required for the largest variant. In addition a control word is required if run-time checks are being generated. Thus: record fixed : integer ; case tag : boolean of false : (a,b : real) ; true : (c : array [1..3] of real) end has a maximum storage requirement of: 2 + max (2,3) = 5 words The need for a control word increases this to 6 words. The fields are assigned offsets to correspond with the following picture: .---------. .---------. | control | 0 | control | |---------| |---------| | fixed | 1 | fixed | |---------| |---------| | tag | 2 | tag | |---------| |---------| | a | 3 | c[1] | |---------| |---------| | b | 4 | c[2] | !---------! |---------| 5 | c[3] | !---------! tag = false tag = true The representation of a variant-part is accumulated by processing each distinct variant in turn. Recursively, the representation of each variant is derived from its local fixed part and its nested variant part. In addition, the CheckValue of each variant representation is set to the selector value associated with the variant. This is derived either from the case constant labelling the variant or, in the case of multiple labels, from an indepen- dent ordinal value that identifies each distinct variant. Finally the representation of the variant part is augmented with pro- cedures to select, preset and postset the individual variants. Storage for packed records is reduced by packing those fields whose representation BitSize is less than MCBitsPerWord. Fields are packed from the most significant end of the P-machine word in order of declaration. If a field cannot be packed within the remaining portion of a word, packing recommences at bit 0 of the following word, and the preceding field is 'spread' to include the unused portion. Thus the fields of packed record i : 0..1; a : packed array [1..2] of 0..100; j : 0..7; k : integer end ; are assigned as follows: bit used 16 14 2 offset .----------------------. | j | a |i| 0 |----------------------| | k | 1 !______________________! Note that j which needs only 4 bits has been spread into a field of 14 bits since k needs a full word. As with arrays, the representation may be augmented with type- specific code generated by the call to AugmentRepresentationFor. The use of the control word when checks have been requested is explained in Chapter 26 where this type-specific code is gen- erated. (7) File Types All file types are represented by a block of 5 words which holds information required to open the file, and a file 'descriptor' which identifies the associated physical file thereafter. Infor- mation is preset into this block by a type-specific preset pro- cedure generated by the call to AugmentRepresentationFor and allows a file buffer to be allocated from the heap when the file is opened. If the file type is the standard type text, a corresponding postset procedure is generated to close the file and discard the buffer. This postset procedure is shared for all other file types. } procedure AugmentRepresentationFor(TheType: TypEntry); forward; procedure SetRepresentationFor (TheType : TypEntry); visible; var ActualBase: TypEntry; NextConst: IdEntry; TheMax, TheMin: ObjectValue; BitsNeeded, ElsPerWord: MCBitRange; WordsNeeded, FirstOffset: Scalar; Members: MCScalar; Packing: Boolean; ThisRepresentation: TypeRepresentation; procedure AllocateLevel(FixedPart: IdEntry; VariantPart: TypEntry; ThisLevel, StartWord: Scalar; StartBits: MCBitRange; var MaxWordsize: Scalar; var MaxBitSize: MCBitRange); var WordFree, ThisVarWordSize: Scalar; BitsFree, ThisVarBitSize: MCBitRange; LastField, ThisField: IdEntry; LastVariant, ThisVariant: TypEntry; SelectorValue: MCIntegerForm; MultipleLabels: Boolean; procedure SpreadLastField; begin if LastField <> nil then begin with LastField^.Offset do if BitOffset = 0 then begin PartWord := false; WordSize := 1 end else BitSize := MCBitsPerWord - BitOffset; WordFree := WordFree + 1; BitsFree := MCBitsPerWord; LastField := nil end else if BitsFree <> MCBitsPerWord then begin WordFree := WordFree + 1; BitsFree := MCBitsPerWord end end { spreadlastfield }; procedure SpreadSelector; var SelectorMustSpread: Boolean; FirstActualField: IdEntry; begin with VariantPart^ do begin SelectorMustSpread := true; ThisVariant := FirstVariant; { with checks on, start each field-list on a } { word boundary in order to pack preset-values } if not (Checks in Requested) then while (ThisVariant <> nil) and SelectorMustSpread do with ThisVariant^ do begin if SubFixedPart <> nil then FirstActualField := SubFixedPart else if SubVarPart = nil then FirstActualField := nil else FirstActualField := SubVarPart^.TagField; if FirstActualField <> nil then if FirstActualField^.IdType <> Unknown then with FirstActualField^.IdType^. Representation do if WordSize = 1 then if BitSize <= BitsFree then SelectorMustSpread := false; ThisVariant := NextVariant end; if SelectorMustSpread then SpreadLastField end end { spreadselector }; procedure Allocate(FieldEntry: IdEntry); var WordsNeeded: Scalar; BitsNeeded: MCBitRange; begin with FieldEntry^ do begin WordsNeeded := IdType^.Representation.WordSize; if Packing and (WordsNeeded = 1) and (IdType^.Representation.BitSize < MCBitsPerWord) then begin BitsNeeded := IdType^.Representation.BitSize; if BitsNeeded > BitsFree then SpreadLastField; with Offset do begin WordOffset := WordFree; Level := ThisLevel; PartWord := true; BitSize := BitsNeeded; BitOffset := MCBitsPerWord - BitsFree end; BitsFree := BitsFree - BitsNeeded; if BitsFree = 0 then begin WordFree := WordFree + 1; BitsFree := MCBitsPerWord; LastField := nil end else LastField := FieldEntry end else begin if Packing then SpreadLastField; with Offset do begin WordOffset := WordFree; Level := ThisLevel; PartWord := false; WordSize := WordsNeeded end; WordFree := WordFree + WordsNeeded; LastField := nil end end end { allocate }; begin { allocatelevel } WordFree := StartWord; BitsFree := StartBits; LastField := nil; ThisField := FixedPart; while ThisField <> nil do begin Allocate(ThisField); ThisField := ThisField^.NextField end; if VariantPart <> nil then begin if ThisLevel > MaxLevels then SystemError(8); with VariantPart^ do begin if TagField <> nil then Allocate(TagField); if SelectorField <> TagField then Allocate(SelectorField); if Packing then SpreadSelector; if BitsFree = MCBitsPerWord then begin MaxWordsize := WordFree; MaxBitSize := MCBitsPerWord end else begin MaxWordsize := WordFree + 1; MaxBitSize := MCBitsPerWord - BitsFree end; Representation := SelectorField^.IdType^.Representation; with Representation do begin Kind := ForSelector; Selector := SelectorField^.Offset end; MultipleLabels := Cardinality(SelectorField^.IdType) < Cardinality(TagType); if MultipleLabels then SelectorValue := 0 end; LastVariant := Unknown; ThisVariant := VariantPart^.FirstVariant; while ThisVariant <> nil do with ThisVariant^ do begin if Distinct then begin AllocateLevel (SubFixedPart, SubVarPart, ThisLevel + 1, WordFree, BitsFree, ThisVarWordSize, ThisVarBitSize); with Representation do begin WordSize := ThisVarWordSize; if ThisVarWordSize = 1 then BitSize := ThisVarBitSize; Kind := ForVariant; { CheckValue.Defined := true; if MultipleLabels then begin CheckValue.Magnitude := SelectorValue; SelectorValue := SelectorValue + 1 end else CheckValue.Magnitude := VariantValue1.Ival } end; if ThisVarWordSize > MaxWordsize then begin MaxWordsize := ThisVarWordSize; MaxBitSize := ThisVarBitSize end else if ThisVarWordSize = MaxWordsize then if ThisVarBitSize > MaxBitSize then MaxBitSize := ThisVarBitSize; LastVariant := ThisVariant end else Representation := LastVariant^.Representation; ThisVariant := NextVariant end; AugmentRepresentationFor(VariantPart) end else begin if Packing and (WordFree > 0) then SpreadLastField; if BitsFree = MCBitsPerWord then begin MaxWordsize := WordFree; if MaxWordsize > 0 then MaxBitSize := MCBitsPerWord else MaxBitSize := 0 end else begin MaxWordsize := WordFree + 1; MaxBitSize := MCBitsPerWord - BitsFree end end end { allocatelevel }; begin if TheType <> Unknown then with TheType^ do case Form of Scalars : if ScalarKind = Declared then with Representation do begin Kind := ForScalar; GetBounds(TheType, TheMin, TheMax); BitSize := BitsFor(TheMax.Ival); Max := TheMax.Ival; ThisRepresentation := Representation; SetCheckValue(ThisRepresentation); Representation := ThisRepresentation end; SubRanges : with Representation do begin Kind := ForScalar; Min := TheType^.Min.Ival; Max := TheType^.Max.Ival; if Min >= 0 then BitSize := BitsFor(Max); ThisRepresentation := Representation; SetCheckValue(ThisRepresentation); Representation := ThisRepresentation end; Pointers : Representation := PtrRepresentation; Sets : if BaseType <> Unknown then with Representation do begin Kind := ForSet; if (BaseType = IntType) and (FormOfSet = Constructed) then Max := MCMaxSet - 1 else begin GetBounds(BaseType, TheMin, TheMax); if TheMin.Ival < 0 then SystemError(7) else Min := TheMin.Ival; if TheMax.Ival >= MCMaxSet then SystemError(6) else Max := TheMax.Ival end; WordSize := WordsFor(Max + 1, MCWordSetBits); ThisRepresentation := Representation; SetCheckValue(ThisRepresentation); Representation := ThisRepresentation end; Arrays : if (AelType <> Unknown) and (InxType <> Unknown) then begin Members := Cardinality(InxType); if PackedArray and (AelType^.Representation.WordSize = 1) then begin ElsPerWord := MCBitsPerWord div AelType^.Representation.BitSize; WordsNeeded := WordsFor(Members, ElsPerWord) end else WordsNeeded := Members * AelType^.Representation.WordSize; Validate(WordsNeeded); with Representation do begin Kind := ForArray; WordSize := WordsNeeded; if (WordsNeeded = 1) and PackedArray then BitSize := Members * (MCBitsPerWord div ElsPerWord); if not StringConstant then AugmentRepresentationFor(TheType) end end; CAPSchema : with Representation do begin Kind := ForCAP; if CompType^.Form = CAPSchema then WordSize := CompType^.Representation.WordSize + CAPBpSize else WordSize := CAPBpSize end; Records : begin Packing := PackedRecord; if VarPart = nil then FirstOffset := 0 else FirstOffset := ord ((Checks in Requested) or VariantFiles(VarPart^.FirstVariant)); AllocateLevel (FixedPart, VarPart, 0, FirstOffset, MCBitsPerWord, WordsNeeded, BitsNeeded); Validate(WordsNeeded); with Representation do begin if VarPart <> nil then Kind := ForVntRecord else Kind := ForARecord; WordSize := WordsNeeded; if WordsNeeded = 1 then BitSize := BitsNeeded; AugmentRepresentationFor(TheType) end end; Files : with Representation do begin Kind := ForFile; WordSize := FileSize; AugmentRepresentationFor(TheType) end end { case } end { setrepresentationfor }; { 12.2 Augmented Representations In principle every type requires procedures to correctly preset, postset and check variables declared with that type. In practice, the model P-machine possesses special P-codes for presetting and checking arrays of words, and "manual" procedures are only required if the type has a manual component, or a packed com- ponent. Furthermore, files or types containing embedded files are the only types requiring a postset action. Further optimisation is possible if a packed structure is con- tained entirely within a word. The representation of packed array [1..2] of 0..63 ; has WordSize = 1 and BitSize = 16. Clearly a variable of this type can be preset by storing the hexadecimal value 4040 to set each of the elements to the 'undefined-value' 64. A manual preset procedure is not required. Unfortunately no such optimization is possible for value-checking since an array of this type will be undefined if either (rather than both) of its elements is unde- fined. Consequently a manual checking procedure is required to check each array element in turn. The scheme for generating manual preset and postset procedures has been extended to incorporate the implementation of value con- formant arrays and variant records. The following utilities are used to determine whether a given type requires manual presetting, postsetting or checking. } function APartWord(Representation: TypeRepresentation): Boolean; visible; begin with Representation do APartWord := (WordSize = 1) and (BitSize < MCBitsPerWord) end { apartword }; function StructuredWord(Representation: TypeRepresentation): Boolean; visible; begin with Representation do StructuredWord := (WordSize = 1) and (BitSize <= MCBitsPerWord) and (Kind in [ForArray, ForARecord]) end { structuredpartword }; function PackedElements(LocallyPacked: Boolean; Element: TypEntry): Boolean; begin with Element^ do PackedElements := LocallyPacked and APartWord(Representation) and (Representation.BitSize <= MCBitsPerWord div 2) end { packedelements }; function PostsetByCall(TheType: TypEntry): Boolean; visible; begin with TheType^ do case Form of Scalars, SubRanges, Pointers, Sets : PostsetByCall := false; Arrays, Records : PostsetByCall := EmbeddedFile(TheType); CAPSchema : PostsetByCall := ValueSchema; Files : PostsetByCall := true; VariantPart : PostsetByCall := VariantFiles(FirstVariant); Variant : PostsetByCall := false end end { postsetbybcall }; function PresetByCall(TheType: TypEntry): Boolean; visible; var Manual: Boolean; Field: IdEntry; begin if not (Checks in Requested) then PresetByCall := PostsetByCall(TheType) else with TheType^ do case Form of Scalars, SubRanges, Pointers, Sets : PresetByCall := false; Arrays : PresetByCall := PresetByCall(AelType) or ((Representation.WordSize > 1) and (PackedElements(PackedArray, AelType) or StructuredWord(AelType^.Representation))); CAPSchema : PresetByCall := ValueSchema; Records : if VarPart <> nil then PresetByCall := true else if Representation.WordSize > 1 then begin Field := FixedPart; Manual := false; while (Field <> nil) and (not Manual) do with Field^ do if PresetByCall(IdType) or StructuredWord(IdType^.Representation) or Offset.PartWord then Manual := true else Field := NextField; PresetByCall := Manual end else PresetByCall := false; Files, VariantPart : PresetByCall := true end end { presetbycall }; function CheckedByCall(TheType: TypEntry): Boolean; visible; { var Manual: Boolean; Field: IdEntry; } begin { if not (Checks in Requested) or EmbeddedFile(TheType) then } CheckedByCall := false { else with TheType^ do case Form of Scalars, SubRanges, Sets, Pointers : CheckedByCall := false; Arrays : CheckedByCall := CheckedByCall(AelType) or PackedElements(PackedArray, AelType); CAPSchema : CheckedByCall := (not ValueSchema) and (CheckedByCall(CompType) or PackedElements(PackedSchema, CompType)); Records : if VarPart <> nil then CheckedByCall := true else begin Field := FixedPart; Manual := false; while (Field <> nil) and (not Manual) do with Field^ do if CheckedByCall(IdType) or Offset.PartWord then Manual := true else Field := NextField; CheckedByCall := Manual end; VariantPart, Variant : CheckedByCall := false end } { case } end { checkedbycall }; { The following procedures generate manual procedures for a given type. To hide low-level details of code-generation, their bodies have been deferred to Chapter 26. In each case, however, the pro- cedure is responsible for recording the entry point of the manual procedure in the representation field of the TypeRecord. } procedure PrsetArray(ArrayType: TypEntry); begin end; procedure PstsetArray(ArrayType: TypEntry); begin end; procedure ChkArray(ArrayType: TypEntry); begin end; { The procedures AcceptACAP and ReleaseACAP are used to generate procedures that manually create and dispose the auxiliary variable associated with a value conformant array-parameter. The procedure CheckACAP generates a procedure to check for undefined elements within a variable conformant array parameter. } procedure AcceptACAP(CAPType: TypEntry); begin end; procedure ReleaseACAP(CAPType: TypEntry); begin end; procedure CheckACAP(CAPType: TypEntry); begin end; { The procedures PresetARecord, PostsetARecord, and CheckARecord generate manual procedures for a record-type. PostSetARecord is called only if the record contains an embedded file. } procedure PrsetRec(RecordType: TypEntry); begin end; procedure PstsetRec(RecordType: TypEntry); begin end; procedure ChkRec(RecordType: TypEntry); begin end; { The procedures GenerateActivator, and GeneratePassivator generate the activator and passivator procedures associated with a variant part. Conceptually, for a given tagtype: tagtype = (one,two,three) the activator derived from: case tag : tagtype of one : (a : real) ; two : (i,j : integer) ; three : (f : text) presets the fields of the newly selected variant according to the new selector value. The passivator is required to postset (close) the file variable f in either of the tag-value transitions three -> one or three -> two. The third procedure GenerateSelector generates a manual variant selection procedure which is called whenever the tag value changes to check and implement the variant (re-)selection. Further details of these activator, passivator and selector procedures generated for a given variant part are to be found in Section 26.3. } procedure GenActivator(PartType: TypEntry); begin end; procedure GenPassivator(PartType: TypEntry); begin end; procedure GenSelector(PartType: TypEntry); begin end; { The procedure PresetaFile is called for every file-type to gen- erate a manual presetting (opening) procedure. The procedure PostsetaFile is used to generate a corresponding postsetting (closing) procedure. In practice it is called only for text files, and shared for all subsequent file types. } procedure PrsetFile(FileType: TypEntry); begin end; procedure PstSetFile(FileType: TypEntry); begin end; { The following procedures are responsible for augmenting the representation and setting the CheckValue for structured types as required. For single word types with embedded structure such as: packed array [1..2] of 0..63 ; the procedure StructuredCheckValue is called to derive the preset value (hex 4040) for the entire array. A variable of this type can then be preset efficiently by storing this value into the assigned storage word. } procedure RsetPrsetBuff; visible; begin { with PresetBuffer do begin Shift := 0; BitsFree := MCBitsPerWord; Buffer.WValue := 0 end } end { resetpresetbuffer }; procedure FlushBuff(var PresetValue: MachineValue); visible; begin { with PresetValue do begin Multiple := 1; Defined := true; Magnitude := PresetBuffer.Buffer.WValue end; RsetPrsetBuff } end { flushbuffer }; procedure PrsetPartWord(PartSize: MCBitRange; PresetValue: MachineValue); visible; var PartValue: MCWordForm; i: MCBitIndex; Bit: MCBit; begin { PartValue.WValue := PresetValue.Magnitude; with PresetBuffer do begin for i := 0 to PartSize - 1 do begin MCGetBit(PartValue, i, Bit); if Bit <> 0 then MCSetBit(Buffer, Shift + i) end; Shift := Shift + PartSize; BitsFree := BitsFree - PartSize end } end { presetpartword }; procedure PrsetStructWord(FieldType: TypEntry; FieldSize: MCBitRange); var PartSize: MCBitRange; Members, Count: Scalar; PartField: IdEntry; begin { with FieldType^ do case Form of Arrays : begin Members := Cardinality(InxType); PartSize := FieldSize div Members; for Count := 1 to Members do with AelType^.Representation do PresetPartWord(PartSize, CheckValue) end; Records : begin PartField := FixedPart; while PartField <> nil do with PartField^ do begin if Offset.PartWord then PartSize := Offset.BitSize else PartSize := MCBitsPerWord; with IdType^.Representation do PresetPartWord(PartSize, CheckValue); PartField := PartField^.NextField end end end } end { presetstructuredword }; procedure StructuredCheckValue(TheType: TypEntry); var ThisRepresentation: TypeRepresentation; PresetValue: MachineValue; begin { with TheType^ do begin ThisRepresentation := Representation; if StructuredWord(ThisRepresentation) then begin RsetPrsetBuff; PrsetStructWord(TheType, ThisRepresentation.BitSize); FlushBuff(PresetValue); ThisRepresentation.CheckValue := PresetValue end else SetCheckValue(ThisRepresentation); Representation := ThisRepresentation end } end { structuredcheckvalue }; procedure AugmentRepresentationFor {TheType : TypEntry}; var SelectorNeeded: Boolean; begin with TheType^ do case Form of Arrays : begin StructuredCheckValue(TheType); if PresetByCall(TheType) then PrsetArray(TheType); if PostsetByCall(TheType) then PstsetArray(TheType); if CheckedByCall(TheType) then ChkArray(TheType) end; CAPSchema : begin if PresetByCall(TheType) then AcceptACAP(TheType); if PostsetByCall(TheType) then ReleaseACAP(TheType); if CheckedByCall(TheType) then CheckACAP(TheType) end; Records : begin StructuredCheckValue(TheType); if PresetByCall(TheType) then PrsetRec(TheType); if PostsetByCall(TheType) then PstsetRec(TheType); if CheckedByCall(TheType) then ChkRec(TheType) end; Files : begin PrsetFile(TheType); if TheType = TextType then PstSetFile(TheType) else with Representation do PostsetCode := TextType^.Representation.PostsetCode end; VariantPart : begin SelectorNeeded := false; if PresetByCall(TheType) then begin GenActivator(TheType); SelectorNeeded := true end; if PostsetByCall(TheType) then begin GenPassivator(TheType); SelectorNeeded := true end; if SelectorNeeded then GenSelector(TheType) end end end { augmentrepresentationfor }; { The procedure AugmentSchema is used during block entry code gen- eration in Chapter 15 to trigger the generation of manual code for conformant array parameters. For efficiency reasons these pro- cedures must be local to the procedure containing the conformant array declaration and their generation is delayed until processing of the procedure statement part is about to begin. The function EnvelopeUsed is a diagnostic used retrospectively by the manual code generation procedures. } procedure AugmentSchema(TheType: TypEntry); visible; begin with TheType^ do begin if CompType^.Form = CAPSchema then AugmentSchema(CompType); if FirstIndex or (not ValueSchema) then AugmentRepresentationFor(TheType) end end { augmentschema }; function EnvelopeUsed(Representation: TypeRepresentation; Action: TypeActions): Boolean; visible; begin with Representation do case Kind of ForScalar, ForReal, ForString, ForPnter, ForSet, ForOther : EnvelopeUsed := false; ForArray, ForARecord, ForVntRecord, ForCAP, ForFile : case Action of Presets : EnvelopeUsed := PresetCode.EntryOffset <> 0; Postsets : EnvelopeUsed := PostsetCode.EntryOffset <> 0; ValueChecks : EnvelopeUsed := CheckCode.EntryOffset <> 0 end end end { envelopeused }; { Constant expression operands are assigned representations which must be updated to reflect the results of folded integer and set operations. The following procedure is called to re-compute the result representation following compile-time set-arithmetic: } procedure SetBaseLimits(var SetRep: TypeRepresentation; SetConstant: ObjectValue); visible; var SetWord: WordEntry; Temp: MCIntegerForm; procedure Adjust(var Limit: MCIntegerForm; SetWord: MCWordForm; Delta, BitIndex: integer); var BitValue: MCBit; begin repeat MCGetBit(SetWord, BitIndex, BitValue); if BitValue = 0 then Limit := Limit + Delta; BitIndex := BitIndex + Delta until BitValue <> 0 end; { adjust } begin with SetRep do begin Min := 0; Max := 0; SetWord := SetConstant.Setval; if SetWord <> nil then begin while SetWord^.Word.WValue = 0 do begin Min := Min + MCWordSetBits; SetWord := SetWord^.Next end; Max := Min + MCMaxSetBit; Temp := Min; Adjust(Temp, SetWord^.Word, +1, 0); Min := Temp; while SetWord^.Next <> nil do begin Max := Max + MCWordSetBits; SetWord := SetWord^.Next end; Temp := Max; Adjust(Temp, SetWord^.Word, -1, MCMaxSetBit); Max := Temp end end end { setbaselimits }; { 12.3 Standard Representations The representations for the standard types integer, real, Boolean and char are held in global variables IntegerRepresentation, Real- Representation, BooleanRepresentation and CharRepresentation and initialised by the procedure InitRepresentations. With the excep- tion of reals, the scalar standard types occupy a single P-machine word. Reals are allocated one or more words, depending on the value of MCRealsize. In addition InitRepresentations composes the value of DefaultRepresentation which is used as the base of all other representations. The variable PointerRepresentation is used for all pointer types. } procedure InitRepresentations; visible; begin with DefaultRepresentation do begin WordSize := 1; BitSize := MCBitsPerWord; Kind := ForOther; Min := 0; Max := 0; PresetCode := DftLabel; PostsetCode := DftLabel; CheckCode := DftLabel; Selector := DftOffset end; SetCheckValue(DefaultRepresentation); { set the standard representations } EmptyRepresentation := DefaultRepresentation; with EmptyRepresentation do begin Kind := ForSet; WordSize := 0; BitSize := 0 end; SetCheckValue(EmptyRepresentation); RealRepresentation := DefaultRepresentation; with RealRepresentation do begin Kind := ForReal; WordSize := MCRealSize end; SetCheckValue(RealRepresentation); BooleanRepresentation := DefaultRepresentation; with BooleanRepresentation do begin Kind := ForScalar; BitSize := 1; Max := 1 end; SetCheckValue(BooleanRepresentation); CharRepresentation := DefaultRepresentation; with CharRepresentation do begin Kind := ForScalar; BitSize := MCBitsPerByte; Max := MCMaxChar; { if Checks in Requested then with CheckValue do begin Defined := true; Magnitude := MCUndefinedChar end } end; IntegerRepresentation := DefaultRepresentation; with IntegerRepresentation do begin Kind := ForScalar; Max := MCMaxint; Min := -MCMaxint end; SetCheckValue(IntegerRepresentation); PtrRepresentation := DefaultRepresentation; with PtrRepresentation do begin Kind := ForPnter; if Checks in Requested then WordSize := 2 end; SetCheckValue(PtrRepresentation) end { initrepresentations }; begin {end of module} end.