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/rewrite.h"
11#include "mim/world.h"
12
13namespace mim {
14
15class Nest;
16class Phase;
17class PhaseMan;
18class World;
19
20using Phases = std::deque<std::unique_ptr<Phase>>;
21
22/// A Phase performs one self-contained task over the whole World.
23/// Phases are intended to run in a classical sequence, one after another.
24/// @see @ref phases_phase
25class Phase : public fe::RuntimeCast<Phase> {
26public:
27 /// @name Construction & Destruction
28 ///@{
29 Phase(World& world, std::string name)
30 : world_(world)
31 , name_(std::move(name)) {}
33
34 virtual ~Phase() = default;
35
36 virtual std::unique_ptr<Phase> recreate(); ///< Creates a new instance; needed by a fixed-point PhaseMan.
37 virtual void apply(const App*) {} ///< Invoked if your Phase has additional args.
38 virtual void apply(Phase&) {} ///< Dito, but invoked by Phase::recreate.
39
40 /// @name Redirection
41 /// A Phase may resolve to a *different* Phase (or to nothing) after Phase::apply.
42 /// This is used by the `%%compile.named` stage that resolves a string to another plugin's annex.
43 ///@{
44 virtual bool redirects() const { return false; } ///< If `true`, Phase::create uses take_resolved().
45 virtual std::unique_ptr<Phase> take_resolved() {
46 return {};
47 } ///< The Phase to use instead; `nullptr` means *elide*.
48 ///@}
49
50 static std::unique_ptr<Phase> create(const Flags2Phases& phases, const Def* def) {
51 auto& world = def->world();
52 auto p_def = App::uncurry_callee(def);
53 world.DLOG("apply phase: `{}`", p_def);
54
55 if (auto axm = p_def->isa<Axm>())
56 if (auto i = phases.find(axm->flags()); i != phases.end()) {
57 auto phase = i->second(world);
58 if (phase) {
59 phase->apply(def->isa<App>());
60 if (phase->redirects()) return phase->take_resolved();
61 }
62 return phase;
63 } else
64 fe::throwf("phase `{}` not found", axm->sym());
65 else
66 fe::throwf("unsupported callee for a phase: `{}`", p_def);
67 }
68
69 template<class A, class P>
70 static void hook(Flags2Phases& phases) {
71 assert_emplace(phases, Annex::base<A>(), [](World& w) { return std::make_unique<P>(w, Annex::base<A>()); });
72 }
73 ///@}
74
75 /// @name Getters
76 ///@{
77 World& world() { return world_; }
78 Driver& driver() { return world().driver(); }
79 Log& log() const { return world_.log(); }
80 std::string_view name() const { return name_; }
81 flags_t annex() const { return annex_; }
82
83 /// Command-line arguments passed to this Phase's plugin via `-X <plugin>:<arg>`.
84 /// Derived from Phase::annex; yields an empty Vector for name-constructed Phase%s.
86 ///@}
87
88 /// @name Fixed-Point Handling
89 ///@{
90 bool todo() const { return todo_; }
91
92 /// Signals that another round of fixed-point iteration is required, either
93 /// as part of
94 /// - a pipeline managed by PhaseMan, or
95 /// - the optional pre-analysis of an RWPhase.
96 ///
97 /// Calling `invalidate(todo)` bitwise-ORs @p todo into the internal `todo_` flag.
98 void invalidate(bool todo = true) { todo_ |= todo; }
99 ///@}
100
101 /// @name run
102 ///@{
103 virtual void run(); ///< Entry point and generates some debug output; invokes Phase::start.
104 virtual void start() = 0; ///< Actual entry.
105
106 /// Runs a single Phase.
107 template<class P, class... Args>
108 static void run(Args&&... args) {
109 P p(std::forward<Args>(args)...);
110 p.run();
111 }
112
113 /// Adds @p n to the custom Profiler counter @p key of the current run; no-op unless profiling is enabled.
114 void profile_count(std::string_view key, uint64_t n = 1);
115 ///@}
116
117private:
118 World& world_;
119 flags_t annex_ = 0;
120 bool todo_ = false;
121
122protected:
123 std::string name_;
124
125 friend class Analysis;
126};
127
128/// Traverses the current World using Rewriter infrastructure while staying in the same world.
129///
130/// It recursively rewrites
131/// 1. all World::annexes() (during which Analysis::is_bootstrapping() is `true`), and then
132/// 2. all World::externals() (during which it is `false`).
133///
134/// Analysis provides a reusable lattice() mapping old Def%s to abstract values, represented as ordinary MimIR Def%s.
135///
136/// Fixed-point iteration is *sparse* by default:
137/// whenever a lattice write changes observable information, the mutable currently being drained is recorded as *dirty*.
138/// The next round then re-drains only those dirty mutables - plus everything reachable from them - instead of walking
139/// the whole World. At the start of such a round, the accumulated lattice() is replayed into the rewriter map, so a
140/// dirty mutable's body sees the substitutions its (non-revisited) producers installed in earlier rounds. Since dirt
141/// tracks *writers* - not readers - a sparse round may miss affected mutables; hence, once sparse rounds quiesce, one
142/// final **full** round certifies the fixed point.
143/// @note You can override
144/// - Rewriter::rewrite(),
145/// - Rewriter::rewrite_imm(),
146/// - Rewriter::rewrite_mut(), etc.
147/// @see @ref phases_analysis
148/// @see @ref ssa-without-dominance for how an Analysis substitutes Def::nests for the classical SSA dominance check.
149class Analysis : public Phase, public Rewriter {
150public:
151 /// @name Construction & Destruction
152 ///@{
153 Analysis(World& world, std::string name)
154 : Phase(world, std::move(name))
155 , Rewriter(world) {}
159
160 /// Clears the rewriter map and resets Phase::todo() for the next fixed-point iteration.
161 /// lattice() is **preserved** across iterations so that abstract values accumulated in earlier
162 /// rounds remain available - this is what makes fixed-point convergence possible.
163 /// The dirty set survives as well; start() consumes it to decide whether the round can be sparse.
164 /// @see RWPhase::analyze
165 virtual void reset();
166 ///@}
167
168 /// @name Getters
169 ///@{
170 using Phase::world; ///< Disambiguates the Phase/Rewriter double base; for an Analysis both denote the same World.
171 bool is_bootstrapping() const { return bootstrapping_; }
172 ///@}
173
174 /// @name Sparse Fixed-Point Iteration
175 ///@{
176 bool is_sparse() const {
177 return curr_sparse_;
178 } ///< Does the current round only re-drain last round's dirty mutables?
179 void make_dense() { dense_ = true; } ///< Forces whole-World rounds unconditionally.
180 /// Bumped on every observable lattice change; snapshot it around a code region to detect changes.
181 size_t version() const { return version_; }
182 ///@}
183
184 /// @name lattice
185 /// Conventions: *absent* = ⊥ (nothing known); `def ↦ def` = ⊤ (keep as is).
186 /// Subclasses may store their own sentinels in between as ordinary Def%s (e.g. SEO's GVN-bundle Proxy%s).
187 ///@{
188 const auto& lattice() const { return lattice_; } ///< The whole map; used e.g. to diff two fixed-point runs.
189
190 /// @returns the abstract value recorded for @p def, or `nullptr` if unknown.
191 const Def* lattice(const Def* def) const {
192 if (auto i = lattice_.find(def); i != lattice_.end()) return i->second;
193 return nullptr;
194 }
195
196 /// @returns whether @p def is pinned to ⊤ (`def ↦ def`).
197 bool is_top(const Def* def) const {
198 auto i = lattice_.find(def);
199 return i != lattice_.end() && i->second == def;
200 }
201 ///@}
202
203protected:
204 /// @name lattice
205 ///@{
206
207 /// **Non-monotone** write of `concr ↦ abstr` into lattice() and map().
208 /// This is the escape hatch for analyses that must overwrite an earlier round's value (descending from ⊤ is fine).
209 /// invalidate()s iff the stored value changed; an *absent* entry counts as changed - even for ⊤ -
210 /// since a non-monotone lattice's consumers may well distinguish ⊥ from ⊤.
211 /// Every change also touch()es curr_mut() - the seed set of the next sparse round.
212 /// @returns `true` iff this changed the entry - i.e. iff it invalidate()d.
213 bool lattice_force(const Def* concr, const Def* abstr) {
214 map(concr, abstr);
215
216 if (auto [i, ins] = lattice_.emplace(concr, abstr); !ins) {
217 if (i->second == abstr) return false;
218 i->second = abstr;
219 }
220 return touch(), true;
221 }
222
223 /// Writes `concr ↦ abstr` into lattice() and map().
224 /// invalidate()s - and thereby triggers another fixed-point round - iff this changes observable information:
225 /// an existing entry was overwritten, or a fresh fact other than ⊤ was inserted.
226 /// Freshly inserting ⊤ (`concr ↦ concr`) stays silent, as it is indistinguishable from *absent* for consumers.
227 /// Every change also touch()es curr_mut() - the seed set of the next sparse round.
228 /// @returns `true` iff this changed observable information - i.e. iff it invalidate()d.
229 bool lattice(const Def* concr, const Def* abstr) {
230 map(concr, abstr);
231
232 if (auto [i, ins] = lattice_.emplace(concr, abstr); !ins) {
233 assert((i->second != concr || abstr == concr) && "monotonicity violation: must not descend from ⊤");
234 if (i->second == abstr) return false;
235 i->second = abstr;
236 } else if (concr == abstr) {
237 return false;
238 }
239 return touch(), true;
240 }
241
242 /// Monotonically forces @p def to ⊤ (keep as is).
243 /// @returns `true` iff this changed observable information - i.e. iff it invalidate()d.
244 bool pin(const Def* def) { return lattice(def, def); }
245
246 /// Additionally schedules @p mut for the next sparse round.
247 /// Use this when a lattice change must re-visit *other* mutables than curr_mut() -
248 /// e.g. all call sites of a Lam whose var's abstract value changed.
249 void taint(Def* mut) { dirty_.emplace(mut); }
250 ///@}
251
252 /// @name Rewrite
253 ///@{
254 virtual void prepare() {} ///< Run **before** the main analysis.
255 /// Run **after** the main analysis - only in **full** rounds, so it always sees the complete abstract World.
256 virtual void finalize() {}
257 void start() override;
258 virtual void rewrite_annex(flags_t, Sym, const Def*);
259 virtual void rewrite_external(Def*);
260 const Def* rewrite_imm_Proxy(const Proxy* proxy) override { return proxy; } ///< By default: ignore Proxy%s.
261
262 /// Schedules @p mut for a breadth-first visit of its dependencies and records `mut -> mut`.
263 /// Mutables are enqueued instead of recursed into; Analysis::drain then walks them in BFS order.
264 /// The `mut -> mut` entry doubles as the per-round "already scheduled" marker (Rewriter::old2news_ is
265 /// cleared by reset()), so each mutable's deps are visited at most once per fixed-point round.
266 Def* rewrite_mut(Def*) override;
267 ///@}
268
269private:
270 /// Observable lattice information changed: records curr_mut() as *dirty* - the seed set of the next sparse
271 /// round - and invalidate()s. Outside of any mutable (annex walk, finalize()) the change cannot be
272 /// attributed to a mutable; then the next round falls back to a full one.
273 void touch() {
274 ++version_;
275 if (auto mut = curr_mut())
276 dirty_.emplace(mut);
277 else
278 nonlocal_ = true;
279 invalidate();
280 }
281
282 /// Walks all enqueued mutables' dependencies - in BFS order - under each mutable's curr_mut() scope.
283 void drain();
284
285 Def2Def lattice_;
286 std::deque<Def*> worklist_;
287 MutSet dirty_; ///< Muts whose drain changed the lattice this round; seeds the next sparse round.
288 size_t version_ = 0; ///< @see version()
289 bool nonlocal_ = false; ///< The lattice changed outside of any mut; the next round must be a full one.
290 bool curr_sparse_ = false; ///< Is the current round sparse?
291 bool dense_ = false; ///< @see make_dense()
292 bool bootstrapping_ = true;
293 size_t num_drained_ = 0; ///< muts drained this round; flushed into the Profiler
294};
295
296/// Rebuilds old_world() into new_world() and then swaps them.
297///
298/// It recursively rewrites
299/// 1. all old World::annexes() (during which RWPhase::is_bootstrapping() is `true`, and then
300/// 2. all old World::externals() (during which it is `false`).
301///
302/// During bootstrapping, rewrites that depend on other annexes may need to be skipped,
303/// since those annexes might not yet exist in the new world.
304///
305/// If an associated Analysis is provided, the rewrite can query its abstract results through lattice().
306///
307/// @note You can override
308/// - Rewriter::rewrite(),
309/// - Rewriter::rewrite_imm(),
310/// - Rewriter::rewrite_mut(), etc.
311/// @see @ref phases_rwphase
312class RWPhase : public Phase, public Rewriter {
313public:
314 /// @name Construction
315 ///@{
316 RWPhase(World& world, std::string name, Analysis* analysis = nullptr)
317 : Phase(world, std::move(name))
318 , Rewriter(world.inherit())
319 , analysis_(analysis) {}
321 : Phase(world, annex)
322 , Rewriter(world.inherit())
323 , analysis_(analysis) {}
324 ///@}
325
326 /// @name Analysis
327 ///@{
328 Analysis* analysis() { return analysis_; }
329 const Analysis* analysis() const { return analysis_; }
330
331 /// Returns the abstract value computed by the associated Analysis for the given old-world Def, or `nullptr` if no
332 /// value is available.
333 const Def* lattice(const Def* old_def) const { return analysis_ ? analysis_->lattice(old_def) : nullptr; }
334
335 /// Returns lattice(@p old_def) if it differs from @p old_def (i.e. we learned something), otherwise `nullptr`.
336 const Def* abstracted(const Def* old_def) const {
337 auto l = lattice(old_def);
338 return l && l != old_def ? l : nullptr;
339 }
340
341 /// Runs the optional pre-analysis on RWPhase::old_world(), typically to a fixed point,
342 /// before rewriting begins.
343 ///
344 /// If analysis() is set, this is the natural place to iterate until Phase::todo() becomes `false`.
345 /// If no Analysis is needed, simply return `false`.
346 virtual bool analyze();
347 ///@}
348
349 /// @name Rewrite
350 ///@{
351 virtual void rewrite_annex(flags_t, Sym, const Def*);
352 virtual void rewrite_external(Def*);
353
354 /// Returns whether we are currently bootstrapping (rewriting annexes).
355 /// While bootstrapping, you have to skip rewrites that refer to other annexes, as they might not yet be available.
356 bool is_bootstrapping() const { return bootstrapping_; }
357 ///@}
358
359 /// @name World
360 /// * Phase::world is the **old** one.
361 /// * Rewriter::world is the **new** one.
362 /// * RWPhase::world is deleted to not confuse this.
363 ///@{
364 using Phase::world;
365 using Rewriter::world;
366 World& world() = delete; ///< Hides both and forbids direct access.
367 World& old_world() { return Phase::world(); } ///< Get **old** Def%s from here.
368 World& new_world() { return Rewriter::world(); } ///< Create **new** Def%s into this.
369 ///@}
370
371protected:
372 void start() override;
373
374private:
375 Analysis* analysis_;
376 bool bootstrapping_ = true;
377};
378
379/// An RWPhase that searches for a pattern and replaces it.
380/// Implement the replace() hook - or use the MIM_REPL macro for an inline definition.
381class Repl : public RWPhase {
382public:
385
386 /// replace() inspects and builds Def%s of the **old** world; the RWPhase machinery carries the result over.
387 World& world() { return old_world(); }
388
389 /// @returns the replacement or `nullptr` if the pattern does not match.
390 virtual const Def* replace(const Def* def) = 0;
391
392private:
393 const Def* rewrite(const Def* def) final {
394 for (bool todo = true; todo;) {
395 todo = false;
396 if (auto subst = replace(def)) todo = true, def = subst;
397 }
398
399 return Rewriter::rewrite(def);
400 }
401};
402
403#define MIM_CONCAT_INNER(a, b) a##b
404#define MIM_CONCAT(a, b) MIM_CONCAT_INNER(a, b)
405
406#define MIM_REPL(__phases, __annex, ...) MIM_REPL_IMPL(__phases, __annex, __LINE__, __VA_ARGS__)
407
408// clang-format off
409#define MIM_REPL_IMPL(__phases, __annex, __id, ...) \
410 struct MIM_CONCAT(Repl_, __id) : ::mim::Repl { \
411 MIM_CONCAT(Repl_, __id)(::mim::World & world, ::mim::flags_t annex) \
412 : Repl(world, annex) {} \
413 \
414 const ::mim::Def* replace(const ::mim::Def* def) final __VA_ARGS__ \
415 }; \
416 ::mim::Phase::hook<__annex, MIM_CONCAT(Repl_, __id)>(__phases)
417// clang-format on
418
419/// Removes unreachable and dead code by rebuilding the whole World into a new one and `swap`ping them afterwards.
420/// @see @ref phases_rwphase
421class Cleanup : public RWPhase {
422public:
424 : RWPhase(world, "cleanup") {}
427};
428
429/// Organizes several Phase%s into a pipeline.
430/// If fixed_point() is `true`, rerun the whole pipeline until all Phase::todo()%s flags remain `false`.
431/// @see @ref phases_phase_man
432class PhaseMan : public Phase {
433public:
434 /// @name Construction
435 ///@{
438
439 void apply(bool, Phases&&);
440 void apply(const App*) final;
441 void apply(Phase&) final;
442 ///@}
443
444 /// @name Getters
445 ///@{
446 bool fixed_point() const { return fixed_point_; }
447 auto& phases() { return phases_; }
448 const auto& phases() const { return phases_; }
449 ///@}
450
451private:
452 void start() final;
453
454 Phases phases_;
455 bool fixed_point_;
456};
457
458/// Transitively visits all *reachable*, [*closed*](@ref Def::is_closed) mutables in the World.
459/// * Select with `elide_empty` whether you want to visit trivial mutables without body.
460/// * Set `schedule` if the mutables should be scheduled to ensure a correct order of dependencies.
461/// * If you are only interested in specific mutables, you can pass this to @p M.
462/// @see @ref phases_closed_mut_phase
463template<class M = Def>
464class ClosedMutPhase : public Phase {
465public:
466 ClosedMutPhase(World& world, std::string name, bool elide_empty, bool schedule = false)
467 : Phase(world, std::move(name))
468 , elide_empty_(elide_empty)
469 , schedule_(schedule) {}
471 : Phase(world, annex)
472 , elide_empty_(elide_empty)
473 , schedule_(schedule) {}
474
475 bool elide_empty() const { return elide_empty_; }
476 bool schedule() const { return schedule_; }
477
478protected:
479 void start() override {
480 world().template for_each<M>(elide_empty(), [this](M* mut) { root_ = mut, visit(mut); }, schedule());
481 }
482 virtual void visit(M*) = 0;
483 M* root() const { return root_; }
484
485private:
486 const bool elide_empty_;
487 const bool schedule_;
488 M* root_;
489};
490
491/// Like ClosedMutPhase but computes a Nest for each NestPhase::visit.
492/// @see @ref phases_nest_phase
493template<class M = Def>
494class NestPhase : public ClosedMutPhase<M> {
495public:
496 NestPhase(World& world, std::string name, bool elide_empty, bool schedule = false)
500
501 const Nest& nest() const { return *nest_; }
502 virtual void visit(const Nest&) = 0;
503
504private:
505 void visit(M* mut) final {
506 Nest nest(mut);
507 nest_ = &nest;
508 visit(nest);
509 }
510
511 const Nest* nest_;
512};
513
514} // namespace mim
Traverses the current World using Rewriter infrastructure while staying in the same world.
Definition phase.h:149
virtual void prepare()
Run before the main analysis.
Definition phase.h:254
size_t version() const
Bumped on every observable lattice change; snapshot it around a code region to detect changes.
Definition phase.h:181
void start() override
Actual entry.
Definition phase.cpp:58
bool lattice_force(const Def *concr, const Def *abstr)
Non-monotone write of concr ↦ abstr into lattice() and map().
Definition phase.h:213
const Def * rewrite_imm_Proxy(const Proxy *proxy) override
By default: ignore Proxys.
Definition phase.h:260
Analysis(World &world, std::string name)
Definition phase.h:153
void make_dense()
Forces whole-World rounds unconditionally.
Definition phase.h:179
bool lattice(const Def *concr, const Def *abstr)
Writes concr ↦ abstr into lattice() and map().
Definition phase.h:229
void taint(Def *mut)
Additionally schedules mut for the next sparse round.
Definition phase.h:249
virtual void rewrite_annex(flags_t, Sym, const Def *)
Definition phase.cpp:101
const Def * lattice(const Def *def) const
Definition phase.h:191
bool is_top(const Def *def) const
Definition phase.h:197
virtual void rewrite_external(Def *)
Definition phase.cpp:102
bool is_sparse() const
Does the current round only re-drain last round's dirty mutables?
Definition phase.h:176
virtual void reset()
Clears the rewriter map and resets Phase::todo() for the next fixed-point iteration.
Definition phase.cpp:51
virtual void finalize()
Run after the main analysis - only in full rounds, so it always sees the complete abstract World.
Definition phase.h:256
const auto & lattice() const
The whole map; used e.g. to diff two fixed-point runs.
Definition phase.h:188
bool pin(const Def *def)
Monotonically forces def to ⊤ (keep as is).
Definition phase.h:244
bool is_bootstrapping() const
< Disambiguates the Phase/Rewriter double base; for an Analysis both denote the same World.
Definition phase.h:171
Analysis(World &world, flags_t annex)
Definition phase.h:156
Def * rewrite_mut(Def *) override
Schedules mut for a breadth-first visit of its dependencies and records mut -> mut.
Definition phase.cpp:104
World & world()
Definition phase.h:77
const Def * uncurry_callee() const
Definition lam.h:327
Definition axm.h:9
Cleanup(World &world, flags_t annex)
Definition phase.h:425
Cleanup(World &world)
Definition phase.h:423
M * root() const
Definition phase.h:483
void start() override
Actual entry.
Definition phase.h:479
ClosedMutPhase(World &world, flags_t annex, bool elide_empty, bool schedule=false)
Definition phase.h:470
bool schedule() const
Definition phase.h:476
bool elide_empty() const
Definition phase.h:475
virtual void visit(M *)=0
ClosedMutPhase(World &world, std::string name, bool elide_empty, bool schedule=false)
Definition phase.h:466
Base class for all Defs.
Definition def.h:261
World & world() const noexcept
Definition def.cpp:483
Some "global" variables needed all over the place.
Definition driver.h:20
Facility to log what you are doing.
Definition log.h:18
void log(Level level, Loc loc, std::format_string< Args... > fmt, Args &&... args) const
Definition log.h:53
virtual void visit(const Nest &)=0
const Nest & nest() const
Definition phase.h:501
void visit(M *mut) final
Definition phase.h:505
NestPhase(World &world, std::string name, bool elide_empty, bool schedule=false)
Definition phase.h:496
NestPhase(World &world, flags_t annex, bool elide_empty, bool schedule=false)
Definition phase.h:498
Builds a nesting tree for all mutables/binders.
Definition nest.h:30
Organizes several Phases into a pipeline.
Definition phase.h:432
PhaseMan(World &world, flags_t annex)
Definition phase.h:436
bool fixed_point() const
Definition phase.h:446
auto & phases()
Definition phase.h:447
void start() final
Actual entry.
Definition phase.cpp:193
const auto & phases() const
Definition phase.h:448
void apply(bool, Phases &&)
Definition phase.cpp:169
A Phase performs one self-contained task over the whole World.
Definition phase.h:25
virtual std::unique_ptr< Phase > recreate()
Creates a new instance; needed by a fixed-point PhaseMan.
Definition phase.cpp:27
std::string name_
Definition phase.h:123
static void hook(Flags2Phases &phases)
Definition phase.h:70
friend class Analysis
Definition phase.h:125
void invalidate(bool todo=true)
Signals that another round of fixed-point iteration is required, either as part of.
Definition phase.h:98
flags_t annex() const
Definition phase.h:81
Log & log() const
Definition phase.h:79
Phase(World &world, std::string name)
Definition phase.h:29
static void run(Args &&... args)
Runs a single Phase.
Definition phase.h:108
static std::unique_ptr< Phase > create(const Flags2Phases &phases, const Def *def)
Definition phase.h:50
void profile_count(std::string_view key, uint64_t n=1)
Adds n to the custom Profiler counter key of the current run; no-op unless profiling is enabled.
Definition phase.cpp:43
virtual std::unique_ptr< Phase > take_resolved()
The Phase to use instead; nullptr means elide.
Definition phase.h:45
virtual void apply(Phase &)
Dito, but invoked by Phase::recreate.
Definition phase.h:38
Driver & driver()
Definition phase.h:78
virtual void run()
Entry point and generates some debug output; invokes Phase::start.
Definition phase.cpp:34
bool todo() const
Definition phase.h:90
std::string_view name() const
Definition phase.h:80
virtual void start()=0
Actual entry.
const Vector< std::string > & args()
Command-line arguments passed to this Phase's plugin via -X <plugin>:<arg>.
Definition phase.cpp:23
virtual void apply(const App *)
Invoked if your Phase has additional args.
Definition phase.h:37
virtual bool redirects() const
If true, Phase::create uses take_resolved().
Definition phase.h:44
virtual ~Phase()=default
World & world()
Definition phase.h:77
virtual bool analyze()
Runs the optional pre-analysis on RWPhase::old_world(), typically to a fixed point,...
Definition phase.cpp:148
World & new_world()
Create new Defs into this.
Definition phase.h:368
RWPhase(World &world, flags_t annex, Analysis *analysis=nullptr)
Definition phase.h:320
virtual void rewrite_annex(flags_t, Sym, const Def *)
Definition phase.cpp:158
const Def * abstracted(const Def *old_def) const
Returns lattice(old_def) if it differs from old_def (i.e. we learned something), otherwise nullptr.
Definition phase.h:336
Analysis * analysis()
Definition phase.h:328
bool is_bootstrapping() const
Returns whether we are currently bootstrapping (rewriting annexes).
Definition phase.h:356
RWPhase(World &world, std::string name, Analysis *analysis=nullptr)
Definition phase.h:316
void start() override
Actual entry.
Definition phase.cpp:128
World & world()=delete
Hides both and forbids direct access.
const Analysis * analysis() const
Definition phase.h:329
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:333
World & old_world()
Get old Defs from here.
Definition phase.h:367
virtual void rewrite_external(Def *)
Definition phase.cpp:160
const Def * rewrite(const Def *def) final
Definition phase.h:393
virtual const Def * replace(const Def *def)=0
Repl(World &world, flags_t annex)
Definition phase.h:383
World & world()
replace() inspects and builds Defs of the old world; the RWPhase machinery carries the result over.
Definition phase.h:387
World & world()
Definition rewrite.h:33
D * curr_mut() const
Definition rewrite.h:89
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
virtual const Def * rewrite(const Def *)
Definition rewrite.cpp:56
This is a thin wrapper for absl::InlinedVector<T, N, A> which is a drop-in replacement for std::vecto...
Definition vector.h:18
The World represents the whole program and manages creation of MimIR nodes (Defs).
Definition world.h:36
const Driver & driver() const
Definition world.h:93
Definition ast.h:14
DefMap< const Def * > Def2Def
Definition def.h:77
auto assert_emplace(C &container, Args &&... args)
Invokes emplace on container, asserts that insertion actually happened, and returns the iterator.
Definition util.h:117
u64 flags_t
Definition types.h:39
absl::flat_hash_map< flags_t, std::function< std::unique_ptr< Phase >(World &)> > Flags2Phases
Maps an axiom of a Phase to a function that creates one.
Definition plugin.h:25
GIDSet< Def * > MutSet
Definition def.h:87
std::deque< std::unique_ptr< Phase > > Phases
Definition phase.h:20
Definition span.h:126
static consteval flags_t base()
Definition plugin.h:150