ModulaTor logo, 7.8KB

The ModulaTor

Oberon-2 and Modula-2 Technical Publication

Ubaye's First Independent Modula-2 & Oberon-2 Journal! Nr. 77, Mar-1999

64 bit Virtual Regions with Modula-2 on OpenVMS Alpha

© (1999) by Günter Dotzel, ModulaWare.com

2nd edition, 30-Mar-1999

Introduction

This article is OpenVMS Alpha specific. It is a follow-up of the 32 bit to 64 bit program migration article.

64 bit memory management routines

OpenVMS Alpha V7 introduces new 64 bit memory handling routines. Apart from the 64 bit memory management routines LIB$GET_VM_64 and LIB$PUT_VM_64, the following procedures are declared in foreign definition module VMS$.DEF in ModulaWare MaX V5 Modula-2:

Some routines require that actual (VARiable) parameters are quadword aligned. To declare a variable which is quadword aligned, use a global or local variable. I tried SYS$CREATE_REGION_64 and SYS$DELETE_REGION_64 with unaligned actual return parameters under OpenVMS Alpha 7.1 and it worked correctly. No unaligned trap was delivered. To set a break point in case of an unaligned data access, use the OpenVMS run-time debugger command

> set break/unaligned

It seems that the alignment requirement was introduced to avoid unaligned access overhead and unalignment traps.

64 bit virtual region example

