{ History ------- 18/10/85 - CheckUntaggedVariant modified to remove explicit reference to SelectorRep.CheckCode. InxReference and InxCAPReference modified to ensure char elements are treated as packed. (agh) 24/10/85 - Adjust FieldReference to preserve access record only when access-checks are required. Adjust BufferReference to make all such references indirect. Adjust IndirectReference to set Vulnerable flag only when HChecks enabled. (agh) 25/10/85 - Delete call to CheckCAPRange in InxCAPreference. (agh) 28/10/85 - Remove all references to ancillary check-list. Simplify access-list generation accordingly. (agh) 07/11/85 - Add function RefSize. (agh) 29/11/85 - Add Class = PnterRef to IndirectReference to distinguish pointer references from normal indirect references. 01/12/85 - Remove calls to CheckRange from InxReference. Index range-checking is now done from the analyser. (agh) 02/12/85 - Many changes to provide bit-level packing. (agh) 04/12/85 - Modify specs for InxCAPReference and InxWCAPReference. (agh) 05/12/85 - Treat calls to InxCAPReference as calls to InxReference if indexing the inner-most dimension and the low bound is fixed. (agh) ------------------------------------------------------------------------- 10/02/86 - modify Adjustment in InxReference & InxWReference } { MODULE 17 Variable Reference Generation 17.1 A Delayed Code Generation Strategy The analyser uses the code generation interface to signal the required program actions as a sequence of 'post fix' operations. With a simple stack machine as target, these calls can output a corresponding sequence of stack manipulation code directly. With this simple strategy, however, difficulties arise in the genera- tion of efficient code for optimised P-machines, and in the imple- mentation of run-time checks. In the Model Compiler, therefore, the interface calls made by the analyser construct tree representations of each variable reference and expression evaluation required, delaying actual code genera- tion until the context of the reference or evaluation is esta- blished. This delayed code generation is achieved using a stack, pointed to by the variable TopStackEntry, to simulate the evaluation-stack contents of a simple stack machine during execution of the opera- tions implied by the interface call sequence. Each operand value or variable reference on this stack is represented by a record of type StackNode. In the case of operand values this record is the root of a tree of stacknodes defining the computation required to obtain the operand value. Each internal node in the tree is of variant Operation, and denotes a value-producing operation whose operands are represented by appended subtrees. Each leaf node, which represents a primary operand, is of variant Reference, denoting a variable access, or of variant AConstant, denoting an explicit constant value. (The variants Address and Result are used to describe references and operands after evaluation code has been generated.) The generation of stack nodes of variants Operation and AConstant is described in Chapter 17. In this chapter the form and genera- tion of variable references is considered. 17.2 Stacked References A stack node representing a variable reference is the head of a list of such nodes (linked by the field AccessList), each representing an access step. The list holds the access steps in reverse order, i.e. the head record represents the last step in the access sequence. The tail record defines a base address within the run-time data stack from which all subsequent address calculations derive. Each access record then implies calculation from this base address of a word or part-word address, using some or all of the following: (a) indirection, as indicated by the field Indirect; (b) indexing by a list of index expression values, pointed to by the field Indices for word references or by the field Index for part-word references; (c) further word address adjustment by the field Adjustment. Multiple access records are generated when the necessary address calculation cannot be achieved by a single step defined as above. } program VariableReferences; #include "globals.x" #include "datareps.pf" #include "objvalues.pf" #include "expeval.pf" #include "semantics.pf" { 17.4 Evaluation Stack Maintenance The following procedures enable creation and maintenance of the evaluation stack itself: } procedure GetEntry(var Entry: StackEntry); visible; begin new(Entry); with Entry^ do begin NextNode := nil; RunError := 0; DataRep := DefaultRepresentation; StringBytes := 0 end end { getentry }; procedure FreeEntry(Entry: StackEntry); visible; var NextWord, ThisWord: WordEntry; begin with Entry^ do if Kind = AConstant then with TheConst do if Kind in [SetValue, StringValue] then begin if Kind = SetValue then NextWord := Setval else NextWord := Stringval; while NextWord <> nil do begin ThisWord := NextWord; NextWord := ThisWord^.Next; dispose(ThisWord) end end; dispose(Entry) end { freeentry }; procedure Push(Entry: StackEntry); visible; begin Entry^.NextNode := TopStackEntry; TopStackEntry := Entry end { push }; procedure Pop(var Entry: StackEntry); visible; begin Entry := TopStackEntry; TopStackEntry := Entry^.NextNode end { pop }; procedure InitStack; visible; begin TopStackEntry := nil end; { 17.5 Reference Creation and Manipulation The following utility procedures simplify creation of maintenance of (access lists of) reference records. } procedure GetReference(var NewEntry: StackEntry); visible; begin GetEntry(NewEntry); with NewEntry^ do begin Kind := Reference; BaseAddress := DftAddress; Adjustment := 0; Indirect := false; AccessList := nil; Class := VarRef; AccessKind := Words; ByteSize := 0; Indexed := false end end { getreference }; procedure ReferencePartWord(var Entry: StackEntry; Unit: AccessUnit); begin with Entry^ do begin AccessKind := Unit; if Unit = Bytes then ByteSize := 0 else begin BitSize := 0; BitOffset := 0; IndexedBitField := false end end end { refpartword }; procedure PushNewAccess(var Entry: StackEntry; Unit: AccessUnit); visible; var NewEntry: StackEntry; begin GetReference(NewEntry); with NewEntry^ do begin AccessList := Entry; if Unit <> Words then ReferencePartWord(NewEntry, Unit) end; Entry := NewEntry end { pushnewaccess }; procedure PreserveAccess(var Entry: StackEntry); visible; begin PushNewAccess(Entry, Entry^.AccessKind) end; procedure PartWordReference(var Entry: StackEntry; Unit: AccessUnit); begin with Entry^ do if Kind <> Reference then PushNewAccess(Entry, Unit) else if AccessKind = Words then if Indexed then PushNewAccess(Entry, Unit) else ReferencePartWord(Entry, Unit) end { partwordreference }; { 17.6 Simple References The interface procedure StackReference, which creates the base reference from which more complex access lists are built, is trivially implemented using the preceding utilities: } procedure StackReference(Indirct: Boolean; Location: RuntimeAddress); visible; var NewEntry: StackEntry; begin if CodeIsToBeGenerated then begin GetReference(NewEntry); with NewEntry^ do begin BaseAddress := Location; Indirect := Indirct end; Push(NewEntry) end end { stackreference }; { 17.7 Indexed References The IndexedReference operation, for indexing normal arrays, is carried out as follows: (a) the index value is checked for range, using the CheckRange procedure defined in Chapter 17. (b) if the index value is constant a direct adjustment of the array reference is made to address the element required, oth- erwise the index value is appended to the appropriate index list for delayed index code generation. The IndexedCAPReference operation for indexing conformant arrays is simpler in that no special treatment of constant indices is possible and the nature of the existing array reference is known. A corresponding procedure CheckCAPRange defined in Chapter 17 is used to check the index value range in this case. } procedure InxReference(PackedArray: Boolean; LowBound, HighBound: ObjectValue; Element: TypeRepresentation); visible; var Entry, ArrayIndex: StackEntry; ConstIndex: Boolean; ThisIndex: IndexEntry; NormalisedIndex: MCIntegerForm; ElsPerWord: 2..MCBitsPerWord; StoreUnit: AccessUnit; begin if CodeIsToBeGenerated then begin Pop(ArrayIndex); if ArrayIndex^.Kind = AConstant then begin ConstIndex := true; NormalisedIndex := ArrayIndex^.TheConst.Ival - LowBound.Ival; FreeEntry(ArrayIndex) end else begin ConstIndex := false; new(ThisIndex); with ThisIndex^ do begin TheIndex := ArrayIndex; Next := nil; WordBounds := false; CAPIndex := false; Lower := LowBound.Ival end end; Pop(Entry); StoreUnit := IndexUnit(PackedArray, Element); if StoreUnit <> Words then PartWordReference(Entry, StoreUnit); with Entry^ do case StoreUnit of Bits : begin ElsPerWord := MCBitsPerWord div Element.BitSize; BitSize := MCBitsPerWord div ElsPerWord; if ConstIndex then begin Adjustment := Adjustment + (NormalisedIndex div ElsPerWord) * MCBytesPerWord; BitOffset := BitOffset + (NormalisedIndex mod ElsPerWord) * BitSize end else begin IndexedBitField := true; ThisIndex^.Factor := ElsPerWord; FieldIndex := ThisIndex end end; Bytes : begin ByteSize := Element.ByteSize; if ConstIndex then Adjustment := Adjustment + NormalisedIndex * ByteSize else begin Adjustment := Adjustment - LowBound.Ival * ByteSize; ThisIndex^.Factor := ByteSize; if Indexed then ThisIndex^.Next := Indices else Indexed := true; Indices := ThisIndex end end; Words : begin if ConstIndex then Adjustment := Adjustment + NormalisedIndex * Element.WordSize * MCBytesPerWord else begin Adjustment := Adjustment - LowBound.Ival * Element.WordSize * MCBytesPerWord; ThisIndex^.Factor := Element.WordSize * MCBytesPerWord; if Indexed then ThisIndex^.Next := Indices else Indexed := true; Indices := ThisIndex end end end; Entry^.DataRep := Element; Push(Entry) end end { inxreference }; procedure InxWReference(PackedArray: Boolean; LowBound, HighBound: ObjectValue; Element: TypeRepresentation); visible; var Entry, ArrayIndex: StackEntry; ConstIndex: Boolean; ThisIndex: IndexEntry; NormalisedIndex: MCIntegerForm; ElsPerWord: 2..MCBitsPerWord; StoreUnit: AccessUnit; begin if CodeIsToBeGenerated then begin Pop(ArrayIndex); if ArrayIndex^.Kind = AConstant then begin ConstIndex := true; NormalisedIndex := int(ArrayIndex^.TheConst.Wval - LowBound.Wval); FreeEntry(ArrayIndex) end else begin ConstIndex := false; new(ThisIndex); with ThisIndex^ do begin TheIndex := ArrayIndex; Next := nil; WordBounds := true; CAPIndex := false; { recast(LowBound.Ival, AsWord) } Lower := LowBound.Ival end end; Pop(Entry); StoreUnit := IndexUnit(PackedArray, Element); if StoreUnit <> Words then PartWordReference(Entry, StoreUnit); with Entry^ do case StoreUnit of Bits : begin ElsPerWord := MCBitsPerWord div Element.BitSize; BitSize := MCBitsPerWord div ElsPerWord; if ConstIndex then begin Adjustment := Adjustment + (NormalisedIndex div ElsPerWord) * MCBytesPerWord; BitOffset := BitOffset + (NormalisedIndex mod ElsPerWord) * BitSize end else begin IndexedBitField := true; ThisIndex^.Factor := ElsPerWord; FieldIndex := ThisIndex end end; Bytes : begin ByteSize := Element.ByteSize; if ConstIndex then Adjustment := Adjustment + NormalisedIndex * ByteSize else begin ThisIndex^.Factor := ByteSize; if Indexed then ThisIndex^.Next := Indices else Indexed := true; Indices := ThisIndex end end; Words : begin if ConstIndex then Adjustment := Adjustment + NormalisedIndex * Element.WordSize * MCBytesPerWord else begin ThisIndex^.Factor := Element.WordSize * MCBytesPerWord; if Indexed then ThisIndex^.Next := Indices else Indexed := true; Indices := ThisIndex end end end; Entry^.DataRep := Element; Push(Entry) end end { inxwreference }; procedure InxCAPReference(PackedSchema, InnerMost: Boolean; LowBound, HighBound: CAPBound; Component: TypeRepresentation); visible; var Entry, ArrayIndex: StackEntry; ThisIndex: IndexEntry; ElsPerWord: 2..MCBitsPerWord; StoreUnit: AccessUnit; begin if CodeIsToBeGenerated and not (InnerMost and LowBound.Fixed) then begin Pop(ArrayIndex); new(ThisIndex); with ThisIndex^ do begin TheIndex := ArrayIndex; Next := nil; WordBounds := false; CAPIndex := true; BpAddress := LowBound.Address; LastLevel := InnerMost end; Pop(Entry); if InnerMost then StoreUnit := IndexUnit(PackedSchema, Component) else StoreUnit := Words; if StoreUnit <> Words then PartWordReference(Entry, StoreUnit); with Entry^ do case StoreUnit of Bits : begin ElsPerWord := MCBitsPerWord div Component.BitSize; BitSize := MCBitsPerWord div ElsPerWord; IndexedBitField := true; ThisIndex^.Factor := ElsPerWord; FieldIndex := ThisIndex end; Bytes : begin ByteSize := Component.ByteSize; ThisIndex^.Factor := ByteSize; if Indexed then ThisIndex^.Next := Indices else Indexed := true; Indices := ThisIndex end; Words : begin if InnerMost then ThisIndex^.Factor := Component.WordSize * MCBytesPerWord; if Indexed then ThisIndex^.Next := Indices else Indexed := true; Indices := ThisIndex end end; Push(Entry) end else InxReference (PackedSchema, LowBound.Value, ZeroValue, Component) end { indexedcapreference }; procedure InxCAPWReference(PackedSchema, InnerMost: Boolean; LowBound, HighBound: CAPBound; Component: TypeRepresentation); visible; var Entry, ArrayIndex: StackEntry; ThisIndex: IndexEntry; ElsPerWord: 2..MCBitsPerWord; StoreUnit: AccessUnit; begin if CodeIsToBeGenerated and not (InnerMost and LowBound.Fixed) then begin Pop(ArrayIndex); new(ThisIndex); with ThisIndex^ do begin TheIndex := ArrayIndex; Next := nil; WordBounds := true; CAPIndex := true; BpAddress := LowBound.Address; LastLevel := InnerMost end; Pop(Entry); if InnerMost then StoreUnit := IndexUnit(PackedSchema, Component) else StoreUnit := Words; if StoreUnit <> Words then PartWordReference(Entry, StoreUnit); with Entry^ do case StoreUnit of Bits : begin ElsPerWord := MCBitsPerWord div Component.BitSize; BitSize := MCBitsPerWord div ElsPerWord; IndexedBitField := true; ThisIndex^.Factor := ElsPerWord; FieldIndex := ThisIndex end; Bytes : begin ByteSize := Component.ByteSize; ThisIndex^.Factor := ByteSize; if Indexed then ThisIndex^.Next := Indices else Indexed := true; Indices := ThisIndex end; Words : begin if InnerMost then ThisIndex^.Factor := Component.WordSize * MCBytesPerWord; if Indexed then ThisIndex^.Next := Indices else Indexed := true; Indices := ThisIndex end end; Push(Entry) end else InxWReference (PackedSchema, LowBound.Value, ZeroValue, Component) end { indexedcapreference }; { 17.8 Field References A field reference involves adjustment of the record address by the word or part-word offset of the field. In practice this is com- plicated by the following: (a) if the field is a tagfield, the record access is vulnerable to a check arising from a subsequent tagfield assignment; (b) if the field is a variant field a cascade of variant checks may have been generated by a preceding call to VariantChecks. VariantChecks generates this cascade by recursive identification of the variant in which the field lies from the outermost variant level. At each level the action required is either to check or to reset the corresponding selector value, depending on whether the variant part has or has not an explicit tag field. Accordingly, the procedures CheckUntaggedVariant and CheckTaggedVariant append appropriate entries on the CheckList for the record-variable reference. } procedure CheckUntaggedVariant(SelectorRep: TypeRepresentation; VariantValue: ObjectValue); begin end; procedure CheckTaggedVariant(SelectorOffset: FieldOffset; VariantRep: TypeRepresentation); begin end ; procedure DoVariantChecks(VarPart: TypEntry; FieldId: IdEntry); visible; var LocalVariant: TypEntry; begin if (VarPart^.TagField <> FieldId) and (EChecks in Requested) then begin LocalVariant := VarPart^.FirstVariant; while not VariantField(LocalVariant, FieldId) do LocalVariant := LocalVariant^.NextVariant; with VarPart^ do if TagField = nil then CheckUntaggedVariant (Representation, LocalVariant^.VariantValue1) else CheckTaggedVariant (SelectorField^.Offset, LocalVariant^.Representation); with LocalVariant^ do if SubVarPart <> nil then if VariantField(SubVarPart, FieldId) then DoVariantChecks(SubVarPart, FieldId) end end { variantchecks }; procedure FieldReference(Field: FieldOffset; TagField: Boolean); visible; var Entry: StackEntry; begin if CodeIsToBeGenerated then begin Pop(Entry); if Entry^.Kind = Address then PreserveAccess(Entry); with Field do if FieldKind <> Words then PartWordReference(Entry, FieldKind); with Entry^ do begin case Field.FieldKind of Bits : begin BitSize := Field.BitSize; BitOffset := BitOffset + Field.BitOffset end; Bytes : ByteSize := Field.ByteSize; Words : end; Adjustment := Adjustment + Field.ByteOffset end; Push(Entry) end end { fieldreference }; { 17.9 Pointer and File Buffer References Pointer and file buffer references, except those to text files, both involve indirection. The procedure IndirectReference imple- ments those aspects of indirection common to pointer and file references. In the case of file buffer references the indirection is via a buffer pointer embedded within the file record, which points to a sliding buffer window within the associated transfer buffer itself. In the case of text files the file buffer is maintained as a char- acter field of the file record itself, so a BufferReference is equivalent to a FieldReference. The access created by a pointer reference, and the file record access involved in a buffer reference are always vulnerable to subsequent access checks. In particular, buffer-variable referen- ces are accompanied by an existence check as described in 17.2 above. } procedure IndirectReference(APointer: Boolean); visible; var Entry: StackEntry; begin if CodeIsToBeGenerated then begin Pop(Entry); with Entry^ do begin if not (Indirect or Indexed or (AccessKind <> Words) or (AccessList <> nil)) then with BaseAddress do ByteOffset := ByteOffset + Adjustment else PreserveAccess(Entry) end; with Entry^ do begin Adjustment := 0; Indirect := true; if APointer then Class := PnterRef end; Push(Entry) end end { indirectreference }; procedure PnterReference; visible; begin if CodeIsToBeGenerated then IndirectReference(true) end { pnterreference }; procedure BufferReference(PackedFile, TextFile: Boolean; Element: TypeRepresentation); visible; { The parameters PackedFile and Element are included for } { completeness but are not used in this implementation. } var Entry: StackEntry; begin if CodeIsToBeGenerated then begin if TextFile then begin Pop(Entry); Entry^.Class := FileRef; Push(Entry) end else begin FieldReference(BufferOffset, false); IndirectReference(false) end end end { bufferreference }; { 17.10 Reference Testing The following function enables subsequent chapters to test whether a reference is simple, i.e. is a word access involving no index- ing, pointer chasing, or access checking code. } function SimpleReference(Entry: StackEntry): Boolean; visible; begin with Entry^ do if Kind <> Reference then SimpleReference := false else if AccessKind = Bits then SimpleReference := false else SimpleReference := (AccessList = nil) and not Indexed and not (Indirect and (Class = PnterRef)) end { simplereference }; begin { end of module } end.