Skip to content

Blaise v0.8.0 (alpha) — House Cleaning🧹 & Performance

Pre-release
Pre-release

Choose a tag to compare

@graemeg graemeg released this 16 May 22:32
· 473 commits to master since this release

Blaise Compiler — Changelog for v0.8.0

Released: 2026-05-16
Previous: v0.7.0 (2026-05-13)
Commits: 62 between v0.7.0..v0.8.0

HEADLINE

v0.8.0 is the first release with a Pascal-native memory allocator.
The compiler RTL, codegen builtins, and all C runtime helpers route
through blaise_mem (_BlaiseGetMem / _BlaiseFreeMem / _BlaiseReallocMem)
instead of libc malloc.

blaise_mem beats libc malloc on three of five microbenchmark workloads
(small alloc, mixed sizes, retain+free-all) and delivers a ~10 %
real-world compile-time win on the 1934-test suite (~89 s → ~80 s).

NEW LANGUAGE / SEMANTICS

  • Dynamic arrays (array of T) — full implementation: SetLength,
    Length, Copy, indexed read/write, ARC-managed lifetime.

  • Typed constants — const Name: Type = Value with array literals
    for both fixed-range and array-of-enum forms.

  • Class-level array constants — constant arrays declared inside a
    class body, addressable via TypeName.ConstName.

  • Range-indexed array constants — array[0..N] of T = (...).

  • Abstract methods and abstract classes — virtual; abstract; syntax,
    with a runtime tombstone (_AbstractMethodError) that aborts with a
    clear message if a vtable miss reaches it.

  • Explicit ordinal values in enum definitions — (A = 1, B = 5).

  • Record methods — methods declared inside a record definition.

  • for .. in over sets — iterate the members of a set of T.

  • Supports() intrinsic for interface queries.

  • Open-array coercion — pass static-array variables directly to
    array of T parameters.

  • Named array type aliases in the type section — type TArr = array[..] of T;.

  • Length() extended to accept open-array and static-array
    parameters in addition to strings and dynamic arrays.

  • Five-type date/time model with full DateUtils unit (records-based;
    TDateTime, TDate, TTime, plus formatting / arithmetic helpers).
    Read more.

  • Generic vars at unit scope and non-identifier interface arguments
    now parse and analyse correctly.

  • Overload resolution now considers the implicit-self expression
    path, matching Delphi/FPC behaviour.

RUNTIME / STANDARD LIBRARY

  • Streams stack (read more):

    • TInputStream / TOutputStream + file and memory streams.
    • buffered wrappers with error propagation.
    • TStreamReader / TStreamWriter text wrappers.
    • TBuffer segment rope + byte-dereference fixes.
    • CopyStream and capability markers; interface ABI fixes.
  • Pascal memory allocator (blaise_mem) — mmap-backed, with size-class
    freelists for small allocations, a LIFO cache for large allocations,
    in-place mremap-based realloc for large blocks. 28 correctness
    tests in runtime/src/test/pascal/test_blaise_mem.pas.

  • Path manipulation functions ported from C to Pascal (ChangeFileExt,
    ExtractFileName, ExtractFilePath, ExtractFileDir, ExtractFileExt,
    IncludeTrailingPathDelimiter, ExcludeTrailingPathDelimiter).

  • TComponent — minimal ownership/notification class.

  • TStringList — CustomSort + CommaText property.

  • StrUtils — pure-Pascal port.

  • SysUtils — ExpandFileName.

  • Math unit — math compiler builtins (Min, Max, Abs, Sign, etc.).

  • IMap<K, V> interface — generic dictionary contract with itab-based
    dispatch for generic→interface use.

  • bcl.testing extended with AssertNotEquals, AssertContains, and a
    --verbose CLI flag.

  • RTL split into runtime/ (always-linked) and stdlib/ (opt-in).
    Testing framework renamed bcl.testing → blaise.testing.

  • LineEnding, sLineBreak, DirectorySeparator, PathSeparator moved
    from rtl.platform to system.pas.

  • Platform layer renamed bcl.platform → rtl.platform with expanded
    SysUtils.

