Fil-C on Nix

Filnix wiki

the formal campaign

rocq/theories/Validate.v

The validator of checked IR, transcribed from ir.pl's valid_module/1 for the core subset: valid_funcmd f is true exactly when ir.pl accepts the function f of the module md, checked stage, filc mode; valid_data and valid_alias do the same for the module's data objects and aliases.

ir.pl's validator throws nanofil_error(Message) at the first problem and fails on a few terms outside the grammar; both are false here. The order of the tests therefore does not matter, and the definitions below follow ir.pl's clause groups, not its order of evaluation. In three places ir.pl's result still depends on how Prolog evaluates, not only on what its clauses say: the memo of offset_interval/8 and the cuts of pointer_distance/4 and op_interval/9; oi, pointer_distance and op_interval follow them.

Parts

Gallinair.plChecks
valid_usevalid_use/3an operand read at a class its definition allows
valid_op, op_result_okvalid_op/1, op_result_ok/3an op's parameters and its result class
valid_inst, valid_jump, valid_phithe sameeach instruction, jump and phi
definitions_dominatethe samestrict SSA: every use has a dominating definition
valid_localsthe samenamed locals declared once, in the entry block, before use
stage_functhe sameevery access covered by evidence that still holds; a poll on every cycle
valid_datavalid_item/4 for dataalignment, linkage, item shapes; a table entry names a function, a weak declaration no local definition, no pointer a table entry
valid_aliasvalid_item/4 for aliaseslinkage, a name of its own, a target the module defines

stage_func is the part Campaign 3 is about: the evidence pass, in which a check must be followed at once by an access it covers, a later scalar access may read a proof whose fact is still live, and each reuse form has its own side conditions, stated over the single definitions of the function, the guard chains of flights from a pointer to its bases and the offset intervals.

From Stdlib Require Import String ZArith List Bool
From Nanofil Require Import Term Maps Syntax Values Memory Ops Semantics Cfg
Import ListNotations
Open Scope string_scope
Open Scope Z_scope
Open Scope list_scope

Names, uses and definitions

Whether the name n is in the list ns.

50Definition memn : name ns : listname : bool ≔
existsbname_eqbn ns

Whether the operands v and w are the same temporary, the same symbol or the same constant.

54Definition value_eqbv w : value : bool ≔
match v, w with
⇒
Z.eqba b
_, _
⇒
false

The temporaries among the operands vs, in order; constants and symbols are dropped.

63Definition value_tempsvs : listvalue : listname ≔
flat_map
λ v  ⇒
match v with
⇒
n
_
⇒
[ ]
vs

As inst_temp_def/3: the temporaries the instruction i defines, each with its class: a let's or a flight's destination, or a call's results.

69Definition inst_temp_defsi : inst : listname × class ≔
match i with
Letd k _ _
⇒
d, k
Flightd _ _ _
⇒
d, P
Callr _ _ | RawCallr _ _
⇒
match r with
⇒
[ ]
⇒
d, k
⇒
rs
_
⇒
[ ]

As cfg:inst_def/2, its first solution: the first temporary the instruction i defines, if it defines one.

80Definition inst_def_firsti : inst : optionname ≔
match inst_temp_defsi with
d, _ :: _
⇒
Somed
[ ]
⇒
None

The operand of a call argument a.

84Definition arg_valuea : arg : value ≔
match a with
Arg_ v
⇒
v

The class a call argument a is passed at.

86Definition arg_classa : arg : class ≔
match a with
Argk _
⇒
k

As cfg:flight_arg_value/2: the operand of a flight argument, whether it is the address operand or a plain value.

90Definition flight_arg_valuea : flight_arg : value ≔
match a with
⇒
v

As cfg:inst_uses/2: the temporaries the instruction i reads, a flight's lower donor included.

95Definition inst_usesi : inst : listname ≔
match i with
Let_ _ _ args
⇒
Flight_ _ args lw
⇒
++
match lw with
⇒
p
⇒
[ ]
RawStore_ _ v a | RawStorePointer_ v a _
⇒
Call_ f args | RawCall_ f args
⇒
value_tempsf :: maparg_value args
LocalAlloc_ _ | Loc_ _
⇒
[ ]

As cfg:jump_uses/2: the temporaries the jump j reads, a branch's condition or the values it returns.

108Definition jump_usesj : jump : listname ≔
match j with
_
⇒
[ ]

As jump_edge/2, all its solutions: the edges the jump j may take, both arms of a branch; a return takes none.

117Definition jump_targetsj : jump : listtarget ≔
match j with
⇒
t
JBranch_ t1 t2
⇒
t1; t2
_
⇒
[ ]

The labels of the blocks the jump j may go to, over poll edges too.

122Definition succ_labelsj : jump : listname ≔

Whether the edge t is a poll edge, one that passes a safepoint on the way to its target.

126Definition is_pollt : target : bool ≔
match t with
⇒
true
⇒
false

Dominance

The labels of the blocks of bs that jump to the label l, unreachable ones included; valid_phi compares a phi's predecessors with them.

133Definition preds_ofbs : listblock l : name : listname ≔
mapb_label filterλb ⇒ meml succ_labelsb_jumpb bs

Whether the label lists a and b hold the same labels, in any order and number.

138Definition sets_eqba b : listname : bool ≔
forallbλx ⇒ memx b a && forallbλx ⇒ memx a b

As cfg:dominates_by_interval/3: whether block a dominates block b, read off the dominator-tree intervals doms that block_intervals numbers.

144Definition dominatesdoms : nmapZ × Z a b : name : bool ≔

Whether the label l is reachable from the entry, that is, has a dominator interval in doms.

148Definition is_reachabledoms : nmapZ × Z l : name : bool ≔
nm_meml doms

A definition site of a temporary, ir.pl's param or at(L, I): SParam for a parameter, SAtl i for index i of block l, where a phi sits at -1, before every instruction.

153Inductive site: Type ≔
|SParam
|SAt
l : name i : Z

As block_sites/1 with the parameters' sites added: each temporary of the function f with every one of its definition sites. A temporary may have several; definitions_dominate asks only that one of them dominate each use.

159Definition temp_sitesf : func : nmaplistsite ≔
let add m : nmaplistsite n : name s : site ≔
s
::
match nm_findn m with
Somess
⇒
ss
None
⇒
[ ]
in
let m0 ≔ fold_leftλm p ⇒ addm sndp SParam fn_paramsf nm_empty in
fold_left
λ m b  ⇒
let m1 ≔
fold_left
λ m ph  ⇒
match ph with
Phid _ _
⇒
addm d SAtb_labelb −1
m
in
fst
fold_left
λ acc : nmaplistsite × Z ins  ⇒
let m, j ≔ acc in
fold_leftλm dk ⇒ addm fstdk SAtb_labelb j inst_temp_defsins m, j + 1
m1, 0
m0

As site_dominates/4: whether the definition site s comes before position i of block l on every path from the entry. A parameter always does; a site in l itself does when its index is smaller; a site in another block does when that block dominates l.

177Definition site_dominatesdoms : nmapZ × Z l : name i : Z s : site : bool ≔
match s with
⇒
true
SAtd j
⇒
→
j <? i
else
dominatesdoms d l

As dominated_use/4: whether some definition of the temporary n, among its sites in sites, dominates position i of block l.

185Definition dominated_usesites : nmaplistsite doms : nmapZ × Z l : name i : Z n : name : bool ≔
match nm_findn sites with
Somedefs
⇒
existsbsite_dominatesdoms l i defs
None
⇒
false

As definitions_dominate/3, the strict SSA (static single assignment) rule: in every reachable block of the function f, each use of a temporary, by an instruction or the jump, is dominated by one of its definitions. A phi's operand counts as used at the end of the predecessor it comes from, if that predecessor is reachable.

198Definition definitions_dominatef : func doms : nmapZ × Z : bool ≔
let sites ≔ temp_sitesf in
let ends ≔ nm_of_listmapλb ⇒ b_labelb, Z.of_natlengthb_instsb fn_blocksf in
let dominated_use ≔ dominated_usesites in
forallb
λ b  ⇒
→
true
else
let l ≔ b_labelb in
let fix insts is : listinst i : Z : bool ≔
match is with
[ ]
⇒
forallbdominated_usedoms l i jump_usesb_jumpb
ins :: rest
⇒
forallbdominated_usedoms l i inst_usesins && instsrest i + 1
in
instsb_instsb 0
&&
forallb
λ ph  ⇒
match ph with
Phi_ _ pairs
⇒
forallb
λ pv  ⇒
match sndpv with
⇒
is_reachabledoms fstpv
→
match nm_findfstpv ends with
Somee
⇒
dominated_usedoms fstpv e n
None
⇒
true
else
true
_
⇒
true
pairs

Classes of temporaries

As temp_defs/3: every definition in the function f, as a temporary with its class: the parameters, then each block's phis and instructions.

234Definition temp_defsf : func : listname × class ≔
mapλp ⇒ sndp, fstp fn_paramsf
++
flat_map
λ b  ⇒
map
λ ph  ⇒
match ph with
Phid k _
⇒
d, k
++

As check_temp_classes/2: each temporary has one class, that is, each of its definitions in defs has the class of its first one.

