Document revision date: 30 March 2001
[Compaq] [Go to the documentation home page] [How to order documentation] [Help on this site] [How to contact us]
[OpenVMS documentation]

Guide to Creating OpenVMS Modular Procedures

Previous Contents Index Implicit Arguments Allocated by the Called Procedure

Implicit arguments allocated by the called procedure are kept in local static storage.

These implicit arguments are usually used to keep track of resources (using resource allocating procedures) and shorten the explicit argument list. However, the use of implicit inputs by non-resource-allocating procedures can lead to unexpected results. For example, assume that procedure A is to leave information for a companion procedure B. This would result in B having both explicit inputs (from its caller) and implicit inputs (from A's storage). Next, consider that a calling program calls A, then calls procedure X, and finally calls B. For the calling program to get correct results from B, it must know that X (and any procedure that X calls) did not make a call to A, because such a call would change the implicit inputs A leaves for B.

Because one of the objectives of modular programming is to permit procedures to be combined arbitrarily without needing to understand each other's internal workings, Compaq does not recommend using implicit arguments. The same problems can occur with any non-resource-allocating procedure that leaves results for itself as future implicit arguments.

2.2.3 How to Avoid Using Implicit Arguments

Procedures that do not allocate resources can be written in the following three ways to avoid the implicit argument problems described in Section 2.2.2: Combining Procedures

Often, non-resource-allocating procedures can be combined into a single procedure that returns all information explicitly in a single call.

Compare Example 2-1 with Example 2-2 to see the effects of combining procedures to avoid the use of implicit arguments.

Example 2-1 FORTRAN Program Showing the Improper Use of Implicit Arguments

! This program demonstrates a situation where 
! the input of a procedure depends on the output 
! of a previously called procedure. 
        REAL*4 X, Y, RESULT 
        X = 1 
        Y = 1 
! Call the procedure that writes into a common data area. 
        CALL SUM_SQUARES (X, Y) 
! Call the procedure that reads from the common data area. 
! Print the result obtained. 
        WRITE (6,10) X, Y, RESULT 
10      FORMAT(1X, 'SQRT(', F6.2, '**2 + ', F6.2, '**2) =',F6.2) 
! This procedure sums the squares of its two inputs and 
! places the result in a common area, for use by some 
! other procedure. 
        TEMP_RESULT = (A ** 2) + (B ** 2) 
! This procedure calculates the square root of whatever 
! number is in the common area. 
        C = SQRT (TEMP_RESULT) 
        END User-Action Routine

Another way to combine several procedures into one call is to let the calling program gain control at a critical point in your procedure's execution. For this to happen, your procedure must specify an action routine argument that is called during execution. Therefore, your procedure can execute twice, before and after the action routine, with no implicit inputs. The OPEN statements in BASIC, FORTRAN, and Pascal use this technique by permitting the user to supply a user-action routine.

To keep the calling program from having to provide implicit inputs for its action routine, your procedure should also provide another argument that is passed to the action routine. The calling program uses the following calling sequence to invoke your procedure:

CALL my-proc (... ,action-routine ,user-arg)

Then your procedure invokes the action routine as follows:

CALL action-routine (... ,user-arg)

For information on writing user-action routines, see Section 3.1.4.

Example 2-2 FORTRAN Program Combining Procedures to Avoid Implicit Arguments

! This procedure shows the subroutines called in 
! the previous example combined into a single subroutine 
! that eliminates the use of COMMON. 
        REAL*4 X, Y, RESULT 
        X = 1 
        Y = 1 
! Call the new procedure. 
        CALL DO_IT_ALL (X, Y, RESULT) 
        WRITE (6,10) X, Y, RESULT 
10      FORMAT (1X, 'SQRT (', F6.2, '**2 + ', F6.2, '**2) = ',F6.2) 
! This procedure calculates the square root of the sum of 
! the squares of its first two arguments, and returns the 
! result in the third argument.  It combines the functions 
! provided by the SUM_SQUARES and GET_SQRT 
! procedures and eliminates the use of COMMON. 
        C = SQRT ((A ** 2) + (B ** 2)) 
        END Designating Responsibility to the Calling Program

You can make the calling program responsible for retaining information from one procedure activation to another. There are three ways to do this:

Figure 2-3 shows a calling program that has responsibility for explicitly indicating the storage to be used by the called procedure.

Figure 2-3 Designating Storage Responsibility to the Caller

Calling Program Allocates Procedure Storage

This method causes the calling program to allocate all storage needed and pass the address of the storage as an explicit argument on each call.

For example, the library procedure MTH$RANDOM requires that the calling program allocate storage for the longword seed and pass its address on each call. MTH$RANDOM takes the seed as input and computes the next random number sequence from the current seed value. MTH$RANDOM returns a random number between 0 and 1 and updates the longword seed passed by the calling program. This ensures that the procedure will generate a different value on the next call.

The next two sections describe interface techniques that permit storage size to change without affecting the interface with the calling program.

Calling Program Passes Pointer

In this method, the calling program allocates only a longword pointer to the dynamic heap storage to be allocated by your procedure. It then passes the address of the longword as an explicit argument. The following two interface techniques can be used to indicate that storage is to be initialized:

Regardless of the method used to indicate storage allocation and initialization, you must also provide a way to indicate storage deallocation. You can do this by using either a separate argument or separate entry point.

For example, the procedure LIB$INIT_TIMER, which gets times and counts from the operating system, uses a single optional argument handle-adr to determine where these values are to be stored. The handle-adr argument is the address of a longword pointing to a block of storage that contains the values of times and counts:

LIB$FREE_TIMER deallocates the block of dynamic heap storage allocated by a previous call to LIB$INIT_TIMER. The handle-adr argument to LIB$FREE_TIMER is the address of a longword that points to a block of dynamic heap storage where times and counts have been stored. That storage is returned to free storage by calling LIB$FREE_VM.

Calling Program Passes a Processwide Identifier

In this method, the calling program passes a processwide identifying value to identify implicit results produced on previous calls, which will be implicit inputs on this call. Any calling program can use the processwide identifier. Examples include BASIC or FORTRAN logical unit numbers and OpenVMS system services I/O channel numbers.

Processwide identifiers are a resource. Modular programming techniques require that all resources allocated by a procedure be allocated by calling a resource-allocating procedure. This prevents conflicts because a single procedure can keep track of multiple allocations to more than one procedure or procedure activation. Therefore, if you use the method described in this section, you will also have to write a resource-allocating procedure to control the resource. If you write a resource-allocating procedure, it is recommended that you place it in an object module library so that other programmers can use it.

The library procedures LIB$GET_LUN and LIB$FREE_LUN allocate and deallocate FORTRAN and BASIC logical unit numbers outside the range normally specified in user programs, that is, outside the range 0 to 99.

2.2.4 Order of Arguments

Procedures in the RTL follow a consistent pattern for positioning arguments. You should follow the same guidelines. Group procedure arguments from left to right in the following order:

  1. Required input arguments (read access)
  2. Required input/output arguments (modify access)
  3. Required output arguments (write access)
  4. Optional input arguments (read access)
  5. Optional input/output arguments (modify access)
  6. Optional output arguments (write access)

Note that optional arguments follow required arguments. Therefore, when the calling program omits the optional arguments, the actual argument list passed to the procedure is shortened.

The called procedure accesses the required arguments from left to right, beginning with the first argument. The only exceptions are procedures that return a large function value of known size. In this case, the calling program uses the first argument to specify where the function value is to be stored, and the other arguments are shifted right one position. (For more information, refer to the OpenVMS Calling Standard.)

2.2.5 Using Optional Arguments

An optional argument is one that the calling program can omit. The calling program indicates the omission by passing argument list entries containing zero. For a trailing optional argument, the calling program can pass a shortened list or a zero argument list entry.

A zero argument list entry is simply a zero passed to the procedure by value. For example, if we call a procedure called GRA_CUBE and omit an optional argument C, the calling sequence from BASIC would be as follows:


In this call, 0 BY VALUE is the zero argument list entry.


Most OpenVMS system services, unlike the run-time library procedures, cannot accept a shortened argument list. Omitted arguments must always be indicated with a zero argument list entry. For arguments passed by value, there is no distinction between passing a zero value and passing a zero argument list entry.

2.3 JSB Entry Points (VAX Only)

On VAX systems, Compaq recommends that you do not use JSB2 entry points in procedures that will be contained in a procedure library. Procedures that can be invoked only by JSB instructions are not callable by high-level languages. If a procedure does use a JSB entry point, it must also provide an equivalent call entry point to maintain language independence. The call entry point must be provided because JSB instructions are only available in VAX MACRO and VAX BLISS-32.

If you provide a JSB entry point for your procedure, the name of the JSB entry point is the same as the name of the procedure, except that it ends in _Rn. The n indicates the highest register modified or used as an input argument.

For example, the JSB entry point of the run-time library procedure LIB$ANALYZE_SDESC is LIB$ANALYZE_SDESC_R2.


2 JSB is a MACRO instruction that means jump to subroutine.

2.4 Using System Resources

The system resources available to you are limited by your account quotas and by the amount of available resources on the system. Efficient use of system resources makes more resources available for all processes.

2.4.1 Choosing a Storage Type

There are three types of storage: stack, heap, and static. The three forms of storage differ in the method and duration of allocation, that is, how long that storage is in use. Stack Storage

A procedure dynamically allocates stack storage on the process stack at run time, as needed. To allocate stack storage, the procedure moves the stack pointer up by decreasing its value. Note that stack storage is not initialized to zero because the stack is created once and reused many times for subsequent stack frames.

The procedure deallocates stack storage by moving the stack pointer down (increasing its value) when that procedure returns control to the calling program. Stack storage exists only for the duration of the procedure activation that creates it. Heap Storage

Dynamic heap storage is allocated at run time from a processwide pool, as the procedure activation needs it and as the account quotas and virtual address space of your process permits.

To allocate heap storage, your procedure calls a system routine such as the Run-Time Library procedure LIB$GET_VM or the system service $EXPREG. The call to the system routine can be within the procedure itself, or you can use a general resource-allocating procedure to centralize your resource allocations.

Heap storage is deallocated---that is, returned to the processwide pool---by calling LIB$FREE_VM. The system service $CNTREG cannot be used to deallocate heap storage.

Figure 2-4 shows how the different types of storage are used.


The type of storage to be used can be determined by the duration or quantity of the storage. Any storage that is of long duration and unknown quantity (at compile time) should be heap storage. Storage of short duration (during the current invocation of the procedure) should be stack storage. Storage of long duration that is needed in only one instance should be static storage.

Figure 2-4 Use of Storage Types Static Storage

At link time, the linker collects storage in similar PSECTs into a single image section. The initial contents of this storage are specified in the source program. The OpenVMS operating system initializes any noninitialized static storage to zero. On calls to a procedure after initialization, the static storage has the same allocation and the contents left from the previous call. Avoiding Use of Static Storage

The following are several disadvantages to using static storage:

Example 2-3 Static Storage and AST Reentrancy

10      !+ 
        ! Program to demonstrate corruption 
        ! of static storage due to ASTs. 
        ! Enable CTRL/C AST handling. 
        ON ERROR GOTO 19000 
        X% = CTRLC 
        ! Increment the number and print the 
        ! current value.  When the number 
        ! reaches 1000, exit. 
        FOR CURRENT_NUMBER = 1% TO 1000% 
        GOTO 32767 
19000   !+ 
        ! Error-handling routine.  If this routine is 
        ! entered due to a CTRL/C 
        ! AST, corrupt CURRENT_NUMBER by setting it to -1. 
        IF ERR = 28 THEN CURRENT_NUMBER = -1% 
        RESUME 100 
32767   END Summary of Storage Use by Language

Table 2-1 summarizes storage available to the programmer in various language procedures.

Table 2-1 Summary of Storage Use by Language
Language Storage Type
  Static Stack Heap
Ada Constants and fixed-size objects contained in library packages Local subprogram and task variables Dynamically sized objects in library packages and objects created by allocators
BASIC All COMMON and MAP data storage Local variables Dynamic strings
  Most arrays Executable DIMENSION statement  
C Objects declared with external or static internal linkage Objects declared inside a function with "automatic" linkage By calling malloc, calloc, or realloc
COBOL All data storage Not applicable By calling LIB$GET_VM
DIBOL All RECORD, COMMON, and LITERAL data storage Not applicable Not applicable
VAX FORTRAN All data storage Not applicable By calling LIB$GET_VM 1
Assembly language Block storage Decrementing stack pointer By calling LIB$GET_VM
Pascal All program or module level storage PROCEDURE and FUNCTION local By calling NEW 2
RPG II All data storage Not applicable By calling LIB$GET_VM

1Storage for DEC Fortran for OpenVMS Alpha is the same as for VAX FORTRAN, except that stack storage is available as a compile time option for some variables.
2Although this is true most of the time, there are other rules that can also determine STATIC versus STACK allocation. For more information, see the Pascal user documentation.
3BASED is the storage class used to allocate heap storage in PL/I. The ALLOCATE statement does the actual allocation.

Previous Next Contents Index

  [Go to the documentation home page] [How to order documentation] [Help on this site] [How to contact us]  
  privacy and legal statement