COMPILER OPTIMISATIONS

  • Function inlining (phase 1) — small leaf functions are inlined at
    call sites when address-taken and ARC-free. Inlinability decided
    by uSemantic; codegen walks the callee body into the caller frame.

  • Function inlining (phase 2) — case statements now allowed inside
    inline candidates, and the body-statement cap raised from 8 to 24.
    This unlocked inlining of blaise_mem's SizeClassIndex and
    SizeClassBytes.

  • mem2reg promotion for tyPointer / tyPChar locals — non-address-taken
    pointer locals are promoted to QBE temps instead of alloca slots,
    yielding direct register access on every read/write.

  • Sanity checker in _StringRelease — verifies the string header looks
    plausible (refcount, length, capacity within sane bounds) before
    treating refcount==0 as a free signal. Aborts with a clear stderr
    message on corruption.

BUG FIXES

  • P[I] := Chr(N) no longer stores the low byte of a heap string
    pointer. EmitByteRhs short-circuits Chr() in byte-store contexts
    and emits the integer value directly, skipping the _Chr allocation.
    Applies to PChar subscript writes, dynamic- and static-array byte
    subscript writes, and byte-typed pointer writes.

  • Static-array element stores of PChar values no longer truncate to
    32 bits. tyPChar was missing from the storel / loadl / alloc8
    case branches and fell through to storew, silently zeroing the
    high 32 bits of every heap pointer. Fixed across five sites in
    uCodeGenQBE.pas.

  • large-alloc LIFO cache in blaise_mem — IsLarge() was reading the
    wrong field of TLargeHeader (AllocSize: Int64 overlapped with the
    Flags slot it probed). Fix made the cache reach ~100 % hit rate
    on 64 KB allocations, dropping the L workload from 33 ms to 0 ms.

  • Static-array variables now coerce correctly when passed to open-
    array parameters.

  • Stale rtl/ directory references in E2E tests updated to
    runtime/ after the directory split.

  • _POSIX_C_SOURCE bumped to 200809L to expose mkstemp() on glibc.

PERFORMANCE

Allocator microbench (median of 3 runs, milliseconds), 1 M small alloc/
free, 500 k mixed-size, 100 k 5-step realloc, 10 k × 64 KB, 100 k
retain-then-free-all:

  Workload                       | libc malloc | blaise_mem | ratio
  -------------------------------+-------------+------------+--------
  Small alloc/free  (1M × 32 B)  |      8      |     7      | 0.88x
  Mixed sizes       (500k × var) |      5      |     4      | 0.80x
  Realloc growth    (100k × 5)   |      5      |     8      | 1.60x
  Large alloc/free  (10k × 64KB) |      0      |     0      |  —
  Retain+free-all   (100k × 64B) |      5      |     4      | 0.80x

Real-world compile workload (Blaise test suite, 1934 tests):

Pre-cutover (v0.7.0 baseline): ~89 s
Post-cutover (v0.8.0 release): ~80 s (~10 % faster)

DOCUMENTATION

  • Testing strategy document — explains the unit / E2E / punit
    three-layer split and when to use each.

  • Stream I/O design rationale.

  • Generic Interfaces and Collections Framework rationale.

  • Cold-bootstrap RTL chicken-and-egg issue documented.

  • Migration analyser rule: detect TObjectListTList<TObject>.

  • Future-improvements: enhanced enumerations section.

  • benchmark.txt expanded with a long-term tracking format and entries
    for each performance milestone in the cycle.

  • Run procedure for the bench scripts updated post-cutover — a
    third bench source (bench_libc_malloc.pas) uses explicit external
    malloc / free / realloc bindings so the libc baseline can still be
    measured.

INFRASTRUCTURE / REFACTORING

  • rtl/ directory split into runtime/ + stdlib/.

  • Monolithic E2E test unit split into 12 topic units (one per
    feature area).

  • TStringList E2E coverage expanded with a shared base class.

  • Six test units re-enabled after fixes to static-array and Pos
    semantics.

  • GitHub funding configuration (Patreon + PayPal).

FIXPOINT

stage-2 IR : 135 311 lines
stage-3 IR : 135 311 lines
diff -q : (empty — byte-identical)
1934 tests : pass