Repository navigation
Free Pascal codegen notes
YadeWira edited this page Oct 18, 2026
·
1 revision
FPC 3.2.2, x86-64. Each point was measured on sylz's hot loops; check a routine with fpc -O3 -al and count its (%rsp) references.
FPC gives each local one location for the whole routine (no live-range splitting), so what matters is the routine's total number of variables, the locals of inlined callees included. When they don't fit, it can spill exactly the pointer on the critical path: in the first decoder ip and op lived on the stack (830 MB/s); keeping them in registers gave 2031 MB/s.
-
Any call in a routine leaves its variables only the 5 callee-saved registers (rbx, r12–r15). One
Movein a hot loop is enough to push the rest to the stack. Move the exact, slow path into its own routine. -
Passing a local by
varpins it to the stack, even when the callee isinline(1834 → 2210 MB/s once removed). - Records larger than a register always live in memory. That is a tool: put values used only in comparisons (loop limits) in a record, and the registers go to the pointers on the critical path (2210 → 2458 MB/s).
-
try/finally and managed locals (dynamic arrays) add an
fpc_setjmpexception frame, and the compressor loop kept nearly every local on the stack with one. Allocate in a wrapper, run the loop on raw pointers. - The goto version of the level-1 compressor kept more locals on the stack (127 vs 104 references) and ran at 226 vs 273 MB/s.
-
-O4,-OoUSERBPand-OoREGVARchanged nothing in these loops.
-
UInt32 * UInt32is evaluated in 64 bits on x86-64: truncate withUInt32(...)before shifting a multiplicative hash, or the high bits leak into the index. - Pascal is case-insensitive: a local
mflimithides a constantMFLIMIT.
After all of the above, the Pascal decoder ran at 72–76% of the C LZ4 decoder on identical streams, and the level-1 compressor at about 55% of lz4's speed for the same table size. The x86-64 assembler closes most of that: +25% decode, +51% compress.