Here is an example program which uses 64 bit virtual regions together with the listing of its execution result:

 
MODULE use_regions;
 (*
   This Modula-2 program is an extended version of the 64 bit virtual
   memory region creation and deletion example which is contained
   as a C program in the OpenVMS Alpha V7.1 documentation.
   The intent is to demonstrate the use of the region services and how
   to allocate virtual address (VA) spaces within a region.
 
   The program creates a region in P2 space using the region creation
   service and then creates VA spaces within that region.
   The region is filled with a pattern in a loop over all VA spaces.
   In the first pass (first call of procedure A), VAs are created until
   no more space is available in the region.
   The resulting error status is displayed.
 
   A string variable is allocated using NEW in VLM (very large memory).
   In case of an error, the status values returned by the system services
   are formatted into an error messages with the system service SYS$GETMSG.
   The result string parameter is passed by a 64-bit string-descriptor.
 
   The second pass (second call of procedure A) creates and fills the
   exact amount of VAs which fit into the region and checks the pattern.
    
   To build and run this program type (assume @V5Modula.com 64):
 
   $ V5MaX/ISO=4/POINTER_SIZE=64/obj=.obj use_regions.mod
   $ LINK use_regions
   $ RUN use_regions
 
   GD/14-Feb-1999
 
   http://www.modulaware.com/
 *)
 IMPORT SYSTEM, STextIO, VMS$, VA$, SYIDefinitions, PSLDefinitions, UnivLOutput;
 FROM Storage IMPORT ALLOCATE, DEALLOCATE;
 
 PROCEDURE A(end: CARDINAL);
 TYPE CARDINAL=SYSTEM.UNSIGNED_64;
 CONST
   REGION_SIZE=4;
   BUFFER_SIZE=132;
 VAR
   msg: POINTER TO ARRAY [0 .. BUFFER_SIZE-1] OF CHAR;
   i,
   status,
   page_size,
   length_64,
   region_id_64,
   master_length_64: CARDINAL;
   master_va_64: SYSTEM.ADDRESS_64;
   
   rva: ARRAY [0..REGION_SIZE] OF
      RECORD
        return_va_64: SYSTEM.ADDRESS_64;
        return_length_64: CARDINAL;
      END;
       
 PROCEDURE print_message (code: CARDINAL; char: ARRAY OF CHAR);
 (* This routine takes the message code passed to the routine and then uses
    SYS$GETMSG to obtain the associated message text. That message is then
    copied to the standard output channel along with a user-supplied text string.
 *)
 VAR length: SYSTEM.SHORTWORD;
 BEGIN
   status:=VMS$.SYS$GETMSG (
         code,     (* Message Code          *)
         length,   (* Returned Length       *)
         msg^,     (* Message Descriptor    *)
         15,       (* Message Flags         *)
         0);       (* Optional Parameter    *)
   msg^[SYSTEM.CAST(CARDINAL,length)]:=CHR(0);
   STextIO.WriteLn;
   STextIO.WriteString("Call to "); STextIO.WriteString(char);
   STextIO.WriteString(" returned: "); STextIO.WriteString(msg^);
 END print_message;
 
 PROCEDURE memset(a: SYSTEM.ADDRESS_64; value, size: CARDINAL);
 (* sets "size" bytes from start address "a" to a byte "value".
    This routine works for any "size".
    Optimisations are possible if "a" is aligned and
    "size" is a multiple of 4 or 8 bytes.
 *)
 TYPE pt=POINTER TO SYSTEM.BYTE;
 VAR  p: pt; i: CARDINAL;
 BEGIN
   p:=SYSTEM.CAST(pt, a);
   FOR i:= 1 TO size DO
     p^:=SYSTEM.CAST(SYSTEM.BYTE, value);
     p :=SYSTEM.CAST(pt,SYSTEM.CAST(SYSTEM.ADDRESS_64,p)+1)
   END;
 END memset;
 
 PROCEDURE memchk(a: SYSTEM.ADDRESS_64; value, size: CARDINAL);
 (* compares "size" bytes from start address "a" to a byte "value".
 *)
 CONST maxsize=65536;
 TYPE pt=POINTER TO ARRAY [0..maxsize-1] OF SYSTEM.BYTE;
 VAR  p: pt; i: CARDINAL;
 BEGIN
   p:=SYSTEM.CAST(pt, a);
   FOR i:= 0 TO size-1 DO
     IF SYSTEM.CAST(CARDINAL, p^[i]) # value THEN HALT END;
   END;
 END memchk;
 
 PROCEDURE WriteAdr(s: ARRAY OF CHAR; from: SYSTEM.ADDRESS_64; size: CARDINAL);
 BEGIN
   STextIO.WriteLn;
   STextIO.WriteString(s);
   UnivLOutput.WriteHex(STextIO.WriteChar, from, 16);
   STextIO.WriteString(" - ");
   UnivLOutput.WriteHex(STextIO.WriteChar, from+size-1, 16);
 END WriteAdr;
 
 PROCEDURE get_page_size (): CARDINAL;
 (* This routine obtains the system page size using SYS$GETSYI. The return
    value is recorded in the global variable 'page_size'.                 
 *)
 VAR iList :ARRAY [0..2-1] OF VMS$.item_desc;
 BEGIN
   iList[0].buff_len := SYSTEM.CAST(SYSTEM.SHORTWORD, SIZE(page_size));
   iList[0].item_code := SYSTEM.CAST(SYSTEM.SHORTWORD, SYIDefinitions.SYI$_PAGE_SIZE);
   iList[0].buff_addr := SYSTEM.ADR(page_size);
   iList[0].len_addr := 0;
   iList[1].buff_len := SYSTEM.CAST(SYSTEM.SHORTWORD, 0);
   iList[1].item_code := iList[1].buff_len;
   RETURN VMS$.SYS$GETSYIW(0, 0, SYSTEM.NOP, iList, 0, 0, 0);
 END get_page_size;
 
 BEGIN
   status := get_page_size (); (* Get system page size, using SYS$GETSYI. *)
   IF ~ ODD (status) THEN RETURN END;
    
   NEW(msg); (* Get a VLM-buffer for the message descriptor. *)
   STextIO.WriteLn;
   STextIO.WriteString("Message Buffer Address =                   ");
   UnivLOutput.WriteHex(STextIO.WriteChar, SYSTEM.ADR(msg^), 16);
    
   (* Create a region in P2 space. *)
   length_64 := REGION_SIZE*page_size;
   status := VMS$.SYS$CREATE_REGION_64 (
     length_64,                   (* Size of Region to Create        *)
     VA$.VA$C_REGION_UCREATE_UOWN,(* Protection on Region            *)
     0,                           (* Allocate in Region to Higher VAs*)
     region_id_64,                (* Region ID                       *)
     master_va_64,                (* Starting VA in Region Created   *)
     master_length_64,            (* Size of Region Created          *)
     SYSTEM.NOP);
   IF ~ ODD(status) THEN
     print_message (status, "SYS$CREATE_REGION_64"); RETURN
   END;
   WriteAdr("SYS$CREATE_REGION_64 Created this Region:  ",
     master_va_64, master_length_64);
    
   (* Create virtual address spaces within the region. *)
   FOR i := 0 TO length_64 DIV page_size - end DO
     status := VMS$.SYS$EXPREG_64 (
       region_id_64,         (* Region to Create VAs In      *)
       page_size,            (* Number of Bytes to Create    *)
       PSLDefinitions.PSL$C_USER, (* Access Mode             *)
       0,                    (* Creation Flags               *)
       rva[i].return_va_64,         (* Starting VA in Range Created *)
       rva[i].return_length_64);    (* Number of Bytes Created      *)
     IF ~ ODD(status) THEN
       print_message (status, "SYS$EXPREG_64"); RETURN
     END;
     WriteAdr("Filling                                    ",
       rva[i].return_va_64, rva[i].return_length_64);
     STextIO.WriteString(" with ");
     UnivLOutput.WriteCard(STextIO.WriteChar, i, 3);
     memset (rva[i].return_va_64, i, page_size);
   END;
   
   (* check values in all VAs *)
   FOR i := 0 TO length_64 DIV page_size - end DO
     WriteAdr("Testing                                    ",
       rva[i].return_va_64, rva[i].return_length_64);
     STextIO.WriteString(" with ");
     UnivLOutput.WriteCard(STextIO.WriteChar, i, 3);
     memchk (rva[i].return_va_64, i, page_size);
   END;
 
   (* Return the virtual addresses created within the region,
      as well as the region itself. *)
   WriteAdr("Returning master region:                   ",
     master_va_64, master_length_64);
   
   status := VMS$.SYS$DELETE_REGION_64 (
     region_id_64,         (* Region to Delete *)
     PSLDefinitions.PSL$C_USER,(* Access Mode  *)
     master_va_64,         (* VA Deleted       *)
     master_length_64); (* Length Deleted      *)
   
   IF ODD(status) THEN
     WriteAdr("SYS$DELETE_REGION_64 Deleted VAs Between:  ",
       master_va_64, master_length_64);
   ELSE
     print_message (status, "SYS$DELETE_REGION_64"); RETURN
   END;
    
   DISPOSE(msg); (* Return message buffer. *)
 END A;
 BEGIN
 A(0); (* print_message results in "SYSTEM-W-REGISFULL, specified region is full" *)
 A(1); (* ok *)
 END use_regions.

