Fil-C on Nix

Filnix wiki

the formal campaign

rocq/theories/Syntax.v

The core subset of the checked IR, transcribed from ir.pl: its grammar as inductive types, one constructor per functor of the intermediate representation (IR), and the small tables of facts the semantics reads.

The subset is formal.md's: filc mode, the checked stage, the integer and pointer classes, check and its reuse vocabulary, the raw_* accesses, flight, poll edges, call, raw_call and free, and named locals, touch (a vector access's check, whose proof it uses without accessing anything); and, for the Lua suite, the float classes with their conversions (Ieee.v) and variadic functions (va_args, va_arg); and setjmp, whose syntax and validation are ir.pl's while its step is stuck, as the interpreter refuses it. Vectors, cas and atomics, asm_fixed and plain mode are deferred: a function that uses them is IOutside, which has an address but no body (Transport.v decides).

Items, blocks and instructions

Gallinair.pl
IFuncffunc(Name, Linkage, Ret, Params, Blocks)
IOutsiden ta func named n outside the core subset, kept as its term t
IDatan lk al itemsdata(Name, Linkage, Align, Items)
IAliasn lk talias(Name, Linkage, Target)
Phid k pairsphi(tmp(D), K, [Pred-Value])
Letd k o argslet(tmp(D), K, Op, Args)
Flightd o args lowerflight(tmp(D), Op, Args, Lower)
RawStorew off v araw_store(Width, Off, Value, Addr)
RawStorePointeroff v a esraw_store_pointer(Off, Value, Addr, Effects)
Callr callee argscall(Res, Callee, [arg(K, Value)])
RawCallr callee argsraw_call(Res, Callee, Args)
LocalAllocx sizelocal_alloc(Name, Size)
Locfile lineloc(File, Line)
Touchqtouch(tmp(Q))
From Stdlib Require Import String ZArith List Bool
From Nanofil Require Import Term
Import ListNotations
Open Scope Z_scope

Classes and widths

As class(K, Kind, Bits): the class of a value, one letter in ir.pl. Besides the integer, pointer and float classes there are the two proof classes of the checked stage, whose values are pointers a check has cleared.

Classir.plValue
Wwa 32-bit integer
Lla 64-bit integer
Ppa pointer: an address with its capability
Aaa proven address, the result of a check
Ffa proven function, the result of check_function
Singlesan IEEE binary32 float, held as its 32-bit pattern
Doubledan IEEE binary64 float, held as its 64-bit pattern
Extendedxan x87 extended float, held as its 80-bit pattern

Extended is C's long double, never a parameter, result, call argument or phi.

63Inductive class: Type ≔
|W
|L
|P
|A
|F
|Single
|Double
|Extended

Equality of classes.

66Definition class_eqbk k′ : class : bool ≔
match k, k′ with
W, W | L, L | P, P | A, A | F, F | Single, Single | Double, Double | Extended, Extended
⇒
true
_, _
⇒
false

As float_class(K): whether the class k is a float class, s, d or x.

75Definition float_classk : class : bool ≔
match k with
_
⇒
false

As int_class(K): whether the class k is an integer class, w or l.

80Definition int_classk : class : bool ≔
match k with
W | L
⇒
true
_
⇒
false

As modulus(K, Mod): the values of class k are the integers from 0 up to Mod - 1, a float's being its bit pattern; a P value's address has the modulus of L. The proof classes have none.

86Definition modulusk : class : optionZ ≔
match k with
⇒
Some2 ^ 32
L | P | Double
⇒
Some2 ^ 64
⇒
Some2 ^ 80
A | F
⇒
None

As class_bits(K, Bits): the width in bits of an integer class k.

96Definition class_bitsk : class : optionZ ≔
match k with
⇒
Some32
⇒
Some64
_
⇒
None

As half_modulus(K, Half): half the modulus of an integer class k; the values from Half up to Mod - 1 are the negative ones when read as signed, in two's complement.

106Definition half_modulusk : class : optionZ ≔
match k with
⇒
Some2 ^ 31
⇒
Some2 ^ 63
_
⇒
None

The width of a store or of a data item, what size of value it writes: Wb and Wh are ir.pl's b and h, a byte and a 16-bit halfword, and the others are the classes w l p s d x.

