MimIR
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
adce.cpp
Go to the documentation of this file.
1#include "mim/phase/adce.h"
2
3namespace mim {
4
5enum {
6 Proxy_Dead, // proxy(var) <- var is optimistically assumed dead
7};
8
9/*
10 * Analysis
11 */
12
15 visited_.clear();
16}
17
18bool ADCE::Analysis::is_dead(const Def* var) const {
19 auto l = lattice(var);
20 return l && Proxy::isa<Proxy_Dead>(l);
21}
22
23const Def* ADCE::Analysis::rewrite_imm_App(const App* app) {
24 auto abstr_arg = rewrite(app->arg());
25 auto abstr_callee = rewrite(app->callee());
26
27 if (auto lam = Lam::isa_rewritable(abstr_callee)) {
28 auto n = lam->num_tvars();
29 auto abstr_vars = DefVec(n, [&](size_t i) -> const Def* {
30 auto var = lam->tvar(i);
31 if (lam->is_dependent()) return pin(var), var;
32 if (auto l = lattice(var)) return l;
33 auto dead = world().proxy(var->type(), {var}, Proxy_Dead)->set(var->dbg_key());
34 return lattice(var, dead), dead;
35 });
36
37 if (n > 1) lattice(lam->var(), world().tuple(abstr_vars));
38 // raw_app: World::app would β-reduce or partially evaluate lam.
39 return world().raw_app(rewrite(app->type()), lam, abstr_arg);
40 }
41
42 return Super::rewrite_imm_App(app);
43}
44
46 for (auto def : world().externals().muts())
47 analyze(def);
48}
49
50void ADCE::Analysis::analyze(const Def* def) {
51 // A Var's lattice spells all of its projections; the live ones already reach us via their Extract.
52 if (def->isa<Var>()) return;
53 if (auto [_, ins] = visited_.emplace(def); !ins) return;
54 if (auto l = lookup(def); l && l != def) return analyze(l);
55
56 if (auto dead = def->isa<Proxy>()) {
57 if (dead->tag() == Proxy_Dead && pin(dead->op(0))) log().d("live: {}", dead->op(0));
58 return; // never walk a proxy's deps: its var would look live
59 }
60
61 if (auto app = def->isa<App>()) {
62 if (auto lam = Lam::isa_rewritable(app->callee())) {
63 analyze(app->type());
64
65 auto n = lam->num_tvars();
66 for (size_t i = 0; i != n; ++i)
67 if (!is_dead(lam->tvar(i))) analyze(app->arg(n, i));
68
69 for (auto d : lam->deps())
70 analyze(d);
71 return;
72 }
73 } else if (auto [lam, var] = def->isa_binder<Lam>(); lam && lam->is_rewritable()) {
74 log().d("unknown lam: {}", lam); // reached as a value, so its signature must stay untouched
75 for (auto v : lam->tvars())
76 pin(v);
77 }
78
79 for (auto d : def->deps())
80 analyze(d);
81}
82
83/*
84 * Transformation
85 */
86
87Sieve ADCE::sieve(Lam* lam) {
88 if (auto s = fe::lookup(lam2sieve_, lam)) return *s;
89
90 auto n = lam->num_tvars();
91 auto keep = !lam->is_rewritable() || !lam->has_var()
92 ? Sieve(n)
93 : Sieve(lam->tvars(), [this](const Def* var) { return !analysis_.is_dead(var); });
94 return lam2sieve_.emplace(lam, std::move(keep)).first->second;
95}
96
97Lam* ADCE::build_lam(Lam* old_lam) {
98 if (auto new_lam = fe::lookup(lam_old2new_, old_lam)) return new_lam;
99
100 invalidate();
101 auto keep = sieve(old_lam);
102 auto n = keep.num_old();
103 auto new_doms = rewrite(keep.gather(old_lam->doms(n)));
104 auto new_lam = new_world().mut_lam(new_doms, rewrite(old_lam->codom()))->set(old_lam->dbg());
105 log().d("{} → {}: {} of {} vars dead", old_lam, new_lam, n - keep.num_new(), n);
106 profile_count("adce.vars.eliminated", n - keep.num_new());
107 lam_old2new_[old_lam] = new_lam;
108 lam_new2old_[new_lam] = old_lam;
109
110 auto old_vars = old_lam->tvars();
111 auto new_vars = DefVec(n, [&](size_t i) -> const Def* {
112 if (auto j = keep[i]; j != Sieve::Gone) return new_lam->var(keep.num_new(), j)->set(old_vars[i]->dbg());
113 return new_world().bot(rewrite(old_lam->dom(n, i)));
114 });
115
116 map(old_lam->var(), new_vars);
117 auto _ = enter(old_lam);
118 new_lam->set(rewrite(old_lam->filter()), rewrite(old_lam->body()));
119 return new_lam;
120}
121
122const Def* ADCE::rewrite_imm_App(const App* old_app) {
123 auto old_lam = old_app->callee()->isa_mut<Lam>();
124 // The callee may only fold to a rebuilt Lam in the new World, e.g. a branch whose condition becomes constant.
125 if (!old_lam)
126 if (auto new_lam = rewrite(old_app->callee())->isa_mut<Lam>()) old_lam = fe::lookup(lam_new2old_, new_lam);
127
128 if (old_lam)
129 if (auto keep = sieve(old_lam); !keep.all()) {
130 auto new_lam = build_lam(old_lam);
131 return new_world().app(new_lam, rewrite(keep.gather(old_app->targs())));
132 }
133
134 return Super::rewrite_imm_App(old_app);
135}
136
137const Def* ADCE::rewrite_mut_Lam(Lam* old_lam) {
138 if (!sieve(old_lam).all()) return build_lam(old_lam);
139 return Super::rewrite_mut_Lam(old_lam);
140}
141
142} // namespace mim
const Def * rewrite_imm_App(const App *) final
Definition adce.cpp:122
const Def * rewrite_mut_Lam(Lam *) final
Definition adce.cpp:137
virtual void reset()
Clears the rewriter map and resets Phase::todo() for the next fixed-point iteration.
Definition phase.cpp:68
virtual void finalize()
Run after the main analysis - only in full rounds, so it always sees the complete abstract World.
Definition phase.h:268
auto targs(Projector auto f) const noexcept
Definition lam.h:252
const Def * callee() const
Definition lam.h:237
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
A function.
Definition lam.h:91
Lam * set(Filter filter, const Def *body)
Definition lam.cpp:34
static Lam * isa_rewritable(const Def *def)
Yields def as a mutable Lam, if it is_rewritable() and nullptr otherwise.
Definition lam.h:193
void invalidate(bool todo=true)
Signals that another round of fixed-point iteration is required, either as part of.
Definition phase.h:79
const fe::Log & log() const
Definition phase.h:60
void profile_count(std::string_view key, uint64_t n=1)
Adds n to the custom fe::Profiler counter key of the current run; no-op unless profiling is enabled.
Definition phase.cpp:60
World & world()
Definition phase.h:58
static const Proxy * isa(const Def *def)
Definition def.h:1089
virtual bool analyze()
Runs the optional pre-analysis on old_world(), typically to a fixed point, before rewriting begins.
Definition phase.cpp:202
World & new_world()
Create new Defs into this.
Definition phase.h:379
const Def * lookup(const Def *) override
Lookup old_def by searching in reverse through the stack of maps.
Definition phase.cpp:212
const Def * lattice(const Def *old_def) const
Returns the abstract value computed by the associated Analysis for the given old-world Def,...
Definition phase.h:347
const Def * rewrite_imm_App(const App *) override
Definition phase.cpp:223
const Def * map(const Def *old_def, const Def *new_def)
Definition rewrite.cpp:29
virtual const Def * rewrite_mut_Lam(Lam *)
virtual const Def * rewrite(const Def *)
Definition rewrite.cpp:62
auto enter(Def *new_mut)
Updates curr_mut() to new_mut and restores it at the end of the scope.
Definition rewrite.h:115
Sieve(size_t num)
Definition tuple.h:123
static constexpr size_t Gone
Definition tuple.h:118
const Def * app(const Def *callee, const Def *arg)
Definition world.cpp:416
const Def * bot(const Def *type)
Definition world.h:604
const Proxy * proxy(const Def *type, Defs ops, u32 tag, bool vars=false, bool muts=false)
Definition world.h:357
const Def * raw_app(const Axm *axm, u8 curry, u8 trip, const Def *type, const Def *callee, const Def *arg)
Definition world.cpp:501
Lam * mut_lam(const Pi *pi)
Definition world.h:415
Definition ast.h:16
@ Proxy_Dead
Definition adce.cpp:6
fe::Vector< const Def * > DefVec
Definition def.h:98
@ Lam
Definition def.h:125