Fil-C on Nix

Filnix wiki

mbrock/filnix/nanofil

copy.pl

Copy propagation, run after promote: copy_module/2 removes copies and trivial phis from each function in strict SSA form, replacing every use by the value it copies.

What disappears

  • A copy of a temporary of the same class.
  • A copy of an integer constant at class w or l, reduced modulo 2^32 or 2^64.
  • Then each trivial phi: one whose operands, ignoring its own temporary, all name one value.

Everything else stays. An l to w copy narrows, and a p copy of a symbol or literal must still materialize a pointer, keeping the module's ABI and mode. Functions that are not in strict SSA form, and functions that call setjmp, are left alone.

How

The eligible copies are indexed, then each chain is resolved once, memoizing every link on the way back, so source order need not be dependency order. The copy work is O((definitions + uses) log definitions), not repeated whole-function scans or one chain walk per use. Trivial phis are found by a worklist that revisits only the phis that read a simplified one.

Dead code

strict_ssa/3 checks dominance only in reachable blocks, so:

  • Dead definitions are never removed: they may contain cycles or uses before definitions. Only copies in reachable blocks are eligible, and only phis in reachable blocks are simplified.
  • Dead uses of removed live copies are rewritten, including phi edges, so every temporary stays defined.
  • Phi simplification counts dead incoming edges too, and leaves all-self phis and nontrivial cycles alone.
useslists
usesassoc
usesordsets
usespairs
usesir
usescfg
usespromotestrict_ssa/3
copy_module+M0, -Mdet

M is the module M0 with copies and trivial phis propagated away in each strict SSA function.

56copy_moduleM0, moduleItems ←
legacy_contextM0, moduleItems0
code_itemsItems0, Code
context_indexItems0, Contexts
maplistcopy_itemContexts, Code, Groups
appendGroups, Items
copy_item+Item0, -Item

Item is the module item Item0, with copies propagated if it is a function. Reps (here and below): assoc Temp -> the value that replaces it. The cut leaves no choicepoint per function (promote_item/4).

65copy_itemAll, F0, Items ←
F0 = funcName, _, _, _, _
!
context_recordsAll, Name, Rs0
copy_contextF0, Rs0, F, Rs
with_contextF, Rs, Items
68copy_item_, dataN, L, A, Is, dataN, L, A, Is ←
!
69copy_item_, aliasN, L, T, aliasN, L, T
copy_context+F0, +Records0, -F, -Recordsdet

F is the function F0 with its copies and trivial phis removed, as copy_module/2 removes them, and Records its context Records0 with each removed value recorded as an alias of its survivor and the sites re-indexed. F0 not in strict SSA form, or calling setjmp, is left alone.

78copy_contextF, Rs, F, Rs ←
79copy_contextfuncName, Linkage, Ret, Params, Blocks0, Rs0, F, Rs ←
F0 = funcName, Linkage, Ret, Params, Blocks0
func_cfgBlocks0, Cfg
cfg_idomsCfg, Idom
strict_ssaF0, Cfg, Idom
→
temp_classesParams, Blocks0, Classes
Cfg = cfg_, Rpo, _, _
findallL−true, memberL, Rpo, ReachPairs
list_to_assocReachPairs, Reach
findallD−V,
memberblockL, _, Insts, _, Blocks0
get_assocL, Reach, _
memberlettmpD, K, copy, Src, Insts
copy_valueSrc, K, Classes, V
,
CopyPairs
list_to_assocCopyPairs, Copies
empty_assocEmpty
foldlcopy_repsCopies, CopyPairs, Empty, Reps
maplistreps_blockReps, Blocks0, Copied, Maps
appendMaps, Map
copied_recordsReps, Map, Rs0, Rs1
trivial_phisCopied, Classes, Reach, Blocks, PhiReps, PhiMap
copied_recordsPhiReps, PhiMap, Rs1, Rs
F = funcName, Linkage, Ret, Params, Blocks
else
F = F0
Rs = Rs0
copied_records+Reps, +Map, +Records0, -Recordsdet

Records are Records0 after the substitution Reps, with sites moved by Map: removed phis lose their sites, and each alias's value is substituted too. A substitution is history, not a new primary name for its survivor.