241Definition temp_classes_okdefs : listname × class : bool ≔
let first ≔
fold_left
λ m d  ⇒
nm_memfstd m
→
m
else
nm_addfstd sndd m
defs
in
forallb
λ d  ⇒
match nm_findfstd first with
Somek
⇒
class_eqbk sndd
None
⇒
false
defs

As proof_class/1: whether k is a proof class, a for an access proof a check defines or f for the function proof check_function defines.

252Definition proof_classk : class : bool ≔
match k with
A | F
⇒
true
_
⇒
false

As valid_use/3: whether the operand v may be read at the class k, given the class of each temporary (classes).

  • A proof or float temporary is read only at its own class, and a proof or float class reads only a temporary of that class.
  • A p (pointer) temporary is read only at p, and p reads only a p temporary.
  • A long (l) temporary may be read as a word (w), not the reverse.
  • A float constant is a bit pattern below its class's modulus; no constant is a proof.
  • No symbol is read here: in filc mode only a copy reads one (valid_inst).
265Definition valid_useclasses : listname × class k : class v : value : bool ≔
match v with
⇒
match lookupn classes with
None
⇒
false
Somek0
⇒
⇒
→
match modulusk with
Somem
⇒
0 ≤? n && n <? m
None
⇒
false
else
⇒
false

Ops

The alignments a check may prove, as valid_op/1 lists them: 1, 2, 4, 8 or 16 bytes.

288Definition check_alignal : Z : bool ≔
existsbZ.eqbal 1; 2; 4; 8; 16

As alloc_align/1: an alloc's alignment al is a power of two from 4 to 4096.

293Definition alloc_alignal : Z : bool ≔
4 ≤? al && al ≤? 4096 && Z.landal al − 1 =? 0

What valid_op/1 asks of a check's parameters: a nonempty span, lo below hi, at an alignment al that check_align allows.

298Definition check_oklo hi al : Z : bool ≔
lo <? hi && check_alignal

As local_offset/1: off is a signed 64-bit offset.

301Definition local_offsetoff : Z : bool ≔
−2 ^ 63 ≤? off && off <? 2 ^ 63

The effects a heap raw_store_pointer declares, as pointer_memory_effects/2 lists them for a store: ensure the aux word, the lower barrier, reload the root, then write the lower and the address.

307Definition store_effects: listeffect ≔

The effects of a pointer store to a proven local object, which has an explicit stack aux: write the lower and the address (pointer_memory_effects/2's local_store).

312Definition local_store_effects: listeffect ≔

The effect of an atomic pointer load, which leaves the whole protocol to Fil-C's helper (pointer_memory_effects/2's atomic_load).

315Definition atomic_load_effects: listeffect ≔

The effect of an atomic pointer store, which leaves the whole protocol to Fil-C's helper (pointer_memory_effects/2's atomic_store).

318Definition atomic_store_effects: listeffect ≔

As comparison/2: whether the condition c compares values of the class k. The signed and unsigned orders compare integers, the float conditions floats, and eq and ne both.

323Definition comparisonc : cond k : class : bool ≔
match c with
Sle | Slt | Sge | Sgt | Ule | Ult | Uge | Ugt
⇒
Le | Lt | Ge | Gt | Ord | Unord
⇒

As valid_op/1 for the core subset's ops: the parameters an op carries are well formed. A check or reuse form needs check_ok; a check_ahead also an early end ahi strictly inside its span; a span test a nonempty span of signed 64-bit offsets; a limit a size from 1 to 2^31 - 1; a check_local a signed 64-bit offset, a size in that range and a check alignment; a raw pointer load one of the heap, local or atomic effect lists.

337Definition valid_opo : op : bool ≔
match o with
Cmpc k
⇒
Checklo hi _ al | Renewlo hi _ al | RenewHulllo hi _ al | RenewIflo hi _ al | CheckLimitlo hi _ al | HalfCheck_ lo hi _ al
⇒
check_oklo hi al
CheckAheadlo hi _ al ahi
⇒
check_oklo hi al && lo <? ahi && ahi <? hi
⇒
lo <? hi && −2 ^ 63 ≤? lo && hi ≤? 2 ^ 63 − 1
Limitsize
⇒
0 <? size && size ≤? 2 ^ 31 − 1
CheckLocal_ off bytes _ al
⇒
local_offsetoff && 0 <? bytes && bytes ≤? 2 ^ 31 − 1 && check_alignal
_
⇒
true

As any_class/1: a class a value may have, every class but the proof classes (proof_class).

356Definition any_classk : class : bool ≔

As op_result_ok/3 in filc mode: the op o may define a temporary of class k. Arithmetic gives an integer or float class as the op allows; conversions give the class they convert to; a check or reuse form gives an access proof (a), check_function a function proof (f), a span test a word and a limit a long.

Signatures

The atom ir.pl writes for the class k, so that a class can be compared with the term of a function outside the core subset.

403Definition eclassk : class : term ≔
match k with
⇒
"w"
⇒
"l"
⇒
"p"
⇒
"a"
⇒
"f"
⇒
"s"
⇒
"d"
⇒
"x"

The term ir.pl writes for the return type r of a function.

410Definition eretr : ret_type : term ≔
match r with
⇒
TAtom"void"
⇒
TApp"tuple" TListmapeclass ks

What a call site needs of its callee, the function func_named/3 finds: its linkage, its parameters' classes, its return type and whether it is variadic, each as ir.pl writes it.

420Record sig: Type ≔ mkSig
sig_linkage
 : term
sig_params
 : listterm
sig_ret
 : term
sig_variadic
 : bool

Whether the term t is a let of va_args, the mark of a variadic function's body.

429Definition is_va_argst : term : bool ≔
match t with
TApp"let" _; TAtom"p"; TAtom"va_args"; _
⇒
true
_
⇒
false

The sig of a function outside the core subset, read off its term t.

437Definition term_sigt : term : optionsig ≔
match t with
TApp"func" _; lk; r; TListps; TListbs
⇒
Some
map
λ p  ⇒
match p with
TApp"param" k; _
⇒
k
_
⇒
p
ps
r
existsb
λ b  ⇒
match b with
TApp"block" _; _; TListis; _
⇒
existsbis_va_args is
_
⇒
false
bs
_
⇒
None

As func_named/3: the sig of the first function the module md defines under the name n, in the core subset or outside it.

450Fixpoint func_sign : name md : module : optionsig ≔
match md with
[ ]
⇒
None
IFuncf :: rest
⇒
IOutsidef t :: rest
⇒
else
func_sign rest
_ :: rest
⇒
func_sign rest

Whether the term t is a float class's atom: s, d or x.

463Definition float_atomt : term : bool ≔
match t with
TAtom"s" | "d" | "x"
⇒
true
_
⇒
false

Whether the term t is the atom of a value class: w, l, p, s, d or x.

468Definition class_atomt : term : bool ≔
match t with
TAtom"w" | "l" | "p" | "s" | "d" | "x"
⇒
true
_
⇒
false

As call_result_ok/4: the result r a call binds fits the callee's return type ret. A tuple must match it exactly; one result follows to_result_ok/4 and class_result_ok/4: floats only as the same class, a long result never from a word return, and a pointer result exactly from a pointer return.

476Definition call_result_okr : call_result ret : term : bool ≔
match r with
⇒
true
⇒
term_eqbret TApp"tuple" TListmapλx ⇒ eclasssndx rs
⇒
&&
else
negbclass_eqbk L && term_eqbret TAtom"w" && Bool.eqbclass_eqbk P term_eqbret TAtom"p"

Whether the terms ps are a prefix of the terms xs: a variadic callee's fixed parameters, against a call's argument classes.

490Fixpoint prefix_eqbps xs : listterm : bool ≔
match ps, xs with
[ ], _
⇒
true
p :: ps′, x :: xs′
⇒
term_eqbp x && prefix_eqbps′ xs′
_, _
⇒
false

Instructions, jumps and phis

The labels of the function f's blocks.

500Definition labelsf : func : listname ≔

As store_value_class/2: the class of the value a raw store of the width w writes. None for a pointer width, which only raw_store_pointer stores.

505Definition width_value_okw : width : optionclass ≔
match w with
Wb | Wh | Ww
⇒
SomeW
⇒
SomeL
⇒
SomeSingle
⇒
SomeDouble
⇒
⇒
None

As not_init_entry/3, negated: whether the symbol s names a constructor or destructor table entry of the module md, which nothing may copy or call.

515Definition init_entry_namedmd : module s : name : bool ≔
existsb
λ it  ⇒
match it with
IDatan LInit_ _ _ _
⇒
_
⇒
false
md

As valid_inst/2: the instruction i of the module md is well formed, its operands read at the classes given (classes, through valid_use).

  • A flight is a copy, add or sub with addresses at p, offsets at l and a pointer lower donor.
  • A copy of a symbol at p (a getter) names no constructor or destructor table entry.
  • Any other let has a valid op (valid_op, op_result_ok) and operands at the classes the op reads; va_args takes a nonnegative constant.
  • A raw store writes a value of its width's class through an access proof (a); a raw pointer store has one of the heap, local or atomic effect lists.
  • A direct call matches its callee's sig when the module defines it; an indirect call reads a pointer callee, a raw_call a function proof (f).
