Fil-C on Nix

Filnix wiki

mbrock/filnix/nanofil

cfg_tabled.pl

The analyses of cfg.pl as tabled relations over IR facts, in the two styles the handoff proposed. They exist to answer PLAN's open question by measurement (tests/bench-cfg.pl, docs/m5.md); passes use cfg.pl.

StyleLoads the factsRelations
versioned factsassert_version/2, retract_version/1version_live_in/3, version_live_out/3, version_dominates/3, version_idoms/2, version_frontier/3
incremental tablingincremental_load/1, incremental_update/1, incremental_clear/0incremental_live_in/2, incremental_live_out/2

Versioned facts

assert_version/2 asserts the function's edges, entry, blocks, per-block gen and kill sets and phi edge uses as facts tagged with a version V. The relations over them take V as an argument, so every version has its own tables. retract_version/1 drops a version's facts and abolishes every table.

Incremental tabling

incremental_load/1 asserts unversioned facts that are declared incremental. incremental_update/1 replaces the gen, kill and phi edge-use facts that differ from those of the new blocks, and leaves the edges as they are; the host then invalidates only the tables that depend on the changed facts. incremental_clear/0 retracts every incremental fact.

Common ground

Both styles take the gen and kill sets from cfg.pl, which computes them by a local scan of each block: tabling that would only add table overhead.

Facts are asserted and retracted here, which is the "versioned facts at pass boundaries" exception in PLAN's rules. The negations (\+ kill, \+ avoid) are called with ground arguments only, so they are sound.

SWI-Prolog has tabling built in (its library(tabling) is deprecated) and declares incremental dynamic predicates through dynamic/2.

49:- if¬current_prolog_flagdialect, swi
usestabling
51:- endif
useslists
usesassoc
usesordsets
usescfg
57:- dynamicvf_edge/3

vf_edge(From, To, V)

58:- dynamicvf_pred/3

vf_pred(To, From, V)

59:- dynamicvf_entry/2

vf_entry(V, Entry)

60:- dynamicvf_block/2

vf_block(B, V)

61:- dynamicvf_gen/3

vf_gen(B, T, V)

62:- dynamicvf_kill/3

vf_kill(B, T, V)

63:- dynamicvf_euse/4

vf_euse(B, Succ, T, V): T read on edge B->Succ

Versioned facts

assert_version+V, +Blocksdet

Assert the edges, entry, blocks, gen/kill sets and phi edge uses of the function Blocks as facts tagged V.

72assert_versionV, Blocks ←
Blocks = blockEntry, _, _, _|_
assertzvf_entryV, Entry
blocks_gen_killBlocks, GenKill
phi_edge_usesBlocks, EdgeUses
forallmemberblockB, _, _, Jump, Blocks,
assertzvf_blockB, V
forall
jump_succsJump, Ss
memberS, Ss
,
assertzvf_edgeB, S, V
assertzvf_predS, B, V
get_assocB, GenKill, Gen−Kill
forallmemberT, Gen, assertzvf_genB, T, V
forallmemberT, Kill, assertzvf_killB, T, V
forall
gen_assocB−S, EdgeUses, Ts
memberT, Ts
,
assertzvf_euseB, S, T, V
retract_version+Vdet

Retract version V's facts and abolish every table (Trealla's abolish_table/1 drops all variants of a predicate, so old tables cannot be dropped one version at a time).

94retract_versionV ←
retractallvf_entryV, _
retractallvf_block_, V
retractallvf_edge_, _, V
retractallvf_pred_, _, V
retractallvf_gen_, _, V
retractallvf_kill_, _, V
retractallvf_euse_, _, _, V
abolish_all_tables

Every call below is a variant of an open version_live_in(V, _, _) or version_live_out(V, _, _), so each version has two tables in all.

106:- tableversion_live_in/3, version_live_out/3
version_live_in+V, ?B, ?Tnondet

Temp T is live on entry to block B in version V.

112version_live_inV, B, T ←
vf_genB, T, V
114version_live_inV, B, T ←

Trealla 12f4cbd: a \+ or if-then-else written inline after a tabled call in a tabled clause gives wrong answers (it lets version_live_in(start, T) through for a T that start defines); the same negation in its own predicate is right. docs/m5.md has the reproduction.

122v_not_killedB, T, V ←
¬vf_killB, T, V
version_live_out+V, ?B, ?Tnondet

Temp T is live on exit from block B in version V.

129version_live_outV, B, T ←
vf_predS, B, V
132version_live_outV, B, T ←
vf_euseB, _, T, V

avoid(V, Avoid, B): B is reachable from the entry in version V on a path that does not pass through the label of Avoid: some(D) avoids D, and none gives plain reachability. One table per Avoid.

