Fil-C on Nix

Filnix wiki

mbrock/filnix/nanofil

provenance.pl

Explanatory context, separate from executable IR and safety evidence.

A context(Function, Records) item accompanies a func/5. Bindings and origins have function-local identities. Origins are shallow typed nodes at the enclosing statement's location, not expression spans. Values retain their original identities and hints even when removed; alias/4 records substitution without renaming the surviving value.

site(inst(Label, Index), Origins) uses a zero-based index in THIS stage's instruction list. Passes produce explicit old-to-new site maps while rewriting. Phis and jumps have stable sites phi(Temp) and jump(Label). Context never participates in CFG or safety proofs.

Records

RecordMeaning
bindingId, Name, CType, Locationdeclaration identity and spelling; shadowed names have distinct IDs
originId, Location, Description, Parentsa typed lowering node, enclosing statement, or generated operation, linked by origin IDs
valueTemp, Hint, Originsoriginal definition, retained as history if eliminated
siteSite, Originsprovenance of a live instruction, phi or terminator
aliasTemp, Replacement, Hint, Originscopy/phi substitution, including replacement by a constant
historyId, Temp, Hint, Origins, Replacementretired definition when an executable name is reused; Replacement is none or replaced(Value)

Location is at(File, Line) or none. A typed description is typed(val|obj|fun, CType, ShallowForm), a statement is statement(Kind), and a synthesized operation is generated(Pass, Reason). Parents express containment or contributing origins, not executable uses. Hint is none or named(BindingId, value|address|piece). C types are explanations of source nodes, never substitutes for IR classes.

If a pass reuses a deleted temporary's spelling, its old definition moves to history/5. Incoming alias/history replacements then use historical(Id), referencing that history record, not the new value. This metadata-only reference can never be an executable operand.

Promotion conservatively cites the slot's declaration/initial value and stores, not an assertion that every store reaches every phi. Copies preserve the survivor's primary name. Raw accesses and their adjacent checks inherit the same access origin; checks and poll edges additionally have generated origins. Erasure cannot authorize an otherwise-invalid check/access pair.

useslists
usesassoc
code_items+Items, -Codedet

Code is Items without their context/2 items: the executable part of a module's item list, in order.

55code_itemsItems, Code ←
excludeis_context, Items, Code
56is_contextcontext_, _
context_records+Items, +Name, -Recordsdet