533Definition valid_instmd : module classes : listname × class i : inst : bool ≔
let use ≔ valid_useclasses in
match i with
Locfile _
⇒
match file with
⇒
true
_
⇒
false
⇒
0 ≤? size && size ≤? 2 ^ 31 − 1
Flight_ o args lw
⇒
match o, args with
Copy, _ | Add, _; _ | Sub, _; _
⇒
true
_, _
⇒
false
&&
forallb
λ a  ⇒
match a with
⇒
useP p
⇒
useL v
args
&&
match lw with
⇒
true
⇒
useP p
Let_ k o args
⇒
&&
match o, args with

va_argsneeds a non − negativeconstant

⇒
0 ≤? off
⇒
false
_, _
⇒
true
&&
match op_arg_classeso k with
Somecs
⇒
lengthcs =? lengthargs%nat && forallbλca ⇒ usefstca sndca combinecs args
None
⇒
false
RawStorew _ v a
⇒
match width_value_okw with
Somek
⇒
usek v && useA a
None
⇒
false
Callr Globalc args
⇒
&&
match func_sigc md with
Somes
⇒
let acs ≔ mapλa ⇒ eclassarg_classa args in
lengthacs =? lengthsig_paramss%nat && forallbλp ⇒ term_eqbfstp sndp combineacs sig_paramss || sig_variadics && prefix_eqbsig_paramss acs && call_result_okr sig_rets
None
⇒
true
&&
forallbλa ⇒ usearg_classa arg_valuea args
Call_ Tmpn args
⇒
useP Tmpn && forallbλa ⇒ usearg_classa arg_valuea args
Call_ _ _
⇒
false
RawCall_ callee args
⇒
useF callee && forallbλa ⇒ usearg_classa arg_valuea args

As valid_jump/2: the jump j of the function f is well formed: its targets are labels of f, a branch reads its condition as a word, and a return returns what f's return type says.

584Definition valid_jumpf : func classes : listname × class j : jump : bool ≔
let exists_ t ≔ memedge_labelt labelsf in
match j with
⇒
exists_t
JBranchv t1 t2
⇒
valid_useclasses W v && exists_t1 && exists_t2
⇒
match fn_retf with
⇒
valid_useclasses k v
_
⇒
false
⇒
match fn_retf with
⇒
lengthks =? lengthvs%nat && forallbλkv ⇒ valid_useclasses fstkv sndkv combineks vs
_
⇒
false
⇒
match fn_retf with
⇒
true
_
⇒
false

As valid_phi/4: the phi ph names exactly the block's predecessors preds, as sets, and each of its values is read at its class.

602Definition valid_phiclasses : listname × class preds : listname ph : phi : bool ≔
match ph with
Phi_ k pairs
⇒
sets_eqbmapfst pairs preds && forallbλpv ⇒ valid_useclasses k sndpv pairs

As valid_block/2: the block b of the function f has valid phis, instructions and jump, and a block with phis has a predecessor.

610Definition valid_blockmd : module f : func classes : listname × class b : block : bool ≔
let preds ≔ preds_offn_blocksf b_labelb in
forallbvalid_phiclasses preds b_phisb && forallbvalid_instmd classes b_instsb && valid_jumpf classes b_jumpb
&&
match b_phisb, preds with
_ :: _, [ ]
⇒
false
_, _
⇒
true

As proof_class_defs/3: only a check defines a proof in the function f; no parameter, phi, call result or call argument has a proof class.

621Definition proof_class_defsf : func : bool ≔
forallbλp ⇒ negbproof_classfstp fn_paramsf
&&
forallb
λ b  ⇒
forallb
λ ph  ⇒
match ph with
Phi_ k _
⇒
&&
forallb
λ i  ⇒
match i with
Callr _ args | RawCallr _ args
⇒
forallbλdk ⇒ negbproof_classsnddk inst_temp_defsi && forallbλa ⇒ negbproof_classarg_classa args
_
⇒
true

Whether no name occurs twice in ns.

635Fixpoint no_dupsns : listname : bool ≔
match ns with
[ ]
⇒
true
n :: rest
⇒
negbmemn rest && no_dupsrest

As valid_locals/3: each local object of the function f is declared once, by a local_alloc in the entry block; there each check_local of it comes after the declaration, and in another block it needs only the declaration.

642Definition valid_localsf : func : bool ≔
let objs ≔ local_objectsf in
no_dupsmapfst objs
&&
match fn_blocksf with
[ ]
⇒
false
entry :: _
⇒
let declared ≔
fst
fold_left
λ acc : listname × Z × Z i : inst  ⇒
let ds, j ≔ acc in
match i with
⇒
ds ++ x, j, j + 1
_
⇒
ds, j + 1
b_instsentry
[ ], 0
in
lengthdeclared =? lengthobjs%nat
&&
forallb
λ b  ⇒
fst
fold_left
λ acc : bool × Z i : inst  ⇒
let ok, j ≔ acc in
ok
&&
match i with
Let_ _ CheckLocalx _ _ _ _ _
⇒
match lookupx declared with
Someat_
⇒
negbname_eqbb_labelb b_labelentry || at_ <? j
None
⇒
false
_
⇒
true
j + 1
true, 0

Evidence

A live fact, ir.pl's live(Q, P, Lo, Hi, Rights, Align): the access proof f_q proves rights f_r over the bytes from offset f_lo up to f_hi of the pointer f_p, at alignment f_al, and its evidence still holds.

676Record fact: Type ≔ mkFact
f_q
 : name
f_p
 : value
f_lo
 : Z
f_hi
 : Z
f_r
 : rights
f_al
 : Z

Whether the facts a and b are equal in every field.

681Definition fact_eqba b : fact : bool ≔
name_eqbf_qa f_qb && value_eqbf_pa f_pb && Z.eqbf_loa f_lob && Z.eqbf_hia f_hib && rights_eqbf_ra f_rb && Z.eqbf_ala f_alb

As evidence_kill/3 with evidence_names/2: the facts fs without those whose proof or pointer is the temporary d, which is being defined again.

687Definition killd : name fs : listfact : listfact ≔
filterλf ⇒ negbname_eqbf_qf d || value_eqbf_pf Tmpd fs

As evidence_after/3: the live facts fs after the instruction i. An instruction that is not evidence_preserving ends them all; otherwise only the facts naming what it defines end.

693Definition evidence_afteri : inst fs : listfact : listfact ≔
→
match inst_def_firsti with
Somed
⇒
killd fs
None
⇒
fs
else
[ ]