138:- tableavoid/3
140avoidV, Avoid, B ←
vf_entryV, B
not_avoidedAvoid, B
143avoidV, Avoid, B ←
avoidV, Avoid, P
vf_edgeP, B, V
not_avoidedAvoid, B
not_avoided+Avoid, +B

B is not the label that Avoid avoids.

149not_avoidednone, _
150not_avoidedsomeD, B ←
B ≢ D
version_dominates+V, ?D, ?Bnondet

D dominates B in version V (both reachable).

157version_dominatesV, D, B ←
avoidV, none, D
avoidV, none, B
Not \+ avoid(V, some(D), B): that call is a new variant, and so a
new reachability table, for every B.
¬
avoidV, someD, X
X ≡ B
version_idoms+V, -Pairsdet

Pairs is the list B-Idom of the reachable blocks B of version V other than the entry and their immediate dominators, from version_dominates/3: the immediate dominator of B is the strict dominator with the most dominators of its own.

171version_idomsV, Pairs ←
findallB−Doms,
avoidV, none, B
findallD, version_dominatesV, D, B, Doms
,
BlockDoms
findallB−N,
memberB−Doms, BlockDoms
lengthDoms, N
,
Counts
list_to_assocCounts, Count
findallB−Idom,
memberB−Doms, BlockDoms
Doms = _, _|_
findallC−D,
memberD, Doms
D ≢ B
get_assocD, Count, C
,
Counted
msortCounted, Sorted
lastSorted, _−Idom
,
Pairs
version_frontier+V, ?X, ?Ynondet

Y is in the dominance frontier of X in version V. A Y can be found once per predecessor that X dominates.

202version_frontierV, X, Y ←
vf_edgeP, Y, V
avoidV, none, P
¬
X ≢ Y

Incremental tabling

if_pred(To, From), if_gen(B, T), if_kill(B, T), if_euse(B, T)

211:- ifcurrent_prolog_flagdialect, swi
212:- dynamicif_pred/2, if_gen/2, if_kill/2, if_euse/2, incrementaltrue
213:- else
214:- dynamicif_pred/2
215:- dynamicif_gen/2
216:- dynamicif_kill/2
217:- dynamicif_euse/2
218:- incrementalif_pred/2, if_gen/2, if_kill/2, if_euse/2
219:- endif
221:- tableincremental_live_in/2 as incremental
222:- tableincremental_live_out/2 as incremental
incremental_live_in?B, ?Tnondet

Temp T is live on entry to block B of the loaded function.

228incremental_live_inB, T ←
if_genB, T
230incremental_live_inB, T ←
235i_not_killedB, T ←
¬if_killB, T
incremental_live_out?B, ?Tnondet

Temp T is live on exit from block B of the loaded function.

242incremental_live_outB, T ←
if_predS, B
245incremental_live_outB, T ←
if_euseB, T
incremental_cleardet

Retract every incremental fact.

252incremental_clear ←
retractallif_pred_, _
retractallif_gen_, _
retractallif_kill_, _
retractallif_euse_, _
incremental_load+Blocksdet

Assert the incremental facts (edges, gen/kill sets, phi edge uses) of the function Blocks.

263incremental_loadBlocks ←
forall
memberblockB, _, _, Jump, Blocks
jump_succsJump, Ss
memberS, Ss
,
assertzif_predS, B
block_factsBlocks, Facts
forallmemberF, Facts, assertzF
incremental_update+Blocksdet

Make the gen, kill and phi edge-use facts those of Blocks: retract the facts no block has any more and assert the new ones. Edges (if_pred/2) are left as they are.

277incremental_updateBlocks ←
block_factsBlocks, New
findallF,
memberF, if_gen_, _, if_kill_, _, if_euse_, _
callF
,
Old0
sortOld0, Old
ord_subtractOld, New, Gone
ord_subtractNew, Old, Added
forallmemberF, Gone, retractF
forallmemberF, Added, assertzF
block_facts+Blocks, -Facts

the sorted if_gen, if_kill and if_euse facts of Blocks.

291block_factsBlocks, Facts ←
blocks_gen_killBlocks, GenKill
phi_edge_usesBlocks, EdgeUses
findallF,
memberblockB, _, _, _, Blocks
get_assocB, GenKill, Gen−Kill
memberT, Gen
F = if_genB, T
or
memberT, Kill
F = if_killB, T
,
GenKillFacts
findallif_euseB, T,
gen_assocB−_, EdgeUses, Ts
memberT, Ts
,
EdgeFacts
appendGenKillFacts, EdgeFacts, Facts0
sortFacts0, Facts