[an error occurred while processing this directive]

HP OpenVMS Systems Documentation

Content starts here

HP Pascal for OpenVMS
User Manual


Previous Contents Index

8.4 Fault and Trap Handling (OpenVMS VAX systems)

If a VAX processor detects an error while executing a machine instruction, it can take one of two actions. The first action, called a fault, preserves the contents of registers and memory in a consistent state so that the instruction can be restarted. The second action, called a trap, completes the instruction, but with a predefined result. For example, if an integer overflow trap occurs, the result is the correct low-order part of the true value.

The action taken when an exception occurs depends on the type of exception. For example, faults occur for access violations and for detection of a floating reserved operand. Traps occur for integer overflow and for integer divide-by-zero exceptions. However, when a floating overflow, floating underflow, or floating divide-by-zero exception occurs, the action taken depends on the type of VAX processor executing the instruction. The original VAX-11/780 processor traps when these errors occur and stores a floating reserved operand in the destination. All other VAX processors fault on these exceptions, which allows the error to be corrected and the instruction restarted.

If your program is written to handle floating traps but runs on a VAX processor that generates faults, execution may continue incorrectly. For example, if a condition handler causes execution to continue after a floating trap, a reserved operand is stored and the next instruction is executed. However, the same handler used on a processor that generates faults causes an infinite loop of faults because it restarts the erroneous instruction. Therefore, you should write floating-point exception handlers that take the appropriate actions for both faults and traps.

Separate sets of condition values are signaled by the processor for faults and traps. Exceptions and their condition code names are as follows:

Exception Fault Trap
Floating overflow SS$_FLTOVF_F SS$_FLTOVF
Floating underflow SS$_FLTUND_F SS$_FLTUND
Floating divide-by-zero SS$_FLTDIV_F SS$_FLTDIV

To convert faults to traps, you can use the run-time library LIB$SIM_TRAP procedure either as a condition handler or as a called routine from a user-written handler. When LIB$SIM_TRAP recognizes a floating fault, it simulates the instruction completion as if a floating trap had occurred.

8.5 Examples of Condition Handlers

The examples in this section inherit the $UNWIND system service routine from SYS$LIBRARY:STARLET.PEN. They also assume the following declaration has been made:


[INHERIT( 'SYS$LIBRARY:STARLET', 'SYS$LIBRARY:PASCAL$LIB_ROUTINES' )]
PROGRAM Error_Handling( INPUT, OUTPUT);

TYPE
   Sig_Args  = ARRAY[0..100] OF INTEGER;        { Signal parameters }
   Mech_Args = ARRAY[0..(SIZE(CHF2$TYPE)-4)DIV 4] OF [UNSAFE] INTEGER;
                                                { Mechanism parameters }

Example 1


[ASYNCHRONOUS] FUNCTION Handler_0
   (VAR SA : Sig_Args;
    VAR MA : Mech_Args)    : [UNSAFE] INTEGER;

   BEGIN
   IF LIB$MATCH_COND (SA[1],   condition-name   ,...) <> 0
   THEN
      BEGIN
          .
          .                       { do something appropriate }
          .
      Handler_0 := SS$_CONTINUE;  { condition handled,
                                    propagate no further }
      END
   ELSE
      Handler_0 := SS$_RESIGNAL;  { propagate condition
                                    status to other handlers }
   END;

This example shows a simple condition handler. The handler identifies the condition being signaled as one that it is prepared to handle and then takes appropriate action. Note that for all unidentified condition statuses, the handler resignals. A handler must always follow this behavior.

Example 2


[ASYNCHRONOUS] FUNCTION Handler_1
   (VAR SA : Sig_Args;
    VAR MA : Mech_Args)   : [UNSAFE] INTEGER;

   BEGIN
   IF SA[1] = SS$_UNWIND
   THEN
      BEGIN
          .
          .                          { cleanup }
          .
      END;
   Handler_1 := SS$_RESIGNAL;
   END;

When writing a handler, remember that it can be activated with a condition of SS$_UNWIND, signifying that the establisher's stack frame is about to be unwound. If the establisher has special cleanup to perform, such as freeing dynamic memory, closing files, or releasing locks, the handler should check for the SS$_UNWIND condition status. If there is no cleanup, the required action of resignaling all unidentified conditions results in the correct behavior. On return from a handler activated with SS$_UNWIND, the stack frame of the routine that established the handler is deleted (unwound).

Example 3


