5#include <fe/container.h>
6#include <fe/worklist.h>
10using namespace std::literals;
16bool is_memop_res(
const Def* fd) {
18 if (!proj)
return false;
19 auto types = proj->tuple()->type()->ops();
20 return std::ranges::any_of(types, [](
auto d) {
return Axm::isa<mem::M>(d); });
24DefSet free_defs(
const Nest& nest) {
26 auto queue = fe::BFSWorklist<DefSet>{nest.root()->mut()};
28 while (!queue.empty()) {
29 for (
auto op : queue.pop()->deps()) {
30 if (
op->is_closed())
continue;
31 if (nest.contains(op))
47void FreeDefAna::classify(
Node* node,
const Def* fd,
bool& spawned_pred, NodeQueue& worklist) {
49 if (fd->is_closed())
return;
52 if (var != lam->ret_var()) node->add_fvs(fd);
54 node->add_fvs(free_bb);
55 }
else if (
auto pred = fd->isa_mut()) {
57 if (pred != node->mut) {
58 auto [pnode, inserted] = build_node(pred, worklist);
59 node->preds.emplace_back(pnode);
60 pnode->succs.emplace_back(node);
61 spawned_pred |= inserted;
63 }
else if (fd->has_dep(
Dep::Var) && !fd->isa<Tuple>()) {
66 }
else if (is_memop_res(fd)) {
69 for (
auto op : fd->ops())
70 classify(node, op, spawned_pred, worklist);
74std::pair<FreeDefAna::Node*, bool> FreeDefAna::build_node(Def* mut, NodeQueue& worklist) {
75 auto [p, inserted] = lam2node_.emplace(mut,
nullptr);
76 if (!inserted)
return {p->second.get(),
false};
77 world().log().d(
"FVA: create node {}", mut);
79 p->second = std::make_unique<Node>(mut);
80 auto node = p->second.get();
81 bool spawned_pred =
false;
82 for (
auto fd : free_defs(
Nest(mut)))
83 classify(node, fd, spawned_pred, worklist);
88 world().log().d(
"FVA: init {}", mut);
93void FreeDefAna::propagate(NodeQueue& worklist) {
94 while (!worklist.empty()) {
95 auto node = fe::pop(worklist);
96 if (is_done(node))
continue;
97 auto changed = is_bot(node);
99 for (
auto pred : node->preds)
100 for (
auto pfv : pred->fvs)
101 changed |= node->add_fvs(pfv).second;
103 for (
auto succ : node->succs)
109 auto worklist = NodeQueue();
110 auto [node, _] = build_node(lam, worklist);
111 if (!is_done(node)) {
128 if (
auto new_mut = new_def->isa_mut(); new_mut && old_mut->
is_external() && !new_mut->is_external())
129 new_mut->externalize();
134 while (!body_worklist_.empty()) {
135 auto fn = body_worklist_.front();
136 body_worklist_.pop();
138 rewrite_body(closures_.at(fn));
145 return RWPhase::rewrite_imm_Pi(pi);
150 return RWPhase::rewrite_mut_Pi(pi);
157 auto stub = make_stub(old_lam);
161 auto env = w.tuple(
DefVec(stub.fvs.size(), [&](
auto i) { return rewrite(stub.fvs[i]); }));
162 auto closure =
clos_pack(env, stub.fn, clos_ty);
163 log().d(
"pack {} → {}: {}", old_lam, closure, clos_ty);
164 return map(old_lam, closure);
171 if (
auto handled = rewrite_attr(a))
return handled;
175 if (new_callee->type()->isa<
Sigma>())
return clos_apply(new_callee, new_arg);
185 if (
auto ret_lam = a->arg()->isa_mut<
Lam>()) {
186 auto new_doms =
DefVec(ret_lam->num_doms(), [&](
auto i) { return rewrite(ret_lam->dom(i)); });
187 auto new_lam = w.mut_lam(w.cn(new_doms))->set(ret_lam->dbg_key());
188 map(ret_lam, new_lam);
189 if (ret_lam->is_set()) new_lam->set(
rewrite(ret_lam->filter()),
rewrite(ret_lam->body()));
197 auto bb_lam = a->arg()->isa_mut<
Lam>();
199 auto stub = make_stub({}, bb_lam);
205 default:
return nullptr;
216 if (
auto [var, lam] =
isa_var_proj<Lam>(ex); var && lam && lam->ret_var() == var) {
217 auto new_fn = make_stub(lam).fn;
219 return new_fn->var(new_idx);
221 return RWPhase::rewrite_imm_Extract(ex);
226 if (
auto i = glob_muts_.find(global); i != glob_muts_.end())
return i->second;
228 auto new_global = RWPhase::rewrite_mut_Global(global);
230 return glob_muts_[global] = new_global;
233const Pi* ClosConv::rewrite_ret_cn(
const Pi* pi) {
235 return new_world().
cn(
DefVec(pi->num_doms(), [&](
auto i) { return rewrite(pi->dom(i)); }));
238const Def* ClosConv::clos_type_of(
const Pi* pi,
const Def* env_type) {
240 if (
auto i = glob_muts_.find(pi); i != glob_muts_.end())
return i->second;
242 auto new_doms =
DefVec(pi->num_doms(), [&](
auto i) {
243 return (i == pi->num_doms() - 1 && Pi::isa_returning(pi)) ? rewrite_ret_cn(pi->ret_pi()) : rewrite(pi->dom(i));
247 glob_muts_.emplace(pi, ct);
248 log().d(
"closure type: {} → {} (pretyped)", pi, ct);
250 log().d(
"closure type: {} → {} (env = {})", pi, ct, env_type);
255ClosConv::Stub ClosConv::make_stub(
const DefSet& fvs, Lam* old_lam) {
260 auto fv_vec =
DefVec(fvs.begin(), fvs.end());
261 std::ranges::sort(fv_vec, GIDLt<const Def*>());
263 auto new_fn_type = clos_type_of(old_lam->type(), env_type)->as<Pi>();
264 auto new_fn = w.mut_lam(new_fn_type)->set(old_lam->dbg_key());
270 auto new_ext_lam = w.mut_lam(new_ext_type)->set(old_lam->dbg_key());
271 log().d(
"wrap external lam {} → stub {}, external {}", old_lam, new_fn, new_ext_lam);
272 if (old_lam->is_set()) {
273 if (old_lam->is_external()) new_ext_lam->externalize();
274 auto env = w.tuple(
DefVec(fv_vec.size(), [&](
auto i) { return rewrite(fv_vec[i]); }));
275 new_ext_lam->app(
false, new_fn,
clos_insert_env(ep, env, new_ext_lam->var()));
278 new_ext_lam->unset();
283 log().d(
"stub {} → {}", old_lam, new_fn);
284 auto stub = Stub{old_lam, std::move(fv_vec), new_fn};
285 closures_.try_emplace(old_lam, stub);
286 closures_.try_emplace(new_fn, stub);
290ClosConv::Stub ClosConv::make_stub(Lam* old_lam) {
291 if (
auto i = closures_.find(old_lam); i != closures_.end())
return i->second;
292 auto stub = make_stub(fva_.run(old_lam), old_lam);
293 body_worklist_.emplace(stub.fn);
297void ClosConv::rewrite_body(
const Stub& stub) {
298 auto old_fn = stub.old_fn;
299 if (!old_fn->is_set())
return;
302 auto new_fn = stub.fn;
303 auto ep =
env_param(new_fn->type()->as<Pi>());
304 auto env_val = new_fn->var(ep)->set(
"closure_env");
305 log().d(
"rewrite body of {} → {}", old_fn, new_fn);
306 if (stub.fvs.size() == 1) {
307 map(stub.fvs.front(), env_val);
309 for (
size_t i = 0, e = stub.fvs.size(); i != e; ++i) {
310 auto fv = stub.fvs[i];
311 auto sym =
w.sym(
"fv_"s + (fv->sym() ? fv->sym().str() : std::to_string(i)));
312 map(fv, env_val->proj(i)->set(sym));
316 auto params =
w.tuple(
DefVec(old_fn->num_doms(), [&](
auto i) { return new_fn->var(skip_env(ep, i)); }));
317 map(old_fn->var(), params);
const Def * callee() const
static auto isa(const Def *def)
bool is_external() 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)
Is this a continuation (Pi::isa_cn) that is not Pi::isa_returning?
bool is_bootstrapping() const
Returns whether we are currently bootstrapping (rewriting annexes).
virtual const Def * rewrite_root(const Def *def)
Rewrites a root - i.e. an annex or an external.
World & new_world()
Create new Defs into this.
World & old_world()
Get old Defs from here.
virtual const Def * map(const Def *old_def, const Def *new_def)
virtual const Def * rewrite(const Def *)
const Def * app(const Def *callee, const Def *arg)
const Def * rewrite_mut_Global(Global *) final
void rewrite_external(Def *) final
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 for an RWPhase 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.
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....
Lam * isa_optimizable(Lam *lam)
These are Lams that are.
fe::Vector< const Def * > DefVec
GIDSet< const Def * > DefSet