The live fact of the proof q, the newest (get_assoc(Q, ByQ, _) in ir.pl's index).

700Fixpoint fact_ofq : name fs : listfact : optionfact ≔
match fs with
[ ]
⇒
None
f :: rest
⇒
→
Somef
else
fact_ofq rest

As evidence_meet/2: the facts in every one of the sets, the evidence a block may assume from all its incoming edges; none for no sets.

706Definition evidence_meetsets : listlistfact : listfact ≔
match sets with
[ ]
⇒
[ ]
s :: ss
⇒
filterλf ⇒ forallbλs′ ⇒ existsbfact_eqbf s′ ss s

Accesses

What a raw access reads or writes, raw_access/5's X: a load type, a store width, or a pointer.

716Inductive access_type: Type ≔
|XLoad
t : load_type
|XWidth
w : width
|XPtr

As raw_access/5: the instruction i as a raw access: the proof q it goes through, what it moves, its offset from q's pointer and the rights it needs (read for a load, write for a store).

721Definition raw_accessi : inst : optionname × access_type × Z × rights ≔
match i with
Let_ _ RawLoadt off Tmpq
⇒
Someq, XLoadt, off, Read
RawStorew off _ Tmpq
⇒
Someq, XWidthw, off, Write
⇒
Someq, XPtr, off, Read
⇒
Someq, XPtr, off, Write
_
⇒
None

As access_size_align/3, its first solution: the bytes an access of type x moves, and the alignment its address needs: 8 for a pointer, 8 for an 80-bit float, 1 for the rest.

733Definition access_size_alignx : access_type : Z × Z ≔
match x with
⇒
8, 1
⇒
4, 1
⇒
10, 8
⇒
8, 8
⇒
10, 8

As rights_allow/2: rights have serve an access that needs need; write rights also allow a read.

746Definition rights_allowhave need : rights : bool ≔
match have, need with
⇒
true
⇒
true
⇒
false

As covered_access/6: the instruction i is a raw access through the proof q whose bytes lie from lo up to hi, whose start is aligned as the access needs given q's alignment al, and that the rights r allow: the coverage a proof gives.

753Definition covered_accessi : inst q : name lo hi : Z r : rights al : Z : bool ≔
match raw_accessi with
Someq′, x, off, need
⇒
let size, aa ≔ access_size_alignx in
name_eqbq′ q && lo ≤? off && off + size ≤? hi && al mod aa =? 0 && off − lo mod aa =? 0 && rights_allowr need
None
⇒
false

Where a raw access goes: the heap or a proven local object.

764Inductive storage: Type ≔
|Heap'
|Local'

As access_storage_effects/2: the access i declares the effects of its storage s. A scalar access always does; a pointer access must declare the heap's, the local's or the atomic effects.

As proof_use/6: the instruction i, right after the check of the proof q, uses it: a heap raw access the proof covers (covered_access).

784Definition proof_usei : inst q : name lo hi : Z r : rights al : Z : bool ≔

As reusing_access/2: whether the access i, of type x, may read a live proof that is not its own check's: a scalar access, or a heap pointer load, which reads aux and box words only. A pointer store may exit while ensuring the aux, and keeps its own check.

791Definition reusing_accessi : inst x : access_type : bool ≔
match x with
⇒
match i with
_
⇒
true

Single definitions, chains and intervals

A temporary's one definition, an entry of the map Once that single_definitions/3 builds: a parameter, a phi or an instruction.

804Inductive def: Type ≔
|DParam
|DPhi
ph : phi
|DInst
i : inst

Every definition of the function f, as a name with what defines it: parameters, phis and instructions.

808Definition all_defsf : func : listname × def ≔
mapλp ⇒ sndp, DParam fn_paramsf
++
flat_map
λ b  ⇒
map
λ ph  ⇒
match ph with
Phid _ _
⇒
d, DPhiph
++
flat_mapλi ⇒ mapλdk ⇒ fstdk, DInsti inst_temp_defsi b_instsb

As single_definitions/3: the temporaries of the function f with exactly one definition, each with it. The reuse forms' rules read only these names: a single definition that dominates a point holds the value it computed there.

818Definition single_definitionsf : func : listname × def ≔
let ds ≔ all_defsf in
filterλd ⇒ lengthfilterλd′ ⇒ name_eqbfstd fstd′ ds =? 1%nat ds

Whether the name n has a single definition in once.

823Definition once_hasonce : listname × def n : name : bool ≔
match lookupn once with
Some_
⇒
true
None
⇒
false

As near_offset/1: k is well inside the signed 64-bit range, within 2^62 of 0. Offsets on a guard chain are exact only modulo 2^64; only near ones are the true distance.

829Definition near_offsetk : Z : bool ≔
−2 ^ 62 ≤? k && k ≤? 2 ^ 62

As signed_value/3: the constant c taken as a signed integer of the integer class k's width.

833Definition signed_valuek : class c : Z : optionZ ≔
let* bits ≔ class_bitsk in
let u ≔ c mod 2 ^ bits in
Some
2 ^ bits − 1 ≤? u
→
u − 2 ^ bits
else
u

As narrow_range/3: the values a narrow type t extends to, for the signed and unsigned bytes and halves; none for the rest.

840Definition narrow_ranget : narrow : optionZ × Z ≔
match t with
⇒
Some0, 255
⇒
Some0, 65535
⇒
Some−128, 127
⇒
Some−32768, 32767
_
⇒
None

As interval_fits/3: an offset interval from lo to hi suits the class k. A word (w) interval must lie in the signed 32-bit range, so no word arithmetic wraps; a long (l) one may be anything, since it bounds the value only modulo 2^64.

851Definition interval_fitsk : class lo hi : Z : bool ≔
match k with
⇒
−2 ^ 31 ≤? lo && hi ≤? 2 ^ 31 − 1
⇒
true
_
⇒
false

The memo of oi: each name walked at a class, with the interval it got, or none.

860Definition memo ≔
listname × class × optionZ × Z

The memo's answer for the name n at the class k, if it has one.

863Fixpoint memo_lookupn : name k : class m : memo : optionoptionZ × Z ≔
match m with
[ ]
⇒
None
nk, r :: rest
⇒
name_eqbfstnk n && class_eqbsndnk k
→
Somer
else
memo_lookupn k rest

Whether the operand v is a constant.

871Definition is_constv : value : bool ≔
match v with
⇒
true
_
⇒
false

As op_interval/9: the offset interval of what the op o computes from args at the class k, with rec the walk of an operand one level deeper and m the memo. Each clause commits once its head matches, as ir.pl's cuts do: an and tries its right operand as the mask before its left one, and a failed ext(sw) does not fall through to a later clause.

879Definition op_intervalrec : value → class → memo → optionZ × Z × memo o : op args : listvalue k : class m : memo : optionZ × Z × memo ≔
match o, args with
Extt, _
⇒
match narrow_ranget with
Somer
⇒
Somer, m
None
⇒
match t, args with
Sw, a
⇒
reca W m
Uw, a
⇒
match reca W m with
Somelo, hi, m1
⇒
0 ≤? lo
→
Somelo, hi, m1
else
None, m1
r
⇒
r
_, _
⇒
None, m
Copy, a
⇒
reca k m
And, _
⇒
let mask c ≔
match signed_valuek c with
Somev
⇒
0 ≤? v
→
Some0, v
else
None
None
⇒
None
in
match args with
_; Constc
⇒
match maskc with
Somer
⇒
Somer, m
None
⇒
match args with
Constc′; _
⇒
maskc′, m
_
⇒
None, m
Constc; _
⇒
maskc, m
_
⇒
None, m
Urem, _; Constc
⇒
match class_bitsk with
Somebits
⇒
let u ≔ c mod 2 ^ bits in
0 <? u && u ≤? 2 ^ bits − 1
→
Some0, u − 1
else
None
m
None
⇒
None, m
Shl, a; Consts
⇒
match class_bitsk with
Somebits
⇒
0 ≤? s && s <? bits
→
match reca k m with
Somelo, hi, m1
⇒
Somelo × 2 ^ s, hi × 2 ^ s, m1
r
⇒
r
else
None, m
None
⇒
None, m
Mul, _
⇒
let scaled a c ≔
match signed_valuek c with
Somev
⇒
match reca k m with
Somelo, hi, m1
⇒
Some
0 ≤? v
→
lo × v, hi × v
else
hi × v, lo × v
m1
r
⇒
r
None
⇒
None, m
in
match args with
a; Constc
⇒
→
match args with
Constc′; a′
⇒
→
None, m
else
scaleda′ c′
_
⇒
None, m
else
scaleda c
Constc; a
⇒
→
None, m
else
scaleda c
_
⇒
None, m
Add, a; b
⇒
match reca k m with
Somelo1, hi1, m1
⇒
match recb k m1 with
Somelo2, hi2, m2
⇒
Somelo1 + lo2, hi1 + hi2, m2
r
⇒
r
r
⇒
r
Sub, a; Constc
⇒
match signed_valuek c with
Somev
⇒
match reca k m with
Somelo, hi, m1
⇒
Somelo − v, hi − v, m1
r
⇒
r
None
⇒
None, m
_, _
⇒
None, m

As offset_interval/8: the interval from Lo to Hi in which every value the operand v of class k can hold lies (for l, modulo 2^64), walked through the single definitions once, at most 32 deep. It threads ir.pl's memo in the same order: each name's answer is kept, so a name met again deeper than the limit allows still has its interval, but a failed walk keeps nothing it memoised on the way, and only records the name as having none.

978Fixpoint oionce : listname × def fuel : nat v : value k : class depth : Z m : memo : optionZ × Z × memo ≔
match v with
⇒
match signed_valuek c with
Somex
⇒
Somex, x
None
⇒
None
m
⇒
None, m
⇒
match memo_lookupn k m with
Somer
⇒
r, m
None
⇒
let 'r, m′ ≔
match fuel with
O
⇒
None, m
Sf
⇒
depth <? 32
→
match lookupn once with
SomeDInstLetn′ k′ o args
⇒
name_eqbn′ n && class_eqbk′ k
→
match op_intervalλv k m ⇒ oionce f v k depth + 1 m o args k m with
Somelo, hi, m1
⇒
→
Somelo, hi, m1
else
None, m
None, _
⇒
None, m
else
None, m
_
⇒
None, m
else
None, m
in
r, n, k, r :: m′

As offset_interval/6 from depth 0: the offset interval of the operand v at the class k, if one is derived. Interval.v proves what it answers derived and sound (offset_interval_iv, iv_sound).

1013Definition offset_intervalonce : listname × def v : value k : class : optionZ × Z ≔
fstoionce 33 v k 0 [ ]

As flight_term/5: when the flight op o with args moves the base b, the offset it adds to b and its sign: an add of an offset, a sub of one, or a copy, which adds 0.

1019Definition flight_termo : op args : listflight_arg b : value : optionvalue × Z ≔
match o, args with
⇒
→
Someov, 1
else
None
⇒
→
Someov, −1
else
None
⇒
→
SomeConst0, 1
else
None
_, _
⇒
None

The walk of guard_chain, fuel steps at most: the pointer p at the offsets lo to hi from the start, then each base it reaches.

1029Fixpoint guard_chain_goonce : listname × def fuel : nat p : value lo hi : Z : listvalue × Z × Z ≔
p, lo, hi
::
match fuel, p with
Sf, Tmpx
⇒
match lookupx once with
SomeDInstFlight_ o args LowerOfb
⇒
match flight_termo args b with
Someov, sign
⇒
match offset_intervalonce ov L with
Someolo, ohi
⇒
sign =? 1
→
guard_chain_goonce f b lo + olo hi + ohi
else
guard_chain_goonce f b lo − ohi hi − olo
None
⇒
[ ]
None
⇒
[ ]
_
⇒
[ ]
_, _
⇒
[ ]

As guard_chain/3: the guard chain of the pointer p. First p at offset 0 from itself, then each base b whose capability p has, through single-definition add, sub and copy flights whose lower donor is their address operand, with the interval of p's offset from b.

1056Definition guard_chainonce : listname × def p : value : listvalue × Z × Z ≔
guard_chain_goonce 64 p 0 0

A chain element e as an exact base: a single-definition temporary at one known offset, with that offset.

1061Definition exact_baseonce : listname × def e : value × Z × Z : optionname × Z ≔
match e with
Tmpn, k, k′
⇒
k =? k′ && once_hasonce n
→
Somen, k
else
None
_
⇒
None

The first answer f gives over the list xs.

1068Fixpoint first_someX Y : Type f : X → optionY xs : listX : optionY ≔
match xs with
[ ]
⇒
None
x :: rest
⇒
match fx with
Somey
⇒
Somey
None
⇒

As pointer_distance/4: the distance D, modulo 2^64, from the pointer p1 to the pointer p, which has p1's capability: both reach one exact single-definition base on their guard chains, and D is the difference of their offsets from it. Like ir.pl, which cuts after the first, it takes the first base of p's chain that p1's chain shares.

1076Definition pointer_distanceonce : listname × def p p1 : value : optionZ ≔
let c1 ≔ guard_chainonce p1 in
λ e  ⇒
match exact_baseonce e with
Somen, k
⇒
λ e1  ⇒
match e1 with
Tmpn1, k1, k1′
⇒
name_eqbn1 n && k1 =? k1′
→
Somek − k1
else
None
_
⇒
None
c1
None
⇒
None

As half_known/6: a half check of the span from lo up to hi of a pointer P may test only its bound bd, when P is d0 bytes from a pointer whose passed check proved the span from lo1 up to hi1 (see docs/half.md). With d0 taken signed modulo 2^64: for upper, P+lo lies at or above the old span's start; for lower, P+hi lies at or below its end; both distances are near (near_offset) and the span is at most 2^62 bytes, so nothing wraps.

1096Definition half_knownbd : bound lo hi d0 lo1 hi1 : Z : bool ≔
let d ≔ d0 + 2 ^ 63 mod 2 ^ 64 − 2 ^ 63 in
let e ≔ d + lo − lo1 in
let f ≔ d + hi − hi1 in
near_offsete && near_offsetf && hi − lo ≤? 2 ^ 62
&&
match bd with
⇒
0 ≤? e
⇒
f ≤? 0

As renew_source_op/6 with stage_sources/4's conditions: a renew source, when the instruction i is a single-definition proof q1 of a single-definition pointer p that a later reuse form may lean on (a check, check_ahead, renew_if, check_limit or half_check): q1 with p, its bounds and its alignment.

1109Definition renew_sourceonce : listname × def i : inst : optionname × name × Z × Z × Z ≔
match i with
Letq1 A o args
⇒
let src ≔
match o, args with
Checklo hi _ al, Tmpp | CheckAheadlo hi _ al _, Tmpp | RenewIflo hi _ al, Tmpp; _ | CheckLimitlo hi _ al, Tmpp; _ | HalfCheck_ lo hi _ al, _; Tmpp
⇒
Somep, lo, hi, al
_, _
⇒
None
in
match src with
Somep, lo, hi, al
⇒
once_hasonce q1 && once_hasonce p
→
Someq1, p, lo, hi, al
else
None
None
⇒
None
_
⇒
None

As hull_renewable/8: a renew_hull of the pointer p over the bytes from lo up to hi, at alignment al, may lean on the two renew sources ql and qh: the span lies between ql's start and qh's end at distances from 0 to 2^62 (pointer_distance), is at most 2^62 bytes, and one of the sources gives the alignment.

1131Definition hull_renewablesrcs : listname × name × Z × Z × Z once : listname × def p : value lo hi al : Z ql qh : value : bool ≔
match ql, qh with
Tmpnl, Tmpnh
⇒
match lookupnl srcs, lookupnh srcs with
Somepl, lol, _, all, Someph, loh, hih, alh
⇒
match pointer_distanceonce p Tmppl, pointer_distanceonce p Tmpph with
Somedl, Somedh
⇒
let e ≔ lo + dl − lol in
let f ≔ hih − hi − dh in
0 ≤? e && e ≤? 2 ^ 62 && 0 ≤? f && f ≤? 2 ^ 62 && hi − lo ≤? 2 ^ 62 && all mod al =? 0 && e mod al =? 0 || alh mod al =? 0 && lo + dh − loh mod al =? 0
_, _
⇒
false
_, _
⇒
false
_, _
⇒
false

As guard_covers/5: the operand g is a single-definition span test of a single-definition pointer n0 whose span contains the bytes from lo up to hi of the pointer p, for every offset p can have from n0 on its guard chain: what a renew_if needs of its guard.

1156Definition guard_coversonce : listname × def p g : value lo hi : Z : bool ≔
match g with
Tmpgn
⇒
match lookupgn once with
SomeDInstLetgn′ W SpanTestglo ghi Tmpn0
⇒
name_eqbgn′ gn && once_hasonce n0
&&
match first_someλe ⇒ if value_eqbfste Tmpn0 then Somesnde else None guard_chainonce p with
Someolo, ohi
⇒
glo ≤? olo + lo && ohi + hi ≤? ghi
None
⇒
false
_
⇒
false
_
⇒
false

As lower_chain/3: the pointer p has the lower of the base b: p is b, or a single-definition flight whose lower donor has b's lower, whatever its address (a lower chain).

1175Fixpoint lower_chainonce : listname × def fuel : nat p b : value : bool ≔
||
match fuel, p with
Sf, Tmpx
⇒
match lookupx once with
⇒
lower_chainonce f d b
_
⇒
false
_, _
⇒
false

As limit_covers/5: the operand u is a single-definition limit of size bytes of a single-definition pointer n0 whose lower p has (lower_chain), and the access span from lo up to hi is exactly from 0 up to that size: what a check_limit needs.

1190Definition limit_coversonce : listname × def p u : value lo hi : Z : bool ≔
match u with
Tmpun
⇒
match lookupun once with
SomeDInstLetun′ L Limitsize Tmpn0
⇒
name_eqbun′ un && lo =? 0 && hi =? size && once_hasonce n0 && lower_chainonce 64 p Tmpn0
_
⇒
false
_
⇒
false

As hull_covered/7: the instruction i, a raw access through the proof q of the pointer p, lies within the hull of the live facts about p's object: one fact's span starts at or before it and one ends at or after it, measured from p by pointer_distance at near offsets (near_offset). It is aligned as q's proof gives (q's offset lo, alignment al), and a write needs a fact with write rights.

