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();
16 const Def* mem =
nullptr;
17 const Def* env =
nullptr;
19 for (
size_t i = 0, n = def->num_projs(); i < n; i++) {
20 auto op = def->proj(n, i);
23 else if (i == Sjlj_Env_Param)
30 auto remaining = new_ops.size() == 1 ? new_ops[0] : def->is_intro() ?
w.tuple(new_ops) :
w.sigma(new_ops);
31 return {mem, env, remaining};
34const Def* rebuild(
const Def* mem,
const Def* env,
Defs remaining) {
35 auto&
w = mem->world();
36 auto new_ops =
DefVec(remaining.size() + 2, [&](
auto i) ->
const Def* {
37 static_assert(Sjlj_Env_Param == 1);
38 if (i == 0) return mem;
39 if (i == 1) return env;
40 return remaining[i - 2];
42 return w.tuple(new_ops);
47void Clos2SJLJ::get_exn_closures(
const Def* def,
DefSet& visited) {
48 if (!def->is_term() || def->isa_mut<Lam>() || visited.contains(def))
return;
51 auto lam =
c.fnc_as_lam();
52 if (
c.is_basicblock() && !ignore_.contains(lam)) {
53 log().d(
"exception closure: {}",
c.fnc_as_lam());
54 lam2tag_[
c.fnc_as_lam()] = {lam2tag_.size() + 1,
c.env()};
56 get_exn_closures(
c.env(), visited);
58 for (
auto op : def->ops())
59 get_exn_closures(op, visited);
63void Clos2SJLJ::get_exn_closures(
const Def* body) {
65 auto app = body->isa<
App>();
67 if (
auto p = app->callee()->isa<Extract>(); p &&
isa_clos_type(p->tuple()->type())) {
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 log().d(
"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;
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;
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->projs());
116 lpad->app(
false, lam, full_args);
117 ignore_.emplace(lpad);
122std::pair<const Def*, const Def*> Clos2SJLJ::convert(Lam* lam,
const Def* filter,
const Def* body) {
125 get_exn_closures(body);
126 if (lam2tag_.empty())
return {filter, body};
132 auto new_args = lam->vars();
134 auto rw =
VarRewriter(lam->has_var(),
w.tuple(new_args));
135 filter = rw.rewrite(filter);
136 body = rw.rewrite(body);
142 get_exn_closures(body);
145 auto app = body->as<
App>();
148 auto branches =
DefVec(lam2tag_.size() + 1);
150 auto env =
w.tuple(app->args().view().subspan(1));
152 auto [m, env_var, _] = split(new_callee->var());
153 auto ne = env->num_projs();
154 auto new_args =
DefVec(ne + 1, [&](
size_t i) {
return (i == 0) ? m : env_var->proj(ne, i - 1); });
155 new_callee->app(
false, app->callee(), new_args);
156 branches[0] =
clos_pack(env, new_callee, branch_type);
159 for (
auto [exn_lam, p] : lam2tag_) {
161 branches[i] =
clos_pack(env, get_lpad(exn_lam, cur_rbuf_), branch_type);
164 auto m0 = app->arg(app->num_args(), 0);
165 assert(m0->type() ==
w.call<
mem::M>(0));
166 auto [m1, tag] =
w.call<
setjmp>(
Defs{m0, cur_jbuf_})->projs<2>();
168 auto branch =
w.extract(
w.tuple(branches), tag);
172 Vars(lam->has_var()));
173 body = subst_exn_closures(
clos_apply(branch, m1));
175 return {filter, body};
183const Def* Clos2SJLJ::subst_exn_closures(
const Def* def) {
184 if (
auto new_def =
lookup(def))
return new_def;
185 if (
auto c =
isa_clos_lit(def); c && lam2tag_.contains(c.fnc_as_lam())) {
187 auto [i, _] = lam2tag_[c.fnc_as_lam()];
188 auto tlam = get_throw(c.fnc_as_lam()->dom());
189 return map(def,
clos_pack(w.tuple({cur_jbuf_, cur_rbuf_, w.lit_idx(i)}), tlam, c.type()));
192 if (def->isa<
Var>())
return def;
198 map(old_lam, new_lam);
199 if (!old_lam->
is_set())
return new_lam;
201 auto _ =
enter(old_lam);
203 new_lam->set(filter, body);
204 return seal(old_lam, new_lam);
Def * set(size_t i, const Def *)
Successively set from left to right.
World & world() const noexcept
T * isa_mut() const
If this is mutable, it will cast constness away and perform a dynamic_cast to T.
bool is_term() const
Is this Def a term, i.e. is its type() a Type?
DbgKey dbg_key() const
Cheap handle for other->set(this->dbg_key()).
auto projs(Projector auto f) const
Splits this Def via Def::projections into an Array (if A == std::dynamic_extent) or std::array (other...
bool is_closed() const
Same as !has_free_vars().
const Def * filter() const
static const Lam * isa_cn(const Def *d)
Lam * set(Filter filter, const Def *body)
const fe::Log & log() const
const fe::Vector< std::string > & args()
Command-line arguments passed to this Phase's plugin via -X <plugin>:<arg>.
A dependent function type.
World & new_world()
Create new Defs into this.
const Def * lookup(const Def *) override
Lookup old_def by searching in reverse through the stack of maps.
const Def * map(const Def *old_def, const Def *new_def)
const Def * seal(Def *old_mut, Def *new_mut)
The tail of rewrite_stub: immutabilizes new_mut in hindsight, as rewriting may have made it vacuous.
virtual const Def * rewrite_imm(const Def *)
virtual const Def * rewrite(const Def *)
auto enter(Def *new_mut)
Updates curr_mut() to new_mut and restores it at the end of the scope.
void push(Vars old_vars={}, Vars new_vars={})
VarRewriter(World &world)
A variable introduced by a binder (mutable).
Lam * mut_lam(const Pi *pi)
const Def * rewrite(const Def *) final
const Def * rewrite_mut_Lam(Lam *) final
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)
fe::View< const Def * > Defs
fe::Vector< const Def * > DefVec
GIDSet< const Def * > DefSet
fe::Patricia< const Var, DefKey >::Set Vars