## make-nextcode-closures-g.pkg
#
# Closures in Mythryl correspond to stackframes in C;
# they hold the parameters and temporaries needed by
# a function while it is executing.
#
# One major difference between our closures and
# C stackframes is that our closures are conceptually
# allocated on the heap and then garbage-collected.
# Among other advantages, this makes tail recursion
# and concurrent programming via 'call/cc' very
# simple to implement and quick to execute.
#
# Allocating closures on the heap is potentially
# much slower than conventional stack allocation.
# Modern multi-generation garbage collection
# largely solves this problem. (For an extended
# discussion of this topic see Chapter 5 of
# Zhong Shao's 1994 PhD thesis, cited below.)
#
# We can also reduce the cost of "heap"-allocated
# closures by a variety of compiler-centric
# strategies such as allocating all or part of
# a given closure in registers or sharing a single
# closure between multiple function calls.
#
# Our job in this file is to implement such
# closure-representation optimizations.
#
# For background, see:
#
# src/A.CLOSURE.OVERVIEW
# Compiled by:
#
src/lib/compiler/core.sublib# This file implements one of the nextcode transforms.
# For context, see the comments in
#
#
src/lib/compiler/back/top/highcode/highcode-form.api############################################################################
#
# ASSUMPTIONS: (1) Five possible combinations of bindings in the same
# ncf::DEFINE_FUNS:
# private,
# escape,
# next,
# private-next,
# private+escape;
#
# (2) 'next' (==fate) function is never recursive;
# there is at most ONE 'next' function definition
# per ncf::DEFINE_FUNS.
#
# (3) The outermost function is always a non-recursive
# escaping function.
#
############################################################################
stipulate
package ncf = nextcode_form; # nextcode_form is from
src/lib/compiler/back/top/nextcode/nextcode-form.pkgherein
api Make_Nextcode_Closures {
#
make_nextcode_closures: ncf::Function -> ncf::Function;
};
end;
# Machine_Properties is from
src/lib/compiler/back/low/main/main/machine-properties.apistipulate
#
package coc = global_controls::compiler; # global_controls is from
src/lib/compiler/toplevel/main/global-controls.pkg package iht = int_hashtable; # int_hashtable is from
src/lib/src/int-hashtable.pkg package lms = list_mergesort; # list_mergesort is from
src/lib/src/list-mergesort.pkg package mfv = make_per_function_free_variable_maps; # make_per_function_free_variable_maps is from
src/lib/compiler/back/top/closures/make-per-function-free-variable-maps.pkg package ncf = nextcode_form; # nextcode_form is from
src/lib/compiler/back/top/nextcode/nextcode-form.pkg package sy = symbol; # symbol is from
src/lib/compiler/front/basics/map/symbol.pkg package tmp = highcode_codetemp; # highcode_codetemp is from
src/lib/compiler/back/top/highcode/highcode-codetemp.pkg include package allot_prof;
include package sorted_list;
remember_highcode_codetemp_names = tmp::remember_highcode_codetemp_names;
clone_highcode_codetemp = tmp::clone_highcode_codetemp;
issue_highcode_codetemp = tmp::issue_highcode_codetemp;
offp0 = ncf::SLOT 0;
dumcs = NULL; # Dummy callee-save reg contents
zip = paired_lists::zip;
pr = global_controls::print::say;
#
fun inc (ri as REF i)
=
ri := i+1;
herein
generic package make_nextcode_closures_g (
# ========================
#
machine_properties: Machine_Properties # Typically
src/lib/compiler/back/low/main/intel32/machine-properties-intel32.pkg #
)
: (weak) Make_Nextcode_Closures # Make_Nextcode_Closures is from
src/lib/compiler/back/top/closures/make-nextcode-closures-g.pkg {
# This generic is (only) invoked from:
#
#
src/lib/compiler/back/top/main/backend-tophalf-g.pkg package mp = machine_properties; # Local synonym.
package sprof
=
static_closure_size_profiling_g ( mp ); # static_closure_size_profiling_g is from
src/lib/compiler/back/top/closures/static-closure-size-profiling-g.pkg #
fun bug s
=
error_message::impossible ("Closure: " + s);
# **************************************************************************
# MISC UTILITY FUNCTIONS *
# **************************************************************************
#
fun partition f l
=
fold_backward
(\\ (e, (a, b))
=
f e ?? (e ! a, b)
:: ( a, e ! b)
)
([], [])
l;
#
fun sublist test
=
subl
where
fun subl arg
=
s (arg, NIL)
where
fun s (a ! r, l)
=>
test a ?? s (r, a ! l)
:: s (r, l);
s (NIL, l)
=>
reverse l;
end;
end;
end;
#
fun formap f
=
iter o (\\ l = (l, 0))
where
fun iter (NIL, _) => NIL;
iter (hd ! tl, i) => f (hd, i) ! iter (tl, i+1);
end;
end;
#
fun clean l # Clean reverses the order of the argument list.
=
vars (NIL, l)
where
fun vars (l, ncf::CODETEMP x ! rest) => vars (x ! l, rest);
vars (l, _ ! rest) => vars ( l, rest);
vars (l, NIL ) => l;
end;
end;
#
fun uniqvar l
=
uniq (clean l);
#
fun entervar (ncf::CODETEMP v, l) => enter (v, l);
entervar (_, l) => l;
end;
#
fun member l (v: Int)
=
f l
where
fun f [] => FALSE;
f (a ! r) => a < v ?? f r
:: v == a;
end;
end;
#
fun member3 l (v: Int)
=
h l
where
fun h [] => FALSE;
h ((a, _, _) ! rest)
=>
a < v ?? h rest
:: a == v;
end;
end;
#
fun merge_v (l1: List( (ncf::Codetemp, Int, Int) ), l2)
=
h (l1, l2)
where
fun h ( l1 as ((u1 as (x1, a1, b1)) ! r1),
l2 as ((u2 as (x2, a2, b2)) ! r2)
)
=>
if (x1 < x2) u1 ! (h (r1, l2));
elif (x1 > x2) u2 ! (h (l1, r2));
else (x1, int::min (a1, a2), int::max (b1, b2)) ! (h (r1, r2));
fi;
h (l1,[]) => l1;
h([], l2) => l2;
end;
end;
#
fun add_v (vl, m, n, l)
=
merge_v (map (\\ x = (x, m, n)) vl, l);
#
fun uniq_v z
=
h (z, [])
where
fun h ( [], l) => l;
h (a ! r, l) => h (r, merge_v([a], l));
end;
end;
#
fun remove_v (vl: List( ncf::Codetemp ), l)
=
h (vl, l)
where
fun h (l1 as (x1 ! r1), l2 as ((u2 as (x2, _, _)) ! r2))
=>
if (x2 < x1) u2 ! (h (l1, r2));
elif (x2 > x1) h (r1, l2);
else h (r1, r2);
fi;
h ([], l2) => l2;
h (l1, []) => [];
end;
end;
#
fun accum_v ([], _)
=>
([], 1000000, 0, 0);
accum_v (vl, free)
=>
fold_backward h ([], 1000000, 0, 0) free
where
fun h ( (v, m, n), (z, i, j, k) )
=
if (member vl v)
(v ! z, int::min (m, i), int::max (n, j), k+1);
else
(z, i, j, k);
fi;
end;
end;
#
fun partition_namings fl
=
h (fl,[],[],[],[],[])
where
fun h ((fe as (ncf::PUBLIC_FN, _, _, _, _)) ! r, el, kl, rl, cl, jl) => h (r, fe ! el, kl, rl, cl, jl);
h ((fe as (ncf::PRIVATE_FN, _, _, _, _)) ! r, el, kl, rl, cl, jl) => h (r, el, fe ! kl, rl, cl, jl);
h ((fe as (ncf::PRIVATE_RECURSIVE_FN, _, _, _, _)) ! r, el, kl, rl, cl, jl) => h (r, el, fe ! kl, fe ! rl, cl, jl);
h ((fe as (ncf::FATE_FN, _, _, _, _)) ! r, el, kl, rl, cl, jl) => h (r, el, kl, rl, fe ! cl, jl);
h ((fe as (ncf::PRIVATE_FATE_FN, _, _, _, _)) ! r, el, kl, rl, cl, jl) => h (r, el, kl, rl, fe ! cl, fe ! jl);
h ((fe as (ncf::PRIVATE_TAIL_RECURSIVE_FN, _, _, _, _)) ! r, el, kl, rl, cl, jl) => h (r, el, fe ! kl, rl, cl, jl);
h (_ ! r, el, kl, rl, cl, jl) => bug "partition_namings in closure phase 231";
h ([], el, kl, rl, cl, jl) => (el, kl, rl, cl, jl);
end;
end;
make_closure_codetemp
=
{ save = *remember_highcode_codetemp_names
then
remember_highcode_codetemp_names := TRUE;
closure = tmp::issue_named_highcode_codetemp (sy::make_value_symbol "closure");
remember_highcode_codetemp_names := save;
\\ () = clone_highcode_codetemp closure;
};
# Build a list of k dummy cells:
#
fun extra_dummy (k)
=
ec (k, [])
where
fun ec (k, l)
=
k <= 0 ?? l
:: ec (k - 1, dumcs ! l);
end;
#
fun extra_lvar (k, t)
=
h (k,[],[])
where
fun h (n, l, z)
=
n < 1 ?? (reverse l, z)
:: h (n - 1, (issue_highcode_codetemp() ! l), t ! z);
end;
#
fun cuthead (n,[]) # Cut out the first n elements from a list.
=>
[];
cuthead (n, l as (_ ! r))
=>
n <= 0 ?? l
:: cuthead (n - 1, r);
end;
#
fun cuttail (n, l) # Cut out the last n elements from a list.
=
reverse (cuthead (n, reverse l));
#
fun sortlud0 x # Sort according to each variable's life time etc.
=
lms::sort_list
#
(\\ ((_, _, i: Int), (_, _, j))
=
i > j
)
#
x;
#
fun sortlud1 x
=
lms::sort_list ludfud1 x
where
fun ludfud1 ((_, m: Int, i: Int), (_, n, j))
=
(i > j) or
(i == j and m > n);
end;
#
fun sortlud2 (l, vl)
=
{ fun h (v, m, i)
=
member vl v ?? i*1000 + m*10
:: i*1000 + m*10 + 1;
#
fun ludfud2 ((_, m, v), (_, n, w))
=
(m > n) or
(m == n and v < w);
nl = map (\\ (u as (v, _, _)) = (u, h u, v))
l;
map #1 (lms::sort_list ludfud2 nl);
};
#
fun partvnum (l, n) # Cut out the first n elements, returning both the header and the rest.
=
h ([], l, n)
where
fun h (vl, [], n)
=>
(vl,[]);
h (vl, s as ((a, _, _) ! r), n)
=>
n <= 0 ?? (vl, s)
:: h (enter (a, vl), r, n - 1);
end;
end;
#
fun spill_free (free, n, vbase, sbase) # Spill (into sbase) if too many free variables (>n).
=
{ len = length free;
#
if (len < n)
#
( merge (map #1 free, vbase),
sbase
);
else
(partvnum (sortlud1 free, n))
->
(nfree, nspill);
( merge (nfree, vbase),
uniq_v (nspill @ sbase)
);
fi;
};
#
fun get_vn ([], v)
=>
NULL;
get_vn((a, m, n) ! r, v: ncf::Codetemp)
=>
if (v > a) get_vn (r, v);
elif (v == a) THE (m, n);
else NULL;
fi;
end;
#
fun subset (x, y) # See if x is a subset of y. x and y must be sorted lists.
=
case (difference (x, y))
#
[] => TRUE;
_ => FALSE;
esac;
#
fun small_chunk (ncf::typ::FLOAT64
| ncf::typ::INT) => TRUE;
# See if a nextcode type is a small constant size chunk.
small_chunk _ => FALSE;
end;
#
fun sharable ((ncf::rk::FATE_FN
|ncf::rk::FLOAT64_FATE_FN), (ncf::PUBLIC_FN|ncf::PRIVATE_FN))
# See if a record_kind is sharable by a function with given callers_info.
=>
not mp::quasi_stack;
sharable _ => TRUE;
end;
# Given a callers_info return the appropriate unboxed closure kind.
# "need runtime support for ncf::rk::FLOAT64_FATE_FN (new tags etc.)" -- This comment may be dated(?) since the compiler generates both
# FLOAT64_FATE_FN and FLOAT64_BLOCK results here without apparent problem. -- 2011-08-21 CrT
# (Or possibly the note is suggesting that the generated code could be improved with better support?)
fun unboxed_float_kind ncf::FATE_FN => ncf::rk::FLOAT64_FATE_FN;
unboxed_float_kind ncf::PRIVATE_FATE_FN => ncf::rk::FLOAT64_FATE_FN;
#
unboxed_float_kind _ => ncf::rk::FLOAT64_BLOCK;
end;
# Given a fix kind return the
# appropriate boxed closure kind
#
fun boxed_kind (ncf::FATE_FN
| ncf::PRIVATE_FATE_FN) => ncf::rk::FATE_FN;
boxed_kind ncf::PRIVATE_FN => ncf::rk::PRIVATE_FN;
boxed_kind _ => ncf::rk::PUBLIC_FN;
end;
#
fun comment f
=
if *coc::comment
f();
();
fi;
# **************************************************************************
# CLOSURE REPRESENTATIONS *
# **************************************************************************
Csregs = Null_Or( (List( ncf::Value ), List( ncf::Value )) );
Closure_Rep = CLOSURE_REP { offset: Int, closure: Closure }
withtype
Closure = { functions: List( (ncf::Codetemp, ncf::Codetemp) ),
values: List( ncf::Codetemp ),
closures: List( (ncf::Codetemp, Closure_Rep) ),
#
kind: ncf::Record_Kind,
core: List( ncf::Codetemp ),
free: List( ncf::Codetemp ),
#
stamp: ncf::Codetemp
};
Knownfun_Rep
=
{ label: ncf::Codetemp,
gpfree: List( ncf::Codetemp ),
fpfree: List( ncf::Codetemp ),
csdef: Null_Or( (List( ncf::Value ), List( ncf::Value )) )
};
Callee_Rep
=
(ncf::Value, List( ncf::Value ), List( ncf::Value ));
Chunk = VALUE ncf::Type
| CALLEE Callee_Rep
| CLOSURE Closure_Rep
| FUNCTION Knownfun_Rep
;
Access = DIRECT
| PATH (ncf::Codetemp, ncf::Fieldpath, List ((ncf::Codetemp, Closure_Rep)))
;
# **************************************************************************
# UTILITY FUNCTIONS FOR ELIMINATING THE CLOSURE OFFSET *
# **************************************************************************
# Should we adjust the offset
#
fun adj_off (i, off)
=
if (i > 0) 1;
elif (off == 0) 0;
else bug "unexpected case in adj_off";
fi;
# Should we treat the mutually recursive functions specially
#
fun mutually_recursive [] => FALSE;
mutually_recursive [_] => FALSE;
mutually_recursive _ => TRUE;
end;
# If no_offset is FALSE, use the following versions:
#
# fun adjOff (i, off) = i - off
# fun mutRec _ = FALSE
# ************************************************************************
# SYMBOL TABLE *
# ************************************************************************
stipulate # Start of abstype-replacement recipe -- see http://successor-ml.org/index.php?title=Degrade_abstype_to_derived_form
Dictionary = DICTIONARY ( List( ncf::Codetemp ), # Values
List( (ncf::Codetemp, Closure_Rep) ), # Closures
List( ncf::Codetemp ), # Disposable cells
iht::Hashtable( Chunk ) # What map
); #
herein #
Dictionary = Dictionary; # End of abstype-replacement recipe.
# *************************************************************************
# Dictionary Initializations and Augmentations *
# *************************************************************************
exception NOT_BOUND;
#
fun empty_dictionary ()
=
DICTIONARY ([],[],[], iht::make_hashtable { size_hint => 32, not_found_exception => NOT_BOUND });
# Add a new chunk to a dictionary:
#
fun augment (m as (v, chunk), e as DICTIONARY (value_l, closure_l, disp_l, what_map))
=
{ iht::set what_map m;
#
case chunk
#
VALUE _ => DICTIONARY (v ! value_l, closure_l, disp_l, what_map);
CLOSURE cr => DICTIONARY ( value_l, (v, cr) ! closure_l, disp_l, what_map);
_ => e;
esac;
};
# Add a simple program variable "v" with type t into dictionary
#
fun aug_value (v, t, dictionary)
=
augment ((v, VALUE t), dictionary);
# Add a list of value variables into dictionary
#
fun faug_value ([],[], dictionary) => dictionary;
faug_value (a ! r, t ! z, dictionary) => faug_value (r, z, aug_value (a, t, dictionary));
faug_value _ => bug "faugValue in closure.249";
end;
# Add a callee-save fate chunk into dictionary
#
fun aug_callee (v, c, csg, csf, dictionary)
=
augment ( (v, CALLEE (c, csg, csf)), dictionary);
# Add a known fate function chunk into dictionary:
#
fun aug_kcont (v, l, gfree, ffree, csg, csf, dictionary)
=
{ kchunk = FUNCTION { label => l,
gpfree => gfree,
fpfree => ffree,
csdef => THE (csg, csf)
};
augment ( (v, kchunk), dictionary);
};
# Add a general known function chunk into dictionary
#
fun aug_known (v, l, gfree, ffree, dictionary)
=
{ kchunk = FUNCTION { label => l,
gpfree => gfree,
fpfree => ffree,
csdef => NULL
};
augment ( (v, kchunk), dictionary);
};
# Add a public-function chunk into dictionary:
#
fun aug_esc_fun (v, i, CLOSURE_REP { offset, closure }, dictionary)
=
{ clo = CLOSURE (CLOSURE_REP { offset => offset+i, closure });
#
augment ( (v, clo), dictionary);
};
###########################################################################
# Dictionary Printing (for debugging)
###########################################################################
im = int::to_string: Int -> String;
vp = pr o tmp::name_of_highcode_codetemp;
#
fun vp' (v, m, n)
=
{ vp v;
pr " fd=";
pr (im m);
pr " ld=";
pr (im n);
};
#
fun ifkind ncf::PRIVATE_TAIL_RECURSIVE_FN => pr " PRIVATE_TAIL_RECURSIVE_FN ";
ifkind ncf::PRIVATE_FN => pr " PRIVATE_FN ";
ifkind ncf::PRIVATE_RECURSIVE_FN => pr " PRIVATE_RECURSIVE_FN ";
#
ifkind ncf::PUBLIC_FN => pr " PUBLIC_FN ";
ifkind ncf::FATE_FN => pr " FATE_FN ";
ifkind ncf::PRIVATE_FATE_FN => pr " PRIVATE_FATE_FN ";
#
ifkind _ => pr " STRANGE_KIND ";
end;
#
fun plist p l
=
{ apply (\\ v = { pr " "; p v; })
l;
pr "\n";
};
ilist = plist vp;
i_vlist = plist vp';
i_klist = plist ifkind;
#
fun sayv (ncf::CODETEMP v) => vp v;
sayv (ncf::LABEL v) => { pr "(L)"; vp v;};
sayv (ncf::INT i) => { pr "(I)"; pr (int::to_string i);};
sayv (ncf::INT1 i) => { pr "(I32)"; pr (one_word_unt::to_string i);};
sayv (ncf::FLOAT64 r) => pr r;
sayv (ncf::STRING s) => { pr "\""; pr s; pr "\"";};
sayv (ncf::CHUNK _) => pr "**CHUNK**";
sayv (ncf::TRUEVOID ) => pr "**TRUEVOID**";
end;
vallist = plist sayv;
#
fun print_dictionary (DICTIONARY (value_l, closure_l, disp_l, what_map))
=
{ fun ip (i: Int)
=
pr (int::to_string i);
tlist = plist (\\ (a, b) = { vp a; pr "/"; sayv (ncf::LABEL b);});
#
fun fp (v, FUNCTION { label, gpfree, fpfree, ... } )
=>
{ vp v;
pr "/known ";
sayv (ncf::LABEL label);
pr " -";
ilist (gpfree@fpfree);
};
fp _ => ();
end;
#
fun cp (v, CALLEE (v', gl, fl))
=>
{ vp v;
pr "/callee (G) ";
sayv v';
pr " -";
vallist gl;
vp v;
pr "/callee (F) ";
sayv v';
pr " -";
vallist fl;
};
cp _ => ();
end;
#
fun p (indent, l, seen)
=
apply c l
where
fun c (v, CLOSURE_REP { offset, closure => { functions, values, closures, stamp, kind, ... } } )
=
{ indent();
pr "Closure ";
vp v;
pr "/";
ip stamp;
pr "@_";
ip offset;
if (member seen stamp)
pr "(seen)\n";
else
pr ":\n";
case functions
NIL => ();
_ => { indent(); pr " Funs:"; tlist functions;};
esac;
case values
NIL => ();
_ => { indent(); pr " Vals:"; ilist values; };
esac;
p ( \\() = { indent();
pr " ";
},
closures,
enter (stamp, seen)
);
fi;
};
end;
pr "Values:"; ilist value_l;
pr "Closures:\n"; p (\\ () = (), closure_l, NIL);
pr "Disposable records:\n"; ilist disp_l;
pr "Known function mapping:\n"; iht::keyed_apply fp what_map;
pr "Callee-save fate mapping:\n";
iht::keyed_apply cp what_map;
};
##########################################################################
# Dictionary Lookup (whatIs, returning chunk type)
##########################################################################
exception LOOKUP (ncf::Codetemp, Dictionary);
#
fun what_is (dictionary as DICTIONARY (_, _, _, what_map), v)
=
iht::get what_map v
except
NOT_BOUND = raise exception LOOKUP (v, dictionary);
# Add v to the access dictionary.
# v must be in what_map already:
#
fun augvar (v, e as DICTIONARY (value_l, closure_l, disp_l, what_map))
=
case (what_is (e, v))
#
VALUE _ => DICTIONARY (v ! value_l, closure_l, disp_l, what_map);
CLOSURE cr => DICTIONARY (value_l, (v, cr) ! closure_l, disp_l, what_map);
_ => bug "augvar in nextcode/make-nextcode-closures-g.pkg:77";
esac;
##########################################################################
# Dictionary Access (where_is, returning chunk access path)
#
fun where_is (dictionary as DICTIONARY (value_l, closure_l, _, what_map), target)
=
{ fun bfs (NIL, NIL) => raise exception LOOKUP (target, dictionary);
bfs (NIL, next) => bfs (next, NIL);
bfs ((h, ox as (_, CLOSURE_REP { offset, closure => { functions, values, closures, stamp, ... } })) ! m, next)
=>
{ fun cls (NIL, _, next)
=>
bfs (m, next);
cls ((u as (v, cr)) ! t, i, next)
=>
if (target == v)
#
h (ncf::VIA_SLOT (i, ncf::SLOT 0), []);
else
nh = \\ (p, z) = h (ncf::VIA_SLOT (i, p), u ! z);
#
cls (t, i+1, (nh, u) ! next);
fi;
end;
#
fun vls (NIL, i)
=>
cls (closures, i, next);
vls (v ! t, i)
=>
if (target == v)
#
h (ncf::VIA_SLOT (i, ncf::SLOT 0), []);
else
vls (t, i+1);
fi;
end;
#
fun fns (NIL, i)
=>
vls (values, adj_off (i, offset));
fns ((v, l) ! t, i)
=>
if (target == v)
#
i == offset ?? h (ncf::SLOT 0, [])
:: h (ncf::SLOT (i-offset),[ox]);
else
fns (t, i+1);
fi;
end;
if (target == stamp)
#
offset == 0 ?? h (ncf::SLOT 0, [])
:: h (ncf::SLOT(-offset), [ox]);
else
fns (functions, 0);
fi;
};
end;
#
fun search closures
=
{ s = map (\\ x = (\\ (p, z) = (#1 x, p, z), x))
closures;
PATH (bfs (s, NIL));
};
#
fun with_tgt (v, CLOSURE_REP { closure, ... })
=
member closure.free target;
#
fun get_c ((v, cr) ! tl)
=>
if (target == v)
#
DIRECT;
else
case cr
#
CLOSURE_REP { closure => { functions => [], ... }, ... }
=>
get_c tl;
#
CLOSURE_REP { offset, closure => { functions, ... } }
=>
{ my (y, _) = list::nth (functions, offset);
if ((target==y)) PATH (v, ncf::SLOT 0, []);
else get_c tl;
fi;
};
esac;
fi;
get_c NIL
=>
search (sublist with_tgt closure_l);
end;
#
fun get_v (v ! tl)
=>
target == v ?? DIRECT
:: get_v tl;
get_v NIL => search closure_l;
end;
case (what_is (dictionary, target))
#
FUNCTION _ => DIRECT;
CALLEE _ => DIRECT;
CLOSURE _ => get_c closure_l;
VALUE _ => get_v value_l;
esac;
};
##########################################################################
# Dictionary Filtering (get the set of current reusable closures)
##########################################################################
# Extract all closures at
# top n levels, containing
# duplicates.
#
fun extract_closures (l, n, base)
=
s (h (n, l, l@base), [], [])
where
fun g (_, CLOSURE_REP { closure => { closures, ... }, ... })
=
closures;
#
fun h (k, r as _ ! _, z)
=>
if (k <= 0)
#
z;
else
nl = list::cat (map g r);
h (k - 1, nl, nl @ z);
fi;
h (k,[], z) => z;
end;
#
fun s ((u as (v, _)) ! z, vl, r)
=>
member vl v ?? s (z, vl, r)
:: s (z, enter (v, vl), u ! r);
s ([], vl, r) => r;
end;
end;
# Fetch all free variables
# residing above level n
# in the closure cr:
#
fun fetch_free (v, CLOSURE_REP { closure => { closures, functions, values, ... }, ... }, n)
=
if (n <= 0)
#
[v];
else
fold_backward g (uniq (v ! values@(map #1 functions))) closures
where
fun g ((x, cr), z)
=
merge (fetch_free (x, cr, n - 1), z);
end;
fi;
# Filter out all closures in
# the current dictionary that are
# safe to reuse:
#
fun fetch_closures (dictionary as DICTIONARY (_, closure_l, _, _), lives, fkind)
=
{ my (closlist, lives)
=
fold_backward
( \\ (v, (z, l))
=
case (what_is (dictionary, v) )
#
(CLOSURE (cr as (CLOSURE_REP { closure, ... })))
=>
((v, cr) ! z, merge (closure.free, l));
_ => (z, l);
esac
)
([], lives)
lives;
#
fun reusable (v, CLOSURE_REP { closure, ... })
=
( (sharable (closure.kind, fkind))
and
( (subset (closure.core, lives))
or
(member lives v)
)
);
#
fun reusable2 (_, CLOSURE_REP { closure, ... })
=
sharable (closure.kind, fkind);
#
fun fblock (_, CLOSURE_REP { closure => { kind => ncf::rk::FLOAT64_BLOCK, ... }, ... }) => TRUE;
fblock (_, CLOSURE_REP { closure => { kind => ncf::rk::FLOAT64_FATE_FN, ... }, ... }) => TRUE;
#
fblock _ => FALSE;
end;
level = 4; # Should be made adjustable in the future XXX BUGGO FIXME
closlist = extract_closures (closure_l, level, closlist);
(partition fblock closlist)
->
(fclist, gclist);
( sublist reusable gclist,
sublist reusable2 fclist
);
};
# Return the immediately enclosing
# closure, if any. This is a hack:
#
fun get_immed_closure (DICTIONARY (_, closure_l, _, _))
=
getc closure_l
where
fun getc ([z]) => THE z;
getc (_ ! tl) => getc tl;
getc NIL => NULL;
end;
end;
##########################################################################
# Fate Frames Book-keeping (in support of quasi-stack frames) *
##########################################################################
# vl is a list of fate frames
# that were reused along this path
#
fun recover_frames (vl, DICTIONARY (value_l, closure_l, disp_l, what_map))
=
DICTIONARY (value_l, closure_l, ndisp_l, what_map)
where
fun h (a, l)
=
if (member vl a) l;
else a ! l;
fi;
ndisp_l = fold_backward h [] disp_l;
end;
# Save the fate closure
# "v" and its descendants:
#
fun save_frames (v, CLOSURE_REP { closure => { free, kind => (ncf::rk::FATE_FN
| ncf::rk::FLOAT64_FATE_FN), ... }, ... }, dictionary)
=>
recover_frames (free, dictionary);
save_frames (_, _, dictionary) => dictionary;
end;
# Install the set of live frames at
# the entrance of this fate:
#
fun install_frames (newd, dictionary as DICTIONARY (value_l, closure_l, disp_l, what_map))
=
DICTIONARY (value_l, closure_l, newd @ disp_l, what_map);
# Split the current disposable frame
# list into two based on the context:
#
fun split_dictionary (DICTIONARY (value_l, closure_l, disp_l, w), inherit)
=
{ (partition inherit disp_l) -> (d1, d2);
#
( DICTIONARY ([], [], d1, w),
DICTIONARY (value_l, closure_l, d2, w)
);
};
# Return the set of disposable frames:
#
fun dead_frames (DICTIONARY (_, _, disp_l, _))
=
disp_l;
end; # Abstype dictionary
Frags = List ( ( ncf::Callers_Info, # If all callers are known, calling convention can be customized for space and time efficiency.
ncf::Codetemp, # fun_id -- an Int uniquely identifying the function.
List( ncf::Codetemp ), # fun_parameters.
List( ncf::Type ), # fun_parameter_types.
ncf::Instruction, # fun_body.
Dictionary,
Int,
List( ncf::Value ),
List( ncf::Value ),
Null_Or( ncf::Codetemp )
)
);
##########################################################################
# UTILITY FUNCTIONS FOR CALLEE-SAVE REGISTERS
##########################################################################
# It doesnot take the looping freevar
# into account, NEEDS MORE WORK. XXX BUGGO FIXME
#
fun fetch_csregs (c, m, n, dictionary)
=
case (what_is (dictionary, c) )
#
CALLEE (_, csg, csf)
=>
( cuthead (m, csg),
cuthead (n, csf)
);
#
FUNCTION { csdef => THE (csg, csf), ... }
=>
( cuthead (m, csg),
cuthead (n, csf)
);
_ => ([], []);
esac;
# Fetch m csgpregs and n csfpgregs
# from the default fate c:
#
fun fetch_csvars (c, m, n, dictionary)
=
{ (fetch_csregs (c, m, n, dictionary))
->
(gpregs, fpregs);
( uniqvar gpregs,
uniqvar fpregs
);
};
# Fill the empty csgpregs
# with the closure:
#
fun fill_csregs (csg, c)
=
h (csg, [], c)
where
fun g ( [], l) => l;
g (a ! r, l) => g (r, a ! l);
end;
#
fun h (NULL ! r, x, c) => g (x, c ! r);
h ( u ! r, x, c) => h (r, u ! x, c);
h ( [], x, c) => bug "no empty slot in fillCSregs in make-nextcode-closures-g.pkg";
end;
end;
# Fill the empty cs formals
# with new variables,
# augment the dictionary:
#
fun fill_csformals (gpbase, fpbase, dictionary, ft)
=
fold_backward h (fold_backward g (dictionary,[],[]) fpbase) gpbase
where
fun h (THE v, (e, a, c))
=>
(augvar (v, e), v ! a, (ft v) ! c);
h (NULL, (e, a, c))
=>
{ v = issue_highcode_codetemp ();
#
(aug_value (v, ncf::bogus_pointer_type, e), v ! a, ncf::bogus_pointer_type ! c);
};
end;
#
fun g (THE v, (e, a, c))
=>
(augvar (v, e), v ! a, ncf::typ::FLOAT64 ! c);
g (NULL, (e, a, c))
=>
{ v = issue_highcode_codetemp ();
#
(aug_value (v, ncf::typ::FLOAT64, e), v ! a, ncf::typ::FLOAT64 ! c);
};
end;
end;
# Get all free variables in cs regs,
# augment the dictionary:
#
fun vars_csregs (gpbase, fpbase, dictionary)
=
(gfree, ffree, dictionary)
where
fun h (NULL, (e, l)) => (e, l);
h (THE v, (e, l)) => (augvar (v, e), enter (v, l));
end;
(fold_backward h (dictionary,[]) gpbase) -> (dictionary, gfree);
(fold_backward h (dictionary,[]) fpbase) -> (dictionary, ffree);
end;
# Get all free variables
# covered by the cs regs
#
fun freev_csregs (gpbase, dictionary)
=
fold_backward h [] gpbase
where
fun h (THE v, l)
=>
case (what_is (dictionary, v) )
#
(CLOSURE (CLOSURE_REP { closure => { free, kind => (ncf::rk::FATE_FN
| ncf::rk::FLOAT64_FATE_FN), ... }, ... }))
=>
(merge (free, l));
#
_ => l;
esac;
h (NULL, l) => l;
end;
end;
# Partnull cuts out the head
# of csregs till the first
# empty position:
#
fun partition_to_null l
=
h (l, [])
where
fun h ( [], r) => bug "partitionToNull. no empty position in closure 343";
h (NULL ! z, r) => (reverse (NULL ! r), z);
h ( u ! z, r) => h (z, u ! r);
end;
end;
# Create a template of the
# base callee-save registers
# (n: extra cs regs)
#
fun make_base (regs, free, n)
=
fold_backward h (extra_dummy (n), []) regs
where
fun h (ncf::CODETEMP v, (r, z))
=>
member free v ?? ((THE v) ! r, enter (v, z))
:: ( dumcs ! r, z );
h (_, (r, z))
=>
(dumcs ! r, z);
end;
end;
# Modify the base, retain only
# those variables in free:
#
fun modify_base (base, free, n)
=
fold_backward h ([], free, n) base
where
fun h (s as (THE v), (r, z, m))
=>
if (member free v)
#
(s ! r, rmv (v, z), m);
else
if (m > 0)
#
( s ! r, z, m - 1);
else
(dumcs ! r, z, m );
fi;
fi;
h (NULL, (r, z, m))
=>
(NULL ! r, z, m);
end;
end;
# Fill the empty callee-save registers,
# assuming newv can be put in base:
#
fun fill_base (base, newv)
=
h (base, [], newv)
where
fun g ( [], s) => s;
g (a ! r, s) => g (r, a ! s);
end;
#
fun h ( s, l, []) => g (l, s);
h ( NULL ! z, l, a ! r) => h (z, (THE a) ! l, r);
h ((u as (THE _)) ! z, l, r) => h (z, u ! l, r);
h ( [], l, _) => bug "no enough slots: fillBase 398 in make-nextcode-closures-g.pkg";
end;
end;
##########################################################################
# VARIABLE ACCESS PATH LOOKUP
##########################################################################
# Simulating the OFFSET operation
# by reconstructing the closures:
#
fun offset ( (z, CLOSURE_REP { offset => n, closure => { functions, values, closures, ... } }), i, u, x, dictionary)
=
{ # Invariant: length functions > 1
(list::nth (functions, n+i))
->
(_, l);
case u
#
ncf::CODETEMP z'
=>
if (z != z') bug "unexpected case in offset 1"; fi;
_ => bug "unexpected case in offset 2";
esac;
label = (ncf::LABEL l, offp0);
vl = case (closures, values)
#
(([(v, _)], [])
| ([], [v])) => [label, (ncf::CODETEMP v, offp0)];
([], []) => [label];
_ => bug "unexpected case in offset 3";
esac;
(record_elements (ncf::rk::PUBLIC_FN, vl, x, dictionary))
->
(header, dictionary);
(header, dictionary);
}
# If no_offset is FALSE, use this version
#
# fun offset (_, i, record, to_temp, dictionary)
# =
# { header = \\ next = ncf::GET_ADDRESS_OF_FIELD_I { i, record, to_temp, next };
# (header, dictionary);
# };
# Build the header by partially
# following an access path:
also
fun pfollow (p, dictionary, header)
=
case p
#
(v, np as ((ncf::SLOT 0)
| (ncf::VIA_SLOT(_, ncf::SLOT 0))), [])
=>
((ncf::CODETEMP v, np), dictionary, header);
(v, np as (ncf::SLOT i), [c as (_, cr as CLOSURE_REP { offset => n, closure })])
=>
{ w = make_closure_codetemp ();
my (nh, dictionary) = offset (c, i, ncf::CODETEMP v, w, dictionary);
dictionary = augment ((w, CLOSURE (CLOSURE_REP { offset => n+i, closure })), dictionary);
((ncf::CODETEMP w, ncf::SLOT 0), dictionary, header o nh);
};
(v, ncf::VIA_SLOT (i, np), (to_temp, cr) ! z)
=>
{ dictionary = augment ((to_temp, CLOSURE cr), dictionary);
nhdr = \\ next = ncf::GET_FIELD_I { i, record => ncf::CODETEMP v, to_temp, type => ncf::bogus_pointer_type, next };
#
pfollow ((to_temp, np, z), dictionary, header o nhdr);
};
_ => bug "pfollow on an inconsistent path";
esac
# Build the header by
# following an access path:
also
fun follow (rootvar, type)
=
g
where
#
fun g ((v, ncf::SLOT 0, []), dictionary, h)
=>
(dictionary, h o (\\ next = ncf::GET_ADDRESS_OF_FIELD_I { i => 0, record => ncf::CODETEMP v, to_temp => rootvar, next }));
g ((v, ncf::SLOT i, [c]), dictionary, h)
=>
{ my (nh, dictionary) = offset (c, i, ncf::CODETEMP v, rootvar, dictionary);
# Dictionary is updated by the client of "follow"
(dictionary, h o nh);
};
g ((v, ncf::VIA_SLOT (i, ncf::SLOT 0), []), dictionary, h)
=>
(dictionary, h o (\\ next = ncf::GET_FIELD_I { i, record => ncf::CODETEMP v, to_temp => rootvar, type, next }));
g ((v, ncf::VIA_SLOT (i, p), (to_temp, cr) ! z), dictionary, h)
=>
{ dictionary = augment ((to_temp, CLOSURE cr), dictionary);
#
g ( (to_temp, p, z),
dictionary,
h o (\\ next = ncf::GET_FIELD_I { i, record => ncf::CODETEMP v, to_temp, type => ncf::bogus_pointer_type, next })
);
};
g _ => bug "follow on an inconsistent path";
end;
end
##########################################################################
# record_elements finds the complete access paths for elements of a record.
# It returns a header for profiling purposes if needed.
##########################################################################
also
fun record_elements (kind, l, to_temp, dictionary)
=
{ fun g (u as (ncf::CODETEMP v, ncf::SLOT 0), (l, cl, header, dictionary))
=>
{ dictionary = case (what_is (dictionary, v)) # May be unnecessary
#
CLOSURE cr => save_frames (v, cr, dictionary);
_ => dictionary;
esac;
my (m, cost, nhdr, dictionary)
=
case (where_is (dictionary, v))
#
DIRECT => (u, 0, header, dictionary);
#
PATH (np as (start, path, _))
=>
{ n = ncf::lenp path;
#
nhdr = if *coc::static_closure_size_profiling
#
sprof::incln n;
header o (\\ next = ncf::STORE_TO_RAM { op => ncf::p::ACCLINK,
args => [ncf::INT n, ncf::CODETEMP start],
next
}
);
else
header;
fi;
my (u, dictionary, nhdr)
=
if (*coc::sharepath)
#
pfollow (np, dictionary, nhdr);
else
((ncf::CODETEMP start, path), dictionary, nhdr);
fi;
(u, n, nhdr, dictionary);
};
esac;
(m ! l, cost ! cl, nhdr, dictionary);
};
g (u as (ncf::CODETEMP _, _), _) => bug "unexpected case in recordEl";
g (u, (l, cl, header, dictionary)) => (u ! l, 0 ! cl, header, dictionary);
end;
(fold_backward g (NIL, NIL, \\ ce = ce, dictionary) l)
->
(fields, cl, header, dictionary);
header = if (*coc::allocprof) header o (prof_rec_links cl);
else header;
fi;
nhdr = \\ next = header (ncf::DEFINE_RECORD { kind, fields, to_temp, next });
(nhdr, dictionary);
};
############################################################################
# fix_access finds the access path to a variable. A header to select the
# variable from the dictionary is returned, along with a new dictionary
# that reflects the actions of the header (this last implements a "lazy
# display"). fix_access actually causes renamings -- the variable
# requested is rebound if it is not immediately available in the
# dictionary, these renamings are later eliminated by an "unrebind" pass
# which basically does the alpha convertions.
#
fun fix_access (args, dictionary)
=
fold_backward access (dictionary, \\ x => x; end ) args
where
#
fun access (ncf::CODETEMP rootvar, (dictionary, header))
=>
{ what = what_is (dictionary, rootvar);
#
my (dictionary, t)
=
case what
#
VALUE x => (dictionary, x);
CLOSURE cr => (save_frames (rootvar, cr, dictionary), ncf::bogus_pointer_type);
_ => bug "Callee or Known in fixAccess closure";
esac;
case (where_is (dictionary, rootvar))
#
DIRECT => (dictionary, header);
#
PATH (p as (_, path, _))
=>
{ my (dictionary, header)
=
follow (rootvar, t) (p, dictionary, header);
dictionary = augment ((rootvar, what), dictionary);
#
fun prof_l (n)
=
if (not *coc::allocprof)
#
if (n > 0 and *coc::static_closure_size_profiling)
#
sprof::incln (n);
\\ next = ncf::STORE_TO_RAM { op => ncf::p::ACCLINK,
args => [ncf::INT n, ncf::CODETEMP rootvar],
next
};
else
\\ ce = ce;
fi;
else
prof_links n;
fi;
( dictionary,
header o prof_l (ncf::lenp path)
);
};
esac;
};
access (_, y) => y;
end;
end;
##########################################################################
# fix_args is a slightly modified version of fix_access. It's used to find
# the access path of function arguments in the APPLY expressions
#
fun fix_args (args, dictionary)
=
fold_backward access ([], dictionary, \\ x = x) args
where
fun access (z as (ncf::CODETEMP rootvar), (result, dictionary, h))
=>
{ what = what_is (dictionary, rootvar);
#
my (dictionary, t)
=
case what
#
VALUE x => (dictionary, x);
CLOSURE cr => (save_frames (rootvar, cr, dictionary), ncf::bogus_pointer_type);
_ => (dictionary, ncf::bogus_pointer_type);
esac;
case what
#
FUNCTION _ => bug "Known in fixArgs make-nextcode-closures-g.pkg";
CALLEE (l, csg, csf)
=>
{ nargs = (l ! csg) @ csf @ result;
#
(fix_access (nargs, dictionary))
->
(dictionary, header);
(nargs, dictionary, h o header);
};
_ => case (where_is (dictionary, rootvar))
#
DIRECT => (z ! result, dictionary, h);
#
PATH (p as (_, path, _))
=>
{ (follow (rootvar, t) (p, dictionary, h))
->
(dictionary, header);
dictionary = augment ((rootvar, what), dictionary);
fun prof_l (n)
=
if (not *coc::allocprof)
#
if (n > 0 and *coc::static_closure_size_profiling)
#
sprof::incln (n);
\\ next = ncf::STORE_TO_RAM { op => ncf::p::ACCLINK,
args => [ncf::INT n, ncf::CODETEMP rootvar],
next
};
else
\\ ce = ce;
fi;
else
prof_links n;
fi;
(z ! result, dictionary, header o prof_l (ncf::lenp path));
};
esac;
esac;
};
access (z, (result, dictionary, h))
=>
(z ! result, dictionary, h);
end;
end;
##########################################################################
# CLOSURE DISPOSAL
##########################################################################
# Dispose the set of dead fate closures
#
fun dispose_frames (dictionary)
=
if mp::quasi_stack
#
vl = dead_frames (dictionary);
(fix_access (map ncf::CODETEMP vl, dictionary))
->
(dictionary, header);
#
fun g (v ! r, h)
=>
g (r, h o (\\ next = ncf::STORE_TO_RAM { op => ncf::p::FREE,
args => [ncf::CODETEMP v],
next
}
) );
g ([], h)
=>
if (*coc::allocprof) ((prof_ref_cell (length vl)) o header o h);
else header o h;
fi;
end;
(dictionary, g (vl, header));
else
(dictionary, \\ ce = ce);
fi;
##########################################################################
# CLOSURE STRATEGIES
##########################################################################
# Produce the nextcode header and
# modify the dictionary for
# the new closure:
#
fun make_closure (cname, contents, cr, record_kind, fkind, dictionary)
=
{ if *coc::static_closure_size_profiling
#
sprof::incfk (fkind, length contents);
fi;
l = map (\\ v = (v, offp0)) contents;
(record_elements (record_kind, l, cname, dictionary))
->
(header, dictionary);
nhdr = if *coc::allocprof
#
prof = case fkind
ncf::PRIVATE_FN => prof_kclosure;
ncf::PUBLIC_FN => prof_closure;
_ => prof_cclosure;
esac;
(prof (length contents)) o header;
else
header;
fi;
dictionary = augment ((cname, CLOSURE cr), dictionary);
case fkind
#
(ncf::FATE_FN
|ncf::PRIVATE_FATE_FN) => (nhdr, dictionary, [cname]);
_ => (nhdr, dictionary, [ ]);
esac;
};
# Build an unboxed closure,
# currently not disposable even if fkind==next_fn.
# Place one_word_int's after floats for proper alignment
#
fun closure_ub_fn (cn, free, rk, fk, dictionary)
=
{ nfree = map (\\ (v, _, _) = v) free;
#
ul = map ncf::CODETEMP nfree;
cr = CLOSURE_REP
{
offset => 0,
closure => { functions => [],
closures => [],
values => nfree,
core => [],
free => enter (cn, nfree),
kind => rk,
stamp => cn
}
};
( make_closure (cn, ul, cr, rk, fk, dictionary),
cr
);
};
#
fun closure_unboxed (cn, int1free, otherfree, fk, dictionary)
=
case (int1free, otherfree)
#
([], []) => bug "unexpected case in closureUnboxed 333";
([], _)
=>
{ rk = unboxed_float_kind fk;
#
#1 (closure_ub_fn (cn, otherfree, rk, fk, dictionary));
};
(_, [])
=>
{ rk = ncf::rk::INT1_BLOCK;
#
#1 (closure_ub_fn (cn, int1free, rk, fk, dictionary));
};
_
=>
{ rk1 = unboxed_float_kind fk;
#
cn1 = make_closure_codetemp ();
(closure_ub_fn (cn1, otherfree, rk1, fk, dictionary))
->
((nh1, dictionary, nf1), cr1);
rk2 = ncf::rk::INT1_BLOCK;
cn2 = make_closure_codetemp ();
(closure_ub_fn (cn2, int1free, rk2, fk, dictionary))
->
((nh2, dictionary, nf2), cr2);
rk = boxed_kind fk;
nfree = map (\\ (v, _, _) = v) (int1free@otherfree);
nfs = [cn1, cn2];
ncs = [(cn1, cr1), (cn2, cr2)];
ul = map ncf::CODETEMP nfs;
cr = CLOSURE_REP
{
offset => 0,
closure => { functions => [],
closures => ncs,
values => [],
core => [],
free => enter (cn, nfs @ nfree),
kind => rk,
stamp => cn
}
};
(make_closure (cn, ul, cr, rk, fk, dictionary))
->
(nh, dictionary, nfs);
(nh1 o nh2 o nh, dictionary, nfs);
};
esac;
# old code
#
# let nfree = map (\\ (v, _, _) => v) (otherfree @ int1free)
# ul = map ncf::CODETEMP nfree
# rk = unboxedKind (fk)
# rk = case (int1free, otherfree)
# of ([], _) => rk
#
| (_,[]) => ncf::rk::INT1_BLOCK
#
| _ => bug "unimplemented one_word_int + float (nclosure.1)"
# cr = CLOSURE_REP { offset => 0, closure => { functions=>[], closures=>[], values=>nfree, core=>[], free=>enter (cn, nfree), kind=rk, stamp=cn } }
# in make_closure (cn, ul, cr, rk, fk, dictionary)
# end
# Partition a set of free variables
# into small frames:
#
fun partition_by_frame (free)
=
if (not (mp::quasi_stack))
#
(free, []);
else
size = mp::quasi_frame_size;
#
fun h ([ ], n, t) => (t,[]);
h ([v], n, t) => (v ! t,[]);
h (z as (v ! r), n, t)
=>
if (n <= 1)
#
my (nb, nt)
=
h (z, size, []);
cn = make_closure_codetemp ();
(cn ! t, (cn, nb) ! nt);
else
h (r, n - 1, v ! t);
fi;
end;
h (free, size, []);
fi;
# Partition the free variables into
# closures and non-closures:
#
fun partition_by_kind (cfree, dictionary)
=
fold_backward g (NIL, NIL, NIL, NIL) cfree
where
fun g (v, (vls, cls, fv, cv))
=
{ chunk = what_is (dictionary, v);
#
case chunk
#
VALUE t
=>
( v ! vls,
cls,
enter (v, fv),
(small_chunk t) ?? cv :: enter (v, cv)
);
CLOSURE (cr as CLOSURE_REP { closure => { free, core, ... }, ... })
=>
( vls,
(v, cr) ! cls,
merge (free, fv),
merge (core, cv)
);
_ => bug "unexpected chunk in kind in nextcode/make-nextcode-closures-g.pkg";
esac;
};
end;
# Closure strategy: flat
#
fun flat (dictionary, cfree, rk, fk)
=
{ my (topfv, clist)
=
case rk
#
(ncf::rk::FATE_FN
| ncf::rk::FLOAT64_FATE_FN)
=>
partition_by_frame cfree;
_ => (cfree, []);
esac;
#
fun g ((cn, free), (dictionary, header, nf))
=
{ (partition_by_kind (free, dictionary))
->
(vls, cls, fvs, cvs);
cr = CLOSURE_REP
{
offset => 0,
closure => { functions => [],
values => vls,
closures => cls,
kind => rk,
stamp => cn,
core => cvs,
free => enter (cn, fvs)
}
};
ul = (map ncf::CODETEMP vls) @ (map (ncf::CODETEMP o #1) cls);
(make_closure (cn, ul, cr, rk, fk, dictionary))
->
(nh, dictionary, nf2);
( dictionary,
header o nh,
nf2 @ nf
);
};
(fold_backward g (dictionary, \\ ce => ce; end, []) clist)
->
(dictionary, header, frames);
(partition_by_kind (topfv, dictionary))
->
(values, closures, fvars, cvars);
(closures, values, header, dictionary, fvars, cvars, frames);
};
# Closure strategy: linked
#
fun link (dictionary, cfree, rk, fk)
=
case (get_immed_closure dictionary)
#
NULL => flat (dictionary, cfree, rk, fk);
#
THE (z, CLOSURE_REP { closure => { free, ... }, ... })
=>
{ not_in = sublist (not o (member free)) cfree;
if (length (not_in) == length (cfree)) flat (dictionary, cfree, rk, fk);
else flat (dictionary, enter (z, cfree), rk, fk);
fi;
};
esac;
# Partition a set of free variables
# into layered groups based on their
# lud:
#
fun partition_into_layers (free, ccl)
=
{ fun find (r, (v, all) ! z)
=>
if (subset (r, all)) THE v;
else find (r, z);
fi;
find (r, []) => NULL;
end;
# Current limit of a new layer: 3
#
fun m ([], t, b) => bug "unexpected case in partitionIntoLayers in closure";
m ([v], t, b) => (enter (v, t), b);
m ([v, w], t, b) => (enter (v, enter (w, t)), b);
m (r, t, b)
=>
case (find (r, ccl))
#
NULL =>
{ nc = make_closure_codetemp ();
( enter (nc, t),
(nc, r) ! b
);
};
THE v =>
(enter(v,t), b);
esac;
end;
# Process the rest groups in free:
#
fun h ([], i: Int, r, t, b)
=>
m (r, t, b);
h ((v, _, j) ! z, i, r, t, b)
=>
if (j == i)
#
h (z, i, enter (v, r), t, b);
else
my (nt, nb) = m (r, t, b);
h (z, j, [v], nt, nb);
fi;
end;
# Cut out the top group and
# then process the rest:
#
fun g ((v, _, i) ! z, j, t)
=>
if (i == j) g (z, j, enter (v, t));
else h (z, i, [v], t, []);
fi;
g ( [], j, t)
=>
(t, []);
end;
my (topfv, botclos)
=
case (sortlud0 free)
#
[] => ([], []);
(u as ((_, _, j) ! _))
=>
g (u, j, []);
esac;
(topfv, botclos);
}; # fun partition_into_layers
# Closure strategy: layered
#
fun layer (dictionary, cfree, rk, fk, ccl)
=
{ (partition_into_layers (cfree, ccl))
->
(topfv, clist);
#
fun g ((cn, vfree), (bh, dictionary, nf))
=
{ (flat (dictionary, vfree, rk, fk))
->
(cls, vls, nh1, dictionary, fvs, cvs, nf1);
cr = CLOSURE_REP
{
offset => 0,
closure => { functions => [],
values => vls,
closures => cls,
kind => rk,
stamp => cn,
core => cvs,
free => enter (cn, fvs)
}
};
ul = (map ncf::CODETEMP vls) @ (map (ncf::CODETEMP o #1) cls);
(make_closure (cn, ul, cr, rk, fk, dictionary))
->
(nh2, dictionary, nf2);
( bh o nh1 o nh2,
dictionary,
nf2 @ nf1 @ nf
);
};
(fold_backward g (\\ ce = ce, dictionary, []) clist)
->
(header, dictionary, frames);
(flat (dictionary, topfv, rk, fk))
->
(cls, vls, nh, dictionary, fvs, cvs, nfr);
(cls, vls, header o nh, dictionary, fvs, cvs, nfr @ frames);
}; # fun layer
# Build a general closure,
# cg_options::closure_strategy matters:
#
fun closure_boxed (cn, fns, free, fk, ccl, dictionary)
=
{ rk = boxed_kind fk;
#
my (closures, values, header, dictionary, fvs, cvs, frames)
=
case *coc::closure_strategy
#
(4
|3) => link (dictionary, map
#1 free, rk, fk);
(2
|1) => flat (dictionary, map
#1 free, rk, fk);
_ => layer (dictionary, free, rk, fk, ccl);
esac;
my (closures, values, header, dictionary, fvs, cvs, frames, labels)
=
if (mutually_recursive fns) # Invariants length fns > 1
#
nlabs = [ ncf::LABEL (#2 (head fns)) ]; # No sharing.
case (closures, values)
#
(([],[_])
| ([_],[]) | ([],[]))
=>
(closures, values, header, dictionary, fvs, cvs, frames, nlabs);
_ => { nv = make_closure_codetemp();
ul = (map ncf::CODETEMP values) @ (map (ncf::CODETEMP o #1) closures);
nfvs = enter (nv, fvs);
cr = CLOSURE_REP
{
offset => 0,
closure => { functions => [],
values,
closures,
kind => rk,
stamp => nv,
core => cvs,
free => nfvs
}
};
(make_closure (nv, ul, cr, rk, fk, dictionary))
->
(nh, nenv, nf);
( [(nv, cr)],
[],
header o nh,
nenv,
nfvs,
cvs,
nf @ frames,
nlabs
);
};
esac;
else
(closures, values, header, dictionary, fvs, cvs, frames, map (ncf::LABEL o #2) fns);
fi;
nfvs = fold_backward enter (enter (cn, fvs)) (map #1 fns);
cr = CLOSURE_REP
{
offset => 0,
#
closure => { functions => fns,
values,
closures,
kind => rk,
stamp => cn,
core => cvs,
free => nfvs
}
};
ul = labels @ (map ncf::CODETEMP values) @ (map (ncf::CODETEMP o #1) closures);
(make_closure (cn, ul, cr, rk, fk, dictionary))
->
(nh, nenv, nf);
( header o nh,
nenv,
cr,
nf @ frames
);
}; # function closure_boxed
##########################################################################
# CLOSURE SHARING VIA THINNING
##########################################################################
# Check if some free variables
# are really not necessary:
#
fun shorten_free ([], [], _)
=>
([], []);
shorten_free (gpfree, fpfree, cclist)
=>
{ fun g ((v, free), l)
=
member3 gpfree v ?? merge (rmv (v, free), l)
:: l;
all = fold_backward g [] cclist;
( remove_v (all, gpfree),
remove_v (all, fpfree)
);
};
end;
# Check if ok to share with
# some closures in the
# enclosing dictionary:
#
fun thin_free (vfree, vlen, closlist, limit)
=
{ fun g (v, (l, m, n))
=
if (member3 vfree v ) (v ! l, m+1, n);
else ( l, m, n+1);
fi;
#
fun h ((v, cr as CLOSURE_REP { closure => { free, ... }, ... }), x)
=
{ (fold_backward g ([], 0, 0) free)
->
(zl, m, n);
if (m < limit) x;
else (v, zl, m*10000-n) ! x;
fi;
};
#
fun worse ((_, _, i), (_, _, j))
=
i < j;
#
fun m ([], s, r, k)
=>
(s, r);
m((v, x, _) ! y, s, r, k)
=>
if (k < limit)
#
(s, r);
else
my (nx, i, n, len)
=
accum_v (x, r);
if (len < limit)
#
m (y, s, r, k);
else
m ( y,
add_v ([v], i, n, s),
remove_v (nx, r),
k - len
);
fi;
fi;
end;
clist = lms::sort_list worse (fold_backward h [] closlist);
m (clist, [], vfree, vlen);
};
#
fun thin_fp_free (free, closlist)
=
thin_free (free, length free, closlist, 1);
#
fun thin_gp_free (free, closlist)
=
{ len = length free;
#
my (spill, free)
=
if (len <= 1) ([], free);
else thin_free (free, len, closlist, int::min (3, len));
fi;
merge_v (spill, free);
};
# Check if there is a closure
# containing all the free variables:
#
fun thin_all ( [], _, _) => [];
thin_all (free as [v], _, _) => free;
thin_all (free, cclist, n)
=>
{ vfree = map (\\ (v, _, _) = v) free;
#
fun g ((v, nfree), (x, y))
=
if (not (subset (vfree, nfree)))
#
(x, y);
else
len = length (difference (nfree, vfree));
len < y ?? (THE v, len)
:: (x, y);
fi;
my (result, _)
=
fold_backward g (NULL, 100000) cclist;
case result
#
NULL => free;
THE u => [(u, n, n)];
esac;
};
end;
##########################################################################
# Generating the true free variables (freeAnalysis), each knownfunc is
# replaced by its free variables and each fate by its callee-save
# registers. Finally, if two free variables are functions from the same
# closure, just one of them is sufficient to access both.
##########################################################################
#
fun same_closure_opt (free, dictionary)
=
case *coc::closure_strategy
#
1 => free; # Flat without aliasing.
3 => free; # Linked without aliasing.
#
_ => map #1 (uniq (map g free)) # All others have aliasing.
where
fun g (v as (z, _, _))
=
(v, what_is (dictionary, z));
#
fun uniq ((hd as (v, CLOSURE (CLOSURE_REP { closure => { stamp => s1, ... }, ... }))) ! tl)
=>
{ m' = uniq tl;
#
fun h (_, CLOSURE (CLOSURE_REP { closure => { stamp => s2, ... }, ... }))
=>
s1 == s2;
h _ => FALSE;
end;
list::exists h m' ?? m'
:: (hd ! m');
};
uniq (hd ! tl) => hd ! uniq tl;
uniq NIL => NIL;
end;
end;
esac;
#
fun free_analysis (gfree, ffree, dictionary)
=
{ fun g (w as (v, m, n), (x, y))
=
case (what_is (dictionary, v))
#
CALLEE (u, csg, csf)
=>
{ gv = add_v (entervar (u, uniqvar csg), m, n, x);
fv = add_v (uniqvar csf, m, n, y);
(gv, fv);
};
FUNCTION { gpfree, fpfree, ... }
=>
( add_v (gpfree, m, n, x),
add_v (fpfree, m, n, y)
);
_ => (merge_v ([w], x), y);
esac;
(fold_backward g ([], ffree) gfree)
->
(ngfree, nffree);
( same_closure_opt (ngfree, dictionary),
nffree
);
};
##########################################################################
# MAIN FUNCTION
#
# This fun is called (only) from
#
#
src/lib/compiler/back/top/main/backend-tophalf-g.pkg #
# where it constitutes one of the phases.
#
fun make_nextcode_closures (fk, f, vl, cl, ce)
=
{
# **************************************************************************
# utility functions that depends on register configurations *
# **************************************************************************
# Get the current register configuration:
#
maxgpregs = mp::num_int_regs;
maxfpregs = mp::num_float_regs - 2; # need 1 or 2 temps
num_csgpregs = mp::num_callee_saves;
num_csfpregs = mp::num_float_callee_saves;
unboxedfloat = mp::unboxed_floats;
untaggedint = mp::untagged_int;
# Check the validity of the callee-save configurations:
#
my (num_csgpregs, num_csfpregs)
=
if (num_csgpregs <= 0)
#
if (num_csfpregs > 0) bug "Wrong CS config 434 - make-nextcode-closures-g.pkg";
else (0, 0);
fi;
else
if (num_csfpregs >= 0) (num_csgpregs, num_csfpregs);
else (num_csgpregs, 0);
fi;
fi;
base_dictionary = empty_dictionary (); # Initialize the base dictionary.
# Find out the nextcode type of an arbitrary program variable
#
fun get_cty v # So "cty" == "nextcode type"? -- CrT
=
case (what_is (base_dictionary, v))
#
VALUE t => t;
_ => ncf::bogus_pointer_type;
esac;
# Check if a variable is a float number:
#
is_flt = if unboxedfloat
#
\\ v = case (get_cty v)
#
ncf::typ::FLOAT64 => TRUE;
_ => FALSE;
esac;
else
\\ _ = FALSE;
fi;
#
fun is_flt3 (v, _, _)
=
is_flt v;
# Check if a variable is of boxed type --- no longer used!
#
# isBoxed3 =
# if untaggedint then
# (\\ (v, _, _) =>
# (case (get_cty v)
# of ncf::typ::FLOAT64 => bug "isBoxed never applied to floats in make-nextcode-closures-g.pkg"
#
| ncf::typ::INT => FALSE
#
| _ => TRUE))
# else
# (\\ (v, _, _) =>
# ((case (get_cty v)
# of INT1t => FALSE
#
| _ => TRUE) except _ => TRUE))
# Check if a variable is an one_word_int:
#
fun is_int1 (v, _, _)
=
case (get_cty v)
#
ncf::typ::INT1 => TRUE;
_ => FALSE;
esac;
# Count the number of GP and FP
# registers needed for a
# list of lvars:
#
fun is_flt_cty ncf::typ::FLOAT64 => unboxedfloat;
is_flt_cty _ => FALSE;
end;
#
fun numgp (m, ncf::typ::FATE ! z) => numgp (m-num_csgpregs - 1, z);
#
numgp (m, x ! z) => if (is_flt_cty x) numgp (m, z);
else numgp (m - 1, z);
fi;
numgp (m, []) => m;
end;
#
fun numfp (m, ncf::typ::FATE ! z) => numfp (m-num_csfpregs, z);
#
numfp (m, x ! z) => if (is_flt_cty x) numfp (m - 1, z);
else numfp (m, z);
fi;
numfp (m, []) => m;
end;
################################################################
# Check the formal arguments of a function and replace the
# fate variable with a set of variables representing
# its callee- save register dictionary variables.
################################################################
adjust_args
=
{ fun adjust1 (args, l, dictionary)
=
fold_backward g (NIL, NIL, NIL, NIL, NULL, dictionary) (zip (args, l))
where
fun g ((a, t), (al, cl, cg, cf, rt, dictionary))
=
if (t == ncf::typ::FATE)
#
w = clone_highcode_codetemp a;
my (csg, clg) = extra_lvar (num_csgpregs, ncf::bogus_pointer_type);
my (csf, clf) = extra_lvar (num_csfpregs, ncf::typ::FLOAT64);
csgv = map ncf::CODETEMP csg;
csfv = map ncf::CODETEMP csf;
dictionary = aug_callee (a, ncf::CODETEMP w, csgv, csfv, dictionary);
nargs = w ! (csg @ csf);
ncl = ncf::typ::FATE ! (clg @ clf);
dictionary = faug_value (nargs, ncl, dictionary);
case rt
NULL => (nargs @ al, ncl @ cl, csgv, csfv, THE a, dictionary);
THE _ => bug "closure/adjustArgs: >1 fate";
esac;
else
( a ! al,
t ! cl,
cg,
cf,
rt,
aug_value (a, t, dictionary)
);
fi;
end;
#
fun adjust2 (args, l, dictionary)
=
fold_backward g (NIL, NIL, NIL, NIL, NULL, dictionary) (zip (args, l))
where
fun g ((a, t), (al, cl, cg, cf, rt, dictionary))
=
( a ! al,
t ! cl,
cg,
cf,
rt,
aug_value (a, t, dictionary)
);
end;
num_csgpregs > 0 ?? adjust1
:: adjust2;
};
#############################################################################
# Calculate the set of free variables and their
# live range for each function naming. # See:
src/lib/compiler/back/top/closures/make-per-function-free-variable-maps.pkg #############################################################################
(mfv::make_per_function_free_variable_maps (fk, f, vl, cl, ce))
->
((fk, f, vl, cl, ce), snum, nfreevars, ekfuns);
# old freevars code, now obsolete, but left here for debugging
# my (ofreevars, _, _) = FreeMap::freemapClose ce
#############################################################################
# makenv: create the dictionaries for functions in a ncf::DEFINE_FUNS.
# here bcsg and bcsf are the current contents of callee-save registers
# bret is the default return fates, sn is the stage number of
# the enclosing function, initDict has the same "whatIs" table as the
# the base_dictionary, however it has the different "whereIs" table.
#############################################################################
#
fun makenv (init_dictionary, namings, bsn, bcsg, bcsf, bret)
=
{
/*** >
#
fun checkfree (v) =
let free = ofreevars v
my { fv=nfree, lv=loopv, size=_} = nfreevars v
nfree = map #1 nfree
if (free != nfree)
then (pr "^^^^ wrong free variable subset ^^^^ \n";
pr "OFree in "; vp v; pr ":"; ilist free;
pr "NFree in "; vp v; pr ":"; ilist nfree;
pr "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ \n")
else ()
case loopv
of NULL => ()
| THE sfree =>
(if subset (sfree, nfree) then ()
else (pr "****wrong free variable subset*** \n";
pr "Free in "; vp v; pr ":"; ilist nfree;
pr "SubFree in "; vp v; pr ":";ilist sfree;
pr "*************************** \n"))
in ()
end
apply checkfree (map #2 namings)
<***/
/*** >
comment (\\() => (pr "BEGINNING MAKENV.\nFunctions: ";
ilist (map #2 namings); pr "Initial dictionary:\n";
printDict initDict; pr "\n"))
comment (\\() => (pr "BASE CALLEE SAVE REGISTERS: ";
vallist bcsg; vallist bcsf; pr "\n"))
<***/
# Partition the function namings
# into different callers_info flavors:
(partition_namings namings)
->
(escape_b, known_b, rec_b, callee_b, kcont_b);
# For the "numCSgpregs = 0" case,
# treat kcontB and calleeB as escapeB:
my (escape_b, callee_b, kcont_b)
=
num_csgpregs > 0 ?? (escape_b, callee_b, kcont_b)
:: (escape_b @ callee_b, [], [] );
escape_v = uniq (map #2 escape_b);
known_v = uniq (map #2 known_b );
#
fun knownlvar3 (v, _, _)
=
member known_v v;
# Check whether the basic
# closure assumptions are
# valid or not:
my (fix_kind, nret)
=
case (escape_b, known_b, callee_b, rec_b, kcont_b) # "escape"=="public"; "known"=="private".
#
([], _,[ ], _,[ ]) => (ncf::PRIVATE_FN, bret );
([],[],[v],[],[_]) => (ncf::PRIVATE_FATE_FN, THE(#2 v));
([],[],[v],[],[ ]) => (ncf::FATE_FN, THE(#2 v));
( _, _,[ ], _,[ ]) => (ncf::PUBLIC_FN, bret );
_ => { pr "^^^ Assumption No.2 is violated in closure phase ^^^\n";
pr "KNOWN namings: "; ilist (map #2 known_b);
pr "ESCAPE namings: "; ilist (map #2 escape_b);
pr "FATE namings: "; ilist (map #2 callee_b);
pr "KNOWN_FATE namings: "; ilist (map #2 kcont_b);
pr "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ \n";
bug "Violating basic closure conventions make-nextcode-closures-g.pkg";
};
esac;
############################################################################
# Initial processing of known functions
############################################################################
/*** >
comment (\\() => (pr "Known functions:"; ilist (map #2 knownB);
pr " "; iKlist (map #1 knownB)))
<***/
/* Get the call graph of all
* known functions in this ncf::DEFINE_FUNS:
*/
known_b
=
map ( \\ (fe as (_, v, _, _, _))
=
{ (nfreevars v) -> { fv=>vn, lv=>lpv, size=>s };
#
(partition knownlvar3 vn) -> (fns, other);
( { v,
fe,
other,
fsz => s,
lpv
},
length fns,
fns
);
}
)
known_b;
# Compute the closure of the call
# graph of the known functions:
#
known_b
=
close_call_graph known_b
where
fun close_call_graph g
=
{ fun get_neighbors l
=
fold_backward
(\\ (( { v, fe, other, fsz, lpv }, _, nbrs), n)
=
if (member3 l v) merge_v (nbrs, n);
else n;
fi
)
l
g;
#
fun traverse ((x, len, nbrs), (l, change))
=
{ nbrs' = get_neighbors nbrs;
len' = length nbrs';
((x, len', nbrs') ! l, change or len!=len');
};
(fold_backward traverse (NIL, FALSE) g)
->
(g', change);
change ?? close_call_graph g'
:: g';
};
end;
# Compute the closure of the
# set of free variables:
#
known_b
=
{ fun gather_nbrs l init
=
fold_backward
(\\ (( { v, other, ... }, _, _), free)
=
case (get_vn (l, v))
#
NULL => free;
THE (m, n)
=>
merge_v (map ( \\ (z, i, j)
=>
( z,
int::min (i, m),
int::max (n, j)
); end
)
other,
free
);
esac
)
init
known_b;
map (\\ ( { v, fe => (k, _, args, cl, body), other, fsz, lpv }, _, fns)
=
{ v,
kind => k,
args,
cl,
body,
lpv,
fsz,
other => gather_nbrs fns other,
fns
}
)
known_b;
};
# See which known function requires a closure, pass 1.
#
my (known_b, recursive_flag)
=
fold_backward
(\\ ((x as { v, kind, args, cl, other, fns, fsz, lpv, body } ), (zz, flag))
=
{ free = remove_v (escape_v, other);
#
callc = (length other) != (length free); # Calls escaping-funs
# If its arguments do not contain
# a return fate, supply one:
#
def_cont
=
case (kind, bret)
#
(ncf::PRIVATE_TAIL_RECURSIVE_FN, THE z)
=>
member3 free z ?? bret
:: NULL; # Issue warnings.
_ => NULL;
esac;
# Find out the true set
# of free variables:
my (fpfree, gpfree) = partition is_flt3 free;
my (gpfree, fpfree) = free_analysis (gpfree, fpfree, init_dictionary);
/*** >
comment (\\() => (pr "*** Current Known Free Variables: ";
iVlist gpfree; pr "\n"))
<***/
# Some free variables must stay
# in registers for ncf::KNOWN_TAIL:
my (rcsg, rcsf) = case def_cont
NULL => ([],[]);
THE k => fetch_csvars (k, #1 fsz, #2 fsz, init_dictionary);
esac;
gpfree = remove_v (rcsg, gpfree);
fpfree = remove_v (rcsf, fpfree);
# The stage number of
# the current function:
sn = snum v;
#
fun deep1 (_, _, n) = (n > sn);
fun deep2 (_, m, n) = (m > sn);
/*** >
comment (\\() => (pr "*** Current Stage number and fun kind: ";
ilist [sn]; ifkind kind; pr "\n"))
<***/
# For recursive functions, always
# spill deeper level free variables:
#
my ((gp_spill, gpfree), (fp_spill, fpfree), nflag)
=
case lpv
#
THE _
=>
{ fun h ((v, _, _), l)
=
case (what_is (init_dictionary, v))
#
(CLOSURE (CLOSURE_REP { closure, ... }))
=>
merge (rmv (v, closure.free), l);
_ => l;
esac;
gpfree = remove_v (fold_backward h [] gpfree, gpfree);
gpfree_part
=
if (length gpfree < num_csgpregs) ([], gpfree);
else partition deep1 gpfree;
fi;
( gpfree_part,
partition deep1 fpfree,
TRUE
);
};
NULL
=>
if (ekfuns v) ( (gpfree, []),
(fpfree, []),
flag
);
else ( partition deep2 gpfree,
partition deep2 fpfree,
flag
);
fi;
esac;
/*** >
comment (\\() => (pr "*** Current Spilled Known Free Variables: ";
iVlist gp_spill; pr "\n"))
<***/
# Find out the register limit for this known function:
gpnmax = maxgpregs;
fpnmax = maxfpregs; # reglimit v
# Does the set of free variables
# fit into FP registers?
#
n = int::min (numfp (maxfpregs - 1, cl), fpnmax) - length (rcsf);
(spill_free (fpfree, n, rcsf, fp_spill))
->
(fpfree, fp_spill);
# Does the set of free variables
# fit into GP registers?
#
m = int::min (numgp (maxgpregs - 1, cl), gpnmax) - length (rcsg);
(spill_free (gpfree, m, rcsg, gp_spill))
->
(gpfree, gp_spill);
( case (gp_spill, fp_spill)
([], [])
=>
(x, gpfree, fpfree, [], [], callc, sn, fns) ! zz;
/*
| ([(z, _, _)],[])
=>
if callc
then
( (x, gpfree, fpfree, gp_spill, [], callc, sn, fns) ! zz)
else ( (x, enter (z, gpfree), fpfree, [],[], FALSE, sn, fns) ! zz)
*/
_ => ( (x, gpfree, fpfree, gp_spill, fp_spill, TRUE, sn, fns) ! zz);
esac,
nflag
);
}
) # fn
([], FALSE)
known_b;
# See which known functions require a closure, pass 2.
my (known_b, gpcollected, fpcollected)
=
fold_backward g ([],[],[]) known_b
where
fun check_nbrs l init
=
fold_backward
(\\ (( { v, ... }, _, _, _, _, callc, _, _), c)
=
c or (callc and (member3 l v))
)
init
known_b;
#
fun g ( ( { kind, v, args, cl, body, fns, fsz, lpv, other },
gpfree,
fpfree,
gp_spill,
fp_spill,
callc,
sn,
zfns
),
(z, gv, fv)
)
=
{ callc = check_nbrs zfns callc;
l = clone_highcode_codetemp v;
( { kind, sn, v, l, args, cl, body, gpfree, fpfree, callc }
!
z,
merge_v (gp_spill, gv),
merge_v (fp_spill, fv)
);
};
end;
############################################################################
# Initial processing of escaping functions
############################################################################
/*** >
comment (\\() => (pr "Escaping functions:"; ilist (map #2 escapeB)))
<***/
# Get the set of free variables
# for escaping functions:
my (escape_b, escape_free)
=
fold_backward g ([],[]) escape_b
where
fun g ((k, v, a, cl, b), (z, c))
=
{ free = .fv (nfreevars v);
l = clone_highcode_codetemp v;
( { kind => k,
v,
l,
args => a,
cl,
body => b
}
!
z,
merge_v (free, c)
);
};
end;
# Get the true set of free variables
# for escaping functions:
#
my (gpfree, fpfree)
=
free_analysis (gpfree, fpfree, init_dictionary)
where
(partition knownlvar3 (remove_v (escape_v, escape_free)))
->
(fns, other);
(partition is_flt3 other)
->
(fpfree, gpfree);
my (gpfree, fpfree)
=
fold_backward
(\\ ( { v, gpfree=>gv, fpfree=>fv, ... }, (x, y))
=
case (get_vn (fns, v))
NULL => (x, y);
THE (m, n)
=>
( add_v (gv, m, n, x),
add_v (fv, m, n, y)
);
esac
)
(gpfree, fpfree)
known_b;
end;
# Here are all free variables that
# ought to be put in the closure:
gp_free = merge_v (gpfree, gpcollected);
fp_free = merge_v (fpfree, fpcollected);
###########################################################################
# Initial processing of callee-save fate functions
###########################################################################
/*** >
comment (\\() => (pr "CS fates:"; ilist (map #2 calleeB);
pr " "; iKlist (map #1 calleeB)))
<***/
# Get the set of free variables
# for fate functions:
my (callee_b, callee_free, gpn, fpn, p_f)
=
{ fun g ( (k, v, a, cl, b), (z, c, gx, fx, pf))
=
{ (nfreevars v)
->
{ fv=>free, lv=>_, size=>(gsz, fsz) };
l = clone_highcode_codetemp v;
sn = snum v;
my (gpn, fpn, pflag)
=
case k
#
ncf::PRIVATE_FATE_FN
=>
if (gsz > 0)
#
(0, 0, FALSE); # A temporary gross hack XXX BUGGO FIXME.
else
x = numgp (maxgpregs - 1, ncf::typ::FATE ! cl);
y = numfp (maxfpregs - 1, ncf::typ::FATE ! cl);
( int::min (x, gx),
int::min (y, fx),
FALSE
);
fi;
_ => (0, 0, sn == bsn+1);
esac;
( { kind => k,
sn,
v,
l,
args => a,
cl,
body => b
}
!
z,
merge_v (free, c),
int::min (gpn, gx),
int::min (fpn, fx),
pflag
);
};
case callee_b
#
[] => ([],[], 0, 0, TRUE);
_ => fold_backward g ([],[], maxgpregs, maxfpregs, TRUE) callee_b;
esac;
};
# Get the true set of free variables
# for fate functions:
#
my (fpcallee, gpcallee) = partition is_flt3 callee_free;
my (gpcallee, fpcallee) = free_analysis (gpcallee, fpcallee, init_dictionary);
# Get all sharable closures from
# the enclosing dictionary:
#
my (gpclist, fpclist)
=
fetch_closures (init_dictionary, lives, fix_kind)
where
lives = merge ( map #1 gpcallee,
map #1 gp_free
);
lives = case (known_b, escape_b)
#
( [ { gpfree => gv, ... } ], [])
=>
merge (gv, lives);
_ => lives;
esac;
end;
# Initialize the callee-save register default:
#
safev = merge ( uniq (map #1 gpclist),
uniq (map #1 fpclist)
);
my (gpbase, gp_src) = make_base (bcsg, merge (safev, map #1 gpcallee), gpn);
my (fpbase, fp_src) = make_base (bcsf, map #1 fpcallee, fpn);
# Thinning the set of free variables
# based on each's contents:
my cclist # For user function, be more conservative
=
case callee_b
#
[] => map (\\ (v, cr) = (v, fetch_free (v, cr, 2))) (fpclist @ gpclist);
_ => map (\\ (v, CLOSURE_REP { closure => { free, ... }, ... }) = (v, free)) (fpclist @ gpclist);
esac;
my (gpcallee, fpcallee)
=
shorten_free (gpcallee, fpcallee, cclist);
my (gp_free, fp_free)
=
recursive_flag ?? (gp_free, fp_free)
:: shorten_free (gp_free, fp_free, cclist);
###########################################################################
# Targeting callee-save registers for fate functions
###########################################################################
# Decide which variables to put
# into FP callee-save registers:
my (gp_spill, fp_spill, fpbase)
=
{ numv = length fpcallee;
numr = num_csfpregs + fpn;
if (numv <= numr)
#
fpv = map #1 fpcallee;
p = if p_f numr-numv; else 0;fi;
my (fpbase, fpv, _) = modify_base (fpbase, fpv, p);
nbase = fill_base (fpbase, fpv);
([], [], nbase);
else
# Need spill:
my (gpfree, fpcallee) = thin_fp_free (fpcallee, fpclist);
numv = length fpcallee;
if (numv <= numr)
#
fpv = map #1 fpcallee;
p = if p_f numr-numv; else 0;fi;
my (fpbase, fpv, _) = modify_base (fpbase, fpv, p);
nbase = fill_base (fpbase, fpv);
(gpfree, [], nbase);
else
fpfree = sortlud2 (fpcallee, fp_src);
my (cand, rest) = partvnum (fpfree, numr);
my (nbase, ncand, _) = modify_base (fpbase, cand, 0);
nbase = fill_base (nbase, ncand);
(gpfree, uniq_v rest, nbase);
fi;
fi;
};
# INT1: here is a place to filter out all the variables with INT1 types,
# they have to be put into closure (gp_spill), because by default, callee-save
# registers always contain pointer values.
(partition is_int1 gpcallee) -> (i32gpcallee, gpcallee);
(partition is_int1 gp_free ) -> (i32gp_free, gp_free);
# Collect all the FP free variables and
# build a closure if necessary:
allfp_free = merge_v (fp_spill, fp_free);
my (gp_spill, gp_free, fpc_info)
=
case allfp_free
#
[] => (gp_spill, gp_free, NULL);
#
_ => { my (gpextra, ufree) = thin_fp_free (allfp_free, fpclist);
my (gpextra, fpc)
=
case ufree
#
[] => (gpextra, NULL);
#
((_, m, n) ! r)
=>
{ fun h ((_, x, y), (i, j))
=
(int::min (x, i), int::max (y, j));
my (m, n) = fold_backward h (m, n) r;
cname = make_closure_codetemp ();
gpextra
=
merge_v ( [ (cname, m, n) ], gpextra);
( gpextra,
THE (cname, ufree)
);
};
esac;
case fix_kind
#
(ncf::FATE_FN
| ncf::PRIVATE_FATE_FN)
=>
( merge_v (gpextra, gp_spill),
gp_free,
fpc
);
_ => ( gp_spill,
merge_v (gpextra, gp_free),
fpc
);
esac;
};
esac;
# Here are free variables that should be
# put in GP callee-save registers by
# convention: gp_spill must not contain
# any one_word_int variables !
gpcallee = merge_v (gp_spill, gpcallee);
my (gpcallee, fpc_info)
=
case (i32gpcallee, fpc_info)
#
([], _)
=>
(gpcallee, fpc_info);
#
((_, m, n) ! r, NULL)
=>
{ fun h ((_, x, y), (i, j))
=
(int::min (x, i), int::max (y, j));
my (m, n) = fold_backward h (m, n) r;
cname = make_closure_codetemp();
( merge_v ( [ (cname, m, n) ],
gpcallee
),
THE (cname, i32gpcallee)
);
};
#
(vs, THE (cname, ufree))
=>
( gpcallee,
THE (cname, merge_v (vs, ufree))
);
esac;
/*
| (_, THE (cname, ufree))
=>
bug "unimplemented one_word_int + float (nclosure.2)"
*/
# If gp_spill is not null,
# there must be an empty
# position in gpbase:
my (gp_spill, gpbase)
=
{ numv = length gpcallee;
numr = num_csgpregs + gpn;
if (numv <= numr)
#
gpv = map #1 gpcallee;
p = if p_f numr - numv;
else 0;fi;
(modify_base (gpbase, gpv, p))
->
(gpbase, gpv, _);
nbase = fill_base (gpbase, gpv);
([], nbase);
else
gpcallee = thin_gp_free (gpcallee, gpclist);
numv = length gpcallee;
if (numv <= numr)
#
gpv = map #1 gpcallee;
p = if p_f numr - numv;
else 0;
fi;
(modify_base (gpbase, gpv, p))
->
(gpbase, gpv, _);
nbase = fill_base (gpbase, gpv);
([], nbase);
else
gpfree = sortlud2 (gpcallee, gp_src);
(partvnum (gpfree, numr - 1))
->
(cand, rest);
(modify_base (gpbase, cand, 0))
->
(nbase, ncand, _);
(partition_to_null nbase)
->
(nbhd, nbtl);
nbtl = fill_base (nbtl, ncand);
(uniq_v rest, nbhd @ nbtl);
fi;
fi;
};
###########################################################################
# Building the closures for all namings in this ncf::DEFINE_FUNS
###########################################################################
# Collect all GP free variables that should be put in closures.
# Assumption: gp_spill does not contain any Int1s; they should
# not be put into gpcallee anyway.
allgp_free = merge_v (gp_spill, gp_free);
unboxed_free = i32gp_free;
# Filter out all unboxed-values.
# INT1: here is the place to filter out all 32-bit integers,
# put them into unboxedFree, then you have to find a way to put both
# 32-bit integers and unboxed float numbers in the same record.
# Currently, I use ncf::rk::FLOAT64_BLOCK to denote this kind of record_kind,
# you might want to put all floats ahead of all 32-bit ints.
# my (allgpFree, unboxedFree) = partition isBoxed3 allgpFree
my (allgp_free, fpc_info)
=
case (fpc_info, unboxed_free)
#
(NULL, []) => (allgp_free, fpc_info);
#
(NULL, (_, m, n) ! r)
=>
{ c = make_closure_codetemp();
#
fun h ((_, x, y), (i, j))
=
(int::min (x, i), int::max (y, j));
my (m, n) = fold_backward h (m, n) r;
( merge_v ( [ (c, m, n) ], allgp_free ),
THE (c, unboxed_free)
);
};
(THE (c, a), r)
=>
(allgp_free, THE (c, merge_v (a, r)));
esac;
# Actually building the closure for unboxed values:
#
my (fphdr, dictionary, nframes)
=
case fpc_info
#
NULL => (\\ ce = ce, init_dictionary,[]);
#
THE (c, a)
=>
{ (partition is_int1 a)
->
(int1a, a);
#
closure_unboxed (c, int1a, a, fix_kind, init_dictionary);
};
esac;
# Sharing with the enclosing closures if possible:
#
my (allgp_free, ccl) # For recursive function, be more conservative
=
if recursive_flag (thin_all (allgp_free, cclist, bsn), cclist);
else (thin_gp_free (allgp_free, gpclist), []);
fi;
# Actually building the closure for all GP (or boxed) values:
#
my (closure_info, closure_name, dictionary, gphdr, nframes)
=
case (escape_b, allgp_free)
#
([], [])
=>
(NULL, NULL, dictionary, fphdr, nframes);
([], [ (v, _, _) ])
=>
(NULL, THE v, dictionary, fphdr, nframes);
_ =>
{ fns = map (\\ { v, l, ... } = (v, l)) escape_b;
cn = make_closure_codetemp ();
(closure_boxed (cn, fns, allgp_free, fix_kind, ccl, dictionary))
->
(header, dictionary, cr, nf);
( THE cr,
THE cn,
dictionary,
fphdr o header,
nf @ nframes
);
};
esac;
###########################################################################
# Final construction of the dictionary for each known function:
###########################################################################
# Add new known functions to the dictionary (side-efffect)
nenv
=
case closure_name
#
NULL
=>
fold_backward
(\\ ( { v, l, gpfree, fpfree, ... }, dictionary)
=
aug_known (v, l, gpfree, fpfree, dictionary)
)
dictionary
known_b;
#
THE cname
=>
fold_backward
(\\ ( { v, l, gpfree, fpfree, callc, ... }, dictionary)
=
if callc aug_known (v, l, enter (cname, gpfree), fpfree, dictionary);
else aug_known (v, l, gpfree, fpfree, dictionary);
fi
)
dictionary
known_b;
esac;
my known_frags: Frags
=
fold_backward g [] known_b
where
fun g ( { kind, sn, v, l, args, cl, body, gpfree, fpfree, callc }, z)
=
{ dictionary = base_dictionary; # Empty whereIs map but same whatMap as nenv
dictionary = fold_backward augvar dictionary gpfree;
dictionary = fold_backward augvar dictionary fpfree;
my (ngpfree, dictionary)
=
case (callc, closure_name)
#
(FALSE, _)
=>
{ inc coc::known_function;
(gpfree, dictionary);
};
#
(TRUE, THE cn)
=>
{ inc coc::known_cl_function;
( enter (cn, gpfree),
augvar (cn, dictionary)
);
};
#
(TRUE, NULL)
=>
bug "unexpected 23324 in closure";
esac;
(adjust_args (args, cl, dictionary))
->
(nargs, ncl, ncsg, ncsf, nret, dictionary);
nargs = nargs @ ngpfree @ fpfree;
ncl = ncl @ (map get_cty ngpfree) @ (map get_cty fpfree);
/*** >
comment (\\ () => (pr "\nDictionary in known ";
vp v; pr ":\n"; printDict dictionary))
<***/
case nret
#
NULL => ((ncf::PRIVATE_FN, l, nargs, ncl, body, dictionary, sn, bcsg, bcsf, bret) ! z);
THE _ => ((ncf::PRIVATE_FN, l, nargs, ncl, body, dictionary, sn, ncsg, ncsf, nret) ! z);
esac;
};
end;
###########################################################################
# Final construction of the dictionary for each escaping function
###########################################################################
# The what_map in nenv is side-effected
# with new escape namings:
#
my escape_frags: Frags
=
case (closure_info, escape_b)
#
(_, []) => [];
#
(NULL, _) => bug "unexpected 23422 in closure";
#
(THE cr, _)
=>
formap f escape_b
where
dictionary = base_dictionary; # Empty whereIs map but same whatMap as nenv
#
fun f ( { kind, v, l, args, cl, body }, i)
=
{ my_cname = v; # My closure name
dictionary = aug_esc_fun (my_cname, i, cr, dictionary);
(adjust_args (args, cl, dictionary))
->
(nargs, ncl, ncsg, ncsf, nret, dictionary);
nargs = issue_highcode_codetemp() ! my_cname ! nargs;
ncl = ncf::bogus_pointer_type ! ncf::bogus_pointer_type ! ncl;
sn = snum v;
/*** >
comment (\\ () => (pr "\nDictionary in escaping ";
vp v; pr ":\n";printDict dictionary))
<***/
inc coc::escape_function; # nret must not be NULL
case nret
#
THE _ => (kind, l, nargs, ncl, body, dictionary, sn, ncsg, ncsf, nret);
NULL => bug "no fate in escapefun in make-nextcode-closures-g.pkg";
esac;
};
end;
esac;
###########################################################################
# Final construction of the dictionary for each callee-save fate
###########################################################################
# The what_map in nenv is side-effected
# with new callee namings:
#
my (nenv, callee_frags: Frags)
=
case callee_b
#
[] => (nenv, []);
#
_ =>
{ gpbase
=
case closure_name
#
NULL => gpbase;
THE _ => fill_csregs (gpbase, closure_name);
esac;
ncsg = map (\\ (THE v) => ncf::CODETEMP v; NULL => ncf::INT 0; end) gpbase;
ncsf = map (\\ (THE v) => ncf::CODETEMP v; NULL => ncf::TRUEVOID ; end) fpbase; # This is the only place in the codebase where ncf::TRUEVOID is introduced.
(split_dictionary (nenv, member (freev_csregs (gpbase, nenv))))
->
(benv, nenv);
#
fun g ( { kind, sn, v, l, args, cl, body }, z)
=
{ dictionary = install_frames (nframes, benv);
my (nk, dictionary, nargs, ncl, csg, csf)
=
case kind
#
ncf::FATE_FN
=>
{ dictionary = aug_callee (v, ncf::LABEL l, ncsg, ncsf, dictionary);
(fill_csformals (gpbase, fpbase, dictionary, get_cty))
->
(dictionary, a, c);
( ncf::FATE_FN,
dictionary,
(issue_highcode_codetemp ()) ! (a @ args),
ncf::bogus_pointer_type ! (c @ cl),
ncsg,
ncsf
);
};
ncf::PRIVATE_FATE_FN
=>
{ (vars_csregs (gpbase, fpbase, dictionary))
->
(gfv, ffv, dictionary);
csg = cuttail (gpn, ncsg);
csf = cuttail (fpn, ncsf);
dictionary = aug_kcont (v, l, gfv, ffv, csg, csf, dictionary);
gcl = map get_cty gfv;
fcl = map (\\ _ = ncf::typ::FLOAT64) ffv;
( ncf::PRIVATE_FN,
dictionary,
args @ gfv @ ffv,
cl @ gcl @ fcl,
csg,
csf
);
};
_ => bug "callee_frags in make-nextcode-closures-g.pkg 748";
esac;
dictionary = faug_value (args, cl, dictionary);
/*** >
comment (\\ () =>
(pr "\nDictionary in callee-save fate ";
vp v; pr ":\n"; printDict dictionary))
<***/
inc coc::callee_function;
( nk,
l,
nargs,
ncl,
body,
dictionary,
sn,
csg,
csf,
bret
)
!
z;
}; # fun g
( nenv,
fold_backward g [] callee_b
);
};
esac;
frags = escape_frags @ known_frags @ callee_frags;
/*** >
comment (\\ () => (pr "\nDictionary after ncf::DEFINE_FUNS:\n";
printDict nenv; pr "MAKENV DONE.\n\n"));
<***/
( gphdr,
frags,
nenv,
nret
);
}; # function makenv
###########################################################################
# MAIN LOOP (closefix and close)
###########################################################################
#
fun closefix (
fk,
f,
vl,
cl,
ce,
dictionary,
sn,
csg,
csf,
ret
)
=
( fk,
f,
vl,
cl,
close (
ce,
dictionary,
sn,
csg,
csf,
ret
)
)
except
LOOKUP (v, dictionary)
=
{ pr "LOOKUP FAILS on ";
vp v;
pr "\nin dictionary:\n";
print_dictionary dictionary;
pr "\nin function:\n";
prettyprint_nextcode::print_nextcode_expression ce;
bug "Lookup failure in nextcode/make-nextcode-closures-g.pkg";
}
also
fun close (ce, dictionary, sn, csg, csf, ret)
=
case ce
#
ncf::DEFINE_FUNS { funs, next }
=>
{ (makenv (dictionary, funs, sn, csg, csf, ret))
->
(header, frags, nenv, nret);
ncf::DEFINE_FUNS
{
funs => map closefix frags,
next => header (close (next, nenv, sn, csg, csf, nret))
};
};
ncf::TAIL_CALL { fn, args }
=>
{ chunk
=
case fn
ncf::CODETEMP v => what_is (dictionary, v);
_ => VALUE ncf::bogus_pointer_type;
esac;
case chunk
#
CLOSURE (CLOSURE_REP { offset, closure => { functions, ... } })
=>
{ (fix_access ( [fn], dictionary)) -> (dictionary, h);
(fix_args (args, dictionary)) -> (nargs, dictionary, nh);
(dispose_frames dictionary ) -> (dictionary, dh);
(list::nth (functions, offset)) -> (_, label);
call = ncf::TAIL_CALL
{
fn => ncf::LABEL label,
args => ncf::LABEL label ! fn ! nargs
};
if (not *coc::allocprof)
#
h (nh (dh call));
else
h (nh (dh case args
[_] => prof_cntk_call call;
_ => prof_stdk_call call;
esac
)
);
fi;
};
FUNCTION { label, gpfree, fpfree, csdef }
=>
{ (map ncf::CODETEMP (gpfree @ fpfree)) -> free;
(fix_args (args @ free, dictionary)) -> (args, dictionary, h);
(dispose_frames dictionary) -> (dictionary, nh);
(ncf::TAIL_CALL { fn => ncf::LABEL(label), args }) -> call;
if (not *coc::allocprof)
#
h (nh call);
else
case csdef
#
NULL => h (nh (prof_known_call call));
_ => h (nh (prof_cscntk_call call));
esac;
fi;
};
CALLEE (label, ncsg, ncsf)
=>
{ (ncsg @ ncsf @ args) -> nargs;
(fix_access (label ! nargs, dictionary)) -> (dictionary, h);
(dispose_frames dictionary) -> (dictionary, nh);
(ncf::TAIL_CALL { fn => label,
args => label ! nargs }) -> call;
if (not *coc::allocprof)
#
h (nh call);
else
case label
#
ncf::LABEL _ => h (nh (prof_cscntk_call call));
_ => h (nh (prof_cscnt_call call));
esac;
fi;
};
VALUE t
=>
{ (fix_access ([fn], dictionary)) -> (dictionary, h);
(fix_args (args, dictionary)) -> (nargs, dictionary, nh);
(dispose_frames dictionary) -> (dictionary, dh);
l = issue_highcode_codetemp ();
call = ncf::GET_FIELD_I
{
i => 0,
record => fn,
to_temp => l,
type => t,
next => (ncf::TAIL_CALL { fn => ncf::CODETEMP(l),
args => ncf::CODETEMP(l) ! fn ! nargs
}
)
};
if (not *coc::allocprof) h (nh (dh ( call)));
else h (nh (dh (prof_std_call call)));
fi;
};
esac;
};
ncf::JUMPTABLE { i, xvar, nexts }
=>
{ (fix_access ([i], dictionary))
->
(dictionary, header);
header (
ncf::JUMPTABLE {
i,
xvar,
nexts => map (\\ c = close (c, dictionary, sn, csg, csf, ret)) nexts
}
);
};
ncf::DEFINE_RECORD { kind as ncf::rk::FLOAT64_BLOCK, fields, to_temp, next }
=>
{ (fix_access (map #1 fields, dictionary))
->
(dictionary, header);
dictionary = aug_value (to_temp, ncf::bogus_pointer_type, dictionary);
header (
ncf::DEFINE_RECORD {
kind,
fields,
to_temp,
next => close (next, dictionary, sn, csg, csf, ret)
}
);
};
ncf::DEFINE_RECORD { kind, fields, to_temp, next }
=>
{ (record_elements (kind, fields, to_temp, dictionary))
->
(header, dictionary);
nc = header (
close (
next,
aug_value (to_temp, ncf::bogus_pointer_type, dictionary),
sn,
csg,
csf,
ret
)
);
if (not *coc::allocprof) nc;
else prof_record (length fields) nc;
fi;
};
ncf::GET_FIELD_I { i, record, to_temp, type, next }
=>
{ (fix_access ([record], dictionary))
->
(dictionary, header);
next = close ( next,
aug_value (to_temp, type, dictionary),
sn,
csg,
csf,
ret
);
header (ncf::GET_FIELD_I { i, record, to_temp, type, next });
};
ncf::GET_ADDRESS_OF_FIELD_I { i, record, to_temp, next }
=>
bug "GET_ADDRESS_OF_FIELD_I in pre-closure in nextcode/make-nextcode-closures-g.pkg";
ncf::IF_THEN_ELSE { op, args, xvar, then_next, else_next }
=>
{ (fix_access (args, dictionary)) -> (dictionary, header);
then_next = close (then_next, dictionary, sn, csg, csf, ret);
else_next = close (else_next, dictionary, sn, csg, csf, ret);
header (ncf::IF_THEN_ELSE { op, args, xvar, then_next, else_next });
};
ncf::STORE_TO_RAM { op, args, next }
=>
{ (fix_access (args, dictionary))
->
(dictionary, header);
next = close (next, dictionary, sn, csg, csf, ret);
header (ncf::STORE_TO_RAM { op, args, next });
};
ncf::FETCH_FROM_RAM { op, args, to_temp, type, next }
=>
{ (fix_access (args, dictionary))
->
(dictionary, header);
next = close (
next,
aug_value (to_temp, type, dictionary),
sn,
csg,
csf,
ret
);
header (ncf::FETCH_FROM_RAM { op, args, to_temp, type, next });
};
ncf::ARITH { op, args, to_temp, type, next }
=>
{ (fix_access (args, dictionary))
->
(dictionary, header);
next = close (
next,
aug_value (to_temp, type, dictionary),
sn,
csg,
csf,
ret
);
header (ncf::ARITH { op, args, to_temp, type, next });
};
ncf::PURE { op, args, to_temp, type, next }
=>
{ (fix_access (args, dictionary))
->
(dictionary, header);
next = close (
next,
aug_value (to_temp, type, dictionary),
sn,
csg,
csf,
ret
);
header (ncf::PURE { op, args, to_temp, type, next });
};
ncf::RAW_C_CALL { kind, cfun_name, cfun_type, args, to_ttemps, next }
=>
{ (fix_access (args, dictionary))
->
(dictionary, header);
next = close (
next,
fold_forward
(\\ ((w, t), dictionary)
=
aug_value (w, t, dictionary)
)
dictionary
to_ttemps,
sn,
csg,
csf,
ret
);
header (ncf::RAW_C_CALL { kind, cfun_name, cfun_type, args, to_ttemps, next });
};
esac;
############################################################################
# Calling the "close" on the nextcode expression with proper initializations
#
nfe = { if *coc::static_closure_size_profiling sprof::initfk (); fi;
#
(adjust_args (vl, cl, base_dictionary))
->
(nvl, ncl, csg, csf, ret, dictionary);
dictionary = aug_value (f, ncf::bogus_pointer_type, dictionary);
nce = close (
ce,
dictionary,
snum f,
csg,
csf,
ret
);
( fk,
issue_highcode_codetemp (),
issue_highcode_codetemp () ! f ! nvl,
ncf::bogus_pointer_type ! ncf::bogus_pointer_type ! ncl,
nce
);
};
# Temporary hack: measuring static XXX BUGGO FIXME
# allocation sizes of closures.
# Previous calls to incfk and initfk
# are also part of this hack.
#
if *coc::static_closure_size_profiling
#
sprof::reportfk ();
fi;
un_rebind::unrebind nfe;
}; # fun make_nextcode_closures
}; # generic package make_nextcode_closures_g
end; # stipulate