Fil-C on Nix

Filnix wiki

the formal campaign

rocq/backend/Escape.v

# Escape classification of allocs

As escape.pl, on terms: which temporaries of a function hold the address of an alloc at a known offset (address_temps), and, for each alloc, whether its address is only used for loads and stores at such offsets (alloc_classes). Promote.v turns the slots of such an alloc into temporaries.

The analysis rests on one fact: a local alloc can only be reached through its own address, so no other pointer or integer can name it. Offsets are exact integers.

escape.pl also says why an alloc escapes; no pass reads that, so it is left out.

From Stdlib Require Import String ZArith List Bool
From Nanofil Require Import Term Maps Semantics Cfg
From Backend Require Import Text Order Pizlonate Liveness TermCfg
Import ListNotations
Open Scope string_scope
Open Scope Z_scope
Open Scope list_scope

String equality, written as Prolog's ==.

25Notation a == b ≔ String.eqba b (at level 70)

Widths

As ir.pl's width_bytes(W, Bytes): the bytes of the width w.

30Definition width_bytesw : term : optionZ ≔
match w with
⇒
a == "b"
→
Some1
a == "h"
→
Some2
a == "w" || a == "s"
→
Some4
a == "l" || a == "p" || a == "d"
→
Some8
a == "x"
→
Some10
else
None
_
⇒
None

As slot_load_width(T, W): the width a load of the type t reads. None for a type whose load is no slot access, as x.

44Definition slot_load_widtht : term : optionterm ≔
match t with
⇒
a == "l" || a == "p" || a == "s" || a == "d"
→
Somet
a == "sw" || a == "uw"
→
SomeTAtom"w"
a == "sh" || a == "uh"
→
SomeTAtom"h"
a == "sb" || a == "ub"
→
SomeTAtom"b"
else
None
_
⇒
None

Instructions

The destination, class, op and arguments when the term i is let(D, K, Op, Args).

59Definition let_partsi : term : optionterm × term × term × listterm ≔
match i with
TAppf d; k; o; TListargs
⇒
f == "let"
→
Somed, k, o, args
else
None
_
⇒
None

Whether the op o is an alloc(Align).

66Definition is_alloco : term : bool ≔
match o with
TAppg _
⇒
g == "alloc"
_
⇒
false

The integer n when the value v is const(N).

70Definition const_intv : term : optionZ ≔
match v with
⇒
f == "const"
→
Somen
else
None
_
⇒
None

As offset_of(Inst, X, N): the instruction i gives its destination the value of the temporary x plus the constant n.

78Definition offset_ofi : term : optionterm × Z ≔
match let_partsi with
Some_, _, TAtomo, args
⇒
match args with
a; b
⇒
o == "add"
→
x, Somen, _, _
⇒
Somex, n
_, _, Somen, x
⇒
Somex, n
_, _, _, _
⇒
None
o == "sub"
→
match tmp_namea, const_intb with
x, Somen
⇒
Somex, −n
_, _
⇒
None
else
None
a
⇒
o == "copy"
→
match tmp_namea with
x
⇒
Somex, 0
_
⇒
None
else
None
_
⇒
None
_
⇒
None

Address temporaries

For each temporary that holds an address of an alloc: the alloc's temporary, the root, and the offset from it.

107Definition addresses: Type ≔
nmapterm × Z

The root and the offset when the value v is an address temporary.

111Definition address_ofaddrs : addresses v : term : listterm × Z ≔
flat_map
λ x  ⇒
match nm_findx addrs with
Somer
⇒
r
None
⇒
[ ]

The edges from the temporary x: the position, the destination and the constant of each instruction that adds a constant to x.

116Definition edges_atm : nmaplistZ × term × Z x : term : listZ × term × Z ≔
match nm_findx m with
Somees
⇒
es
None
⇒
[ ]

Of the pairs ps, sorted by key, the first with each key.

120Fixpoint first_of_keyA : Type ps : listterm × A : listterm × A ≔
match ps with
[ ]
⇒
[ ]
p :: ps′
⇒
match first_of_keyps′ with
[ ]
⇒
p
q :: rest
⇒
term_eqbfstp fstq
→
p :: rest
else
p :: q :: rest

As layers_addrs/4: the addresses addrs and those derived from the temporaries of layer, layer by layer. A temporary that two instructions of a layer define takes the first one's source. Every layer but the last adds a temporary that an instruction defines, so a fuel of their number, and one, is enough.

