The ancestor of the cc that ignited gcc — how did the first C compiler build itself?
In Episode 3 we said "the first gcc was ignited by the machine's existing cc," and promised to descend toward that cc's ancestor. That ancestor is the first C compiler, written in the early 1970s by Dennis Ritchie. But here the ring reaches its very deepest point — the C compiler is written in C, yet there is not a single C anywhere in the world yet. Even an "existing C compiler" to borrow doesn't exist. So how? The answer is that he didn't write C outright — he grew C little by little out of a language called B, which already existed.
C is not a language designed from a blank page. Its family tree is like this — BCPL → (Ken Thompson made) B → (Dennis Ritchie made) C. When the PDP-11 arrived in 1970, B's nature — "typeless, word-oriented" — didn't fit the byte-oriented PDP-11. So Ritchie began adding types (char, int, etc.) to B. This transitional language was called NB (New B), but it was so short-lived that no proper specification of it survives. As B sprouted types and was reworked to emit machine code, it had become C.
Episode 3's gcc got by with "lend me one existing cc." There was someone to borrow from. But the first C compiler had no one to borrow from — because C did not yet exist. This is the very bottom of the chicken-and-egg of self-hosting.
It looks unsolvable because you imagine "building a complete C compiler, all at once, from nothing." In reality that's not how it goes. Start from a small language that already works (B), add one feature to the compiler and recompile, and widen the language it can handle little by little. Each stage can be built with just the compiler from the previous stage. You don't make C from nothing — you continuously deform B into C.
Let's see this "way of growing" as a staircase. Advance the slider in the figure below. At each step, the language the compiler is written in and the language the compiler can compile climb upward, chasing each other. The moment the two meet at C is the moment self-hosting is achieved.
① Add a new feature (types, structs…) to the compiler. ② Recompile with the current compiler. ③ Now the new feature can also be used in the compiler's own source code. ④ Go back to ①.
Each turn of this small ring makes both the language and the compiler one step richer. "Rewrite yourself using the feature you just built" — the snowballing growth unique to self-hosting.
Eventually, the C compiler reaches the point where it is written in C and can compile C (its own source). Once that happens, the B seed used for ignition is no longer needed. Same as discarding stage1 in Episode 3 — you remove the scaffold once it's done its job. In 1973, Ritchie and Thompson used this C to rewrite the Unix kernel itself. In the figure below, cut the seed and watch the moment the C↔C ring begins to run on its own.
The self-hosting ring we saw in Episode 0: "gcc ← gcc ← gcc …" — its very first turn was this C compiler of Ritchie's. The gcc ring, and the ring of the compiler you use today, are all, traced back, a continuation of the same ring ignited here. The moment C became a language that "can compile itself" was the starting point of everything that followed.
"Grown out of B" solved C's chicken and egg. But of course the next question waits — then what built that B? B, too, did not simply spring into existence. B came from BCPL, and ran on top of the PDP-7's assembly. The hand outside the ring is still further down. Next time: B and BCPL, and "intermediate code (O-code)."
(1) The first C compiler is sometimes introduced as "written in assembly," but more precisely — the C compiler evolved continuously from the B compiler, becoming C as Ritchie added types to the B compiler and reworked it toward machine-code output. That foundation (the B implementation) is supported by PDP-11 assembly and threaded code. "Grown out of B" and "assembly underneath" are both true at once.
(2) The self-growing loop of "add a feature and recompile" is a real technique, but the actual work of 1971–73 was far more involved; here we idealize the skeleton. (3) Even under self-hosting, assembly remains in things like the runtime library and startup. It does not mean "everything is completed in C alone." (4) NB was short-lived and poorly documented, and there are various accounts of the details of B→C.
The ancestor of the cc that ignited gcc is Ritchie's first C compiler. Here the chicken-and-egg of self-hosting reaches its deepest point — the C compiler is written in C, yet there is no C compiler anywhere in the world to borrow. It was solved by giving up on "making complete C from nothing." Following the family tree BCPL→B→C, he added types, machine-code output, and structs to the already-working B compiler, recompiled each time, and continuously deformed the language up to C.
Turning the self-growing loop of "rewrite yourself with the feature you just built," the C compiler reached the point of being written in C and compiling C, and the B seed for ignition exited (same as discarding stage1). In 1973, the Unix kernel was rewritten in this C. The self-hosting ring we saw in Episode 0 was ignited here. — But the ring still continues down. What built that B? Next time: B and BCPL, and the intermediate code O-code.
Print / save as PDF: ⌘+P (Ctrl+P on Windows). On screen, use Figure 1's slider for the staircase of language extension, and Figure 2's button to try the seed (B) exiting and the C↔C loop running on its own. "Show the answer" reveals each solution.