Fil-C on Nix

Filnix wiki

mbrock/filnix/nanofil

filc_abi.pl

Fil-C ABI lowering, before AMD64 placement and instruction selection.

lower_function/4 reads two synchronized views: checked operations (proof/access pairs already fused) and ordinary operations for liveness. It never inserts an instruction into either view. Each abi(Actions) occupies its original closed instruction interval, including all its helper calls. Memory operations pass through unchanged.

The product is a function with a frame, entry actions, lowered blocks, exception exit, public object and origins. Frames name payload slots, ordered roots and aliases, not native offsets. Fast words explicitly split pointers into address/lower; generic cells carry separate data and aux sources, including zero aux for scalars. registers/buffer(N) chooses the return protocol; AMD64 assigns registers and buffer bytes.

framePlain, Roots, Aliases includes localName, Size in Plain for nonescaping objects, separate from temporary slots. The special stack_auxName roots form a suffix before the setjmp slots; originWhich, Catch, NRoots, NSetjmps, NLocals describes that order to the collector. initialize_localName zeroes payload and aux and publishes the stack-aux root without a safepoint. It does not produce a heap lower or an address value.

returns_twice invokes native setjmp in the current body and restores payloads without a safepoint before writing the full native int result. Setjmp disables sharing even for unreachable sites. Its snapshots contain every logical temporary/phi payload, never frame bookkeeping or jmpbuf payloads. Snapshot roots are stable from entry; jmpbuf roots are last and persist across nonlocal returns. Variadic entry promotes the incoming packet eagerly and passes an invocation-lived hidden pointer to the private body. promote_incoming uses the original packet size, then roots that pointer in the body before any further safepoint. See docs/setjmp.md and abi-findings.md.

useslists
usesassoc
usesir
lower_function+Module, +FusedFunction, +LiveBlocks, -Product
42lower_functionModule, Func, LiveBlocks, abi_functionName, Linkage, Ret, Sig, Frame, Entry, Blocks, Exit, Object, Origins ←
Func = funcName, Linkage, Ret, Params, Blocks0
param_kindsParams, Ks
signatureRet, Ks, Sig
return_protocolRet, Return
body_paramsFunc, Params, BodyParams
frameBodyParams, LiveBlocks, Return, Frame, Snapshots
Frame = framePlain, Roots, _
lengthRoots, NRoots
findallN, memberlocalN, _, Plain, LocalNames
lengthLocalNames, NLocals
fast_paramsBodyParams, Incoming
Snapshots = snapshotsSites, Payload
sortSites, InitSites
Entry = native_frameReturn, stack_check, save_thread, save_returnReturn, zero_rootsNRoots, push_framec, receiveReturn, Incoming, snapshotsInitSites, Payload
Exit = pop_frame, exceptionReturn
maplistlower_blockModule, Ret, Return, Snapshots, Blocks0, Blocks
public_objectFunc, Return, Object
Snapshots = snapshotsSites, _
lengthSites, NSites
Origins = originc, catches, NRoots, NSites, NLocals, originn, no_catch, NRoots, NSites, NLocals

The bijective base-11 encoding is independent of native locations.

65signatureRet, ParamKs, Sig ←
ret_kindsRet, RetKs
maplistclass_digit, RetKs, RetDigits
maplistclass_digit, ParamKs, ArgDigits
lengthArgDigits, NArgs
NArgs ≤ 16
→
true
else
thrownanofil_error'more than 16 arguments'
bijective_valueRetDigits, R
bijective_valueArgDigits, A
Sig is 1 + R + 133 × A
class_digit
75w0
76l0
77p7
78s1
79d2
80bijective_value[], 0
81bijective_valueD|Ds, V ←
V is D + 1 + 11 × V1
84param_kindsPs, Ks ←
findallK, memberparamK, _, Ps, Ks
ret_kinds
85void[]
86tupleKsKs
87ww
88ll
89pp
90ss
91dd

A buffer has one exception byte followed by N full payload/lower words.