135Fixpoint layers_addrsfuel : nat by_source : nmaplistZ × term × Z layer : listterm × term × Z addrs : addresses : addresses ≔
match fuel, layer with
O, _
⇒
addrs
_, [ ]
⇒
addrs
Sfuel′, _
⇒
let found ≔
flat_map
λ e  ⇒
flat_map
λ edge  ⇒
let d ≔ sndfstedge in
nm_memd addrs
→
[ ]
else
fstfstedge, d, fstsnde, sndsnde + sndedge
edges_atby_source fste
layer
in
let ordered ≔ mapsnd msortλa b ⇒ Z.comparefsta fstb found in
let new ≔ first_of_keymsortλa b ⇒ term_comparefsta fstb ordered in
layers_addrsfuel′ by_source new fold_leftλm p ⇒ nm_addfstp sndp m new addrs

As address_temps(F, Addrs): the address temporaries of the blocks bs. The temporary of each alloc is its own root, at offset 0; a temporary defined as an address plus or minus a constant, or as its copy, is an address too. None when two allocs define one temporary, where list_to_assoc/2 throws.

160Definition address_tempsbs : listtblock : optionaddresses ≔
let insts ≔ flat_maptb_insts bs in
let allocs ≔
flat_map
λ i  ⇒
match let_partsi with
Somed, _, o, _
⇒
else
[ ]
None
⇒
[ ]
insts
in
let* addrs0 ≔
fold_left
λ acc a  ⇒
let* m ≔ acc in
→
None
else
Somenm_adda a, 0 m
allocs
in
let by_source ≔
fold_left
λ m pi  ⇒
match let_partssndpi, offset_ofsndpi with
Somed, _, _, _, Somex, n
⇒
match tmp_named with
d
⇒
nm_addx fstpi, d, n :: edges_atm x m
_
⇒
m
_, _
⇒
m
rev'numbered0 insts
in
Somelayers_addrsSlengthinsts by_source mapλa ⇒ a, a, 0 nsortallocs addrs0

Classes

An access acc(Off, W): the bytes of the width w at the offset off of an alloc.

190Definition acc: Type ≔
Z × term%type

The standard order of two accesses, as terms acc(Off, W).

193Definition acc_comparea b : acc : comparison ≔
thencZ.comparefsta fstb term_comparesnda sndb

Whether a and b are the same access.

197Definition acc_eqba b : acc : bool ≔
fsta =? fstb && term_eqbsnda sndb

The accesses the map m has for the alloc a, none if it has no entry.

202Definition accs_atm : nmaplistacc a : term : listacc ≔
match nm_finda m with
Somexs
⇒
xs
None
⇒
[ ]

What an instruction does to an alloc, the root: an access, or, with None, a use that lets its address escape.

207Definition event: Type ≔
term × optionacc%type

An escape for each address among the values vs.

210Definition escapes_ofaddrs : addresses vs : listterm : listevent ≔
mapλr ⇒ fstr, None flat_mapaddress_ofaddrs vs

Whether the op o is of a kind whose address arguments escape (kind_event/4): the arithmetic, a copy, the conversions between pointers and integers, va_args and va_arg, setjmp, a comparison, the extension of a narrow integer and an alloc.

217Definition escaping_opo : term : bool ≔
match o with
⇒
existsbString.eqba "add"; "sub"; "mul"; "div"; "udiv"; "rem"; "urem"; "and"; "or"; "xor"; "sar"; "shr"; "shl"; "neg"; "copy"; "ptoi"; "itop"; "va_args"; "va_arg"
TAppg x
⇒
g == "setjmp"
→
is_atom"setjmp" x || is_atom"_setjmp" x || is_atom"sigsetjmp" x
g == "ext"
→
match x with
⇒
match narrow_bytesa with
Some_
⇒
true
None
⇒
false
_
⇒
false
else
g == "alloc"
TAppg _; _
⇒
g == "cmp"
_
⇒
false

As inst_event(Inst, Addrs, Event): the events of the instruction i.

  • A load from an address is an access of the type's width, or an escape for a type with none.
  • A store to an address is an access; a store of an address lets it escape.
  • An address that an atomic access, a compare-and-swap, an access, an asm_fixed or a call reads escapes.
  • So does an address argument of an op of escaping_op, but in an instruction that derives an address from an address (offset_of).
  • A va_arg of an address is, besides, an access of a pointer.
