Fil-C on Nix

Filnix wiki

mbrock/filnix/nanofil

inline.pl

Inlining of static functions on unchecked IR before promotion: --inline=small (docs/perf.md item 4) takes small leaves in two rounds; --inline=wide (item 20) goes callees first through the call graph and takes non-leaf callees, callees with one call site, and always_inline ones.

A call callRes, globalF, Args to a function F of the module is replaced by F's blocks when F is eligible and so is the caller. The calling block is split at the call: its first part copies the arguments into F's renamed parameters and jumps to F's renamed entry; each retV of F jumps to a join block, whose phi takes V, and the join block holds the rest of the calling block and its jump. F's blocks are placed right after the first part, so edges into and out of the body are forward and pizlonation, which polls by block order, polls exactly the cycles it polled before. Every allocation stays where it was, a fresh object at each execution; promotion takes the ones that do not escape (promote.pl promotes in any block).

Eligibility

Callee FWhy
local linkagean exported definition can be interposed
no call to a function of the module (a leaf)no recursion; two rounds give depth 2
no va_args, no setjmp, no tuple returnits own entry's argument snapshot, returns twice, two words
a ret, strict SSAthe join needs a predecessor; promotion needs strict SSA
every alloc of constant size at most 256, promotable or escaping (filc), promotable (plain)a local one that could not promote would become a heap object per call; a plain stack alloc would outlive the call
at most 120 instructions and jumpssize
CallerWhy
no setjmpreference selection, temporaries live across returns-twice
in no cycle of direct callsrecursion depth before stack overflow is unchanged
at most 2000 instructions, growing by at most half its size or 120, whichever is moreframe size and compile time

Wide

