5#include <fe/container.h>
6#include <fe/worklist.h>
10using namespace std::literals;
16bool is_memop_res(
const Def* fd) {
22DefSet free_defs(
const Nest& nest) {
24 auto queue = fe::BFSWorklist<DefSet>{nest.root()->mut()};
26 while (!queue.empty()) {
27 for (
auto op : queue.pop()->deps()) {
28 if (
op->is_closed())
continue;
29 if (nest.contains(op))
45void FreeDefAna::classify(
Node* node,
const Def* fd,
bool& spawned_pred, NodeQueue& worklist) {
47 if (fd->is_closed())
return;
50 if (var != lam->ret_var()) node->add_fvs(fd);
52 node->add_fvs(free_bb);
53 }
else if (
auto pred = fd->isa_mut()) {
55 if (pred != node->mut) {
56 auto [pnode, inserted] = build_node(pred, worklist);
57 node->preds.emplace_back(pnode);
58 pnode->succs.emplace_back(node);
59 spawned_pred |= inserted;
61 }
else if (fd->local_vars() && !fd->isa<Tuple>()) {
64 }
else if (is_memop_res(fd)) {
67 for (
auto op : fd->ops())
68 classify(node, op, spawned_pred, worklist);
72std::pair<FreeDefAna::Node*, bool> FreeDefAna::build_node(Def* mut, NodeQueue& worklist) {
73 auto [p, inserted] = lam2node_.emplace(mut,
nullptr);
74 if (!inserted)
return {p->second.get(),
false};
75 world().log().d(
"FVA: create node {}", mut);
77 p->second = std::make_unique<Node>(mut);
78 auto node = p->second.get();
79 bool spawned_pred =
false;
80 for (
auto fd : free_defs(
Nest(mut)))
81 classify(node, fd, spawned_pred, worklist);
86 world().log().d(
"FVA: init {}", mut);
91void FreeDefAna::propagate(NodeQueue& worklist) {
92 while (!worklist.empty()) {
93 auto node = fe::pop(worklist);
94 if (is_done(node))
continue;
95 auto changed = is_bot(node);
97 for (
auto pred : node->preds)
98 for (
auto pfv : pred->fvs)
99 changed |= node->add_fvs(pfv).second;
101 for (
auto succ : node->succs)
107 auto worklist = NodeQueue();
108 auto [node, _] = build_node(lam, worklist);
109 if (!is_done(node)) {
122 while (!body_worklist_.empty()) {
123 auto fn = body_worklist_.front();
124 body_worklist_.pop();
125 auto& stub = closures_.at(fn);
128 for (
auto fv : stub.fvs)
129 vars = vs.merge(vars, fv->free_vars());
152 auto stub = make_stub(old_lam);
156 auto env = w.tuple(
DefVec(stub.fvs.size(), [&](
auto i) { return rewrite(stub.fvs[i]); }));
157 auto closure =
clos_pack(env, stub.fn, clos_ty);
158 log().d(
"pack {} → {}: {}", old_lam, closure, clos_ty);
159 return map(old_lam, closure);
164 if (
auto handled = rewrite_anno(a))
return handled;
168 if (new_callee->type()->isa<
Sigma>())
return clos_apply(new_callee, new_arg);
178 if (
auto ret_lam = a->arg()->isa_mut<
Lam>()) {
179 auto new_doms =
DefVec(ret_lam->num_doms(),
180 [&, nd = ret_lam->num_doms()](
auto i) { return rewrite(ret_lam->dom(nd, i)); });
181 auto new_lam = w.mut_lam(w.cn(new_doms))->set(ret_lam->dbg_key());
182 map(ret_lam, new_lam);
183 if (ret_lam->is_set()) new_lam->set(
rewrite(ret_lam->filter()),
rewrite(ret_lam->body()));
191 auto bb_lam = a->arg()->isa_mut<
Lam>();
193 auto stub = make_stub({}, bb_lam);
199 default:
return nullptr;
210 auto new_fn = make_stub(lam).fn;
212 return new_fn->var(new_fn->num_vars(), new_idx);
217const Pi* ClosConv::rewrite_ret_cn(
const Pi* pi) {
222const Pi* ClosConv::rewrite_telescope_cn(
const Pi* pi) {
225 if (
auto new_sig =
lookup(sig))
return w.cn(new_sig);
231 for (
size_t i = 0, e = sig->num_ops(); i != e; ++i)
232 new_sig->set(i, i == ret ? rewrite_ret_cn(sig->op(i)->as<
Pi>()) :
rewrite(sig->op(i)));
233 return w.cn(
seal(sig, new_sig));
236const Def* ClosConv::clos_type_of(
const Pi* pi,
const Def* env_type) {
238 if (
auto i = glob_muts_.find(pi); i != glob_muts_.end())
return i->second;
241 auto new_doms =
DefVec(nd, [&](
auto i) {
246 glob_muts_.emplace(pi, ct);
247 log().d(
"closure type: {} → {} (pretyped)", pi, ct);
249 log().d(
"closure type: {} → {} (env = {})", pi, ct, env_type);
254ClosConv::Stub ClosConv::make_stub(
const DefSet& fvs, Lam* old_lam) {
258 auto fv_vec =
DefVec(fvs.begin(), fvs.end());
259 std::ranges::sort(fv_vec, GIDLt<const Def*>());
261 auto new_fn_type = clos_type_of(old_lam->type(), env_type)->as<
Pi>();
262 auto new_fn =
w.mut_lam(new_fn_type)->set(old_lam->dbg_key());
264 if (!old_lam->is_rewritable()) {
268 auto new_ext_lam =
w.mut_lam(new_ext_type)->set(old_lam->dbg_key());
269 log().d(
"wrap external lam {} → stub {}, external {}", old_lam, new_fn, new_ext_lam);
270 if (old_lam->is_set()) {
271 if (old_lam->is_external()) new_ext_lam->externalize();
272 auto env =
w.tuple(
DefVec(fv_vec.size(), [&](
auto i) { return rewrite(fv_vec[i]); }));
273 new_ext_lam->app(
false, new_fn,
clos_insert_env(ep, env, new_ext_lam->var()));
280 log().d(
"stub {} → {}", old_lam, new_fn);
281 auto stub = Stub{old_lam, std::move(fv_vec), new_fn};
282 closures_.try_emplace(old_lam, stub);
283 closures_.try_emplace(new_fn, stub);
287ClosConv::Stub ClosConv::make_stub(Lam* old_lam) {
288 if (
auto i = closures_.find(old_lam); i != closures_.end())
return i->second;
289 auto stub = make_stub(fva_.run(old_lam), old_lam);
290 body_worklist_.emplace(stub.fn);
294void ClosConv::rewrite_body(
const Stub& stub) {
295 auto old_fn = stub.old_fn;
296 if (!old_fn->is_set())
return;
299 auto new_fn = stub.fn;
300 auto ep =
env_param(new_fn->type()->as<Pi>());
301 auto nv = new_fn->num_vars();
302 auto env_val = new_fn->var(nv, ep)->set(
"closure_env");
303 log().d(
"rewrite body of {} → {}", old_fn, new_fn);
304 if (stub.fvs.size() == 1) {
305 map(stub.fvs.front(), env_val);
307 for (
size_t i = 0, e = stub.fvs.size(); i != e; ++i) {
308 auto fv = stub.fvs[i];
309 auto sym =
w.sym(
"fv_"s + (fv->sym() ? fv->sym().str() : std::to_string(i)));
310 map(fv, env_val->proj(e, i)->set(sym));
314 auto params =
w.tuple(
DefVec(old_fn->num_doms(), [&](
auto i) { return new_fn->var(nv, skip_env(ep, i)); }));
315 map(old_fn->var(), params);
const Def * callee() const
static auto isa(const Def *def)
T * as_mut() const
Asserts that this is a mutable, casts constness away and performs a static_cast to T.
constexpr size_t num_ops() const noexcept
static const Lam * isa_cn(const Def *d)
static const Lam * isa_basicblock(const Def *d)
static T as(const Def *def)
const fe::Log & log() const
A dependent function type.
static const Pi * isa_cn(const Def *d)
static const Pi * isa_basicblock(const Def *d)
const Pi * ret_pi() const
Yields the last Pi::dom, if Pi::isa_basicblock.
nat_t num_doms() const noexcept
static const Pi * isa_returning(const Def *d)
World & new_world()
Create new Defs into this.
const Def * lookup(const Def *) override
Lookup old_def by searching in reverse through the stack of maps.
World & old_world()
Get old Defs from here.
void bind_old(const Var *var)
Adds the old var - if any - to the innermost scope.
virtual const Def * rewrite_imm_Extract(const Extract *)
const Def * map(const Def *old_def, const Def *new_def)
virtual const Def * rewrite_mut_Lam(Lam *)
virtual const Def * rewrite_imm_Pi(const Pi *)
const Def * seal(Def *old_mut, Def *new_mut)
The tail of rewrite_stub: immutabilizes new_mut in hindsight, as rewriting may have made it vacuous.
virtual const Def * rewrite(const Def *)
auto enter(Def *new_mut)
Updates curr_mut() to new_mut and restores it at the end of the scope.
void push(Vars old_vars={}, Vars new_vars={})
virtual const Def * rewrite_mut_Pi(Pi *)
const Def * app(const Def *callee, const Def *arg)
const Def * rewrite_imm_Extract(const Extract *) final
const Def * rewrite_mut_Pi(Pi *) final
const Def * rewrite_imm_App(const App *) final
const Def * rewrite_imm_Pi(const Pi *) final
void finalize() final
Run after all roots have been walked but still before the two worlds are swapped.
const Def * rewrite_mut_Lam(Lam *) final
const DefSet & run(Lam *lam)
Returns the free defs lam has to capture; see the class description.
bool is_telescope_cn(const Def *def)
Inserting the environment would shift the indices a telescope's Var refers to, so ClosConv leaves suc...
const Def * clos_remove_env(size_t ep, size_t i, std::function< const Def *(size_t)> f)
const Def * ctype(World &w, Defs doms, const Def *env_type=nullptr)
Builds a closure type from the domains doms of a Cn.
const Def * clos_insert_env(size_t ep, size_t i, const Def *env, std::function< const Def *(size_t)> f)
std::tuple< const Extract *, N * > isa_var_proj(const Def *def)
If def is a projection var#i of the Var of some mutable of type N, returns (projection,...
size_t skip_env(size_t ep, size_t i)
Same as shift_env, but skips the env param instead.
const Def * clos_pack(const Def *env, const Def *fn, const Def *ct=nullptr)
Pack a typed closure.
const Def * clos_apply(const Def *closure, const Def *args)
Apply a closure to arguments.
size_t env_param(Defs doms)
Describes where the environment is placed in the argument list: right after a leading mem....
bool has_mem_ty(const Def *type)
Is type mem.M or a Sigma with a mem.M component?
fe::Vector< const Def * > DefVec
GIDSet< const Def * > DefSet
fe::Patricia< const Var, DefKey >::Set Vars