Categories
All MacBU

Pseudo Code

Odysseus asked about Pcode in response to my last post, and Rick Schaut answered on his blog. As Rick noted, we compiled C and C++ into Pcode, as opposed to writing in Pcode. You can think of Pcode kind of like assembly language, or better yet, like Java bytecodes. At the time I joined Microsoft, we weren’t using Pcode for any processor-agnostic reasons because, as Rick mentioned, the Windows and Mac OS API sets are so wildly divergent that there’s no point in compiling for a pseudo ‘common’ processor between them.
So, why use it as our assembly base instead of going directly to PPC assembly? Because a major advantage to Pcode is that it is small! Let me get a little geeky for you here (apologies to non-techy readers. If you want me to elaborate on some of the upcoming discussion in a later post, please ask!)
All PowerPC instructions are always four bytes in size, no more and no less. This means that by definition, the average PPC instruction is also exactly four bytes in size. PCode, on the other hand, is specifically designed to make common PCPU (Pseudo-CPU) ‘instructions’ as small as possible, such that the average PCPU instruction is something like 1.2 or 1.3 bytes in length. Now, obviously instructions have to be an integral number of bytes, so this means that many instructions are only 1 or 2 bytes long, and just a few are 3 or more. This means that code compiled into Pcode is on average only about 1/3 as large as PPC-native code (1.3 divided by 4).
Now, since Pcode is obviously not native PPC instructions, you have to have an interpreter to run the code. The Pcode interpreter was hand-coded assembly totaling roughly 10k or so of native code. I don’t recall for sure, but I believe the average Pcode instruction required roughly 20 native instructions to run (standard fetch, decode, execute loop with common instructions optimized to be as close to native as possible.)
So, you have about 1/3 as much code to load, but you are running it on average 20 times slower. Why do this? Where this matters is when your hard disk and memory subsystems are so slow that it is more expensive time-wise to load data off disk than it is to run the interpreter.
Case in point: Mac OS 7.x would load the entire executable code section of your CFM app into memory before executing a single instruction of your application. One particular release (7.5.3?) had a bug where it would actually load the entire data fork into memory, not just the executable part! This meant that the larger your app was, the longer it took to boot. The VM system on Mac OS 7 and 8 was also particularly slow to page data in and out, so if you had minimal RAM and the app was too large, you’d spend inordinate amounts of time waiting for the disk to get your code ready for you. This meant that using Pcode enabled us to fit our code in much fewer pages of RAM and thus avoid the dreaded disk swap.
Some might ask at this point, why not just factor your app into sections, or overlays, and load and unload them dynamically to avoid having so much code in place? That would let you run all your code natively, right? Well, yes, it would, but notice the word “load” in that last sentence? That’s the whole thing we were trying to avoid — the disk! Also, doing dynamic overlays adds significant complexity to your code. (Anybody ever work on 68k Macs with CODE resources? You had to decide what code to put in what resource because the OS couldn’t handle chunks of code larger than 32k! You were forced into dynamic code, and woe be unto those who scattered their code willy-nilly across segments.) Pcode was just simpler and less prone to runtime bugs.
It’s kind of interesting that my very first major task in the MacBU (around March 1997) was turning on Pcode for Excel 98. I was well suited to it, because I was a student consultant for the Introduction to Assembly course in college. When I took it in the spring of 1993, CS314 used Motorola 68k assembly, and then when I helped teach it in 1995 and 1996 we used PPC assembly, so I was Real Good(tm) with PPC assembly and MacsBug. Ahh, MacsBug.
Anyway, we used Pcode for Office 98 and started developing Office 2001 with it too. Mac OS 9 came out little while prior to Mac Office 2001, and both it and the new hardware Apple was shipping were much faster, so the benefits of code compression suddenly became quite a bit smaller relative to the performance hit of the interpreter. However, we were getting too close to shipping and the risk of removing Pcode was to large, so we shipped Office 2001 still using it.
One of the downsides to Pcode from a developer’s point of view is that it is a pain to debug in anything other than a source-level debugger. When you look at Pcode in MacsBug, all you really see is the interpreter instructions; the compiled Pcode is just a bytestream of data fed to the interpreter. This means that trying to track down a gnarly bug in shipping code that has had symbols removed is a real pain. That, combined with the huge improvements in OS X (real VM, anyone?), larger code caches in the new PPC chips, etc, all made the decision to remove Pcode for Office X pretty easy.
More on assembly for Intel and PPC tomorrow (perhaps).

One reply on “Pseudo Code”

Thanks for explaining this. OK. here’s another question. Does it really make sense still to have parts of a program (are there other apps besides Excel) coded in assembly language? Is basically all of the important Office code getting affected, if not revised, by the switch to Xcode?

Leave a Reply

Your email address will not be published. Required fields are marked *