11constexpr size_t Sjlj_Env_Param = 1_u64;
13std::array<const Def*, 3> split(
const Def* def) {
14 auto new_ops =
DefVec(def->num_projs() - 2,
nullptr);
15 auto&
w = def->world();
18 for (
size_t i = 0; i < def->num_projs(); i++) {
19 auto op = def->proj(i);
22 else if (i == Sjlj_Env_Param)
29 auto remaining = new_ops.size() == 1 ? new_ops[0] : def->is_intro() ?
w.tuple(new_ops) :
w.sigma(new_ops);
30 return {mem, env, remaining};
33const Def* rebuild(
const Def* mem,
const Def* env,
Defs remaining) {
34 auto&
w = mem->world();
35 auto new_ops =
DefVec(remaining.size() + 2, [&](
auto i) ->
const Def* {
36 static_assert(Sjlj_Env_Param == 1);
37 if (i == 0) return mem;
38 if (i == 1) return env;
39 return remaining[i - 2];
41 return w.tuple(new_ops);
46void Clos2SJLJ::get_exn_closures(
const Def* def,
DefSet& visited) {
47 if (!def->is_term() || def->isa_mut<
Lam>() || visited.contains(def))
return;
50 auto lam =
c.fnc_as_lam();
51 if (
c.is_basicblock() && !ignore_.contains(lam)) {
52 DLOG(
"FOUND exn closure: {}",
c.fnc_as_lam());
53 lam2tag_[
c.fnc_as_lam()] = {lam2tag_.size() + 1,
c.env()};
55 get_exn_closures(
c.env(), visited);
57 for (
auto op : def->ops())
58 get_exn_closures(op, visited);
62void Clos2SJLJ::get_exn_closures(
Lam* lam) {
65 auto app = lam->body()->isa<
App>();
68 auto p2 = p->tuple()->isa<
Extract>();
69 if (p2 && p2->tuple()->isa<
Tuple>()) {
72 auto branches = p2->tuple()->ops();
73 for (
auto b : branches) {
76 ignore_.emplace(
c.fnc_as_lam());
77 DLOG(
"IGNORE {}",
c.fnc_as_lam());
83 get_exn_closures(app->arg(), visited);
86Lam* Clos2SJLJ::get_throw(
const Def* dom) {
88 auto [p, inserted] = dom2throw_.emplace(dom,
nullptr);
89 auto& tlam = p->second;
90 if (inserted || !tlam) {
91 tlam =
w.mut_con(
clos_sub_env(Sjlj_Env_Param, dom,
w.sigma({jb_type(), rb_type(), tag_type()})))->set(
"throw");
92 auto [m0, env, var] = split(tlam->var());
93 auto [jbuf, rbuf, tag] = env->projs<3>();
99 ignore_.emplace(tlam);
104Lam* Clos2SJLJ::get_lpad(
Lam* lam,
const Def* rb) {
106 auto [p, inserted] = lam2lpad_.emplace(
w.tuple({lam, rb}),
nullptr);
107 auto& lpad = p->second;
108 if (inserted || !lpad) {
109 auto [_, env_type, dom] = split(lam->dom());
111 auto [m, env, __] = split(lpad->var());
115 auto full_args = (lam->num_doms() == 3) ? rebuild(m2, env, {
args}) : rebuild(m2, env,
args->ops());
116 lpad->app(
false, lam, full_args);
117 ignore_.emplace(lpad);
122void Clos2SJLJ::convert(
Lam* lam) {
124 get_exn_closures(lam);
125 if (lam2tag_.empty())
return;
131 auto new_args = lam->vars();
133 auto new_defs = lam->reduce(
w.tuple(new_args));
134 lam->unset()->set(new_defs);
140 get_exn_closures(lam);
143 auto body = lam->body()->as<
App>();
146 auto branches =
DefVec(lam2tag_.size() + 1);
148 auto env =
w.tuple(body->args().view().subspan(1));
150 auto [m, env_var, _] = split(new_callee->var());
151 auto new_args =
DefVec(env->num_projs() + 1, [&](
size_t i) { return (i == 0) ? m : env_var->proj(i - 1); });
152 new_callee->app(
false, body->callee(), new_args);
153 branches[0] =
clos_pack(env, new_callee, branch_type);
156 for (
auto [exn_lam, p] : lam2tag_) {
158 branches[i] =
clos_pack(env, get_lpad(exn_lam, cur_rbuf_), branch_type);
161 auto m0 = body->arg(0);
162 assert(m0->type() ==
w.call<
mem::M>(0));
163 auto [m1, tag] =
w.call<
setjmp>(
Defs{m0, cur_jbuf_})->projs<2>();
165 auto filter = lam->filter();
166 auto branch =
w.extract(
w.tuple(branches), tag);
167 lam->unset()->set({filter,
clos_apply(branch, m1)});
171 auto new_body = subst_exn_closures(lam->body(), memo);
172 lam->unset()->set({filter, new_body});
177const Def* Clos2SJLJ::subst_exn_closures(
const Def* def,
Def2Def& memo) {
178 if (
auto i = memo.find(def); i != memo.end())
return i->second;
179 if (
auto c =
isa_clos_lit(def);
c && lam2tag_.contains(
c.fnc_as_lam())) {
181 auto [i, _] = lam2tag_[
c.fnc_as_lam()];
182 auto tlam = get_throw(
c.fnc_as_lam()->dom());
183 return memo[def] =
clos_pack(
w.tuple({cur_jbuf_, cur_rbuf_, w.lit_idx(i)}), tlam,
c.type());
185 if (def->isa_mut() || !def->is_term())
return def;
186 if (def->isa<
Var>())
return def;
187 auto new_ops =
DefVec(def->num_ops(), [&](
size_t i) { return subst_exn_closures(def->op(i), memo); });
188 return memo[def] = def->rebuild(def->type(), new_ops);
192 auto new_def = RWPhase::rewrite_mut_Lam(old);
auto projs(F f) const
Splits this Def via Def::projections into an Array (if A == std::dynamic_extent) or std::array (other...
static const Lam * isa_cn(const Def *d)
Lam * set(Filter filter, const Def *body)
const Vector< std::string > & args()
Command-line arguments passed to this Phase's plugin via -X <plugin>:<arg>.
World & new_world()
Create new Defs into this.
bool is_bootstrapping() const
Returns whether we are currently bootstrapping (rewriting annexes).
const Def * rewrite_mut_Lam(Lam *) final
#define DLOG(...)
Vaporizes to nothingness in Debug build.
ClosLit isa_clos_lit(const Def *def, bool fn_isa_lam=true)
Tries to match a closure literal.
const Def * clos_sub_env(size_t ep, const Def *tup_or_sig, const Def *new_env)
Sigma * clos_type(const Pi *pi)
Creates a typed closure type from pi.
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.
const Sigma * isa_clos_type(const Def *def)
const Def * mem_var(Lam *lam)
Returns the memory argument of a function if it has one.
Lam * mut_con(World &w, nat_t a=0)
Yields con[mem.M 0].
const Def * op_alloc(const Def *type, const Def *as, const Def *mem)
DefMap< const Def * > Def2Def
Vector< const Def * > DefVec
GIDSet< const Def * > DefSet