111copied_recordsReps, Map, Rs0, Rs ←
remap_sitesRs0, Map, Rs1
excluderemoved_phi_siteReps, Rs1, Rs2
maplistalias_repsReps, Rs2, Rs3
findallaliasT, V, Hint, Os,
membervalueT, Hint, Os, Rs0
get_assocT, Reps, V
,
Aliases
appendRs3, Aliases, Rs
119removed_phi_siteReps, sitephiT, _ ←
get_assocT, Reps, _
120alias_reps_, aliasT, historicalId, H, Os, aliasT, historicalId, H, Os ←
!
121alias_repsReps, aliasT, V0, H, Os, aliasT, V, H, Os ←
!
value_repsV0, Reps, V
123alias_reps_, R, R
temp_classes+Params, +Blocks, -Classesdet

Assoc Temp -> class of its definition.

128temp_classesParams, Blocks, Classes ←
findallT−K, memberparamK, tmpT, Params, ParamPairs
findallT−K,
memberblock_, Phis, Insts, _, Blocks
memberphitmpT, K, _, Phis
or
memberlettmpT, K, _, _, Insts
or
membercallresulttmpT, K, _, _, Insts
or
membercalltupleTos, _, _, Insts
memberresulttmpT, K, Tos
or
memberasm_fixed_, _, Outs, Insts
member_−tmpT, Outs
K = w
,
BlockPairs
appendParamPairs, BlockPairs, Pairs
list_to_assocPairs, Classes
copy_value+Src, +K, +Classes, -V

a copy at class K of Src can be replaced by V (a same-class temp, or a w/l constant reduced mod 2^bits).

146copy_valuetmpS, K, Classes, tmpS ←
get_assocS, Classes, K
147copy_valueconstN, K, _, constV ←
memberchkK, w, l
V is N mod M
copy_reps+Copies, +D−_, +Reps0, -Repsdet

Reps is Reps0 with the value that replaces D, and every link on the way to it, added. Strict dominance rules out cycles among the live copy definitions. copy_chain/6 is tail recursive even when a chain has thousands of links.

158copy_repsCopies, D−_, Reps0, Reps ←
copy_chaintmpD, Copies, Reps0, [], Path, V
foldlvalue_temp_repsV, Path, Reps0, Reps
copy_chain+V0, +Copies, +Reps, +Path0, -Path, -V

following copies (and the values already in Reps) from V0 ends at V, a value that is not one; Path is the temps on the way that are not in Reps, in front of Path0. Copies and Reps hold temps and constants only.

166copy_chaintmpT, Copies, Reps, Path0, Path, V ←
get_assocT, Reps, V
→
Path = Path0
get_assocT, Copies, Src
→
copy_chainSrc, Copies, Reps, T|Path0, Path, V
else
V = tmpT
Path = Path0
174copy_chainconstN, _, _, Path, Path, constN
176value_temp_repsV, T, Reps0, Reps ←
put_assocT, Reps0, V, Reps

Copy propagation exposes invariant phis. Index their users so that a simplification revisits only dependent phis, not the whole function. Redirect those dependencies too: if A becomes B and B later becomes C, every remaining phi that used A must see C.

183trivial_phisBlocks0, Classes, Reach, Blocks, Reps, Map ←
findallD−Vs,
memberblockL, PhiList, _, _, Blocks0
get_assocL, Reach, _
memberphitmpD, K, Args, PhiList
maplistphi_valueK, Classes, Args, Vs
,
PhiPairs
list_to_assocPhiPairs, Phis
findallS−D,
memberD−Vs, PhiPairs
membertmpS, Vs
,
Edges0
sortEdges0, Edges
group_pairs_by_keyEdges, UserPairs
list_to_assocUserPairs, Users
assoc_to_keysPhis, Pending
empty_assocEmpty
phi_worklist_repsPending, Phis, Users, Empty, Aliases
assoc_to_listAliases, AliasPairs
foldlcopy_repsAliases, AliasPairs, Empty, Reps
maplistreps_blockReps, Blocks0, Blocks, Maps
appendMaps, Map
202phi_valueK, Classes, _−Src, V ←
copy_valueSrc, K, Classes, V
phi_worklist_reps+Pending, +Phis0, +Users0, +Reps0, -Reps

Reps is Reps0 with the value of each trivial phi, as found by a worklist over the phi temps Pending. Phis0: assoc D -> operand values, for the phis not yet simplified; Users0: assoc S -> ordset of the phis that read tmp(S). Invariant: no phi left in Phis0 reads the temp of a phi already simplified, and each one that may have become trivial is in Pending.

