What about Tiger stability ?

PPC was designed from the start so that you can run 32 bit programs on 64 bit chips, so I think it should work out ok.
 
Viro said:
A fundamental change is the size of pointers. The word size is no longer 4 bytes (32 bits) but 8 bytes (64 bits). This alone has the potential to break tonnes of C apps that are out there.

Nope... as I said, you do 32-bit arithmetic on a 64-bit PPC, and all it does is do the math in 64-bit, and shave the top 32-bits off. r0 (general register 0) used to be 32 bits, now is 64 bits, for all operations, but the instruction tells it how many bits are supposed to be in the result. So a 32-bit pointer in PPC scales to 64-bit for the CPU. The OS needs to adapt slightly, but each app is isolated from each other for the most part, so the OS just needs to expand each app's memory partition from 2GB to something larger on a 64-bit processor.

To my knowledge, you can't mix 32 bit and 64 bit libraries/kernel extensions. I'm not familiar enough with PPC64 to be authoritative on this, but with the AMD64 architecture, you could run existing 32 bit applications on a 64 bit OS, but you needed the supporting 32 bit libraries to be present. This ended up causing Linux systems to be in a real mess having to maintain two separate sets of libraries.

In this case, you can mix, as long as you aren't trying to run 64-bit stuff on a 32-bit processor. Since every register is 64 bits, instead of being addressed as both 32 and 64 bits, a 32 bit app doesn't do anything weird with the registers that could cause serious headaches. So, the '64-bit' Tiger is really the needed OS and library support to allow a 64-bit program to pass 64-bit pointers and such to the OS and libraries.

(PPC design is one of the things I have researched, as we had to make a choice for a microcontroller for our final project for our undergrad study... we wound up choosing ColdFire, but PPC was a close second)
 
Krevinek said:
Nope... as I said, you do 32-bit arithmetic on a 64-bit PPC, and all it does is do the math in 64-bit, and shave the top 32-bits off. r0 (general register 0) used to be 32 bits, now is 64 bits, for all operations, but the instruction tells it how many bits are supposed to be in the result. So a 32-bit pointer in PPC scales to 64-bit for the CPU. The OS needs to adapt slightly, but each app is isolated from each other for the most part, so the OS just needs to expand each app's memory partition from 2GB to something larger on a 64-bit processor.

I think you misunderstood what I said, which isn't hard since many in real life do anyway ;)

The word size will increase with the move to 64 bits from 4 bytes to 8 bytes. This can cause problems and it did especially when porting apps that are written in C. Other languages will be affected, but C and perhaps C based languages suffer the most.

Let's illustrate this with an example. We'll define a structure and dynamically allocate some memory for it.

Code:
//define a structure for a simple inventory system
typedef struct _PRODUCT {
  int _id;
  char name[80];
  int _price;
} PRODUCT;
...
...
...

//somewhere else in your code you allocate space for an array of 100 _PRODUCTS
PRODUCT *myArray = (PRODUCT*)malloc(100 * sizeof(PRODUCT));

//Do your thing with the array
...
...
...
//Store your array in a binary file
FILE *fp =  fopen("data.dat","wb");
for(int i = 0; i < 100; i++)
  fwrite(&myArrau[i], 1, sizeof(PRODUCT), fp);
fclose(fp);

//program continues
...
...

Now on a 32 bit program, this size of the PERSON is 80 + 4 + 4 = 88 bytes. However on a 64 bit program, this then becomes 80 + 8 + 8 = 96 bytes. This poses a heck of a lot of problems especially if you're the type of programmer who loves to directly manipulate arrays using pointers (you will if you are a C programmer who labours under the false assumption that array access is slow compared to pointer access).

Not only that, it breaks compatibility with what ever *data* files you have. Back to the code again, that's how many programs in circulation store data. It's simple to write, very quick, and very unportable! A 64 bit program will incorrectly read the data file generated by a 32 bit program since it's assuming the size of PERSON is larger and so it will read 96 bytes at a time instead of the actual 88.

There are many ways a program breaks when moving to an architecture that has larger 'bitness' (for lack of a better word). I cut my teeth trying to get my old code ported from x86 to x86-64. Granted, I should have coded better and strived to make my code more portable, but I was only using the techniques that I had learnt.
 
That seems like more of a problem with reading in files, rather than a pointer problem. If you do myArray++ the pointer will still advance 96 bytes to the next array element(hoping I remember my C correctly :rolleyes: ).

I think the best way to store data is to do it like in a bitmap. The first few bytes(forget exactly how many) tell you how large each pixel is(16/32 bit) and then you read the file accordingly.

Then you don't have to worry about the system changing stuff on you, you can always know how many bytes to read.
 
