Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2006-110
$eventId
ID of event: wwdc2006
$eventContentId
ID of session without event part: 110
$eventShortId
Shortened ID of event: wwdc06
$year
Year of session: 2006
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: ...

WWDC06 • Session 110

64-bit Overview

Application Technologies • 50:34

Today's software uses increasing amounts of memory - and many products are pushing the boundaries of 32-bit addressing. Now in Leopard you have a full 64-bit application stack for both PowerPC and Intel-based Macintoshes. Learn the what, why, and how basics of 64-bit development, and understand what you need to know to scope out your 64-bit development efforts today.

Speaker: Ali Ozer

Unlisted on Apple Developer site

Transcript

This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.

Welcome to the 64-bit Overview session. My name is Ali Ozer. I'm the manager of the Cocoa Frameworks team. I've also been coordinating the 64-bit effort across Leopard for the past year, and that's why I'm speaking to you today. Okay, so today's talk, we're first going to cover what 64-bit is, then we're going to look at the 64-bit landscape, we're going to talk about what 64-bit means to you, then we're going to talk a bit about the porting and pitfalls that you might encounter during the porting, and have some closing thoughts at the end. So what is 64-bit? 64-bit is support for 64-bit address space. That's the most basic definition. 32-bit can support 4 gigabytes, which is about 4 billion bytes of course, while 64-bit extends that to 16 exabytes, which is 16 billion billion bytes, or 16 quintillion.

Now, the important thing about 64-bit support is that it's concurrent use of 64-bit. There are already some 32-bit applications out there, such as iMovie, Final Cut Pro, database programs, etc., that let you manage more than 4 gigabytes of data. However, the way they do that is by loading those files into memory in chunks, and they sort of do their own paging if needed, and they never load more than 4 gigabytes into memory at once. With 64-bit support, you get concurrent access to potentially 64-bit's worth of address space.

Now let me give you sort of a scale of what 64-bit means. Now 4 billion 32-bit is a pretty large number. If you started counting to 4 billion today, it would take you about 200 years to get there. On the other hand, 4 billion is not really such a big number. It is not enough to represent the world population. It's not big enough to represent the U.S. debt.

It's not big enough to even represent the U.S. deficit. It's not big enough to represent Bill Gates's fortune. On good days, it's not enough to represent Steve Jobs's fortune. And it's not enough to represent the number of pages indexed by Google or the number of hamburgers sold by McDonald's.

So really, 32-bit is very inadequate, in fact, for many purposes. On the other hand, 64-bit is huge. If you started counting to 16 quintillion, and let's say you started the day the universe started, which was about 14 billion years ago, you'd only be about 1.5% of the way there today. So it's a pretty big number.

Okay, so let's talk about the data type changes in 64-bit. Since it's a 64-bit address space, and since you have concurrent access to this address space, pointers go from 32 to 64 bits. In addition, the long data type goes from 32 to 64 bits, and all other data types remain the same.

And that's why this is referred to as the LP64 model - longs and pointers go to 64-bit. Now the interesting thing about the LP64 model is it is the model that other Unixes use, so therefore it is compatible with a lot of open source software that you will find out there, which of course is a good thing.

And here is a chart of the data type changes. In ILP32, which is where we are today, integers, longs, and pointers are 32-bit. These are the data types. And in the LP64 world, they all remain the same size, except long goes to 8 bytes and pointer goes to 8 bytes as well. Now one interesting thing to note here is I said other Unixes are LP64, but Windows in the 64-bit world is LLP64.

What that means is Windows programs under 64-bit have longs which are 32-bits, but long longs which are 64-bits. So if you have part of your source space which is cross-platform that needs to go in Windows as well, that's something to keep in mind. You might want to avoid the long data type in those cases.

So given that a pointer is 64-bit, that sort of implies that the process is either 32-bit or 64-bit, but it cannot be both. There is no notion of mixed mode where a 64-bit process can load 32-bit plugins or libraries. So this means all libraries, frameworks, plugins needed for your application have to be 64-bit for your application to run as 64-bit.

So here's the way the world looked about a year and a half ago. It was nice and simple. We had PowerPC, text-edit, 32-bit, and it in turn depended on a 32-bit PowerPC, Cocoa, and that depended on application services, core services, lib system, down what we refer to as the framework stack. And everything was 32-bit PowerPC, and that's all we had.

Now with 64-bit, you basically have another parallel stack of frameworks. And the 64-bit text-edit, to run a 64-bit text-edit, you need a 64-bit Cocoa, which needs application services, core services, lib system, etc. And this is very similar really to the way we did the Intel transition when we introduced it a year ago.

32-bit Intel text-edit requires a 32-bit Intel Cocoa, and so on down the stack. They all have to be built for that architecture. And of course, yesterday we introduced 64-bit Intel as well, and that's also yet another stack. Now of course, the interesting thing to know that is although these are all separate architectures, they are all bundled together in the universal packaging format. So it is actually one binary that contains four separate architectures.