94return_protocolRet, Protocol ←
ret_kindsRet, Ks
foldlreturn_words, Ks, 0, N
N > 2
→
Protocol = bufferN
else
Protocol = registers
97return_wordsp, N0, N ←
N is N0 + 2
98return_wordsw, N0, N ←
N is N0 + 1
99return_wordsl, N0, N ←
N is N0 + 1
100return_wordss, N0, N ←
N is N0 + 1
101return_wordsd, N0, N ←
N is N0 + 1
103body_paramsFunc, Ps, Body ←
→
appendPs, paramp, tmphiddenva_snapshot, Body
else
Body = Ps
108frameParams, Blocks, Return, framePlain, Roots, Aliases, snapshotsSites, Payload ←
findallN−K,
memberparamK, tmpN, Params
or
memberblock_, Phis, Is, _, Blocks
memberphitmpN, K, _, Phis
or
memberI, Is
inst_destI, N, K
,
Temps0
sortTemps0, Temps
findallN−K,
memberblock_, Phis, _, _, Blocks
memberphitmpN, K, _, Phis
,
Shadows0
sortShadows0, Shadows
findallshadowN, memberN−_, Shadows, ShadowKeys
findallD,
memberblock_, _, Is, _, Blocks
memberlettmpD, w, setjmp_, _, Is
,
Sites
temp_storageSites, Params, Blocks, Temps, Keys, Lowers, Aliases
return_storageReturn, Extra
findalltmphiddensetjmp_bufD, memberD, Sites, JPlain
local_objectsfuncframe, local, void, Params, Blocks, Locals
findalllocalN, Size, memberN−Size, Locals, LocalStorage
findallstack_auxN, memberN−_, Locals, LocalRoots
appendKeys, ShadowKeys, thread, aux, Extra, JPlain, LocalStorage, Plain
findalllowershadowN, memberN−p, Shadows, ShadowRoots
findalllowersetjmp_snapshotD, memberD, Sites, SnapRoots
findalllowertmphiddensetjmp_bufD, memberD, Sites, JRoots
Explicit stack auxes are the final root group before jmpbufs,
as filc_function_origin_stack_aux_begin/end_index specifies.
appendLowers, ShadowRoots, SnapRoots, LocalRoots, JRoots, Roots
Preserve the old assoc's canonical payload traversal order.
findalltmpN, memberN−_, Temps, TempPayload
appendTempPayload, ShadowKeys, Payload0
sortPayload0, Payload
138temp_storage[], Ps, Bs, Temps, Keys, Lowers, Aliases ←
temp_slotsPs, Bs, Temps, Map, NS, NP
storage_keysscalar, NS, Scalars
storage_keyspointer, NP, Pointers
appendScalars, Pointers, Keys
mapliststorage_lower, Pointers, Lowers
assoc_to_listMap, Pairs
foldlstorage_alias, Pairs, Aliases, []
143temp_storage_|_, _, _, Temps, Keys, Lowers, [] ←
findalltmpN, memberN−_, Temps, Keys
findalllowertmpN, memberN−p, Temps, Lowers
146storage_keysPool, N, Keys ←
Last is N−1
findallstoragePool, I, between0, Last, I, Keys
148storage_lowerKey, lowerKey
149storage_aliasN−storagepointer, I, tmpN−storagepointer, I, lowertmpN−lowerstoragepointer, I|As, As
151storage_aliasN−storagescalar, I, tmpN−storagescalar, I|As, As
152return_storageregisters, []
153return_storagebuffer_, retbuf
154inst_destlettmpN, K, _, _, N, K
155inst_destflighttmpN, _, _, _, N, p
156inst_destlocal_loadtmpN, K, _, _, _, N, K
157inst_destcallresulttmpN, K, _, _, N, K
158inst_destcalltupleTos, _, _, N, K ←
memberresulttmpN, K, Tos

No physical locations: these are ordered words and their sources.

