ModulaTor logo, 7.8KB

The ModulaTor

Oberon-2 and Modula-2 Technical Publication

Ubaye's First Independent Modula-2 & Oberon-2 Journal! Nr. 72, Sep-1997


Object Model Implementation Notes for the OpenVMS Alpha Oberon System AOS_64 and Oberon-2 Compiler A2O

by Peter Pirkelbauer and Günter Dotzel, 26-Sep-1997, revised 09-Oct-1997

In module based programming languages one has to be sure that changes of a module invalidate all affected client modules. Usually a module key is used, which is calculated taking into account all exported components. Therefore all client modules were invalidated by any minor change like extending the interface. The so-called object model goes a step further. It calculates a finger-print per exported components. One drawback of this method is that the addresses of the module components are not fixed at compile time but at link time. This problem is solved by using fixup-chains for every imported and used component (procedures, variables and type-descriptors).

The extension for the A2O-Compiler is based on the implementation of the object model for the HP-compiler written by R. Crelier. A2O produces either stand-alone code suited for the OpenVMS linker or so-called Oberon load files. In order to keep the compiler maintainable, the changes to the common part of the code-generator should be as small as possible.

This is one reason why we do not use fix-up chains like the HP-compiler, where the linker fixed these offsets in the program section for imported variables, external procedures and base address of the imported type-descriptors.

The second reason is, that the Alpha processor needs 1 to 3 instructions to load a 32 bit signed integer literal, but there is no easy way to construct 64 bit values (addresses) in the program code. To avoid any 32 bit address restriction, we store the address of the object whose address is to be resolved at link-time at a fixed offset in the string section. In contrast to the HP compiler, this allows A2O to emit program code which is not changed by the linker.

The HP compiler always uses two (32 bit wide) instructions to load a 32 bit value to be used as 32 bit virtual address. The HP-compiler used the two instructions as placeholder to store information about the fix-up and the offset to next patch location for the linker/loader.

A2O uses 64 bit finger-print values, whereas the HP-Compiler uses 32 bit values.

Accessing imported procedures

Up to now procedures have been called via linkage-pairs consisting of the address of the pdr and the entry address. These linkage-pairs were filled at load-time, but the offsets of the underlying pdr were already known at compile time. This changed with the object model. Certainly the use of the linkage pair remains the same but the offset of the pdr in the pdr section of the imported module must be determined at link time.

Accessing imported variables and type-descriptors

Instead of using fix-up chains we allocate 8 bytes in the string section (AllocConstLiteral with value 0 of qinttyp), for each imported (used) variable and for the type-descriptor to store a 64 bit virtual address used at run-time. The linker/loader resolves the virtual address of each used variable and type-descriptor and stores it into the corresponding place in the string section. This mechanism increases the size needed at run-time by about 7 percent compared to the non object model system. Compared to the object model with fixup-chains the additional size needed increases by about 3 percent.

The number of instructions needed to access imported variables is unchanged with the object model, even if the base of the string section is already loaded into RS at procedure entry code, because the final address of the variable is stored in the string section. In the non-object model, the base address of the data section of the imported module loaded is loaded for each access and then the variable is accessed with the data offset.

To access a type-descriptor we need also one machine instruction less, because we do not need to read the pointer to the type-descriptor section of the imported module.

Fixup of imported components

This is done similar to the implementation in the HP-compiler. Every module disposes of an export list, which contains the name, the finger-print and the absolute address (in case of procedures the procedure-number) of the exported components. Of course name and finger-print are known at compile time, the loader adds the virtual address.

Every imported and used component has an entry in the use block of the Oberon code file. This entry contains the name, the finger-print and the offset into the string section (in the case of procedures the offset into the linkage-section), where the program code expects the absolute address. The linker looks-up the entries of the use block in the export section of the imported module, checks if the finger-prints do match and puts the resolved addresses into the string section at the corresponding locations reserved with AllocConstLiteral.

Contrary to the HP-compiler type-descriptors are unsorted, so the linker can not use binary search. The loader fills-in the linkage pairs of inherited methods in the type-descriptor. But currently this is done for imported and inherited methods only, because the back-end can not easily distinguish between own and inherited methods declared in the same module.

Symbol file

The file extension for symbol files in object model format is .SYO. Except for the additional data types (floating point and 64 bit integers, pointers, procedure types), the symbol file remained unchanged but we included the procedure names of hidden methods in the symbol file to assure a correct calculation of the structure finger-print.

Changes in the compiler

In the front-end, the code for OPT.Import and Export had to be nearly duplicated, because we wanted to keep the non-object model for the stand alone compiler. When the object model is used, OPL.GlbMod[0] points to the scope of the currently compiled module, whereas in the non-object model, index 0 points to the first imported module. Therefore the access to this variable in the front- and back-end now looks like this:


IF ~objmodel THEN DEC(index) END; do_something_with OPT.GlbMod[index]; 
Only small changes in the back-end were required. We introduced one new field called litOffset in both, OPT.StrDesc and in OPL.Item to store the string section offset of imported and used variables (type-descriptors). These fields are set by the procedures OPC.CompleteItem and OPC.FillCstRecordKey respectively. A few further changes were made in 4 places (Load/Store) in OPC, OPL and OPLS, where a memory access to imported variables is performed with Item.mode=extmode. The offset in the string section, where the linker has to resolve the references to imported variables is stored in the field ssecOfs in refUVar of the Use-Blk (see OLF file-format below).