Now, so let me give you a perspective on what we're doing. The 64-bit effort requires that all libraries, frameworks, etc. be available as 64-bit. In that way, it's sort of similar to the widening of the Panama Canal. Panama Canal used to be, when it was first built, it was wide enough for most ships, or all ships of today, but these days it no longer is wide enough for the large super tankers or the larger cargo ships. And there is an effort, or a desire, to get the Panama Canal widened.

Now one interesting thing about this effort is that it's not an effort you can do 95%. Because if you did, this is what it would look like. And that big ship would get through most of the canal and still get stuck. So really, the effort, doing something like the Panama Canal, requires that you do the job 100%. Similarly, if you're an application that wants to run a 64-bit, you need to make sure everything you depend on is available to you as 64-bit. If only 95% of the things are available, your application will not be able to run.

So, and that's, you know, that's what you need. So that's, you know, that's sort of a cheerful realization. So let's think about why we're doing, why we're doing all this, why we're going to all this effort. Why 64-bit? Now one reason is developers. There are certain developers, certain applications where you're already running into limits of 32-bit and telling us about it. You have applications that really are pushing the limits of 32-bit and you know you need 64-bit to get those applications running.

Similarly, users are also telling us the same thing. They hear from developers that there are certain applications which really need more than 32-bit to be effective. Another reason for doing 64-bit is competition. Microsoft already has 64-bit version of Windows XP out, and there is 64-bit version of Vista on the horizon. So this is something which again developers and users point to as something that they need to have on Mac OS X as well.

Hardware is another very important reason for doing 64-bit. We already have 64-bit capable hardware out there. 64-bit chips are available, and on this hardware, like especially Intel 64, there is more registers available in 64-bit, there are wider registers, there is the instruction relative addressing mode, which actually helps out. So therefore, there are some hardware benefits to running in 64-bit.

Not to mention the fact that you can put up to 8 or 16 gigabytes of memory into these machines. So really having an application which can access just the piddly 4 gigabytes on your 16-gigabyte hardware, that seems pretty limiting. So today's hardware is already pushing the limits, so the software has to catch up in some way.

[Transcript missing]

So let's look at the 64-bit landscape. Let's look at where we are and where others are in 64-bit. Now in Tiger, only libsystem and accelerated frameworks are available, and it's only C and C++. We don't have support for Objective-C, and we don't have support for Java in Tiger.

So what this means is in Tiger you don't have Carbon, you don't have Cocoa, you don't have Quartz, you don't have core services, you don't have core foundation. So a lot of the user interface level functionality is missing. So this is useful for command line tools, but really no GUI applications.

And we recommend that to create a GUI application you create a 64-bit backend, and you create a 32-bit frontend, and you basically communicate using IPC. And that's what Mathematica, for instance, has done. That's how Mathematica works in the 64-bit mode. It's got a 32-bit frontend with a 64-bit backend.

Now in Leopard, of course, we're enabling any Cocoa or Carbon application to run as 64-bit. And this also includes Java. However, we're making Java available only for Intel 64-bit. Now let me talk about what Microsoft has done with Windows. Its 64-bit version of Windows XP, which is known as XP x64, was released more than a year ago, in April of last year.

And it's really marketed at advanced users. It's not something you can go easily get at your local Costco and install on your machine. You have to jump through a few hoops. But it's not been wildly successful, and that's according to Microsoft. One of the reasons for this is there are compatibility issues. 64-bit Windows require 64-bit drivers, which originally were a very small number, and since then there's been new ones introduced. 16-bit DOS and Windows applications are unsupported, and also some 32-bit applications don't work.

Because really, under 64-bit Windows, 32-bit applications are running under this WOW64. So, it's a Windows on Windows 64 environment, which Microsoft used the term emulation for. And, you know, that's good enough for many, many apps, but not all. So some 32-bit applications do not work under XP x64.

And this is a quote from Paul Thoreau of Supersight for Windows, a recent quote. And you know, he said, "XP-X64 is doomed to failure because of hardware and software incompatibilities." And truth is, I don't think Microsoft really also put a lot behind this product. For Microsoft, their intent is to make 64-bit mainstream with Vista. And that's where they're really looking forward to, it seems like. In Vista, 64-bit is not a separate product.

However, it is still a separate DVD. And I think that's the way it's going to be shipping. Most editions of Vista will come with 64-bit version and 32-bit version in the box. And at the time you install, you choose which one you want to install. So the user has to make a choice.

Am I installing 32 or 64? And from what I understand, it will have some of the same sort of compatibility issues as XP-X64. And of course, also some benefits as well that 64-bit brings to the environment. Now let's compare that to Leopard. In Leopard, it's a single install. You know, you buy your Mac OS ...and you install Leopard.