1209Definition hull_coveredi : inst q : name p : value lo al : Z live : listfact once : listname × def : bool ≔
match raw_accessi with
Someq′, x, off, need
⇒
let size, aa ≔ access_size_alignx in
let end_ ≔ off + size in
let spans ≔
flat_map
λ f  ⇒
match pointer_distanceonce f_pf p with
Somed
⇒
let l1 ≔ f_lof + d in
let h1 ≔ f_hif + d in
→
l1, h1, f_rf
else
[ ]
None
⇒
[ ]
live
in
name_eqbq′ q && al mod aa =? 0 && off − lo mod aa =? 0 && near_offsetoff && near_offsetend_
&&
existsb
λ s  ⇒
let 'l1, _, _ ≔ s in
l1 ≤? off
spans
&&
existsb
λ s  ⇒
let '_, h1, _ ≔ s in
end_ ≤? h1
spans
&&
match need with
⇒
true
⇒
existsb
λ s  ⇒
let '_, _, r ≔ s in
spans
None
⇒
false

Scheduled checks

The needs at a point of the backward pass of scheduled checks: each base with the offset its bytes are certain to be accessed up to.

1240Definition facts ≔
listname × Z

The needs fs without the base n's.

1243Definition deln : name fs : facts : facts ≔
filterλf ⇒ negbname_eqbfstf n fs

As ahead_meet/2: the bases in every one of the need sets fss, each with its least need; none for no sets.

1247Definition ahead_meetfss : listfacts : facts ≔
match fss with
[ ]
⇒
[ ]
f :: rest
⇒
flat_map
λ bh  ⇒
match fold_leftλacc f′ ⇒ let* h ≔ acc in let* h1 ≔ lookupfstbh f′ in SomeZ.minh h1 rest Somesndbh with
Someh
⇒
fstbh, h
None
⇒
[ ]
f

As raise_need/6: the needs fs after an access of size bytes at offset off from the chain element e: when e is an exact base n at offset k, and k and its end are near (near_offset), n's need is raised to at least that end.

1264Definition raise_needonce : listname × def off size : Z fs : facts e : value × Z × Z : facts ≔
match exact_baseonce e with
Somen, k
⇒
let h ≔ k + off + size in
→
match lookupn fs with
Someh0
⇒
h ≤? h0
→
fs
else
putn h fs
None
⇒
putn h fs
else
fs
None
⇒
fs

As ahead_step/5: the needs fs before the instruction i, given those after it. An instruction that is not ahead-transparent ends every need; one that defines a base ends that base's; a raw access through a proof of ptrs raises the need of each base on its pointer's guard chain (raise_need).

1283Definition ahead_steponce : listname × def ptrs : listname × value i : inst fs : facts : facts ≔
→
let fs1 ≔
match first_someλdk ⇒ match lookupfstdk fs with Some_ ⇒ Somefstdk | None ⇒ None end inst_temp_defsi with
Somed
⇒
deld fs
None
⇒
fs
in
match raw_accessi with
Someq, x, off, _
⇒
match lookupq ptrs with
Somep
⇒
fold_leftraise_needonce off fstaccess_size_alignx guard_chainonce p fs1
None
⇒
fs1
None
⇒
fs1
else
[ ]

