Chapter Contents

Previous

Next
Systems Programming with the SAS/C Compiler

SPE Internals

The previous section explained how the SPE framework is created and destroyed with start-up routines and L$UMAIN. This section discusses other SPE framework routines that are required at execution time.

The first routines discussed are the routines that handle the stack, L$UPROL and L$UEPIL. Next, the details of L$UPREP are covered. Following this discussion is an explanation of L$UTFPE, the routine responsible for handling math function exceptions. Then, L$UTZON, a routine that allows you to define the offset between local time and Greenwich time, and L$UWARN, the routine that handles conditions that call for a diagnostic message, are covered. Finally, the routine L$UHALT, which is called by the library to force abnormal termination, is described.


L$UPROL: Stack Manipulation

Implementation of C under the IBM 370 architecture requires the use of a software stack for storing auto variables, temporary results, and items of miscellaneous status, such as saved registers. Each function must obtain stack space on entry and free stack space on return. Stack management is provided by prolog and epilog routines. The compiler generates instructions to call these routines on function entry and return.

Because the prolog and epilog are used for every function call, their performance has a significant impact on the performance of the entire program. But there is a trade-off between the performance of the prolog and epilog and their functionality. For convenient debugging, it can be helpful to add instructions to the prolog and epilog to save additional status information, even though performance is reduced.

While the exact behavior of the prolog and epilog can be changed according to the needs of the framework, note that many components of the SPE implementation are interdependent with the prolog and epilog. Notably, L$UEXIT ( exit ), L$UJUMP ( longjmp ), L$UZSIR (USS signal handling), L$UEXLK ( bldexit ), and L$UBTRC ( btrace ) are closely involved with the details of stack management. Any change to the prolog and epilog has the possibility of some effect on these routines.

The prolog entry point is L$UPROL, which is called when a function that requires a DSA is entered. The epilog entry point is L$UEPIL, which is called when a function that requires a DSA returns. Linkage to both routines is indirect. The addresses of L$UPROL and L$UEPIL are stored in the CRAB (in fields CRABPRLG and CRABEPLG) by L$UMAIN as part of framework creation. The compiler generates code to load these addresses and branch to them. (L$UPROL is also called by L$UPREP.)

Prolog conventions

In each C function, the compiler generates code to branch to the prolog, using the following conventions:

The prolog returns to the function via R5. To return, the prolog branches to offset X'3E' (that is, the symbolic name CPROGO) from the address in R5. Usually, when the prolog is invoked, R5 and R15 have the same value, but this is not necessarily so for INDEP functions.

When the prolog returns to CPROGO, the code generated by the compiler at this point expects the following to have been done:

Although not required by compiled code, the epilog requires that the value of R15 on entry be saved. L$UPROL stores this value in the DSAPRBSV field of the new DSA.

Epilog conventions

The compiler generates code to branch to the epilog, using the following conventions:

To return to the function, the epilog branches to offset X'36' (symbolic name CPROEXIT) from the function's entry point. Note that the epilog cannot obtain the function's entry point from the R15 slot of the previous save area because the function may have stored a return value there. R1 and registers 6 through 12 must be unchanged when the epilog returns. Registers 2 through 5 and R13 are restored as necessary by compiler-generated instructions in the function.

L$UPROL operation

The SPE version of L$UPROL uses a single block of memory allocated by L$UMAIN during framework creation as a stack and issues an abend if it overflows. This is a common way to implement a software stack, representing a compromise between maximum dependability and maximum performance.

L$UPROL performs the following steps. Certain operations may be useful for debugging and are marked as optional. In the object code for L$UPROL, optional steps have been disabled. L$UPROL

  1. stores registers 6 through 11 (optional).

  2. checks for stack overflow and abends if a new DSA cannot be allocated.

  3. updates the stack top pointer.

  4. stores the address of the previous save area in the new DSA.

  5. stores the address of the new DSA in the previous save area (optional).

  6. saves R15 in DSAPRBSV for the epilog.

  7. copies an eye-catcher and a flag byte into the first word of the DSA. (Unless the program is in an INDEP framework, the eye-catcher is useful only for debugging.)

  8. links the new DSA to the previous DSA (which may not be the same as the previous save area if there is an intervening non-C routine), and saves the new DSA address in the CRAB.

  9. copies the function name to the DSAOWNER field (optional).

  10. saves R1 in the DSAPARMS field.

  11. loads R4 with the address of the constant CSECT from the CPROCONS field.

  12. returns to the function.


L$UEPIL operation

The L$UEPIL entry point in L$UPROL performs the following steps:

  1. updates the stack top and current DSA fields in the CRAB

  2. loads R14 with the address of the entry point of the function from the DSAPRBSV field

  3. restores registers 6 through 11 (optional)

  4. returns to the returning function at the CPROEXIT offset.


L$UPREP: Framework Creation and Recovery

L$UPREP is mentioned in the preceding section as one of the INDEP framework support routines. Its function is to determine if the C framework has been created. If it has been created, then L$UPREP recovers the framework and returns to the calling function. If not, then L$UPREP calls L$UMAIN to create the framework.

