12 std::ranges::sort(defs, {}, &
Def::gid);
13 defs.erase(std::ranges::unique(defs).begin(), defs.end());
20 if (def->isa<
Var>() || !def->
type())
return res;
23 if (!may_hold(def->
type())) {
25 if (
auto app = res->isa<
App>(); app && !
Pi::isa_cn(app->callee()->type()) && !
mem_def(app->arg()))
31 if (
auto app = res->isa<
App>()) {
33 carried.emplace_back(app);
35 join(carried, app->arg());
36 if (!app->callee()->isa_mut<
Lam>()) join(carried, app->callee());
41 for (
auto op : res->ops())
46 if (carried.empty())
return res;
47 if (res != def) aliases_[res] = carried;
48 aliases_[def] = std::move(carried);
59 if (!lam || !lam->is_set())
return;
61 auto tail = lam->body()->isa<
App>();
62 auto ops = tail ? walk(lam, tail) : std::nullopt;
64 if (!tail)
return escape(lam->body());
65 escape(tail->callee());
70 auto escaping = escaping_.contains(lam);
71 auto& chain = chains_[lam] =
Chain{tail, std::move(*ops), {}, {}};
72 auto own =
DefSet(chain.ops.begin(), chain.ops.end());
73 for (
size_t i = 0, n = chain.ops.size(); i != n; ++i) {
74 auto op = chain.ops[i];
76 join(carried, op->callee());
77 join(carried, op->arg());
79 for (
auto d : carried) {
80 if (
auto k = d->isa_mut<
Lam>()) {
87 else if (!own.contains(d))
93 escape(
store->arg(3, 2));
100 if (
auto known = jumps(tail->callee()); !known.empty()) {
102 jump(k, tail, chain.targets);
104 control(tail->callee(), chain.targets);
105 for (
auto arg : tail->targs())
106 pass(
rewrite(arg), chain.targets);
110 for (
auto target : chain.targets)
111 if (
auto k = target->isa_mut<
Lam>()) escape(k);
114bool EscapeAnalysis::may_hold(
const Def* type)
const {
116 if (
auto sigma = type->isa<
Sigma>())
117 return std::ranges::any_of(sigma->ops(), [
this](
const Def* op) { return may_hold(op); });
118 if (
auto arr = type->isa<
Arr>())
return may_hold(arr->body());
122std::optional<fe::Vector<const App*>> EscapeAnalysis::walk(
Lam* lam,
const App* tail) {
123 auto ops = fe::Vector<const App*>();
125 if (
auto mem = exit_mem(tail, done);
mem && !walk(lam,
mem, ops))
return {};
126 std::ranges::reverse(ops);
130const Def* EscapeAnalysis::walk(Lam* lam,
const Def* mem, fe::Vector<const App*>& ops) {
132 auto lvar = lam->has_var();
134 if (!var && (!lvar || !mem->has_free_var(lvar)))
break;
135 auto ex = mem->isa<Extract>();
136 auto op = ex ? ex->tuple()->isa<App>() : mem->isa<App>();
137 if (!op || !(mem =
mem_def(op->arg())))
return nullptr;
138 ops.emplace_back(op);
143const Def* EscapeAnalysis::exit_mem(
const App* tail,
LamSet& done) {
146 const Def* res =
nullptr;
147 for (
auto arg :
tail->targs())
148 if (
auto k = arg->isa_mut<Lam>(); k && k->is_set() && !
mem_var(k) && done.emplace(k).second)
149 if (
auto body = k->body()->isa<App>())
150 if (
auto mem = exit_mem(body, done)) {
151 auto ops = fe::Vector<const App*>();
152 auto start = walk(k, mem, ops);
153 if (!
start || (res &&
start != res))
return nullptr;
159void EscapeAnalysis::join(
DefVec& carried,
const Def* def)
const {
160 if (
auto lam = def->isa_mut<Lam>()) {
161 if (!lam->is_closed()) carried.emplace_back(lam);
163 if (
auto val =
lattice(def); val && val != def) join(carried, val);
165 if (
auto lam = var->binder()->isa_mut<Lam>())
166 for (
auto tvar : lam->tvars())
167 if (
auto val =
lattice(tvar); val && val != tvar) join(carried, val);
168 }
else if (
auto i = aliases_.find(def); i != aliases_.end()) {
169 carried.insert(carried.end(), i->second.begin(), i->second.end());
173void EscapeAnalysis::escape(
const Def* def) {
176 for (
auto d : carried)
177 if (
auto lam =
d->isa_mut<Lam>())
183fe::Vector<Lam*> EscapeAnalysis::jumps(
const Def* callee) {
184 auto known = [](
const Def* def) -> Lam* {
189 if (
auto k = known(callee))
return {k};
190 auto res = fe::Vector<Lam*>();
191 if (
auto sw =
Switch(callee)) {
192 for (
size_t i = 0, n = sw.num_targets(); i != n; ++i) {
193 auto k = known(sw.target(i));
201void EscapeAnalysis::jump(Lam* k,
const App* tail,
DefVec& targets) {
202 targets.emplace_back(k);
203 for (
size_t i = 0, n = k->num_tvars(); i != n; ++i) {
205 auto var = k->tvar(i);
207 control(arg, targets);
208 else if (may_hold(var->type()))
209 propagate(k, var, arg);
213void EscapeAnalysis::propagate(Lam* k,
const Def* var,
const Def* val) {
214 if (val == var)
return;
216 if (cur == var)
return escape(val);
217 auto outside = val->in_scope(k) && !val->free_vars().contains(k->has_var());
218 if (outside && !cur) {
222 if (outside && cur == val)
return;
224 if (cur) escape(cur);
228void EscapeAnalysis::control(
const Def* def,
DefVec& targets) {
229 if (def->is_closed())
return;
230 if (
auto tuple = def->isa<Tuple>()) {
231 for (
auto op : tuple->ops())
232 control(op, targets);
233 }
else if (
auto sw =
Switch(def)) {
234 for (
size_t i = 0, n = sw.num_targets(); i != n; ++i)
235 control(sw.target(i), targets);
236 }
else if (
auto app = def->isa<App>(); app && app->axm()) {
237 control(app->callee(), targets);
238 pass(app->arg(), targets);
240 targets.emplace_back(def);
241 if (!def->isa_mut()) escape(def);
const Def * rewrite(const Def *) override
Rewrites def and then maps the result to its repr().
const auto & lattice() const
The whole map; used e.g. to diff two fixed-point runs.
bool pin(const Def *def)
Monotonically forces def to ⊤ (keep as is).
A (possibly paramterized) Array.
static auto isa(const Def *def)
const Def * type() const noexcept
Yields the "raw" type of this Def (maybe nullptr).
constexpr u32 gid() const noexcept
Global id - unique number for this Def.
bool is_dependent() const
Does this type() itself or dom() has a Var?
static Lam * isa_rewritable(const Def *def)
Yields def as a mutable Lam, if it is_rewritable() and nullptr otherwise.
virtual void start()=0
Actual entry.
A dependent function type.
static const Pi * isa_cn(const Def *d)
virtual const Def * rewrite_imm(const Def *)
virtual const Def * rewrite_imm_App(const App *)
A variable introduced by a binder (mutable).
static std::pair< const Var *, std::optional< nat_t > > isa_tproj(const Def *def)
Matches a Var v itself or an Extract v#j of it.
const Def * rewrite_imm_App(const App *) override
static bool allocates(const App *app)
void leave() override
Called after curr_mut() has been completely dealt with.
bool escaping(Lam *lam) const
May lam run at any time, as it is used as a value or jumped to by an escaping Lam?
static const App * freed_alloc(const Def *def)
The allocation def releases if it is a mem.free of one; nullptr otherwise.
const Def * rewrite_imm(const Def *) override
Records what each value may carry: the allocations it aliases and the nested Lams in it.
const Def * mem_var(Lam *lam)
Returns the memory argument of a function if it has one.
const Def * mem_def(const Def *def)
Returns the (first) element of type mem.M a from the given tuple.
fe::Vector< const Def * > DefVec
GIDSet< const Def * > DefSet