{

                          CHAPTER 12


                     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.

                                                                     }

procedure Validate(var DataSize: Scalar);

  begin
    if DataSize > MCMaxMemWord
    then
      begin
        SystemError(2);
        DataSize := 0
      end
  end { validate };

function WordsFor(Units, UnitsPerWord: Scalar): Scalar;

  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);

  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;

  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};

  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 := VariantValue.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 := PointerRepresentation;
          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;

  begin
    with Representation do
      APartWord := (WordSize = 1) and (BitSize < MCBitsPerWord)
  end { apartword };

function StructuredWord(Representation: TypeRepresentation): Boolean;

  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;

  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;

  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;

  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 PresetAnArray(ArrayType: TypEntry);
  forward;

procedure PostsetAnArray(ArrayType: TypEntry);
  forward;

procedure CheckAnArray(ArrayType: TypEntry);
  forward;

{

 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);
  forward;

procedure ReleaseACAP(CAPType: TypEntry);
  forward;

procedure CheckACAP(CAPType: TypEntry);
  forward;

{

 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 PresetARecord(RecordType: TypEntry);
  forward;

procedure PostsetARecord(RecordType: TypEntry);
  forward;

procedure CheckARecord(RecordType: TypEntry);
  forward;

{

 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 GenerateActivator(PartType: TypEntry);
  forward;

procedure GeneratePassivator(PartType: TypEntry);
  forward;

procedure GenerateSelector(PartType: TypEntry);
  forward;

{

 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 PresetAFile(FileType: TypEntry);
  forward;

procedure PostSetAFile(FileType: TypEntry);
  forward;

{

 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 ResetPresetBuffer;

  begin
    with PresetBuffer do
      begin
        Shift := 0;
        BitsFree := MCBitsPerWord;
        Buffer.WValue := 0
      end
  end { resetpresetbuffer };

procedure FlushBuffer(var PresetValue: MachineValue);

  begin
    with PresetValue do
      begin
        Multiple := 1;
        Defined := true;
        Magnitude := PresetBuffer.Buffer.WValue
      end;
    ResetPresetBuffer
  end { flushbuffer };

procedure PresetPartWord(PartSize: MCBitRange;
                         PresetValue: MachineValue);

  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 PresetStructuredWord(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
            ResetPresetBuffer;
            PresetStructuredWord(TheType, ThisRepresentation.BitSize);
            FlushBuffer(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 PresetAnArray(TheType);
            if PostsetByCall(TheType) then PostsetAnArray(TheType);
            if CheckedByCall(TheType) then CheckAnArray(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 PresetARecord(TheType);
            if PostsetByCall(TheType) then PostsetARecord(TheType);
            if CheckedByCall(TheType) then CheckARecord(TheType)
          end;
        Files :
          begin
            PresetAFile(TheType);
            if TheType = TextType
            then PostSetAFile(TheType)
            else
              with Representation do
                PostsetCode := TextType^.Representation.PostsetCode
          end;
        VariantPart :
          begin
            SelectorNeeded := false;
            if PresetByCall(TheType)
            then
              begin
                GenerateActivator(TheType);
                SelectorNeeded := true
              end;
            if PostsetByCall(TheType)
            then
              begin
                GeneratePassivator(TheType);
                SelectorNeeded := true
              end;
            if SelectorNeeded then GenerateSelector(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);

  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;

  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);

  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;

  begin
    with DefaultRepresentation do
      begin
        WordSize := 1;
        BitSize := MCBitsPerWord;
        Kind := ForOther;
        Min := 0;
        Max := 0;
        PresetCode := DefaultLabel;
        PostsetCode := DefaultLabel;
        CheckCode := DefaultLabel;
        Selector := DefaultOffset
      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);
    PointerRepresentation := DefaultRepresentation;
    with PointerRepresentation do
      begin
        Kind := ForPnter;
        if Checks in Requested then WordSize := 2
      end;
    SetCheckValue(PointerRepresentation)
  end { initrepresentations };