116Inductive width: Type ≔
|Wb
|Wh
|Ww
|Wl
|Wp
|Ws
|Wd
|Wx

As width_bytes(Width, Size): the bytes a width w writes; an x87 extended float takes 10.

120Definition width_bytesw : width : Z ≔
match w with
⇒
1
⇒
2
⇒
4
⇒
8
⇒
8
⇒
4
⇒
8
⇒
10

A narrow integer type, the T of a narrow load and of ext(T): ir.pl's sw uw sh uh sb ub, a signed or unsigned word (4 bytes), halfword (2) or byte (1).

129Inductive narrow: Type ≔
|Sw
|Uw
|Sh
|Uh
|Sb
|Ub

Whether an integer is read as signed, in two's complement, or as unsigned.

133Inductive signedness: Type ≔
|Signed
|Unsigned

As narrow_type(T, Bytes, Signedness): the size in bytes and the signedness of the narrow type t.

137Definition narrow_typet : narrow : Z × signedness ≔
match t with
⇒
⇒
⇒

The type of a raw load: LdL for l, LdS, LdD and LdX for the float classes s d x, or a narrow type. This is load_type/1 without p, which RawLoadPointer reads instead.

148Inductive load_type: Type ≔
|LdL
|LdS
|LdD
|LdX
|LdNarrow
t : narrow

Ops

A comparison's Cond: eq and ne at any numeric class, the signed and unsigned orders at w and l, and le lt ge gt o (ordered) and uo (unordered) at a float class.

155Inductive cond: Type ≔
|Eq
|Ne
|Sle
|Slt
|Sge
|Sgt
|Ule
|Ult
|Uge
|Ugt
|Le
|Lt
|Ge
|Gt
|Ord
|Unord

The rights an access asks for: to read, or to write, which a read-only object refuses.

161Inductive rights: Type ≔
|Read
|Write

Equality of rights.

164Definition rights_eqbr r′ : rights : bool ≔
match r, r′ with
⇒
true
_, _
⇒
false

Which end of an object's bounds a half check tests natively, ir.pl's upper or lower; an earlier check proves the other.

170Inductive bound: Type ≔
|Upper
|Lower

One step of the protocol by which a pointer load or store moves an address and its capability, as pointer_memory_effects/2 lists them:

  • a heap load reads the aux word (AuxRead), resolves the lower from it (LowerResolve), then reads the address (AddressRead);
  • a heap store makes sure the aux exists (AuxEnsure), applies the collector's barrier to the stored lower (LowerBarrier), reloads its destination from a root (RootedReload), then writes the lower (LowerWrite) before the address (AddressWrite);
  • AtomicRead and AtomicWrite are Fil-C's atomic pointer helpers, each a whole protocol.
185Inductive effect: Type ≔
|AuxRead
|LowerResolve
|AddressRead
|AtomicRead
|AuxEnsure
|LowerBarrier
|RootedReload
|LowerWrite
|AddressWrite
|AtomicWrite

Equality of effects.

Equality of two effect lists, step by step.

200Definition effects_eqbes es′ : listeffect : bool ≔
lengthes =? lengthes′%nat && forallbλp ⇒ effect_eqbfstp sndp combinees es′

As pointer_memory_effects(load, Effects): the protocol of a heap pointer load, one of the two that evidence_preserving accepts.

205Definition load_effects: listeffect ≔

As pointer_memory_effects(local_load, Effects): the protocol of a pointer load from a named local, whose explicit stack aux has no atomic box, so there is no lower to resolve.

209Definition local_load_effects: listeffect ≔

Which of the three returns-twice calls a setjmp(Kind) is: setjmp, '_setjmp' or sigsetjmp (op_kind/2).

213Inductive setjmp_kind: Type ≔
|SjSetjmp
|SjUnderscore
|SjSig

