AR-mode programming support

With the METAL option, an AR-mode function can access data stored in data spaces by using the hardware access registers. For more information about AR-mode, see z/OS MVS Programming: Assembler Services Guide. A non-AR-mode function is said to be in primary mode.

The following sections describe the compiler options, language constructs, and built-in functions that support AR-mode programming.

AR-mode function declaration

You can declare a function to be an AR-mode function with the armode attribute. The syntax is:
  	void armode_func() __attribute__((armode));  
You can also use the ARMODE compiler option to declare that all functions in the source program to be AR-mode functions. If you use the ARMODE compiler option and you want to single out the functions in the source program to be primary mode functions you can declare the function with the noarmode attribute. The syntax is:
  	void nonarmode_func() __attribute__((noarmode)); 

Far pointer declaration, reference, and dereference

The ability to reference data stored in different data spaces is achieved through a C language extension to pointer types called far pointer types. A far pointer type is declared by adding the __far qualifier. The syntax is
  	int * __far my_far_pointer;
A far pointer can be declared in a function of any mode (AR mode or primary mode). But only an AR-mode function can directly or indirectly dereference a far pointer. In other words, only an AR-mode program can access data stored in data spaces with far pointers.
Note: For an example of a simple dereference of a far pointer, see Figure 7.
Regardless of the mode of the function, a far pointer can be manipulated in the following ways:
  • It can be passed as a parameter.
  • It can be received as a function return value.
  • It can be compared with another pointer.
  • It can be cast as another pointer type.
  • It can be used in pointer arithmetic expressions.
A far pointer consists of ALET and an offset. Although an ALET is always 32 bits in length, the size of a far pointer is twice the size of a regular pointer. The layout of a far pointer in memory depends on the AMODE of the function:
  • Under AMODE 31, a far pointer occupies eight bytes.
    • The ALET occupies the first four bytes.
    • The offset occupies the last four bytes.
  • Under AMODE 64, a far pointer occupies 16 bytes.
    • The first four bytes are unused.
    • The ALET occupies the second four bytes.
    • The offset occupies the last eight bytes.
This difference in pointer size is illustrated in Figure 1.
Figure 1. Far pointer sizes under different addressing modes
A 31-bit far pointer is eight bytes in length. A 64-bit far pointer is 16 bytes in length.

C language constructs and far pointers

Table 1 describes the effects of language constructs that might have special impact on far pointers.
Table 1. Language constructs that may have special impact on far pointers
Language Construct Effect
Implicit or explicit cast from normal to far pointer Because the normal pointer is assumed to point to primary address space, the ALET of the far pointer is set to 0.
Explicit cast from far pointer to normal pointer The offset of the far pointer is extracted and used to form the normal pointer. Unless the ALET of the far pointer was 0, the normal pointer is likely to be invalid.
Operators !=, == If either operand is a far pointer, the other operand is implicitly cast to a far pointer before the operands are compared. The comparison is performed on both the ALET and offset components of a far pointer.
Operators <, <=, >, >= Only the offset of the far pointer is used in the comparison. Unless the ALETs of the far pointers were the same, the result might be meaningless.
Compare to NULL Because of the implicit cast of NULL to a far pointer, the != and == operators compare both the ALET and the offset to zero. A test of !(p>NULL) is not sufficient to ensure that the ALET is also 0.
Pointer arithmetic The effects of pointer arithmetic are applied to the offset component of a far pointer only. The ALET component remains unchanged.
Address of Operator, operand of & The result is a normal pointer, except in the following cases:
  • If the operand of & is the result of an indirection operator (*), the type of & is the same as the operand of the indirection operator.
  • If the operand of & is the result of the arrow operator (->, structure member access), the type of & is the same as the left operand of the arrow operator.

Implicit ALET association

In addition to explicitly specifying ALETs that use far pointers to access data in data spaces, the compiler must associate those ALETs with all the memory references contained in the AR-mode function.

In a non-AR-mode function, all variable references are to primary data space (ALET 0). In an AR-mode function, the compiler manages access registers (ARs) so that every memory reference uses an ALET associated with the variable type to reach the appropriate data space. Table 2 lists the ALET associations for different types of variables.
Table 2. Implicit ALET associations for AR-mode-function variables
Variable type Implied ALET
File-scope variable ALET 0 (primary data space)
Stack variables (function local variable) The ALET that is in AR 13 at the time of function entry. This points to the stack frame.
Parameters (function formal parameters) The ALET that is in AR 1 at the time of function entry. This points to the parameter list.
Data pointed to by regular pointers ALET 0 (primary data space).
Data pointed by far pointer ALET contained in far pointer.

Far pointer construction

The Metal C Runtime Library does not provide functions for allocating or deallocating alternative data spaces. You can use the DSPSERV and ALESERV HLASM macros to allocate space and obtain a valid ALET and offset. For an example, see Figure 5. For more information, see z/OS MVS Programming: Assembler Services Guide.

Built-in functions that manage far-pointer components

The compiler provides built-in functions for setting and getting the individual components of far pointers. Whenever you use these built-in functions, you must:
  • Define the macro _MI.BUILTN to "1".
  • Include the header file builtins.h.
