Fil-C on Nix

Filnix wiki

mbrock/filnix/nanofil

simplify.pl

A cheap middle end (docs/perf.md item 8), run after copy propagation on unchecked IR in strict SSA form: simplify_module/2 folds constants, simplifies booleans and identities, numbers pure values, folds constant branches, removes unreachable blocks and dead pure definitions, and merges straight-line blocks. Selected with --simplify=full.

What it may change

Only values and control flow that no memory access, call or trap observes. Every load, store, call, alloc, division, va_arg, cas and fence stays, in its order, with its arguments' values: a removed or rewritten definition is pure (pure_op/2: no memory, no effect, no native trap), so no check is added, removed or moved, and no trap changes its time or reason. Constant operations are evaluated by the interpreter's own alu/4, so a folded value is the one the IR's semantics gives (a division by zero is 0, as Fil-C's is since deluge); one that would be undefined (an invalid float conversion) is left alone. Pollchecks are not in unchecked IR: pizlonation adds one on every edge to a block at or before its source, so every cycle of the result still polls.

Rules

RewriteWhen
Op(c1, c2) to its valueinteger Op of w or l constants, its value defined
x + 0, x - 0, x | 0, x ^ 0, x << 0, x * 1, x & ~0 to x; x * 0, x & 0, x - x, x ^ x to 0integer classes; a shift count is read modulo the width
x * 2^k to x << k, x udiv 2^k to x >> k, x urem 2^k to x & (2^k - 1)integer classes
a constant first operand of add, mul, and, or, xor or an integer cmp secondthe backends encode an immediate second operand
b != 0, b == 1, extT of a boolean b (0 or 1) to bthe result's class has an equal boolean
extT2 of a loadT or extT value x to xevery T is a T2, and the result's class is x's
(a C b) == 0 to a C' bC an integer condition, C' its negation
branchb ≡ 0, L1, L2 to branchb, L2, L1b a boolean
a pure operation already computed in a dominating block to that valuesame class, operation and operands (commutative operands in either order)
branchc, L1, L2 to gotoc constant, or L1 = L2

Then blocks no longer reachable go, with their phi edges; copies and trivial phis go (copy.pl), up to three rounds while branches fold; pure definitions nobody uses go; a block whose only predecessor jumps to it is appended to that predecessor; and an empty block that only jumps on, whose predecessors do not already reach its successor, is bypassed, its phis moved into the successor's.

Functions that are not in strict SSA form, and functions that call setjmp, are left alone. Source contexts follow: instruction sites are re-indexed, sites of removed blocks, instructions and phis go, and the values keep their records as history.

useslists
usesassoc
usespairs
usesirmodulus/2, class_bits/2, op_kind/2, op_arg_classes/4, module_mode/2, setjmp_func/1
usescfgfunc_cfg/2, cfg_idoms/2, dom_children/3, jump_succs/2, edge_label/2, inst_uses/2, jump_uses/2
usespromotestrict_ssa/3
usescopytemp_classes/3, reps_block/4, copied_records/4, copy_reps/4
usesinterpalu/4
usesprovenancelegacy_context/2, code_items/2, context_index/2, context_records/3, with_context/3, remap_sites/3, emission_sites/7
simplify_module+M0, -Mdet

M is M0 with each strict SSA function simplified.

69simplify_moduleM0, moduleItems ←
legacy_contextM0, moduleItems0
module_modemoduleItems0, Mode
code_itemsItems0, Code
context_indexItems0, Contexts
maplistsimplify_itemMode, Contexts, Code, Groups
appendGroups, Items

The cut leaves no choicepoint per function (promote_item/4).

76simplify_itemMode, All, F0, Items ←
F0 = funcName, _, _, _, _
!
context_recordsAll, Name, Rs0
oncesimplify_contextMode, F0, Rs0, F, Rs
with_contextF, Rs, Items
79simplify_item_, _, Item, Item
simplify_context+Mode, +F0, +Records0, -F, -Recordsdet

F is the function F0 simplified as the header says, and Records its context: sites re-indexed, those of removed blocks, instructions and phis dropped, and removed values kept as history. F0 not in strict SSA form, or calling setjmp, is left alone.

87simplify_context_, F, Rs, F, Rs ←
88simplify_contextMode, F0, Rs0, F, Rs ←
ssa_cfgF0, Cfg, Idom
→
F0 = func_, _, _, Params, Blocks0
temp_classesParams, Blocks0, Classes
Cx = cxClasses, Mode
fold_rounds3, Cx, F0, Cfg, Idom, Rs0, F1, Rs1
dead_codeF1, Rs1, F2, Rs2
merge_blocksF2, Rs2, F3, Rs3, Merged
Merged ≡ true
→
trivial_phisCx, F3, Rs3, F, Rs
else
F = F3
Rs = Rs3
else
F = F0
Rs = Rs0
102ssa_cfgF, Cfg, Idom ←
F = func_, _, _, _, Blocks
func_cfgBlocks, Cfg
cfg_idomsCfg, Idom
strict_ssaF, Cfg, Idom
fold_rounds+N, +Cx, +F0, +Cfg, +Idom, +Rs0, -F, -Rs