And it's 64-bit capable, assuming your hardware is 64-bit capable. 32-bit and 64-bit applications coexist. That's because I showed you, you know, each application runs in its own framework. It uses its own binary. So there's no emulation or anything like that going on here. A 32-bit application runs fully well on a 64-bit machine, and so will a 64-bit application.

So it's full compatibility. So the good thing here is that... ...the user's choice for 64-bit is whether they bought a 64-bit capable machine or not. And, you know, you as the developer decide whether 64-bit is right for you. But even when you ship a 32-bit app, you know, there's not this danger that, "Oh, because the user installed 64-bit, my app won't run." So we believe, you know, this gives you somewhat more of a choice in choosing 64-bit.

So, okay, the OS has 64-bit. What does this mean for you? Okay, so there are a number of considerations for moving to 64-bit. And most of them are performance related, because 64-bit is really touted for performance most of the time. Now, if your application needs large address space, the answer is a definite yes.

I mean, this is the example we saw with the DNA demo Simon showed yesterday. Simon Patience showed it. I don't know if you saw it, but basically a 32-bit version took many minutes to go through the frames, because it had to load a 4-gigabyte file in chunks and process it, while the 64-bit version could load the whole 4-gigabyte file and just run through it and generate a frame every second. I mean, there are the orders of magnitude faster for the 64-bit case. And there are many applications like this, so if you have one of those applications, the answer is yes. 64-bit is definitely for you.

Now, how about others? So there are a few other performance considerations. One is that when you're running in 64-bit mode, your pointers are larger, some of your data types are larger, so you're going to be using a little more memory. But you know, it's probably not that much more.

In reality, most of the memory used by applications tend to be in window backing stores and images, which don't change size. So this additional memory impact isn't that bad. On the other hand, there's performance benefits. As I mentioned earlier, there are hardware benefits to running in 64-bit. On Intel, you get more registers, there are wider registers, you get addressing mode.

So with those, compute-bound, CPU-bound applications can actually get a good boost from running in 64-bit. So if your application is very much compute-bound and you're really interested in getting every percentage of CPU out of the machine, 64-bit might be interesting for you as well, although you might not be handling lots of data. One other consideration is the overall memory impact of 64-bit to the system.

Now let's look at the earlier framework stack we had. You have the stack of 32-bit frameworks. Now when you run 32-bit text edit, it pulls in the 32-bit stack of frameworks. When you go ahead and run another 32-bit application like Safari, it also uses the same frameworks. Of course, it might use some other frameworks as well, but the basic system ones are the same ones.

So 32-bit mail and 32-bit iChat and all other 32-bit applications all use the same stack of frameworks. Now let's assume you ran 64-bit chess. And by the way, chess on your system is actually 64-bit today. Of course, it's also 32-bit and that's why it runs on your laptops, but if you run it on a 64-bit machine, it will run as a 64-bit application.

So when you run 64-bit chess, it pulls in the 64-bit architectures from each one of those frameworks. So clearly, there is a little bit of an additional memory over time. So there is an additional memory overhead here. And so therefore, 64-bit in a very low memory machine will cause some memory pressure. So that's yet another consideration to keep in mind.

So with those in mind, our answer is that in Leopard, if your application is not going to benefit from 64-bit, it's probably not a good idea to move to 64-bit. However, post-Leopard, the answer changes. And we believe the answer is, yeah, likely someday. We don't believe that 64-bit is going to remain a niche for specialized applications going forward. At some point, we believe, most applications will probably become 64-bit. It's almost like today, you do not see 16-bit applications anywhere, really. They're really part of the history, because the systems are optimized for running 32-bit applications.

And that's probably going to be the case. Five, ten years from now, do you want to be the only 32-bit app in a block of 64-bit applications? That might be the scenario. Now we don't have a crystal ball, we don't know what date this is, so on, so it's hard to tell, but we believe this is the case.

Now, if you have a framework, a library, or a plugin, and especially if you have a public framework, or a public library, public plugin, like a plugin that goes into, say, WebKit or some other plugin mechanism, we really do want you to move to 64-bit as soon as possible, because by making your product, your framework, library, or plugin, available as 64-bit, you'll be enabling others who might be thinking of moving to 64-bit themselves. So, just like we're making the system libraries available as 64-bit, we'd ask you to consider doing the same thing as well.

Now let's talk about levels of 64-bitness. This is like the levels you attain as you go through the conversion process. The very first level is the 64-bit wannabes. And these are who can build their applications as 64-bit. What this means is you open up your project, you check that checkbox, and you deal with the warnings and the errors, and eventually your project builds as 64-bit. Congratulations, you're now a 64-bit wannabe.

Of course, that's not where you really wanna be. The next level is the 64-bit novice. And that's when you can actually run your application as 64-bit, which might require some debugging. Now, believe me, it's a real thrill to have your application come up as 64-bit. It's probably the same as the thrill you get when you ran it, and you hopefully did, as running your application on Intel. You have just conquered a brand new architecture. Congratulations. But again, novice is not where you wanna be either. You wanna go to a few more levels above this.

