Fil-C on Nix

Filnix wiki

mbrock/filnix/nanofil

amd64_contract.pl

Audited machine forms for the scalar stack cache. Unknown forms fail, selecting the unchanged implementation, rather than guessing effects. Reads name register families. write(Family,Bits) records partial writes: 32 bits zero-extend, 8/16 preserve the rest. Memory effects include addressing-register reads; flags and control/frame effects are separate. This describes selected instructions, not IR liveness or GC roots. Calls are unconditional cache boundaries; their ABI argument reads are not enumerated. Do not use this as a general call-liveness contract. Three compiler-local save stubs additionally preserve r10/r11 on normal return (docs/abi-findings.md); their original PLT calls do not.

useslists
15:- discontiguousinstruction_effects/2
register?Name, ?Family, ?Bitsnondet

Name is the Bits-wide name of the general-purpose register Family (rax, ..., r15, rsp, rbp): register(eax, rax, 32). Concrete names index directly; family and width queries generate the name without searching every family for every operand in a large function.

register
23raxrax64
23eaxrax32
24axrax16
24alrax8
25rcxrcx64
25ecxrcx32
26cxrcx16
26clrcx8
27rdxrdx64
27edxrdx32
28dxrdx16
28dlrdx8
29rbxrbx64
29ebxrbx32
30bxrbx16
30blrbx8
31rsirsi64
31esirsi32
32sirsi16
32silrsi8
33rdirdi64
33edirdi32
34dirdi16
34dilrdi8
35rbprbp64
35ebprbp32
36bprbp16
36bplrbp8
37rsprsp64
37esprsp32
38sprsp16
38splrsp8
39r8r864
39r8dr832
40r8wr816
40r8br88
41r9r964
41r9dr932
42r9wr916
42r9br98
43r10r1064
43r10dr1032
44r10wr1016
44r10br108
45r11r1164
45r11dr1132
46r11wr1116
46r11br118
47r12r1264
47r12dr1232
48r12wr1216
48r12br128
49r13r1364
49r13dr1332
50r13wr1316
50r13br138
51r14r1464
51r14dr1432
52r14wr1416
52r14br148
53r15r1564
53r15dr1532
54r15wr1516
54r15br158
55xmm0xmm0128
55xmm1xmm1128
56xmm2xmm2128
56xmm3xmm3128
57xmm4xmm4128
57xmm5xmm5128
58xmm6xmm6128
58xmm7xmm7128
width
60b8
60w16
60l32
60q64
61expressionE ←
integerE
!
62expressionE ←
atomE
!
63expressionA + B ←
64addressmemD, B, B ←
integerD
registerB, B, 64
65addressmemD, B, I, B, I ←
integerD
registerB, B, 64
registerI, I, 64
66addressripE, [] ←
67addressgotpcrelS, [] ←
atomS
explicit_registers+Line, -Familiesdet

Families are the register families Line's operands name, addressing registers included, with repeats; [] for a label or directive.

73explicit_registersins_, _, Os, Rs ←
!
findallR,
memberO, Os
,
Rs
75explicit_registers_, []
operand_register+Operand, -Familynondet

Operand names the register Family: as a register, as a base or index of a memory operand, or under an indirection.

80operand_registerregN, F ←
registerN, F, _
81operand_registerO, R ←
addressO, Rs
memberR, Rs
82operand_registerindO, R ←
84sourceregR, W, F, [] ←
registerR, F, W
85sourceimmE, _, [], [] ←
86sourceM, W, Rs, readM, N ←
addressM, Rs
N is W//8
87destinationregR, W, [], writeF, W, [] ←
registerR, F, W
88destinationM, W, Rs, [], writeM, N ←
addressM, Rs
N is W//8

x86 does not admit a memory-to-memory two-operand instruction.

90not_two_memoryA, B ←
¬
instruction_effects+Line, -Effectsemidet

Effect is effect(Reads, Writes, Flags, Memory, Flow) for the audited asm.pl line Line: the register families it reads (addressing registers included), its writes write(Family, Bits) (32 bits zero-extend, 8 and 16 preserve the rest), its effect on the flags (preserve among them), its memory reads and writes read(M, Bytes) and write(M, Bytes), and its control or frame effect (next for a line that falls through). Fails for a form not audited here, so a caller keeps the unchanged implementation rather than guessing its effects.

