MimIR
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
clos_conv_prep.cpp
Go to the documentation of this file.
2
3#include <mim/nest.h>
4
6
8
9namespace {
10
11Lam* isa_retvar(const Def* def) {
12 if (auto [var, lam] = isa_var_proj<Lam>(def); var && lam && var == lam->ret_var()) return lam;
13 return nullptr;
14}
15
16} // namespace
17
19 if (analyzed_) return false;
20 analyzed_ = true;
21
22 // Collect all returning Lams and assign each basicblock Lam in their Nest to them.
23 DefSet done;
24 auto visit = [&](auto&& visit, const Def* def) -> void {
25 if (!done.emplace(def).second) return;
26
27 if (auto lam = def->isa_mut<Lam>(); lam && Pi::isa_returning(lam)) {
28 lam2fscope_[lam] = lam;
29 log().d("scope: {} → {}", lam, lam);
30 auto nest = Nest(lam);
31 for (auto mut : nest.muts())
32 if (auto bb_lam = Lam::isa_mut_basicblock(mut)) {
33 log().d("scope: {} → {}", bb_lam, lam);
34 lam2fscope_[bb_lam] = lam;
35 }
36 }
37
38 if (auto mut = def->isa_mut()) {
39 if (mut->is_set())
40 for (auto op : mut->deps())
41 visit(visit, op);
42 } else {
43 for (auto op : def->deps())
44 visit(visit, op);
45 }
46 };
47 for (auto def : old_world().externals().muts())
48 visit(visit, def);
49
50 return false;
51}
52
53const Def* ClosConvPrep::eta_wrap(const Def* old_op, anno a) {
54 auto [entry, inserted] = old2wrapper_.emplace(old_op, nullptr);
55 auto& wrapper = entry->second;
56 if (inserted) {
57 auto& w = new_world();
58 wrapper = w.mut_lam(rewrite(old_op->type())->as<Pi>());
59 wrapper->app(false, rewrite(old_op), wrapper->var());
60 }
61 return new_world().call(a, wrapper);
62}
63
64const Def* ClosConvPrep::rewrite_arg(const App* app, const Def* old_op, size_t i, size_t n) {
65 if (auto lam = isa_retvar(old_op); lam && from_outer_scope(lam)) {
66 log().d("return var from an enclosing scope: {}", old_op);
67 return eta_wrap(old_op, anno::free_bb)->set("free_ret");
68 }
69 if (auto bb_lam = Lam::isa_mut_basicblock(old_op); bb_lam && from_outer_scope(bb_lam)) {
70 log().d("BB from an enclosing scope: {}", old_op);
71 return new_world().call(anno::free_bb, rewrite(old_op));
72 }
73 if (Pi::isa_returning(app->callee_type()) && i == n - 1) {
74 if (Axm::isa<anno>(anno::returning, old_op) || isa_retvar(old_op)) {
75 return rewrite(old_op);
76 } else if (auto contlam = old_op->isa_mut<Lam>()) {
77 return new_world().call(anno::returning, rewrite(contlam));
78 } else {
79 auto wrapper = eta_wrap(old_op, anno::returning)->set("eta_cont");
80 log().d("eta-expand return continuation: {} → {}", old_op, wrapper);
81 return wrapper;
82 }
83 }
84
85 if (auto bb_lam = Lam::isa_mut_basicblock(old_op)) {
86 log().d("first-class use of BB: {}", bb_lam);
87 return new_world().call(anno::fstclass_bb, rewrite(bb_lam));
88 }
89 // @note This relies on branches staying in `Extract`-of-`Tuple` form; if eta-reduction were to collapse
90 // a branch back into a bare continuation, it would have to be re-wrapped here.
91 if (isa_retvar(old_op)) {
92 log().d("first-class use of return var: {}", old_op);
93 return eta_wrap(old_op, anno::fstclass_bb)->set("fstclass_ret");
94 }
95
96 return rewrite(old_op);
97}
98
99const Def* ClosConvPrep::rewrite_callee_op(const Def* old_op) {
100 if (!old_op->isa_mut<Lam>()) {
101 auto wrapper = eta_wrap(old_op, anno::bottom)->set("eta_br");
102 log().d("eta-wrap branch: {} → {}", old_op, wrapper);
103 return wrapper;
104 }
105 return rewrite(old_op);
106}
107
109 if (Axm::isa<anno>(app)) return RWPhase::rewrite_imm_App(app);
110
111 // Skip if the surrounding mutable is no Lam with a continuation call as body.
112 auto mut = curr_mut() ? curr_mut()->isa_mut<Lam>() : nullptr;
113 auto body = mut && mut->is_set() ? mut->body()->isa<App>() : nullptr;
114 if (!body || !Pi::isa_cn(body->callee_type())) return RWPhase::rewrite_imm_App(app);
115
116 auto& w = new_world();
117
118 // Eta-expand branches in callee position.
119 const Def* new_callee = nullptr;
120 if (Pi::isa_cn(app->callee_type())) {
121 if (auto sw = Switch(app->callee()); sw && sw.tuple()->isa<Tuple>()) {
122 auto new_ops = DefVec(sw.num_targets(), [&](size_t i) { return rewrite_callee_op(sw.target(i)); });
123 new_callee = w.extract(w.tuple(new_ops), rewrite(sw.index()));
124 }
125 }
126 if (!new_callee) new_callee = rewrite(app->callee());
127
128 // Wrap the argument's projections.
129 const Def* new_arg;
130 auto arg = app->arg();
131 if (arg->isa<Var>()) {
132 new_arg = rewrite(arg);
133 } else {
134 auto new_args = DefVec(arg->num_projs(),
135 [&, n = arg->num_projs()](size_t i) { return rewrite_arg(app, arg->proj(n, i), i, n); });
136 new_arg = arg->num_projs() == 1 ? new_args[0] : w.tuple(new_args);
137 }
138
139 return w.app(new_callee, new_arg);
140}
141
142} // namespace mim::plug::clos::phase
const Pi * callee_type() const
Definition lam.h:239
const Def * callee() const
Definition lam.h:237
const Def * arg() const
Definition lam.h:251
static auto isa(const Def *def)
Definition axm.h:112
Base class for all Defs.
Definition def.h:289
Def * set(size_t i, const Def *)
Successively set from left to right.
Definition def.cpp:188
T * isa_mut() const
If this is mutable, it will cast constness away and perform a dynamic_cast to T.
Definition def.h:588
const Def * var(nat_t a, nat_t i) noexcept
Definition def.h:492
const Def * type() const noexcept
Yields the "raw" type of this Def (maybe nullptr).
Definition def.h:1119
nat_t num_projs() const
Yields Def::arity(), if it is a Lit, or 1 otherwise.
Definition def.h:1140
A function.
Definition lam.h:91
static Lam * isa_mut_basicblock(const Def *d)
Definition lam.h:130
Nest(Def *root)
Definition nest.cpp:9
const fe::Log & log() const
Definition phase.h:60
A dependent function type.
Definition lam.h:14
static const Pi * isa_cn(const Def *d)
Definition lam.h:50
static const Pi * isa_returning(const Def *d)
Definition lam.h:51
World & new_world()
Create new Defs into this.
Definition phase.h:379
World & old_world()
Get old Defs from here.
Definition phase.h:378
const Def * rewrite_imm_App(const App *) override
Definition phase.cpp:223
D * curr_mut() const
Definition rewrite.h:98
virtual const Def * rewrite(const Def *)
Definition rewrite.cpp:62
Switch(const Def *)
Definition tuple.cpp:84
Data constructor for a Sigma.
Definition tuple.h:84
A variable introduced by a binder (mutable).
Definition def.h:857
const Def * call(const Def *callee, auto &&arg, Args &&... args)
Definition world.h:674
const Def * rewrite_imm_App(const App *) final
bool analyze() final
Fills lam2fscope_: assigns each basicblock Lam to its enclosing returning Lam.
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,...
Definition clos.h:73
fe::Vector< const Def * > DefVec
Definition def.h:98
GIDSet< const Def * > DefSet
Definition def.h:94
@ Lam
Definition def.h:125