There's a 64-bit apprentice. Here, not only does your application run under 64-bit, it can run with addresses above 4 gigabytes. Now, why is this interesting? One of the most common errors that you'll see in 64-bit applications is truncation of addresses, where somebody will take an address, put it into a 32-bit quantity, and then extract it. Now clearly if the address is below 4 gigabytes, that will work. But once you start managing addresses above 4 gigabytes, that will start to truncate addresses and causing your app to crash.

So therefore this might be a harder level to attain than just novice. Now the good news is When you build your application for 10.5, at least on Intel 64, we build with certain options that make your application run with addresses above 4 GB out of the gate. So therefore by the time your application runs, it's already an apprentice. On PowerPC the situation is a little more complicated, and I think we'll go into it in tomorrow's in-depth talk.

But again, a 10.5 built app will have most of the addresses above 4 GB. And once you get your application running, it will be an apprentice. But for instance, if you had a Tiger-based 64-bit command line tool, and you run it on Leopard, it's not yet an apprentice because it doesn't have these options enabled. Now apprentice is not really where you want to be either. You really want to be the 64-bit master.

I mean, probably the important reason you went to 64-bit is that you want to handle large amounts of data. And you've proven yourself a master of 64-bit which can actually manage more than 4 GB of data. So why is this hard to achieve? Well, maybe not hard, but why might it involve difficulties? I don't want to discourage anybody here. Types like int, Sint32, uint32t, et cetera, these are all 32 bits. And they're 32 bits even in a 64-bit environment.

If your application still uses these types and uses APIs with those types, you will only be managing up to 4 GB of data. So really to make your application fully 64-bit capable, you need to switch those ints to longs, switch them to 64-bit capable types, and where necessary, move to APIs that enable 64-bit management of data. And that will make you a master. Now master isn't the end of this chain either because there's one more level. And that's when you get to be 64-bit shippable.

And that is not only do you do all this, you can run and you can run about 4 GB, but you do it efficiently. And the reason I mention this is 64-bit gives you the impression of, oh yeah, it's performance nirvana. You get there, you got all the elbow room, you can do whatever you want, allocate memory, just throw it away, who cares, a lot of memory. And that's not true at all. One thing to keep in mind is not every user who has 64-bit machine has 16 GB of memory, for instance.

So really, even under 64-bit, you have to stick to both the tried and true performance techniques you have, and in fact, some others as well, as a way to make sure. your application is as efficient as possible. So really, we want you to design those supertankers that can go through the Panama Canal, but we want them to be very efficient, 0 to 60 in 4 seconds.

Okay, now there are some additional framework considerations when you're going through this. So if you own a framework, Now let's remind ourselves, to be a master, you need to be able to handle more than 4GB of data. Now if you're a framework or a library, meaning you have some public API other people use, there are two steps to attaining this. And this is true for the system APIs as well. Step one is to make your API handle 64-bit quantities.

And step two is to make your implementation handle 64-bit quantities. And you can do these at different times, and let me talk about that. Now as I mentioned earlier, types such as INT, INT32, etc. - these are not 64-bit capable. So if you use these types in your APIs, it cannot express 64-bit quantities.

Here's an example. This is from Core Foundation. Core Foundation has a type called CFIndex. And in Tiger, this is declared to be Sint32, which is clearly a 32-bit type. And an API such as CFArrayGetCount returns a CFIndex, so therefore it's only capable of managing 32-bit worth of data. And since it's signed, CFArrayGetCount cannot represent more than 2 billion elements.

So how do you deal with this? Well, one solution is to use 64-bit types: Long, SizeT, S-SizeT, there's a number of them. Another solution, a separate path you can take, is to update definitions of existing type-defs. Like CFIndex could be updated, byte count could be updated, and so on. And we've in fact done these with some of these types.

And another solution is to just add new type-defs and start using them where appropriate. And we've done this in some other cases. For instance, we've introduced NSInteger, PrefCon, and so on. With that, our sample, core foundation CFIndex, now is declared to return a long, and thus CFArrayGetCount is now able to represent up to at least 8 quintillion elements. Not 16, but 8, because of the sign in this. Now, one thing that's important is, as you do this, you want to make sure that the 32-bit API remains compatible.

And here, after making this change, one thing to notice is that CFIndex is still declared to return a long. And here, after making this change, one thing to notice is that CFIndex is still declared to return a long. declared to be a 32-bit quantity on 32 bits, so therefore it is compatible in the 32-bit world. In fact, Sint32 used to be declared as long in the 32-bit environment, so therefore we have not changed anything on the 32-bit platform after this change. This is important, especially like us. You're in a position of providing binary compatibility release-to-release.