Each function, callees first, takes its admitted calls; then, with simplified callees, it is promoted, copy-propagated and simplified, and that body is what its callers inline (so depth is unbounded and sizes are the prepared ones). A callee is local, on no cycle of direct calls, not noinline, and inlinable/3 (as small, but calls of the module's functions, tuple returns and constant allocs of any size are fine). Its class decides admitted/5:

ClassAdmitted into
always_inline (c_lower c_module/3)any caller on no cycle and without setjmp, up to 4000 instructions
single: one direct call site, no other referencea caller of at most 2000 instructions before inlining that stays within 2000, up to 500
plainas small: at most 120, within the caller's budget and the 2000 cap

Afterwards a local function that no exported or weak function, data object or alias reaches is dropped, with its context: nothing can observe it, and a single-site callee's body would otherwise be emitted twice.

Function objects, getters and entries of inlined functions stay while anything refers to them, so their addresses are unchanged. Names are fresh in the caller: iR_N.X for round R and instance N, with N raised past any collision. Source contexts follow: callee records are renamed (origin and binding identities wrapped as inlR, N, Id), and the split block's sites are re-indexed.

useslists
usesassoc
usesordsets
usesirsetjmp_func/1, module_mode/2
usescfgfunc_cfg/2, cfg_idoms/2, inst_def/2
usespromotepromotable/3, promotable/4, strict_ssa/3, promote_context/5
usescopycopy_context/4
usessimplifysimplify_context/5
usesprovenancelegacy_context/2, code_items/2, context_index/2, context_records/3, with_context/3
77callee_limit120
78caller_limit2000
79single_limit500
80always_limit4000
81grown_limit2000
inline_module+M0, -Mdet
inline_module+Callees, +M0, -Mdet

M is M0 with two rounds of inlining of eligible calls. With Callees simplified, the functions that may be callees of the second round are first promoted, copy-propagated and simplified (prepared/4), as the pipeline does later, so their size and body are what they become. With Callees widePrep, Attributes, M is M0 with wide inlining (the header's Wide), Prep plain or simplified as above.

92inline_moduleM0, M ←
inline_moduleplain, M0, M
93inline_modulewidePrep, Attributes, M0, M ←
!
wide_modulePrep, Attributes, M0, M
95inline_moduleCallees, M0, M ←
M1 = moduleItems1
findallN−S,
memberfuncN, _, _, _, Bs, Items1
,
Sizes0
list_to_assocSizes0, Sizes
inline_round1, Sizes, M1, M2
preparedCallees, M2, M3
inline_round2, Sizes, M3, M

Wide inlining (--inline=wide)

inline_module(wide(Prep, Attributes), M0, M): each function, callees before callers, takes its admitted calls (admitted/5), and the result, prepared as prepared/3 prepares callees when Prep is simplified, is what its own callers inline. Functions on a cycle of direct calls take none and give none. Attributes are Sym-A pairs, A always_inline or noinline (c_lower c_module/3). Local functions that nothing refers to afterwards leave the module.

115wide_modulePrep, Attributes, M0, M ←
M1 = moduleItems1
module_modeM1, Mode
code_itemsItems1, Code
context_indexItems1, Contexts
findallN, memberfuncN, _, _, _, _, Code, Ns
name_setNs, Defined
cyclic_functionsCode, Defined, Cyclic0
name_setCyclic0, Cyclic
findallS−A, memberS−A, Attributes, Pairs0
sort1, @<, Pairs0, Pairs
ord_list_to_assocPairs, Attrs
maplist/3 shares the functions; findall/3 would copy them.
includeis_func, Code, Fs
maplistfunc_pair, Fs, Pairs1
sort1, @<, Pairs1, FPairs
ord_list_to_assocFPairs, Funcs
findallN−Cs,
memberfuncN, _, _, _, Bs, Code
direct_callsDefined, Bs, Cs
,
Calls
call_sitesCalls, Sites
other_referencesItems1, Others
findallN−Cs,
memberN−Cs0, Calls
sortCs0, Cs
,
Graph0
list_to_assocGraph0, Graph
post_orderNs, Graph, Order
Env = envMode, Prep, Cyclic, Attrs, Sites, Others, Funcs, Contexts
empty_assocE
foldlwide_function_releasedEnv, Order, E−E−0, _−Out−_
maplistwide_itemOut, Code, Groups
appendGroups, Items2
referenced_itemsItems2, Items3
A module's mode is filc if any item needs it (ir:module_mode/2):
dropping the only such function must not make it plain.
module_modemoduleItems3, Mode
→
Items = Items3
else
Items = Items2
M = moduleItems
wide_function_released+Env, +Name, +S0, -S

wide_function/4, and release_stacks/0 after every 256th function: SWI's limit counts what is allocated, and one pass over SQLite's 3,000 functions otherwise leaves the garbage of all of them (sqlite3.c peaked 1.4 GB higher).

149wide_function_releasedEnv, Name, C0−O0−N0, C−O−N ←
wide_functionEnv, Name, C0−O0, C−O
N is N0 + 1
N mod 256 =:= 0
→
else
true
wide_function+Env, +Name, +Callees0−Out0, −Callees−Out

Name's calls inlined, its result in Out, and in Callees when it can be a callee: local, on no cycle, called, not noinline. Its class is always_inline, single (one call site and no other reference) or plain.

159wide_functionEnv, Name, Callees0−Out0, Callees−Out ←
Env = envMode, Prep, Cyclic, Attrs, Sites, Others, Funcs, Contexts
get_assocName, Funcs, F0
context_recordsContexts, Name, Rs0
F0 = func_, Linkage, _, _, Bs0
func_sizeBs0, Orig
¬get_assocName, Cyclic, _
→
inline_funcw, wideOrig, Orig, Callees0, F0, Rs0, F1, Rs1
else
F1 = F0
Rs1 = Rs0
Linkage ≡ local
¬get_assocName, Cyclic, _
get_assocName, Sites, NSites
callee_className, Attrs, NSites, Others, Class
F1 = func_, _, _, _, Bs1
func_sizeBs1, S1
prepare_limitClass, S1
→
prepared_funcPrep, Mode, F1, Rs1, F, Rs
inlinableMode, any, F
→
F = func_, _, _, _, Bs
func_sizeBs, CS
put_assocName, Callees0, calleeF, Rs, CS, Class, Callees
else
Callees = Callees0
else
F = F1
Rs = Rs1
Callees = Callees0
put_assocName, Out0, F−Rs, Out
182is_funcfunc_, _, _, _, _
183func_pairF, N−F ←
F = funcN, _, _, _, _
185callee_className, Attrs, NSites, Others, Class ←
get_assocName, Attrs, A
→
A ≡ always_inline
Class = always_inline
NSites =:= 1
¬get_assocName, Others, _
→
Class = single
else
Class = plain
prepare_limit+Class, +Size

a function of Size before preparation may be admitted as a callee of Class once prepared. Preparation shrinks a body (its parameters' and locals' allocs, loads and stores go), so the limits are four times the admitted sizes, as for small's second round.

196prepare_limitalways_inline, _
197prepare_limitsingle, S ←
S ≤ 4 × Max
198prepare_limitplain, S ←
S ≤ 4 × Max
200prepared_funcplain, _, F, Rs, F, Rs
201prepared_funcsimplified, Mode, F0, Rs0, F, Rs ←
oncepromote_contextMode, F0, Rs0, F1, Rs1
oncecopy_contextF1, Rs1, F2, Rs2
oncesimplify_contextMode, F2, Rs2, F, Rs
206wide_itemOut, F0, Items ←
F0 = funcName, _, _, _, _
!
get_assocName, Out, F−Rs
with_contextF, Rs, Items
209wide_item_, Item, Item
direct_calls+Defined, +Bs, -Cs

Cs lists the module's functions that Bs call directly, once per call.

213direct_callsDefined, Bs, Cs ←
findallG,
memberblock_, _, Is, _, Bs
membercall_, globalG, _, Is
get_assocG, Defined, _
,
Cs
call_sites+Calls, -Sites

Sites maps each function called directly to its number of call sites.

219call_sitesCalls, Sites ←
findallG,
member_−Cs, Calls
memberG, Cs
,
Gs0
msortGs0, Gs
ord_list_to_assocPairs, Sites
223clumped_counts[], []
224clumped_countsX|Xs, X−N|Ps ←
same_runX, Xs, 1, N, Rest
225same_runX, Y|Ys, N0, N, Rest ←
X ≡ Y
!
N1 is N0 + 1
same_runX, Ys, N1, N, Rest
226same_run_, Rest, N, N, Rest
other_references+Items, -Others

Others holds every symbol that Items refer to other than as the callee of a direct call: an operand global(S), a data item's sym(S, _), an alias's target.

231other_referencesItems, Others ←
findallS,
memberItem, Items
item_referenceItem, operand, S
,
Ss
name_setSs, Others
item_reference+Item, +Calls, -S

Item refers to symbol S; with Calls operand, not counting a direct call's callee.

237item_referencefunc_, _, _, _, Bs, Calls, S ←
memberblock_, Phis, Is, J, Bs
memberI, Is
I = call_, Callee, Args
→
Calls ≡ all
Callee = globalS
or
else
or
or
247item_referencedata_, _, _, Ds, _, S ←
memberitem_, symS, _, Ds
248item_referencealias_, _, S, _, S
250term_globalglobalS, S ←
atomS
!
251term_globalT, S ←
compoundT
functorT, _, Arity
between1, Arity, I
argI, T, A
referenced_items+Items0, -Items

Items0 less the local functions, with their contexts, that no exported or weak function, data object or alias refers to, directly or through other functions.

258referenced_itemsItems0, Items ←
findallN−F,
memberF, Items0
F = funcN, local, _, _, _
,
Pairs0
sort1, @<, Pairs0, Pairs
ord_list_to_assocPairs, Locals
findallS,
memberItem, Items0
Item ≠ func_, local, _, _, _
Item ≠ context_, _
item_referenceItem, all, S
,
Roots
empty_assocSeen0
reachRoots, Locals, Seen0, Seen
excludeunreferencedLocals, Seen, Items0, Items
267reach[], _, Seen, Seen
268reachS|Ss, Locals, Seen0, Seen ←
get_assocS, Seen0, _
→
reachSs, Locals, Seen0, Seen
else
put_assocS, Seen0, true, Seen1
get_assocS, Locals, F
→
findallT, item_referenceF, all, T, Ts
appendTs, Ss, Work
else
Work = Ss
reachWork, Locals, Seen1, Seen
278unreferencedLocals, Seen, Item ←
Item = funcN, local, _, _, _
or
Item = contextN, _
get_assocN, Locals, _
¬get_assocN, Seen, _
post_order+Names, +Graph, -Order

Order holds each of Names after every function it reaches in Graph (name to callees), except along a cycle.

285post_orderNames, Graph, Order ←
empty_assocSeen0
foldlvisitGraph, Names, Seen0−Order, _−[]
visit+Graph, +N, +Seen0−Order0, −Seen−Order

difference list Order0 gains, after what is already there, N's unseen callees' visits, then N.

291visitGraph, N, Seen0−Order0, Seen−Order ←
get_assocN, Seen0, _
→
Seen = Seen0
Order = Order0
else
put_assocN, Seen0, true, Seen1
get_assocN, Graph, Cs
→
true
else
Cs = []
foldlvisitGraph, Cs, Seen1−Order0, Seen−Order1
Order1 = N|Order
prepared+Callees, +M0, -M

with simplified, M is M0 with each function that could become a callee (local, no call to a function of the module, at most four callee limits) promoted, copy-propagated and simplified. Others are unchanged.

303preparedplain, M, M
304preparedsimplified, moduleItems0, moduleItems ←
module_modemoduleItems0, Mode
code_itemsItems0, Code
context_indexItems0, Contexts
findallN, memberfuncN, _, _, _, _, Code, Ns
name_setNs, Defined
maplistprepared_itemMode, Defined, Contexts, Code, Groups
appendGroups, Items
311prepared_itemMode, Defined, Contexts, F0, Items ←
F0 = funcName, local, _, _, Bs
!
context_recordsContexts, Name, Rs0
S ≤ 4 × Max
¬
memberblock_, _, Is, _, Bs
membercall_, globalG, _, Is
get_assocG, Defined, _
→
oncepromote_contextMode, F0, Rs0, F1, Rs1
oncecopy_contextF1, Rs1, F2, Rs2
oncesimplify_contextMode, F2, Rs2, F, Rs
else
F = F0
Rs = Rs0
with_contextF, Rs, Items
323prepared_item_, _, Contexts, F, Items ←
F = funcName, _, _, _, _
!
context_recordsContexts, Name, Rs
with_contextF, Rs, Items
326prepared_item_, _, _, Item, Item

release_stacks: as main.pl's, collect the first round's and the preparation's garbage and return the stack memory before the second round (preparing SQLite's callees left 1.1 GB allocated, 200 MB live). SWI's limit counts what is allocated: without it, sqlite3.c overflows 1 GB with 200 MB live, and peaks 0.7 GB above --inline=none under the 4 GB limit its builds use.

333release_stacks ←
catchgarbage_collect, errorexistence_error_, _, _, true
catchtrim_stacks, errorexistence_error_, _, _, true

Sizes holds each function's size before the first round: both caps on a caller are on that size, so the second round cannot reset them.

339inline_roundRound, Sizes, moduleItems0, moduleItems ←
module_modemoduleItems0, Mode
code_itemsItems0, Code
context_indexItems0, Contexts
findallN, memberfuncN, _, _, _, _, Code, Ns
name_setNs, Defined
include/3 shares the callees' terms; findall/3 would copy them and
their records, hundreds of MB on SQLite.
includeeligible_calleeMode, Defined, Code, CalleeFs
maplistcallee_pairContexts, CalleeFs, CalleePairs
list_to_assocCalleePairs, Callees
cyclic_functionsCode, Defined, Cyclic0
name_setCyclic0, Cyclic
maplistinlined_itemRound, Sizes, Callees, Cyclic, Contexts, Code, Groups
appendGroups, Items
name_set+Names, -Set

Set is an assoc with key each of Names, for lookups in SQLite's 3,000 functions and its large callers' names.

355name_setNames, Set ←
sortNames, Sorted
findallN−true, memberN, Sorted, Pairs
ord_list_to_assocPairs, Set
359callee_pairContexts, F, N−calleeF, Rs, S, plain ←
F = funcN, _, _, _, Bs
context_recordsContexts, N, Rs
362inlined_itemRound, Sizes, Callees, Cyclic, Contexts, F0, Items ←
F0 = funcName, _, _, _, _
!
context_recordsContexts, Name, Rs0
get_assocName, Sizes, Orig
eligible_callerCyclic, Orig, F0
→
inline_funcRound, small, Orig, Callees, F0, Rs0, F, Rs
else
F = F0
Rs = Rs0
with_contextF, Rs, Items
371inlined_item_, _, _, _, _, Item, Item

Eligibility

375eligible_calleeMode, Defined, F ←
F = func_, _, Ret, _, Bs
Ret ≠ tuple_
S ≤ Max
¬
memberblock_, _, Is, _, Bs
membercall_, globalG, _, Is
get_assocG, Defined, _
inlinableMode, 256, F
inlinable+Mode, +Max, +F

F's body can replace a call of it, whatever its size and its calls: local, no setjmp, a return, no va_args, constant allocs of at most Max bytes (any, with Max any) that promote or escape (allocs_ok/2), strict SSA. Small inlining takes 256 and also refuses a tuple return. Wide takes any size: an alloc that escapes is a heap object per execution inlined or not, and allocs_ok/2 refuses a local one that would not promote.

390inlinableMode, Max, F ←
F = func_, local, _, _, Bs
memberblock_, _, _, J, Bs
!
¬
memberblock_, _, Is, _, Bs
memberI, Is
allocs_okMode, F
func_cfgBs, Cfg
cfg_idomsCfg, Idom
strict_ssaF, Cfg, Idom
returnsretret_rets_
402excluded_inst_, let_, _, va_args, _
403excluded_instMax, let_, p, alloc_, V ←
¬
V = constN
Max ≡ any
→
true
else
N ≤ Max

In filc mode every alloc either promotes or escapes, to a heap object per execution as before. In plain mode every alloc promotes: a stack alloc that stayed would live until the caller returns.

409allocs_okfilc, F ←
promotablefilc, F, Roots, anClasses, _, _
forallmemberA−local_, _, Classes, memberchkA−_, Roots
412allocs_okplain, F ←
promotableplain, F, Roots
F = func_, _, _, _, Bs
forall
memberblock_, _, Is, _, Bs
memberlettmpA, p, alloc_, _, Is
,
memberchkA−_, Roots
418eligible_callerCyclic, Orig, F ←
F = funcName, _, _, _, _
¬get_assocName, Cyclic, _
Orig ≤ Max
424func_sizeBs, S ←
foldlblock_size, Bs, 0, S
425block_sizeblock_, _, Is, _, S0, S ←
lengthIs, N
S is S0 + N + 1
cyclic_functions+Code, +Defined, -Cyclic

the ordset of functions on a cycle of direct calls between functions of the module. Functions that reach no cycle or that no cycle reaches are peeled off first; each remaining one is on a cycle if it reaches itself among them.

431cyclic_functionsCode, Defined, Cyclic ←
findallN−Cs,
memberfuncN, _, _, _, Bs, Code
direct_calleesDefined, Bs, Cs
,
Pairs
peelPairs, Core0
findallC−N,
memberN−Cs, Core0
memberC, Cs
,
Rev0
reverse_graphCore0, Rev0, Rev
peelRev, CoreR
findallN, memberN−_, CoreR, Ns0
sortNs0, Keep
findallN−Cs,
memberN−Cs0, Core0
ord_memberchkN, Keep
ord_intersectionCs0, Keep, Cs
,
Core
list_to_assocCore, Graph
includeon_cycleGraph, Keep, Cyclic
444direct_calleesDefined, Bs, Cs ←
findallG,
memberblock_, _, Is, _, Bs
membercall_, globalG, _, Is
get_assocG, Defined, _
,
Cs0
sortCs0, Cs
peel+Graph, -Core

Core is Graph less, repeatedly, the nodes with no edge to a remaining node.

451peelGraph, Core ←
findallN,
memberN−Cs, Graph
Cs ≡ []
,
Sinks0
sortSinks0, Sinks
Sinks ≡ []
→
Core = Graph
else
findallN−Cs,
memberN−Cs0, Graph
¬ord_memberchkN, Sinks
ord_subtractCs0, Sinks, Cs
,
Graph1
peelGraph1, Core
459reverse_graphGraph, Rev0, Rev ←
findallN−Ps,
memberN−_, Graph
findallP, memberN−P, Rev0, Ps0
sortPs0, Ps
,
Rev
463on_cycleGraph, N ←
get_assocN, Graph, Cs
empty_assocSeen
reachesGraph, Cs, N, Seen
467reaches_, N|_, N, _ ←
!
468reachesGraph, C|Cs, N, Seen ←
get_assocC, Seen, _
→
reachesGraph, Cs, N, Seen
else
put_assocC, Seen, true, Seen1
get_assocC, Graph, Next
→
true
else
Next = []
appendNext, Cs, Work
reachesGraph, Work, N, Seen1

The transformation

inline_func+Round, +Policy, +Orig, +Callees, +F0, +Rs0, -F, -Rs

F is F0 with its eligible calls inlined, as admitted/5 admits each under Policy. A callee that the budget counts (all of them under small) is admitted while F stays within its original size Orig plus half of Orig, or plus one callee limit for a small caller. Splits and callee records are collected as the blocks are walked and applied to the caller's records once at the end: SQLite's largest functions inline hundreds of calls over tens of thousands of records.

486inline_funcRound, Policy, Orig, Callees, F0, Rs0, F, Rs ←
F0 = funcName, L, R, Ps, Bs0
once
memberblock_, _, Is, _, Bs0
membercall_, globalG, _, Is
get_assocG, Callees, _
→
used_namesF0, Used0
name_setUsed0, Used
func_sizeBs0, S0
Budget0 is maxOrig//2, Floor−S0−Orig
policy_budgetPolicy, Budget0, S0, Budget
blocks_inlinedBs0, Policy−Callees, Used, Bs1, Moves, stRound, 1, Budget, [], [], st_, _, _, Splits, Ctxs
list_to_assocMoves, MoveIndex
maplistmoved_phisMoveIndex, Bs1, Bs
F = funcName, L, R, Ps, Bs
list_to_assocSplits, SplitIndex
reverseCtxs, Ctxs1
appendCtxs1, CtxRs
One copy of the caller's records, ending in the callees'.
foldlsplit_recordSplitIndex, MoveIndex, Rs0, Rs, CtxRs
else
F = F0
Rs = Rs0
admitted+Policy, +Class, +Size, +Budget0, -Budget

a callee of Class and Size may be inlined under Policy with Budget0 left, which leaves Budget. Under small every callee counts against the budget. Under wide(Orig), for a caller of original size Orig, an always_inline callee is admitted whatever the caller (up to a size that only guards against pathological headers), a callee called from one site only (its body then leaves the module) into a caller within the caller limit while the caller stays within grown_limit/1, and any other one as small would admit it.

515admittedsmall, _, CS, B0, B ←
CS ≤ B0
B is B0−CS
516admittedwide_, always_inline, CS, bB, S0, bB, S ←
CS ≤ Max
S is S0 + CS
518admittedwideOrig, single, CS, bB, S0, bB, S ←
Orig ≤ C
CS ≤ Max
S is S0 + CS
S ≤ G
521admittedwideOrig, plain, CS, bB0, S0, bB, S ←
Orig ≤ C
CS ≤ Max
CS ≤ B0
B is B0−CS
S is S0 + CS
policy_budget+Policy, +Budget0, +Size, -Budget

small counts the budget; wide also the caller's current size, which single callees may grow to grown_limit/1.

528policy_budgetsmall, B, _, B
529policy_budgetwide_, B, S, bB, S
531used_namesfunc_, _, _, Ps, Bs, Used ←
findallX,
memberparam_, tmpX, Ps
or
memberblockX, _, _, _, Bs
or
memberblock_, Phis, _, _, Bs
memberphitmpX, _, _, Phis
or
memberblock_, _, Is, _, Bs
memberI, Is
,
Xs
list_to_ord_setXs, Used
blocks_inlined+Bs0, +Policy−Callees, +Used, -Bs, -Moves, +St0, -St

Moves pairs each split block's label with the label of its last piece, which its successors' phis must now name. Used holds the caller's names before this round; the round's own names cannot collide with each other, since iR_N. determines N.

543blocks_inlined[], _, _, [], [], St, St
544blocks_inlinedB|Bs0, Callees, Used, Bs, Moves, St0, St ←
B = blockL, _, _, _
block_inlinedB, Callees, Used, Out, Last, St0, St1
Last ≡ L
→
Moves = Moves1
else
Moves = L−Last|Moves1
blocks_inlinedBs0, Callees, Used, Bs1, Moves1, St1, St
appendOut, Bs1, Bs
block_inlined+Block, +Policy−Callees, +Used, -Blocks, -Last, +St0, -St

Block with its first eligible call inlined, and the rest of it recursively; Last is the label of the last piece. St collects each split as L-split(NPre, NCopies, Join) and each instance's records.

555block_inlinedblockL, Phis, Is, J, Callees, Used, Out, Last, St0, St ←
St0 = stRound, N0, Budget0, Splits0, Ctxs0
Callees = Policy−Index
appendPre, callRes, globalG, Args|Post, Is
get_assocG, Index, calleeCallee, CRs, CS, Class
admittedPolicy, Class, CS, Budget0, Budget
→
fresh_prefixRound, N0, Callee, Used, N, P, Join
renamed_calleeP, Join, Callee, CBs, CPs, Rets
CBs = blockEntry, _, _, _|_
maplistparam_copy, CPs, Args, Copies
appendPre, Copies, PreIs
join_phisRes, Rets, JPhis
lengthPre, NPre
lengthCopies, NC
callee_contextrRound, N, P, CRs, CtxRs
N1 is N + 1
block_inlinedblockJoin, JPhis, Post, J, Callees, Used, JoinOut, Last, stRound, N1, Budget, L−splitNPre, NC, Join|Splits0, CtxRs|Ctxs0, St
appendblockL, Phis, PreIs, gotoEntry, CBs, JoinOut, Out
else
Out = blockL, Phis, Is, J
Last = L
St = St0
fresh_prefix+Round, +N0, +Callee, +Used, -N, -P, -Join

P is iR_N. for the first N from N0 at which no renamed callee name is in Used, and Join is P before the first of join, join_, ... that is neither a callee name nor, with P, in Used.

581fresh_prefixRound, N0, Callee, Used, N, P, Join ←
formatatomP0, 'i~w_~w.', Round, N0
used_namesCallee, Names
¬
memberX, Names
atom_concatP0, X, Y
get_assocY, Used, _
→
N = N0
P = P0
join_labelP0, Names, Used, join, Join
else
N1 is N0 + 1
fresh_prefixRound, N1, Callee, Used, N, P, Join
589join_labelP, Names, Used, Base, Join ←
atom_concatP, Base, J0
¬ord_memberchkBase, Names
¬get_assocJ0, Used, _
→
Join = J0
else
atom_concatBase, '_', Base1
join_labelP, Names, Used, Base1, Join
596param_copyparamK, tmpX, arg_, V, lettmpX, K, copy, V
renamed_callee+P, +Join, +Callee, -Blocks, -Params, -Rets

the callee's blocks and parameters with P before every temp and label, each return a jump to Join, Rets the join phi's arguments.

601renamed_calleeP, Join, func_, _, _, Ps0, Bs0, Bs, Ps, Rets ←
maplistrenamed_paramP, Ps0, Ps
maplistrenamed_blockP, Join, Bs0, Bs, Retss
appendRetss, Rets
606renamed_paramP, paramK, tmpX, paramK, tmpY ←
atom_concatP, X, Y
608renamed_blockP, Join, blockL, Phis, Is, J0, blockL1, Phis1, Is1, J, Rets ←
atom_concatP, L, L1
maplistrenamed_phiP, Phis, Phis1
maplistrenamed_tempsP, Is, Is1
J0 = retV
→
J = gotoJoin
Rets = L1−V1
J0 = retsVs
→
renamed_tempsP, Vs, Vs1
J = gotoJoin
Rets = L1−Vs1
J0 ≡ ret
→
J = gotoJoin
Rets = L1−none
else
renamed_jumpP, J0, J
Rets = []
618renamed_phiP, phiD, K, Ins, phiD1, K, Ins1 ←
maplistrenamed_phi_argP, Ins, Ins1
621renamed_phi_argP, L−V, L1−V1 ←
atom_concatP, L, L1
623renamed_jumpP, gotoT, gotoT1 ←
624renamed_jumpP, branchV, A, B, branchV1, A1, B1 ←
626renamed_jumpP, J, J1 ←
628renamed_targetP, pollL, pollL1 ←
!
atom_concatP, L, L1
629renamed_targetP, L, L1 ←
atom_concatP, L, L1
631renamed_tempsP, tmpX, tmpY ←
!
atom_concatP, X, Y
632renamed_tempsP, T, T1 ←
compoundT
!
T =.. F|As
maplistrenamed_tempsP, As, As1
T1 =.. F|As1
635renamed_temps_, T, T

A void callee's returns give no values: no phi.

638join_phisvoid, _, []
639join_phisresultD, K, Rets, phiD, K, Rets
640join_phistupleRs, Rets, Phis ←
foldltuple_phiRets, Rs, Phis, 1, _
643tuple_phiRets, resultD, K, phiD, K, Ins, I, I1 ←
findallL−V,
memberL−Vs, Rets
nth1I, Vs, V
,
Ins
I1 is I + 1
646moved_phisMoves, blockL, Phis0, Is, J, blockL, Phis, Is, J ←
maplistmoved_phiMoves, Phis0, Phis
648moved_phiMoves, phiD, K, Ins0, phiD, K, Ins ←
maplistmoved_argMoves, Ins0, Ins
650moved_argMoves, F−V, T−V ←
get_assocF, Moves, T0
→
T = T0
else
T = F

Source contexts

split_record+Splits, +Moves, +R//

the caller's records for R after every split. A split of block L at a call with NPre instructions before it keeps the sites before the call, gives the call's origins to each of the NC parameter copies, and moves the rest to Join, shifted, where a later split of Join applies in turn. A split block's jump site moves to its last piece. (The callees' records name no split block.)