102instruction_effectsinsmov, S, A, B, effectRs, Ws, preserve, Ms, next ←
widthS, W
sourceA, W, R1, M1
destinationB, W, R2, Ws, M2
appendR1, R2, Rs
appendM1, M2, Ms
105instruction_effectsinsmovabs, q, immN, regR, effect[], writeF, 64, preserve, [], next ←
integerN
registerR, F, 64
108instruction_effectsinsOp, S, A, regR, effectRs, writeF, W, preserve, Ms, next ←
extensionOp, From
widthS, W
W > From
sourceA, From, Rs, Ms
A ≠ imm_
registerR, F, W
111instruction_effectsinslea, q, M, regR, effectRs, writeF, 64, preserve, [], next ←
addressM, Rs
registerR, F, 64

leal computes the 64-bit address and writes its low 32 bits, zero-extended, like any 32-bit write.

115instruction_effectsinslea, l, M, regR, effectRs, writeF, 32, preserve, [], next ←
addressM, Rs
registerR, F, 32
117instruction_effectsinsOp, S, A, B, effectRs, Ws, write, Ms, next ←
memberchkOp, add, sub, and, or, xor, imul, btc
widthS, W
memberchkOp, imul, btc
→
W ≥ 16
B = reg_
else
true
sourceA, W, R1, M1
sourceB, W, R2, M2
B ≠ imm_
destinationB, W, _, Ws, M3
appendR1, R2, Rs
appendM1, M2, M3, Ms
123instruction_effectsinsOp, S, A, B, effectRs, [], write, Ms, next ←
memberchkOp, cmp, test
widthS, W
sourceA, W, R1, M1
sourceB, W, R2, M2
B ≠ imm_
appendR1, R2, Rs
appendM1, M2, Ms
127instruction_effectsinsneg, S, A, effectRs, Ws, write, Ms, next ←
widthS, W
sourceA, W, Rs, M1
destinationA, W, _, Ws, M2
appendM1, M2, Ms
130instruction_effectsinsOp, S, Count, A, effectRs, Ws, read_write, Ms, next ←
memberchkOp, shl, shr, sar
widthS, W
shift_countCount, R1
sourceA, W, R2, M1
destinationA, W, _, Ws, M2
appendR1, R2, Rs
appendM1, M2, Ms
134instruction_effectsinsOp, none, regR, effectF, writeF, 8, read, [], next ←
atom_concatset, CC, Op
registerR, F, 8
136instruction_effectsinsOp, q, regA, regB, effectFA, FB, writeFB, 64, read, [], next ←
atom_concatcmov, CC, Op
registerA, FA, 64
registerB, FB, 64
138instruction_effectsinscltd, none, [], effectrax, writerdx, 32, preserve, [], next
139instruction_effectsinscqto, none, [], effectrax, writerdx, 64, preserve, [], next
140instruction_effectsinsOp, S, A, effectrax, rdx|Rs, Ws, write, Ms, next ←
memberchkOp, div, idiv
widthS, W
memberchkW, 32, 64
sourceA, W, Rs, Ms
A ≠ imm_
Ws = writerax, W, writerdx, W
extension
144movzb8
144movsb8
145movzw16
145movsw16
145movsl32
146shift_countregcl, rcx
147shift_countimmN, [] ←
integerN
N ≥ 0
N ≤ 255
148conditionCC ←
memberchkCC, e, ne, z, nz, b, be, a, ae, l, le, g, ge, s, ns, p, np

SSE forms use full-family symbolic outputs: the cache never changes one, so the validator need not emulate floating arithmetic or lanes.