Okay, so now let me do a quick case study of Cocoa. In Cocoa we solved our problem of getting to the master level by adding new types, NSInteger and NSUInteger. And as you might know, Cocoa used ints and unsigned ints in almost all of the API, and we just replaced all of those with NSInteger and NSUInteger. And NSInteger and UInteger are declared like this. In LP64, which is how you denote 64-bit, they're defined to be longs, and in 32-bit they're defined to be ints. And again, you know, we have this definition because we want to maintain that absolute binary compatibility in 32-bit.

And with that, we've changed the definition of the NSRA methods, for instance, for count and objected index to deal with NSU integers, which means they're now capable of 64-bit. Now one thing you might be wondering is why do this to all the APIs, and the reason for that is consistency, impedance match, and so on.

Across Cocoa APIs we want consistency, so even though there are some APIs which might not benefit from 64-bit, just changing all of them introduces or preserves that kind of consistency that Cocoa is well known for. So we want to continue that consistency across API by just using the same APIs throughout. Another interesting case is Quartz. In the Quartz case we added the CG float type. It's a float on 32-bit, but it's a double on 64-bit.

This value is used for graphical quantities, coordinates, sizes, etc. And using doubles gives us much bigger range and much better precision. We didn't do this though for LP64 reasons. In fact, LP64 doesn't dictate anything about floating-point types. There's no changes. But the reason we did this is to take advantage of this opportunity we have, the binary incompatibility opportunity.

You know, everybody will be rebuilding their applications, so why not introduce this very forward-looking change so we can benefit from this in years to come? So all Quartz and Quartz-based APIs will now be using CG floats to represent coordinates, and they're doubles on 64-bit. Here are some examples: Move to Point takes two CG floats, Set Font Size takes a CG float, CG Color Create, which used to take an array of floats, now takes an array of CG floats.

OK, so at this point you might be thinking, you know, give me a break. Can CFArray really manage billions of values? That doesn't sound realistic at all. And the answer is, you know, even if something doesn't handle 64 bits behind the scenes, behind the API today, it might someday, you know, someday it will probably do it. And the important thing is to get the API right today because we cannot change APIs very easily. Every time we change APIs, we have to go through a deprecation process. We have to let you all know. You all have to get off the old APIs.

It's a hassle for us, hassle for you, hassle for our text pubs folks. You know, it's a lot of work. So it's important to get the APIs right today. The implementation can follow. And one other thing to keep in mind is that APIs like this, which are capable of 64-bit, can enable new approaches. For instance, with a CFArray capable of representing up to 64-bit services.

It's worth of elements. You might even imagine using sparse arrays. So arrays which actually don't have, you know, billions and billions of elements in it, but which have indexes in the billions and billions of range where the implementation is smart enough to deal with that. And that could enable you to do, you know, different things in the future using such a data structure.

Okay, so let me talk about the porting process and go through some of the pitfalls you might encounter during this. So porting to 64-bit could be as simple as checking that checkbox and going, and you know, you can try that. If you're a command line type of person, you can also compile with the appropriate architectures. You can go ahead and say, you know, just compile your file with -arch ppc64 or -arch x8664. Or you can even do Xcode build from the command line by specifying arches with these architectures. That's all pretty straightforward.

Now, some code conversion will likely be needed. 64-bit is not necessarily source compatible with 32-bit. There are changes in some of the APIs, some APIs are not there anymore, so on. So you'll get warnings, you'll get errors, and you'll have to deal with those. But our intent is that once you've converted your source base to 64-bit, that you can use the same source base to generate both 32-bit and 64-bit versions of your application or or framework, and so on. And that will just give you one source space.

Now for Cocoa we have this conversion script. I demoed that earlier in another session, but it's described pretty completely in the Cocoa 64-bit transition guide. And basically this conversion script will take your Cocoa application and do a fairly reasonable job of converting it so it's 64-bit ready. And you'll still have to do some work on it to complete the job. But anyway, it'll get you part of the way there, and maybe even 80-90% if you're lucky.

So, when you port, you know, you're going to get compiler warnings and errors. So it's a good idea to build your project warning-free before you do the port. That way you know all the warnings you're getting are due to 64-bit and not, you know, oh, the warning was there, and so on. And sometimes, you know, the warnings you fix in 32-bit end up fixing real crashers in 64-bit. So it's a good idea to pay attention to those.

There's some additional flags to help some catching some conversion problems. For instance, shorten 64 to 32. You can either use it from the command line, or there's a checkbox in Xcode for this. This will tell you potentially any place where you're truncating a 64-bit prompt to the 32-bit.

Unfortunately, this can be a little too noisy. You'll get warnings where you really didn't want to. However, it might still be a good idea to turn it on at some phase during your port and see what warnings you're getting, because some might be interesting. The W format flag will tell you where you have the wrong printf or sprintf arguments, and it's also very useful. This flag is actually on by default if you're using W all or W most.

Now, beyond compiler warnings and beyond compiler errors, you might get runtime crashes when you try to run your application. That's actually good news, because that means, you know, you're still able to catch your errors before you ship your application to the users. And, you know, then you work through your runtime crashes. And right now, let me go through some of the pitfalls that you might have to deal with.

