MimIR 0.1
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
clos2sjlj.cpp
Go to the documentation of this file.
2
4
5namespace mim::plug::clos {
6
7namespace {
8
9std::array<const Def*, 3> split(const Def* def) {
10 auto new_ops = DefVec(def->num_projs() - 2, nullptr);
11 auto& w = def->world();
12 const Def *mem, *env;
13 auto j = 0;
14 for (size_t i = 0; i < def->num_projs(); i++) {
15 auto op = def->proj(i);
16 if (op == w.annex<mem::M>() || op->type() == w.annex<mem::M>())
17 mem = op;
18 else if (i == Clos_Env_Param)
19 env = op;
20 else
21 new_ops[j++] = op;
22 }
23 assert(mem && env);
24 auto remaining = def->is_intro() ? w.tuple(new_ops) : w.sigma(new_ops);
25 if (new_ops.size() == 1 && remaining != new_ops[0]) {
26 // FIXME: For some reason this is not constant folded away??
27 remaining = new_ops[0];
28 }
29 return {mem, env, remaining};
30}
31
32const Def* rebuild(const Def* mem, const Def* env, Defs remaining) {
33 auto& w = mem->world();
34 auto new_ops = DefVec(remaining.size() + 2, [&](auto i) -> const Def* {
35 static_assert(Clos_Env_Param == 1);
36 if (i == 0) return mem;
37 if (i == 1) return env;
38 return remaining[i - 2];
39 });
40 return w.tuple(new_ops);
41}
42
43} // namespace
44
45void Clos2SJLJ::get_exn_closures(const Def* def, DefSet& visited) {
46 if (!def->is_term() || def->isa_mut<Lam>() || visited.contains(def)) return;
47 visited.emplace(def);
48 if (auto c = isa_clos_lit(def)) {
49 auto lam = c.fnc_as_lam();
50 if (c.is_basicblock() && !ignore_.contains(lam)) {
51 DLOG("FOUND exn closure: {}", c.fnc_as_lam());
52 lam2tag_[c.fnc_as_lam()] = {lam2tag_.size() + 1, c.env()};
53 }
54 get_exn_closures(c.env(), visited);
55 } else {
56 for (auto op : def->ops())
57 get_exn_closures(op, visited);
58 }
59}
60
61void Clos2SJLJ::get_exn_closures() {
62 lam2tag_.clear();
63 if (!curr_mut()->is_set() || !Lam::isa_cn(curr_mut())) return;
64 auto app = curr_mut()->body()->isa<App>();
65 if (!app) return;
66 if (auto p = app->callee()->isa<Extract>(); p && isa_clos_type(p->tuple()->type())) {
67 auto p2 = p->tuple()->isa<Extract>();
68 if (p2 && p2->tuple()->isa<Tuple>()) {
69 // branch: Check the closure environments, but be careful not to traverse
70 // the closures themselves
71 auto branches = p2->tuple()->ops();
72 for (auto b : branches) {
73 auto c = isa_clos_lit(b);
74 if (c) {
75 ignore_.emplace(c.fnc_as_lam());
76 DLOG("IGNORE {}", c.fnc_as_lam());
77 }
78 }
79 }
80 }
81 auto visited = DefSet();
82 get_exn_closures(app->arg(), visited);
83}
84
85Lam* Clos2SJLJ::get_throw(const Def* dom) {
86 auto& w = world();
87 auto [p, inserted] = dom2throw_.emplace(dom, nullptr);
88 auto& tlam = p->second;
89 if (inserted || !tlam) {
90 tlam = w.mut_con(clos_sub_env(dom, w.sigma({jb_type(), rb_type(), tag_type()})))->set("throw");
91 auto [m0, env, var] = split(tlam->var());
92 auto [jbuf, rbuf, tag] = env->projs<3>();
93 auto [m1, r] = mem::op_alloc(var->type(), m0)->projs<2>();
94 auto m2 = w.call<mem::store>(Defs{m1, r, var});
95 rbuf = w.call<core::bitcast>(world().call<mem::Ptr0>(world().call<mem::Ptr0>(var->type())), rbuf);
96 auto m3 = w.call<mem::store>(Defs{m2, rbuf, r});
97 tlam->set(false, w.call<longjmp>(Defs{m3, jbuf, tag}));
98 ignore_.emplace(tlam);
99 }
100 return tlam;
101}
102
103Lam* Clos2SJLJ::get_lpad(Lam* lam, const Def* rb) {
104 auto& w = world();
105 auto [p, inserted] = lam2lpad_.emplace(w.tuple({lam, rb}), nullptr);
106 auto& lpad = p->second;
107 if (inserted || !lpad) {
108 auto [_, env_type, dom] = split(lam->dom());
109 lpad = mem::mut_con(env_type)->set("lpad");
110 auto [m, env, __] = split(lpad->var());
111 auto [m1, arg_ptr] = w.call<mem::load>(Defs{m, rb})->projs<2>();
112 arg_ptr = w.call<core::bitcast>(world().call<mem::Ptr0>(dom), arg_ptr);
113 auto [m2, args] = w.call<mem::load>(Defs{m1, arg_ptr})->projs<2>();
114 auto full_args = (lam->num_doms() == 3) ? rebuild(m2, env, {args}) : rebuild(m2, env, args->ops());
115 lpad->app(false, lam, full_args);
116 ignore_.emplace(lpad);
117 }
118 return lpad;
119}
120
122 auto& w = world();
123 get_exn_closures();
124 if (lam2tag_.empty()) return;
125
126 {
127 auto m0 = mem::mem_var(curr_mut());
128 auto [m1, jb] = w.call<clos::alloc_jmpbuf>(m0)->projs<2>();
129 auto [m2, rb] = mem::op_slot(void_ptr(), m1)->projs<2>();
130 auto new_args = curr_mut()->vars();
131 new_args[0] = m2;
132 auto new_defs = curr_mut()->reduce(w.tuple(new_args));
133 curr_mut()->unset()->set(new_defs);
134
135 cur_jbuf_ = jb;
136 cur_rbuf_ = rb;
137
138 // apparently apply() can change the id of the closures, so we have to do it again :(
139 get_exn_closures();
140 }
141
142 auto body = curr_mut()->body()->as<App>();
143
144 auto branch_type = clos_type(w.cn(w.annex<mem::M>()));
145 auto branches = DefVec(lam2tag_.size() + 1);
146 {
147 auto env = w.tuple(body->args().view().subspan(1));
148 auto new_callee = mem::mut_con(env->type())->set("sjlj_wrap");
149 auto [m, env_var, _] = split(new_callee->var());
150 auto new_args = DefVec(env->num_projs() + 1, [&](size_t i) { return (i == 0) ? m : env_var->proj(i - 1); });
151 new_callee->app(false, body->callee(), new_args);
152 branches[0] = clos_pack(env, new_callee, branch_type);
153 }
154
155 for (auto [exn_lam, p] : lam2tag_) {
156 auto [i, env] = p;
157 branches[i] = clos_pack(env, get_lpad(exn_lam, cur_rbuf_), branch_type);
158 }
159
160 auto m0 = body->arg(0);
161 assert(m0->type() == w.annex<mem::M>());
162 auto [m1, tag] = w.call<setjmp>(Defs{m0, cur_jbuf_})->projs<2>();
163 tag = w.call(core::conv::s, branches.size(), tag);
164 auto filter = curr_mut()->filter();
165 auto branch = w.extract(w.tuple(branches), tag);
166 curr_mut()->unset()->set({filter, clos_apply(branch, m1)});
167}
168
169const Def* Clos2SJLJ::rewrite(const Def* def) {
170 if (auto c = isa_clos_lit(def); c && lam2tag_.contains(c.fnc_as_lam())) {
171 auto& w = world();
172 auto [i, _] = lam2tag_[c.fnc_as_lam()];
173 auto tlam = get_throw(c.fnc_as_lam()->dom());
174 return clos_pack(w.tuple({cur_jbuf_, cur_rbuf_, w.lit_idx(i)}), tlam, c.type());
175 }
176 return def;
177}
178
179} // namespace mim::plug::clos
Base class for all Defs.
Definition def.h:251
const Def * proj(nat_t a, nat_t i) const
Similar to World::extract while assuming an arity of a, but also works on Sigmas and Arrays.
Definition def.cpp:581
auto projs(F f) const
Splits this Def via Def::projections into an Array (if A == std::dynamic_extent) or std::array (other...
Definition def.h:390
const Def * type() const noexcept
Yields the "raw" type of this Def (maybe nullptr).
Definition def.h:295
auto vars(F f) noexcept
Definition def.h:429
Lam * unset()
Definition lam.h:180
const Def * filter() const
Definition lam.h:123
static const Lam * isa_cn(const Def *d)
Definition lam.h:142
Lam * set(Filter filter, const Def *body)
Definition lam.cpp:29
Defs reduce(Defs) const
Definition lam.cpp:39
const Def * body() const
Definition lam.h:124
Lam * curr_mut() const
Definition pass.h:307
World & world()
Definition pass.h:64
const Def * call(Id id, Args &&... args)
Complete curried call of annexes obeying implicits.
Definition world.h:546
const Def * rewrite(const Def *) override
void enter() override
Invoked just before Pass::rewriteing PassMan::curr_mut's body.
#define DLOG(...)
Vaporizes to nothingness in Debug build.
Definition log.h:95
The clos Plugin
Definition clos.h:7
ClosLit isa_clos_lit(const Def *def, bool fn_isa_lam=true)
Tries to match a closure literal.
Definition clos.cpp:59
Sigma * clos_type(const Pi *pi)
Creates a typed closure type from pi.
Definition clos.cpp:116
static constexpr size_t Clos_Env_Param
Describes where the environment is placed in the argument list.
Definition clos.h:107
const Def * clos_sub_env(const Def *tup_or_sig, const Def *new_env)
Definition clos.h:139
const Def * clos_pack(const Def *env, const Def *fn, const Def *ct=nullptr)
Pack a typed closure. This assumes that fn expects the environment as its Clos_Env_Paramth argument.
Definition clos.cpp:73
const Def * clos_apply(const Def *closure, const Def *args)
Apply a closure to arguments.
Definition clos.cpp:95
const Sigma * isa_clos_type(const Def *def)
Definition clos.cpp:106
const Def * op(trait o, const Def *type)
Definition core.h:33
const Def * op_slot(const Def *type, const Def *mem)
Definition mem.h:144
const Def * mem_var(Lam *lam)
Returns the memory argument of a function if it has one.
Definition mem.h:38
const Def * op_alloc(const Def *type, const Def *mem)
Definition mem.h:136
Lam * mut_con(World &w)
Yields con[mem.M].
Definition mem.h:16
View< const Def * > Defs
Definition def.h:76
Vector< const Def * > DefVec
Definition def.h:77
GIDSet< const Def * > DefSet
Definition def.h:74
@ Lam
Definition def.h:114
@ Extract
Definition def.h:114
@ App
Definition def.h:114
@ Tuple
Definition def.h:114