[ASYNCHRONOUS] FUNCTION Handler_2
   (VAR SA : Sig_Args;
    VAR MA : Mech_Args)    : [UNSAFE] INTEGER;

   BEGIN
   IF LIB$MATCH_COND (SA[1],   condition-name   ,...) <> 0
   THEN
      BEGIN
          .
          .                       { cleanup }
          .

      MA[3] := expression;        { establish function result seen by caller
                                    (MA[11] on OpenVMS I64/OpenVMS Alpha)}
     $UNWIND;                     { unwind to caller of establisher }

     END;
   Handler_2 := SS$_RESIGNAL;
   END;

A handler can perform a default unwind to force return to the caller of its establisher. If the establisher is a function whose result is expected in R0 or R0 and R1, the handler must establish the return value by modifying the appropriate positions of the mechanism array (the locations of the return R0 and R1 values). If the establisher is a function whose result is returned by the extra-parameter method, the handler must establish the condition value by assignment to the function identifier. In this case, you must observe two additional restrictions:

  • The handler must be nested within the function
  • The function result must be declared VOLATILE

Example 4


[ASYNCHRONOUS] FUNCTION Handler_3
   (VAR SA : Sig_Args;
    VAR MA : Mech_Args)    : [UNSAFE] INTEGER;

   BEGIN
   IF LIB$MATCH_COND (SA[1],   condition-name   ,...) <> 0
   THEN
      BEGIN
          .
          .                   { cleanup }
          .
      MA[3] := expression;    { establish function result seen by caller }
      $UNWIND (MA[2]);        { unwind to establisher[4] for
                                OpenVMS I64/OpenVMS Alpha }
      END;
   Handler_3 := SS$_RESIGNAL;
   END;

A handler can also force return to its establisher immediately following the point of call. In this case, you should make sure that the handler understands whether the currently uncompleted call was a function call (in which case a returned value is expected) or a procedure call. If the uncompleted call is a function call that will return a value in R0 or R0 and R1, then the handler can modify the mechanism array to supply a value. If, however, the uncompleted call is a function call that will return a value using the extra-parameter mechanism, then there is no way for the handler to supply a value.

Example 5


[ASYNCHRONOUS] FUNCTION Handler_4
   (VAR SA : Sig_Args;
    VAR MA : Mech_Args)    : [UNSAFE] INTEGER;

   BEGIN
   IF LIB$MATCH_COND (SA[1],   condition-name   ,...) <> 0
   THEN
      GOTO 99;
   Handler_4 := SS$_RESIGNAL;
   END;

A handler can force control to resume at an arbitrary label in its scope. Note that this reference is to a label in an enclosing block, because a GOTO to a local label will remain within the handler. In accordance with the HP OpenVMS Calling Standard, HP Pascal implements references to labels in enclosing blocks by signaling SS$_UNWIND in all stack frames that must be deleted.

Example 6


FUNCTION EXP_With_Status
   (X : REAL;
    VAR Status : INTEGER )    : REAL;

   FUNCTION MTH$EXP
      (A : REAL) : REAL;
      EXTERNAL;

   [ASYNCHRONOUS] FUNCTION Math_Error
      (VAR SA : Sig_Args;
       VAR MA : Mech_Args)       : [UNSAFE] INTEGER;

      BEGIN   { Math_Error }
      IF LIB$MATCH_COND (SA[1], MTH$_FLOOVEMAT, MTH$_FLOUNDMAT) <> 0
      THEN
         BEGIN
            IF ODD( Status )                { record condition status
            THEN                              if no previous error }

            Status := SA[1]::Cond_Status;   { condition handled,
            Math_Error := SS$_CONTINUE;       propagate no further }
         END
       ELSE
          Math_Error := SS$_RESIGNAL;       { propagate condition status
                                              to other handlers }
       END;

   BEGIN   { EXP_With_Status }
   STATUS := SS$_SUCCESS;
   ESTABLISH (Math_Error);
   EXP_With_Status := MTH$EXP (X);
   END;

This example shows a handler that records the condition status if a floating overflow or underflow error is detected during the execution of the mathematical function MTH$EXP.

Example 7



[INHERIT('SYS$LIBRARY:STARLET')]
PROGRAM Use_A_Handler(INPUT,OUTPUT);

TYPE
   Sigarr  = ARRAY [0..9] OF INTEGER;
   Mecharr = ARRAY [0..(Size(CHF2$TYPE)-4)DIV 4)] OF INTEGER;
VAR
   F1,F2 : REAL;
