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.
modulecopy
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.

55copy_moduleM0, moduleItems ←
legacy_contextM0, moduleItems0
code_itemsItems0, Code
maplistcopy_itemItems0, 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.

62copy_itemAll, F0, Items ←
F0 = funcName, _, _, _, _
context_recordsAll, Name, Rs0
copy_contextF0, Rs0, F, Rs
with_contextF, Rs, Items
65copy_item_, dataN, L, A, Is, dataN, L, A, Is
67copy_contextF, Rs, F, Rs ←
68copy_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

A substitution is history, not a new primary name for its survivor.

95copied_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
103removed_phi_siteReps, sitephiT, _ ←
get_assocT, Reps, _
104alias_reps_, aliasT, historicalId, H, Os, aliasT, historicalId, H, Os ←
!
105alias_repsReps, aliasT, V0, H, Os, aliasT, V, H, Os ←
!
value_repsV0, Reps, V
107alias_reps_, R, R
temp_classes+Params, +Blocks, -Classes

assoc Temp -> class of its definition.

111temp_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
,
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).

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

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.

138copy_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.

146copy_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
154copy_chainconstN, _, _, Path, Path, constN
156value_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.

163trivial_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
182phi_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.

191phi_worklist_reps[], _, _, Reps, Reps
192phi_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
212is_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.

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

Substitution

reps_block+Reps, +Block0, -Block

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.

233reps_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
239phis_reps[], _, []
240phis_repsP0|Ps0, Reps, Ps ←
P0 = phitmpD, _, _
get_assocD, Reps, _
→
Ps = Rest
else
Ps = P|Rest
reps_phiReps, P0, P
phis_repsPs0, Reps, Rest
248reps_phiReps, phiD, K, Args0, phiD, K, Args ←
maplistreps_edgeReps, Args0, Args
251reps_edgeReps, L−V0, L−V ←
value_repsV0, Reps, V
254insts_reps[], _, [], _, _, _, []
255insts_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
264inst_repslocF, L, _, locF, L
265inst_repsletD, K, Op, Vs0, Reps, letD, K, Op, Vs ←
maplistreps_valueReps, Vs0, Vs
267inst_repsstoreW, V0, A0, Reps, storeW, V, A ←
value_repsV0, Reps, V
value_repsA0, Reps, A
270inst_repscallR, F0, Args0, Reps, callR, F, Args ←
value_repsF0, Reps, F
maplistreps_argReps, Args0, Args
274reps_argReps, argK, V0, argK, V ←
value_repsV0, Reps, V
277jump_repsgotoL, _, gotoL
278jump_repsbranchV0, T, F, Reps, branchV, T, F ←
value_repsV0, Reps, V
280jump_repsretV0, Reps, retV ←
value_repsV0, Reps, V
282jump_repsret, _, ret
283jump_repsretsVs0, Reps, retsVs ←
maplistreps_valueReps, Vs0, Vs
285jump_repsunreachable, _, unreachable
287reps_valueReps, V0, V ←
value_repsV0, Reps, V
value_reps+V0, +Reps, -V

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

292value_repstmpT, Reps, V ←
get_assocT, Reps, V1
→
V = V1
else
V = tmpT
297value_repsconstN, _, constN
298value_repsglobalG, _, globalG