So, the very first one is the very common one. It's, you know, pretty obvious. I've already referred to it. It's basically truncating pointers, because the code made an assumption that the size of a pointer is equal to the size of an int. So, the very first one is the very common one.

It's, you know, pretty obvious. I've already referred to it. It's basically truncating pointers, because the code made an assumption that the size of a pointer is equal to the size of an int. You have code here, void star a pointer. You assign that to an int, and then you extract that later on. So, if you had the value 2 billion something, or, you know, that's, you know, a little bit more than that, then you would have to do a lot of work. So, that's 2 with 8 zeros following it, which is clearly above 4 gigabytes.

After you're done, you know, the top part of the pointer is gone. So, clearly a bad idea. To fix this, you can use a type like int pointer t. Int pointer t was designed for this purpose, to be the same size as a pointer, an integer which is the same size as a pointer. You can also use other types like long or any other 64-bit quantity, depending on your use. But anyway, something like this is the way you fix this issue.

Another common mistake is mismatch in formatting characters. You know that %d refers to ints. However, it will work with longs in 32-bit. But in 64-bit, if you have a long and if you have %d, that's not a good combination. Because if you have a value like, say, 5 billion or whatever that is - too many zeros - anyway, so if you have a value that's above 4 billion, it will go ahead and print a truncated value. So, not a good idea. In this case, the fix is to simply use %ld. Because %ld is what refers to a long, and that is the right thing to use here. And I believe Wformat will catch errors like this.

Failing to use size of. This is probably a pretty elementary problem, but there could be some reason why you use the four there. Maybe the memory you're copying into is four. But anyway, if you have a long and you're copying it somewhere, you really wanna use size of. 'Cause if you don't, it will again truncate a part of the data you're trying to copy, and you will get different results and different architectures. So this is also a bad idea.

Now, this one's easy. Just go ahead and use size of. And in other cases, the fixes might vary. But it's a common problem as well. Mixing signed and unsigned arithmetic. This is a common pitfall that even in 32-bit that sometimes becomes a problem. This occurs because of C's rules for how it deals with unsigned and signed. When you mix the two, the result is unsigned.

Now, typically, when you mix the two and assign the result to assigned value, you don't feel the effects of this problem. But in this case, for instance, where you have an int and you have an unsigned int, you add them together, and then you assign to assigned larger quantity the signed extension is a problem.

Let me give you an example. If you had a value-- here's what happens. In this case, minus 2 plus 1 is supposed to give you minus 1. However, because it's treated as unsigned, it gives you 4.2 billion whatever. And then when that's assigned to the long, it still retains that 4.2 billion, which is clearly wrong. Not what you wanted.

Now, there are many fixes to this. The easiest one probably is to make sure that unless you really want an unsigned result, cast your unsigns to signed values when you're doing arithmetic. That way the compiler knows what you mean. And this is one way to fix this. There are other possible solutions.

Failing to specify the appropriate type of constant. So you're going to start seeing a lot of decorations on your constants in 64-bit. In this case we have an int and we're trying to shift the number 1 by a certain number of bits. Now if you happen to pass 30, this works as expected. If you happen to pass 31, you get this garbage, mainly because of the sign extension to unsigned.

Because you're rolling - or you're shifting a signed 1. So you might think the fix is to use a 1u over there, which is of course an unsigned 1. Now that gets you over the 31, which now behaves correctly. However, 32 doesn't work. And the reason for that is you have an int quantity, and if you try to shift an int more than 32, it just becomes 0. Because it's only 32 bits, even though you're assigning it to a long. So really the fix here is that you have a long int, and you're not going to be able to get a long int.

is to use 1UL. And there you're specifying that you want an unsigned 64-bit quantity that you're going to shift, and you might be shifting it more than 32. You're going to see these kinds of decorations, U's and UL's and so on, a lot in your code as you move forward. So constants are a good thing to take a look at.

Now here's another one. You might assume this code is pretty straightforward: if def ppc print PowerPC else print Intel. And on 64-bit PowerPC this prints Intel. Because if def_ppc is only true for 32-bit PPC. There are a number of preprocessor macros you should use. The first four here: PPC, PPC64, i386, x8664 - these are all architecture specific.

These are a good idea to use in some cases, for instance if you have assembly code, but they're really not what you might want to use in other cases. If you want to distinguish BigAndian, for instance, you would use FDefBigAndian. If you want to distinguish 64-bit, you would use this FLP64 that I showed earlier. So again, here's how you might handle BigNDN, and here's how you would handle 64-bit - #iflp64.

Now there is the pitfall of wrong underlying type, and that's something that impacted us, and it might also hit you as well. And the example is SN32, which I showed earlier. In Tiger, SN32 is declared to be signed long. Now SN32 is clearly a 32-bit quantity, the name says so. So if you were to go ask for the size of SN32 in a 64-bit app, you will get 8, which is not what you want. So, not a good idea.