[ASYNCHRONOUS] FUNCTION My_Handler
   (VAR Sigargs  : Sigarr;
    VAR Mechargs : Mecharr) : INTEGER;

   VAR
      Outfile : TEXT;

   [ASYNCHRONOUS] FUNCTION LIB$FIXUP_FLT
      (VAR Sigargs  : Sigarr;
       VAR Mechargs : Mecharr;
          New_Opnd : REAL := %IMMED 0) : INTEGER;
      EXTERNAL;

   [ASYNCHRONOUS] FUNCTION LIB$SIM_TRAP
      (VAR Sigargs  : Sigarr;
       VAR Mechargs : Mecharr) : INTEGER;
      EXTERNAL;
      BEGIN
      OPEN(Outfile,'TT:');
      REWRITE(Outfile);

      { Handle various conditions }
      CASE Sigargs[1] OF

      { Convert floating faults to traps }
      SS$_FLTDIV_F, SS$_FLTOVF_F :
         LIB$SIM_TRAP(Sigargs,Mechargs);

      { Handle the floating divide by zero trap }
      SS$_FLTDIV :
         BEGIN
            WRITELN(Outfile,'Floating divide by zero');
            My_Handler := SS$_CONTINUE;
         END;

      { Handle the floating overflow trap }
      SS$_FLTOVF :
         BEGIN
            WRITELN(Outfile,'Floating overflow');
            My_Handler := SS$_CONTINUE;
         END;

      { Handle taking the square root }
      MTH$_SQUROONEG :
          BEGIN
             WRITELN(Outfile,'Square root of a negative number');
             My_Handler := SS$_CONTINUE;
          END;

      { Handle the reserved operand left by SQRT }
      SS$_ROPRAND :
          BEGIN
             WRITELN(Outfile,'Reserved floating operand');
             LIB$FIXUP_FLT(Sigargs,Mechargs);
             My_Handler := SS$_CONTINUE;
          END;

      OTHERWISE
         BEGIN
            WRITELN(Outfile,'Condition occurred, ',HEX(Sigargs[1]));
            My_Handler := SS$_RESIGNAL;
         END;

      END;

      CLOSE(Outfile);

      END;
BEGIN
ESTABLISH(My_Handler);
F1 := 0.0;
F2 := 1E38;

{ Generate exception conditions }
F1 := F2 / 0.0;
F1 := F2 * f2;
F1 := SQRT(-1.0);
END.


Chapter 9
Migrating from OpenVMS VAX to OpenVMS I64 or OpenVMS Alpha

This chapter provides information on issues that affect programs being moved from OpenVMS VAX systems to OpenVMS I64 or OpenVMS Alpha systems:

9.1 Sharing Environment Files Across Platforms

HP Pascal inherits environment files created from a compiler for the same target platform. For example, you cannot inherit environment files generated by VAX Pascal with the HP Pascal compiler for OpenVMS I64 or OpenVMS Alpha systems.

9.2 Default Size for Enumerated Types and Booleans

The default size for enumerations and Booleans in unpacked structures is longword on OpenVMS I64 and OpenVMS Alpha systems. On OpenVMS VAX systems, the default size is byte for Booleans and small enumerations or words for larger enumerations.

If you need the OpenVMS VAX behavior on OpenVMS I64 or OpenVMS Alpha systems, you can use one of the following:

  • /ENUMERATION_SIZE=BYTE qualifier
  • [ENUMERATION_SIZE(BYTE)] attribute
  • Individual [BYTE] or [WORD] attributes on the affected fields or components

The default for OpenVMS VAX compilers is /ENUMERATION_SIZE=BYTE, for compatibility.

9.3 Default Data Layout for Unpacked Arrays and Records

On OpenVMS I64 and OpenVMS Alpha systems, the default data layout is "natural" alignment, where record fields and array components are aligned on boundaries based on their size (for example, INTEGERs on longword boundaries, INTEGER64s on quadword boundaries).

On OpenVMS VAX systems, the default alignment rule is to allocate such fields on the next byte boundary. If you need the OpenVMS VAX behavior on OpenVMS I64 or OpenVMS Alpha systems, you can use the /ALIGN=VAX qualifier or the [ALIGN(VAX)] attribute.

9.4 Overflow Checking

When overflow checking is enabled on OpenVMS I64 and OpenVMS Alpha systems, the INT built-in signals a run-time error if its actual parameter cannot be represented as an INTEGER32 value.

If you have a large unsigned value that you wish to convert to a negative integer, you must use a typecast to perform the operation.

9.5 Bound Procedure Values