L$UPREP makes a distinction between two types of function calls based on the register save area (addressed by R13) associated with the caller. The caller may be a C function, in which case the save area is part of a C DSA, or a non-C function, in which case the save area is not a C DSA. As mentioned in the L$UPROL discussion, C DSAs are distinguished by the "CSA" marker at offset 0 from R13. Note that functions written in assembler using the CENTRY and CEXIT macros are indistinguishable from functions written in the C language and are therefore considered to be C functions. Non-C functions include routines written in another high-level language, assembler routines that do not use CENTRY and CEXIT, and the operating system components.

L$UPREP is used only in an INDEP framework. When the indep compiler option is used, the code generated by the compiler to call the prolog is changed. The indep option causes the compiler to generate a branch to the L$UPREP routine. A function compiled with the indep option takes this branch almost immediately after entry.

L$UPREP takes one of the following three paths:

When L$UPREP is entered, it inspects the save area addressed by R13 to determine whether the caller is a C function. If "CSA" appears at offset 0 (the DSACSA field in the DSA), then L$UPREP assumes that the caller is a C function.

If L$UPREP does not find "CSA", it invokes the L$UCENV macro to determine if the framework has already been created. L$UCENV returns the address of a location where a pointer to the CRAB should be (or has been) stored. If the location contains 0, then the framework has not been created. If it has been created, then L$UPREP loads the CRAB address into R12.

If the framework has been created, then, before returning control to the function, L$UPREP checks to see if the function requires a DSA. If it does, L$UPREP invokes L$UPROL to create a DSA.

When L$UPROL returns, L$UPREP marks the DSA as one belonging to a function compiled with the indep option and sets the DSANJUMP flag in the DSAFLGT field. This flag prohibits a longjmp over the function. This prohibition is established for functions compiled with the indep option because the caller of such a function may be written in another language, and most high-level languages do not expect longjmp type returns. If the caller can handle this sort of branching, the DSANJUMP flag does not need to be set.

If the framework has not been created, L$UPREP calls L$UMAIN to create it. L$UPREP loads R1 with the address returned by L$UCENV so that L$UMAIN will store the CRAB pointer for later recovery. After the framework has been created, L$UMAIN calls the function directly, placing its own return address in R5. Upon entry, the function again calls L$UPREP immediately. (Note that this is a recursive call.) Using the logic described above, L$UPREP determines that the function was called from a C function.

L$UPREP also enforces a basic convention of the INDEP framework: if the called function is the main function, the framework is destroyed when main returns by calling L$UMAIN. However, in the general case, upon return from the function, L$UPREP restores the registers (including R14) from the save area of its caller's caller and returns to the address in R14. This branch transfers control back to the routine that invoked the function that created the INDEP framework. L$UMAIN's save area, anchored in CRABMDSA, is left intact. This leaves the C framework in place for subsequent calls.

L$UPREP in the full C framework

The SPE L$UPREP is identical to the standard C library L$UPREP and can be used to replace the standard C library L$UPREP. Refer to Using the indep Option for Interlanguage Communication for more information.

The L$UCENV macro

When the C framework is created, L$UMAIN stores the CRAB address in some appropriate location. The L$UCENV macro defines a CSECT also named L$UCENV for this purpose. When the macro is invoked by L$UPREP, L$UCENV returns the address of the CSECT as the address of the CRAB pointer. Because this implementation forces the program to be non-reentrant, applications that need to be reentrant should use a different method of storing the CRAB address.

Under CICS, the L$UCENV macro uses the first word of the CICS transaction work area (TWA) to store the address of the CRAB pointer. You may need to decide whether or not this technique is appropriate for your application's environment. The storage allocated by the CICS SPE library is CLASS=USER; this storage is released automatically at task termination.


L$UTFPE: Math Error Handling

L$UTFPE handles floating-point error conditions such as overflow and underflow. L$UTFPE can be invoked by character to floating-point conversion functions such as strtod and sscanf . The SPE L$UTFPE uses bldexit and the SPIE/ESPIE SVCs to handle these conditions. Of course, other implementations may be possible that do not rely on these SVCs. The sample code for L$UTFPE is not supported in CICS.

Note that L$UTFPE uses the ESPIE macro only if the program is executing in 31-bit addressing mode. This means that the ESPIE SVC is never used under 370 mode CMS, which does not support the ESPIE SVC.

The L$UTFPE source module defines a function named L$CTFPE, which is the name of the corresponding full library implementation. The full library requires its own implementation of L$CTFPE and does not execute correctly with the SPE implementation.

L$CTFPE is called as a normal C function. It is defined as follows:

struct FPE {
      union {
         char space [12];
         int active;
      } hdr;
      jmp_buf get_away;
   };

   void L$CTFPE(int func, struct FPE *elt);

The func argument to L$CTFPE is either 1, to define a floating-point error trap, or 0, to cancel a previously defined trap.

When func is 1, elt addresses a trap element containing work space and a jump buffer. L$CTFPE must set elt->hdr.active to a non-zero value to indicate that the trap element is active. When a floating-point error occurs, the defined trap should perform the following:

longjmp(elt->get_away, ic)

ic is the program check interrupt code.

When func is 0, elt addresses the trap element for the trap to be cancelled. L$CTFPE must reset func->hdr.active to 0 to show that the trap has been cancelled. Note that the library routines that call L$CTFPE always cancel traps in last-in, first-out order. Also note that only one trap is ever defined at a time unless a function such as a math function, is interrupted by a user bldexit routine that also calls a math function.


L$UTZON: Local Time Offset Determination

L$UTZON is called by library timing functions to obtain the difference between Greenwich time and local time. The timing routines assume that time_t values contain Greenwich time and use the information returned by L$UTZON to convert them to local time. L$UTZON supports several return value formats, since some information is more readily available in some environments than in others.

The L$UTZON routine is also used in the SAS/C Generalized Operating System (GOS) interface. The linkage conventions for L$UTZON in SPE and GOS are similar enough that the same routine can be used for both. See SAS Technical Report C-115, The Generalized Operating System Interface for the SAS/C Compiler Run-Time System, Release 5.50 for more information.

When L$UTZON is called, register 1 addresses a parameter list in the format shown in L$UTZON Parameter List Format.


L$UTZON Parameter List Format
TZONPRMS DS 0D DS A zero (nonzero for GOS) DS A zero (can be used as a work area) DS A address of a doubleword return value

Register 13 addresses a standard save area when L$UTZON is called; however, it is not necessary to save and restore registers, as this is done by the caller of L$UTZON.

When L$UTZON returns, the value in register 15 indicates the format and meaning of the data addressed by the third word of the parameter list.

If L$UTZON returns a code of 0, it stores a signed integer in the first word of the return area, indicating the number of seconds difference between local time and Greenwich time. For example, if it is 4 p.m. locally when it is 2 p.m. Greenwich time, +7200 (2 hours in seconds) is stored.

If L$UTZON returns a code of 1, it stores a value in TOD clock format in the return area, indicating the local time. More precisely, this value represents the number of seconds since the local midnight of January 1, 1900, where bit 51 of the doubleword represents a microsecond.

If L$UTZON returns a code of 2, it stores the local date and time in the return area in the format used by the OS TIME BIN macro. More precisely, the first word of the doubleword should contain the local time, expressed as the number of hundredths of a second since midnight, represented as an unsigned binary integer. The second word of the doubleword should contain the packed decimal local date in the form 00YYDDDF, where YY is the number of years since 1900, and DDD is the number of days since January 1.

If L$UTZON cannot determine the local time offset, it should return a code of 8 in register 15.


L$UWARN: Issue Diagnostic Messages

Some SPE library functions, such as memcpy and sqrt , are designed to issue diagnostic messages. In the SPE framework, the routine L$UWARN is called whenever a diagnostic is appropriate. The SPE version of this routine simply stores an appropriate value in errno and returns. Depending on the needs of the application, some other action, such as issuing an abend or actually writing a diagnostic, may be preferred.

L$UWARN can be called through either of its entry points, #WARNING or $WARNING. When it is called, R1 addresses a variable length parameter list in the format shown in L$UWARN Parameter List Format.


L$UWARN Parameter List Format
WARNPRMS DS 0D DS F message number DS F value to be stored in errno EQU * zero or more replacement values . . .

The first two words in the list are the diagnostic message number and the value to be stored in errno . Any additional arguments represent values to be inserted into the message text. (These values can be processed using the va_arg macro.)

Two special errno values should be noted. If the value to be stored in errno is 0, the diagnostic is a note rather than a warning, and errno should not be changed. If the value to be stored is negative, it indicates a severe error, and an abend is recommended.

If L$UWARN is to write diagnostic messages, obtain the message texts from the SASC.ERRMSGS data set (under OS/390) or LSU ERRMSGS (under CMS). Each record in this file contains a message number in columns 1-8 and the corresponding message text beginning in column 9. The message texts are suitable for use as formats with the vsprintf function.


L$UHALT: Terminate Execution Abnormally

After certain error conditions, the library needs to abnormally terminate program execution. For instance, if the program calls the POSIX getpid function, but USS is not running, execution cannot continue because the function call cannot succeed. However, the function definition does not provide a way for the function to fail. The SPE library forces abnormal termination by calling the routine L$UHALT. The supplied version of this routine simply issues the assembler ABORT macro, which, in all systems other than CICS, issues an ABEND.

L$UHALT is called via the entry point L$CHALT. When it is called, R1 addresses a parameter list in the format shown in L$UHALT Parameter List Format.


L$UHALT Parameter List Format
HALTPRMS DS 0D DS F intended ABEND code DS F message suppression flag

The first word in the argument list is the intended ABEND code, an integer between 1200 and 1240. The second argument is an integer which, if not zero, requests suppression of any library messages about the ABEND. Since the SPE library does not diagnose ABENDs, this argument can be ignored.

Note that if L$UHALT returns to its caller, the effects of further execution are completely undefined.


Chapter Contents

Previous

Next

Top of Page

Copyright © 2001 by SAS Institute Inc., Cary, NC, USA. All rights reserved.