Fil-C on Nix

Filnix wiki

mbrock/filnix/nanofil

local_storage.pl

Nonescaping memory that remains after scalar promotion.

local_storage_module/2 replaces an entry allocation and its address chain with a named local object and fixed-offset accesses. A local object's address is not a value: it cannot become a heap lower, a call argument or a stored pointer. Pointer *contents* still have ordinary Fil-C capabilities.

Selection is deliberately smaller than escape classification: strict SSA, no returns-twice operation, a constant entry size, and all accesses within the exact size. Heap allocations have rounded bounds; selecting an access in their padding would change the IR's meaning. Overlapping and misaligned accesses remain, with all their checks. Local payload/aux storage is limited to 4096 bytes per function; larger objects and the remainder keep the heap fallback. Plain-mode and checked modules are unchanged.

useslists
usesassoc
usesir
usescfg
usesescape
usespromotestrict_ssa/4
local_storage_module+M0, -Mdet

M has named local objects for the eligible allocations of M0.

31local_storage_moduleM0, M ←
module_modeM0, filc
module_stageM0, unchecked
→
legacy_contextM0, moduleItems0
code_itemsItems0, Code
context_indexItems0, Contexts
maplistlocal_itemContexts, Code, Groups
appendGroups, Items
M = moduleItems
else
M = M0

The cut leaves no choicepoint per function (promote_item/4).

41local_itemAll, F0, Items ←
F0 = funcN, _, _, _, _
!
context_recordsAll, N, Rs0
local_contextF0, Rs0, F, Rs
with_contextF, Rs, Items
44local_item_, dataN, L, A, Is, dataN, L, A, Is ←
!
45local_item_, aliasN, L, T, aliasN, L, T
local_storage_func+F0, -Fdet

F0 is an unchecked Fil-C function. Unknown or unsupported allocation uses keep their heap representation.

51local_storage_funcF0, F ←
local_contextF0, [], F, _
53local_contextF0, Rs0, F, Rs ←
F0 = funcN, L, R, Ps, Bs0
Only a constant alloc in the entry block can become local
(local_objects/4): most functions have none, so look first.
Bs0 = block_, _, Entry, _|_
memberchklet_, p, alloc_, const_, Entry
func_cfgBs0, Cfg
cfg_idomsCfg, Idom
def_countsF0, Counts
strict_ssaF0, Counts, Cfg, Idom
address_tempsF0, Addresses
local_objectsF0, Addresses, Counts, Objects
Objects ≢ []
→
maplistlocal_blockObjects, Addresses, Bs0, Bs, Maps
appendMaps, Map
remap_sitesRs0, Map, Rs
F = funcN, L, R, Ps, Bs
else
F = F0
Rs = Rs0

Only in-bounds accesses qualify, but not necessarily disjoint ones: those are the objects promote could not turn into SSA values.

73local_objectsF, Addresses, Counts, Objects ←
F = func_, _, _, _, Blocks
Blocks = block_, _, Entry, _|_
alloc_classesF, Addresses, Counts, Classes
escape's fixed-slot vocabulary currently excludes float loads.
Missing access events are not evidence that an object is unused.
findallA,
memberblock_, _, Is, _, Blocks
memberlet_, _, loadT, tmpP, Is
get_assocP, Addresses, A−_
,
Unsupported
findallA−Size,
memberA−localconstSize, Accesses, Classes
integerSize
Size ≥ 0
¬memberchkA, Unsupported
A local object is 16-aligned (local_alloc has no Align).
memberchklettmpA, p, allocAlign, constSize, Entry
Align ≤ 16
maplistaccess_insideSize, Accesses
,
Candidates
within_budgetCandidates, 4096, Objects

Payload and aux round to 16; reserve a header, a root, and alignment slack too. This is selection policy, not a relaxation of local bounds.

97within_budget[], _, []
98within_budgetA−Size|As, Budget, Objects ←
Cost is 2 × Size + 15//16 × 16 + 32
Cost ≤ Budget
→
Objects = A−Size|Rest
Remaining is Budget−Cost
else
Objects = Rest
Remaining = Budget
within_budgetAs, Remaining, Rest
105access_insideSize, accOff, Width ←
width_bytesWidth, Bytes
Off ≥ 0
Off + Bytes ≤ Size
109local_blockObjects, Addresses, blockL, Phis, Is0, Jump, blockL, Phis, Is, Jump, Map ←
local_instsIs0, Objects, Addresses, Is, L, 0, 0, Map
113local_insts[], _, _, [], _, _, _, []
114local_instsI0|Is0, Objects, Addresses, Is, L, Old, New, Map ←
I0 = lettmpA, p, alloc_, constSize
memberchkA−Size, Objects
→
Out = local_allocA, Size
local_accessI0, Objects, Addresses, I
→
Out = I
I0 = lettmpD, p, _, _
get_assocD, Addresses, A−_
memberchkA−_, Objects
→
Out = []
else
Out = I0
appendOut, Rest, Is
emission_sitesL, Old, New, Out, Map, Maps, Next
Old1 is Old + 1
local_instsIs0, Objects, Addresses, Rest, L, Old1, Next, Maps
129local_accessletD, K, loadT, tmpP, Objects, Addresses, local_loadD, K, T, A, Off ←
get_assocP, Addresses, A−Off
memberchkA−_, Objects
132local_accessstoreW, V, tmpP, Objects, Addresses, local_storeW, V, A, Off ←
get_assocP, Addresses, A−Off
memberchkA−_, Objects