So what we did to fix this is we declared SN32 with two definitions. In 64-bit it's defined as an int, and in 32-bit it's defined as a long, which gives you a 32-bit definition. Now we did this because we want to maintain binary compatibility. If this sort of thing happened in your code, and you weren't part of a framework, it's all in an app, then you could just go ahead and do the simple declaration, which is just declared to be int32t. You know, this is really the straightforward thing. This is what you really want to do here. You have a type called 32-bit, it's signed int. Go ahead and declare it as an underlying signed integer.

Okay, another pitfall is not upgrading source code to new types. As I said, we're not using CG floats everywhere for Quartz. And this actually, even if you don't update your source code, it will seem to work in many, many cases, because a float will get upgraded to a double when you call a function.

However, if you have something like this, where you have an array of four floats and you're trying to create a color with them, and CGColorCreate takes an array of CG floats. So if you call this on a 64-bit app, you will get this result, which is not what you expected.

Really, you need to be passing in an array of CG floats. Now this is interesting to keep in mind because, again, this CG float conversion cases will work most of the time except when pointers to these quantities are involved. So something to keep in mind. And note that this pitfall could occur with NSInteger and NSUInteger as well.

You know, writing data to files is always tricky across architectures. Using size-changing data types across architectures is interesting. Now with Intel, you might have had to worry about endianness of stuff you wrote to files. With 64-bit you of course have to worry about the size, in addition to endianness. Let's say you have a 32-bit process.

It writes a long out, and it does it by just F-writing the long using size of long. That's fine, it writes out 4 bytes. Now if the corresponding process is run in 64-bit, to read it, it will clearly try to read 8 bytes. So while you really meant to write out 20,000 hexes, it will read back 20,000 followed by garbage, and might even crash or give you some other unexpected result.

And again, the problem is size of long is 8 bytes. There are multiple fixes here. One possible one is be very explicit about what you're writing. Use int32t and write out just 4 bytes in both input and output. This is pretty straightforward. This sort of pitfall exists for the old archiver in Cocoa as well.

Let's say you have an instance variable, and let's say it used to be an int. And let's say you ran the conversion script we told you about. The conversion script will convert your int to an NSInteger, and you might think that's a good idea, because that seems fine. And that's the way you want to leave it, which is good. However, if you're using the old non-keyed archiver, you need to look at the code now.

The code might have looked like this: you're writing out your instance variable using the type i, which clearly refers to a 32-bit element. And here is the reader that corresponds to this. Clearly if the reader is in a 64-bit application, there is now an inconsistency between the read and the written value. So this also is a bad idea. So let me show you how you might fix this.

Assuming you don't want to change your file format, because you want to keep your file format the same between 32-bit and 64-bit, this is one way you could approach this. And this also is complete with error handling here. If the instance variable's value is less than int max, assign it to a temporary integer, and write it out using the code you were using before.

Now if the value is greater than int max, clearly you have a value that can no longer be written to that file, and it's something you might want to warn the user about. You might need to upgrade your file format, but that's left as an exercise to you.

And then the reader is - you do the similar kind of change. You read the value into a temp, which is an integer, and then you assign it to your instance variable. And the reason we have to use that temporary integer is because we're reading the value via a pointer by the address. So we really need to provide the correct size there.

Now the good news is this is only a problem with the old archiver in Cocoa. And we've actually introduced a new archiver back in 10.2, and we've been encouraging you to move over to it. So hopefully this is only a problem for a very small number of cases. One case is clearly if you have old document formats that you're still sporting, this is something to keep in mind if you do that.

Now here's unexpected API behaviors. Let's say you want to write a function that returns a random number from 0 to 1. And you saw in standard lib.h that random returns a long. So you went ahead and wrote this function. It gets the value of random, divides it by long_max, and gets you a value between 0 and 1.

Now if you were to run this on a 64-bit application, you will get these tiny little values most of the time. And people playing your poker application or whatnot will either be delighted that they're winning all the time, or they'll be sad that they'll be losing all the time. But it's not going to seem very random. And the reason for that is because if you read the documentation for random, it says that random returns values from 0 to 2^31 - 1. So although the header says one thing, the documentation clarifies it a little further.

So that's the way random is documented to behave. And this is one of these cases where you can trust the documentation. So... This is one possible fix. Since you know that it returns 2^ , you go divide that by value, so this will give you quantities from 0 to 1.

Now this is in a category of pitfalls that deserves a special mention. There are some other API changes across the system. Now this random one was not an API change, but there are some other things to watch out in APIs you're using. And one of them is these API changes where we're making our APIs more POSIX compliant. Functions like mmap, putenv, open, select, and a few more now have different behaviors when run in POSIX compliance mode. And by default, these changes apply to 10.5 only applications.

