Saturday, July 23, 2005

64Bit C Programmer's Tips, Tricks and Traps

 P64 Gotcha List
 ----------------

 1. sizeof long array can overflow on lp64.

   uchar n = sizeof( 50*sizeof(long)); // 200 bytes in ilp32, 400 bytes in lp64.

 2. Use u U L l suffix on large const.

   long l = 1 <<32; // 0 on ilp32,lp64
   long l = 1L<<32; // 2^32 on lp64, 0 on ilp32.

   Solaris: see /usr/include/sys/inttypes.h

   INT64_C(5)  => 5ll for ILP32, and 5l for LP64.

   0x80000000L  ulong in lp32,  long in lp64.

 3. Bit fields are converted to int and sign-extended (or uint if too large) in expr.
   struct { uint b:19, r:13 }x;
   uint y = x.b<<13 // x.b first converted to int and sign extended!
   uint y = (uint)x.b << 13; // ok.

 4. Integer Arithmetic on ptr, ptr is trucated to int32.

   char *p="x";
   (int)p+1=='\0' // p is trunc to int32.
   (uintptr_t)p+1=='\0' // ok.

 5. Unprototyped func returning ptr is trucated to int32.

   // int login(...); -- implicit C decl.
   char *p = getenv("HOME");  // p is trucated to int32, crash.

 6. Saving ptr in int, losses upper 4 bytes of ptr.

   void *q;
   int p = (int)q; // truncated to int32

 7. Unions with long.

   union {
     long x;
     int  y; // on lp64, y gets lsb of x.
   }

 8. Print format specifier

   char *p="x";
   printf("p=0x%08x",p); // losses msb.
   printf("p=%p",   p); // ok.

   long x=LONG_MAX;

   printf("x=%d ",x); // truncated, wrong.
   printf("x=%ld",x); // ok.

 9. Casting *int into *long.

   int  *i;
   long *p;
   p+1; // p+=4 on ilp32
   p+1; // p+=8 on  lp64
   p = (long*) i; // p misaligned pointing to *i and *(i+1).
   i = ( int*) p; // i pointing to lsb of p.

 10. Unsigned bit fields (default sign and alignment).

   struct { b:12, int:0, d:5 } x;  // b is unsigned in lp64, signed
 in ilp32.
   // :0 affects alignment in ilp32, no effect in lp64.

 11. LP64 Perf Problems

   Larger exe, need more TLB, cache, mem (wrt same src compiled as ILP32).
   Long div slow in lp64, eg. 1L/9.
   Int Array indexes are sign-ext on every use, need more instruction in LP64, eg. int i; a[i];

 12. Expression sizing:

  Example.


   int x,y;
   long z = x + y;  // x+y done in 32bit, then assignment will resize result for z.

  Example.

   ulong y = (1 <<32); // overflows in lp32 and lp64.
   ulong y = (1L<<32); // overflows in lp32, ok 2^32 in lp64.

 13. Promotions:

   result = a op b;

   // 1. type of tmp is of max of a and b.
   // 2. tmp = a op b
   // 3. promote tmp to type of result, ptmp.
   // 4. result = ptmp;

  Example.

   long l=-1; uint i=1;
   if( l > i ) printf("ilp32"); // -1 is promoted to uint32.
   else     printf("lp64");  // both promoted to signed 64.

   if( l > (long)i ) // fix
     ...;

  Example.

   main(){  // k is -1 in LP32, 4294967295 in LP64
     int i = -2;
     unsigned int j = 1;
     long k = i+j;
     printf("int %d+ uint %ld= long k is %ld\n",i,j,k);
   }

 15. Prototype:

   // #include <stdlib.h> missing.
   int *i = malloc(sizeof(*i));
   // Ok in lp32, Crash on lp64 because
   // malloc will return 32bit int value because of missing prototype.

 }

 ////////////////////////////////////////////////////



 Examples

  > cc +DA2.0W +w1 +M2 enquire.c    # +w1 for warnings, +M2 for 64bits

  long double=128 bits

   # Sort LP64 warning by problem:


   > make 2>&1 | grep LP64 | xsort 'warning:\s+\S+:' | align warn > lp64.log
   > vim lp64.log
   :set isk+=46  " Make . part of tag, so file.c is a keyword for tags"

   > emacs lp64.log

   > M-x compilation-minor-mode
   > M-x next-error
   ; if cant find files, set
   ; (setq compilation-search-path '("dir1" "dir2")) C-xC-e

 /714: Function "malloc" called with no prototype or definition in scope
 /724: Initialization converts default int return type to pointer
 /722:.*cast converts 32 bit constant to pointer
 /725:.*cast converts 32 bit integer to pointer
 /727:.*cast truncates pointer into 32 bit integer
 /729:.*converts int* to long*
 /732:.*different types treated as unsigned
 /740:.*casting to a smaller size may truncate
 /530:.*casting from loose to strict alignment
 /720:.*Assignment may overflow

 // Example

 > catt -n x.c

 1  void main(){
 2   int *x, *q, i;
 3   long l=-1;
 4   x = malloc(sizeof(int)*10);
 5   (int*)i;
 6   (int)  &i;
 7   (long*)&i;
 8   i = l;
 9  }

 hp64> cc  +DA2.0W +M2 +w1 -Aa x.c

 /*
 line 4: warning 714: Function "malloc" called with no prototype or definition in scope.
 line 4: warning 724: LP64 migration: Assignment converts default int return type to pointer "x".
 line 5: warning 725: LP64 migration: Cast converts 32 bit integer to pointer.
 line 6: warning 727: LP64 migration: Cast truncates pointer into 32 bit integer.
 line 7: warning 530: LP64 migration: Casting from loose to strict alignment: Resulting pointer may be misaligned.
 line 7: warning 729: LP64 migration: Cast converts int* to long*.
 line 8: warning 720: LP64 migration: Assignment may overflow integer "i".
 */

 /////////////////////////////////////////////////

 A. Sizes
     Ansi sizeof() is always unsigned, try:
     if( 0-sizeof(int) > 0 ) printf("See: 0-sizeof(int) is unsigned!\n");

   ILP32: win32 hp11 solaris linux

   ANSI C Integer Requirements, see C book.  moshtag=ansi_int

     sizeof(int) >= sizeof(short)  >= 2.
     sizeof(long) >=sizeof(int)
     sizeof(long) >= 4.

   LP64:  hp11_64 solaris_64 (assume default for 64bit).

     New: sizeof(long)==sizeof(void*)==8, rest unchanged.

   P64:  win64

     New: sizeof(void*)==8, rest unchanged.

 //////////////////////////////////////////////////////////

 Linux64                       moshtag=linux64

   char = 8 bits, signed
   short=16 int=32 long=64 float=32 double=64 bits
   long double=128 bits
   Type size_t is unsigned long
   ALIGNMENTS
     char=1 short=2 int=4 long=8 float=4 double=8 long double=16
     char*=8 int*=8 func*=8
   short: BA
   int:  DCBA
   long:  HGFEDCBA
   Maximum long = 9223372036854775807 (= 2**63-1)
   Minimum long = -9223372036854775808
   Maximum unsigned short = 65535
   Maximum unsigned int = 4294967295
   Maximum unsigned long = 18446744073709551615
   PROMOTIONS
     unsigned short promotes to signed int
     long+unsigned gives signed long

 HP64 ALIGNMENTS

   char=1 short=2 int=4 long=8 float=4 double=8 long double=16
   char*=8 int*=8 func*=8

 

1 Comments:

Blogger Saurabh Chokshi said...

How would you display the pointer data in Hex? For example,

long n = LONG_MAX;
printf("%08x \n",n);

It says format ‘%08x’ expects type ‘unsigned int’, but argument 3 has type.

If I have unsigned short * pointer, I want to use this with void * pointer,

short *ptr;
void *aligned_ptr;

ptr = (short *)aligned_ptr;

In this case, I want to store 16-bit register value in that pointer.

Could you please give me some solution?

3:40 PM  

Post a Comment

<< Home