A History of UNIX That ClicksEpisode 6 / The Escape Route of Intermediate Code

B, the foundation that grew C. Where did that B come from?

B and BCPL — the Escape Route of Intermediate Code C's parent, B, was a child of Martin Richards's BCPL. And BCPL had an invention that anticipated Episode 4's EM by years — an intermediate code called O-code. "When the machine changes, you don't have to rebuild all the way to the middle." An old, strong escape route that underpinned porting and bootstrapping.

Tools you'll need: Episode 4 (EM), Episode 5 (B→C) The reveal: place a "machine-independent island" in the middle

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.

01The family tree continues — CPL → BCPL → B → C

Let's go back one more step in Episode 5's family tree. C's ancestry is a single road.

/* language family tree (continued) */ CPL ──▶ BCPL // Martin Richards, 1967, Cambridge. Designed for portability BCPL ──▶ B // Thompson, 1969. Simplified from BCPL, typeless (one word), PDP-7 B ──▶ C // Ritchie (Episode 5). Added types to reach C

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.

02BCPL's invention — an intermediate code called O-code

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.

Figure 1: The O-code pipeline. The front end (BCPL→O-code) is machine-independent and shared. Change the machine, and all you swap is the back end (O-code→machine code) — about 1/5 of the compiler.
The concrete numbers O-code gave us

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.

03The reveal — the escape route is really a "machine-independent island"

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 episode's crux — line up the names of the family

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 bytecodeLLVM 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).

◇ ◇ ◇

04Crossing from machine to machine via the escape route

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.

Figure 2: From machine to machine via O-code. Turn the BCPL compiler into O-code, carry it to the new machine, write one small receiver, and the compiler starts running there — the principle-level version of Episode 8's "carry it on paper tape."
Bridging voice — this is the "principle" of Episode 8 "Put it into a machine-independent form, carry it to another machine, and receive it on the other side" — this storyline is just like how the first Unix was made, which we'll see in Episode 8. There, the result assembled on a different machine was physically carried to a PDP-7, punched as holes in paper tape. The only difference is whether what's carried is O-code or paper tape; the skeleton — "make it elsewhere and hand it over" = the cross approach — is the same. Intermediate code was a trick to make that handoff lighter (we dig deeper in the bonus episode "Cross-compilation").

05And yet, below the escape route lies the machine

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.

The honest line — carefully

(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.

Practice problems (solvable with just this episode)
  1. List C's family tree from CPL to C.
    Show the answer
    CPL → BCPL (Richards, 1967) → B (Thompson, 1969, typeless) → C (Ritchie).
  2. Wedge in O-code, and what do you rewrite when porting to a new machine? Roughly how much?
    Show the answer
    Just the back end (O-code→machine code). About 1/5 of the compiler. The front end (about 4/5) is machine-independent, so it's reusable.
  3. Place a "machine-independent island," and where does knowledge of the machine collect?
    Show the answer
    It's quarantined in the back end (island→machine code). The front end gets by without knowing the machine, handling everything up to the island (the intermediate code).
  4. Does O-code solve the "chicken and egg"? How does it connect to Episode 8?
    Show the answer
    It doesn't. A new machine still needs a first receiver (machine code) separately. The skeleton of "make it elsewhere and carry it" is the same as the cross approach in Episode 8, carrying paper tape from another machine to the PDP-7.

SummaryPlace an island in the middle, and when the machine changes you only re-bridge

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.

This document is Episode 6 of the series "A History of UNIX That Clicks." Historical notes: BCPL was a language first implemented by Martin Richards at the University of Cambridge in 1967, derived from CPL. The BCPL compiler was structured (actually in three stages) so that the front end generates a machine-independent intermediate code, O-code (OCODE), and the back end converts it into the machine code of the target machine; the rewrite needed to support a new machine was about 1/5 of the compiler, estimated at the time as 2–5 person-months. Because the BCPL compiler was itself written in BCPL and easy to port, it was widely used for bootstrapping systems. Richards received the IEEE Computer Society's Computer Pioneer Award in 2003 for "pioneering the portability of system software via BCPL." B was a direct descendant of BCPL created by Ken Thompson at Bell Labs in 1969; an initial attempt at a Fortran compiler for the PDP-7 wouldn't fit in memory, and it became a typeless language (its only data type the computer word) that simplified BCPL. The earliest PDP-7 implementation was compiled to threaded code, and Ritchie wrote a B compiler generating machine code using TMG. The design of passing through an intermediate code connects to P-code, EM, JVM bytecode, LLVM IR, and WebAssembly. There is a range of accounts and estimates on the numbers, the number of stages, and the details of the family tree. — To print, use your browser's "Print" and "Save as PDF" (in the printed version, the figure controls and answers are frozen / hidden).

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.