248Definition inst_eventsaddrs : addresses i : term : listevent ≔
match i with
TAppf xs
⇒
f == "let"
→
match xs with
_; _; o; TListargs
⇒
let by_kind ≔
&&
negb
match offset_ofi with
Somey, _
⇒
nm_memy addrs
None
⇒
false
→
escapes_ofaddrs args
else
[ ]
in
match o with
TAppg t
⇒
g == "load"
→
match args with
a
⇒
mapλr ⇒ fstr, option_mapλw ⇒ sndr, w slot_load_widtht address_ofaddrs a
_
⇒
[ ]
g == "atomic_load"
→
match args with
a
⇒
escapes_ofaddrs a
_
⇒
[ ]
g == "cas"
→
escapes_ofaddrs args
else
by_kind
⇒
match args with
x
⇒
a == "va_arg"
→
by_kind ++ mapλr ⇒ fstr, Somesndr, TAtom"p" address_ofaddrs x
else
by_kind
_
⇒
by_kind
_
⇒
by_kind
_
⇒
[ ]
f == "store"
→
match xs with
w; v; a
⇒
escapes_ofaddrs v ++ mapλr ⇒ fstr, Somesndr, w address_ofaddrs a
_
⇒
[ ]
f == "atomic_store"
→
match xs with
_; v; a
⇒
escapes_ofaddrs v; a
_
⇒
[ ]
f == "access"
→
match xs with
_; a; _; _
⇒
escapes_ofaddrs a
_
⇒
[ ]
f == "asm_fixed"
→
match xs with
_; TListins; _
⇒
escapes_ofaddrs flat_mappair_value ins
_
⇒
[ ]
f == "call"
→
match xs with
_; g; TListargs
⇒
escapes_ofaddrs g :: flat_maparg_value args
_
⇒
[ ]
else
[ ]
_
⇒
[ ]

As jump_value(Jump, V): the values the jump j reads.

313Definition jump_valuesj : term : listterm ≔
match j with
TAppf v; _; _
⇒
f == "branch"
→
v
else
[ ]
TAppf v
⇒
f == "ret"
→
v
f == "rets"
→
else
[ ]
_
⇒
[ ]

Every definition of a temporary in a function: by a param, a phi, a let, a call or an asm_fixed. A temporary defined twice is there twice.

323Definition all_defsparams : listterm bs : listtblock : listterm ≔
flat_mapparam_def params ++ flat_mapλb ⇒ flat_mapphi_def tb_phisb ++ flat_mapcounted_defs tb_instsb bs

As alloc_classes(F, Addrs, DefCounts, Classes): for each let(tmp(A), _, alloc(_), [Size]) of the blocks bs, in their order, the temporary a, the size and the class: None when the address escapes, or else the ordset of the alloc's accesses, local(Size, Accs). Besides the instructions' events, an address escapes as a phi argument, as a jump's value, and when its temporary has two definitions.

334Definition alloc_classesaddrs : addresses counts : nmapnat params : listterm bs : listtblock : listterm × term × optionlistacc ≔
let insts ≔ flat_maptb_insts bs in
let twice d ≔
match nm_findd counts with
Somen
⇒
Nat.ltb1 n
None
⇒
false
in
let events ≔
flat_mapinst_eventsaddrs insts
++
flat_map
λ b  ⇒
flat_map
λ p  ⇒
match p with
TAppf _; _; TListargs
⇒
f == "phi"
→
escapes_ofaddrs flat_mappair_value args
else
[ ]
_
⇒
[ ]
bs
++
flat_mapλb ⇒ escapes_ofaddrs jump_valuestb_jumpb bs ++ escapes_ofaddrs maptmp filtertwice all_defsparams bs
in
let escaped ≔
fold_left
λ m e  ⇒
match snde with
None
⇒
nm_addfste tt m
Some_
⇒
m
events
in
let accessed ≔
fold_left
λ m e  ⇒
match snde with
Somea
⇒
nm_addfste a :: accs_atm fste m
None
⇒
m
events
in
flat_map
λ i  ⇒
match let_partsi with
Somed, _, o, size
⇒
→
map
λ a  ⇒
a
size
nm_mema escaped
→
None
else
Somesortacc_compare accs_ataccessed a
else
[ ]
_
⇒
[ ]
insts