This chapter explains the levels of compatibility between the new 32-bit compilation mode (n32), the old 32-bit mode, and 64-bit programs. It also describes the porting procedure to follow and the changes you must make to port your application from old 32-bit mode to n32-bit mode. For more details, see the MIPSpro 64-Bit Porting and Transition Guide
Specifically, this chapter discusses the following topics:
“Compatibility”, describes compatibility between 32, n32, and 64-bit programs.
“N32 Porting Guidelines”, explains guidelines for porting high-level languages.
“Porting Code to 64-Bit SGI Systems”, describes data types, typedefs, maximum memory allocation, and use of large files on XFS.
This chapter uses the following terminology:
o32 | The old 32-bit ABI generated by the ucode compiler; that is, 32-bit compilers prior to IRIX 6.1 operating system. For information about this compiler, see the MIPS O32 Compiling and Performance Tuning Guide |
n32 | The new 32-bit ABI generated by the MIPSPro 64-bit compiler (for a list of n32 features, see Chapter 1, “About the MIPSpro Compiler System”). For information about the n32 ABI, see the MIPSpro N32 ABI Handbook. |
In order to execute different ABIs, support must exist at three levels:
The operating system must support the ABI.
The libraries must support the ABI.
The application must be recompiled with a compiler that supports the ABI.
Figure 6-1, shows how applications rely on library support to use the operating system resources that they need.
![]() | Note: Each o32, n32, and n64 application must be linked against unique libraries that conform to its respective ABI. As a result, you cannot mix and match object files or libraries from any of the different ABIs. |
Figure 6-2, illustrates the library locations for different ABIs.
An operating system that supports all three ABIs is also needed for running the application. Consequently, all applications that want to use the features of n32 must be ported. The next section covers the steps in porting an application to the N32 ABI.
This section describes the guidelines/steps necessary to port IRIX 5. x 32-bit applications to n32. Typically, any porting project can be divided into the following tasks:
Identifying and creating the necessary porting environment (see “Porting Environment”)
Identifying and making the necessary source code changes (see “Source Code Changes”)
Rebuilding the application for the target machine (see “Build Procedure”)
Analyzing and debugging run-time issues (see “Run-time Issues”)
Each of these tasks is described in the following sections. You can also find additional information about n32 in the MIPSpro N32 ABI Handbook.
The porting environment consists of a compiler and associated tools, include files, libraries, and makefiles, all of which are necessary to compile and build your application. To generate n32 code, you must:
Check all libraries needed by your application to make sure they are recompiled using n32. The default root location for n32 libraries is /usr/lib32. If the n32 library needed by your application does not exist, recompile the library for n32.
Modify existing Makefiles (or set environment variables) to reflect the locations of these n32 libraries.
Since no differences exist in the sizes of fundamental types between the old 32-bit mode and n32, porting to n32 requires no source code changes for applications written in high-level languages such as C, C++, and Fortran. The only exception to this is that C functions that accept variable numbers of floating point arguments must be prototyped.
Assembly language code, however, must be modified to reflect the new subprogram interface. Guidelines for following this interface are described in Chapter 3 of the MIPSpro N32 ABI Handbook in the section titled “Assembly Language Programming Guidelines.”
Recompiling for n32 involves either setting the -n32 argument in the compiler invocation or running the compiler with the environment variable SGI_ABI set to -n32. That's all you must do after you set up a native n32 compilation environment (that is, all necessary libraries and include files reside on the host system).
Applications that are ported to n32 may get different results than their o32 counterparts. Reasons for this include:
Differences in algorithms used by n32 libraries and o32 libraries.
Operand reassociation or reduction performed by the optimizer for n32.
Hardware differences of the R8000 and R1000 (madd instructions round slightly differently than a multiply instruction followed by an add instruction).
This section covers porting code to 64-bit SGI systems, including:
Using Data Types
Using Predefined Types
Using Typedefs
Maximum Memory Allocation
Using Large Files with XFS
You can find additional information about porting to 64-bit SGI systems in the MIPSpro 64-Bit Porting and Transition Guide.
Data types and sizes are listed in Table 6-1.
Table 6-1. Data Types and Sizes
Data Type | (old) 32 Bit | n32 Bit | 64 Bit |
---|---|---|---|
8 | 8 | 8 | |
16 | 16 | 16 | |
32 | 32 | 32 | |
32 | 32 | 64 | |
64 (emulated with 32-bit integer operations) | 64 (native 64-bit integer operations) | 64 | |
32 | 32 | 64 | |
32 | 32 | 32 | |
64 | 64 | 64 | |
64 | 128 | 128 | |
void | 32 | 32 | 64 |
Note that in 64-bit mode, types long and int have different sizes and ranges; a long always has the same size as a pointer. A pointer (or address) has 64-bit representation in 64-bit mode and 32-bit representation in 32-bit mode. An int has a smaller range than a pointer in 64-bit mode. On 32-bit compiles, the long double generates a warning message indicating that the long qualifier is not supported.
Characteristics of integral types and floating point types are defined in the standard files limits.h and float.h.
The cc, CC, and as compiler drivers produce predefined macros listed in Table 6-2. These macros are used in sys/asm.h, sys/regdef.h , and sys/fpregdef.h.
32-Bit Executables | 64-Bit Executables |
---|---|
-D_MIPS_FPSET=32 | |
-D_MIPS_ISA=_MIPS_ISA_MIPS3 | |
-D_MIPS_SIM=_MIPS_SIM_ABI64 | |
-D_MIPS_SZINT=32 | |
-D_MIPS_SZLONG=64 | |
-D_MIPS_SZPTR=64 |
_MIPS_FPSET describes the number of floating point registers. The 64-bit compilation mode makes use of the extended floating point registers.
MIPS_ISA determines the MIPS Instruction Set Architecture. MIPS_ISA_MIPS1 and MIPS_ISA_MIPS3 are the defaults for 32 bits and 64 bits, respectively. For example:
/* Define a parameter for the integer register size: */ #if (_MIPS_ISA == _MIPS_ISA_MIPS1 || _MIPS_ISA == _MIPS_ISA_MIPS2) #define SZREG 4 #else #define SZREG 8 #endif |
MIPS_SIM determines the MIPS Subprogram Interface Model, which describes the subroutine linkage convention and register naming/usage convention.
_MIPS_SZINT, _MIPS_SZLONG, and _MIPS_SZPTR define the size of types int, long, and pointer, respectively.
The 64-bit MIPSpro compiler drivers generate 64-bit pointer and long and 32-bit int. Therefore, assembler code that uses either pointer or long types must be converted to use double-word instructions for MIPS III code (-64), and must continue to use word instructions for MIPS I and MIPS II code ( -32).
Also, new subroutine linkage conventions and register naming conventions exist. The compiler predefined macro _MIPS_SIM enables macros in sys/asm.h and sys/regdef.h.
Eight argument registers exist: $4 through $11. Four additional argument registers replace the temp registers in sys/regdef.h. These temp registers are not lost, however, as the argument registers can serve also as scratch registers, with certain constraints.
In the _MIPS_SIM_ABI64 model, registers t4 through t7 are not available, so any code using these registers does not compile. Similarly, registers a4 through a7 are not available under the _MIPS_SIM_ABI32 model.
If you are converting assembler code, the new registers ta0 , ta1, ta2, and ta3 are available under both _MIPS_SIM models. These alias with registers t4 through t7 in 32-bit mode, and with registers a4 through a7 in 64-bit mode.
Note that the caller no longer has to reserve space for a called function in which to store its arguments. The called routine allocates space for storing its arguments on its own stack, if desired. The NARGSAVE macro in sys/asm.h facilitates this.
This section describes typedefs that you can use to write portable code for a range of target environments, including 32- and 64-bit workstations as well as 16- and 32-bit PCs. These typedef s are enabled by compiler-predefined macros (listed in Table 6-2), and are in the file inttypes.h. (This discussion applies to C, although the same macros are predefined by the C++ compiler.)
Portability problems exist because an int (32 bits) is no longer the same size as a pointer (64 bits) and a long (64 bits) in 64-bit programs. Typedefs free you from having to know the underlying compilation model or worry about type sizes. In the future, if that model changes, the code should still work.
Typically, you want source code that you can compile either in 32- or 64-bit mode. (In this discussion, 32-bit mode implies -mips1/ 2; 64-bit mode implies -mips3/4.)
The following typedefs are defined in inttypes.h:
typedef signed char int8_t; typedef unsigned char uint8_t; typedef signed short int16_t; typedef unsigned short uint16_t; typedef signed int int32_t; typedef unsigned int uint32_t; typedef signed long long int int64_t; typedef unsigned long long int uint64_t; typedef signed long long int intmax_t; typedef unsigned long long int uintmax_t; typedef signed long int intptr_t; typedef unsigned long int uintptr_t; |
The intmax_t and uintmax_t types are guaranteed to be the largest integer type supported by this implementation. Use them in code that must be able to deal with any integer value. intptr_t and uintptr_t are guaranteed to be exactly the size of a pointer .
The total memory allocation for a program, and individual arrays, can exceed 2 gigabytes (2 Gbytes, or 2,048 Mbytes).
Previous implementations of Fortran, C, and C++ limited the total program size, as well as the size of any single array, to 2 GBytes. The current release allows the total memory in use by the program to exceed 2 gigabytes.
The IRIX 6.2 (MIPSPro 7.1) compilers (and above) support arrays that are larger than 2 gigabytes for programs compiled under the -64 ABI. The arrays can be local, global, and dynamically created as the following example demonstrates. (Initializers are not provided for these arrays.) Large array support is limited to Fortran, C, and C++.
The following code shows an example of arrays larger than 2 gigabytes.
#include <stdlib.h> int i[0x100000008]; void foo() { int k[0x100000008]; k[0x100000007] = 9; printf(“%d \n”, k[0x100000007]); } main() { char *j; j = malloc(0x100000008); i[0x100000007] = 7; j[0x100000007] = 8; printf(“%d \n”, i[0x100000007]); printf(“%d \n”, j[0x100000007]); foo(); } |
You must run this program on a 64-bit operating system with IRIX version 6.2 (or higher). You can verify the system you have by typing uname -a. You must have enough swap space to support the working set size and you must have your shell limit datasize, stacksize, and vmemoryuse variables set to values large enough to support the sizes of the arrays (see sh(1) man page).
The following example compiles and runs the preceding code after setting the stack size to a correct value:
% uname -a IRIX64 cydrome 6.2 03131016 IP19 $cc -64 -mips3 a2.c $limit cputime unlimited filesize unlimited datasize unlimited stacksize 65536 kbytes coredumpsize unlimited memoryuse 754544 kbytes descriptors 200 vmemoryuse unlimited $limit stacksize unlimited $limit cputime unlimited filesize unlimited datasize unlimited stacksize unlimited coredumpsize unlimited memoryuse 754544 kbytes descriptors 200 vmemoryuse unlimited $a.out 7 8 9 |
An application may create or encounter files greater than 2 gigabytes with the XFS file system. If a program is doing sequential I/O and does not maintain internal byte counters, files greater than 2 gigabytes will not encounter problems.
However, if an application uses internal byte counters, then modifications are required. Table 6-3, lists potential problems and modifications required to enable files greater than 2 gigbytes to run on XFS.
Table 6-3. Modifications for Applications on XFS
Application | Modification |
---|---|
Uses an internal byte counter while reading | Change to type long long |
Uses certain system calls such as lseek() and stat() that use 32-bit off_t | Use lseek64(), stat64(), and so forth |
Relies on internal features of EFS (such as reads the raw disk) | Rewrite the application (so it does not read the raw disk) |
For more information about XFS, see Getting Started with XFS Filesystems.