Self-hosting and the fixed point

The Oberon compiler is written in Oberon. To build it, therefore, an Oberon compiler is needed. This is not a paradox but ordinary self-hosting — but it has a checkable consequence worth understanding precisely, because it is regularly confused.

What self-hosting does not prove

The phrase "the compiler builds itself" means nothing on its own. A compiler with a bug will build itself too — it will simply emit wrong code, and since there is nobody to check, the bug goes unnoticed.

What a fixed point proves

The real check looks different. Take the compiler sitting on the disk — call it generation 0 — and build its own sources with it. That gives generation 1. Now unload the old compiler from memory, load generation 1 and build the very same sources again. That gives generation 2.

If generations 1 and 2 match byte for byte, a fixed point has been reached.

Why that is substantial. Generation 1 was built by the old binary, generation 2 by the new one. If the new compiler emitted even slightly different code from the old, the generations would diverge. Matching means the transformation "source → binary" is stable: applied to its own output, it changes nothing.

This does not prove the compiler correct. It proves it consistent with itself, and that a compiler it builds behaves as the one that built it.

What it looks like on screen

In lab 7 you run one module, but the full experiment goes the same way — two blocks in the log:

OR Compiler  18.4.2016
  compiling ORS  1756    992 76547166
  compiling ORB  2325    408 2F03B698
  compiling ORG  6650  34980 8F476858
  compiling ORP  6188    144 E6FCC519
System.Free
ORP unloading
ORG unloading
ORB unloading
ORS unloading
OR Compiler  18.4.2016
  compiling ORS  1756    992 76547166
  compiling ORB  2325    408 2F03B698
  compiling ORG  6650  34980 8F476858
  compiling ORP  6188    144 E6FCC519

Code sizes, data sizes and keys match for all four modules. The line OR Compiler 18.4.2016 is printed anew in the second block: that is the new ORP.rsc, loaded from disk, announcing itself. So the second generation really was built by fresh binaries and not by old ones left in memory — without System.Free the experiment would have been meaningless.

The whole run is 715 million instructions and 1.1 billion cycles on simulated hardware.

Where C remains in this system

The honest answer: in two places, and both outside the machine.

The simulator. Verilator translates Verilog into C++, and that C++ executes the processor's logic. But this is not an implementation of RISC5 — it is an execution of its description. On an FPGA the same Verilog has no C anywhere.

The bridge to the host file system. When the compiler runs not inside the system but directly on the core, something has to hand it files. That wrapper is in C, and it substitutes for the file system only; it computes nothing itself.

The browser version does not even have that bridge: there the files live in a disk image and the compiler runs from inside the system, by mouse and keyboard. No C remains in the loop at all.

Rebuilding the whole system

The compiler is only four modules out of forty-two. The full experiment is to rebuild the entire system: Kernel, Files, Display, Viewers, Texts, Oberon, the editor, the graphics, the compiler itself — and compare the result against what was on the disk.

We did that. Thirty-seven object files matched byte for byte, and the rebuilt image boots and gives the same screen checksum. Three modules do not compile and one shipped file is out of date; that is in the chapter on modules.

A trap in the check

Finally, a methodological note useful well beyond Oberon.

The first version of our check compared disk images byte for byte and happily reported "fixed point reached". It then turned out to pass just as happily on an untouched image, where no rebuild had run at all. A byte comparison does not distinguish "rebuilt and matched" from "never touched".

File dates were no help: this machine has no clock and every timestamp is zero.

The fix was positive evidence: after a real rebuild the disk must gain PIO.rsc and PIO.smb, which were not there, and Math.rsc must change. Their absence now counts as failure rather than as leniency.

The moral: a check that cannot fail checks nothing. Before believing a green tick, break what it guards and make sure it turns red.