As proof_pointer/3: the pointer that the check or reuse form o with args proves.

1306Definition proof_pointero : op args : listvalue : optionvalue ≔
match o, args with
Check_ _ _ _, p | CheckAhead_ _ _ _ _, p
⇒
Somep
Renew_ _ _ _, _; p | HalfCheck_ _ _ _ _, _; p
⇒
Somep
RenewHull_ _ _ _, _; _; p
⇒
Somep
RenewIf_ _ _ _, p; _ | CheckLimit_ _ _ _, p; _
⇒
Somep
_, _
⇒
None

As proof_pointers/3: each single-definition access proof of the function f with the pointer it proves.

1317Definition proof_pointersf : func once : listname × def : listname × value ≔
flat_map
λ b  ⇒
flat_map
λ i  ⇒
match i with
Letq A o args
⇒
match proof_pointero args with
Somep
⇒
once_hasonce q
→
q, p
else
[ ]
None
⇒
[ ]
_
⇒
[ ]

The proof ahead_insts/8 accepts at the instruction i: a single-definition check_ahead q of the pointer P whose access is followed by the needs next, when some exact base N of P at offset K, with K and K + lo near (near_offset), has a need of at least K + hi there.

1333Definition ahead_acceptsonce : listname × def next : facts i : inst : optionname ≔
match i with
Letq A CheckAheadlo hi _ _ _ p
⇒
once_hasonce q
&&
existsb
λ e  ⇒
match exact_baseonce e with
Somen, k
⇒
&&
match lookupn next with
Someh
⇒
k + hi ≤? h
None
⇒
false
None
⇒
false
→
Someq
else
None
_
⇒
None

As ahead_insts/8: the check_ahead proofs accepted in one block, walking its instructions ris from the last. after are the needs after the current instruction, next those after the one that follows it, its access; js collects the accepted proofs.

1351Fixpoint ahead_instsonce : listname × def ptrs : listname × value ris : listinst after next : facts js : listname : facts × listname ≔
match ris with
[ ]
⇒
after, js
i :: rest
⇒
let js1 ≔
match ahead_acceptsonce next i with
Someq
⇒
q :: js
None
⇒
js
in
ahead_instsonce ptrs rest ahead_steponce ptrs i after after js1

The position of the label l in ls, counting from n.

1361Fixpoint index_ofl : name ls : listname n : nat : optionnat ≔
match ls with
[ ]
⇒
None
l′ :: rest
⇒
name_eqbl l′
→
Somen
else
index_ofl rest Sn

As ahead_edge/5: the needs the edge t carries back to the block at position pos: its target's entry needs if it is a plain edge to a later block, none over a poll edge or an edge to the same or an earlier block.

1368Definition ahead_edgeorder : listname pos : optionnat ins : listname × facts t : target : facts ≔
match t with
⇒
[ ]
⇒
match index_ofl order 0, pos, lookupl ins with
Sometp, Somep, Somefs
⇒
p <? tp%nat
→
fs
else
[ ]
_, _, _
⇒
[ ]

As phi_ends_need/3, folded over the phis phs: a phi redefines its destination at the block's entry, which ends that base's need.

1381Definition phis_end_needsphs : listphi fs : facts : facts ≔
fold_left
λ fs : facts ph : phi  ⇒
match ph with
Phid _ _
⇒
deld fs
phs
fs

As ahead_block/6: one block b of the backward pass. Its exit needs are the meet over its jump's edges; its entry needs go into ins and its accepted proofs into js.

1387Definition ahead_blockorder : listname once : listname × def ptrs : listname × value st : listname × facts × listname b : block : listname × facts × listname ≔
let ins, js ≔ st in
let out ≔ ahead_meetmapahead_edgeorder index_ofb_labelb order 0 ins jump_targetsb_jumpb in
let 'entry0, js′ ≔ ahead_instsonce ptrs revb_instsb out [ ] js in
putb_labelb phis_end_needsb_phisb entry0 ins, js′

Every block's entry needs and the accepted check_ahead proofs, from one pass over the function f's blocks in reverse order.

1396Definition ahead_foldf : func once : listname × def : listname × facts × listname ≔
fold_leftahead_blockmapb_label fn_blocksf once proof_pointersf once revfn_blocksf [ ], [ ]

As ahead_justified/3: the scheduled checks of the function f that are justified: right after its access, its pointer's base needs at least the bytes up to the check's end, so where the check fails early the original program would have trapped later on every path. The needs are computed backward in one pass over the blocks in reverse order; any edge into a later block was walked first.

1405Definition ahead_justifiedf : func once : listname × def : listname ≔
sndahead_foldf once

Getters

The linkage of the first function the module md defines under the name g, in the core subset or outside it.

1412Fixpoint first_func_linkageg : name md : module : optionterm ≔
match md with
[ ]
⇒
None
IFuncf :: rest
⇒
IOutsiden t :: rest
⇒
→
match t with
TApp"func" _; lk; _; _; _
⇒
Somelk
_
⇒
None
_ :: rest
⇒

The linkage of the first data the module md defines under the name g, skipping extern(weak) declarations, which define nothing.

1424Fixpoint first_data_linkageg : name md : module : optionlinkage ≔
match md with
[ ]
⇒
None
IDatan lk _ _ :: rest
⇒
match lk with
_ :: rest
⇒

As local_global/2: g is a function or data object the module md defines with local linkage, as the first item of that name says, so its getter is a local symbol only this module calls.

1438Definition local_globalmd : module g : name : bool ≔
match first_func_linkageg md with
Somelk
⇒
term_eqblk TAtom"local"
None
⇒
match first_data_linkageg md with
SomeLLocal
⇒
true
SomeLRol | SomeLTlsl
⇒
term_eqbl TAtom"local"
_
⇒
false

As repeated_getters/4: each single-definition getter copy d of a local symbol g (local_global) that another copy of g dominates, earlier in its block or in a block that strictly dominates it. Such a copy repeats a call of the getter that has already returned, so it reaches no safepoint, frees nothing and stores nothing, and the evidence pass lets the live facts survive it.

1456Definition repeated_gettersmd : module f : func doms : nmapZ × Z once : listname × def : listname × name ≔
let sites ≔
flat_map
λ b  ⇒
snd
fold_left
λ acc : Z × listname × Z × name × name i : inst  ⇒
let j, ss ≔ acc in
j + 1
match i with
⇒
→
ss ++ b_labelb, j, d, g
else
ss
_
⇒
ss
0, [ ]
in
flat_map
λ s  ⇒
let 'l, i, d, g ≔ s in
once_hasonce d && is_reachabledoms l
&&
existsb
λ s1  ⇒
let 'l1, i1, _, g1 ≔ s1 in
&&
→
i1 <? i
else
dominatesdoms l1 l
sites
→
d, g
else
[ ]
sites

The evidence pass

What stage_sources gathers for one function, stage_sources/4's src: the renew sources, the single definitions, the justified scheduled checks and the repeated getters.

1482Record src: Type ≔ mkSrc
s_renews
 : listname × name × Z × Z × Z
s_once
 : listname × def
s_ahead
 : listname
s_getters
 : listname × name

As stage_sources/4: the src of the function f of the module md, with doms its dominator intervals.

1491Definition stage_sourcesmd : module f : func doms : nmapZ × Z : src ≔
let once ≔ single_definitionsf in
flat_map
λ b  ⇒
flat_map
λ i  ⇒
match renew_sourceonce i with
Somer
⇒
r
None
⇒
[ ]
once
repeated_gettersmd f doms once

The live facts after a check-like instruction i of the proof q over the bytes from lo up to hi and its access: as ir.pl does, evidence_after/3 of the check, then evidence_add/3 of the new fact, then evidence_after/3 of the access.

1503Definition after_checki access : inst q : name p : value lo hi : Z r : rights al : Z live : listfact : listfact ≔
evidence_afteraccess mkFactq p lo hi r al :: evidence_afteri live

As checked_inst_rest/7, one step of the evidence pass: None when ir.pl throws; otherwise the instructions left after the instruction i, and the live facts after.

  • A check or reuse form (renew, renew_hull, half_check, renew_if, check_limit, an accepted check_ahead) must be followed at once by an access it covers, which it consumes, and adds its fact.
  • A check_local and a check_function are used once, right away.
  • Any other raw access must read a live fact that covers it, directly or by the hull of the live facts (hull_covered). A raw_call not right after its check_function, an indirect call, and pointer arithmetic, itop or a pointer copy outside a flight are refused.
  • A repeated getter (repeated_getters) ends only the facts naming its destination; any other getter ends them all.
