32-Bit To 64-Bit Migration Considerations

Download as pdf or txt
Download as pdf or txt
You are on page 1of 4

32-bit to 64-bit Migration Considerations

This section outlines various portability considerations in moving C programs from 32-bit to 64-bit mode.

• Constants
• Undeclared Functions
• Assignment of Long Types to Integer and Pointers
• Structure Sizes and Alignment
• Bitfields
• Miscellaneous
• Interlanguage Calls with Fortran

Constants
The limits of constants change. This table shows changed items in the limits.h header file, their hexadecimal value, and decimal equivalent. The
equation gives an idea of how to construct these values.

Type Hexadecimal Equation Decimal

signed long min 0x8000000000000000L -(263) -9,223,372,036,854,775,808


(LONG_MIN)

signed long max 0x7FFFFFFFFFFFFFFFL 263-1 +9,223,372,036,854,775,807


(-LONG_MIN-1)
(LONG_MAX)

unsigned long max 0xFFFFFFFFFFFFFFFFUL 264-1 +18,446,744,073,709,551,616


(ULONG_MAX)

In C, type identification of constants follows explicit rules. However, programs that use constants exceeding the limit (relying on a 2's complement
representation) will experience unexpected results in the 64-bit mode. This is especially true of hexadecimal constants and unsuffixed constants,
which are more likely to be extended into the 64-bit long type.

Problematic behaviors will generally occur at boundary areas such as:

• constant >= UINT_MAX


• constant < INT_MIN
• constant > INT_MAX

Some examples of undesirable boundary side effects are:

Constant assigned to long 32 bit mode 64 bit mode


-2,147,483,649 (INT_MIN-1) +2,147,483,647 -2,147,483,649

+2,147,483,648 (INT_MAX+1) -2,147,483,648 +2,147,483,648

+4,294,496,726 (UINT_MAX+1) 0 +4,294,967,296

0xFFFFFFFF (UINT_MAX) -1 +4,294,496,295

0x100000000 (UINT_MAX+1) 0 +4,294,967,296

0xFFFFFFFFFFFFFFFF (ULONG_MAX) -1 -1

Currently, the compiler gives out of range warning messages when attempting to assign a value larger than the designated range into a long type. The
warning message is:

1506-207 (W) Integer constant 0x100000000 out of range.

Page | 1 

 
This warning message may not appear for every case.

When you bit left-shift a 32-bit constant and assign it into a long type, signed values are sign-extended and unsigned values are zero-extended. The
examples in the table below show the effects of performing a bit-shift on both 32- and 64-bit constants, using the following code segment:

long l=constantL<<1;

Constant Value after Bit-Shift


Initial Constant Value
32-bit 64-bit

0x7FFFFFFFL (INT_MAX) 0xFFFFFFFE 0xFFFFFFFE

0x80000000L (INT_MIN) 0 0x100000000

0xFFFFFFFFL (UINT_MAX) 0xFFFFFFFE 0x1FFFFFFFE

Unsuffixed constants can lead to type ambiguity that can impact other parts of your program, such as the result of sizeof operations. For example, in
32-bit mode the compiler types a number like 4294967295 (UINT_MAX) as an unsigned long. In 64-bit mode, this same number becomes a signed
long. To avoid this possibility, explicitly add a suffix to all constants that have the potential of impacting constant assignment or expression
evaluation in other parts of your program. The fix for the above case is to write the number as 4294967295U. This forces the compiler to always see
that constant as an unsigned int regardless of compiler mode.

Assignment of Long Variables to Integers and Pointers


Using int and long types in expressions and assignments can lead to implicit conversion through promotions and demotions, or explicit conversions
through assignments and argument passing. The following should be avoided:

• Using integer and long types interchangeably, leading to truncation of significant digits or unexpected results.
• Passing long arguments to functions expecting type int
• Exchanging pointers and int types, causing segmentation faults.
• Passing pointers to a function expecting an int type, resulting in truncation.
• Assignment of long types to float, causing possible loss of accuracy.

Assigning a long constant to an integer will cause truncation without warning. For example:

int i;
long l=2147483648; /* INT_MAX+1*/
i=l;

What will be the value of i? INT_MAX+1 is 2147483647+1 (0x80000000), which becomes INT_MIN when assigned into a signed type. Truncation
occurs because the highest bit is treated as a sign bit. The rule here is that there will be a loss of significant digits.

Similar problems occur when passing constants directly to functions, and in functions that return long types. Making explicit use of the L and UL
suffix will avoid most, but not all, problems. Alternately, you can avoid accidental conversions by using explicit prototyping. Another good practice
is to avoid implicit type conversion by using explicit type casting to change types.