Yeah, it wreaks havoc if you let it. The thing is, if you wrote that code and compiled with gcc 3.3 on a 32-bit system... it will NOT behave any differently on a G5. The pointers were manipulated using 32-bit math instructions, so as I said, you get a 64-bit number with only 32 bits of information. You store using the 32-bit store instructions, which does NOT change the size of the data structure. Only when you attempt a 64-bit compile does it pose problems.

(I actually am a control freak in my code, so I never use int unless it is a local variable I just don't care about)
 
Except in C, since there aren't any generics, it is common to cast things to void (argh!!!) and manipulate them. This is where the problems come in.
 
Except in C, since there aren't any generics, it is common to cast things to void (argh!!!) and manipulate them. This is where the problems come in.

(void) typecasts are invalid constructs in C, as void is the 'lack of a type'. (void *) is still valid, but is still a pointer, just like an int pointer or a long pointer, or every other type. In fact, a pointer is always the same size: the native size of the processor the code was compiled for. A 32-bit app running on a G5 uses 64-bit pointers, but with only 32 bits of data (I am starting to repeat myself here). This is because the 32 bit instructions shave excess bits off the top.

Plus, anyone caught writing pointers to a file should be fired on the spot in my book. This is where problems really occur, when data is written (you said so yourself). Pointers are temporary variables at best, and do not need to exist beyond the scope of the program's memory space. Hence, they pose no problem.

Additional: Plus, it isn't hard to 'shrink' data when serializing it to a file. If you designed your file structure well, this will not bite you because you are writing a record using fixed length values, rather than relying on sizeof(int). I wouldn't want to keep around a programmer doing that practice either, as we never know what platform some code might wind up running.
 
Krevinek said:
In fact, a pointer is always the same size: the native size of the processor the code was compiled for. A 32-bit app running on a G5 uses 64-bit pointers, but with only 32 bits of data (I am starting to repeat myself here). This is because the 32 bit instructions shave excess bits off the top.

Actually this is wrong. When the G5 is running 32bit code it uses 32 bit pointers. You seem to be stating that doing something like loading a pointer into a register would transfer 64 bits of information, that is incorrect it only transfers 32 bits. Also you cannot just ignore the upper 32 bits as that would change the semantics of the bit shift operations as well as operations like add. Think about it if you add two 32 bit values to get a 33 bit value to work properly the processor has to set the overflow or carry bit. If it was just ignoring upper half of the word this data would be lost when it did not overflow.

In order to do the right thing the processor has to know if it is performing a 32 bit operation or a 64 bit operation. I believe that this is accomplished in the PPC by setting a bit in the processor's control status register for pointers. This is one of the registers that is updated by a context switch so it is possible to be running a 32bit process at the same time as a 64 bit process.

So just to repeat myself a 32bit app uses 32bit pointers period. My only guess is that you may be confused about how virtual address translation happens, but that is another topic.
 
He means that the other 32 bits are unused and are set to zero. There's 64 bits in the register and you can't change that, so it basically ignores the upper 32 bits and uses the lower 32 bits for the pointer.

Same thing if you use a "short" on a 32 bit CPU. The upper 16 bits are useless in that case.
 
Captain Code said:
He means that the other 32 bits are unused and are set to zero. There's 64 bits in the register and you can't change that, so it basically ignores the upper 32 bits and uses the lower 32 bits for the pointer.

Same thing if you use a "short" on a 32 bit CPU. The upper 16 bits are useless in that case.

Yes, thank you. The PPC spec states how instructions work. Every instruction (well, nearly every instruction) specifies how many bits you want the result to be in. So when you load a number into a register, you have to specify how many bits are going to be loaded. (8, 16, 32, and now 64 are available or have been, since the FPU has been 64-bit for quite awhile)

After that, once in the register it is treated as if it was a native-size number. So if I load an 8 bit number on a G5, the processor treats it like a 64 bit number while in the register. Math instructions even require that you tell it how many bits you want in the result. All the original math calculations are done at the native size, and then 'chopped' for the desired size, and other bits are properly set from the remaining data, wherever it needs to come from to be accurate. So when you do pointers (which are just unsigned numbers!), the CPU instructions still require that operations on them declare the desired bit size for the result. So, in fact when you run a 32-bit app on a G5, it is native from the standpoint that there is no 'emulation' involved. It simply tells the G5 it wants the results as 32-bit results on the pointers, although all the actual math is done at the native 64-bits behind the scenes. No bits need to be set, no funny stuff, it just works.

Another example is that the PPC FPU is double-precision, so even if you do floating point math on two single-precision numbers, it does the math as double precision internally, and chops the result to a single-precision number if that is what the instruction said to give the result as. The whole design of the PPC is that it could be expanded to larger register sizes without impacting how previous scales worked, or incurring massive performance penalties.

I have this odd urge to write a 16-bit PPC app in assembly now... :)
 