211phi_worklist_reps[], _, _, Reps, Reps
212phi_worklist_repsD|Pending0, Phis0, Users0, Reps0, Reps ←
get_assocD, Phis0, Vs
exclude=tmpD, Vs, Other
sortOther, V
→
del_assocD, Phis0, _, Phis1
put_assocD, Reps0, V, Reps1
get_assocD, Users0, DUsers
→
true
else
DUsers = []
includeis_phiPhis1, DUsers, Live
foldlphi_redirectedD, V, Live, Phis1, Phis
V = tmpS
get_assocS, Phis, _
→
get_assocS, Users0, Old
→
true
else
Old = []
ord_unionOld, Live, New
put_assocS, Users0, New, Users
else
Users = Users0
ord_unionPending0, Live, Pending
phi_worklist_repsPending, Phis, Users, Reps1, Reps
else
phi_worklist_repsPending0, Phis0, Users0, Reps0, Reps
232is_phiPhis, D ←
get_assocD, Phis, _
phi_redirected+D, +V, +U, +Phis0, -Phis

in Phis, phi U reads V wherever it reads tmp(D) in Phis0.

237phi_redirectedD, V, U, Phis0, Phis ←
get_assocU, Phis0, Vs0
maplistvalue_redirectedD, V, Vs0, Vs
put_assocU, Phis0, Vs, Phis
242value_redirectedD, V, V0, R ←
V0 ≡ tmpD
→
R = V
else
R = V0

Substitution

reps_block+Reps, +Block0, -Block, -Mapdet

Block is Block0 without the phis and copies whose temp is in Reps, and with every use of such a temp replaced by its value. Map pairs each kept instruction's old site with its new one (remap_sites/3).

255reps_blockReps, blockL, Phis0, Insts0, Jump0, blockL, Phis, Insts, Jump, Map ←
phis_repsPhis0, Reps, Phis
insts_repsInsts0, Reps, Insts, L, 0, 0, Map
jump_repsJump0, Reps, Jump
261phis_reps[], _, []
262phis_repsP0|Ps0, Reps, Ps ←
P0 = phitmpD, _, _
get_assocD, Reps, _
→
Ps = Rest
else
Ps = P|Rest
reps_phiReps, P0, P
phis_repsPs0, Reps, Rest
270reps_phiReps, phiD, K, Args0, phiD, K, Args ←
maplistreps_edgeReps, Args0, Args
273reps_edgeReps, L−V0, L−V ←
value_repsV0, Reps, V
276insts_reps[], _, [], _, _, _, []
277insts_repsI0|Is0, Reps, Is, L, Old, New, Map ←
I0 = lettmpD, _, copy, _
get_assocD, Reps, _
→
Is = Rest
Out = []
else
Is = I|Rest
inst_repsI0, Reps, I
Out = I
emission_sitesL, Old, New, Out, Map, Maps, Next
Old1 is Old + 1
insts_repsIs0, Reps, Rest, L, Old1, Next, Maps
286inst_repslocF, L, _, locF, L
287inst_repsletD, K, Op, Vs0, Reps, letD, K, Op, Vs ←
maplistreps_valueReps, Vs0, Vs
289inst_repsstoreW, V0, A0, Reps, storeW, V, A ←
value_repsV0, Reps, V
value_repsA0, Reps, A
292inst_repsatomic_storeW, V0, A0, Reps, atomic_storeW, V, A ←
value_repsV0, Reps, V
value_repsA0, Reps, A
295inst_repsfence, _, fence
296inst_repsaccessK, A0, S, Al, Reps, accessK, A, S, Al ←
value_repsA0, Reps, A
297inst_repsasm_fixedM, Ins0, Outs, Reps, asm_fixedM, Ins, Outs ←
maplistreps_inReps, Ins0, Ins
299inst_repscallR, F0, Args0, Reps, callR, F, Args ←
value_repsF0, Reps, F
maplistreps_argReps, Args0, Args
303reps_inReps, R−V0, R−V ←
value_repsV0, Reps, V
305reps_argReps, argK, V0, argK, V ←
value_repsV0, Reps, V
308jump_repsgotoL, _, gotoL
309jump_repsbranchV0, T, F, Reps, branchV, T, F ←
value_repsV0, Reps, V
311jump_repsretV0, Reps, retV ←
value_repsV0, Reps, V
313jump_repsret, _, ret
314jump_repsretsVs0, Reps, retsVs ←
maplistreps_valueReps, Vs0, Vs
316jump_repsunreachable, _, unreachable
318reps_valueReps, V0, V ←
value_repsV0, Reps, V
value_reps+V0, +Reps, -V

V is the replacement of V0 in Reps, or V0 itself.

323value_repstmpT, Reps, V ←
get_assocT, Reps, V1
→
V = V1
else
V = tmpT
328value_repsconstN, _, constN
329value_repsglobalG, _, globalG