Records are the context of function Name in Items, or [] if it has none. Items may be the contexts(Index) of context_index/2: a pass over every function of a large module (SQLite's 3,000) would otherwise scan its items for each.

64context_recordscontextsIndex, Name, Records ←
!
get_assocName, Index, Rs
→
Records = Rs
else
Records = []
66context_recordsItems, Name, Records ←
memberchkcontextName, Rs, Items
→
Records = Rs
else
Records = []
context_index+Items, -Contextsdet

Contexts indexes the contexts of Items by function name, the first of a name as memberchk/2 finds it, for context_records/3.

73context_indexItems, contextsIndex ←
empty_assocIndex0
foldlindex_context, Items, Index0, Index
76index_contextItem, Index0, Index ←
Item = contextName, Rs
¬get_assocName, Index0, _
→
put_assocName, Index0, Rs, Index
else
Index = Index0
with_context+Func, +Records, -Itemsdet

Items are the module items for function Func with context Records: Func and context(Name, Records), or Func alone when Records is [].

86with_contextF, [], F ←
!
87with_contextF, Rs, F, contextName, Rs ←
F = funcName, _, _, _, _
site_index+Records, -Sitesdet

Sites is the assoc from each site of Records' site(Site, Origins) records to its Origins.

93site_indexRs, Sites ←
findallS−Os, membersiteS, Os, Rs, Pairs
list_to_assocPairs, Sites
site_origins+Sites, +Site, -Originsdet

Origins are those Sites (site_index/2) gives Site, or [] if none.

99site_originsSites, Site, Os ←
get_assocSite, Sites, Origins
→
Os = Origins
else
Os = []
origin_ids+Records, -Idsdet

Ids is the assoc of the identities of Records' origin/4 records, each to true, for fresh_origin/3,4.

106origin_idsRs, Ids ←
findallId−true, memberoriginId, _, _, _, Rs, Pairs
list_to_assocPairs, Ids
fresh_origin+Base, +Ids0, -Ids, -Iddet

Id is a new origin identity: Base when Ids0 does not have it, else the first derived(Base, N) it does not have. Ids is Ids0 with Id. Only metadata names consult metadata: executable fresh names stay exactly what the metadata-free transformation would choose.

115fresh_originBase, Ids0, Ids, Id ←
get_assocBase, Ids0, _
→
unused_originBase, 0, Ids0, Id
else
Id = Base
put_assocId, Ids0, true, Ids
fresh_origin+Base, +Ids, -Iddet

Id is fresh_origin/4's for Base, but Ids is not extended: for a batch of bases that are distinct and so cannot choose the same Id (derived ids keep their base), inserting each one in a function's whole origin assoc only costs time.

124fresh_originBase, Ids, Id ←
get_assocBase, Ids, _
→
unused_originBase, 0, Ids, Id
else
Id = Base
126unused_originBase, N, Ids, Id ←
Candidate = derivedBase, N
get_assocCandidate, Ids, _
→
N1 is N + 1
unused_originBase, N1, Ids, Id
else
Id = Candidate
retire_values+Records0, +Temps, +Ids0, -Ids, -Recordsdet

Records are Records0 after a pass has given the temporaries Temps new definitions (pizlonate's chkN proofs, say) whose spellings Records0 already uses: each old value/3 or alias/4 record of such a name becomes a history/5 record with a fresh origin retired(T), and references to the old definition become historical(Id), so no record confuses the old value with the new one. Ids0 are Records0's origin identities (origin_ids/2); Ids is Ids0, since the retired(T) bases cannot meet any other base and so need not be added.

141retire_valuesRs0, Temps, Ids0, Ids, Rs ←
findallT−true, memberT, Temps, NewPairs
list_to_assocNewPairs, New
findallT−V, memberaliasT, V, _, _, Rs0, APairs
list_to_assocAPairs, Aliases
findallT−true, membervalueT, _, _, Rs0, VPairs
list_to_assocVPairs, Values
findallT,
memberR, Rs0
get_assocT, New, _
,
Ts0
sortTs0, Ts
retirement_idsTs, Ids0, Ids, Pairs
list_to_assocPairs, Retired
retire_recordsRs0, Retired, Aliases, Values, Records
findallRs1,
memberT−Id, Pairs
¬get_assocT, Values, _
¬get_assocT, Aliases, _
retiredT, none, [], none, Id, Rs1, []
,
Missing
appendRecords|Missing, All
maplistretired_referenceRetired, All, Rs
historical_temp
152valueT, _, _T
153aliasT, _, _, _T
154alias_, tmpT, _, _T
155history_, _, _, _, replacedtmpTT

The temps Ts are distinct, so their bases retired(T) are, and no other base is retired/1: the new ids cannot meet each other or a later fresh_origin's, and Ids0 need not grow (inserting each one in a large function's origin assoc was most of retirement's time).

161retirement_idsTs, Ids, Ids, Pairs ←
findallT−Id,
memberT, Ts
fresh_originretiredT, Ids, Id
,
Pairs
164retire_records[], _, _, _, []
165retire_recordsvalueT, H, Os|Rs0, New, As, Vs, Rs ←
get_assocT, New, Id
!
get_assocT, As, V
→
Replacement = replacedV
else
Replacement = none
retiredT, H, Os, Replacement, Id, Rs, Rest
retire_recordsRs0, New, As, Vs, Rest
170retire_recordsaliasT, V, H, Os|Rs0, New, As, Vs, Rs ←
get_assocT, New, Id
!
get_assocT, Vs, _
→
Rs = Rest
else
retiredT, H, Os, replacedV, Id, Rs, Rest
retire_recordsRs0, New, As, Vs, Rest
175retire_recordsR|Rs0, New, As, Vs, R|Rs ←
retire_recordsRs0, New, As, Vs, Rs
178retiredT, H, Os, Replacement, Id, originId, none, generatedcontext, retiredT, Os, historyId, T, H, Os, Replacement|Rest, Rest

Incoming references identify the old definition, not a new proof that happens to reuse its spelling. Historical references are not IR values.

184retired_referenceIds, aliasT, tmpOld, H, Os, aliasT, historicalId, H, Os ←
get_assocOld, Ids, Id
!
186retired_referenceIds, historyHId, T, H, Os, replacedtmpOld, historyHId, T, H, Os, replacedhistoricalId ←
get_assocOld, Ids, Id
!
189retired_reference_, R, R
remap_sites+Records0, +Map, -Recordsdet

Records are Records0 with every instruction site moved by Map, a list of OldSite-NewSite pairs with one pair per emitted instruction: an instruction that expands has several destinations, one erased has none. Phi and jump sites are unchanged.

197remap_sitesRs0, Map, Rs ←
site_indexRs0, Sites
excludeinstruction_site, Rs0, Kept
findallsiteNew, Os,
memberOld−New, Map
get_assocOld, Sites, Os
,
NewSites
appendKept, NewSites, Rs
204instruction_sitesiteinst_, _, _
emission_sites+Label, +Old, +New, +Emitted, -Map, ?Rest, -Nextdet

Map, ending in Rest, pairs the site inst(Label, Old) with each of the instructions Emitted for it, numbered from New in block Label; Next is the number after the last. A rewriting loop calls this with the instructions it just emitted, to build remap_sites/3's map. The cut leaves no choicepoint per instruction (10,000 in SQLite's sqlite3VdbeExec), which would pin the rest of the function's trail.

214emission_sites_, _, J, [], Rest, Rest, J ←
!
215emission_sitesL, I, J, _|Is, instL, I−instL, J|Ms, Rest, End ←
J1 is J + 1
emission_sitesL, I, J1, Is, Ms, Rest, End
legacy_context+M0, -Mdet

M is M0 with its loc(File, Line) markers turned into context records: each marker becomes an origin, and the instructions after it, up to the next, sites of that origin. Legacy loc markers remain valid input (hand-written IR); they become context at unchecked transformation boundaries, never before checked-IR validation, so a checked module is its own result. The frontend writes no loc markers: such a module is its own result too, shared rather than copied (a copy of SQLite's contexts is 60 MB, and the caller still holds the original).

228legacy_contextM, M ←
ir:module_stageM, checked
!

The frontend writes no loc markers: such a module is its own result, shared rather than copied (a copy of SQLite's contexts is 60 MB, and the caller still holds the original).

232legacy_contextmoduleIs, moduleIs ←
¬
memberfunc_, _, _, _, Bs, Is
memberblock_, _, Insts, _, Bs
memberchkloc_, _, Insts
!
235legacy_contextmoduleIs0, moduleIs ←
context_indexIs0, Contexts
maplistlegacy_item_onceContexts, Is0, Groups
appendGroups, Is
239legacy_item_onceAll, Item, Items ←
oncelegacy_itemAll, Item, Items
241legacy_itemAll, funcN, L, R, Ps, Bs0, Items ←
!
context_recordsAll, N, Rs0
origin_idsRs0, Ids
legacy_blocksBs0, Bs, Ids, _, Added, Map
remap_sitesRs0, Map, Rs1
site_indexRs1, Sites0
foldllegacy_site, Added, Sites0, Sites
assoc_to_listSites, Pairs
findallsiteS, Os, memberS−Os, Pairs, SiteRecords
appendRs1, Added, AllRecords
excludeis_site, AllRecords, Kept
appendKept, SiteRecords, Rs
with_contextfuncN, L, R, Ps, Bs, Rs, Items
250legacy_item_, context_, _, [] ←
!
251legacy_item_, I, I
253is_sitesite_, _
254legacy_sitesiteS, Os, Sites0, Sites ←
!
site_originsSites0, S, Before
appendBefore, Os, Both
sortBoth, Merged
put_assocS, Sites0, Merged, Sites
257legacy_site_, Sites, Sites
259legacy_blocks[], [], Ids, Ids, [], []
260legacy_blocksblockL, Ps, Is0, J|Bs0, blockL, Ps, Is, J|Bs, Ids0, Ids, Rs, Map ←
legacy_instsIs0, L, 0, 0, none, Is, IRs, Last, Ids0, Ids1, M0
Is0 = loc_, _|_
IRs = originFirst, _, _, _|_
→
findallsitephiT, First, memberphitmpT, _, _, Ps, PRs
else
PRs = []
Last ≡ none
→
JRs = []
else
JRs = sitejumpL, Last
legacy_blocksBs0, Bs, Ids1, Ids, Rest, M1
appendPRs, IRs, JRs, Rest, Rs
appendM0, M1, Map
269legacy_insts[], _, _, _, Loc, [], [], Loc, Ids, Ids, []
270legacy_instslocF, Line|Is0, L, Old, New, _, Is, originId, atF, Line, statementlegacy, []|Rs, Last, Ids0, Ids, Map ←
!
fresh_originlegacyL, Old, Ids0, Ids1, Id
Next is Old + 1
legacy_instsIs0, L, Next, New, Id, Is, Rs, Last, Ids1, Ids, Map
274legacy_instsInst|Is0, L, Old, New, Loc, Inst|Is, Rs, Last, Ids0, Ids, instL, Old−instL, New|Map ←
Loc ≡ none
→
Rs = Rest
else
Rs = siteinstL, New, Loc|Rest
Old1 is Old + 1
New1 is New + 1
legacy_instsIs0, L, Old1, New1, Loc, Is, Rest, Last, Ids0, Ids, Map
valid_contexts+Itemssemidet

Every context(Name, Records) of Items is for a function of Items, no two for the same one, and each record is well formed: its identity unique among records of its kind, every origin it cites defined, every hint's binding declared, and every site an instruction, phi or jump the function has. ir:valid_module/1 calls this last.

287valid_contextsItems ←
findallN, membercontextN, _, Items, Names
sortNames, Unique
lengthNames, Count
lengthUnique, Count
Each context's function is found in an index of the module's, not
by a scan of its items per context (quadratic for SQLite). The
records are read in place: a findall/3 would copy them all.
empty_assocFuncs0
foldlindex_func, Items, Funcs0, Funcs
forallmembercontextN, Rs, Items, valid_contextFuncs, N−Rs
296index_funcItem, Fs0, Fs ←
Item = funcName, _, _, _, Bs
¬get_assocName, Fs0, _
→
put_assocName, Fs0, Bs, Fs
else
Fs = Fs0
302valid_contextFuncs, Name−Rs ←
get_assocName, Funcs, Bs
groundRs
is_listRs
record_keysRs, Ids, Bindings0, Values, Aliases, Hs, Sites
unique_assocIds, Origins
unique_assocBindings0, Bindings
unique_assocValues, _
unique_assocAliases, _
unique_assocHs, Histories
unique_assocSites, _
findallL−N,
memberblockL, _, Is, _, Bs
lengthIs, N
,
Sizes
list_to_assocSizes, Blocks
findallT−true,
memberblock_, Ps, _, _, Bs
memberphitmpT, _, _, Ps
,
Phis0
list_to_assocPhis0, Phis
forall/2 leaves no choicepoint per record, which a large function's
hundreds of thousands of records would otherwise keep alive.
forallmemberR, Rs, valid_recordindexOrigins, Bindings, Histories, sitesBlocks, Phis, R
record_keys+Rs, -Origins, -Bindings, -Values, -Aliases, -Histories, -Sites

the K-true pairs of each kind of record's key, in order, in one pass over a context's records. Other records are left to valid_record/3, which rejects them.

321record_keys[], [], [], [], [], [], []
322record_keysR|Rs, Os, Bs, Vs, As, Hs, Ss ←
record_keyR, Os, Bs, Vs, As, Hs, Ss, Os1, Bs1, Vs1, As1, Hs1, Ss1
record_keysRs, Os1, Bs1, Vs1, As1, Hs1, Ss1
326record_keyoriginId, _, _, _, Id−true|Os, Bs, Vs, As, Hs, Ss, Os, Bs, Vs, As, Hs, Ss ←
!
327record_keybindingB, _, _, _, Os, B−true|Bs, Vs, As, Hs, Ss, Os, Bs, Vs, As, Hs, Ss ←
!
328record_keyvalueT, _, _, Os, Bs, T−true|Vs, As, Hs, Ss, Os, Bs, Vs, As, Hs, Ss ←
!
329record_keyaliasT, _, _, _, Os, Bs, Vs, T−true|As, Hs, Ss, Os, Bs, Vs, As, Hs, Ss ←
!
330record_keyhistoryId, _, _, _, _, Os, Bs, Vs, As, Id−true|Hs, Ss, Os, Bs, Vs, As, Hs, Ss ←
!
331record_keysiteS, _, Os, Bs, Vs, As, Hs, S−true|Ss, Os, Bs, Vs, As, Hs, Ss ←
!
332record_key_, Os, Bs, Vs, As, Hs, Ss, Os, Bs, Vs, As, Hs, Ss

The pairs are all K-true: the assoc is built from the sorted keys in linear time.

336unique_assocPairs, Assoc ←
sortPairs, Unique
lengthPairs, N
lengthUnique, N
ord_list_to_assocUnique, Assoc
340valid_record_, _, binding_, Name, _, Loc ←
atomName
341valid_recordRs, _, origin_, Loc, Description, Parents ←
descriptionDescription
originsRs, Parents
343valid_recordRs, _, valueT, Hint, Os ←
atomT
hintRs, Hint
originsRs, Os
344valid_recordRs, _, aliasT, V, Hint, Os ←
atomT
hintRs, Hint
originsRs, Os
346valid_recordRs, _, historyId, T, Hint, Os, Replacement ←
atomT
hintRs, Hint
originsRs, Os
Replacement ≡ none
or
Replacement = replacedV
349valid_recordRs, Bs, siteS, Os ←
351locationnone
352locationatF, L ←
atomF
integerL
L ≥ 0
353descriptionstatementK ←
atomK
354descriptiontypedC, _, _ ←
memberchkC, val, obj, fun
355descriptiongeneratedP, _ ←
atomP
356hint_, none
357hintindex_, Bs, _, namedB, Role ←
memberchkRole, value, address, piece
get_assocB, Bs, _
359originsRs, Os ←
is_listOs
maplistorigin_existsRs, Os
360origin_existsindexOrigins, _, _, O ←
get_assocO, Origins, _
361replacement_, tmpT ←
atomT
362replacement_, constN ←
integerN
363replacement_, globalN ←
atomN
364replacementindex_, _, Hs, historicalId ←
get_assocId, Hs, _
365valid_sitesitesBs, _, instL, I ←
get_assocL, Bs, N
integerI
I ≥ 0
I < N
367valid_sitesites_, Ps, phiT ←
get_assocT, Ps, _
368valid_sitesitesBs, _, jumpL ←
get_assocL, Bs, _