- Modula-2 47.1%
- Macaulay2 42%
- DTrace 6.5%
- Shell 4%
- AMPL 0.3%
- Other 0.1%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
Record that the C18 already emits CX 0 2 / TRA for SYSTEM.NEWPROCESS / TRANSFER, that mcode implements both (ProcessDescriptor, per-coroutine spill), that mp/COR runs and CORT aborts, and that the Interpreter showcase prints the same output under mcc and m3pl. |
||
| gm2-port | ||
| selfhost | ||
| src | ||
| .gitignore | ||
| LICENSE | ||
| License-Agreement | ||
| README.md | ||
Historic ETH Zürich Modula-2 Multipass Compiler for the Lilith architecture
The ETH Zürich Modula-2 Multipass Compiler for the Lilith architecture was developed by a team consisting of Leo Geissmann, Christian Jacobi, Urs Ammann, Svend Erik Knudsen, and others under the direction of Niklaus Wirth at the
Institut für Informatik ETH-Zürich CH-8092 Zürich
These are, however, not the original sources as shipped by ETH Zürich (unfortunately, no machine-readable copy of them survived at our site) but those we adapted to our needs such that we could run them on our Lilith emulator on a Perkin-Elmer 3220 machine running UNIX Edition VII.
Please note that this software is not free (free as freedom) in a sense comparable to the GNU General Public License. See the file License-Agreement how the original software was licensed to us, and see LICENSE for some further notes and explanations.
You'll find the sources in the src subdirectory along with the binaries C18.Base, C18.Init, C18.Lister, C18.Pass1, C18.Pass2, C18.Pass3, C18.Pass4, and C18.Symfile. These binaries were generated by exactly these sources in combination with mcl, the M-Code linker (to be found in a separate package) and they can be run on our ancient Lilith emulator (to be found in another package), i.e. this compiler is delivered in a steady-state.
The script mc allows to compile Modula-2 sources using this compiler, running on the Lilith emulator, into M-Code which can likewise be linked using mcl and run on the Lilith emulator.
See https://github.com/afborchert/lilith for more infos.
Andreas F. Borchert
gm2 port (gm2-port/, GNU Modula-2, PIM dialect)
./build.sh in gm2-port/ builds ./mcc (multi-phase link via a
module list — direct linking fails with duplicate main). mcc
compiles one or more files per invocation (.d definitions first for
their .sy files, then .m2); scratch files are process-unique
(TMP.<pid>.*), so several mcc runs may share a directory.
It writes .o (M-code) + .r (debugger info).
SYSTEM coroutines (NEWPROCESS/TRANSFER) are emitted as CX 0 2 and
TRA <mask>; mcode implements both (MEDOS-2 ProcessDescriptor in the
workspace, context switch with the expression stack spilled per coroutine),
so Interpreter/tests/mp/COR.m2 runs and CORT (transfer to a nil
coroutine) aborts. The Interpreter/examples/showcase program exercises
them together with the libraries and prints the same output when compiled
by this compiler or by m3pl.
Batch mode: mcc T.m2 SC.m2 compiles the file(s) directly (no
piping); several files run in one invocation, resetting the per-unit
compiler state, and the objects are byte-identical to
one-file-per-invocation compiles. --version/--help print the
banner/usage. Compile errors print *** error <n> at line <l> pos <p>
in addition to the driver's ---- error. Its .o files run
under ../../Interpreter/src/mcode (see that repo's BUILD.md §4
and TUTORIAL-LANGUAGE.md §10–12); the interpreter synthesizes HostIO /
Terminal / Math / Files / Time / Storage (traps) for .o
programs, with multipass definitions in ../../Interpreter/mplib/.
Origin note: src/ holds the adapted originals; gm2-port/ holds the
Linux port (see gm2-port/PORT-SUMMARY.md for the porting changes).
Self-hosting status
All of it is gated by one command: selfhost/bootstrap.sh (or
make bootstrap from ../../Interpreter/) builds the three binaries and
runs every check — single-pass fixpoint 6/6, make test, make test-multi,
the phase-3 corpus, phase 5, the MEDOS triage and phase 4 — exiting 0 only
if all pass (10 ok, 0 failed). SKIP_BUILD=1 reuses existing binaries.
The multipass chain rebuilds itself — exactly. selfhost/build-phase4.sh
compiles every src/ module with the self-hosted C18 (the phase-2 staging,
driven by MCPublic.o under mcode): 34/34 modules compile, and the
self-hosted objects are byte-identical to the host mcc's (0 bytes
differ). The one residue was never emitted code: MCP4Global's
loadAddress := procnumber must run after Pass2 (the original runs each pass
via Program.Call with shared globals); the single-process driver now redoes
it at the start of RunPass4.
mcc (the host binary) compiles its own gm2-port/ sources:
selfhost/build-phase5.sh reports 37/37 core modules, 0 failures (only
FileSystem/HostAddr/Program/Terminal stay out, by design), so the
port's sources are in mcc's own dialect. The single-pass sibling is also
exact: test-fixpoint.sh is now 6/6 — MAX(REAL) is the exact single max
and PutLit masks to the 16-bit target width (see
../../Interpreter/Open-Work.md § 1–2).
selfhost/build-phase1.sh is the beachhead: it compiles the
dependency leaves of src/ with host mcc (renaming files to module
names — mcc names outputs after the file — shortening
MCOperations/WriteStrings to the 11-char .sy lookup limit, and
normalizing Terminal.d to trap order) and loads every .o under
mcode, plus Conversions/NewStreams functional checks (all green).
selfhost/build-phase2.sh goes further: every src/ module
compiles (33 .sy + 33 .o), every library .o loads, and a capacity
report confirms all fit the interpreter (largest: MCPass3, 7288 code
bytes / 473 relocs — well under 16KB / 1024). Loads fall in three
expected classes: clean inits (end.), MCPublic-driver inits
(status 1, the Program.Call stub has no C18 chain), and
file-dependent inits (clean NewStreams abort — bare loads provide no
pass files). The port's own-source dialect work is done (18 Oct): the
last CARDINAL64 in the core was moved into HostAddr (and
MCP3IO/MCP4Global routed through it), the 32-bit REAL code was
rewritten with REAL arithmetic, VAL(<basic type>, x) conversions were
replaced by the casts mcc accepts, nested-module imports were
normalised, and the remaining .defs got explicit EXPORT clauses.
build-phase5.sh reports 37/37 core modules, 0 failures; see
../../Interpreter/Open-Work.md § 1 for the full list.
Two host-width codegen fixes found by phase 2 (both verified by failing-then-passing builds, both locked by regression tests):
- Set-constant bit order is Lilith order, unconditionally
(
MCPass2/MCPass3SetConstructor,MCOperationsfoldedIN): the host-TSIZEbranch emitted PDP-11 inverse order on 64-bit hosts ({wr}→ 4096). Locked bySEinmake test-multi. - Named string constants crashed pass 3 (
AllocStringfault): pass 2 never storedsvalue, so pass 3 read stale scannerval(e.g.3,22H) as a pointer.MCPass2ConstFactornow capturesMCP2IO.GetSVal()(new accessor) intocval.svalue. Locked bySCin both suites. BY -1loops failed pass 4 (spurious error 202):step := valrelied on 16-bit wraparound to recover negativity.Pass4sign-extends the 16-bitBYword. (Found inMCP4CodeSys,FOR ... BY -1twice.)
Phase 2a verdict: the FileSystem footprint (measured, not guessed)
Every qualified FileSystem.X use in src/:
| module | procedures used | record fields touched |
|---|---|---|
NewStreams |
SetOpen, SetPos, Close, SetWrite, Doio, SetRead, GetPos |
ela×18, flags×20, elodd×9, topa/ina/inodd, eof, res |
FileLookup |
Lookup, Close (+Response) |
res |
FilePool |
none (type File + Storage only) |
— |
MCPublic |
Lookup, Rename, Close (+File, Response) |
res |
MCInit |
Create, Close (+Response) |
res |
Union: {Lookup, Create, Rename, Close, SetRead, SetWrite, SetOpen, SetPos, GetPos, Doio} — and all Doio/mode/position traffic
flows through NewStreams.m2 alone (176 lines, the narrow waist),
which also owns the buffer arithmetic (ela/topa/ina pointers,
elodd byte-half packing, bytemode flag) in plain M-code.
Decision: implement a Doio trap plus the mode/position/open traps
against the verbatim src/FileSystem.d record (field-for-field
offsets the trap relies on), rather than re-porting NewStreams.
Reasons: byte-exactness for free (.o CODETEXT bytes are packed
hi-first by MCP4CodeSys.PutCodeText into whole words, and text
streams pack hi-first as well — NewStreams writes the first byte
high with elodd starting FALSE, matching Medos LXB semantics;
any re-port must preserve both, while the trap approach runs the
original buffering untouched), and a single
narrow implementation (~one trap + record reader) instead of
re-validating every stream client. Exact Doio/seek semantics come
from FileSystem.MOD in ETHZ_Medos-master/medos_txt/.
Two load-time consequences, both handled: the multipass FileSystem
needs its own synth (same module name, fuller shape — sessions are
format-pure: .OBJ programs keep the 10-proc synth, .o programs
get the full-record one), and Terminal.d copies stay normalized to
trap order (WriteString before WriteLn; src/ has them swapped).
Still open for 2b: SetModify/Length/Reset/Again are declared
but unused — map them to abort stubs; confirm GetPos/SetPos units
stay self-consistent within a toolchain run (positions are never
shared across formats).
Phase 3 triage: real MEDOS-2 modules (2026-10-16)
First five ETHZ_Medos-master/medos_txt/ modules compile with host
mcc and load/run under mcode, all unchanged except mechanical
renames (11-char .sy limit): Conversions (functional check
ConvertCardinal(421,5) → 3232525049, ConvertHex(255,4) →
48487070), MCMnemonics, MCSymFileDefs (as MCSymDefs — it is
the compiler's own mcsymf, C19 vs C18 version comment only),
UpdateStringIO (as UpdStringIO; functional round-trip → 656667),
WriteStrings (as WriteString; WriteLn emits the Medos 36C EOL
byte, kept verbatim — the compiler tree's copy uses 12C instead).
Conversions.MOD/.DEF are byte-identical to the compiler tree's
(except nothing); the Medos tree generally holds newer versions of
shared modules. Next candidates: CardinalIO (Terminal only),
FilePool/InOut (FileSystem+Storage, both synthesized), Memory.
Avoid first: the 4 corrupt String/Printer entries (disk dumps, not
source) and the 52 CODE modules.
Later status (19 Oct). triage-medos.sh now covers the library
and system/kernel layers with no compiler change: 37/37 definitions
and 33/33 implementations, adding Program (execution + heap),
System, FilePool, FileLookup, FileNames, Options, MathLib0,
RealInOut, OutWindow, WindowHandler, MCLookup, the Linker
(file link.MOD) and CompFile. The two fixes were in the closure
parser: it now reads the old-style IMPORT a, b, c; list (MEDOS uses it —
WindowHandler imports ScreenDriver2 that way) and traverses a
target's own .DEF as well as its .MOD.
triage-medos.sh --all targets the whole tree: 119/120 reachable
definitions and 144/178 implementations compile. Staging keys on the
module name, not the file name (link.MOD = MODULE Linker,
MIA.TextIO.DEF = MODULE TextIO), which is what made the MIA.*/MD*/
ED.* family resolve. The remaining 32 implementation failures all
cascade from one malformed source (CursorStuff.DEF has a stray *) at
line 25); MCP2Main/MCP3Main target an older MCP2IO/MCP3IO
interface. No port bug surfaced.