HP OpenVMS Systems Documentation
Guide to Creating OpenVMS Modular Procedures
126.96.36.199 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.
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:
188.8.131.52 Combining Procedures
Often, non-resource-allocating procedures can be combined into a single procedure that returns all information explicitly in a single call.
184.108.40.206 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:
Then your procedure invokes the action routine as follows:
For information on writing user-action routines, see Section 3.1.4.
220.127.116.11 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
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.
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.
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.
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.)
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.
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.
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
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.
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.
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.
Figure 2-4 Use of Storage Types
18.104.22.168 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.
The following are several disadvantages to using static storage:
22.214.171.124 Summary of Storage Use by Language
Table 2-1 summarizes storage available to the programmer in various language procedures.
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.