© (1999) by Günter Dotzel, ModulaWare.com
2nd edition, 30-Mar-1999
This article is OpenVMS Alpha specific. It is a follow-up of the 32 bit to 64 bit program migration article.
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:
> set break/unaligned
It seems that the alignment requirement was introduced to avoid unaligned access overhead and unalignment traps.
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$ 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
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 ]
Webdesign by www.otolo.com/webworx,
30-Mar-1999