On OpenVMS VAX systems, a Bound Procedure Value is a 2-longword data structure holding the address of the entry point and a frame-pointer to define the nested environment. HP Pascal expects one of these 2-longword structures for PROCEDURE or FUNCTION parameters.

A routine not written in Pascal needs different code depending on whether it will receive a Bound Procedure Value or a simple routine address. When passing routines to %IMMED formal routine parameters, HP Pascal passes the address of the entry point; otherwise, it passes the address of a Bound Procedure Value.

On OpenVMS I64 and OpenVMS Alpha systems, a Bound Procedure Value is a special type of procedure descriptor that invokes a hidden jacket routine that in turn initializes the frame-pointer and calls the real routine. HP Pascal expects a procedure descriptor for PROCEDURE or FUNCTION parameters.

A routine not written in Pascal does not require difference code for Bound Procedure Values. When passing routines to %IMMED formal routine parameters, (or asking for the IADDRESS of a routine) HP Pascal passes the address of a procedure descriptor as if the %IMMED was not present. There is no direct way in HP Pascal to obtain the actual code address of a routine because it is not generally useful without the associated procedure descriptor.

9.6 Different Descriptor Classes for Conformant Array Parameters

HP Pascal uses the "by descriptor" mechanism to pass conformant parameters from one routine to another. For conformant array parameters, HP Pascal uses a CLASS_A descriptor on OpenVMS VAX systems and a CLASS_NCA descriptor on OpenVMS I64 and OpenVMS Alpha systems. The CLASS_NCA descriptors generate more efficient code when accessing array components and are able to describe arrays with alignment holes or padding (more common on Itanium and Alpha systems).

If you have a foreign routine that constructs CLASS_A descriptors for Pascal, you need to examine the code to see if changes are necessary:

  • For certain actual parameters, the CLASS_A and CLASS_NCA descriptors are identical except for the DSC$B_CLASS field (which HP Pascal does not examine).
  • For other parameters, you will either have to generate a CLASS_NCA descriptor or you can add an explicit CLASS_A attribute to the formal conformant parameter in the Pascal routine.

9.7 Data Layout and Conversion

On OpenVMS I64 and OpenVMS Alpha systems (and to a lesser extent OpenVMS VAX systems), the layout of data can severely impact performance. The Itanium and Alpha architecture and the OpenVMS I64 and OpenVMS Alpha systems have strong preferences about data alignment and size.

The HP Pascal compiler has several features to enable you to write Pascal code that will get the best performance on the target system.

The remainder of this section describes the different types of record layouts, HP Pascal features that support them, how to get the best performance with your data structures, and how to convert existing code for better performance.

This section focuses on records, but arrays also have similar properties. In almost all cases, where record fields are discussed, you can substitute array components.

9.7.1 Natural Alignment, VAX Alignment, and Enumeration Sizes

The compiler has the ability to lay out records in two ways:

  • OpenVMS VAX alignment
    Fields and components less than or equal to 32 bits are allocated on the next available bit; otherwise they are allocated on the next available byte.
  • Natural alignment where an object is aligned based on its size
    Essentially fields and components are allocated on the next naturally aligned address for their data type. For example:
    • 8-bit character strings should start on byte boundaries
    • 16-bit integers should start at addresses that are a multiple of 2 bytes (word alignment)
    • 32-bit integers and single-precision real numbers should start at addresses that are a multiple of 4 bytes (longword alignment)
    • 64-bit integers and double-precision real numbers should start at addresses that are a multiple of 8 bytes (quadword alignment)

For aggregates such as arrays and records, the data type to be considered for purposes of alignment is not the aggregate itself, but rather the elements of which the aggregate is composed. Varying 8-bit character strings must, for example, start at addresses that are a multiple of 2 bytes (word alignment) because of the 16-bit count at the beginning of the string. For records, the size is rounded up to a multiple of their natural alignment (a record with natural alignment of longword has a size that is a multiple of longwords, for example).

The OpenVMS VAX and naturally aligned record formats are fully documented in the HP OpenVMS Calling Standard.

The size as well as the alignment of record fields and array components can affect performance. On OpenVMS I64 and OpenVMS Alpha systems, HP Pascal uses larger allocation for unpacked Booleans and enumeration types to help performance, as shown in Table 9-1.

Table 9-1 Unpacked Sizes of Fields and Components
Datatype Unpacked Size on VAX Unpacked Size on I64/Alpha
Boolean 1 byte 4 bytes
Enumerated types 1 or 2 bytes 4 bytes