Figure 2 lists the constructors.
Figure 2. Built-in functions for setting far-pointer components
   void * __far __set_far_ALET_offset(unsigned int alet, void * offset); 
   void * __far __set_far_ALET(unsigned int alet, void * __far offset);  1 
   void * __far __set_far_offset(void * __far alet, void * offset);      2 
Notes:
  1. The __set_far_ALET function does not modify the far-pointer parameter offset. It simply uses it to provide the offset component of the far pointer being constructed. Its return value is the constructed far pointer.
  2. Similarly, the __set_far_offset function that uses the far-pointer parameter ALET is not modified; it simply provides the ALET for the far pointer being constructed.
Figure 3 lists the extractors.
Figure 3. Built-in functions for getting far-pointer components
   unsigned int __get_far_ALET(void * __far p); 
   void * __get_far_offset(void * __far p);

For information about ARMODE built-in functions, see Using hardware built-in functions in z/OS XL C/C++ Programming Guide.

Library functions that manipulate data stored in data spaces

The XL C compiler provides far versions of some of the standard C string and memory library functions. The far versions can be called by either AR-mode or primary-mode functions. If these functions are called by an AR mode function, the compiler will generate inline code for them.

Whenever you use these functions, you must:
  • Define the macro _MI.BUILTN to "1".
  • Include the header file builtins.h.

The semantics of these functions, listed in Figure 4, are identical to the standard version.

Figure 4. Library functions for use only in AR-mode functions
   void * __far __far_memcpy(void * __far s1, const void * __far s2, size_t n); 
   int __far_memcmp(const void * __far s1, const void * __far s2, size_t n); 
   void * __far __far_memset(void * __far s, int c, size_t n); 
   void * __far __far_memchr(const void * __far s, int c, size_t n); 
   char * __far __far_strcpy(char * __far s1, const char * __far s2);  See Note 
   char * __far __far_strncpy(char * __far s1, const  char * __far s2, size_t n);
   int __far_strcmp(const char * __far s1, const char * __far s2);
   int __far_strncmp(const char *__far s1, const char * __far s2, size_t n); 
   char * __far __far_strcat(char * __far s1, const char * __far s2); 
   char * __far  __far_strncat(char * __far s1, const char * __far s2, size_t n); 
   char * __far __far_strchr(const char * __far s, int c); 
   char * __far __far_strrchr(const char * __far s, int c);  
   size_t __far_strlen(const char * __far s);
Note: For an example that illustrates the use of this function, see Figure 6.

AR-mode function linkage conventions

AR mode functions follow the same linkage conventions as do primary-mode functions, with the following additional requirements:
  • Any function that calls an AR-mode function must supply the 54-word F7SA save area for saving the access registers.
  • The AR-mode function must preserve the calling function’s access registers.
  • The AR-mode function is responsible for switching into AR mode on entry and switching back to calling function’s ASC mode on exit.
    Note: A primary-mode function does not switch the ASC mode when calling an AR-mode function.
  • An AR-mode function must switch to primary mode before calling a primary mode function.
  • A far pointer is passed and returned as a struct that is based on the layout for the calling function’s AMODE.

Default prolog and epilog code for AR-mode functions

If the calling function is in non-AR mode, the DSA and parameter areas are assumed to be located in the primary address space.

For AR-mode functions, the default prolog code generates additional instructions that:
  • Save the calling function’s access registers in the F7SA save area.
  • Save the ASC mode of the calling function in the F7SA save area.
  • Switch to AR mode.
  • Prime AR 1 and AR 13 with LAE instructions.
For AR-mode functions, the default epilog code generates additional instructions that:
  • Restore the calling function’s access registers.
  • Restore the ASC mode of the calling function.

Data space allocation and deallocation

Figure 5 provides examples of routines for allocating and deallocating data space.

Copying a string pointer to a far pointer

Figure 6 provides an example of using a built-in function to copy a C string pointer to a far pointer.

Figure 6. Copying a C string pointer to a far pointer
   /*****************************************************/
   /* __far_strcpy example                              */
   /*****************************************************/
   
   char *__far far_strcpy_example() __attribute__((armode));
   char *__far far_strcpy_example()
   {
     char *__far far_string;
     char * near_string;

  near_string = "Hello World!\n";

     far_string = allocate_far(1024);

     __far_strcpy(far_string,near_string);

     return far_string; /* Assume caller will free allocated data space */
   }

Far pointer dereference

The Metal C Runtime Library does not provide functions for allocating or deallocating alternative data spaces. Figure 7 provides an example of code that dereferences a far pointer.

Figure 7. Example of a simple dereference of a far pointer
   /*****************************************************/
   /* Simple dereference example                        */
   /*****************************************************/

   char get_ith_character(char *__far s, int i) __attribute__((armode));
   char get_ith_character(char *__far s, int i)
   {
     return s[i];
   }


   int main()
   {
     char c;
     char *__far far_string;

     far_string = far_strcpy_example();

     c = get_ith_character(far_string,1);
  
     free_far(far_string);

     return c;
   }