3#include <fe/worklist.h>
10 auto shared = fe::Vector<std::pair<Lam*, const Def*>>();
11 for (
const auto& [lam, chain] : escape_.chains()) {
12 for (
size_t i = 0, n = chain.ops.size(); i != n; ++i) {
13 auto alloc = chain.ops[i];
15 if (escape_.confined(
alloc)) {
16 auto last = fe::lookup(chain.last,
alloc);
17 auto j = (last ? *last : i) + 1;
18 frees_[j < n ? chain.ops[j] : chain.tail].emplace_back(
alloc);
20 shared.emplace_back(lam,
alloc);
25 if (!shared.empty()) {
27 for (
auto [lam, _] : shared)
30 auto roots = fe::Vector<Def*>();
31 for (
auto lam : homes)
32 if (std::ranges::none_of(homes, [lam](
Lam* home) {
return home->
nests(lam); })) roots.emplace_back(lam);
35 mentions(nest.root());
36 for (
auto [lam,
alloc] : shared)
37 place(nest[lam], nest[lam],
alloc);
40 log().d(
"free_locals: frees before {} ops, on {} edges", frees_.size(), edges_.size());
47 if (
auto i = escape_.
chains().find(lam); i != escape_.
chains().end())
48 for (
auto [
alloc, _] : i->second.last)
51 const auto& sub = mentions(child);
52 res.insert(sub.begin(), sub.end());
54 return mentions_[node] = std::move(res);
58 if (
auto i = reach_.find(node); i != reach_.end())
return i->second;
61 auto todo = fe::BFSWorklist<GIDSet<const Nest::Node*>>();
63 while (!
todo.empty()) {
65 const auto& sub = mentions_.at(s);
66 res.insert(sub.begin(), sub.end());
67 todo.push(s->sibl_deps().nodes());
69 return reach_[node] = std::move(res);
72void FreeLocals::place(
const Nest::Node* home,
const Nest::Node* node,
const Def*
alloc) {
73 for (
auto child : node->children().nodes())
74 place(home, child,
alloc);
76 auto lam = node->mut()->isa_mut<Lam>();
77 if (!lam || escape_.
escaping(lam) || (node != home && !live(home, node,
alloc)))
return;
78 auto i = escape_.
chains().find(lam);
79 if (i == escape_.
chains().end())
return;
81 const auto& chain = i->second;
83 for (
auto target : chain.targets)
84 if (dead(home, target,
alloc)) deads.emplace_back(target);
86 if (deads.size() == chain.targets.size())
87 frees_[chain.tail].emplace_back(
alloc);
89 for (
auto target : deads)
90 edges_[chain.tail][target].emplace_back(
alloc);
93bool FreeLocals::live(
const Nest::Node* home,
const Nest::Node* node,
const Def*
alloc) {
94 if (mentions_.at(node).contains(
alloc))
return true;
95 for (
auto x = node; x != home; x = x->inest())
100bool FreeLocals::dead(
const Nest::Node* home,
const Def* target,
const Def*
alloc) {
101 auto scope = home->mut();
102 if (!scope->nests(target))
return true;
103 if (
auto lam = target->isa_mut<Lam>())
return !live(home, home->nest()[lam],
alloc);
108 auto allocs = fe::lookup(frees_, app);
109 auto edges = fe::lookup(edges_, app);
114 if (allocs && !
mem)
log().d(
"free_locals: `{}` threads no memory, leaking {} allocations", app, allocs->size());
119 auto arg = app->targ(i);
120 return Pi::isa_cn(arg->type()) ? rewrite_target(arg, *edges) : new_arg->proj(app->num_targs(), i);
122 return w.app(rewrite_target(app->
callee(), *edges), w.tuple(
args));
133 for (
auto alloc : allocs)
140 if (
auto allocs = fe::lookup(edges, target))
return trampoline(
rewrite(target), *allocs);
141 if (
auto tuple = target->isa<
Tuple>())
142 return w.tuple(
DefVec(tuple->num_ops(), [&](
size_t i) { return rewrite_target(tuple->op(i), edges); }));
143 if (
auto sw =
Switch(target))
return w.extract(rewrite_target(sw.tuple(), edges),
rewrite(sw.index()));
147const Def* FreeLocals::trampoline(
const Def* cont,
const DefVec& allocs) {
149 auto lam = w.mut_con(
Pi::isa_cn(cont->type())->
dom())->set(
"free_then");
151 auto mem = std::ranges::find_if(
args, [](
const Def* arg) {
return isa_mem(arg); });
152 if (mem ==
args.end()) {
153 log().d(
"free_locals: `{}` takes no memory, leaking {} allocations", cont, allocs.size());
156 *mem = free(*mem, allocs);
157 lam->app(
false, cont,
args);
const Def * callee() const
nat_t num_targs() const noexcept
T * isa_mut() const
If this is mutable, it will cast constness away and perform a dynamic_cast to T.
auto projs(Projector auto f) const
Splits this Def via Def::projections into an Array (if A == std::dynamic_extent) or std::array (other...
bool nests(Def *mut)
Does this nest mut?
const SiblDeps & sibl_deps() const
Def * mut() const
The mutable capsulated in this Node or nullptr, if it's a virtual root comprising several Nodes.
const Children & children() const
const fe::Log & log() const
const fe::Vector< std::string > & args()
Command-line arguments passed to this Phase's plugin via -X <plugin>:<arg>.
static const Pi * isa_cn(const Def *d)
virtual bool analyze()
Runs the optional pre-analysis on old_world(), typically to a fixed point, before rewriting begins.
World & new_world()
Create new Defs into this.
World & old_world()
Get old Defs from here.
const Def * rewrite_imm_App(const App *) override
virtual const Def * rewrite(const Def *)
Data constructor for a Sigma.
const Def * call(const Def *callee, auto &&arg, Args &&... args)
const Def * tuple(Defs ops)
static bool allocates(const App *app)
const LamMap< Chain > & chains() const
bool escaping(Lam *lam) const
May lam run at any time, as it is used as a value or jumped to by an escaping Lam?
const Def * rewrite_imm_App(const App *) final
bool analyze() final
Runs the optional pre-analysis on old_world(), typically to a fixed point, before rewriting begins.
const Def * mem_def(const Def *def)
Returns the (first) element of type mem.M a from the given tuple.
const App * isa_mem(const Def *def)
If def is a mem.M-typed value, yields its memory type mem.M a; otherwise nullptr.
fe::View< const Def * > Defs
GIDMap< const Def *, To > DefMap
fe::Vector< const Def * > DefVec
GIDSet< const Def * > DefSet