Krevinek said:
(void) typecasts are invalid constructs in C, as void is the 'lack of a type'. (void *) is still valid, but is still a pointer, just like an int pointer or a long pointer, or every other type. In fact, a pointer is always the same size: the native size of the processor the code was compiled for. A 32-bit app running on a G5 uses 64-bit pointers, but with only 32 bits of data (I am starting to repeat myself here). This is because the 32 bit instructions shave excess bits off the top.

I meant void pointers. That's the only time anyone would typecast anything to void and I thought the meaning would have been obvious to anyone who knew basic C.

A pointer on the G5 is going to be 32 bits *if* the G5 is running a 32 bit OS. Don't believe me? Do sizeof and tell me what you get. Lurk has explained it very well and I'll leave that to him.

Plus, anyone caught writing pointers to a file should be fired on the spot in my book. This is where problems really occur, when data is written (you said so yourself). Pointers are temporary variables at best, and do not need to exist beyond the scope of the program's memory space. Hence, they pose no problem.

That's non sequitur. No one writes pointers to files unless they have no clue about what they're doing. The problem is with binary files, and especially with structures, the move to 64 bits will cause the structures to become larger. This is what causes problems, and I thought this was explained clearly in the code I posted. The code actually demonstrated actual writing of data to a file, not the pointer location.

Additional: Plus, it isn't hard to 'shrink' data when serializing it to a file. If you designed your file structure well, this will not bite you because you are writing a record using fixed length values, rather than relying on sizeof(int). I wouldn't want to keep around a programmer doing that practice either, as we never know what platform some code might wind up running.

This isn't going to work. If you have a structure in the listing that I posted, moving to 64 bits will cause the structure to grow in size. From 88 bytes to 96 bytes. Reading/writing 88 bytes when the size of the structure is 96 bytes is going to bite you in the behind. Hard.

This is why the code at the very least needs to be changed. The ints in the structure will have to be converted either to shorts or longs, whatever suits it. Incidently, a classic example of this problem is the PCX loading routines that were very common among budding game programmers during the DOS days.

DOS was 16 bits, and the size of ints were 16 bits. Migrating the PCX loading code to Windows often caused problems because ints were 32 bits in size, and as a result the headers of the PCX files weren't read in correctly, causing the programs to crash. I learnt this the hardway and I finally figured out the problem the hard way and finally managed to solve the crashes by converting the ints in the PCX header structure into shorts. Do a google search for the PCX loading routines for 16 bit DOS apps and compare that with the PCX loading routines for 32 bit DOS apps. The bit you're interested in is the PCX header structure, and how it needed to be changed to cope with the migration.

Writing portable binary files for different platforms is very difficult and a lot of considerations need to be taken into account. If you can, read the chapter on "Offline Data Storage and Retrieval" from the book "C Unleashed" which is a very good source of info into this topic. It goes into all the pitfalls of writing portable code and the solution ain't as easy as you're making it out to be.
 
Captain Code said:
He means that the other 32 bits are unused and are set to zero. There's 64 bits in the register and you can't change that, so it basically ignores the upper 32 bits and uses the lower 32 bits for the pointer.

Same thing if you use a "short" on a 32 bit CPU. The upper 16 bits are useless in that case.

Fully agree. However it is non sequitur. The size of the registers has no real bearing on what we're talking about, when addressing portability (i.e. pointer arithmetic and binary File IO).
 
Viro said:
I meant void pointers. That's the only time anyone would typecast anything to void and I thought the meaning would have been obvious to anyone who knew basic C.

A pointer on the G5 is going to be 32 bits *if* the G5 is running a 32 bit OS. Don't believe me? Do sizeof and tell me what you get. Lurk has explained it very well and I'll leave that to him.
[\QUOTE]

sizeof() is computed at compile time. An int on an app you compiled 2 years ago is still gonna be 32-bits when it runs on Tiger. It changes when you recompile using a compiler set for 64-bit ints. People aren't aware you have to turn on 64-bit ints in GCC to get a sizeof(int) to return 8.

That's non sequitur. No one writes pointers to files unless they have no clue about what they're doing. The problem is with binary files, and especially with structures, the move to 64 bits will cause the structures to become larger. This is what causes problems, and I thought this was explained clearly in the code I posted. The code actually demonstrated actual writing of data to a file, not the pointer location.

Then why bring up void pointers as a problem? Because clearly it isn't. I was addressing a comment you made about pointers being a problem. They aren't. The data size is.

This isn't going to work. If you have a structure in the listing that I posted, moving to 64 bits will cause the structure to grow in size. From 88 bytes to 96 bytes. Reading/writing 88 bytes when the size of the structure is 96 bytes is going to bite you in the behind. Hard.