$ run use_regions Message Buffer Address = 00000000800021D0 SYS$CREATE_REGION_64 Created this Region: FFFFFFFBFFFF8000 - FFFFFFFBFFFFFFFF Filling FFFFFFFBFFFF8000 - FFFFFFFBFFFF9FFF with 0 Filling FFFFFFFBFFFFA000 - FFFFFFFBFFFFBFFF with 1 Filling FFFFFFFBFFFFC000 - FFFFFFFBFFFFDFFF with 2 Filling FFFFFFFBFFFFE000 - FFFFFFFBFFFFFFFF with 3 Call to SYS$EXPREG_64 returned: %SYSTEM-W-REGISFULL, specified region is full Message Buffer Address = 0000000080002260 SYS$CREATE_REGION_64 Created this Region: FFFFFFFBFFFF0000 - FFFFFFFBFFFF7FFF Filling FFFFFFFBFFFF0000 - FFFFFFFBFFFF1FFF with 0 Filling FFFFFFFBFFFF2000 - FFFFFFFBFFFF3FFF with 1 Filling FFFFFFFBFFFF4000 - FFFFFFFBFFFF5FFF with 2 Filling FFFFFFFBFFFF6000 - FFFFFFFBFFFF7FFF with 3 Testing FFFFFFFBFFFF0000 - FFFFFFFBFFFF1FFF with 0 Testing FFFFFFFBFFFF2000 - FFFFFFFBFFFF3FFF with 1 Testing FFFFFFFBFFFF4000 - FFFFFFFBFFFF5FFF with 2 Testing FFFFFFFBFFFF6000 - FFFFFFFBFFFF7FFF with 3 Returning master region: FFFFFFFBFFFF0000 - FFFFFFFBFFFF7FFF

