Fil-C on Nix

Filnix wiki

the formal campaign

rocq/backend/EmitThunk.v

# Weak thunks

The lines of a weak thunk from its plan (Plan.v's thunk), as filc_amd64.pl's thunk//1 gives them. The thunk resolves its symbol through the getter and calls the fast entry when the callee's signature is the call's, else it transports its words as generic cells and the generic result back.

Every incoming word is saved below %rbp before the getter can clobber it: word i at -32-8i(%rbp). %rbx holds the thread, %r12 the return buffer of a buffered return, and -24(%rbp) the callee's function object.

Where the Prolog compiler has no clause for a form, the lines hold a todo (Layout.v) in that place.

From Stdlib Require Import String ZArith List Bool
From Nanofil Require Import Term Syntax Maps
From Backend Require Import Text Asm Plan PlanTerm Layout EmitObject Emit EmitInst
Import ListNotations
Open Scope string_scope
Open Scope Z_scope
Open Scope list_scope

The words a thunk saves

As never_signature/1: the signature no function object has, which a call of more than 16 words is compared with, so that it always goes generic.

30Definition thunk_never_signature: Z ≔
18446744073709551615

The offsets, below %rbp, of the first saved word and of the callee's function object.

34Definition saved_base: Z ≔
−32
35Definition saved_object: Z ≔
−24

As saved_operand/2: the fast word saved(I) is at -32-8I(%rbp). A cell of a thunk holds nothing else.

39Definition saved_operands : cell_source : optionadapter_operand ≔
match s with
⇒
SomeAoMemsaved_base − eight × i "rbp"
⇒
None

As saved_words/3: each word's class and where it is saved, the first at the index i.

47Fixpoint saved_wordsws : listword i : Z : listclass × optionadapter_operand ≔
match ws with
[ ]
⇒
[ ]
Wfk _ :: ws′
⇒
k, SomeAoMemsaved_base − eight × i "rbp" :: saved_wordsws′ i + 1

As mixed_thunk_thread//1: the thread into %rbx. A buffered return has its buffer in %rdi, kept in %r12, and the thread in %rsi, which the getter takes in %rdi.

56Definition mixed_thunk_threadr : protocol : listline ≔
match r with
⇒
ins"mov" "q" reg"rdi"; reg"rbx"
⇒
ins"mov" "q" reg"rdi"; reg"r12"; ins"mov" "q" reg"rsi"; reg"rbx"; ins"mov" "q" reg"rsi"; reg"rdi"

As incoming_bits//2: the word at a location of the fast convention into %rax; a stack word is at 16+8I(%rbp).

67Definition incoming_bitsl : location k : class : listline ≔
match l with
⇒
ins"mov" "q" regreg64r; reg"rax"
⇒
from_xmmk x "a"
⇒
match class_halfk with
Somehalf
⇒
ins"mov" class_suffixhalf memsixteen + eight × i "rbp"; regreg_name"a" half
None
⇒
todo"incoming class"

As save_mixed_words//3: each incoming word to where it is saved. An integer stack word is copied whole, as a register one is: w and l share a signature digit, so a thunk emitted for a 32-bit call also serves another object's 64-bit calls.

83Fixpoint save_mixed_wordsws : listword ls : listlocation ss : listclass × optionadapter_operand : listline ≔
match ws, ls, ss with
[ ], [ ], [ ]
⇒
[ ]
Wfk0 _ :: ws′, l :: ls′, _, s :: ss′
⇒
match k0 with
⇒
_
⇒
k0
++
match s with
SomeAoMemoff b
⇒
ins"mov" "q" reg"rax"; memoff b
_
⇒
todo"saved word"
++
save_mixed_wordsws′ ls′ ss′
_, _, _
⇒
todo"thunk words"

The callee

The getter's call, as thunk_getter(strong, Getter, G): the callee's function object in %rax and %rcx.

101Definition thunk_getter_callgetter : string : listline ≔
ins"xor" "l" reg"esi"; reg"esi"; ins0"call" pltgetter; ins"mov" "q" reg"rdx"; reg"rcx"

As thunk_getter//3. The getter of a weak declaration may be null, and then so is the object: the function check fails, as clang's thunk does.

109Definition thunk_getterref : thunk_ref getter g : string : listline ≔
match ref with
⇒
let null ≔ g ++ ".null"%string in
let got ≔ g ++ ".got"%string in
ins"cmp" "q" imm0; gotpcrelgetter; ins0"je" symnull ++ thunk_getter_callgetter ++ ins0"jmp" symgot; labelnull; ins"xor" "l" reg"eax"; reg"eax"; ins"xor" "l" reg"ecx"; reg"ecx"; labelgot

As mixed_thunk_call//1: the call of the fast entry of the object at -24(%rbp), with the thread, and before it the buffer of a buffered return.

126Definition mixed_thunk_callr : protocol : listline ≔
match r with
⇒
ins"mov" "q" reg"rbx"; reg"rdi"; ins"mov" "q" memsaved_object "rbp"; reg"rsi"; ins"call" "q" indmemfunction_object_fast_entry "rsi"
⇒
ins"mov" "q" reg"r12"; reg"rdi"; ins"mov" "q" reg"rbx"; reg"rsi"; ins"mov" "q" memsaved_object "rbp"; reg"rdx"; ins"call" "q" indmemfunction_object_fast_entry "rdx"

The generic call

As outline_ensure_rdi//2: with the thread in %rdi, a packet of more than the inline bytes needs the thread's outline buffers to hold the rest.

144Definition outline_ensure_rdisize : Z ok : string : listline ≔
size >? inline_bytes
→
ins"cmp" "q" immsize − inline_bytes; memthread_cc_outline_size "rdi"; ins0"jae" symok; ins"mov" "q" immsize − inline_bytes; reg"rsi"; ins0"call" plt"filc_thread_ensure_cc_outline_buffer_slow"; labelok
else
[ ]

As thunk_outline_ensure//2.

154Definition thunk_outline_ensuresize : Z g : string : listline ≔
size >? inline_bytes
→
ins"mov" "q" reg"rbx"; reg"rdi" :: outline_ensure_rdisize g ++ ".outline"%string
else
[ ]

As operand_thread_store//2: the operand into the thread at an offset, through %rax if it is in memory.

162Definition operand_thread_storeo : adapter_operand off : Z : listline ≔
match o with
⇒
ins"mov" "q" regr; memoff "rbx"
⇒
ins"mov" "q" memd b; reg"rax"; ins"mov" "q" reg"rax"; memoff "rbx"
⇒
todo"thread store"

One cell of saved_generic//1: an inline cell goes straight to the thread at %rbx, a later one through cell_stores//2 into the outline buffers. A cell with no lower gets a zero one.

172Definition saved_generic_cellc : cell : listline ≔
match c with
Celli k data lower
⇒
let ao ≔ thread_cc_inline_buffer + eight × i in
let lo ≔ thread_cc_inline_aux_buffer + eight × i in
match saved_operanddata, lower with
None, _
⇒
todo"thunk cell"
Somea, Somel
⇒
match saved_operandl with
None
⇒
todo"thunk cell lower"
Somelw
⇒
else
match a, lw with
AoMemad ab, AoMemld lb
⇒
ins"mov" "q" memad ab; reg"rax"; ins"mov" "q" memld lb; reg"rcx"; ins"mov" "q" reg"rbx"; reg"rdx" ++ cell_storesi "rdx"
_, _
⇒
todo"thunk cell"
Somew, None
⇒
→
adapter_loadk w "unused" ++ ins"mov" "q" reg"rax"; memao "rbx"; ins"mov" "q" imm0; memlo "rbx"
else
adapter_loadk w "unused" ++ ins"xor" "l" reg"ecx"; reg"ecx"; ins"mov" "q" reg"rbx"; reg"rdx" ++ cell_storesi "rdx"

As saved_generic//1: the saved words into the cells of the generic packet.

206Definition saved_genericcells : listcell : listline ≔
flat_mapsaved_generic_cell cells

The generic return

As rets_check//2: the check that the generic return's size in %rdx is at least need, with a call of filc_cc_rets_check_failure if it is not.

214Definition rets_checkneed : Z retok : string : listline ≔
ins"cmp" "q" immneed; reg"rdx"; ins0"jae" symretok; ins"mov" "q" reg"rdx"; reg"rdi"; ins"mov" "l" immneed; reg"esi"; ins"xor" "l" reg"edx"; reg"edx"; ins0"call" plt"filc_cc_rets_check_failure"; ins0"ud2" [ ]; labelretok

As thunk_typed_loads//2: each word of a returned tuple from its thread offset into its register, a float through %rax.

226Fixpoint thunk_typed_loadsrs : listresult_reg offs : listZ : listline ≔
match rs, offs with
[ ], [ ]
⇒
[ ]
RrGprg :: rs′, off :: offs′
⇒
ins"mov" "q" memoff "rbx"; regg :: thunk_typed_loadsrs′ offs′
RrXmmk x :: rs′, off :: offs′
⇒
ins"mov" "q" memoff "rbx"; reg"rax" :: to_xmmk "a" x ++ thunk_typed_loadsrs′ offs′
_, _
⇒
todo"typed result words"

As thunk_reg_loads//2: each generic return word, at its thread offset, into its register.

238Fixpoint thunk_reg_loadsoffs : listZ rs : liststring : listline ≔
match offs, rs with
[ ], _
⇒
[ ]
off :: offs′, r :: rs′
⇒
ins"mov" "q" memoff "rbx"; regr :: thunk_reg_loadsoffs′ rs′
_ :: _, [ ]
⇒
todo"result registers"

As thunk_generic_reg_words//4: the result words, if the channel has any, in %rdx and %rcx, or %xmm0 and %xmm1.

247Definition thunk_generic_reg_wordsc : adapter_channel need : Z srcs : listZ retok : string : listline ≔
match c with
⇒
[ ]
⇒
match srcs, class_halfk with
off, Somehalf
⇒
rets_checkneed retok ++ ins"mov" class_suffixhalf memoff "rbx"; regreg_name"d" half ++ to_xmmk "d" "xmm0"
_, _
⇒
todo"float result"
⇒
match result_registerstyped_ret_kindsks "rdx"; "rcx" "xmm0"; "xmm1" with
Somers
⇒
rets_checkneed retok ++ thunk_typed_loadsrs srcs
None
⇒
todo"result registers"
⇒
rets_checkneed retok ++ thunk_reg_loadssrcs "rdx"; "rcx"

As thunk_buffer_copies//2: each generic return word, at its thread offset, into the return buffer at off(%r12).

269Fixpoint thunk_buffer_copiessrcs : listZ off : Z : listline ≔
match srcs with
[ ]
⇒
[ ]
src :: srcs′
⇒
ins"mov" "q" memsrc "rbx"; reg"rax"; ins"mov" "q" reg"rax"; memoff "r12" ++ thunk_buffer_copiessrcs′ off + eight

As thunk_generic_return//7: after the generic entry's call and the test of its exception flag, the return of its result words, at the thread offsets srcs, then a jump to out. A buffered return's flag is the buffer's first byte.

281Definition thunk_generic_returnr : protocol c : adapter_channel need : Z srcs : listZ out retok exc : string : listline ≔
match r with
⇒
ins0"jnz" symout ++ thunk_generic_reg_wordsc need srcs retok ++ ins"xor" "l" reg"eax"; reg"eax"; ins0"jmp" symout
⇒
ins0"jnz" symexc ++ rets_checkneed retok ++ thunk_buffer_copiessrcs eight ++ ins"mov" "b" imm0; mem0 "r12"; ins"mov" "q" reg"r12"; reg"rax"; ins0"jmp" symout; labelexc; ins"mov" "b" imm1; mem0 "r12"; ins"mov" "q" reg"r12"; reg"rax"; ins0"jmp" symout

The thunk

The value a thunk compares the callee's signature with.

304Definition thunk_signatures : sigref : Z ≔

As thunk//1.

311Definition thunk_linest : thunk : listline ≔
let f ≔ name_textth_namet in
let fi ≔ entry_symbolth_sigt f in
let getter ≔ getter_symbolf in
let g ≔ ".L" ++ fi%string in
let generic ≔ g ++ ".generic"%string in
let out ≔ g ++ ".out"%string in
let fail ≔ g ++ ".fail"%string in
let retok ≔ g ++ ".retok"%string in
let exc ≔ g ++ ".exc"%string in
let res ≔ th_resultt in
let form ≔ ar_returnres in
let ws ≔ th_wordst in
let 'ls, ns, _ ≔ typed_locationsws form in
let pop ≔ stack_bytesns in
let saved ≔ saved_wordsws 0 in
let alloc ≔ round16eight + eight × Zlengthws in
Somesrcs, Some_
⇒
++
match th_reft with
++
DirDTypeLSymfi LSym"function"; labelfi; ins"push" "q" reg"rbp"; ins"mov" "q" reg"rsp"; reg"rbp"; ins"push" "q" reg"rbx"; ins"push" "q" reg"r12"; ins"sub" "q" immalloc; reg"rsp" ++ mixed_thunk_threadform ++ save_mixed_wordsws ls saved ++ thunk_getterth_reft getter g ++ function_checkfail ++ ins"mov" "q" reg"rcx"; memsaved_object "rbp"; ins"movabs" "q" immthunk_signatureth_sigt; reg"rsi"; ins"cmp" "q" reg"rsi"; memfunction_object_signature "rcx"; ins0"jne" symgeneric ++ reserve_stackpop ++ adapter_stacksaved ls g ++ adapter_registerssaved ls g ++ mixed_thunk_callform ++ ins0"jmp" symout; labelgeneric ++ thunk_outline_ensureth_args_sizet g ++ saved_genericth_cellst ++ ins"mov" "q" reg"rbx"; reg"rdi"; ins"mov" "q" memsaved_object "rbp"; reg"rsi"; ins"mov" "l" immth_args_sizet; reg"edx"; ins"call" "q" indmemfunction_object_generic_entry "rsi"; ins"test" "b" imm1; reg"al" ++ thunk_generic_returnform ar_channelres th_needt srcs out retok exc ++ labelfail; ins"mov" "q" reg"rax"; reg"rdi"; ins"mov" "q" reg"rcx"; reg"rsi"; ins0"call" plt"filc_check_function_call_fail"; ins0"ud2" [ ]; labelout; ins"lea" "q" mem−16 "rbp"; reg"rsp"; ins"pop" "q" reg"r12"; ins"pop" "q" reg"rbx"; ins"pop" "q" reg"rbp"; ins0"ret" [ ]; DirDSizeLSymfi
_, _
⇒
todo"thunk packet"

The third word is saved at -48(%rbp).

378Example saved_words_third: saved_operandCellSaved2 = SomeAoMem−48 "rbp"
Proof 1 step Qed
reflexivity