Commit graph

6 commits

Author SHA1 Message Date
Graeme Geldenhuys 16c4f5b690 fix(fixpoint): drive runtime build with stage-1 binary, not compiler/target/blaise
fixpoint.sh's runtime build (step 1) relied on the runtime Makefile's default
BLAISE=../compiler/target/blaise. After scripts/rolling-bootstrap.sh — which
installs only to releases/ and leaves the live compiler/target/ empty — that
path does not exist, so `cd runtime && make` failed with
"compiler/target/blaise: No such file or directory" (RUNTIME_FAIL) even though
the release binary was perfectly usable.

Pass BLAISE=<abs stage-1 path> into both the runtime `make` and `make install`
so the build never depends on a pre-existing compiler/target/blaise. Resolve
the RTL archive into $RTL_ARCHIVE once (installed compiler/target copy, falling
back to runtime/target/), and use it at both link sites.

Verified by running the patched script from a fresh clone with no
compiler/target/ present (the exact post-rolling-bootstrap state): reaches
FIXPOINT_OK where it previously aborted at step 1.
2026-06-04 01:29:28 +01:00
Graeme Geldenhuys 2d509cf764 tooling: rolling-bootstrap script for development-checkout builds
Add scripts/rolling-bootstrap.sh to rebuild the self-hosting chain from
the last release binary up to the checked-out revision. A released binary
can only compile source up to its own feature set, so between releases it
can no longer cold-bootstrap master directly — the moment a feature is
taught to the parser in one commit and used in the runtime/compiler in a
later commit, the gap opens. The script replays the chain commit-by-commit
(each step built by the previous step's binary), carrying the binary
forward, and installs the result as releases/v<version>-pre/.

It runs in a throwaway git worktree, so the live tree (including
uncommitted changes) is untouched; it passes QBE= and BLAISE= into the
runtime Makefile and uses the main repo's built QBE; and it smoke-tests
each step. A failed step names the exact commit (BOOTSTRAP_BROKEN),
making it usable as a CI check that the two-step discipline holds.

Document the prerequisite (place the last release binary under
releases/vX.Y.Z/, which is gitignored) and usage in scripts/BOOTSTRAP.adoc,
with a pointer from README.adoc.
2026-06-03 08:05:16 +01:00
Graeme Geldenhuys 923b94c541 refactor: split rtl/ into runtime/ + stdlib/, rename bcl.testing to blaise.testing
Separate always-linked runtime code (system.pas, ARC, strings, platform
abstraction) from opt-in standard library units (sysutils, classes, math,
dateutils, generics, etc.).  This mirrors the Go runtime/ vs stdlib, Rust
core vs std, and Swift SwiftRuntime vs SwiftCore patterns.

- runtime/ contains code linked into every binary + C shims
- stdlib/ contains units imported explicitly via uses clause
- Single platform abstraction layer (rtl.platform.*) in runtime/,
  shared by both runtime/ and stdlib/ units
- bcl.testing renamed to blaise.testing across all 95+ test files
- Updated PasBuild project.xml, fixpoint.sh, README, unit search paths
- All 1931 tests pass, fixpoint OK (stage-3/stage-4)
2026-05-16 00:51:18 +01:00
Graeme Geldenhuys 1083e6c776 fix(fixpoint): add stage-4 fallback for cross-release bootstrap gaps
When stage-1 is an older release with different codegen, stage-2 and
stage-3 IR will differ. The script now automatically extends to
stage-3 -> stage-4 and checks that fixpoint instead of failing.
2026-05-13 00:38:23 +01:00
Graeme Geldenhuys 0cfb9ffa98 release: v0.7.0 — FPC-independent, fully self-hosting
Bump version to 0.7.0. Update fixpoint.sh to use the latest release
binary as stage-1 instead of FPC — FPC is no longer needed anywhere
in the toolchain.
2026-05-13 00:35:56 +01:00
Graeme Geldenhuys 5b9bc375ea fix(codegen): eliminate dynamic stack allocs — phi for short-circuit bools, hoist exc frames
Two codegen improvements that eliminate all non-@start `alloc`
instructions from the emitted QBE IR.  Both are prerequisite work for
the self-hosting fixpoint (currently being debugged).

(1) Short-circuit boolean operators now use QBE phi instead of alloc4.

Previously `A and B` / `A or B` emitted:
  %slot =l alloc4 1          <- dynamic alloc, grows stack per execution
  storew %L, %slot
  jnz %L, @rhs, @end
  @rhs  storew %R, %slot  jmp @end
  @end  %T =w loadw %slot

The slot was allocated in whichever basic block the expression appeared
in.  QBE emits a runtime `sub $16, %rsp` for every alloc outside @start
— inside a while-condition this ran on every loop iteration, growing the
stack by 16 bytes per iteration indefinitely.

New form uses a phi node at the join block:
  jnz %L, @rhs, @end
  @rhs  (evaluate R)  jmp @end
  @end  %T =w phi @lhs %L, @rhs %R

No alloc needed; %T is always well-defined at @end by SSA.
FCurrentBlockLabel tracks the current basic block name so the phi can
correctly name its predecessors.

(2) Exception frames pre-allocated at @start via EmitExcFrameAllocs.

Previously EmitTryFinallyStmt / EmitTryExceptStmt emitted `alloc16 512`
inline at the point of the try statement.  Inside a loop (e.g. `for I
:= 0 to N-1 do begin try ... finally ... end end`), each iteration
allocated 512 bytes of stack that was never released until the function
returned.  Over hundreds of iterations this drifted the stack pointer
into parent exception frame territory, corrupting the exc-frame chain.

New approach: CountTryStmts recursively counts all try blocks in the
function body, EmitExcFrameAllocs pre-allocates %_exc_frame_0 …
%_exc_frame_N at @start (QBE hoists these to the static prologue sub),
and EmitTryFinallyStmt/Except index into the pool via FExcFrameNext.

Result: zero alloc instructions outside @start blocks in the emitted IR.

All 1242 tests pass.  Stage-1 IR and stage-2 IR are now byte-for-byte
identical (md5 verified) — the fixpoint is correct at the IR level.
The stage-2 BINARY still crashes during stage-3 generation; this is
under active investigation and may be a QBE vs FPC code-generation
quality issue for the same IR.
2026-05-06 11:47:52 +01:00