So existing 32-bit applications are unaffected, but they are enabled for 64-bit applications. So when you run your 64-bit application and when you debug it, you need to pay attention to how you're using these functions. The documentation goes through detail as to what the changes are, so it's something you really need to pay attention to. And some of the changes are subtle, and you have to pass the right options and so on.

Now speaking of these API changes, there's of course another category of API changes to be aware of, and these are APIs not available in 64-bit. Simon mentioned these yesterday. I'm not going to go through it in any detail today, but these are some of the APIs that are not available. And these are some of the cars. And I really love that the CFM there.

But these are some of the APIs that are not available to you in 64-bit. Now for tomorrow's in-depth talk, which is tomorrow morning at 9:00 a.m., Matthew Formica will actually go through these APIs and go through what the replacements are in detail. So be sure to come for that. Okay, so with that let's go to our closing thoughts on 64-bit.

Now one thing that's important as you move to 64-bit is this notion of interoperability. You really want the file formats used between 32-bit and 64-bit to be compatible. For instance, Nib files that Interface Builder generates will work either in 32-bit apps or 64-bit apps with no changes needed. This is true for preference files, all other data files your app writes, and also documents as well.

Unless there is a reason that your document needs to start holding 64-bit quantities, so you need to upgrade your document format, be sure that the document formats are interchangeable. The user should not care which version of the app wrote the document. So in this way, the interoperability stakes are similar to 32-bit Intel.

Just like users don't know whether they're using 32-bit Intel or PowerPC version of the app, the same holds true for 64-bit versions of the applications. Now another consideration is performance. Now we already talked about whether you should move applications 64-bit or not. And the general recommendation is there's absolutely no benefit to move it, no performance benefit.

You probably don't need to move it. You know, it's not going to get you any benefit. Why do it? Now however, even in cases where it seems like there might be no benefit, let me give you an example. Earlier in another session I showed how to convert the TextEdit application to 64-bit.

And you would think TextEdit is a fairly small, harmless application, and it is harmless, but when run under 64-bit it actually becomes capable of opening documents larger than 4 gigabytes. That's one hidden benefit. Is it a good enough benefit? Probably not. In fact, we're not going to ship TextEdit as a 64-bit application on the system, but you know, it is something to keep in mind. Now there is one other performance consideration.

If you are moving to 64-bit and you're using 64-bit because of the large memory access, you need to keep in mind, you need to check the physical memory in the machine and adjust your algorithms accordingly. Earlier I said, just because a user has a 64-bit machine, it does not mean they have 8 or 16 gigabytes of memory in their machine. In fact, if you're in that DNA demo we saw yesterday, if you ran the 64-bit version of that on an iMac G5 with 512 megs of memory, it would just bog down terribly because it assumes it's got at least 4 gigabytes of memory.

So when you write large memory algorithms where you take advantage of large memory, one thing you should do is you should check the memory in the machine and if, you know, enough memory isn't available, either fall back to 32-bit algorithms or maybe your application isn't able to run. And here is one way you could do that. You use the syscadl or syscadl by name with the mem size and it returns to you the amount of physical memory in the machine. Fairly straightforward. And this is one way you can check for this.

And porting stakes, again as I said earlier, it's totally our intent to use the same source base for 32-bit and 64-bit versions of your binary. This means less sources, less divergence, and less opportunity for problems. Now if you do need to conditionalize parts of your source code, and you might need to, use #if LP64 to distinguish those cases.

And opportunities in the 64-bit transition. You already saw one of the opportunities that we took, and that was to introduce CGFloat and make our graphics subsystem capable of more accuracy, more range, wider range. 64-bit is really an opportunity to remove deprecated APIs, to remove compatibility hacks and old subsystems.

It's really an opportunity for us to modernize the OS by removing all the stuff, and for you to modernize your apps by also doing things brand new ways, taking advantage of the six-word address space and moving to new APIs and getting rid of the old APIs that might be holding your application back. Now we will talk about this in the in-depth talk a great deal, so please come tomorrow.

Now for more coverage for 64-bit, for the API changes, for unavailable APIs, for replacements, state of 64-bit in Leopard Preview, because not everything is quite perfect, we have to admit that. And also, a good amount of information on architectural details, especially Intel 64, which is a brand new environment to many of us.

Please come to the in-depth talk tomorrow morning at 9:00 AM, and I believe it's in the big hall in the Presidio. We have a bunch of documentation. This documentation is not on your DVD, so you have to download it from the WWDC site. We have a 64-bit transition guide, which is an upgraded version of the Tiger document.

And we have transition guides for both Carbon, and Cocoa. And these cover the topics I touched upon and a bunch more. And the 64-bit Cocoa one, as I mentioned, goes through the conversion process step by step. So if you're going to be doing that, it's a good thing to read through.

We also have three 64-bit labs starting tomorrow afternoon, and Thursday morning, and Friday morning. And there will be many engineers from Appleto's, and there will also be 64-bit capable machines. So please come with your projects if you're interested in converting to 64-bit, and we can try to answer some questions.