5using namespace std::literals;
11bool is_memop_res(
const Def* fd) {
13 if (!proj)
return false;
14 auto types = proj->tuple()->type()->ops();
15 return std::ranges::any_of(types, [](
auto d) {
return Axm::isa<mem::M>(d); });
19DefSet free_defs(
const Nest& nest) {
21 std::queue<const Def*> queue;
22 queue.emplace(nest.root()->mut());
24 while (!queue.empty()) {
25 for (
auto op :
pop(queue)->deps()) {
26 if (
op->is_closed())
continue;
27 if (nest.contains(op)) {
28 if (
auto [_, ins] = bound.emplace(op); ins) queue.emplace(op);
44void FreeDefAna::classify(
Node* node,
const Def* fd,
bool& spawned_pred, NodeQueue& worklist) {
46 if (fd->is_closed())
return;
49 if (var != lam->ret_var()) node->add_fvs(fd);
52 }
else if (
auto pred = fd->isa_mut()) {
54 if (pred != node->mut) {
55 auto [pnode, inserted] = build_node(pred, worklist);
56 node->preds.emplace_back(pnode);
57 pnode->succs.emplace_back(node);
58 spawned_pred |= inserted;
63 }
else if (is_memop_res(fd)) {
66 for (
auto op : fd->ops())
67 classify(node, op, spawned_pred, worklist);
71std::pair<FreeDefAna::Node*, bool> FreeDefAna::build_node(Def* mut, NodeQueue& worklist) {
72 auto [p, inserted] = lam2node_.emplace(mut,
nullptr);
73 if (!inserted)
return {p->second.get(),
false};
74 world().DLOG(
"FVA: create node: {}", mut);
76 p->second = std::make_unique<Node>(mut);
77 auto node = p->second.get();
78 bool spawned_pred =
false;
79 for (
auto fd : free_defs(Nest(mut)))
80 classify(node, fd, spawned_pred, worklist);
85 world().DLOG(
"FVA: init {}", mut);
90void FreeDefAna::propagate(NodeQueue& worklist) {
91 while (!worklist.empty()) {
92 auto node =
pop(worklist);
93 if (is_done(node))
continue;
94 auto changed = is_bot(node);
96 for (
auto pred : node->preds)
97 for (
auto pfv : pred->fvs)
98 changed |= node->add_fvs(pfv).second;
100 for (
auto succ : node->succs)
106 auto worklist = NodeQueue();
107 auto [node, _] = build_node(lam, worklist);
108 if (!is_done(node)) {
121 for (
const auto& [flags, e] :
old_world().annexes())
126 for (
auto old_mut :
old_world().externals().muts()) {
128 auto new_def =
rewrite(old_mut);
132 if (
auto new_mut = new_def->isa_mut(); new_mut && old_mut->is_external() && !new_mut->is_external())
133 new_mut->externalize();
137 while (!body_worklist_.empty()) {
138 auto fn = body_worklist_.front();
139 body_worklist_.pop();
141 rewrite_body(closures_.at(fn));
149 if (converting_ &&
Pi::isa_cn(pi))
return clos_type_of(pi);
150 return RWPhase::rewrite_imm_Pi(pi);
154 if (converting_ &&
Pi::isa_cn(pi))
return clos_type_of(pi);
155 return RWPhase::rewrite_mut_Pi(pi);
159 if (!converting_ || !
Lam::isa_cn(old_lam))
return RWPhase::rewrite_mut_Lam(old_lam);
162 auto stub = make_stub(old_lam);
166 auto env = w.tuple(
DefVec(stub.fvs.size(), [&](
auto i) { return rewrite(stub.fvs[i]); }));
167 auto closure =
clos_pack(env, stub.fn, clos_ty);
168 DLOG(
"RW: pack {} ~> {} : {}", old_lam, closure, clos_ty);
169 return map(old_lam, closure);
173 if (!converting_)
return RWPhase::rewrite_imm_App(app);
176 if (
auto handled = rewrite_attr(a))
return handled;
180 if (new_callee->type()->isa<
Sigma>())
return clos_apply(new_callee, new_arg);
190 if (
auto ret_lam = a->arg()->isa_mut<
Lam>()) {
191 auto new_doms =
DefVec(ret_lam->num_doms(), [&](
auto i) { return rewrite(ret_lam->dom(i)); });
192 auto new_lam = w.mut_lam(w.cn(new_doms))->set(ret_lam->dbg());
193 map(ret_lam, new_lam);
194 if (ret_lam->is_set()) new_lam->set(
rewrite(ret_lam->filter()),
rewrite(ret_lam->body()));
202 auto bb_lam = a->arg()->isa_mut<
Lam>();
204 auto stub = make_stub({}, bb_lam);
210 default:
return nullptr;
221 if (
auto [var, lam] =
isa_var_proj<Lam>(ex); var && lam && lam->ret_var() == var) {
222 auto new_fn = make_stub(lam).fn;
224 return new_fn->var(new_idx);
226 return RWPhase::rewrite_imm_Extract(ex);
231 if (
auto i = glob_muts_.find(global); i != glob_muts_.end())
return i->second;
233 auto new_global = RWPhase::rewrite_mut_Global(global);
235 return glob_muts_[global] = new_global;
238const Pi* ClosConv::rewrite_ret_cn(
const Pi* pi) {
240 return new_world().
cn(
DefVec(pi->num_doms(), [&](
auto i) { return rewrite(pi->dom(i)); }));
243const Def* ClosConv::clos_type_of(
const Pi* pi,
const Def* env_type) {
245 if (
auto i = glob_muts_.find(pi); i != glob_muts_.end())
return i->second;
247 auto new_doms =
DefVec(pi->num_doms(), [&](
auto i) {
248 return (i == pi->num_doms() - 1 && Pi::isa_returning(pi)) ? rewrite_ret_cn(pi->ret_pi()) : rewrite(pi->dom(i));
252 glob_muts_.emplace(pi, ct);
253 DLOG(
"C-TYPE: pct {} ~~> {}", pi, ct);
255 DLOG(
"C-TYPE: ct {}, env = {} ~~> {}", pi, env_type, ct);
260ClosConv::Stub ClosConv::make_stub(
const DefSet& fvs,
Lam* old_lam) {
262 auto fv_vec =
DefVec(fvs.begin(), fvs.end());
264 auto new_fn_type = clos_type_of(old_lam->type(), env_type)->as<
Pi>();
265 auto new_fn = w.mut_lam(new_fn_type)->set(old_lam->dbg());
271 auto new_ext_lam = w.mut_lam(new_ext_type)->set(old_lam->dbg());
272 DLOG(
"wrap ext lam: {} -> stub: {}, ext: {}", old_lam, new_fn, new_ext_lam);
273 if (old_lam->is_set()) {
274 if (old_lam->is_external()) new_ext_lam->externalize();
275 auto env = w.tuple(
DefVec(fv_vec.size(), [&](
auto i) { return rewrite(fv_vec[i]); }));
276 new_ext_lam->app(
false, new_fn,
clos_insert_env(ep, env, new_ext_lam->var()));
279 new_ext_lam->unset();
284 DLOG(
"STUB {} ~~> {}", old_lam, new_fn);
285 auto stub = Stub{old_lam, std::move(fv_vec), new_fn};
286 closures_.try_emplace(old_lam, stub);
287 closures_.try_emplace(new_fn, stub);
291ClosConv::Stub ClosConv::make_stub(
Lam* old_lam) {
292 if (
auto i = closures_.find(old_lam); i != closures_.end())
return i->second;
293 auto stub = make_stub(fva_.run(old_lam), old_lam);
294 body_worklist_.emplace(stub.fn);
298void ClosConv::rewrite_body(
const Stub& stub) {
299 auto old_fn = stub.old_fn;
300 if (!old_fn->is_set())
return;
303 auto new_fn = stub.fn;
305 auto env_val = new_fn->var(ep)->set(
"closure_env");
306 DLOG(
"rw body: {} [old={}]", new_fn, old_fn);
307 if (stub.fvs.size() == 1) {
308 map(stub.fvs.front(), env_val);
310 for (
size_t i = 0, e = stub.fvs.size(); i != e; ++i) {
311 auto fv = stub.fvs[i];
312 auto sym =
w.sym(
"fv_"s + (fv->sym() ? fv->sym().str() : std::to_string(i)));
313 map(fv, env_val->proj(i)->set(sym));
317 auto params =
w.tuple(
DefVec(old_fn->num_doms(), [&](
auto i) { return new_fn->var(skip_env(ep, i)); }));
318 map(old_fn->var(), params);
const Def * callee() const
static auto isa(const Def *def)
static const Lam * isa_cn(const Def *d)
static const Lam * isa_basicblock(const Def *d)
static T as(const Def *def)
A dependent function type.
static const Pi * isa_cn(const Def *d)
Is this a continuation - i.e. is the Pi::codom mim::Bottom?
static const Pi * isa_basicblock(const Def *d)
Is this a continuation (Pi::isa_cn) that is not Pi::isa_returning?
World & new_world()
Create new Defs into this.
virtual void rewrite_annex(flags_t, Sym, const Def *)
World & old_world()
Get old Defs from here.
friend void swap(Rewriter &rw1, Rewriter &rw2) noexcept
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
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
const Def * rewrite_mut_Lam(Lam *) final
void start() override
Actual entry.
const DefSet & run(Lam *lam)
Returns the free defs lam has to capture; see the class description.
#define DLOG(...)
Vaporizes to nothingness in Debug build.
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....
auto pop(S &s) -> decltype(s.top(), typename S::value_type())
Vector< const Def * > DefVec
Lam * isa_optimizable(Lam *lam)
These are Lams that are.
GIDSet< const Def * > DefSet