Blaise v0.8.0 (alpha) — House Cleaning🧹 & Performance
Pre-releaseBlaise 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 = Valuewith 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
recorddefinition. -
for .. inover sets — iterate the members of aset of T. -
Supports()intrinsic for interface queries. -
Open-array coercion — pass static-array variables directly to
array of Tparameters. -
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
DateUtilsunit (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_SOURCEbumped 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
TObjectList→TList<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 intoruntime/+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