An operation, the Op of a let (ir.pl's Ops table): one constructor per functor in the core subset, with the functor's parameters as its arguments. The arithmetic and conversions come first, then the alloc, the checks of the checked stage and their reuse forms, the raw loads, the variadic ops and setjmp. What each reads is op_arg_classes.

221Inductive op: Type ≔
|Add
|Sub
|Mul
|Div
|Udiv
|Rem
|Urem
|And
|Or
|Xor
|Sar
|Shr
|Shl
|Neg
|Copy
|Ptoi
|Itop
|Cmp
c : cond k : class
1 if two values of class k compare by c, else 0
|Ext
t : narrow
the low bytes of a w, extended by t

The float conversions: ext(s) and ext(d) name their source class; stosi is FloatToIntSingle Signed, dtoui is FloatToIntDouble Unsigned; swtof is IntToFloatW Signed, ultof is IntToFloatL Unsigned.

|ExtF
k : class
widen a float of class k
|Truncd
|Truncx
|FloatToInt
k : class sg : signedness
a float of class k to an integer, truncated
|IntToFloat
k : class sg : signedness
an integer of class k to the nearest float
|Cast
the same bits at the other class: w and s, l and d
|Sqrt
the square root of a float, rounded to nearest
|Alloc
align : Z
a new zeroed heap object of the l size read
|Check
lo hi : Z r : rights align : Z
check of a pointer P: trap unless P may access from P+lo up to P+hi
|CheckAhead
lo hi : Z r : rights align ahi : Z
a check whose bytes from ahi up to hi are for a later access: check_ahead
|Renew
lo hi : Z r : rights align : Z
reuse a dominating check's bounds: renew
|RenewHull
lo hi : Z r : rights align : Z
a renew whose bounds two earlier proofs cover: renew_hull
|HalfCheck
b : bound lo hi : Z r : rights align : Z
a check that tests only the bound b: half_check
|SpanTest
lo hi : Z
1 if the pointer's object holds the span, else 0: span_test
|RenewIf
lo hi : Z r : rights align : Z
a check whose bounds a passing span test proves: renew_if
|Limit
size : Z
an opaque snapshot of the pointer's bounds: limit
|CheckLimit
lo hi : Z r : rights align : Z
a check of the bounds against a limit: check_limit
|CheckLocal
x : name off bytes : Z r : rights align : Z
prove an access to the named local x
|CheckFunction
trap unless the pointer is a function's exact address
|RawLoad
t : load_type off : Z
a non-pointer load at the proven address plus off
|RawLoadPointer
off : Z es : listeffect
a pointer load at the proven address plus off, by the protocol es

va_args takes a variadic function's entry argument snapshot at a byte offset; va_arg loads the next argument through a va_list.

|VaArgs
|VaArg

setjmp(Kind) saves its caller's context into the buffer it reads and returns 0, and returns again, nonzero, when a longjmp restores it (docs/setjmp.md).

|Setjmp
kind : setjmp_kind

Values, instructions and jumps

A value an instruction reads (ir.pl's Values table).

262Inductive value: Type ≔
|Tmp
n : name
tmp(N), a temporary, defined once
|Const
n : Z
const(N), an integer; at a float class, its bit pattern
|Global
s : name
global(S), the address of the function or data object s

An argument of a flight's address computation: an l value, or address(P), the address bits of the pointer P.

269Inductive flight_arg: Type ≔
|FValue
v : value
|FAddress
p : value

A flight's Lower, where its capability comes from: none, or lower(P), exactly the capability of the pointer P.

273Inductive flight_lower: Type ≔
|LowerNone
|LowerOf
p : value

A call's Res, where its results go.

276Inductive call_result: Type ≔
|CVoid
void: none
|CResult
d : name k : class
result(tmp(D), K): the temporary d, of class k
|CTuple
rs : listname × class
tuple([result(tmp(D1), K1), ...]): a pair of results

As arg(K, Value): a call's argument, the value v passed at class k.

283Inductive arg: Type ≔
|Arg
k : class v : value

An instruction of a block, in the core subset; the table at the top of the page gives each one's ir.pl term. The raw forms do no checking of their own: they read a proof of class A or F.

288Inductive inst: Type ≔
|Let
d : name k : class o : op args : listvalue
d, of class k, is o of args
|Flight
d : name o : op args : listflight_arg lower : flight_lower
the pointer d: the address o computes, with the capability lower names
|RawStore
w : width off : Z v a : value
store v at the proven address a plus off
|RawStorePointer
off : Z v a : value es : listeffect
store the pointer v there, by the protocol es
|Call
r : call_result callee : value args : listarg
call callee, a symbol or a pointer
|RawCall
r : call_result callee : value args : listarg
call the proven function callee, of class F
|LocalAlloc
x : name size : Z
declare the named local x, of size zeroed bytes
|Loc
file : term line : Z
a source position; does nothing
|Touch
q : name
touch(tmp(Q)): use the proof q of the check just before it, and do nothing else

A jump's target: L, or poll(L), a pollcheck on the edge before going to L.

301Inductive target: Type ≔
|To
l : name
|Poll
l : name

As edge_label(T, L): the label a target t goes to, polling or not.

305Definition edge_labelt : target : name ≔
match t with
Tol | Polll
⇒
l

The jump that ends a block (ir.pl's Jumps table).

308Inductive jump: Type ≔
|JGoto
t : target
goto(L)
|JBranch
v : value t1 t2 : target
to t1 if v is nonzero, else to t2
|JRet
v : value
ret(V): return v
|JRets
vs : listvalue
rets([V1, V2]): return a tuple
|JRetVoid
ret, from a void function
|JUnreachable
stop the program, a trap instruction

As phi(tmp(D), K, [Pred-Value]): a phi at the head of a block, defining d, of class k, as the value paired with the block control came from.

319Inductive phi: Type ≔
|Phi
d : name k : class pairs : listname × value

As block(Label, Phis, Insts, Jump): a block, its phis, then its instructions, then the one jump that ends it.

323Record block: Type ≔ mkBlock
b_label
 : name
b_phis
 : listphi
b_insts
 : listinst
b_jump
 : jump

A function's Ret: a class, void, or tuple([K1, K2]).

331Inductive ret_type: Type ≔
|RClass
k : class
|RVoid
|RTuple
ks : listclass
two words, a struct of 9 to 16 bytes

As func(Name, Linkage, Ret, Params, Blocks): a function.

337Record func: Type ≔ mkFunc
fn_name
 : name
fn_linkage
 : term
export, local or weak, kept as its term
fn_ret
fn_params
 : listclass × name
each a class and a temporary
fn_blocks
 : listblock
the entry block first

As variadic_func(Func): whether the function f takes the entry argument snapshot of a variadic function, which it does with a va_args anywhere in its blocks.

348Definition variadic_funcf : func : bool ≔
existsb
λ b  ⇒
existsb
λ i  ⇒
match i with
⇒
true
_
⇒
false

Data

A data object's Linkage (ir.pl's Data section). LExport, LLocal and LWeak are export, local and weak; LRo l is ro(L), read-only data, and LTls l is tls(L), an object each thread has its own instance of, both of the inner linkage l.

360Inductive linkage: Type ≔
|LExport
|LLocal
|LWeak
|LRo
l : term
|LTls
l : term
|LInit
which prio : term
init(ctor or dtor, Priority): a constructor or destructor table entry, not an object
|LExternWeak
extern(weak): a weak declaration of a symbol defined elsewhere, not an object
|LOther
t : term
any other Linkage term: valid_module/1 rejects it, so only the validator meets it

A data Item, a piece of a data object's initial contents. item(l, sym(Name, Off)) is plain mode's, outside the subset; item(p, sym(Name, Off)) is Sym.

370Inductive data_item: Type ≔
|Item
w : width n : Z
item(W, const(N)): n at width w
|Str
codes : listZ
item(b, str(Chars)): the bytes themselves
|Sym
s : name off : Z
a pointer to s plus off, with the capability of s
|Zero
n : Z
zero(N): n zero bytes
|ItemOther
t : term
any other term, kept as it is: valid_module/1 rejects it (data_item_shape/1), so only the validator meets it

As item_size(Item, Size0, Size): the bytes a data item takes.

378Definition item_sizeit : data_item : Z ≔
match it with
Strcs
⇒
Z.of_natlengthcs
Sym_ _
⇒
8
⇒
n

The bytes a list of data items takes, the size of the object they make.

389Definition items_sizeits : listdata_item : Z ≔
fold_leftλs it ⇒ s + item_sizeit its 0

Modules

An item of a module; the table at the top of the page gives each one's ir.pl term.

397Inductive item: Type ≔
|IFunc
f : func
a function of the core subset
|IOutside
n : name t : term
a function outside it: an address, no body
|IData
n : name lk : linkage align : Z items : listdata_item
a data object
|IAlias
n : name lk : term target : name
a second symbol n for the function or data object target

A module, as module(Items): its items in order.

404Definition module ≔
listitem

Facts about instructions

As op_arg_classes(Op, K, filc, Classes): the classes at which the op o reads its arguments when its result has class k; none where o cannot have a result of class k.

411Definition op_arg_classeso : op k : class : optionlistclass ≔
match o with
⇒
match k with
⇒
SomeW; W
⇒
SomeL; L
⇒
SomeP; L
_
⇒
None
Mul | Div | Udiv | Rem | Urem | And | Or | Xor
⇒
Somek; k
Sar | Shr | Shl
⇒
Somek; W
⇒
Somek
⇒
SomeP
⇒
SomeL
Cmp_ ck
⇒
Someck; ck
⇒
SomeW
⇒
Somefk
⇒
Somefk
⇒
Someik
⇒
Somek
kind_arg_classes(float_convert, sqrt, K, _, K)
⇒
match k with
⇒
SomeSingle
⇒
SomeW
⇒
SomeDouble
⇒
SomeL
_
⇒
None
⇒
SomeL
Check_ _ _ _ | CheckAhead_ _ _ _ _
⇒
SomeP
Renew_ _ _ _
⇒
SomeA; P
RenewHull_ _ _ _
⇒
SomeA; A; P
HalfCheck_ _ _ _ _
⇒
SomeA; P
⇒
SomeP
RenewIf_ _ _ _
⇒
SomeP; W
⇒
SomeP
CheckLimit_ _ _ _
⇒
SomeP; L
CheckLocal_ _ _ _ _
⇒
Some[ ]
⇒
SomeA
⇒
SomeA
⇒
SomeL
⇒
SomeP
⇒
match k with
⇒
SomeP; W
_
⇒
None
kind_arg_classes(setjmp, _, w, filc, p, w)

Whether the value v is a symbol's address, a Global.

459Definition is_globalv : value : bool ≔
match v with
⇒
true
_
⇒
false

As evidence_preserving(Inst): the instruction i reaches no safepoint and cannot free an object or change its bounds or rights, so the evidence of checks before it still holds after it.

466Definition evidence_preservingi : inst : bool ≔
match i with
Loc_ _
⇒
true
⇒
true
Flight_ _ _ _
⇒
true
Let_ A Check_ _ _ _ _ | Let_ A CheckAhead_ _ _ _ _ _ | Let_ A Renew_ _ _ _ _ | Let_ A RenewHull_ _ _ _ _ | Let_ A HalfCheck_ _ _ _ _ _ | Let_ W SpanTest_ _ _ | Let_ A RenewIf_ _ _ _ _ | Let_ L Limit_ _ | Let_ A CheckLimit_ _ _ _ _
⇒
true
Let_ _ RawLoad_ _ _
⇒
true
RawStorew _ _ _
⇒
match w with
⇒
false
_
⇒
true
Let_ _ Add | Sub | Mul | Div | Udiv | Rem | Urem | And | Or | Xor | Sar | Shr | Shl | Neg | Ptoi _
⇒
true
Let_ _ Cmp_ _ _
⇒
true
Let_ _ Ext_ _
⇒
true
⇒
true
Let_ _ Copy args
⇒
negbexistsbis_global args
_
⇒
false

As ahead_transparent(Inst): the instruction i may come between a check_ahead and the access whose need it checks early. It is evidence_preserving, and not a division, whose fault would end the program otherwise than a Fil-C trap.

493Definition ahead_transparenti : inst : bool ≔
&&
match i with
Let_ _ Div | Udiv | Rem | Urem _
⇒
false
_
⇒
true

As setjmp_func/1: the function f calls setjmp somewhere, reachable or not. Its second return is a control edge the blocks do not show (docs/setjmp.md), so the validator refuses what would need it.

504Definition setjmp_funcf : func : bool ≔
existsb
λ b  ⇒
existsb
λ i  ⇒
match i with
Let_ _ Setjmp_ _
⇒
true
_
⇒
false

As local_objects(Func, Objects): the named locals the function f declares, each with its size, in instruction order.

513Definition local_objectsf : func : listname × Z ≔
flat_map
λ b  ⇒
flat_map
λ i  ⇒
match i with
⇒
x, size
_
⇒
[ ]