The example program use_regions can also be compiled with /pointersize=32, but what is the result when executing it?
 $ run use_regions

 Message Buffer Address =                   00000000000840E8
 SYS$CREATE_REGION_64 Created this Region:  FFFFFFFBFFFF8000 - FFFFFFFBFFFFFFFF
 Filling                                    FFFFFFFBFFFF8000 - FFFFFFFBFFFF9FFF with   0
 %SYSTEM-F-ACCVIO, access violation, reason mask=00, virtual address=FFFFFFFFFFFF8000,
 PC=00000000000302D8, PS=0000001B
 %TRACE-F-TRACEBACK, symbolic stack dump follows
   image    module    routine             line      rel PC           abs PC
  USE_REGIONS  USE_REGIONS  MEMSET          88 00000000000002D8 00000000000302D8
  USE_REGIONS  USE_REGIONS  A              171 0000000000000B00 0000000000030B00
  USE_REGIONS  USE_REGIONS  USE_REGIONS    204 0000000000000DDC 0000000000030DDC

Line number 88 is the statement beginning with "p^ :=" in procedure memset.

Discussion of the 32/64 bit pointer-size effects

The first difference is the message buffer, which is now allocated in the P0- instead of P2-space.
The pervasive procedures NEW / DISPOSE are mapped to the procedures ALLOCATE / DEALLOCATE by the Modula-2 compiler.
Module Storage uses LIB$GET_VM / LIB$PUT_VM for 32 bit memory management with /pointersize=32, instead of LIB$GET_VM_64 / LIB$PUT_VM_64 with /pointersize=64 under OpenVMS V7.0 or later.
This does not impose any functional problem, since the only reason to have the message buffer in VLM was to show that 64 bit string descriptors work.
The access violation is caused by a 64 bit address being truncated to 32 bit. 32 bit pointers can't access storage at 64 bit addresses. The (truncated) pointer is assigned using a SYSTEM.CAST in the statement preceding the FOR-statement in procedure memset.
The trunaction goes unnoticed, because CAST never performs a range check.

Due to clever design of the OpenVMS VLM 64 bit address space layout, it is guaranteed that you always get an access violation when using a truncated address. See OpenVMS VLM layout.

Independent from the /pointersize, the procedure memset would work correctly when replacing the type declaration

  pt = POINTER TO SYSTEM.BYTE(*=SYSTEM.LOC*);
by
  pt = SYSTEM.ADDRESS_64;
in its procedure header. This works, because SYSTEM.ADDRESS_64 is defined as a 64 bit pointer to SYSTEM.BYTE, in analogy with SYSTEM.ADDRESS_32 being a 32 bit pointer to SYSTEM.BYTE.

For educational reasons, procedure memchk shows another possibility how to access data at 64 bit addresses. Using the same 64 bit access method as shown for memset, procedure memset could also be made independent from /pointersize, if that was the goal.

So is MaX V5 a 32/64 bit compiler or a true 64 bit compiler? The answer is: Both! See also: What is a 64 bit compiler?

The method to replace a typed pointer by ADDRESS_64 as shown above would not work for other base-types than SYSTEM.BYTE.

So 64 bit pointers are needed. Because many existing programs do have 32 bit data type dependencies, MaX V5 still provides support for 32 pointers for backward compatibility, although MaX V5 is a true 64 bit compiler, only limited by restrictions of the OpenVMS linker.

The OpenVMS linker restricts the size of each program sections (global data, constant/literal, code) and the size of the stack to max. 2GB. But dynamic data, such as the Modula-2 heap, can exploit the full 64 bit address space (VLM).

In Modula-2, in order to get an array allocated in VLM, compile with /pointersize=64 and use

   VAR p: POINTER TO ARRAY [0..arrayMax] of anyType;
   ...
     NEW(p);
     ...
     p^[i]
instead of
  VAR p: ARRAY [0..arrayMax] of anyType;
   ...
     p[i]
(Same in Oberon-2 (A2O), only that the "^" to indicate pointer dereferencing is optional. This is one of the many advantages of Oberon-2 versus Modula-2. In the Alpha Oberon System (AOS) you not even need the pointer, because in AOS global variables are also allocated in VLM by the built-in dynamic module loader/linker.)

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 Günter Dotzel; he can be reached at mailto:[email deleted due to spam]


[ Home | Site_index | Legal | OpenVMS_compiler | Alpha_Oberon_System | ModulaTor | Bibliography | Oberon[-2]_links | Modula-2_links | General interesting book recommendations ]

Amazon.com [3KB] [Any browser]


Webdesign by www.otolo.com/webworx, 30-Mar-1999