Fil-C on Nix

Filnix wiki

the formal campaign

rocq/backend/Liveness.v

Liveness of temporaries over fused blocks (cfg.pl's temp_liveness/3): the temporaries live on entry to and on exit from each block, reachable or not, the least solution of

  • LiveOut(B) = the union over successors S of LiveIn(S) and the temporaries the phis of S read on the edge from B;
  • LiveIn(B) = Gen(B) ∪ (LiveOut(B) − Kill(B)).

The frame layout (FrameSlots.v) reads it. The sets are ordsets of names in the standard order, as the Prolog compiler's are. A least solution is the same in whatever order the blocks are visited, so this visits them in rounds where cfg.pl keeps a worklist.

From Stdlib Require Import String ZArith List Bool
From Nanofil Require Import Term Syntax Validate Maps Cfg
From Backend Require Import Order Plan Fuse
Import ListNotations
Open Scope Z_scope

Sets of names

An ordset of names: sorted in the standard order, each once.

24Definition names: Type ≔
listname

As sort/2 on names: the ordset of the names ns.

27Definition nsortns : listname : names ≔

As ord_union/3 on names.

30Definition nunionxs ys : names : names ≔

As ord_subtract/3 on names.

33Definition nsubtractxs ys : names : names ≔

The ordset the map m has for the label l, empty if it has none.

36Definition names_atm : nmapnames l : name : names ≔
match nm_findl m with
Somens
⇒
ns
None
⇒
[ ]

Gen and Kill

A block's events so far: the names defined, the names read before any definition, and the names defined, in reverse order of event.

44Record events: Type ≔ mkEvents
ev_defined
 : nmapunit
ev_gen
 : listname
ev_kill
 : listname

The events ev and then a use of n: n is read first if nothing defined it before.

52Definition use_eventev : events n : name : events ≔

The events ev and then a definition of n.

57Definition def_eventev : events n : name : events ≔

As block_gen_kill/3: Gen, the temporaries the block b reads before it writes them, and Kill, those it writes. Its events are the phi definitions, then each instruction's uses before its definitions, then the jump's uses.

64Definition block_gen_killb : fblock : names × names ≔
let ev0 ≔
fold_left
λ ev p  ⇒
match p with
Phid _ _
⇒
in
let ev1 ≔ fold_leftλev i ⇒ fold_leftλev d ⇒ def_eventev fstd finst_defsi fold_leftuse_event finst_usesi ev fb_instsb ev0 in
let ev2 ≔ fold_leftuse_event jump_usesfb_jumpb ev1 in

Edge uses

As phi_edge_uses/2, for one block: each predecessor p of the block mapped to the ordset of the temporaries its phis read on the edge from p.

79Definition block_edge_usesb : fblock : nmapnames ≔
let pairs ≔
flat_map
λ p  ⇒
match p with
Phi_ _ args
⇒
flat_map
λ a  ⇒
match snda with
⇒
fsta, t
_
⇒
[ ]
args
in
let by_pred ≔ fold_leftλm e ⇒ nm_addfste snde :: names_atm fste m pairs nm_empty in
fold_leftλm p ⇒ nm_addp nsortnames_atby_pred p m nsortmapfst pairs nm_empty

As phi_edge_uses/2: each block's label mapped to its block_edge_uses, for the blocks with phis.

94Definition phi_edge_usesbs : listfblock : nmapnmapnames ≔
fold_left
λ m b  ⇒
match fb_phisb with
bs

The fixpoint

As live_out/5: the live-out set of the block l, whose successors are ss, given the live-in sets.

104Definition live_outeu : nmapnmapnames live_in : nmapnames l : name ss : listname : names ≔
fold_left
λ out s  ⇒
let out1 ≔ nunionout names_atlive_in s in
match nm_finds eu with
Somem
⇒
None
⇒
out1
ss
[ ]

One round: each block of order in turn takes the live-in set its equation gives from the sets so far. The result has the new sets and whether any grew.

116Definition live_roundorder : listname succs : nmaplistname gk : nmapnames × names eu : nmapnmapnames live_in : nmapnames : nmapnames × bool ≔
fold_left
λ st l  ⇒
let m ≔ fstst in
let out ≔
live_outeu m l
match nm_findl succs with
Somess
⇒
ss
None
⇒
[ ]
in
match nm_findl gk with
Somegen, kill
⇒
let new ≔ nuniongen nsubtractout kill in
→
st
else
nm_addl new m, true
None
⇒
st
order
live_in, false

Up to 2^k rounds of step from m, stopping at the first that changes nothing; the flag says whether the last round run still changed something. The sets only grow, and there are finitely many names and blocks, so enough rounds reach the least solution.

135Fixpoint live_roundsk : nat step : nmapnames → nmapnames × bool m : nmapnames : nmapnames × bool ≔
match k with
O
⇒
stepm
Sk′
⇒
let r ≔ live_roundsk′ step m in
sndr
→
live_roundsk′ step fstr
else
r

As temp_liveness/3: the live-in and the live-out set of every block of bs, by label. The blocks are visited in postorder, a block after its successors where there is no cycle, then the unreachable ones; 2^40 rounds are more than any function needs.

148Definition temp_livenessbs : listfblock : optionnmapnames × nmapnames ≔
let* g ≔ func_cfgmapλb ⇒ mkBlockfb_labelb [ ] [ ] fb_jumpb bs in
let reached ≔ nm_of_listmapλl ⇒ l, tt cfg_rpog in
let order ≔ rev'cfg_rpog ++ filterλl ⇒ negbnm_meml reached mapfb_label bs in
let gk ≔ nm_of_listmapλb ⇒ fb_labelb, block_gen_killb bs in
let eu ≔ phi_edge_usesbs in
let r ≔ live_rounds40 live_roundorder cfg_succsg gk eu nm_empty in
sndr
→
None
else
let live_in ≔ fstr in
let live_outs ≔
map
λ b  ⇒
live_outeu live_in fb_labelb
Somess
⇒
ss
None
⇒
[ ]
bs
in
Somelive_in, live_outs