MimIR
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
rewrite.cpp
Go to the documentation of this file.
1#include "mim/rewrite.h"
2
3#include <absl/container/fixed_array.h>
4#include <fe/assert.h>
5
6#include "mim/world.h"
7
8// Don't use fancy C++-lambdas; it's way too annoying stepping through them in a debugger.
9
10namespace mim {
11
12/*
13 * Rewriter
14 */
15
16Rewriter::Rewriter(std::unique_ptr<World>&& ptr)
17 : ptr_(std::move(ptr))
18 , world_(ptr_.get()) {
19 push(); // create root map
20}
21
23 : world_(&world) {
24 push(); // create root map
25}
26
27Rewriter::~Rewriter() = default;
28
29void Rewriter::reset(std::unique_ptr<World>&& ptr) {
30 ptr_ = std::move(ptr);
31 world_ = ptr_.get();
32 reset();
33}
34
36 pop();
37 assert(old2news_.empty());
38 push();
39}
40
41const Def* Rewriter::map(const Def* old_def, Defs new_defs) {
42 auto new_tuple = world().tuple(new_defs);
43 return map(old_def, new_tuple);
44}
45const Def* Rewriter::map(Defs old_defs, const Def* new_def) {
46 auto old_tuple = world().tuple(old_defs);
47 return map(old_tuple, new_def);
48}
49const Def* Rewriter::map(Defs old_defs, Defs new_defs) {
50 auto old_tuple = world().tuple(old_defs);
51 auto new_tuple = world().tuple(new_defs);
52 return map(old_tuple, new_tuple);
53}
54
55const Def* Rewriter::rewrite(const Def* old_def) {
56 if (auto new_def = lookup(old_def)) return new_def;
57
58 auto new_def = old_def->isa_mut() ? rewrite_mut((Def*)old_def) : rewrite_imm(old_def);
59 return new_def->set(old_def->dbg_key());
60}
61
62// clang-format off
63#define CODE_MUT(N) case Node::N: new_def = rewrite_mut_##N(old_mut->as<N>()); break;
64#define CODE_IMM(N) case Node::N: new_def = rewrite_imm_##N(old_def->as<N>()); break;
65// clang-format on
66
67const Def* Rewriter::rewrite_imm(const Def* old_def) {
68 const Def* new_def;
69 switch (old_def->node()) {
71 default: fe::unreachable();
72 }
73 return map(old_def, new_def);
74}
75
76const Def* Rewriter::rewrite_mut(Def* old_mut) {
77 const Def* new_def;
78 switch (old_mut->node()) {
80 default: fe::unreachable();
81 }
82 return new_def;
83}
84
85#undef CODE_MUT
86#undef CODE_IMM
87
89 auto new_ops = DefVec(ops.size());
90 for (size_t i = 0, e = ops.size(); i != e; ++i)
91 new_ops[i] = rewrite(ops[i]);
92 return new_ops;
93}
94
95#ifndef DOXYGEN
96// clang-format off
97const Def* Rewriter::rewrite_imm_Idx (const Idx* ) { return world().type_idx(); }
98const Def* Rewriter::rewrite_imm_Nat (const Nat* ) { return world().type_nat(); }
99const Def* Rewriter::rewrite_imm_Univ (const Univ* ) { return world().univ(); }
100const Def* Rewriter::rewrite_imm_Lit (const Lit* d) { return world().lit (rewrite(d->type()), d->get()); }
101const Def* Rewriter::rewrite_imm_Match (const Match* d) { return world().match (rewrite(d->ops())); }
102const Def* Rewriter::rewrite_imm_Reform(const Reform* d) { return world().reform(rewrite(d->dom())); }
103const Def* Rewriter::rewrite_imm_Sigma (const Sigma* d) { return world().sigma (rewrite(d->ops())); }
104const Def* Rewriter::rewrite_imm_Type (const Type* d) { return world().type (rewrite(d->level())); }
105const Def* Rewriter::rewrite_imm_UInc (const UInc* d) { return world().uinc (rewrite(d->op()), d->offset()); }
106const Def* Rewriter::rewrite_imm_UMax (const UMax* d) { return world().umax (rewrite(d->ops())); }
107const Def* Rewriter::rewrite_imm_Uniq (const Uniq* d) { return world().uniq (rewrite(d->op())); }
108const Def* Rewriter::rewrite_imm_Var (const Var* d) { return world().var (rewrite(d->binder())->as_mut()); }
109const Def* Rewriter::rewrite_imm_Top (const Top* d) { return world().top (rewrite(d->type())); }
110const Def* Rewriter::rewrite_imm_Bot (const Bot* d) { return world().bot (rewrite(d->type())); }
111const Def* Rewriter::rewrite_imm_Meet (const Meet* d) { return world().meet (rewrite(d->ops())); }
112const Def* Rewriter::rewrite_imm_Join (const Join* d) { return world().join (rewrite(d->ops())); }
113
114const Def* Rewriter::rewrite_imm_Arr (const Arr* d) { return rewrite_imm_Seq(d); }
115const Def* Rewriter::rewrite_imm_Pack(const Pack* d) { return rewrite_imm_Seq(d); }
116const Def* Rewriter::rewrite_mut_Arr ( Arr* d) { return rewrite_mut_Seq(d); }
117const Def* Rewriter::rewrite_mut_Pack( Pack* d) { return rewrite_mut_Seq(d); }
118// clang-format on
119
120const Def* Rewriter::rewrite_imm_App(const App* d) {
121 // Rewrite the arg before the callee:
122 // the callee may be a recursive mutable that, when rewritten first, eagerly expands its body before the concrete
123 // argument is available to specialize it, breaking partial-evaluation termination
124 auto new_arg = rewrite(d->arg());
125 auto new_callee = rewrite(d->callee());
126 return world().app(new_callee, new_arg);
127}
128
129const Def* Rewriter::rewrite_imm_Inj(const Inj* d) {
130 auto new_type = rewrite(d->type());
131 auto new_value = rewrite(d->value());
132 return world().inj(new_type, new_value);
133}
134
135const Def* Rewriter::rewrite_imm_Insert(const Insert* d) {
136 auto new_tuple = rewrite(d->tuple());
137 auto new_index = rewrite(d->index());
138 auto new_value = rewrite(d->value());
139 return world().insert(new_tuple, new_index, new_value);
140}
141
142const Def* Rewriter::rewrite_imm_Lam(const Lam* d) {
143 auto new_type = rewrite(d->type())->as<Pi>();
144 auto new_filter = rewrite(d->filter());
145 auto new_body = rewrite(d->body());
146 return world().lam(new_type, new_filter, new_body);
147}
148
149const Def* Rewriter::rewrite_imm_Merge(const Merge* d) {
150 auto new_type = rewrite(d->type());
151 auto new_ops = rewrite(d->ops());
152 return world().merge(new_type, new_ops);
153}
154
155const Def* Rewriter::rewrite_imm_Pi(const Pi* d) {
156 auto new_dom = rewrite(d->dom());
157 auto new_codom = rewrite(d->codom());
158 return world().pi(new_dom, new_codom, d->is_implicit());
159}
160
161const Def* Rewriter::rewrite_imm_Proxy(const Proxy* d) {
162 auto new_type = rewrite(d->type());
163 auto new_ops = rewrite(d->ops());
164 return world().proxy(new_type, new_ops, d->tag());
165}
166
167const Def* Rewriter::rewrite_imm_Rule(const Rule* d) {
168 auto new_type = rewrite(d->type())->as<Reform>();
169 auto new_lhs = rewrite(d->lhs());
170 auto new_rhs = rewrite(d->rhs());
171 auto new_guard = rewrite(d->guard());
172 return world().rule(new_type, new_lhs, new_rhs, new_guard);
173}
174
175const Def* Rewriter::rewrite_imm_Split(const Split* d) {
176 auto new_type = rewrite(d->type());
177 auto new_value = rewrite(d->value());
178 return world().split(new_type, new_value);
179}
180
181const Def* Rewriter::rewrite_imm_Tuple(const Tuple* d) {
182 auto new_type = rewrite(d->type());
183 auto new_ops = rewrite(d->ops());
184 return world().tuple(new_type, new_ops);
185}
186
187const Def* Rewriter::rewrite_mut_Global(Global* d) {
188 return rewrite_stub(d, world().global(rewrite(d->type()), d->is_mutable()));
189}
190const Def* Rewriter::rewrite_mut_Lam(Lam* d) { return rewrite_stub(d, world().mut_lam(rewrite(d->type())->as<Pi>())); }
191const Def* Rewriter::rewrite_mut_Rule(Rule* d) {
192 return rewrite_stub(d, world().mut_rule(rewrite(d->type())->as<Reform>()));
193}
194
195const Def* Rewriter::rewrite_mut_Pi(Pi* d) {
196 if (d->is_immutabilizable()) return rewrite_imm_Pi(d);
197 return rewrite_stub(d, world().mut_pi(rewrite(d->type()), d->is_implicit()));
198}
199
200const Def* Rewriter::rewrite_mut_Sigma(Sigma* d) {
201 if (d->is_immutabilizable()) return rewrite_imm_Sigma(d);
202 return rewrite_stub(d, world().mut_sigma(rewrite(d->type()), d->num_ops()));
203}
204
205const Def* Rewriter::rewrite_imm_Axm(const Axm* a) {
206 if (&a->world() != &world()) {
207 auto type = rewrite(a->type());
208 return world().axm(a->normalizer(), a->curry(), a->trip(), type, a->plugin(), a->tag(), a->sub());
209 }
210 return a;
211}
212
213const Def* Rewriter::rewrite_imm_Extract(const Extract* ex) {
214 auto new_index = rewrite(ex->index());
215 if (auto index = Lit::isa(new_index)) {
216 if (auto tuple = ex->tuple()->isa<Tuple>()) return map(ex, rewrite(tuple->op(*index)));
217 if (auto pack = ex->tuple()->isa_imm<Pack>(); pack && pack->arity()->is_closed())
218 return map(ex, rewrite(pack->body()));
219 }
220
221 auto new_tuple = rewrite(ex->tuple());
222 return world().extract(new_tuple, new_index);
223}
224
225const Def* Rewriter::rewrite_mut_Hole(Hole* hole) {
226 auto [last, op] = hole->find();
227 return op ? rewrite(op) : rewrite_stub(last, world().mut_hole(rewrite(last->type())));
228}
229
230#endif
231
233 auto new_arity = rewrite(seq->arity());
234 if (auto l = Lit::isa(new_arity); l && *l == 0) return world().prod(seq->is_intro());
235 return world().seq(seq->is_intro(), new_arity, rewrite(seq->body()));
236}
237
239 if (seq->is_immutabilizable()) return rewrite_imm_Seq(seq);
240
241 if (!seq->is_set()) {
242 auto new_seq = world().mut_seq(seq->is_intro(), rewrite(seq->type()));
243 return map(seq, new_seq);
244 }
245
246 auto new_arity = rewrite(seq->arity())->zonk();
247 auto l = Lit::isa(new_arity);
248 if (l && *l == 0) return world().prod(seq->is_intro());
249
250 if (auto var = seq->has_var(); var && l && *l <= world().flags().scalarize_threshold) {
251 auto new_ops = absl::FixedArray<const Def*>(*l);
252 for (size_t i = 0, e = *l; i != e; ++i) {
253 push();
254 map(var, world().lit_idx(e, i));
255 new_ops[i] = rewrite(seq->body());
256 pop();
257 }
258 return map(seq, world().prod(seq->is_intro(), new_ops));
259 }
260
261 if (!seq->has_var()) return map(seq, world().seq(seq->is_intro(), new_arity, rewrite(seq->body())));
262 return rewrite_stub(seq, world().mut_seq(seq->is_intro(), rewrite(seq->type())));
263}
264
265const Def* Rewriter::rewrite_stub(Def* old_mut, Def* new_mut) {
266 map(old_mut, new_mut);
267
268 if (old_mut->is_set()) {
269 auto _ = enter(old_mut);
270 for (size_t i = 0, e = old_mut->num_ops(); i != e; ++i)
271 new_mut->set(i, rewrite(old_mut->op(i)));
272
273 // Immutabilize the *new* binder in hindsight:
274 // even when the old binder was not immutabilizable, rewriting may have made it vacuous.
275 if (new_mut->is_immutabilizable())
276 if (auto new_imm = new_mut->immutabilize()) return map(old_mut, new_imm);
277 }
278
279 return new_mut;
280}
281
282/*
283 * VarRewriter
284 */
285
286const Def* VarRewriter::rewrite(const Def* old_def) {
287 if (auto new_def = lookup(old_def)) return new_def;
288
289 if (auto old_mut = old_def->isa_mut())
290 return has_intersection(old_mut) ? rewrite_mut(old_mut)->set(old_mut->dbg_key()) : old_mut;
291
292 if (old_def->local_vars().empty() && old_def->local_muts().empty()) return old_def; // safe to skip
293
294 return has_intersection(old_def) ? rewrite_imm(old_def)->set(old_def->dbg_key()) : old_def;
295}
296
298 if (auto var = mut->has_var()) {
299 auto& vars = vars_.back();
300 vars = world().vars().insert(vars, var);
301 }
302
303 return Rewriter::rewrite_mut(mut);
304}
305
306/*
307 * Zonker
308 */
309
310const Def* Zonker::map(const Def* old_def, const Def* new_def) {
311 auto repr = lookup(new_def); // always normalize new_def to its representative
312 if (!repr) repr = new_def;
313 return old2news_.back()[old_def] = repr;
314}
315
316const Def* Zonker::lookup(const Def* old_def) {
317 for (auto& old2new : old2news_ | std::views::reverse) {
318 const Def* repr;
319 auto path = DefVec();
320 while (true) {
321 repr = get(old_def);
322
323 if (repr == nullptr) break;
324
325 path.emplace_back(repr);
326 if (repr == old_def) break; // explicit self-map
327
328 old_def = repr;
329 }
330
331 if (path.empty()) continue;
332
333 // path compression: flatten all visited nodes
334 for (auto def : path)
335 old2new[def] = repr;
336
337 return repr;
338 }
339
340 return nullptr;
341}
342
343const Def* Zonker::rewrite(const Def* def) {
344 if (auto hole = def->isa_mut<Hole>()) {
345 auto [last, op] = hole->find();
346 def = op ? op : last;
347 }
348
349 return def->needs_zonk() ? Rewriter::rewrite(def) : def;
350}
351
353 map(mut, mut);
354
355 auto old_type = mut->type();
356 auto old_ops = absl::FixedArray<const Def*>(mut->ops().begin(), mut->ops().end());
357
358 mut->unset()->set_type(rewrite(old_type));
359
360 for (size_t i = 0, e = mut->num_ops(); i != e; ++i)
361 mut->set(i, rewrite(old_ops[i]));
362
363 if (auto new_imm = mut->immutabilize()) return map(mut, new_imm);
364
365 return mut;
366}
367
368} // namespace mim
A (possibly paramterized) Array.
Definition tuple.h:110
Definition axm.h:9
Base class for all Defs.
Definition def.h:273
bool is_set() const
Definition def.h:370
constexpr Node node() const noexcept
Definition def.h:297
Def * set(size_t i, const Def *)
Successively set from left to right.
Definition def.cpp:196
T * as_mut() const
Asserts that this is a mutable, casts constness away and performs a static_cast to T.
Definition def.h:589
const Def * zonk() const
If Holes have been filled, reconstruct the program without them.
Definition check.cpp:21
Def * set_type(const Def *)
Update type.
Definition def.cpp:207
bool is_intro() const noexcept
Definition def.h:321
constexpr auto ops() const noexcept
Definition def.h:348
Vars local_vars() const
Vars reachable by following immutable deps().
Definition def.h:514
T * isa_mut() const
If this is mutable, it will cast constness away and perform a dynamic_cast to T.
Definition def.h:580
const Def * op(size_t i) const noexcept
Definition def.h:351
DbgKey dbg_key() const
Cheap handle for other->set(this->dbg_key()).
Definition def.h:610
bool is_immutabilizable()
Definition def.cpp:140
Muts local_muts() const
Mutables reachable by following immutable deps(); mut->local_muts() is by definition the set { mut }...
Definition def.h:507
const Def * type() const noexcept
Yields the "raw" type of this Def (maybe nullptr).
Definition def.h:1111
const Def * immutabilize()
Definition def.cpp:555
const Def * arity() const
Number of elements available to Extract / Insert (may be dynamic).
Definition def.cpp:592
Def * unset()
Unsets all Def::ops; works even, if not set at all or only partially set.
Definition def.cpp:213
bool needs_zonk() const
Yields true, if Def::local_muts() contain a Hole that is set.
Definition check.cpp:12
const Var * has_var()
Only returns not nullptr, if Var of this mutable has ever been created.
Definition def.h:483
constexpr size_t num_ops() const noexcept
Definition def.h:352
Extracts from a Sigma or Array-typed Extract::tuple the element at position Extract::index.
Definition tuple.h:161
This node is a hole in the IR that is inferred by its context later on.
Definition check.h:16
A built-in constant of type Nat -> *.
Definition def.h:974
Constructs a Join value.
Definition lattice.h:67
Creates a new Tuple / Pack by inserting Insert::value at position Insert::index into Insert::tuple.
Definition tuple.h:186
A function.
Definition lam.h:113
static std::optional< T > isa(const Def *def)
Definition def.h:937
Scrutinize Match::scrutinee() and dispatch to Match::arms.
Definition lattice.h:109
Constructs a Meet value.
Definition lattice.h:52
A (possibly paramterized) Tuple.
Definition tuple.h:137
A dependent function type.
Definition lam.h:14
Used as intermediate value during optimizatinos such as Analysis.
Definition def.h:1032
Type formation of a rewrite Rule.
Definition rule.h:9
virtual const Def * rewrite_imm_Seq(const Seq *seq)
Definition rewrite.cpp:232
virtual const Def * rewrite_mut_Seq(Seq *seq)
Definition rewrite.cpp:238
World & world()
Definition rewrite.h:35
virtual const Def * rewrite_mut(Def *)
Definition rewrite.cpp:76
virtual void push()
Definition rewrite.h:40
virtual const Def * rewrite_stub(Def *, Def *)
Definition rewrite.cpp:265
virtual const Def * map(const Def *old_def, const Def *new_def)
Definition rewrite.h:47
virtual void pop()
Definition rewrite.h:41
void reset(std::unique_ptr< World > &&ptr)
Definition rewrite.cpp:29
virtual ~Rewriter()
void reset()
Definition rewrite.cpp:35
std::deque< Def2Def > old2news_
Definition rewrite.h:106
virtual const Def * rewrite_imm(const Def *)
Definition rewrite.cpp:67
Rewriter(std::unique_ptr< World > &&ptr)
Definition rewrite.cpp:16
virtual const Def * rewrite(const Def *)
Definition rewrite.cpp:55
auto enter(Def *new_mut)
Updates curr_mut() to new_mut and restores it at the end of the scope.
Definition rewrite.h:109
virtual const Def * lookup(const Def *old_def)
Lookup old_def by searching in reverse through the stack of maps.
Definition rewrite.h:62
A rewrite rule.
Definition rule.h:40
Base class for Arr and Pack.
Definition tuple.h:75
const Def * body() const
Definition tuple.h:85
A dependent tuple type.
Definition tuple.h:23
Picks the aspect of a Meet [value](Pick::value) by its [type](Def::type).
Definition lattice.h:88
Data constructor for a Sigma.
Definition tuple.h:61
A singleton wraps a type into a higher order type.
Definition lattice.h:173
const Def * rewrite_mut(Def *) final
Definition rewrite.cpp:297
const Def * rewrite(const Def *) final
Definition rewrite.cpp:286
A variable introduced by a binder (mutable).
Definition def.h:825
The World represents the whole program and manages creation of MimIR nodes (Defs).
Definition world.h:40
const Def * insert(const Def *d, const Def *i, const Def *val)
Definition world.cpp:482
const Def * meet(Defs ops)
Definition world.h:599
const Def * uinc(const Def *op, level_t offset=1)
Definition world.cpp:151
const Lit * lit(const Def *type, u64 val)
Definition world.cpp:564
const Def * seq(bool is_pack, const Def *arity, const Def *body)
Definition world.cpp:535
const Type * type(const Def *level)
Definition world.cpp:140
const Proxy * proxy(const Def *type, Defs ops, flags_t tag)
Definition world.h:338
const Def * sigma(Defs ops)
Definition world.cpp:316
const Def * app(const Def *callee, const Def *arg)
Definition world.cpp:237
const Def * match(Defs)
Definition world.cpp:651
const Pi * pi(const Def *dom, const Def *codom, bool implicit=false)
Definition world.h:373
const Univ * univ()
Definition world.h:322
const Def * bot(const Def *type)
Definition world.h:591
const Idx * type_idx()
Definition world.h:617
Seq * mut_seq(bool is_pack, const Def *type)
Definition world.h:491
const Reform * reform(const Def *dom)
Definition world.h:429
const Nat * type_nat()
Definition world.h:616
const Lam * lam(const Pi *pi, Lam::Filter f, const Def *body)
Definition world.h:401
const Def * tuple(Defs ops)
Definition world.cpp:326
const Def * inj(const Def *type, const Def *value)
Definition world.cpp:636
const Axm * axm(NormalizeFn n, u8 curry, u8 trip, const Def *type, plugin_t p, tag_t t, sub_t s)
Definition world.h:355
const Def * extract(const Def *d, const Def *i)
Definition world.cpp:373
const Def * join(Defs ops)
Definition world.h:598
const Def * var(Def *mut)
Definition world.cpp:216
const Def * uniq(const Def *inhabitant)
Definition world.cpp:701
const Def * prod(bool term, Defs ops)
Definition world.h:481
const Def * umax(Defs)
Definition world.cpp:171
const Def * merge(const Def *type, Defs ops)
Definition world.cpp:618
const Def * top(const Def *type)
Definition world.h:592
auto & vars()
Definition world.h:694
const Def * split(const Def *type, const Def *value)
Definition world.cpp:644
const Rule * rule(const Reform *type, const Def *lhs, const Def *rhs, const Def *guard)
Definition world.h:431
const Def * rewire_mut(Def *)
Definition rewrite.cpp:352
const Def * lookup(const Def *old_def) final
Lookup old_def by searching in reverse through the stack of maps.
Definition rewrite.cpp:316
const Def * rewrite(const Def *) final
Definition rewrite.cpp:343
const Def * map(const Def *old_def, const Def *new_def) final
Definition rewrite.cpp:310
#define MIM_MUT_NODE(X)
Definition def.h:60
#define MIM_IMM_NODE(X)
Definition def.h:44
Definition ast.h:16
fe::View< const Def * > Defs
Definition def.h:91
TBound< true > Join
AKA union.
Definition lattice.h:167
TExt< true > Top
Definition lattice.h:165
fe::Vector< const Def * > DefVec
Definition def.h:93
TExt< false > Bot
Definition lattice.h:164
TBound< false > Meet
AKA intersection.
Definition lattice.h:166
@ Pi
Definition def.h:122
@ Reform
Definition def.h:122
#define CODE_MUT(N)
Definition rewrite.h:79
#define CODE_IMM(N)
Definition rewrite.h:78