1523Definition checked_inst_rests : src i : inst rest : listinst live : listfact : optionlistinst × listfact ≔
let once ≔ s_onces in
let with_access q p lo hi r al ok : bool ≔
negbok
→
None
else
match rest with
access :: rest′
⇒
proof_useaccess q lo hi r al
→
Somerest′, after_checki access q p lo hi r al live
else
None
[ ]
⇒
None
in
match i with
Letq A Checklo hi r al p
⇒
with_accessq p lo hi r al true
Letq A CheckAheadlo hi r al ahi p
⇒
negbmemq s_aheads
→
None
else
match rest with
access :: rest′
⇒
proof_useaccess q lo ahi r al && ahead_transparentaccess
→
Somerest′, after_checki access q p lo hi r al live
else
None
[ ]
⇒
None
Letq A CheckLocal_ _ bytes r al [ ]
⇒
match rest with
access :: rest′
⇒
match raw_accessaccess with
Someq′, _, 0, _
⇒
name_eqbq′ q && covered_accessaccess q 0 bytes r al && storage_effectsaccess Local'
→
Somerest′, evidence_afteraccess killq live
else
None
_
⇒
None
[ ]
⇒
None
⇒
match rest with
RawCall_ Tmpq′ _ :: rest′
⇒
name_eqbq′ q
→
Somerest′, [ ]
else
None
_
⇒
None
Letq A Renewlo hi r al q1; p
⇒
match q1 with
Tmpn1
⇒
match lookupn1 s_renewss with
Somep1, lo1, hi1, al1
⇒
match pointer_distanceonce p Tmpp1 with
Somed
⇒
with_accessq p lo hi r al lo1 ≤? lo + d && hi + d ≤? hi1 && al1 mod al =? 0 && lo + d − lo1 mod al =? 0
None
⇒
None
None
⇒
None
_
⇒
None
Letq A RenewHulllo hi r al ql; qh; p
⇒
with_accessq p lo hi r al hull_renewables_renewss once p lo hi al ql qh
Letq A HalfCheckbd lo hi r al q1; p
⇒
with_accessq p lo hi r al
match q1 with
Tmpn1
⇒
match lookupn1 s_renewss with
Somep1, lo1, hi1, _
⇒
match pointer_distanceonce p Tmpp1 with
Somed0
⇒
half_knownbd lo hi d0 lo1 hi1
None
⇒
false
None
⇒
false
_
⇒
false
Letq A RenewIflo hi r al p; g
⇒
with_accessq p lo hi r al guard_coversonce p g lo hi
Letq A CheckLimitlo hi r al p; u
⇒
with_accessq p lo hi r al limit_coversonce p u lo hi
_
⇒
let live′ ≔
match i with
⇒
match lookupd s_getterss with
Someg1
⇒
→
killd live
else
None
⇒
_
⇒
in
let ok ≔
match raw_accessi with
Someq, x, _, _
⇒
&&
match fact_ofq live with
Somefa
⇒
covered_accessi q f_lofa f_hifa f_rfa f_alfa || hull_coveredi q f_pfa f_lofa f_alfa live once
None
⇒
false
None
⇒
match i with
RawCall_ _ _
⇒
false
Call_ Tmp_ _
⇒
false
unchecked_access
Let_ P Add | Sub | Itop _
⇒
false
implicit_flight
⇒
false
_
⇒
true
in
ok
→
Somerest, live′
else
None

As checked_insts/5: the live facts at the end of the instructions is, starting from live, or None when one is refused; fuel bounds the steps.

1631Fixpoint checked_instss : src fuel : nat is : listinst live : listfact : optionlistfact ≔
match is with
[ ]
⇒
Somelive
i :: rest
⇒
match fuel with
O
⇒
None
Sf
⇒
let* rl ≔ checked_inst_rests i rest live in
checked_instss f fstrl sndrl

As incoming_edges/2 for the label l: its incoming edges, each with whether it is a poll edge, one per jump target, the last first (add_edge/3 prepends).

1647Definition incomingbs : listblock l : name : listname × bool ≔
rev
flat_map
λ b  ⇒
flat_map
bs

As checked_block/6, folded over the function f's blocks in order (in stage_func/6): the evidence pass. A block's entry evidence is the meet over its incoming edges of the facts each carries, none over a poll edge or from a block not yet walked; a phi ends the facts naming its destination; then each instruction must pass checked_inst_rest.

1658Definition evidence_passs : src f : func : bool ≔
match fold_leftλacc : optionlistname × listfact b : block ⇒ let* exits ≔ acc in let sets ≔ mapλe : name × bool ⇒ if snde then [ ] else match lookupfste exits with Somefs ⇒ fs | None ⇒ [ ] end incomingfn_blocksf b_labelb in let live0 ≔ evidence_meetsets in let live1 ≔ fold_leftλfs : listfact ph : phi ⇒ match ph with Phid _ _ ⇒ filterλfa ⇒ negbname_eqbf_qfa d || value_eqbf_pfa Tmpd fs end b_phisb live0 in let* out ≔ checked_instss Slengthb_instsb b_instsb live1 in Someputb_labelb out exits fn_blocksf Some[ ] with
Some_
⇒
true
None
⇒
false

As limit_reads/2: a limit of the function f is read only as a check_limit's second argument, never by a phi, a jump or another instruction.

1682Definition limit_readsf : func : bool ≔
let limits ≔
flat_map
λ b  ⇒
flat_map
λ i  ⇒
match i with
Letn _ Limit_ _
⇒
n
_
⇒
[ ]
in
let none_read us : listname ≔ negbexistsbλu ⇒ memu limits us in
match limits with
[ ]
⇒
true
_
⇒
forallb
λ b  ⇒
forallb
λ i  ⇒
match i with
Let_ _ CheckLimit_ _ _ _ p; u
⇒
none_readvalue_tempsp
&&
match u with
⇒
memn limits
_
⇒
false
_
⇒
none_readinst_usesi
&&
none_readjump_usesb_jumpb
&&
forallb
λ ph  ⇒
match ph with
Phi_ _ pairs
⇒
none_readvalue_tempsmapsnd pairs

The search of polls_every_cycle: blocks with no unpolled edge into what is left of the graph g are removed until none is left (acyclic) or none can be (a cycle without a poll).

1709Fixpoint peelfuel : nat g : listname × listname : bool ≔
match g with
[ ]
⇒
true
_
⇒
match fuel with
O
⇒
false
Sf
⇒
let left ≔ mapfst g in
let sinks ≔ filterλnl ⇒ negbexistsbλs ⇒ mems left sndnl g in
match sinks with
[ ]
⇒
false
_
⇒
peelf filterλnl ⇒ negbmemfstnl mapfst sinks g

As polls_every_cycle/1: every cycle of the function f's control-flow graph, unreachable blocks included, has a poll edge: without them the graph is acyclic.

1728Definition polls_every_cyclef : func : bool ≔
let g ≔ mapλb ⇒ b_labelb, mapedge_label filterλt ⇒ negbis_pollt jump_targetsb_jumpb fn_blocksf in
forallbλnl ⇒ forallbλs ⇒ mems mapfst g sndnl g && peelSlengthg g

As stage_func/6 for the checked stage: the function f passes the evidence pass, reads its limits only in check_limit, and polls on every cycle.

1738Definition stage_funcmd : module f : func doms : nmapZ × Z : bool ≔

The stage

Whether the op o is a word only the checked stage uses: a check, a reuse form, a span test, a limit or a raw load (the let clauses of checked_inst/1).

1746Definition checked_opo : op : bool ≔
match o with
Check_ _ _ _ | CheckAhead_ _ _ _ _ | Renew_ _ _ _ | RenewHull_ _ _ _ | HalfCheck_ _ _ _ _ | SpanTest_ _ | RenewIf_ _ _ _ | Limit_ | CheckLimit_ _ _ _ | CheckLocal_ _ _ _ _ | CheckFunction | RawLoad_ _ | RawLoadPointer_ _
⇒
true
_
⇒
false

As checked_inst/1: the instruction i uses a word of the checked stage: a checked op, a raw store, a raw call or a flight.

1756Definition checked_insti : inst : bool ≔
match i with
Let_ _ o _
⇒
RawStore_ _ _ _ | RawStorePointer_ _ _ _ | RawCall_ _ _ | Flight_ _ _ _
⇒
true
_
⇒
false

As checked_jump/1: the jump j takes a poll edge.

1764Definition checked_jumpj : jump : bool ≔

checked_op, read off the term t of a function outside the core subset.

1768Definition checked_op_termt : term : bool ≔
match t with
TAtom"check_function"
⇒
true
TApp"check" | "check_ahead" | "renew" | "renew_hull" | "half_check" | "span_test" | "renew_if" | "limit" | "check_limit" | "check_local" | "raw_load" | "raw_load_pointer" | "raw_cas" _
⇒
true
_
⇒
false

checked_inst, read off the term t of an instruction outside the core subset, a touch included.

1779Definition checked_inst_termt : term : bool ≔
match t with
TApp"let" _; _; o; _
⇒
TApp"raw_store" | "raw_store_pointer" | "raw_call" | "flight" | "touch" _
⇒
true
_
⇒
false

checked_jump, read off the term t of a jump outside the core subset.

1788Definition checked_jump_termt : term : bool ≔
match t with
TApp"goto" TApp"poll" _
⇒
true
TApp"branch" _; a; b
⇒
match a with
TApp"poll" _
⇒
true
_
⇒
match b with
TApp"poll" _
⇒
true
_
⇒
false
_
⇒
false

As module_stage/2: the module md is in the checked stage when any function uses a word of it, a check, raw access, flight or poll edge. The stage is read off the vocabulary of the whole module, never stated: a module whose only checked word is one poll edge is unchecked without it, and then needs no poll on its cycles.