There are easy ways around this, and hard ways around this. Easy way would be to force the compiler to use 32-bit ints when compiling G5-only code. Hard way would be to change it to 'long', (search and replace, anyone?) or writing up a nice set of macros that let you more clearly declare the size you want... Heck, every platform has em. (int32, int64, int8, int16, etc) One of the first things I did when starting cross-platform development was to make one of these... I still keep it around. Either way, the hard way is something that should have been done from the start. I thought people learned from the 16->32 bit mess.

This is why the code at the very least needs to be changed. The ints in the structure will have to be converted either to shorts or longs, whatever suits it.

Yup, honestly not a big deal yet though unless you are going to start writing G5-only code soon.

Writing portable binary files for different platforms is very difficult and a lot of considerations need to be taken into account. If you can, read the chapter on "Offline Data Storage and Retrieval" from the book "C Unleashed" which is a very good source of info into this topic. It goes into all the pitfalls of writing portable code and the solution ain't as easy as you're making it out to be.

Yup, well aware of the pitfalls, but I don't personally find it 'very difficult'. Planning for endian-ness, especially in network apps is key. Planning for fixed record sizes is key, and int should NEVER be used to describe data that will hit the network or disk. Plus, a fair number of the solutions require no effort on the part of the programmer beyond making sure a couple compile switches are set right. You aren't going to be using 64-bit ints? No problem, don't enable em. You made it sound as if us programmers are being forced to move to 64-bit clean code, which is not true. The "performance increase" from moving to 64-bit isn't gonna do jack for most applications either.

Although, to try to move back onto the topic to some extent... your apps from 10.3 will be about as stable on 10.4 as 10.2 apps are on 10.3... in other words... nobody needs to worry about Tiger breaking anything that wasn't broken to begin with (using undisclosed APIs).
 
Krevinek said:
Yes, thank you. The PPC spec states how instructions work. Every instruction (well, nearly every instruction) specifies how many bits you want the result to be in. So when you load a number into a register, you have to specify how many bits are going to be loaded. (8, 16, 32, and now 64 are available or have been, since the FPU has been 64-bit for quite awhile)

If the upper bits were just ignored then the only instructions that would prompt for this information would be the load and store operations. The fact that other operations require the similar declarations should imply that they matter.

Krevinek said:
After that, once in the register it is treated as if it was a native-size number. So if I load an 8 bit number on a G5, the processor treats it like a 64 bit number while in the register. Math instructions even require that you tell it how many bits you want in the result. All the original math calculations are done at the native size, and then 'chopped' for the desired size, and other bits are properly set from the remaining data, wherever it needs to come from to be accurate.

That is a crude approximation of what is going on but still wrong. The problem is that not all operations translate into truncated versions for smaller data types. The rotate and shift operations are good examples of this as is the task of setting the condition bits correctly. It is more efficient to have a 32bit add instruction that itself ignores the high order 32 bits and properly sets the conditions than to use a 64bit add and then try to calculate the proper condition form the resulting garbage in the high order half of the register. This is why the size is encoded in the opcode, so it can do the right thing.

Krevinek said:
So when you do pointers (which are just unsigned numbers!), the CPU instructions still require that operations on them declare the desired bit size for the result. So, in fact when you run a 32-bit app on a G5, it is native from the standpoint that there is no 'emulation' involved. It simply tells the G5 it wants the results as 32-bit results on the pointers, although all the actual math is done at the native 64-bits behind the scenes. No bits need to be set, no funny stuff, it just works.

The model you are proposing is the one that is emulation, where 32 bit operations are emulated by 64 bit operations and some magic fix-ups afterward.

I am pretty sure that bits also need to be set to get the proper behavior in the face of traps, function calls, and address translation. Now to be honest I don't have time to dust off my PPC architecture books to look it up but I was under the impression that there was a control bit to distinguish operations in 32bit vs 64bit mode. IIRC while it is OK to do 64bit computations in 32 bit mode the high order bits will not survive a system call so you need to save them off before hand, although that is SOP anyway. I could be wrong there but I don't have time to dig for the real answer is has been a while since I looked into it.

Krevinek said:
Another example is that the PPC FPU is double-precision, so even if you do floating point math on two single-precision numbers, it does the math as double precision internally, and chops the result to a single-precision number if that is what the instruction said to give the result as. The whole design of the PPC is that it could be expanded to larger register sizes without impacting how previous scales worked, or incurring massive performance penalties.

The reason that you can round off floating point values is that all you are doing is dropping precision. The way that floating point conditions are handled is totally different that integer conditions, you are comparing apples to oranges.

Krevinek said:
I have this odd urge to write a 16-bit PPC app in assembly now... :)

RSA would be a good candidate as I am sure it will illustrate the difficulties associated with the emulation approach. :)

I guess my whole point is that your idea of chopping is kinda right but not quite. There is some complexity there that you oversimplified and brushed under the rug. I think that I agree with the overall point you were trying to make it is just that you were starting from a faulty premise to get there.

Have fun,
-Eric
 
Back
Top