fold, remove the replaced definitions (copy.pl's substitution) and the unreachable blocks; again, up to N rounds, while a branch folded (its successor's phis may have become constant). Each rewrite keeps strict SSA, so later rounds need not check it again.

112fold_roundsN, Cx, F0, Cfg, Idom, Rs0, F, Rs ←
F0 = funcName, L, R, Ps, Blocks0
fold_funcCx, F0, Cfg, Idom, func_, _, _, _, Blocks1, Reps, Folded
empty_assocReps
→
Blocks2 = Blocks1
Rs2 = Rs0
else
maplistreps_blockReps, Blocks1, Blocks2, Maps
appendMaps, Map
copied_recordsReps, Map, Rs0, Rs2
Cfg = cfg_, Rpo, _, _
Folded ≡ false
lengthRpo, NR
lengthBlocks0, NR
→
F3 = funcName, L, R, Ps, Blocks2
Rs3 = Rs2
else
prunefuncName, L, R, Ps, Blocks2, Rs2, F2, Rs4
trivial_phisCx, F2, Rs4, F3, Rs3
Folded ≡ true
N > 1
→
N1 is N−1
F3 = func_, _, _, _, Blocks3
func_cfgBlocks3, Cfg3
cfg_idomsCfg3, Idom3
fold_roundsN1, Cx, F3, Cfg3, Idom3, Rs3, F, Rs
else
F = F3
Rs = Rs3
trivial_phis+Cx, +F0, +Rs0, -F, -Rs