1802Definition module_checkedmd : module : bool ≔
existsb
λ it  ⇒
match it with
⇒
existsbλb ⇒ existsbchecked_inst b_instsb || checked_jumpb_jumpb fn_blocksf
IOutside_ TApp"func" _; _; _; _; TListbs
⇒
existsb
λ b  ⇒
match b with
TApp"block" _; _; TListis; j
⇒
_
⇒
false
bs
_
⇒
false
md

Functions and modules

The x rule of valid_item/4 for a function: whether an x (80-bit extended float) value of the function f crosses where it may not: as a parameter, the result, a call's argument or result, or a phi.

1821Definition extended_crossesf : func : bool ≔
let ext k ≔ class_eqbk Extended in
match fn_retf with
⇒
extk
_
⇒
false
||
existsbλp ⇒ extfstp fn_paramsf
||
existsb
λ b  ⇒
existsb
λ ph  ⇒
match ph with
Phi_ k _
⇒
extk
||
existsb
λ i  ⇒
match i with
Callr _ args
⇒
match r with
⇒
extk
_
⇒
false
||
existsbλa ⇒ extarg_classa args
_
⇒
false

As valid_item/4 for a function f of the core subset of the module md, in filc mode: it has blocks and a linkage of export, local or weak, a valid return type, no x crossing (extended_crosses), distinct labels, one class per temporary, no jump to the entry block, proofs defined only by checks, the strict SSA rule, valid locals and valid blocks; in a checked module also stage_func. In the unchecked stage there is no evidence to check, and the core subset then has no memory access at all.

1843Definition valid_funcmd : module f : func : bool ≔
let bs ≔ fn_blocksf in
let defs ≔ temp_defsf in
match bs, block_intervalsbs with
[ ], _ | _, None
⇒
false
entry :: _, Somedoms
⇒
match fn_linkagef with
TAtom"export" | "local" | "weak"
⇒
true
_
⇒
false
&&
match fn_retf with
⇒
lengthks =? 2%nat && forallbλk ⇒ existsbclass_eqbk L; P; Single; Double ks
⇒
true
&&
&&
&&
&&
negbexistsbλb ⇒ memb_labelentry succ_labelsb_jumpb bs
&&
&&
&&
&&
forallbvalid_blockmd f defs bs
&&
negbmodule_checkedmd || stage_funcmd f doms

Data

As data_align/1: a data object's alignment a is a power of two from 1 to 4096.

1872Definition data_aligna : Z : bool ≔
1 ≤? a && a ≤? 4096 && Z.landa a − 1 =? 0

The linkage clauses of valid_item/4 for data: the linkages lk the data of a Fil-C module may have, and what extern(weak) (no items) and init(Kind, Prio) (a constructor or destructor, a priority up to 65535, one pointer item at offset 0) ask of its items its.

1879Definition data_linkage_oklk : linkage its : listdata_item : bool ≔
match lk with
⇒
true
LRoTAtom"export" | "local" | "weak"
⇒
true
LTlsTAtom"export" | "local"
⇒
true
⇒
match its with
[ ]
⇒
true
_
⇒
false
LInitk prio
⇒
match k with
TAtom"ctor" | "dtor"
⇒
true
_
⇒
false
&&
match prio with
TAtom"none"
⇒
true
⇒
0 ≤? n && n ≤? 65535
_
⇒
false
&&
match its with
Sym_ off
⇒
off =? 0
_
⇒
false
_
⇒
false

What valid_item/4 asks of each data item it: it has one of the header's shapes (data_item_shape/1), a float item's constant is below its class's modulus, a string holds bytes, and a pointer names its symbol by an atom. In a Fil-C module item(l, sym(_, _)), like any other term, decodes to ItemOther and is refused.

1901Definition data_item_okit : data_item : bool ≔
match it with
⇒
0 ≤? n && n <? 2 ^ 32
⇒
0 ≤? n && n <? 2 ^ 64
⇒
0 ≤? n && n <? 2 ^ 80
Item_ _
⇒
true
Strcs
⇒
forallbλc ⇒ c ≤? 255 cs
⇒
true
Sym_ _
⇒
false
⇒
0 ≤? n
⇒
false

As pointer_items_aligned/3: each pointer item of its, counting off bytes into the object, starts at a multiple of 8.

1916Fixpoint pointer_items_alignedoff : Z its : listdata_item : bool ≔
match its with
[ ]
⇒
true
it :: rest
⇒
match it with
Sym_ _
⇒
off mod 8 =? 0
_
⇒
true
&&

Whether the module md defines a function named n, in the core subset or not (func_named/3).

1926Definition func_namedmd : module n : name : bool ≔
match first_func_linkagen md with
Some_
⇒
true
None
⇒
false

As data_named/2: the module md defines data named n, that is, data of that name not declared extern(weak).

1931Definition data_namedmd : module n : name : bool ≔
match first_data_linkagen md with
Some_
⇒
true
None
⇒
false

What data asks of the rest of the module md: a constructor or destructor table entry names a function the module defines; a weak declaration is of no name the module defines with local linkage (local_global/2), whose symbols it would make weak and global; and no pointer item names a table entry, which has no address.

1939Definition data_names_okmd : module n : name lk : linkage its : listdata_item : bool ≔
match lk, its with
LInit_ _, Symf _
⇒
_, _
⇒
true
&&
forallb
λ it  ⇒
match it with
Syms _
⇒
_
⇒
true
its

As valid_item/4 for data(Name, Linkage, Align, Items): the data object n of the module md has a valid alignment, linkage and items, its pointer items aligned, and names it may refer to.

1950Definition valid_datamd : module n : name lk : linkage align : Z its : listdata_item : bool ≔
data_alignalign && data_linkage_oklk its && forallbdata_item_ok its && pointer_items_aligned0 its && data_names_okmd n lk its

Aliases

The first function of each name in the module md, as module_symbols/2 indexes them; seen are the names met so far.

1959Fixpoint first_funcsseen : listname md : module : listitem ≔
match md with
[ ]
⇒
[ ]
IFuncf :: rest
⇒
→
first_funcsseen rest
else
IFuncf :: first_funcsfn_namef :: seen rest
IOutsiden t :: rest
⇒
memn seen
→
first_funcsseen rest
else
IOutsiden t :: first_funcsn :: seen rest
_ :: rest
⇒
first_funcsseen rest

memberchk(call(_, global(Name), _), Is) for some block of the function it: whether it calls the symbol n directly, read off its blocks in the core subset or off its term outside it.

1974Definition calls_globalit : item n : name : bool ≔
match it with
⇒
existsb
λ b  ⇒
existsb
λ i  ⇒
match i with
_
⇒
false
IOutside_ TApp"func" _; _; _; _; TListbs
⇒
existsb
λ b  ⇒
match b with
TApp"block" _; _; TListis; _
⇒
existsb
λ i  ⇒
match i with
TApp"call" _; TApp"global" g; _
⇒
_
⇒
false
is
_
⇒
false
bs
_
⇒
false

The name of a function item it, in the core subset or not.

1995Definition item_nameit : item : optionname ≔
match it with
⇒
Somefn_namef
⇒
Somen
_
⇒
None

As valid_item/4 for alias(Name, Linkage, Target): the alias n of the module md has linkage export or weak, no function or data shares its name, and its target is a function the module defines, or data of a linkage an alias may name (export, local, weak or ro(_)) that no other function calls through the alias.

2003Definition valid_aliasmd : module n : name lk : term target : name : bool ≔
match lk with
TAtom"export" | "weak"
⇒
true
_
⇒
false
&&
negbfunc_namedmd n || data_namedmd n
&&
func_namedmd target
||
match first_data_linkagetarget md with
SomeLExport | LLocal | LWeak | LRo_
⇒
negb
existsb
λ it  ⇒
match item_nameit with
Someg
⇒
negbname_eqbg n && calls_globalit n
None
⇒
false
_
⇒
false

Modules

As defined_name/2: the name the item it defines; a weak declaration defines none.

2020Definition defined_nameit : item : optionname ≔
match it with
⇒
Somefn_namef
⇒
Somen
⇒
None
IDatan _ _ _
⇒
Somen
IAliasn _ _
⇒
Somen

As unique_names/1: no two items of the module md define the same name. Functions, data and aliases share one namespace.

2031Definition unique_namesmd : module : bool ≔
match fold_leftλacc it ⇒ let* seen ≔ acc in match defined_nameit with Somen ⇒ if nm_memn seen then None else Somenm_addn tt seen | None ⇒ Someseen end md Somenm_empty with
Some_
⇒
true
None
⇒
false

No two items of the module md share a name, and each item the model can judge is valid: functions, data and aliases.

2044Definition valid_itemsmd : module : bool ≔
&&
forallb
λ it  ⇒
match it with
IDatan lk al its
⇒
valid_datamd n lk al its
IAliasn lk t
⇒
valid_aliasmd n lk t
⇒
true
md

As valid_module/1 for a Fil-C module without its contexts: whether the validator accepts the module md, or None when a function is outside the core subset, which the model cannot judge. The context/2 items are provenance's records, which provenance:valid_contexts/1 checks and interp:run/5 never reads; the model has none (docs/rocq.md).

2060Definition validmd : module : optionbool ≔
existsb
λ it  ⇒
match it with
⇒
true
_
⇒
false
md
→
None
else