B, the foundation that grew C. Where did that B come from?
In Episode 5 we saw C grow out of B, and ended by leaving open the question "so what did B come from?" The answer is BCPL, a language Martin Richards created in 1967. In fact, Ken Thompson at first tried to write a Fortran compiler for the PDP-7, couldn't fit it in memory, and pared BCPL right down into typeless (a single word) B. And BCPL had this episode's star — an intermediate code called O-code. It's a much older ancestor of the "wedge something into the middle" idea we saw with EM in Episode 4. This escape route was the "porting magic" that appears again and again on our journey back up the chicken and egg.
Let's go back one more step in Episode 5's family tree. C's ancestry is a single road.
B was a language that "narrowed BCPL's semantics and packed them into a different notation." The earliest PDP-7 version was compiled to threaded code, and later Ritchie used a tool called TMG (a compiler for building compilers) to write a B compiler that emitted machine code. — Tools, too, have their own tools. The ring really does continue endlessly.
The reason BCPL is remembered in the history of compilers lies not in the language itself but in how the compiler was built. Richards clearly split the BCPL compiler into two parts (actually three stages) — the first half reads the source and emits O-code (a machine-independent intermediate code), and the second half translates that O-code into machine code for that particular machine. It is exactly the same skeleton as ACK/EM in Episode 4.
When moving BCPL to a new machine, what you rewrite is about 1/5 of the compiler (just the back end). By the estimates of the day, that was 2–5 person-months. The remaining 4/5 (the front end) knows nothing about the machine, so it can be reused as is.
As a result, the BCPL compiler was itself written in BCPL and easy to port. That's why it became, at the time, "the go-to language for bootstrapping a new system." In 2003 Richards was honored for this achievement ("pioneering the portability of system software via BCPL"). This is the very theme of our series.
The true nature of intermediate code is to place one "island that knows nothing of the machine" between the source language and machine code. The front end only has to carry things to the island; it needn't know about the machine. Knowledge of the machine is quarantined in the back end. So when the machine changes, you only re-bridge from the island onward (the back end).
This "machine-independent island" appears again and again under different names — O-code (BCPL, 1967) → Pascal's P-code → Episode 4's EM (ACK) → JVM bytecode → LLVM IR (clang) → WebAssembly. It's one and the same invention, running through half a century. In the bootstrapping context its effect is especially vivid — build up to the island, and on a new machine you only bridge the last short span (a small back end / interpreter).
Where O-code truly shows its power is in carrying the compiler itself to a new machine. Because O-code is machine-independent, if you keep the compiler in O-code form, you can carry it straight to a new machine. All that's left is to prepare, on the new machine, a small receiver (the second half that runs O-code). In the figure below, follow how the crossing works, step by step.
O-code makes porting lighter, but it does not make the chicken and egg vanish (the same caveat as Episode 4). On a new machine, you ultimately have to prepare "the first receiver that runs O-code," in machine code. And who builds that receiver? — From here on, the magic of intermediate code stops working. B, BCPL — at the very bottom they run on assembly language, and then on the first Unix itself. From next time, we finally descend below the language, just above the machine.
(1) "About 1/5" and "2–5 person-months" are representative estimates of the day, and vary by machine and era. The skeleton (the front end is shared, only the back end is swapped) is the substance. (2) We said the BCPL compiler "splits into two," but it was actually a three-stage structure, and O-code is the name of the middle stage. A simpler intermediate form (an interpreter-based approach) was also provided to make porting much lighter. Here we prioritize the skeleton.
(3) It's true that B is "a simplification of BCPL," but Thompson's own tastes and the influence of an earlier language (bon) are also mixed in. The family tree is not a single thin thread but a bundle. (4) Intermediate code only makes porting easier; the first receiver (machine code) and the first machine still have to be prepared separately — the chicken and egg is merely passed down a stage, the same point as Episodes 3 and 4.
The B that grew C was a child of Martin Richards's BCPL (1967). B was what came from Thompson failing at a Fortran compiler on the PDP-7 and simplifying BCPL into a typeless language (CPL→BCPL→B→C). BCPL's true worth lay in how its compiler was built: the first half emits O-code (a machine-independent intermediate code), and the second half translates it into machine code. For a new machine you only rewrite the back end (about 1/5, 2–5 person-months), which is why BCPL became the go-to language for bootstrapping.
The true nature of intermediate code is to place a "machine-independent island" in the middle and quarantine knowledge of the machine in the back end. From O-code → P-code → EM → JVM → LLVM IR → WASM, it's the same invention running through half a century. What's more, O-code becomes an escape route for carrying the compiler to another machine — the principle-level version of Episode 8's first Unix, "carried on paper tape." But below the escape route, a new machine still needs a first receiver (machine code) prepared separately, and the chicken and egg is passed down a stage. — Next time, at last, below the language: the first Unix, written in assembly.
Print / save as PDF: ⌘+P (Ctrl+P on Windows). On screen, use Figure 1's buttons to change the target machine (only the back end is swapped), and Figure 2's slider to trace porting between machines via O-code. "Show the answer" reveals each solution.