F is F0 without the phis whose operands, other than the phi itself, are one value, which replaces it. A pruned edge or a bypassed block leaves these; one pass, following replacements by other such phis (copy.pl's copy_reps/4: after pruning every phi is reachable, so strict SSA rules out cycles among them).

138trivial_phisCx, funcN, L, R, Ps, Blocks0, Rs0, funcN, L, R, Ps, Blocks, Rs ←
findallD−V,
memberblock_, Phis, _, _, Blocks0
memberphitmpD, K, Pairs, Phis
pairs_valuesPairs, Vs
exclude==tmpD, Vs, Others
sortOthers, V0
replacementCx, K, V0, V
,
Pairs0
Pairs0 ≡ []
→
Blocks = Blocks0
Rs = Rs0
else
list_to_assocPairs0, Direct
empty_assocE
foldlcopy_repsDirect, Pairs0, E, Reps
maplistreps_blockReps, Blocks0, Blocks, Maps
appendMaps, Map
copied_recordsReps, Map, Rs0, Rs

Folding: a walk of the dominator tree

The state st(Reps, Bools, Defs, Out, Folded) is threaded through the walk; Avail, the pure values computed in the dominating blocks, is passed down only, so it is scoped to the subtree.

PartIs
Repsassoc Temp -> the value that replaces it (a same-class temp or a w/l constant)
Boolsassoc Temp -> K-Temp pairs equal to it, each 0 or 1
Defsassoc Temp -> cmp(Cond, K, A, B) for an integer comparison, not(B) for B == 0 of a boolean B, or narrow(T) for a load or extension of narrow type T
Outassoc Label -> the rewritten block
Foldedtrue once a branch became a goto
165fold_funcCx, funcN, L, R, Ps, Blocks0, Cfg, Idom, funcN, L, R, Ps, Blocks, Reps, Folded ←
Cfg = cfgEntry, _, _, _
dom_childrenCfg, Idom, Kids
findallLb−B,
memberB, Blocks0
B = blockLb, _, _, _
,
Pairs
list_to_assocPairs, BlockIndex
empty_assocE
walkEntry, Kids, BlockIndex, Cx, E, stE, E, E, E, false, stReps, _, _, Out, Folded
maplistout_blockOut, Blocks0, Blocks
174out_blockOut, B0, B ←
B0 = blockL, _, _, _
get_assocL, Out, B1
→
B = B1
else
B = B0
178walkL, Kids, Index, Cx, Avail0, St0, St ←
get_assocL, Index, blockL, Phis0, Insts0, Jump0
St0 = stReps0, Bools0, Defs, Out0, F0
foldlfold_phiCx, Phis0, Phis, Reps0−Bools0, Reps1−Bools1
fold_instsInsts0, Insts, L, Cx, Avail0, Avail, stReps1, Bools1, Defs, Out0, F0, St1
St1 = stReps2, Bools2, Defs2, Out1, F1
fold_jumpJump0, Jump, Reps2, Bools2, Defs2, F1, F2
put_assocL, Out1, blockL, Phis, Insts, Jump, Out2
get_assocL, Kids, Cs
foldlwalk_kidKids, Index, Cx, Avail, Cs, stReps2, Bools2, Defs2, Out2, F2, St
189walk_kidKids, Index, Cx, Avail, L, St0, St ←
walkL, Kids, Index, Cx, Avail, St0, St

A phi whose operands, other than itself, are one value is that value; its operands from blocks not yet visited are not substituted yet, so this is conservative, and copy.pl finds the rest.

195fold_phiCx, phitmpD, K, Pairs0, phitmpD, K, Pairs, Reps0−Bools0, Reps−Bools ←
maplistsubst_pairReps0, Pairs0, Pairs
pairs_valuesPairs, Vs
exclude==tmpD, Vs, Others
sortOthers, Distinct
Distinct = V
replacementCx, K, V, R
→
put_assocD, Reps0, R, Reps
else
Reps = Reps0
Others ≢ []
maplistbooleanBools0, K, Others
→
put_assocD, Bools0, K−D, Bools
else
Bools = Bools0
208subst_pairReps, P−V0, P−V ←
substReps, V0, V
210substReps, tmpT, V ←
!
get_assocT, Reps, V0
→
V = V0
else
V = tmpT
211subst_, V, V
replacement+Cx, +K, +V, -R

every use of a temp of class K that is always V may read R instead: a temp of class K, or a w/l constant.

215replacementcxClasses, _, K, tmpS, tmpS ←
get_assocS, Classes, K
216replacement_, K, constN, constV ←
memberchkK, w, l
V is N mod M
219boolean_, _, constN ←
integerN
memberchkN, 0, 1
220booleanBools, _, tmpT ←
get_assocT, Bools, _
222fold_insts[], [], _, _, Avail, Avail, St, St
223fold_instsI0|Is0, I|Is, L, Cx, Avail0, Avail, St0, St ←
fold_instI0, I, L, Cx, Avail0, Avail1, St0, St1
fold_instsIs0, Is, L, Cx, Avail1, Avail, St1, St
227fold_instlettmpD, K, Op0, Args0, I, L, Cx, Avail0, Avail, St0, St ←
!
St0 = stReps0, Bools0, Defs0, Out, F
maplistsubstReps0, Args0, Args1
oncesimplifiedK, Op0, Args1, Cx, Bools0, Defs0, R
R = valueV
→
I = lettmpD, K, copy, V
replacementCx, K, V, Rep
→
put_assocD, Reps0, Rep, Reps
else
Reps = Reps0
booleanBools0, K, V
→
put_assocD, Bools0, K−D, Bools
else
Bools = Bools0
Avail = Avail0
Defs = Defs0
else
R = opOp, Args
numberedK, Op, Args, L, Key
get_assocKey, Avail0, E
→
I = lettmpD, K, copy, tmpE
put_assocD, Reps0, tmpE, Reps
get_assocE, Bools0, Eq
→
put_assocD, Bools0, K−D|Eq, Bools
else
Bools = Bools0
Avail = Avail0
Defs = Defs0
else
I = lettmpD, K, Op, Args
Reps = Reps0
numberedK, Op, Args, L, Key
→
put_assocKey, Avail0, D, Avail
else
Avail = Avail0
factsD, K, Op, Args, Bools0, Defs0, Bools, Defs
St = stReps, Bools, Defs, Out, F
248fold_instI0, I, _, _, Avail, Avail, St, St ←
St = stReps, _, _, _, _
inst_substI0, Reps, I
252inst_substlocF, L, _, locF, L
253inst_subststoreW, V0, A0, Reps, storeW, V, A ←
substReps, V0, V
substReps, A0, A
255inst_substatomic_storeW, V0, A0, Reps, atomic_storeW, V, A ←
substReps, V0, V
substReps, A0, A
257inst_substfence, _, fence
258inst_substaccessK, A0, S, Al, Reps, accessK, A, S, Al ←
substReps, A0, A
259inst_substasm_fixedM, Ins0, Outs, Reps, asm_fixedM, Ins, Outs ←
maplistsubst_pairReps, Ins0, Ins
261inst_substcallR, F0, Args0, Reps, callR, F, Args ←
substReps, F0, F
maplistarg_substReps, Args0, Args
264arg_substReps, argK, V0, argK, V ←
substReps, V0, V
facts+D, +K, +Op, +Args, +Bools0, +Defs0, -Bools, -Defs

what the kept definition D = Op(Args) tells later rewrites.

268factsD, K, Op, Args, Bools0, Defs0, Bools, Defs ←
bool_resultK, Op, Args, Bools0, Eq
→
put_assocD, Bools0, K−D|Eq, Bools
else
Bools = Bools0
Op = cmpeq, CK
Args = tmpB, constZ
zeroCK, Z
get_assocB, Bools0, _
→
put_assocD, Defs0, notB, Defs
Op = cmpC, CK
Args = A, B
→
put_assocD, Defs0, cmpC, CK, A, B, Defs
Op = loadT
or
Op = extT
fitsT, T
→
put_assocD, Defs0, narrowT, Defs
else
Defs = Defs0
fits?T, ?T2

every value of the narrow type T is one of T2, so extending a T value as T2 gives it back.

fits
285ubub
285ubuh
285ubuw
285ubsh
285ubsw
286uhuh
286uhuw
286uhsw
287sbsb
287sbsh
287sbsw
288shsh
288shsw
289uwuw
289swsw
bool_result+K, +Op, +Args, +Bools, -Eq

Op(Args) is 0 or 1; Eq are temps equal to it.

293bool_result_, cmp_, _, _, _, []
294bool_result_, ext_, tmpB, Bools, Eq ←
get_assocB, Bools, Eq
295bool_result_, and, _, const1, _, []
296bool_result_, Op, X, Y, Bools, [] ←
memberchkOp, and, or, xor
booleanBools, _, X
booleanBools, _, Y
300int_classw
301int_classl
303zeroK, N ←
integerN
N mod M =:= 0
304oneK, N ←
integerN
N mod M =:= 1

Simplification of one operation

simplified(+K, +Op, +Args, +Cx, +Bools, +Defs, -R): R is value(V), the operation is V read at class K (a copy), or op(Op1, Args1), an equivalent operation. Args have the replacements applied.

312simplified_, copy, globalG, _, _, _, opcopy, globalG
313simplified_, copy, V, _, _, _, valueV
314simplifiedp, Op, X, constN, _, _, _, valueX ←
memberchkOp, add, sub
zerol, N
316simplifiedK, Op, Args, Cx, _, _, valueconstV ←
maplistis_const, Args
Cx = cx_, Mode
op_arg_classesOp, K, Mode, Classes
maplistarg_number, Classes, Args, Xs
aluOp, K, Xs, valV
321simplifiedK, Op, X, Y, Cx, Bools, Defs, R ←
X = const_
Y ≠ const_
!
simplifiedK, Op, Y, X, Cx, Bools, Defs, R
324simplifiedK, cmpC, CK, X, Y, Cx, Bools, Defs, R ←
X = const_
Y ≠ const_
!
simplifiedK, cmpC1, CK, Y, X, Cx, Bools, Defs, R
328simplifiedK, Op, Args, Cx, Bools, Defs, R ←
identityOp, K, Args, Cx, Bools, Defs, R
!
330simplifiedK, cmpC, CK, Args, Cx, Bools, Defs, R ←
boolean_cmpC, CK, K, Args, Cx, Bools, Defs, R
!
332simplifiedK, ext_, tmpB, Cx, Bools, _, valuetmpE ←
get_assocB, Bools, Eq
same_classCx, K, Eq, E
334simplifiedK, extT, tmpD, cxClasses, _, _, Defs, valuetmpD ←
get_assocD, Defs, narrowT0
fitsT0, T
get_assocD, Classes, K
336simplified_, Op, Args, _, _, _, opOp, Args
338is_constconstN ←
integerN
340arg_numberK, constN, X ←
X is N mod M

Integer operations alu/4 evaluates on integers. Division is among them and is total (a zero divisor gives 0, MIN / -1 gives MIN), so it folds like any other.

345foldableOp ←
op_kindOp, Kind
memberchkKind, additive, alu, shift, neg, copy, cmp, ext
Op = cmp_, CK
→
else
true
commutativeaddmulandorxor
mirrored?C, ?C1

x C y is y C1 x.

mirrored
353eqeq
353nene
354sltsgt
354sgtslt
354slesge
354sgesle
355ultugt
355ugtult
355uleuge
355ugeule
negated?C, ?C1

not (x C y) is x C1 y, for integers.

negated
358eqne
358neeq
359sltsge
359sgeslt
359slesgt
359sgtsle
360ultuge
360ugeult
360uleugt
360ugtule
identity+Op, +K, +Args, +Cx, +Bools, +Defs, -R

algebraic identities of integer operations at class K; a constant operand is second.

364identityOp, K, X, constN, _, _, _, valueX ←
memberchkOp, add, sub, or, xor
zeroK, N
366identityOp, K, X, constN, _, _, _, valueX ←
memberchkOp, shl, shr, sar
class_bitsK, Bits
N mod Bits =:= 0
368identityOp, K, X, constN, _, _, _, valueX ←
memberchkOp, mul, udiv, div
oneK, N
370identityand, K, X, constN, _, _, _, valueX ←
N mod M =:= M−1
372identityand, _, X, const1, _, Bools, _, valueX ←
booleanBools, _, X
374identityOp, K, _, constN, _, _, _, valueconst0 ←
memberchkOp, mul, and
zeroK, N
376identityOp, _, tmpX, tmpX, _, _, _, valueconst0 ←
memberchkOp, sub, xor
378identityOp, _, tmpX, tmpX, _, _, _, valuetmpX ←
memberchkOp, and, or
380identitymul, K, X, constN, _, _, _, opshl, X, constS ←
S > 0
382identityudiv, K, X, constN, _, _, _, opshr, X, constS ←
S > 0
384identityurem, K, X, constN, _, _, _, opand, X, constMask ←
S > 0
Mask is 1 << S−1
power_of_two+K, +N, -S

N at class K is 2^S.

388power_of_twoK, N, S ←
V is N mod M
V > 0
V /\ V−1 =:= 0
log2V, 0, S
392log21, S, S ←
!
393log2V, S0, S ←
V1 is V >> 1
S1 is S0 + 1
log2V1, S1, S
boolean_cmp+C, +CK, +K, +Args, +Cx, +Bools, +Defs, -R

a comparison of a boolean with 0 or 1, or of a value with itself.

397boolean_cmpC, _, _, tmpX, tmpX, _, _, _, valueconstV ←
memberchkC, eq, ule, uge, sle, sge
→
V = 1
else
V = 0
399boolean_cmpult, CK, _, _, constN, _, _, _, valueconst0 ←
zeroCK, N
400boolean_cmpuge, CK, _, _, constN, _, _, _, valueconst1 ←
zeroCK, N
401boolean_cmpC, CK, K, tmpB, constN, Cx, Bools, Defs, R ←
get_assocB, Bools, Eq
C ≡ ne
zeroCK, N
→
Truth = same
C ≡ eq
oneCK, N
→
Truth = same
C ≡ eq
zeroCK, N
→
Truth = negated
C ≡ ne
oneCK, N
→
Truth = negated
Truth ≡ same
→
same_classCx, K, K−B|Eq, E
R = valuetmpE
member_−E, K−B|Eq
get_assocE, Defs, cmpC0, CK0, A0, B0
→
negatedC0, C1
R = opcmpC1, CK0, A0, B0
same_class+Cx, +K, +Eq, -E

E is a temp of class K among Eq.

415same_classcxClasses, _, K, Eq, E ←
member_−E, Eq
get_assocE, Classes, K
!
numbered+K, +Op, +Args, +L, -Key

Op is pure, and Key identifies its value: commutative integer operands in a fixed order. A pointer plus or minus a constant (a field's address) is numbered only within its block L: recomputing it is one add the backend folds into the access, while sharing it down the dominator tree keeps a pointer and its capability live across the function. In bzip2's BZ2_decompress that cost 20% more cycles for 4% fewer instructions (docs/perf.md, item 8).

425numberedK, Op, Args, L, K−Op−Key ←
pure_opOp, K
Op ≢ copy
→
msortArgs, Key0
else
Key0 = Args
K ≡ p
memberchkconst_, Args
→
Key = Key0−L
else
Key = Key0
removable+Inst

Inst defines a temp by a pure operation. A copy of a symbol in a Fil-C module calls its getter, which stays.

432removablelettmp_, K, Op, Args ←
pure_opOp, K
¬
Op ≡ copy
Args = global_
pure_op+Op, +K

Op reads no memory, has no effect and does not trap natively, so its definition can be removed or shared. (A float_to_int whose value is undefined is ub in the interpreter, which refinement allows to disappear; Fil-C does not trap there.)

439pure_opOp, _ ←
op_kindOp, Kind
memberchkKind, additive, shift, neg, copy, ptoi, itop, cmp, ext, float_convert, int_to_float, float_to_int, cast
!
443pure_opOp, _ ←
memberchkOp, mul, and, or, xor
446fold_jumpbranchV0, T1, T2, J, Reps, Bools, Defs, F0, F ←
!
substReps, V0, V
V = constN
→
N mod 4294967296 ≠ 0
→
J = gotoT1
else
J = gotoT2
F = true
T1 ≡ T2
→
J = gotoT1
F = true
V = tmpX
get_assocX, Defs, notB
get_assocB, Bools, _
→
J = branchtmpB, T2, T1
F = F0
else
J = branchV, T1, T2
F = F0
456fold_jumpretV0, retV, Reps, _, _, F, F ←
!
substReps, V0, V
457fold_jumpretsVs0, retsVs, Reps, _, _, F, F ←
!
maplistsubstReps, Vs0, Vs
458fold_jumpJ, J, _, _, _, F, F

Unreachable blocks

prune+F0, +Rs0, -F, -Rs

F is F0 without its unreachable blocks, and each phi without the operands of edges that no longer exist.

464prunefuncN, L, R, Ps, Blocks0, Rs0, funcN, L, R, Ps, Blocks, Rs ←
func_cfgBlocks0, cfg_, Rpo, Succs, _
findallLb−true, memberLb, Rpo, RPairs
list_to_assocRPairs, Reach
includereachableReach, Blocks0, Kept
maplistlive_phi_edgesReach, Succs, Kept, Blocks
Blocks0 ≡ Blocks
→
Rs = Rs0
else
excludereachableReach, Blocks0, Gone
findallLb, memberblockLb, _, _, _, Gone, GoneLabels
findallT,
memberblock_, Phis, _, _, Gone
memberphitmpT, _, _, Phis
,
GonePhis
drop_sitesGoneLabels, GonePhis, Rs0, Rs
476reachableReach, blockL, _, _, _ ←
get_assocL, Reach, _
478live_phi_edgesReach, Succs, blockL, Phis0, Is, J, blockL, Phis, Is, J ←
maplistlive_phiReach, Succs, L, Phis0, Phis
481live_phiReach, Succs, L, phiD, K, Pairs0, phiD, K, Pairs ←
includelive_edgeReach, Succs, L, Pairs0, Pairs
484live_edgeReach, Succs, L, P−_ ←
get_assocP, Reach, _
get_assocP, Succs, Ss
memberchkL, Ss
drop_sites+Labels, +Phis, +Rs0, -Rs

Rs is Rs0 without the sites of the blocks Labels and the phis Phis.

489drop_sitesLabels, Phis, Rs0, Rs ←
findallL−true, memberL, Labels, LP
list_to_assocLP, LA
findallT−true, memberT, Phis, TP
list_to_assocTP, TA
excludedropped_siteLA, TA, Rs0, Rs
494dropped_siteLA, _, siteinstL, _, _ ←
get_assocL, LA, _
495dropped_siteLA, _, sitejumpL, _ ←
get_assocL, LA, _
496dropped_site_, TA, sitephiT, _ ←
get_assocT, TA, _

Dead pure definitions

dead_code+F0, +Rs0, -F, -Rs

F is F0 without the pure definitions and phis whose values nothing that stays uses, transitively.

502dead_codefuncN, L, R, Ps, Blocks0, Rs0, funcN, L, R, Ps, Blocks, Rs ←
findallD−Us,
memberblock_, Phis, Insts, _, Blocks0
memberphitmpD, _, Pairs, Phis
pairs_valuesPairs, Vs
temps_ofVs, Us
or
memberI, Insts
I = lettmpD, _, _, _
,
PureUses
list_to_assocPureUses, Pure
findallU,
memberblock_, _, Insts, J, Blocks0
memberI, Insts
¬pure_defPure, I
or
memberU, Us
,
Roots
empty_assocE
markRoots, Pure, E, Live
forallgen_assocD, Pure, _, get_assocD, Live, _
→
Blocks = Blocks0
Rs = Rs0
else
maplistlive_blockPure, Live, Blocks0, Blocks, Maps
appendMaps, Map
remap_sitesRs0, Map, Rs1
findallT,
memberblock_, Phis, _, _, Blocks0
memberphitmpT, _, _, Phis
¬get_assocT, Live, _
,
DeadPhis
drop_sites[], DeadPhis, Rs1, Rs
531pure_defPure, lettmpD, _, _, _ ←
get_assocD, Pure, _
533temps_ofVs, Ts ←
findallT, membertmpT, Vs, Ts0
sortTs0, Ts
535mark[], _, Live, Live
536markT|Ts, Pure, Live0, Live ←
get_assocT, Live0, _
→
markTs, Pure, Live0, Live
else
put_assocT, Live0, true, Live1
get_assocT, Pure, Us
→
appendUs, Ts, Ts1
else
Ts1 = Ts
markTs1, Pure, Live1, Live
544live_blockPure, Live, blockL, Phis0, Insts0, J, blockL, Phis, Insts, J, Map ←
excludedead_phiLive, Phis0, Phis
live_instsInsts0, Pure, Live, Insts, L, 0, 0, Map
548dead_phiLive, phitmpD, _, _ ←
¬get_assocD, Live, _
550live_insts[], _, _, [], _, _, _, []
551live_instsI|Is0, Pure, Live, Is, L, Old, New, Map ←
I = lettmpD, _, _, _
get_assocD, Pure, _
¬get_assocD, Live, _
→
Is = Rest
Out = []
else
Is = I|Rest
Out = I
emission_sitesL, Old, New, Out, Map, Maps, Next
Old1 is Old + 1
live_instsIs0, Pure, Live, Rest, L, Old1, Next, Maps

Merging blocks

Both rewrites below run over the blocks in order once, with Index (assoc Label -> block) and Preds (assoc Label -> the labels of its incoming edges, one per jump target) kept current.

merge_blocks+F0, +Rs0, -F, -Rs, -Merged

F is F0 with each empty block that only jumps on bypassed, then each block whose only predecessor ends in a goto to it appended to that predecessor. Merged is true if a block went.

569merge_blocksfuncN, L, R, Ps, Blocks0, Rs0, funcN, L, R, Ps, Blocks, Rs, Merged ←
Blocks0 = blockEntry, _, _, _|_
findallLb−B,
memberB, Blocks0
B = blockLb, _, _, _
,
Pairs
list_to_assocPairs, Index0
pairs_keysPairs, Order
edge_predsBlocks0, Preds0
use_countsBlocks0, Uses0
empty_assocE
foldlbypassEntry, Order, mIndex0, Preds0, Uses0, [], [], mIndex1, Preds1, _, Bypassed, DeadPhis
foldlabsorbEntry, Order, aIndex1, Preds1, E, E, aIndex, _, Moves, JumpOf
Bypassed ≡ []
empty_assocMoves
→
Blocks = Blocks0
Rs = Rs0
Merged = false
else
findallB,
memberLb, Order
get_assocLb, Index, B
,
Blocks
merged_recordsMoves, JumpOf, Bypassed, DeadPhis, Rs0, Rs
Merged = true
586edge_predsBlocks, Preds ←
findallT−From,
memberblockFrom, _, _, J, Blocks
memberT0, Ts
,
Pairs0
keysortPairs0, Pairs
group_pairs_by_keyPairs, Groups
list_to_assocGroups, Preds
jump_targets
594gotoTT
595branch_, T1, T2T1, T2
596ret_[]
597ret[]
598rets_[]
599unreachable[]
601preds_ofPreds, L, Ps ←
get_assocL, Preds, Ps0
→
Ps = Ps0
else
Ps = []
603atom_labelT ←
T ≠ poll_
use_counts+Blocks, -Uses

assoc Temp -> how many operands read it.

606use_countsBlocks, Uses ←
findallT,
memberblock_, Phis, Insts, J, Blocks
memberphi_, _, Pairs, Phis
member_−tmpT, Pairs
or
memberI, Insts
or
,
Ts0
msortTs0, Ts
clumped_pairsTs, Counts
list_to_assocCounts, Uses
616clumped_pairs[], []
617clumped_pairsT|Ts, T−N|Ps ←
same_runT, Ts, 1, N, Rest
618same_runT, U|Us, N0, N, Rest ←
U ≡ T
!
N1 is N0 + 1
same_runT, Us, N1, N, Rest
619same_run_, Rest, N, N, Rest
621operand_templet_, _, _, Args, T ←
membertmpT, Args
622operand_tempstore_, V, A, T ←
membertmpT, V, A
623operand_tempatomic_store_, V, A, T ←
membertmpT, V, A
624operand_tempasm_fixed_, Ins, _, T ←
member_−tmpT, Ins
625operand_tempaccess_, tmpT, _, _, T
626operand_tempcall_, F, Args, T ←
F = tmpT
or
memberarg_, tmpT, Args
628jump_operandbranchtmpT, _, _, T
629jump_operandrettmpT, T
630jump_operandretsVs, T ←
membertmpT, Vs
632countUses, T, N ←
get_assocT, Uses, N0
→
N = N0
else
N = 0
634add_useD, tmpT, Uses0, Uses ←
!
countUses0, T, N0
N is N0 + D
put_assocT, Uses0, N, Uses
635add_use_, _, Uses, Uses
bypass+Entry, +E, +M0, -M

M is m(Index, Preds, Uses, Bypassed, DeadPhis). E, empty, not the entry, ending in goto(T), goes when each predecessor P reaches it once, by a plain edge, and does not reach T already, and E's phis are read only by T's operands from E. Each P then jumps to T, and T's operand from E becomes, for each P, that operand with E's phis read as their operands from P: a value read there was defined in a block dominating E, so it dominates each P.

644bypassEntry, E, mIndex0, Preds0, Uses0, Gone0, Dead0, M ←
get_assocE, Index0, blockE, EPhis, [], gotoT
E ≢ Entry
T ≢ E
preds_ofPreds0, E, EPreds
EPreds ≢ []
sortEPreds, Distinct
lengthEPreds, N
lengthDistinct, N
¬memberchkE, EPreds
preds_ofPreds0, T, TPreds
¬
memberP, EPreds
memberchkP, TPreds
forallmemberP, EPreds, plain_edgeIndex0, P, E
get_assocT, Index0, blockT, TPhis0, TInsts, TJump
small_joinIndex0, Preds0, T
forallmemberphitmpD, _, _, EPhis,
countUses0, D, ND
forwarded_readsTPhis0, E, D, ND
→
findallD−Pairs, memberphitmpD, _, Pairs, EPhis, PhiPairs
list_to_assocPhiPairs, EPhiA
foldlbypassed_phiE, EPreds, EPhiA, TPhis0, TPhis, Uses0, Uses1
foldldropped_phi_uses, EPhis, Uses1, Uses
put_assocT, Index0, blockT, TPhis, TInsts, TJump, Index1
foldlretargetE, T, EPreds, Index1, Index2
del_assocE, Index2, _, Index
exclude==E, TPreds, TPreds1
appendTPreds1, EPreds, TPreds2
put_assocT, Preds0, TPreds2, Preds1
del_assocE, Preds1, _, Preds
findallD, memberphitmpD, _, _, EPhis, Ds
appendDs, Dead0, Dead
M = mIndex, Preds, Uses, E|Gone0, Dead
else
M = mIndex0, Preds0, Uses0, Gone0, Dead0
small_join+Index, +Preds, +S

S has no phis or at most 64 incoming edges. Rewriting a join's phis is linear in its edges, so each block merged into a dispatch loop's header would otherwise cost the whole header, quadratic over its cases.

675small_joinIndex, Preds, S ←
get_assocS, Index, blockS, Phis, _, _
Phis ≡ []
→
true
else
preds_ofPreds, S, Ps
lengthPs, N
N ≤ 64
681plain_edgeIndex, P, E ←
get_assocP, Index, blockP, _, _, J
memberchkE, Ts
¬memberchkpollE, Ts
forwarded_reads+TPhis, +E, +D, +N

T's phis read tmp(D) N times, all as operands from E.

687forwarded_readsTPhis, E, D, N ←
findallx,
memberphi_, _, Pairs, TPhis
memberE−tmpD, Pairs
,
Xs
lengthXs, N
691bypassed_phiE, EPreds, EPhiA, phiD, K, Pairs0, phiD, K, Pairs, Uses0, Uses ←
selectE−V, Pairs0, Rest
→
findallP−VP,
memberP, EPreds
forwardedEPhiA, P, V, VP
,
New
appendRest, New, Pairs
add_use-1, V, Uses0, Uses1
pairs_valuesNew, NewVs
foldladd_use1, NewVs, Uses1, Uses
else
Pairs = Pairs0
Uses = Uses0
701forwardedEPhiA, P, tmpX, V ←
get_assocX, EPhiA, Pairs
!
memberchkP−V, Pairs
702forwarded_, _, V, V
704dropped_phi_usesphi_, _, Pairs, Uses0, Uses ←
pairs_valuesPairs, Vs
foldladd_use-1, Vs, Uses0, Uses
707retargetE, T, P, Index0, Index ←
get_assocP, Index0, blockP, Phis, Insts, J0
retargetedJ0, E, T, J
put_assocP, Index0, blockP, Phis, Insts, J, Index
712retargetedgotoE, E, T, gotoT ←
!
713retargetedbranchV, T1, T2, E, T, branchV, U1, U2 ←
!
T1 ≡ E
→
U1 = T
else
U1 = T1
T2 ≡ E
→
U2 = T
else
U2 = T2
716retargetedJ, _, _, J
absorb+Entry, +B, +A0, -A

A is a(Index, Preds, Moves, JumpOf). While B ends in goto(T), T is not the entry, has no phis and B is its only predecessor, T's instructions and jump follow B's. Moves: T -> B-Offset, T's instructions now in B after Offset others; JumpOf: B -> the label whose jump B now ends with.

723absorbEntry, B, aIndex0, Preds0, Moves0, JumpOf0, A ←
get_assocB, Index0, blockB, Phis, Insts, gotoT
T ≢ B
T ≢ Entry
preds_ofPreds0, T, B
get_assocT, Index0, blockT, [], TInsts, TJump
jump_targetsTJump, Ts0
findallS,
memberS0, Ts0
,
Ss0
sortSs0, Ss
forallmemberS, Ss, small_joinIndex0, Preds0, S
→
lengthInsts, Off
appendInsts, TInsts, Insts1
del_assocT, Index0, _, Index1
put_assocB, Index1, blockB, Phis, Insts1, TJump, Index2
foldlrenamed_predT, B, Ss, Index2−Preds0, Index−Preds1
del_assocT, Preds1, _, Preds
put_assocT, Moves0, B−Off, Moves
get_assocT, JumpOf0, J
→
del_assocT, JumpOf0, J, JumpOf1
else
J = T
JumpOf1 = JumpOf0
put_assocB, JumpOf1, J, JumpOf
absorbEntry, B, aIndex, Preds, Moves, JumpOf, A
else
A = aIndex0, Preds0, Moves0, JumpOf0
renamed_pred+T, +B, +S, +State0, -State

S's edges from T come from B.

746renamed_predT, B, S, Index0−Preds0, Index−Preds ←
get_assocS, Index0, blockS, Phis0, Is, J
maplistrenamed_phiT, B, Phis0, Phis
put_assocS, Index0, blockS, Phis, Is, J, Index
preds_ofPreds0, S, Ps0
maplistrenamed_labelT, B, Ps0, Ps
put_assocS, Preds0, Ps, Preds
754renamed_phiT, B, phiD, K, Pairs0, phiD, K, Pairs ←
maplistrenamed_pairT, B, Pairs0, Pairs
757renamed_pairT, B, P0−V, P−V ←
renamed_labelT, B, P0, P
759renamed_labelT, B, L0, L ←
L0 ≡ T
→
L = B
else
L = L0
merged_records+Moves, +JumpOf, +Bypassed, +DeadPhis, +Rs0, -Rs

an appended block's instruction sites follow it, a holder's jump site is that of the block whose jump it now has, and the sites of bypassed blocks and their phis go.

765merged_recordsMoves, JumpOf, Bypassed, DeadPhis, Rs0, Rs ←
findallS−Os, membersitejumpS, Os, Rs0, JP0
sortJP0, JP
list_to_assocJP, JumpSites
findallsitejumpH, Os,
gen_assocH, JumpOf, S
get_assocS, JumpSites, Os
,
NewJumps
findallL−true, memberL, Bypassed, BP
list_to_assocBP, BA
findallT−true, memberT, DeadPhis, DP
list_to_assocDP, DA
merged_records_Rs0, Moves, JumpOf, BA, DA, Kept
appendKept, NewJumps, Rs
775merged_records_[], _, _, _, _, []
776merged_records_R0|Rs0, Moves, JumpOf, BA, DA, Rs ←
merged_recordMoves, JumpOf, BA, DA, R0, R
→
Rs = R|Rs1
else
Rs = Rs1
merged_records_Rs0, Moves, JumpOf, BA, DA, Rs1
780merged_recordMoves, _, _, _, siteinstL, I, Os, siteinstH, I1, Os ←
!
holderMoves, L, H, Off
I1 is I + Off
782merged_recordMoves, JumpOf, BA, _, sitejumpL, Os, sitejumpL, Os ←
!
¬get_assocL, Moves, _
¬get_assocL, JumpOf, _
¬get_assocL, BA, _
784merged_record_, _, _, DA, sitephiT, Os, sitephiT, Os ←
!
¬get_assocT, DA, _
786merged_record_, _, _, _, R, R
holder+Moves, +L, -H, -Off

L's instructions are in block H from instruction Off on.

790holderMoves, L, H, Off ←
get_assocL, Moves, B−Off0
→
holderMoves, B, H, Off1
Off is Off0 + Off1
else
H = L
Off = 0