For compatibility reasons, the size of all data types in PACKED records and arrays are the same for both VAX and natural alignment formats.

9.7.2 HP Pascal Features Affecting Data Alignment and Size

HP Pascal has the following DCL qualifiers:

  • /ALIGN=keyword, where keyword is either NATURAL1 or VAX
  • /ENUMERATION_SIZE=keyword, where keyword is either BYTE or LONG

The /ALIGN qualifier option controls the default record format used by the compiler. The /ENUMERATION_SIZE qualifier option controls whether the compiler allocates Boolean and enumeration types as longwords or as 1 or 2 bytes.

On OpenVMS VAX systems, the default alignment format is VAX and the default enumeration size is BYTE. On OpenVMS I64 and OpenVMS Alpha systems, the default alignment format is NATURAL and the default enumeration size is LONG.

A corresponding pair of attributes can be used at the PROGRAM/MODULE level and on VAR and TYPE sections to specify the desired alignment format and enumeration size:

  • ALIGN(keyword), where keyword is either NATURAL or VAX
  • ENUMERATION_SIZE(keyword), where keyword is either BYTE or LONG

By using these attributes at the MODULE level, you can extract the records into a separate module and create an environment file with the desired alignment format. By using these attributes on VAR or TYPE sections, you can isolate the records in the same source file.

9.7.3 Optimal Record Layout

The optimal record layout is one where all the record's fields are naturally sized on naturally aligned boundaries and the overall record is as small as possible (for example, the fewest number of padding bytes required for proper alignment).

On OpenVMS I64 and OpenVMS Alpha systems, the compiler automatically places all fields of unpacked records on naturally aligned boundaries. On OpenVMS VAX systems, you have to explicitly ask for natural alignment by using either a DCL qualifier or the corresponding attribute.

To allow the compiler to do this placement, you should refrain from using explicit positioning and alignment attributes on record fields unless required by your application. The keyword PACKED should be avoided in all cases except:

  • PACKED ARRAY OF CHARs require the PACKED keyword to be manipulated as strings. Since chars are each 1 byte, using the PACKED keyword does not hurt their performance in any way.
  • PACKED SETs may perform better than unpacked SETs. For PACKED SETs, the compiler can sometimes allocate fewer bits for the set field or variable. These smaller sets can often be manipulated directly with longword or quadword instructions, instead of using a generic run-time library routine for larger sets.
    Inside unpacked records, PACKED SET fields are no slower than unpacked SET fields. The same holds true for variables of PACKED SETs. PACKED SETs of size 32 or 64 bits are the best performing set types; otherwise a multiple of 8 bits improves performance to a lesser degree.

You may still need to use PACKED if you rely on the record for compatability with binary data files or when assuming that types like PACKED ARRAY OF BOOLEAN are implemented as bit strings.

While the compiler can position record fields at natural boundaries, it cannot minimize the alignment bytes that are required between fields. The calling standard requires the compiler to allocate record fields in the same lexical order that they appear in the source file. For example:


type t1 = record
          f1 : char;
          f2 : integer;
          f3 : char;
          f4 : integer;
          end;

The size of this record is 16 bytes:

  • F1 is a byte field, followed by 3 padding bytes to position F2 at a longword boundary
  • F2 is 4 bytes
  • F3 is a single byte, followed by 3 more padding bytes to position F4 at a longword boundary
  • F4 is 4 bytes

The optimal layout would be:


type t2 = record
          f1,f2 : integer;
          f3,f4 : char;
          end;

The size of this record is only 12 bytes:

  • F1 and F2 are placed on adjacent longword boundaries
  • F3 and F4 can immediately follow, since they can appear on any byte boundary, they in turn are followed by 2 padding bytes to round the size of the record up to a multiple of its natural alignment of longword.

To achieve the fewest alignment bytes, you should place larger fields at the beginning of the record and smaller fields at the end. If you have record fields of schema types that have run-time size, you should place those at the very end of the record, since their offset requires run-time computation.

You can get the optimal record layout by:

  • Avoiding the PACKED keyword except for PACKED ARRAY OF CHARs and possibly PACKED SETs
  • Avoiding explicit POS or ALIGNED attributes
  • Placing larger fields before smaller fields
  • Placing fixed-size fields before run-time sized fields

Note

1 Previous versions of HP Pascal used ALPHA_AXP for this keyword. The NATURAL keyword is now the recommended spelling for the same behavior. The ALPHA_AXP keyword will continue to be recognized for compatibility with old command lines.


Previous Next Contents Index