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.
Product is the ABI plan, abi_function/10, of the checked function FusedFunction, whose checks are fused with their accesses (filc_amd64's fused_blocks/5), LiveBlocks being the same blocks as liveness reads them: its frame(Plain, Roots, Aliases), entry actions, lowered blocks, exception exit, public object and origins (this module's header). Symbols is the module's ir:module_symbols/2 (or the module itself). Calls and getters look their targets up in it. The frame's pools are shared; lower_function/5 chooses.
As lower_function/4, with the temporaries' slot pools shared or own (frame_slots:temp_slots/9).
Sig is Fil-C's fast-call signature number for a function returning Ret (a class, void or tuple([K1, K2])) on parameters of classes ParamKs: 1 + R + 133 * A, R and A the bijective base-11 encodings of the return and argument word digits, independent of native locations. More than 16 argument words have no fast signature, and get never_signature/1. It names private bodies, pizlonatedFIP<Sig>_F.
More than 16 words have no fast signature (clang's is generic, 0). A call site gets one no function object has, the largest 64-bit value (fast signatures stay below 2^63), so it always goes generic; the frontend makes such a definition generic-only, and its private body is named with this one.
A buffer has one exception byte followed by N full payload/lower words.
the phi shadows' plain keys, root keys and aliases (docs/perf.md item 9). A shadow is stored and loaded only inside one edge's parallel copy (filc_amd64.pl's edge_moves//4, which checks that no safepoint is inside it), so in the allocating modes (pools not shared) of a function without setjmp sites, the i-th phi of class C in its block uses shadow_slot(C, i), C being int (w, l), p or float (s, d), and a pointer phi's lower the root lower(shadow_slot(p, i)). A temporary defined more than once (Defs, one N-K per definition) keeps a shadow of its own, as every phi does in the reference layout.
N-Class-Index for each phi of a block whose destination is defined once, numbered within its class.
Under --lowers=shared a derived flight's address has a slot of its own and its lower is its root's lowers[] entry (lower_roots.pl).
No physical locations: these are ordered words and their sources.
Generic cells are one eightbyte per source value, not per fast word. Source names for adapters are abstract too: data(I), aux(I), snapshot.
Results name their channels explicitly. Scalar floating results are distinct from integer slots; the target must test the exception first. A tuple with a float word names each slot's class: typed(K-Slot).
Returns are actions too: materialize before popping roots, then signal success in the appropriate exception channel. No safepoint intervenes.
Sig names the weak thunk a direct call goes through. Every call of more than 16 words has the never signature, so its thunk is named never(R-A) by the whole word list instead: two such calls with different words (a variadic callee's, say) must not share one thunk and its packet size.
the call is a memcpy or memmove of a constant 1 to 16 bytes that the module does not define, so the emitter may put the inline filter of docs/perf.md item 7a in front of the unchanged call; Filter is copy_filter(Dst, Src, N, Res).
A weak definition may be replaced by another object's, so the module's own calls and addresses of it go through its PLT getter, as an external symbol's do; a weak declaration's getter may be null.
Adapter plans are consumed by public generic entries and weak thunks. The void generic result is one zero word, despite having no fast value.
Thunks are the plans of the weak external thunks Module needs: one per symbol and signature for each direct call to a function the module does not define, as clang emits one per external callee (docs/abi-findings.md, Weak thunks are shared by signature).
Ref is weak if the callee is declared weak: its getter may be null; local if this module defines it with local linkage, so that the thunk is local too and never merges with another object's thunk for its own local function of the same name, as clang keeps such thunks local. Local data called directly is the same: its thunk calls this module's getter, whose function check traps, and another object's function d must not replace it.
Native saves are indexed by flattened word, generic cells by value. These saves are not roots: until the callee's entry records them, an incoming capability is rooted only by its caller (abi-findings.md).