Undeclared Functions
Any function that returns a pointer should be explicitly declared when compiling in 64-bit mode. Otherwise, the compiler will assume the function
returns an int and truncate the resulting pointer, even if you were to assign it into a valid pointer.

Code such as:

a=(char *) calloc(25);

Page | 2 

 
which used to work in 32-bit mode will in 64-bit mode will now silently get a truncated pointer. Even the type casting will not avoid this because the
calloc has already been truncated after the return.

The fix in this case is to include the appropriate header file, which is stdlib.h and not malloc.h.

Structure Sizes and Alignments


Structures may face potential porting problems.

The 64-bit specification changes the size, member and structure alignment of all structures that are recompiled in 64-bit mode. Structures with long
types and pointers will generally change size and alignment in 64-bit mode. Some structures may not change in size because they happen to fall on an
exact 8-byte boundary even in 32-bit mode.

Sharing data structures between 32- and 64-bit processes is no longer possible unless the structure is devoid of pointer and long types. Unions that
attempt to share long and int types, or overlay pointers onto int types will now be aligned differently, or be corrupted. In general, all but the simplest
structures must be checked for alignment and size dependencies.

The alignment for -qalign=full, power or natural changes for 64-bit mode. Structure members are aligned on their natural boundaries. Long types and
pointer types are word-aligned in 32-bit mode, and doubleword aligned in 64-bit mode. Additional spaces could be used for padding members.

The alignment for -qalign=twobyte and -qalign=mac68k are not supported in 64-bit mode.

Structures are aligned according to the strictest aligned member. This remains unchanged from 32-bit mode. Because of the padding introduced by
the member alignment, structure alignment may not be exactly the same as in the 32-bit mode. This is especially important when you have arrays of
structures which contain pointer or long types. The member alignment will change, most likely leading to the structure alignment to change to
doubleword alignment (if there are no long long types, double types and long double types).

Bitfields
Structure bitfields are limited to 32 bits, and can be of type signed int, unsigned int or plain int. Bit fields are packed into the current word. Adjacent
bit fields that cross a word boundary will start at storage unit. This storage unit is a word in power and full alignment, halfword in the mac68k and
twobyte alignment, and byte in the packed alignment. 64-bit bitfields are not supported.

In 32-bit mode, non-integer bitfields are tolerated (but not respected) only in the C extended language level.

If you use long bit fields in 64-bit mode, their exact alignment may change in future versions of the compiler, even if the bitfield is under 32 bits in
length.

Miscellaneous Issues

• The sizeof operator will now return size_t which is an unsigned long.
• The length of the integer required to hold the difference between two pointers is ptrdiff_t, and is a signed long type.
• Masks will generally lead to different results when compiled in 64-bit mode from their 32-bit mode behavior.
• Many include files have pointers and structures in them, and their inclusion in 64-bit mode will change the size of your data section even if
your program does not use structures and pointers explicitly.
• __int64 is a long type in 64-bit mode, but will look like a long long type in 32-bit mode. __int64 types can participate in promotion rules
and arithmetic conversion when in 64-bit mode. When in 32-bit mode, these types can not participate in the usual arithmetic conversions.
• In 64-bit mode, member values in a structure passed by value to a va_arg argument may not be accessed properly if the size of the
structure is not a multiple of 8-bytes. This is a known limitation of the operating system.
• In 64-bit extended mode, zero-extension from unsigned int to an unsigned long preserves the bit pattern. For example, zero-extending an
unsigned int with value 0xFFFF FFFF (large negative value) results in an unsigned long with value 0x0000 0000 FFFF FFFF (large
positive value).

Page | 3 

 
Interlanguage Calls with Fortran
A significant number of applications use C, C++, and Fortran together, by calling each other or sharing files. Such applications are among the early
candidates for porting to 64-bit platforms for its abilities to solve larger mathematical models. Experience shows that it is easier to modify data
sizes/types on the C side than the Fortran side of such applications. The following table lists the equivalent Fortran type in the different modes.
C/C++ type 32-bit 64-bit

int INTEGER INTEGER

unsigned int LOGICAL LOGICAL

signed long INTEGER INTEGER*8

unsigned long LOGICAL LOGICAL*8

pointer INTEGER INTEGER*8

A user must not mix XCOFF object formats from different modes. A 32-bit Fortran XCOFF cannot mix with a 64-bit C or C++ XCOFF object and
vice versa. Since Fortran77 usually does not have an explicit pointer type, it is common practice to use INTEGER variables to hold C or
C++ pointers in 32-bit mode. In 64-bit mode, the user should use INTEGER*8 in Fortran. Fortran90 does have a pointer, but it is unsuitable for
conversion to the basic C and C++ types.

In 64-bit mode, Fortran will have a POINTER*8 that is 8 bytes in length as compared to their POINTER which is 4-bytes in length.

Page | 4 

You might also like