The object model allows to extend record types by additional fields. In order not to invalidate clients, the code for NEW (pointerTo[ArrayOf]RecordType) in OPC now gets the size of an imported RecordType via the type-descriptor at run-time. If a client uses SIZE(RecordType), it needs to be recompiled, whenever the the size of the RecordType changes.

In addition to the existing procedures (1) PutCode to write OpenVMS object-files (.obj for 32 bit and .obj64 for 64 bit pointers) and (2) OutOberonCode to write AlphaOberon load-files (.OLF64), an additional procedure OutObjModel writes AlphaOberon object model load files (.OLF). Note that in AlphaOberon pointers always are 64 bit wide. This also works under OpenVMS V6.x, but then the P2 address space is not used. Both AlphaOberon load files formats, .OLF64 and .OLF are 64 bit only.

Changes in the run-time data-structure

Type-descriptors got a new field pvfprint to make the identification of anonymous types possible. The record type Modules.Moduledesc got a new field (pointer to export) for the above described export list. The fields modulekey and entries of this record type are not needed in the object model.

In the object model, we only support /pointersize=64, because the memory requirements for the Oberon System increases by only 30% (this comprises 64 bit pointers and LONGINT) in comparison with 32 bit.

Common to the HP-compiler is one side effect of the object model: an imported anonymous type is compatible with a local type of the same structure, because they get the same finger-print.

In object model, the newly generated symbol file is compared structurally to the old symbol file, whereas the non-object model compares the symbol files binary (disregarding the module key which is the date/time).

OLF-Fileformat


OLFFile = OLFTag Header ImpBlk ExpBlk CmdBlk PtrBlk LnkBlk CstBlk CodBlk UseBlk RefBlk DbgBlk.

OLFTag  = 0f9H VersionCode

VersionCode = 036H.

Header  = nofexp:2 nofcom:2 nofptr:2 nofdesc:2 nofimp nofOwnProc nofImpProc
          dataSize constSize codeSize
          refSize rtsFlags modulename.

ImpBlk  = 81H {name}

ExpBlk  = 82H {EConst | EType | EVar | EProc | EStruc | ETDesc} 0X.
EConst  = 1X name fprint.
EType   = 2X name fprint.
EVar    = 3X name fprint offset.
EProc   = 4X name fprint procnum.
EStruc  = 6X name pbfprint pvfprint.
ETDesc  = 8X (name | 0X pvfprint) tdescNr recSize (-1 | mnoAtBaseType 
          (name | 0X pvfprint)) nofmeth nofinhmeth nofptr
          {methno procno} -1 {ptroffset}*nofptr.

CmdBlk  = 83H {name procno}.

PtrBlk  = 84H {ptroffs}.

LnkBlk  = 86H {PDR}.
PDR     = flags:2 rsaoffset:2 reserved:1 fretem:1 sign:2 entry size:4 res:2
          entrylen:2 ireg:4 freg:4 handlerEntry
          handlerData.

CstBlk  = 87H {data:4}*constSize.

CodBlk  = 88H {data}.

UseBlk  = 89H {UConst | UType | UVar | UProc | UFProc | UPvStr | UPbStr | 
          UTDesc}. (*each module terminated with 0X*)
UConst  = 1X name fprint.
UType   = 2X name fprint.
UVar    = 3X name fprint ssecOfs.
UProc   = 4X name fprint procnum.
UFProc  = 5X name procnum.  ;foreign procedure
UPvStr  = 6X name pbfprint.
UPbStr  = 7X name pvfprint.
UTDesc  = 8X (name | 0X pvfprint) ssecOfs.

RefBlk  = 8aH {data}. (* see Ref.Mod *)

DbgBlk  = 8bH {data}.

The ModulaTor Forum

Recommended Reading

Hidden Order - How adaption builds complexity, by John Holland, 1995.
Quote from Murray Gell-Mann's comment on the book's cover: ... [John Holland] explains in a clear and entertaining manner important properties of composite complex adaptive systems, especially those based on computers. Along the way, he provides invaluable insights into economics, ecology, biological evolution, and thinking.

The Selfish Gene, by Richard Dawkins, 1976.
Quote from the cover notes: The world of the selfish gene is one of savage competition, ruthless exploitation, and deceit. Dawkins shows that the selfish gene is also a subtle gene. And he holds out the hope that our species -alone on earth- has the power to rebel against the designs of the selfish gene. This book is a call to arms. It is both manual and manifesto, and it grips like a thriller.


IMPRESSUM: The ModulaTor is an unrefereed journal. Technical papers are to be taken as working papers and personal rather than organizational statements. Items are printed at the discretion of the Editor based upon his judgement on the interest and relevancy to the readership. Letters, announcements, and other items of professional interest are selected on the same basis. Office of publication: The Editor of The ModulaTor is Guenter Dotzel; he can be reached by tel/fax: [removed due to abuse] or by mailto:[email deleted due to spam]


Home Site_index Contact Legal Buy_products OpenVMS_compiler Alpha_Oberon_System ModulaTor Bibliography Oberon[-2]_links Modula-2_links General interesting book recommendations
Amazon.com [3KB] [Any browser]
© (1998-1999) by modulaware.com
Webdesign by www.otolo.com/webworx, 07-Dec-1998