Dive
A single-page technical overview of PXX. Every figure on it was re-measured on
2026-09-27/28, with pin v445 (compiler sha256 caf21ac399f1…) unless it names
another compiler. The rest of the documentation, linked at the end, covers each
topic in depth.
Overview
PXX is a from-scratch, self-hosting Pascal compiler. It emits Linux ELF executables directly: no external assembler, no external linker, and no libc dependency in the default build path. The lexer, parser, intermediate representation, all seven backends, the runtime library, and the GTK-based component library are original code written for this project. The dialect follows Free Pascal's naming and semantics where practical, but the implementation is independent — nothing is copied or ported from FPC, Borland, or elsewhere.
Example
program hello;
begin
writeln('Hello, world!');
end.
./pxx hello.pas hello
./hello
Hello, world!
This produces a complete static ELF binary in one step, with no external assembler, linker, or C compiler invoked during the build.
Design points
- Self-hosting. The compiler is written in its own dialect, and every change is gated on it compiling itself to a byte-identical fixed point: the compiler builds the compiler, that result builds the compiler again, and the two must be equal to the byte. This runs natively, at the default optimisation level; the other self-hosting targets are covered by the cross-target suites in the test matrix rather than by the per-change gate.
- Two string ABIs. The default build uses managed, reference-counted
strings. Compiling with
-uPXX_MANAGED_STRINGselects an older frozen-string ABI with no dynamic allocation for strings. On x86-64 it no longer changes binary size, because unused runtime code is dropped anyway: a one-writelnhello world is 4,544 bytes both ways, and a program with a string concat is 25,048 bytes both ways. It still matters on ESP bare metal; see ESP32. See also Types. - Seven code-generating targets, one compiler. x86-64, i386, aarch64, and
arm32 self-host byte-identical. riscv32 covers both bare-metal ESP32-C3 and
hosted 32-bit RISC-V Linux, whose binaries run under
qemu-riscv32; xtensa targets the ESP32-S2/S3; wasm32 produces WebAssembly modules run with wasmtime. The three cross self-hosts are proved by a triple-stage check — cross-compile the compiler, run that binary under QEMU to compile the compiler again,cmpthe two — and are run in the managed-string build, which thecross-bootstraprule states is required for them; the native fixedpoint passes no build flags at all. Same property, two configurations, worth naming because "all four self-host" reads as one gate and is two. All three cross checks were re-run on 2026-09-28 with the compiler built at0381cb6e4d(sha256848d33f2668a…), and each was byte-identical: i386 in 2 minutes, aarch64 in 14, arm32 in 16, under QEMU.--target=selects the backend, and Targets is the table that stays current. - Multiple frontends. The same backend also compiles a C frontend
(all 220 c-testsuite programs pass with pin v445, and it compiles real C:
the SQLite amalgamation, Lua, QuickJS, zlib and cJSON each pass their test
row with the compiler at
0381cb6e4d), a statically-typed Python-like dialect (Nil Python,.npy), and an assembly-source frontend. - It boots. PXX compiles a 19-applet BusyBox 1.36.1 userland, including
the
ashshell, as 86 separate translation units. With pin v445 it is byte-identical to a GCC build of the same sources over 132 differential cases, and PXX links the 86 objects itself (pascal26 --link): no external linker, no libc, noPT_INTERP.tools/mkminimal.shpackages such a shell, a stock Linux kernel and the compiler itself into one BIOS+EFI ISO, a system whose entire userland is PXX output. The kernel is not ours, and the ISO ships no compiler sources. The ISO was not rebuilt for this page; A minimal Linux system records when it was and what was measured in the guest.
<details markdown="1"> <summary>Compilation pipeline</summary>
Compilation proceeds through five stages, with no external tools invoked:
- Lexer — source text to tokens. Dispatched by file extension:
.pasPascal,.cC,.npyor.pyNil Python,.basBASIC, and the experimental.rsand.zig. - Parser — tokens to an AST. Frontends share expression-parsing and type-checking where their semantics overlap.
- IR — the AST lowers to a linear, target-agnostic intermediate representation.
- Codegen — the IR lowers to target machine bytes. Seven backends share one IR.
- ELF writer — the compiler's own linker. Static output by default;
PT_DYNAMIC/DT_NEEDED/GOT/PLT are added automatically when a C library is imported.
Optimization runs at -O2 by default (peephole passes, procedure inlining,
and dead-code elimination, tiered by -O level); -O0 disables it. The
self-host fixedpoint is proved at the default level, not at -O0 — the
compiler rebuilds itself with no -O flag at all, so "it reproduces itself" is a
statement about one optimisation level. There is no whole-program or
SSA-based optimizer — the passes are local and the emitted code stays close to
the source. See the command-line reference
for the -O levels.
See Architecture for the full pipeline. </details>
<details markdown="1"> <summary>Current status</summary>
Established: the core Pascal language — classes, interfaces, generics,
exceptions, managed strings, dynamic arrays — compiles on all four Linux
self-host targets: a class with an exception, caught and printed, ran on
x86-64, i386, aarch64 and arm32 (the last three under QEMU). The C
and Nil Python frontends compile against real-world C headers and libraries.
DWARF debug information (-g) is available on all four of those targets, and
not on riscv32 or wasm32.
Known gaps: xtensa and riscv32 are not self-host targets — the compiler emits
for them but does not compile itself with them (though classes with virtual
dispatch now work on both).
Optimization is local only — there is no whole-program or SSA-based pass.
Integer overflow, range, and IO checking exist but are opt-in per region
({$Q+}, {$R+}, {$I+}); the lax default wraps and does not range-check.
Member visibility (private/protected/strict) is enforced only under
--strict-visibility; the lax default parses the markers but grants access
anywhere. Nil Python has no pointer syntax of its own. See Limits for the
complete list.
PXX is early, experimental software. It should not be used for security-sensitive, safety-sensitive, financial, legal, or medical work. </details>
<details markdown="1"> <summary>Licensing</summary>
PXX is open source, licensed per directory: the compiler is MPL 2.0, the
runtime and libraries (compiler/builtin/, lib/rtl, lib/pcl, lib/crtl
and lib/asmcore) are zlib, examples are
0BSD, and these docs are CC BY 4.0. The runtime embedded into compiled
programs is zlib-licensed, which asks nothing of a binary. Three runtime files
carry other licences and are embedded only into programs that use them: two
MIT files derived from MicroPython (lib/rtl/platform/esp/espmpyport.pas and
lib/rtl/mimic_framebuf_font.py) and C's fenv (lib/crtl/src/fenv.c,
MPL 2.0). See Licensing for
the full table and rationale, or
LICENSE.md for the
binding terms.
</details>