MimIR
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
phase.h
Go to the documentation of this file.
1#pragma once
2
3#include <memory>
4
5#include <fe/assert.h>
6#include <fe/cast.h>
7
8#include "mim/def.h"
9#include "mim/nest.h"
10#include "mim/pass.h"
11#include "mim/rewrite.h"
12
13namespace mim {
14
15class Nest;
16class Phase;
17class PhaseMan;
18class Repl;
19class World;
20
21using Repls = std::deque<std::unique_ptr<Repl>>;
22using Phases = std::deque<std::unique_ptr<Phase>>;
23
24/// Unlike a Pass, a Phase performs one self-contained task and does not
25/// interleave with other phases. Phases are intended to run in a classical sequence, one after another.
26/// @see @ref phases_phase
27class Phase : public Stage {
28public:
29 /// @name Construction & Destruction
30 ///@{
31 Phase(World& world, std::string name)
32 : Stage(world, name) {}
35
36 ///@}
37
38 /// @name Fixed-Point Handling
39 ///@{
40 bool todo() const { return todo_; }
41
42 /// Signals that another round of fixed-point iteration is required, either
43 /// as part of
44 /// - a pipeline managed by PhaseMan, or
45 /// - the optional pre-analysis of an RWPhase.
46 ///
47 /// Calling `invalidate(todo)` bitwise-ORs @p todo into the internal `todo_` flag.
48 void invalidate(bool todo = true) { todo_ |= todo; }
49 ///@}
50
51 /// @name run
52 ///@{
53 virtual void run(); ///< Entry point and generates some debug output; invokes Phase::start.
54 virtual void start() = 0; ///< Actual entry.
55
56 /// Runs a single Phase.
57 template<class P, class... Args>
58 static void run(Args&&... args) {
59 P p(std::forward<Args>(args)...);
60 p.run();
61 }
62 ///@}
63
64private:
65 bool todo_ = false;
66
67 friend class Analysis;
68};
69
70/// Traverses the current World using Rewriter infrastructure while staying in the same world.
71///
72/// It recursively rewrites
73/// 1. all World::annexes() (during which Analysis::is_bootstrapping() is `true`), and then
74/// 2. all World::externals() (during which it is `false`).
75///
76/// Analysis provides a reusable lattice() mapping old Def%s to abstract values, represented as ordinary MimIR Def%s.
77/// @note You can override
78/// - Rewriter::rewrite(),
79/// - Rewriter::rewrite_imm(),
80/// - Rewriter::rewrite_mut(), etc.
81/// @see @ref phases_analysis
82class Analysis : public Phase, public Rewriter {
83public:
84 /// @name Construction & Destruction
85 ///@{
86 Analysis(World& world, std::string name)
87 : Phase(world, std::move(name))
88 , Rewriter(world) {}
92
93 /// Clears the rewriter map and resets Phase::todo() for the next fixed-point iteration.
94 /// lattice() is **preserved** across iterations so that abstract values accumulated in earlier
95 /// rounds remain available - this is what makes fixed-point convergence possible.
96 /// @see RWPhase::analyze
97 virtual void reset();
98 ///@}
99
100 /// @name Getters
101 ///@{
102 World& world() { return Phase::world(); }
103 Def* curr_mut() const { return curr_mut_; }
104 bool is_bootstrapping() const { return bootstrapping_; }
105 ///@}
106
107 /// @name lattice
108 ///@{
109 auto& lattice() { return lattice_; }
110 const auto& lattice() const { return lattice_; }
111
112 /// Records the abstract value @p abstr for @p concr in both lattice() (the analysis result)
113 /// and map() (so the rewriter short-circuits future rewrites of @p concr to @p abstr).
114 void set(const Def* concr, const Def* abstr) {
115 lattice_[concr] = abstr;
116 map(concr, abstr);
117 }
118 ///@}
119
120protected:
121 /// Helps to keep track of curr_mut().
122 /// @see enter()
123 class Enter {
124 public:
125 Enter(Analysis* analysis, Def* new_mut)
126 : analysis_(analysis)
127 , prev_mut_(analysis->curr_mut()) {
128 analysis->curr_mut_ = new_mut;
129 }
130 ~Enter() { analysis_->curr_mut_ = prev_mut_; }
131
132 private:
133 Analysis* analysis_;
134 Def* prev_mut_;
135 };
136
137 /// @name Rewrite
138 ///@{
139 void start() override;
140 Enter enter(Def* new_mut) { return {this, new_mut}; } //< Updates curr_mut() to @p new_mut.
141 virtual void rewrite_annex(flags_t, Sym, const Def*);
142 virtual void rewrite_external(Def*);
143
144 /// Walks @p mut's dependencies under its curr_mut() scope.
145 /// Unlike rewrite_mut(), does **not** record `mut -> mut` and does **not** seed
146 /// any binder-related lattice state.
147 ///
148 /// Use this when you have already populated custom lattice entries for @p mut's
149 /// binder (typically inside a `rewrite_imm_App` override that propagates abstract
150 /// values from call arguments into the callee's tvars) and need to traverse the
151 /// body without rewrite_mut() clobbering that state.
152 virtual Def* rewrite_deps(Def*);
153
154 /// Default "visit a mutable" entry point: maps `mut -> mut`, seeds Lam binder
155 /// vars to **top** (`v -> v`) in the lattice, and delegates to rewrite_deps()
156 /// for the recursive traversal.
157 ///
158 /// If a binder var already carried a non-top lattice value, it is reset to top
159 /// and invalidate() is called: reaching a Lam through this default path means
160 /// it has been used as a value (not as an `App` callee) and has therefore
161 /// escaped, so any prior propagation for it is unsound and must be retracted.
162 Def* rewrite_mut(Def*) override;
163 ///@}
164
166
167private:
168 Def* curr_mut_ = nullptr;
169 bool bootstrapping_ = true;
170
171 friend class Enter;
172};
173
174/// Rebuilds old_world() into new_world() and then swaps them.
175///
176/// It recursively rewrites
177/// 1. all old World::annexes() (during which RWPhase::is_bootstrapping() is `true`, and then
178/// 2. all old World::externals() (during which it is `false`).
179///
180/// During bootstrapping, rewrites that depend on other annexes may need to be skipped,
181/// since those annexes might not yet exist in the new world.
182///
183/// If an associated Analysis is provided, the rewrite can query its abstract results through lattice().
184///
185/// @note You can override
186/// - Rewriter::rewrite(),
187/// - Rewriter::rewrite_imm(),
188/// - Rewriter::rewrite_mut(), etc.
189/// @see @ref phases_rwphase
190class RWPhase : public Phase, public Rewriter {
191public:
192 /// @name Construction
193 ///@{
194 RWPhase(World& world, std::string name, Analysis* analysis = nullptr)
195 : Phase(world, std::move(name))
196 , Rewriter(world.inherit())
197 , analysis_(analysis) {}
199 : Phase(world, annex)
200 , Rewriter(world.inherit())
201 , analysis_(analysis) {}
202 ///@}
203
204 /// @name Analysis
205 ///@{
206 Analysis* analysis() { return analysis_; }
207 const Analysis* analysis() const { return analysis_; }
208
209 /// Returns the abstract value computed by the associated Analysis for the given old-world Def, or `nullptr` if no
210 /// value is available.
211 const Def* lattice(const Def* old_def) {
212 if (auto i = analysis_->lattice().find(old_def); i != analysis_->lattice().end()) return i->second;
213 return nullptr;
214 }
215
216 /// Runs the optional pre-analysis on RWPhase::old_world(), typically to a fixed point,
217 /// before rewriting begins.
218 ///
219 /// If analysis() is set, this is the natural place to iterate until Phase::todo() becomes `false`.
220 /// If no Analysis is needed, simply return `false`.
221 virtual bool analyze();
222 ///@}
223
224 /// @name Rewrite
225 ///@{
226 virtual void rewrite_annex(flags_t, Sym, const Def*);
227 virtual void rewrite_external(Def*);
228
229 /// Returns whether we are currently bootstrapping (rewriting annexes).
230 /// While bootstrapping, you have to skip rewrites that refer to other annexes, as they might not yet be available.
231 bool is_bootstrapping() const { return bootstrapping_; }
232 ///@}
233
234 /// @name World
235 /// * Phase::world is the **old** one.
236 /// * Rewriter::world is the **new** one.
237 /// * RWPhase::world is deleted to not confuse this.
238 ///@{
239 using Phase::world;
240 using Rewriter::world;
241 World& world() = delete; ///< Hides both and forbids direct access.
242 World& old_world() { return Phase::world(); } ///< Get **old** Def%s from here.
243 World& new_world() { return Rewriter::world(); } ///< Create **new** Def%s into this.
244 ///@}
245
246protected:
247 void start() override;
248
249private:
250 Analysis* analysis_;
251 bool bootstrapping_ = true;
252};
253
254/// Simple Stage that searches for a pattern and replaces it.
255/// Combine them in a ReplPhase.
256class Repl : public Stage {
257public:
260
261 virtual const Def* replace(const Def* def) = 0;
262};
263
264class ReplMan : public Repl {
265public:
268
269 void apply(Repls&&);
270 void apply(const App*) final;
271 void apply(Stage& stage) final { apply(std::move(static_cast<ReplMan&>(stage).repls_)); }
272
273 void add(std::unique_ptr<Repl>&& repl) { repls_.emplace_back(std::move(repl)); }
274 const auto& repls() const { return repls_; }
275
276private:
277 const Def* replace(const Def*) final { fe::unreachable(); }
278
279 Repls repls_;
280};
281
282#define MIM_CONCAT_INNER(a, b) a##b
283#define MIM_CONCAT(a, b) MIM_CONCAT_INNER(a, b)
284
285#define MIM_REPL(__stages, __annex, ...) MIM_REPL_IMPL(__stages, __annex, __LINE__, __VA_ARGS__)
286
287// clang-format off
288#define MIM_REPL_IMPL(__stages, __annex, __id, ...) \
289 struct MIM_CONCAT(Repl_, __id) : ::mim::Repl { \
290 MIM_CONCAT(Repl_, __id)(::mim::World & world, ::mim::flags_t annex) \
291 : Repl(world, annex) {} \
292 \
293 const ::mim::Def* replace(const ::mim::Def* def) final __VA_ARGS__ \
294 }; \
295 ::mim::Stage::hook<__annex, MIM_CONCAT(Repl_, __id)>(__stages)
296// clang-format on
297
298class ReplManPhase : public RWPhase {
299public:
300 /// @name Construction
301 ///@{
302 ReplManPhase(World& world, std::unique_ptr<ReplMan>&& man)
303 : RWPhase(world, "pass_man_phase")
304 , man_(std::move(man)) {}
307
308 void apply(const App*) final;
309 void apply(Stage&) final;
310 ///@}
311
312 const ReplMan& man() const { return *man_; }
313
314private:
315 void start() final;
316 const Def* rewrite(const Def*) final;
317
318 std::unique_ptr<ReplMan> man_;
319};
320
321/// Removes unreachable and dead code by rebuilding the whole World into a new one and `swap`ping them afterwards.
322/// @see @ref phases_rwphase
323class Cleanup : public RWPhase {
324public:
326 : RWPhase(world, "cleanup") {}
329};
330
331/// Wraps a PassMan pipeline as a Phase.
332class PassManPhase : public Phase {
333public:
334 /// @name Construction
335 ///@{
336 PassManPhase(World& world, std::unique_ptr<PassMan>&& man)
337 : Phase(world, "pass_man_phase")
338 , man_(std::move(man)) {}
341
342 void apply(const App*) final;
343 void apply(Stage&) final;
344 ///@}
345
346 const PassMan& man() const { return *man_; }
347
348private:
349 void start() final { man_->run(); }
350
351 std::unique_ptr<PassMan> man_;
352};
353
354/// Organizes several Phase%s into a pipeline.
355/// If fixed_point() is `true`, rerun the whole pipeline until all Phase::todo()%s flags remain `false`.
356/// @see @ref phases_phase_man
357class PhaseMan : public Phase {
358public:
359 /// @name Construction
360 ///@{
363
364 void apply(bool, Phases&&);
365 void apply(const App*) final;
366 void apply(Stage&) final;
367 ///@}
368
369 /// @name Getters
370 ///@{
371 bool fixed_point() const { return fixed_point_; }
372 auto& phases() { return phases_; }
373 const auto& phases() const { return phases_; }
374 ///@}
375
376private:
377 void start() final;
378
379 Phases phases_;
380 bool fixed_point_;
381};
382
383/// Transitively visits all *reachable*, [*closed*](@ref Def::is_closed) mutables in the World.
384/// * Select with `elide_empty` whether you want to visit trivial mutables without body.
385/// * Set `schedule` if the mutables should be scheduled to ensure a correct order of dependencies.
386/// * If you are only interested in specific mutables, you can pass this to @p M.
387/// @see @ref phases_closed_mut_phase
388template<class M = Def>
389class ClosedMutPhase : public Phase {
390public:
391 ClosedMutPhase(World& world, std::string name, bool elide_empty, bool schedule = false)
392 : Phase(world, std::move(name))
393 , elide_empty_(elide_empty)
394 , schedule_(schedule) {}
396 : Phase(world, annex)
397 , elide_empty_(elide_empty)
398 , schedule_(schedule) {}
399
400 bool elide_empty() const { return elide_empty_; }
401 bool schedule() const { return schedule_; }
402
403protected:
404 void start() override {
405 world().template for_each<M>(elide_empty(), [this](M* mut) { root_ = mut, visit(mut); }, schedule());
406 }
407 virtual void visit(M*) = 0;
408 M* root() const { return root_; }
409
410private:
411 const bool elide_empty_;
412 const bool schedule_;
413 M* root_;
414};
415
416/// Like ClosedMutPhase but computes a Nest for each NestPhase::visit.
417/// @see @ref phases_nest_phase
418template<class M = Def>
419class NestPhase : public ClosedMutPhase<M> {
420public:
421 NestPhase(World& world, std::string name, bool elide_empty, bool schedule = false)
425
426 const Nest& nest() const { return *nest_; }
427 virtual void visit(const Nest&) = 0;
428
429private:
430 void visit(M* mut) final {
431 Nest nest(mut);
432 nest_ = &nest;
433 visit(nest);
434 }
435
436 const Nest* nest_;
437};
438
439} // namespace mim
Helps to keep track of curr_mut().
Definition phase.h:123
Enter(Analysis *analysis, Def *new_mut)
Definition phase.h:125
Traverses the current World using Rewriter infrastructure while staying in the same world.
Definition phase.h:82
void start() override
Actual entry.
Definition phase.cpp:29
void set(const Def *concr, const Def *abstr)
Records the abstract value abstr for concr in both lattice() (the analysis result) and map() (so the ...
Definition phase.h:114
Analysis(World &world, std::string name)
Definition phase.h:86
virtual void rewrite_annex(flags_t, Sym, const Def *)
Definition phase.cpp:39
Enter enter(Def *new_mut)
Definition phase.h:140
World & world()
Definition phase.h:102
Def2Def lattice_
Definition phase.h:165
virtual void rewrite_external(Def *)
Definition phase.cpp:40
virtual Def * rewrite_deps(Def *)
Walks mut's dependencies under its curr_mut() scope.
Definition phase.cpp:42
auto & lattice()
Definition phase.h:109
Def * curr_mut() const
Definition phase.h:103
virtual void reset()
Clears the rewriter map and resets Phase::todo() for the next fixed-point iteration.
Definition phase.cpp:23
const auto & lattice() const
Definition phase.h:110
bool is_bootstrapping() const
Definition phase.h:104
Analysis(World &world, flags_t annex)
Definition phase.h:89
Def * rewrite_mut(Def *) override
Default "visit a mutable" entry point: maps mut -> mut, seeds Lam binder vars to top (v -> v) in the ...
Definition phase.cpp:51
Cleanup(World &world, flags_t annex)
Definition phase.h:327
Cleanup(World &world)
Definition phase.h:325
M * root() const
Definition phase.h:408
void start() override
Actual entry.
Definition phase.h:404
ClosedMutPhase(World &world, flags_t annex, bool elide_empty, bool schedule=false)
Definition phase.h:395
bool schedule() const
Definition phase.h:401
bool elide_empty() const
Definition phase.h:400
virtual void visit(M *)=0
ClosedMutPhase(World &world, std::string name, bool elide_empty, bool schedule=false)
Definition phase.h:391
Base class for all Defs.
Definition def.h:246
virtual void visit(const Nest &)=0
const Nest & nest() const
Definition phase.h:426
void visit(M *mut) final
Definition phase.h:430
NestPhase(World &world, std::string name, bool elide_empty, bool schedule=false)
Definition phase.h:421
NestPhase(World &world, flags_t annex, bool elide_empty, bool schedule=false)
Definition phase.h:423
Builds a nesting tree for all mutables/binders.
Definition nest.h:18
PassManPhase(World &world, flags_t annex)
Definition phase.h:339
PassManPhase(World &world, std::unique_ptr< PassMan > &&man)
Definition phase.h:336
void start() final
Actual entry.
Definition phase.h:349
const PassMan & man() const
Definition phase.h:346
An optimizer that combines several optimizations in an optimal way.
Definition pass.h:185
Organizes several Phases into a pipeline.
Definition phase.h:357
PhaseMan(World &world, flags_t annex)
Definition phase.h:361
bool fixed_point() const
Definition phase.h:371
auto & phases()
Definition phase.h:372
const auto & phases() const
Definition phase.h:373
Unlike a Pass, a Phase performs one self-contained task and does not interleave with other phases.
Definition phase.h:27
friend class Analysis
Definition phase.h:67
void invalidate(bool todo=true)
Signals that another round of fixed-point iteration is required, either as part of.
Definition phase.h:48
Phase(World &world, std::string name)
Definition phase.h:31
static void run(Args &&... args)
Runs a single Phase.
Definition phase.h:58
Phase(World &world, flags_t annex)
Definition phase.h:33
virtual void run()
Entry point and generates some debug output; invokes Phase::start.
Definition phase.cpp:13
bool todo() const
Definition phase.h:40
virtual void start()=0
Actual entry.
virtual bool analyze()
Runs the optional pre-analysis on RWPhase::old_world(), typically to a fixed point,...
Definition phase.cpp:90
World & new_world()
Create new Defs into this.
Definition phase.h:243
RWPhase(World &world, flags_t annex, Analysis *analysis=nullptr)
Definition phase.h:198
virtual void rewrite_annex(flags_t, Sym, const Def *)
Definition phase.cpp:100
Analysis * analysis()
Definition phase.h:206
bool is_bootstrapping() const
Returns whether we are currently bootstrapping (rewriting annexes).
Definition phase.h:231
RWPhase(World &world, std::string name, Analysis *analysis=nullptr)
Definition phase.h:194
void start() override
Actual entry.
Definition phase.cpp:71
const Def * lattice(const Def *old_def)
Returns the abstract value computed by the associated Analysis for the given old-world Def,...
Definition phase.h:211
World & world()=delete
Hides both and forbids direct access.
const Analysis * analysis() const
Definition phase.h:207
World & old_world()
Get old Defs from here.
Definition phase.h:242
virtual void rewrite_external(Def *)
Definition phase.cpp:102
const Def * rewrite(const Def *) final
Definition phase.cpp:154
ReplManPhase(World &world, flags_t annex)
Definition phase.h:305
const ReplMan & man() const
Definition phase.h:312
void start() final
Actual entry.
Definition phase.cpp:146
ReplManPhase(World &world, std::unique_ptr< ReplMan > &&man)
Definition phase.h:302
void apply(const App *) final
Invoked if your Stage has additional args.
Definition phase.cpp:132
void apply(Stage &stage) final
Dito, but invoked by Stage::recreate.
Definition phase.h:271
const auto & repls() const
Definition phase.h:274
const Def * replace(const Def *) final
Definition phase.h:277
ReplMan(World &world, flags_t annex)
Definition phase.h:266
void add(std::unique_ptr< Repl > &&repl)
Definition phase.h:273
void apply(Repls &&)
Definition phase.cpp:111
Simple Stage that searches for a pattern and replaces it.
Definition phase.h:256
virtual const Def * replace(const Def *def)=0
Repl(World &world, flags_t annex)
Definition phase.h:258
World & world()
Definition rewrite.h:33
virtual const Def * map(const Def *old_def, const Def *new_def)
Definition rewrite.h:45
Rewriter(std::unique_ptr< World > &&ptr)
Definition rewrite.cpp:17
Common base for Phase and Pass.
Definition pass.h:26
World & world()
Definition pass.h:77
std::string_view name() const
Definition pass.h:80
Stage(World &world, std::string name)
Definition pass.h:30
flags_t annex() const
Definition pass.h:81
The World represents the whole program and manages creation of MimIR nodes (Defs).
Definition world.h:36
Definition ast.h:14
DefMap< const Def * > Def2Def
Definition def.h:77
u64 flags_t
Definition types.h:39
std::deque< std::unique_ptr< Repl > > Repls
Definition phase.h:21
std::deque< std::unique_ptr< Phase > > Phases
Definition phase.h:22
Definition span.h:122