161fast_paramsPs, Ts ←
foldlfast_param, Ps, Ts, []
162fast_paramparamp, T, wfl, T, wfl, lowerT|Ts, Ts
163fast_paramparamK, T, wfK, T|Ts, Ts ←
K ≢ p
164fast_argsAs, Ts ←
foldlfast_arg, As, Ts, []
165fast_argargp, V, wfl, addrV, wfl, lowerV|Ts, Ts
166fast_argargK, V, wfK, valK, V|Ts, Ts ←
K ≢ p

Generic cells are one eightbyte per source value, not per fast word. Source names for adapters are abstract too: data(I), aux(I), snapshot.

170generic_argsArgs, Cells ←
findallcellI, B, V, Lower,
nth0I, Args, argK, V
value_lowerK, V, Lower
,
Cells
173payload_classp, l
174payload_classK, K ←
K ≢ p
175value_lowerp, V, lowerV
176value_lowerK, _, none ←
K ≢ p
177generic_paramsKs, Ts ←
findallwfB, Source,
nth0I, Ks, K
packet_wordK, I, B, Source
,
Ts
179packet_wordp, I, l, dataI
180packet_wordp, I, l, auxI
181packet_wordK, I, K, dataI ←
K ≢ p
result_tos
183void[]
184resultT, KresultT, K
185tupleTosTos
186result_retvoid, void
187result_retresult_, K, K
188result_rettupleTos, tupleKs ←
findallK, memberresult_, K, Tos, Ks
190result_slotsTos, Slots ←
foldlresult_slot, Tos, Slots, []
191result_slotresultT, p, T, lowerT|Ss, Ss
192result_slotresultT, K, T|Ss, Ss ←
K ≢ p

Results name their channels explicitly. Scalar floating results are distinct from integer slots; the target must test the exception first.

196fast_resultRes, resultReturn, Channels ←
result_retRes, Ret
return_protocolRet, Return
Res = resultT, K
memberchkK, s, d
→
Channels = floatK, T
else
result_tosRes, Tos
result_slotsTos, Slots
Channels = slotsSlots
200generic_resultRes, receive_genericNeed, Cells ←
result_tosRes, Tos
lengthTos, N
Need is 8 × N
findallcellI, K, T, Lower,
nth0I, Tos, resultT, K
value_lowerK, T, Lower
,
Cells
205lower_blockModule, Ret, Return, Snapshots, blockL, Ps, Is0, J0, blockL, Ps, Is, J ←
maplistlower_instModule, Snapshots, Is0, Is
lower_jumpRet, Return, J0, J
209lower_instModule, _, callRes, globalF, Args, abiActions ←
!
call_signatureRes, Args, Sig
direct_targetModule, F, Sig, Target
fast_resultRes, Result
Result = resultReturn, _
fast_argsArgs, Words
Actions = originc, reserve_resultReturn, invokeTarget, Return, Words, Result, release_resultReturn
214lower_inst_, _, fusedcheck_function, callRes, P, Args, abiActions ←
!
call_signatureRes, Args, Sig
fast_resultRes, Result
Result = resultReturn, _
fast_argsArgs, Words
generic_argsArgs, Cells
lengthArgs, N
Size is 8 × N
generic_resultRes, GenericResult
Actions = originc, reserve_resultReturn, dispatchP, Sig, Return, Words, Result, Size, Cells, GenericResult, release_resultReturn
222lower_instModule, _, lettmpD, p, copy, globalS, abioriginc, getterTarget, c, D ←
!
getter_targetModule, S, Target
225lower_inst_, snapshots_, Keys, lettmpD, w, setjmpKind, Buf, Mask, abioriginc, returns_twiceD, KindNo, Buf, Mask, Keys ←
!
setjmp_kindKind, KindNo
228lower_inst_, _, lettmpD, p, va_args, constOff, abisnapshot_cursortmphiddenva_snapshot, Off, D ←
!
230lower_inst_, _, lettmpD, K, va_arg, P, abioriginc, consume_vaK, P, Size, D ←
!
233lower_inst_, _, local_allocName, _, abiinitialize_localName ←
!
234lower_inst_, _, I, I

Returns are actions too: materialize before popping roots, then signal success in the appropriate exception channel. No safepoint intervenes.