660split_recordSplits, Moves, siteinstL, I, Os ⟶
get_assocL, Splits, Split
!
split_siteSplit, Splits, Moves, L, I, Os
662split_record_, Moves, sitejumpL, Os ⟶
get_assocL, Moves, T
!
sitejumpT, Os
664split_record_, _, R ⟶
R
666split_sitesplitNPre, NC, Join, Splits, Moves, L, I, Os ⟶
I < NPre
→
siteinstL, I, Os
I =:= NPre
→
copy_sites0, NC, L, NPre, Os
else
I1 is I−NPre−1
split_recordSplits, Moves, siteinstJoin, I1, Os
672copy_sitesK, NC, _, _, _ ⟶
K ≥ NC
!
673copy_sitesK, NC, L, NPre, Os ⟶
I is NPre + K
K1 is K + 1
siteinstL, I, Os
copy_sitesK1, NC, L, NPre, Os
callee_context+rRound, N, P, +CRs, -Rs

the callee's records renamed into the caller: temps and labels prefixed with P, origin and binding identities wrapped as inl(Round, N, Id). A return's jump site stays at its block, whose jump is now the goto to the join.

682callee_contextR, CRs, Rs ←
maplistcontext_recordR, CRs, Rs
684context_recordR, bindingB, Nm, Ty, Loc, bindingId, Nm, Ty, Loc ←
!
ctx_idR, B, Id
685context_recordR, originId0, Loc, D, Ps0, originId, Loc, D, Ps ←
!
ctx_idR, Id0, Id
maplistctx_idR, Ps0, Ps
687context_recordR, valueT0, H0, Os0, valueT, H, Os ←
!
ctx_tempR, T0, T
ctx_hintR, H0, H
maplistctx_idR, Os0, Os
689context_recordR, aliasT0, V0, H0, Os0, aliasT, V, H, Os ←
!
ctx_tempR, T0, T
ctx_valueR, V0, V
ctx_hintR, H0, H
maplistctx_idR, Os0, Os
691context_recordR, historyId0, T0, H0, Os0, Rep0, historyId, T, H, Os, Rep ←
!
ctx_idR, Id0, Id
ctx_tempR, T0, T
ctx_hintR, H0, H
maplistctx_idR, Os0, Os
Rep0 = replacedV0
→
ctx_valueR, V0, V
Rep = replacedV
else
Rep = Rep0
694context_recordR, siteS0, Os0, siteS, Os ←
!
ctx_siteR, S0, S
maplistctx_idR, Os0, Os
696context_record_, Rec, Rec
698ctx_idrRound, N, _, Id, inlRound, N, Id
699ctx_tempr_, _, P, T0, T ←
atom_concatP, T0, T
700ctx_hintR, namedB, Role, namedId, Role ←
!
ctx_idR, B, Id
701ctx_hint_, H, H
702ctx_valueR, tmpT0, tmpT ←
!
ctx_tempR, T0, T
703ctx_valueR, historicalId0, historicalId ←
!
ctx_idR, Id0, Id
704ctx_value_, V, V
705ctx_siter_, _, P, instL0, I, instL, I ←
atom_concatP, L0, L
706ctx_siter_, _, P, phiT0, phiT ←
atom_concatP, T0, T
707ctx_siter_, _, P, jumpL0, jumpL ←
atom_concatP, L0, L