152instruction_effectsinsOp, none, A, B, effectRs, Ws, preserve, Ms, next ←
sse_transferA, B, W, Rs, Ws, Ms
154instruction_effectsinsOp, S, regA, regB, effectA, B, writeB, 128, preserve, [], next ←
memberchkOp, add, sub, mul, div
memberchkS, ss, sd
registerA, A, 128
registerB, B, 128
158instruction_effectsinsOp, none, regA, regB, effectA, B, [], write, [], next ←
memberchkOp, ucomiss, ucomisd
registerA, A, 128
registerB, B, 128
160instruction_effectsinsOp, none, regA, regB, effectA, B, writeB, 128, preserve, [], next ←
memberchkOp, cvtss2sd, cvtsd2ss, sqrtss, sqrtsd
registerA, A, 128
registerB, B, 128
163instruction_effectsinsOp, S, regA, regB, effectA, writeF, W, preserve, [], next ←
memberchkOp, cvttss2si, cvttsd2si
widthS, W
memberchkW, 32, 64
registerA, A, 128
registerB, F, W
167instruction_effectsinsOp, S, regA, regB, effectF, B, writeB, 128, preserve, [], next ←
memberchkOp, cvtsi2ss, cvtsi2sd
widthS, W
memberchkW, 32, 64
registerA, F, W
registerB, B, 128
171sse_movemovd, 32
171sse_movemovq, 64
172sse_transferregA, regB, W, F, writeB, 128, [] ←
registerA, F, W
registerB, B, 128
174sse_transferregA, regB, W, A, writeF, W, [] ←
registerA, A, 128
registerB, F, W
177instruction_effectsinsjmp, none, T, effectRs, [], preserve, Ms, branch ←
targetT, Rs, Ms
179instruction_effectsinsOp, none, T, effect[], [], read, [], branch ←
atom_concatj, CC, Op
targetT, [], []
181instruction_effectsinscall, S, T, effectrsp|Rs, Ws, write, Ms, call ←
memberchkS, none, q
targetT, Rs, M
preserving_callinscall, S, T
→
Kept = r10, r11
else
Kept = []
findallwriteR, W,
¬memberchkR, Kept
,
Ws
appendM, read_writeruntime, unknown, Ms
186instruction_effectsinspush, q, regR, effectF, rsp, writersp, 64, preserve, writestack, 8, frame ←
registerR, F, 64
189instruction_effectsinspop, q, regR, effectrsp, writeF, 64, writersp, 64, preserve, readstack, 8, frame ←
registerR, F, 64
192instruction_effectsinsleave, none, [], effectrbp, writerbp, 64, writersp, 64, preserve, readstack, 8, frame
194instruction_effectsinsret, none, [], effectrsp, writersp, 64, preserve, readstack, 8, terminal
196instruction_effectsinsud2, none, [], effect[], [], preserve, [], terminal

A fence orders memory; it reads and writes no register or frame slot.

198instruction_effectsinsmfence, none, [], effect[], [], preserve, [], next

lock cmpxchg R, M compares %rax's low W bits with M; equal, it writes R to M, else M to them. It reads rax, R and M's addressing registers, reads and writes M (the write happens either way), and defines the flags. At 32 bits a failure zero-extends rax and a success leaves it, so 32 and 64 record a whole write of an unknown value; 8 and 16 preserve the rest either way. A locked access is also a full fence.

205instruction_effectsins'lock cmpxchg', S, regR, M, effectrax, F|Rs, writerax, WA, write, readM, N, writeM, N, next ←
widthS, W
registerR, F, W
addressM, Rs
N is W//8
W ≥ 32
→
WA = 64
else
WA = W

A fixed-register asm line (docs/asm-fixed.md) is not an ins/3, so the register checker treats it as a region boundary. Its writes are 32 bits; cpuid's line saves rbx in rsi and brings its b result out in edi, so rbx is preserved. None of them changes the flags.

213instruction_effectsasm_fixedcpuid, effectrax, rcx, writerax, 32, writercx, 32, writerdx, 32, writersi, 64, writerdi, 32, preserve, [], next
216instruction_effectsasm_fixedxgetbv, effectrcx, writerax, 32, writerdx, 32, preserve, [], next
217instruction_effectsasm_fixedrdtsc, effect[], writerax, 32, writerdx, 32, preserve, [], next
218instruction_effectsasm_fixedrdtscp, effect[], writerax, 32, writercx, 32, writerdx, 32, preserve, [], next
220instruction_effectsasm_fixedM, effect[], [], preserve, [], next ←
memberchkM, lfence, sfence, pause
222instruction_effectslabelS, effect[], [], preserve, [], label ←
atomS
223instruction_effectsD, effect[], [], preserve, [], directive ←
225targetsymS, [], [] ←
atomS
226targetpltS, [], [] ←
atomS
227targetindO, Rs, Ms ←
sourceO, 64, Rs, Ms
O ≠ imm_
slow_call_stub?Helper, ?Labelnondet

Label is the compiler-local r10/r11 save stub for exactly Helper. Its emitter template is trusted; the native ABI fixture checks it.

slow_call_stub
233filc_pollcheck_slow'.Lnanofil_save_filc_pollcheck_slow'
234filc_store_barrier_for_lower_slow'.Lnanofil_save_filc_store_barrier_for_lower_slow'
235filc_object_ensure_aux_ptr_outline'.Lnanofil_save_filc_object_ensure_aux_ptr_outline'
preserving_call+Linesemidet

Line calls an authenticated local stub preserving full r10/r11 on normal return. No PLT or indirect target inherits this guarantee.

241preserving_callinscall, none, symLabel ←
243caller_clobberR, 64 ←
memberR, rax, rcx, rdx, rsi, rdi, r8, r9, r10, r11, rsp
244caller_clobberR, 128 ←
registerR, R, 128
245directivesectiontext
246directivep2alignN ←
integerN
247directivegloblS ←
atomS
248directivetypeS, function ←
atomS
249directivesizeS ←
atomS