238lower_jumpK, Return, retV, abi_returnreturn_valueK, V, pop_frame, successReturn ←
!
239lower_jumptupleKs, bufferN, retsVs, abi_returnreturn_bufferWords, success_buffer, pop_frame, native_return ←
!
maplistreturn_arg, Ks, Vs, Args
fast_argsArgs, Words
lengthWords, N
242lower_jump_, registers, retsVs, abi_returnreturn_registersVs, pop_frame, successregisters ←
!
244lower_jump_, Return, ret, abi_returnpop_frame, successReturn ←
!
245lower_jump_, _, J, J
246return_argK, V, argK, V
setjmp_kind
248setjmp0
249'_setjmp'1
250sigsetjmp2
252call_signatureRes, Args, Sig ←
result_retRes, Ret
findallK, memberargK, _, Args, Ks
signatureRet, Ks, Sig
254direct_targetModule, F, _, implementationF, Sig ←
func_namedModule, F, Func
!
Func = func_, _, Ret, Ps, _
signatureRet, Ks, Sig
257direct_target_, F, Sig, entryF, Sig
258getter_targetmoduleItems, S, Target ←
memberchkfuncS, _, _, _, _, Items
or
memberchkdataS, _, _, _, Items
→
Target = local_getterS
else
Target = external_getterS

Adapter plans are consumed by public generic entries and weak thunks. The void generic result is one zero word, despite having no fast value.

264adapter_returnRet, adapter_resultReturn, Channel, Size, Packet ←
return_protocolRet, Return
ret_kindsRet, Ks
lengthKs, N
Size is 8 × max1, N
Ks = []
→
Channel = void
Ks = K
memberchkK, s, d
→
Channel = floatK
else
Channel = words
findallS, memberwf_, S, Words, Copies
Ks = []
→
Zeros = data0, aux0
else
findallauxI,
nth0I, Ks, K
K ≢ p
,
Zeros
Packet = packetCopies, Zeros
274public_objectFunc, _, Object ←
Func = func_, _, Ret, Ps, _
lengthPs, N
Need is 8 × N
adapter_returnRet, Result
→
generic_paramsKs, Prefix
appendPrefix, wfl, snapshot, wfl, snapshot, Words
Object = generic_onlypromote_incomingNeed, Words, Result
else
signatureRet, Ks, Sig
Object = fast_and_genericSig, adaptNeed, Words, Result
284lower_thunksModule, Thunks ←
Module = moduleItems
findalltF, Sig, Ret, Ks,
memberfunc_, _, _, _, Bs, Items
memberblock_, _, Is, _, Bs
membercallRes, globalF, Args, Is
direct_targetModule, F, unused, Target
Target = entryF, unused
result_retRes, Ret
findallK, memberargK, _, Args, Ks
signatureRet, Ks, Sig
,
Ts0
sortTs0, Ts1
maplistlower_thunk, Ts, Thunks
293one_per_symbol[], []
294one_per_symbolT|Ts0, T|Ts ←
T = tF, Sig, _, _
excludesame_symbolF, Sig, Ts0, Ts1
296same_symbolF, Sig, tF, Sig, _, _
297lower_thunktF, Sig, Ret, Ks, thunkF, Sig, Words, Cells, ArgsSize, Result, Need ←
findallparamK, tmpI, nth0I, Ks, K, Ps
fast_paramsPs, Words
thunk_cellsKs, 0, 0, Cells
lengthKs, N
ArgsSize is 8 × N
adapter_returnRet, Result
ret_kindsRet, RKs
lengthRKs, NR
Need is 8 × NR

Native saves are indexed by flattened word, generic cells by value. The caller, not these saves, roots every incoming capability.

305thunk_cells[], _, _, []
306thunk_cellsK|Ks, I, W, cellI, B, savedW, Lower|Cells ←
K ≡ p
→
L is W + 1
Lower = savedL
Next is W + 2
else
Lower = none
Next is W + 1
I1 is I + 1
thunk_cellsKs, I1, Next, Cells