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/cast.h>
6#include <fe/term.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 Phase;
16class PhaseMan;
17
18using Phases = std::deque<std::unique_ptr<Phase>>;
19
20/// A Phase performs one self-contained task over the whole World.
21/// Phases are intended to run in a classical sequence, one after another.
22/// @see @ref phases_phase
23class Phase : public fe::RuntimeCast<Phase> {
24public:
25 /// @name Construction & Destruction
26 ///@{
27 Phase(World& world, std::string name)
28 : world_(world)
29 , name_(std::move(name)) {}
31
32 virtual ~Phase() = default;
33
34 virtual std::unique_ptr<Phase> recreate(); ///< Creates a new instance; needed by a fixed-point PhaseMan.
35 virtual void apply(const App*) {} ///< Invoked if your Phase has additional args.
36 virtual void apply(Phase&) {} ///< Dito, but invoked by Phase::recreate.
37
38 /// @name Redirection
39 /// A Phase may resolve to a *different* Phase (or to nothing) after Phase::apply.
40 /// This is used by the `compile.named` stage that resolves a string to another plugin's annex.
41 ///@{
42 virtual bool redirects() const { return false; } ///< If `true`, Phase::create uses take_resolved().
43 virtual std::unique_ptr<Phase> take_resolved() {
44 return {};
45 } ///< The Phase to use instead; `nullptr` means *elide*.
46 ///@}
47
48 static std::unique_ptr<Phase> create(const Flags2Phases& phases, const Def* def);
49
50 template<class A, class P>
51 static void hook(Flags2Phases& phases) {
52 fe::assert_emplace(phases, Annex::base<A>(), [](World& w) { return std::make_unique<P>(w, Annex::base<A>()); });
53 }
54 ///@}
55
56 /// @name Getters
57 ///@{
58 World& world() { return world_; }
59 Driver& driver() { return world().driver(); }
60 const fe::Log& log() const { return world_.log(); }
61 std::string_view name() const { return name_; }
62 flags_t annex() const { return annex_; }
63
64 /// Command-line arguments passed to this Phase's plugin via `-X <plugin>:<arg>`.
65 /// Derived from Phase::annex; yields an empty fe::Vector for name-constructed Phase%s.
66 const fe::Vector<std::string>& args();
67 ///@}
68
69 /// @name Fixed-Point Handling
70 ///@{
71 bool todo() const { return todo_; }
72
73 /// Signals that another round of fixed-point iteration is required, either
74 /// as part of
75 /// - a pipeline managed by PhaseMan, or
76 /// - the optional pre-analysis of an RWPhase.
77 ///
78 /// Calling `invalidate(todo)` bitwise-ORs @p todo into the internal `todo_` flag.
79 void invalidate(bool todo = true) { todo_ |= todo; }
80 ///@}
81
82 /// @name run
83 ///@{
84 virtual void run(); ///< Entry point and generates some debug output; invokes Phase::start.
85 virtual void start() = 0; ///< Actual entry.
86
87 /// Runs a single Phase.
88 template<class P, class... Args>
89 static void run(Args&&... args) {
90 P p(std::forward<Args>(args)...);
91 p.run();
92 }
93
94 /// Adds @p n to the custom fe::Profiler counter @p key of the current run; no-op unless profiling is enabled.
95 void profile_count(std::string_view key, uint64_t n = 1);
96 ///@}
97
98private:
99 World& world_;
100 flags_t annex_ = 0;
101 bool todo_ = false;
102
103protected:
104 std::string name_;
105
106 friend class Analysis;
107};
108
109/// Traverses the current World using Rewriter infrastructure while staying in the same world.
110///
111/// It recursively rewrites all World::externals() but treats World::Annexes::opaque() annexes as opaque.
112///
113/// Analysis provides a reusable lattice() mapping old Def%s to abstract values, represented as ordinary MimIR Def%s.
114///
115/// Fixed-point iteration is *sparse* by default:
116/// whenever a lattice write changes observable information, the mutable currently being drained is recorded as *dirty*.
117/// The next round then re-drains only those dirty mutables - plus everything reachable from them - instead of walking
118/// the whole World. At the start of such a round, the accumulated lattice() is replayed into the rewriter map, so a
119/// dirty mutable's body sees the substitutions its (non-revisited) producers installed in earlier rounds. Since dirt
120/// tracks *writers* - not readers - a sparse round may miss affected mutables; hence, once sparse rounds quiesce, one
121/// final **full** round certifies the fixed point.
122/// That replay only *prunes* the walk; what the lattice means is applied by rewrite() through repr(), in every kind of
123/// round alike - see repr() for why an analysis whose values are expressions needs that.
124/// @note You can override
125/// - Rewriter::rewrite(),
126/// - Rewriter::rewrite_imm(),
127/// - Rewriter::rewrite_mut(), etc.
128/// @see @ref phases_analysis
129/// @see @ref ssa-without-dominance for how an Analysis substitutes Def::nests for the classical SSA dominance check.
130class Analysis : public Phase, public Rewriter {
131public:
132 /// @name Construction & Destruction
133 ///@{
134 Analysis(World& world, std::string name)
135 : Phase(world, std::move(name))
136 , Rewriter(world) {}
140
141 /// Clears the rewriter map and resets Phase::todo() for the next fixed-point iteration.
142 /// lattice() is **preserved** across iterations so that abstract values accumulated in earlier
143 /// rounds remain available - this is what makes fixed-point convergence possible.
144 /// The dirty set survives as well; start() consumes it to decide whether the round can be sparse.
145 /// @see RWPhase::analyze
146 virtual void reset();
147 ///@}
148
149 /// @name Getters
150 ///@{
151 using Phase::world; ///< Disambiguates the Phase/Rewriter double base; for an Analysis both denote the same World.
152 ///@}
153
154 /// @name Sparse Fixed-Point Iteration
155 ///@{
156 void make_dense() { dense_ = true; } ///< Forces whole-World rounds unconditionally.
157 /// Bumped on every observable lattice change; snapshot it around a code region to detect changes.
158 size_t version() const { return version_; }
159 ///@}
160
161 /// @name lattice
162 /// Conventions: *absent* = ⊥ (nothing known); `def ↦ def` = ⊤ (keep as is).
163 /// Subclasses may store their own sentinels in between as ordinary Def%s (e.g. SEO's GVN-bundle Proxy%s).
164 ///@{
165 const auto& lattice() const { return lattice_; } ///< The whole map; used e.g. to diff two fixed-point runs.
166
167 /// @returns the abstract value recorded for @p def, or `nullptr` if unknown.
168 const Def* lattice(const Def* def) const {
169 if (auto abstr = fe::lookup(lattice_, def)) return abstr;
170 return is_singleton(def) ? def : nullptr;
171 }
172
173 /// The representative of @p def: follows `def ↦ lattice(def)` to the end of its chain.
174 ///
175 /// The lattice records *expressions*, so one and the same abstract value can be spelled at several depths
176 /// of a chain (a var, and the value it was propagated to).
177 /// repr() collapses those spellings to one def, so that comparing two of them answers whether they mean the same
178 /// value - not whether they were derived by the same route. This is union-find's *find* without the path
179 /// compression: the chain is what the lattice means, and its links keep changing between rounds - see
180 /// Analysis::lattice_force.
181 const Def* repr(const Def* def) const {
182 assert(!is_cyclic(def) && "lattice chain must not loop");
183 for (auto next = follow(def); next != def; def = next, next = follow(def)) {}
184 return def;
185 }
186
187 /// @returns whether @p def is pinned to ⊤ (`def ↦ def`).
188 bool is_top(const Def* def) const {
189 auto i = lattice_.find(def);
190 return i != lattice_.end() && i->second == def;
191 }
192 ///@}
193
194protected:
195 /// @name lattice
196 ///@{
197
198 /// The sole inhabitant of a singleton type is ⊤ and shared by all binders of that type, so it is never recorded.
199 static bool is_singleton(const Def* def) { return def->world().inhabitant(def->type()) == def; }
200
201 /// **Non-monotone** write of `concr ↦ abstr` into lattice() and map().
202 /// This is the escape hatch for analyses that must overwrite an earlier round's value (descending from ⊤ is fine).
203 /// invalidate()s iff the stored value changed; an *absent* entry counts as changed - even for ⊤ -
204 /// since a non-monotone lattice's consumers may well distinguish ⊥ from ⊤.
205 /// Every change also touch()es curr_mut() - the seed set of the next sparse round.
206 /// @returns `true` iff this changed the entry - i.e. iff it invalidate()d.
207 bool lattice_force(const Def* concr, const Def* abstr);
208
209 /// **Non-monotone** removal of @p concr from lattice(), so no later sparse round replays it.
210 /// Like lattice_force, a removal invalidate()s and touch()es curr_mut().
211 /// @returns `true` iff @p concr had an entry.
212 bool lattice_erase(const Def* concr) {
213 if (!lattice_.erase(concr)) return false;
214 return touch(), true;
215 }
216
217 /// Writes `concr ↦ abstr` into lattice() and map().
218 /// invalidate()s - and thereby triggers another fixed-point round - iff this changes observable information:
219 /// an existing entry was overwritten, or a fresh fact other than ⊤ was inserted.
220 /// Freshly inserting ⊤ (`concr ↦ concr`) stays silent, as it is indistinguishable from *absent* for consumers.
221 /// Every change also touch()es curr_mut() - the seed set of the next sparse round.
222 /// @returns `true` iff this changed observable information - i.e. iff it invalidate()d.
223 bool lattice(const Def* concr, const Def* abstr);
224
225 /// Monotonically forces @p def to ⊤ (keep as is).
226 /// @returns `true` iff this changed observable information - i.e. iff it invalidate()d.
227 bool pin(const Def* def) { return lattice(def, def); }
228
229 /// pin%s every def in @p def%'s immutable subgraph for which @p pinnable holds.
230 /// Mutables are visited but not descended into; defs already in @p visited are skipped.
231 void pin_imm(DefSet& visited, const Def* def, DefPred auto pinnable) {
232 if (!visited.emplace(def).second) return;
233 if (pinnable(def)) pin(def);
234 if (def->isa_mut()) return;
235 for (auto d : def->deps())
236 pin_imm(visited, d, pinnable);
237 }
238
239 void pin_imm(const Def* def, DefPred auto pinnable) {
240 auto visited = DefSet();
241 pin_imm(visited, def, pinnable);
242 }
243
244 /// Additionally schedules @p mut for the next sparse round.
245 /// Use this when a lattice change must re-visit *other* mutables than curr_mut() -
246 /// e.g. all call sites of a Lam whose var's abstract value changed.
247 void taint(Def* mut) { dirty_.emplace(mut); }
248
249 /// Observable analysis information changed: records curr_mut() as *dirty* - the seed set of the next sparse
250 /// round - and invalidate()s. Outside of any mutable (e.g. in finalize()) the change cannot be
251 /// attributed to a mutable; then the next round falls back to a full one.
252 /// The lattice writes above do this for you; call it directly when a subclass keeps facts of its own.
253 void touch() {
254 ++version_;
255 if (auto mut = curr_mut())
256 dirty_.emplace(mut);
257 else
258 nonlocal_ = true;
259 invalidate();
260 }
261 ///@}
262
263 /// @name Rewrite
264 ///@{
265 void start() override;
266 virtual void prepare() {} ///< Run **before** the main analysis.
267 /// Run **after** the main analysis - only in **full** rounds, so it always sees the complete abstract World.
268 virtual void finalize() {}
269 virtual void rewrite_external(Def*);
270 const Def* lookup(const Def*) override;
271 const Def* rewrite_imm_Proxy(const Proxy* proxy) override { return proxy; } ///< By default: ignore Proxy%s.
272
273 /// Rewrites @p def and then maps the result to its repr().
274 ///
275 /// This is how the lattice is applied: at the *point of use*, and only after the structural rewrite - so an
276 /// Analysis's hooks always see the program itself. Pre-installing the substitutions into map() instead
277 /// would skip those hooks, and only a sparse round would do so - putting the two kinds of round in
278 /// different regimes, where they spell one and the same abstract value differently.
279 const Def* rewrite(const Def*) override;
280
281 /// Schedules @p mut for a breadth-first visit of its dependencies and records `mut -> mut`.
282 /// Mutables are enqueued instead of recursed into; Analysis::drain then walks them in BFS order.
283 /// The `mut -> mut` entry doubles as the per-round "already scheduled" marker (Rewriter::scopes_ are
284 /// cleared by reset()), so each mutable's deps are visited at most once per fixed-point round.
285 /// Then dispatches to the rewrite_mut_* hook, which merely observes @p mut.
286 const Def* rewrite_mut(Def*) final;
287
288#define CODE_MUT(N) \
289 const Def* rewrite_mut_##N(N* mut) override { return mut; }
291#undef CODE_MUT
292 virtual void leave() {} ///< Called after curr_mut() has been completely dealt with.
293 ///@}
294
295private:
296 /// The next def on @p def's chain, or @p def itself if it ends it (⊥ or ⊤).
297 const Def* follow(const Def* def) const {
298 auto i = lattice_.find(def);
299 return i == lattice_.end() ? def : i->second;
300 }
301
302 bool is_cyclic(const Def* def) const;
303
304 /// Walks all enqueued mutables' dependencies - in BFS order - under each mutable's curr_mut() scope.
305 void drain();
306
307 Def2Def lattice_;
308 std::deque<Def*> worklist_;
309 MutSet dirty_; ///< Muts whose drain changed the lattice this round; seeds the next sparse round.
310 size_t version_ = 0; ///< @see version()
311 bool nonlocal_ = false; ///< The lattice changed outside of any mut; the next round must be a full one.
312 bool curr_sparse_ = false; ///< Is the current round sparse?
313 bool dense_ = false; ///< @see make_dense()
314 size_t num_drained_ = 0; ///< muts drained this round; flushed into the fe::Profiler
315};
316
317/// Rebuilds old_world() into new_world() and then swaps them.
318///
319/// It is a Phase *and* a Rewriter that runs an optional analyze() to a fixed point.
320/// Then, it rewrites all old World::externals().
321/// An World::Annexes::opaque() annex is not rewritten but resolved to the annex of the same flags in new_world().
322///
323/// If an associated Analysis is provided, the rewrite can query its abstract results through lattice().
324///
325/// @note You can override
326/// - Rewriter::rewrite(),
327/// - Rewriter::rewrite_imm(),
328/// - Rewriter::rewrite_mut(), etc.
329/// @see @ref phases_rwphase
330class RWPhase : public Phase, public Rewriter {
331public:
332 /// @name Construction
333 ///@{
334 RWPhase(World& world, std::string name, Analysis* analysis = nullptr)
335 : RWPhase(world, std::move(name), analysis, world.inherit()) {}
338 ///@}
339
340 /// @name Analysis
341 ///@{
342 Analysis* analysis() { return analysis_; }
343 const Analysis* analysis() const { return analysis_; }
344
345 /// Returns the abstract value computed by the associated Analysis for the given old-world Def, or `nullptr` if no
346 /// value is available.
347 const Def* lattice(const Def* old_def) const { return analysis_ ? analysis_->lattice(old_def) : nullptr; }
348
349 /// Returns lattice(@p old_def) if it differs from @p old_def (i.e. we learned something), otherwise `nullptr`.
350 const Def* abstracted(const Def* old_def) const {
351 auto l = lattice(old_def);
352 return l && l != old_def ? l : nullptr;
353 }
354
355 bool is_top(const Def* old_def) const { return analysis_->is_top(old_def); }
356
357 /// Runs the optional pre-analysis on old_world(), typically to a fixed point, before rewriting begins.
358 ///
359 /// If analysis() is set, this is the natural place to iterate until Phase::todo() becomes `false`.
360 /// If no Analysis is needed, simply return `false`.
361 virtual bool analyze();
362 ///@}
363
364 /// @name Rewrite
365 ///@{
366 virtual void rewrite_external(Def*);
367 const Def* lookup(const Def*) override;
368 ///@}
369
370 /// @name World
371 /// * Phase::world is the **old** one.
372 /// * Rewriter::world is the **new** one.
373 /// * RWPhase::world is deleted to not confuse this.
374 ///@{
375 using Phase::world;
376 using Rewriter::world;
377 World& world() = delete; ///< Hides both and forbids direct access.
378 World& old_world() { return Phase::world(); } ///< Get **old** Def%s from here.
379 World& new_world() { return Rewriter::world(); } ///< Create **new** Def%s into this.
380 ///@}
381
382protected:
383 void start() override;
384
385 /// @name Coercion
386 /// Every checking position - an App's argument, a Tuple's ops, a Lam's body - coerce()s its value.
387 /// This reconciles a representation the phase changed with one it did not, e.g. an Axm's signature.
388 ///@{
389 const Def* rewrite_imm_App(const App*) override;
390 const Def* rewrite_imm_Tuple(const Tuple*) override;
391 const Def* rewrite_stub(Def*, Def*) override;
393
394 /// Converts the *new* @p new_val into a value of the *new* @p new_type, where a checking position demands it.
395 /// Both are matched leaf by leaf: tuples are exploded and reassembled, Pi%s are η-wrapped, and
396 /// information-free components are dropped or filled in with their sole inhabitant.
397 const Def* coerce(const Def* new_type, const Def* new_val);
398 /// Does coerce() explode and reassemble values of @p type?
399 static bool splittable(const Def* type) {
400 return (type->isa<Sigma>() || type->isa_imm<Arr>()) && type->num_tprojs() != 1;
401 }
402 ///@}
403
404 /// Run **after** all roots have been walked but still **before** the two worlds are swapped.
405 /// This is where you drain a worklist of rewrites your hooks deferred (see e.g. clos::phase::ClosConv).
406 virtual void finalize() {}
407
408private:
409 RWPhase(World& world, std::string name, Analysis* analysis, std::unique_ptr<World>&& new_world)
410 : Phase(world, std::move(name))
412 , analysis_(analysis)
413 , new_world_(std::move(new_world)) {}
414 RWPhase(World& world, flags_t annex, Analysis* analysis, std::unique_ptr<World>&& new_world)
415 : Phase(world, annex)
417 , analysis_(analysis)
418 , new_world_(std::move(new_world)) {}
419
420 /// η-wraps @p val into a Lam of @p type, coerce()ing its Var to @p val%'s domain and its result to the codomain.
421 /// @returns `nullptr` unless both @p val%'s type and @p type are Pi%s.
422 const Def* eta_coerce(const Def* type, const Def* val);
423 /// Builds a value of @p type from the top of the stack @p todo.
424 /// Greedy matching is sound as assignable types share their leaves.
425 const Def* reshape(DefVec& todo, const Def* type);
426
427 DefMap<Def2Def> etas_; ///< type ↦ val ↦ eta_coerce(type, val)
428
429 Analysis* analysis_;
430 std::unique_ptr<World> new_world_; ///< Owns Rewriter::world.
431};
432
433/// An RWPhase that searches for a pattern and replaces it.
434/// Implement the replace() hook - or use the MIM_REPL macro for an inline definition.
435class Repl : public RWPhase {
436public:
437 using RWPhase::RWPhase;
438
439 /// replace() inspects and builds Def%s of the **old** world; the RWPhase machinery carries the result over.
440 World& world() { return old_world(); }
441
442 /// @returns the replacement or `nullptr` if the pattern does not match.
443 virtual const Def* replace(const Def* def) = 0;
444
445private:
446 const Def* rewrite(const Def* def) final {
447 while (auto subst = replace(def))
448 def = subst;
449
450 return Rewriter::rewrite(def);
451 }
452};
453
454#define MIM_CONCAT_INNER(a, b) a##b
455#define MIM_CONCAT(a, b) MIM_CONCAT_INNER(a, b)
456
457#define MIM_REPL(__phases, __annex, ...) MIM_REPL_IMPL(__phases, __annex, __LINE__, __VA_ARGS__)
458
459// clang-format off
460#define MIM_REPL_IMPL(__phases, __annex, __id, ...) \
461 struct MIM_CONCAT(Repl_, __id) : ::mim::Repl { \
462 using Repl::Repl; \
463 \
464 const ::mim::Def* replace(const ::mim::Def* def) final __VA_ARGS__ \
465 }; \
466 ::mim::Phase::hook<__annex, MIM_CONCAT(Repl_, __id)>(__phases)
467// clang-format on
468
469/// Removes unreachable and dead code by rebuilding the whole World into a new one and `swap`ping them afterwards.
470/// @see @ref phases_rwphase
471class Cleanup : public RWPhase {
472public:
473 using RWPhase::RWPhase;
474};
475
476/// Organizes several Phase%s into a pipeline.
477/// If fixed_point() is `true`, rerun the whole pipeline until all Phase::todo()%s flags remain `false`.
478/// @see @ref phases_phase_man
479class PhaseMan : public Phase {
480public:
481 /// @name Construction
482 ///@{
485
486 void apply(bool, Phases&&);
487 void apply(const App*) final;
488 void apply(Phase&) final;
489 ///@}
490
491 /// @name Getters
492 ///@{
493 bool fixed_point() const { return fixed_point_; }
494 auto& phases() { return phases_; }
495 const auto& phases() const { return phases_; }
496 ///@}
497
498private:
499 void start() final;
500
501 Phases phases_;
502 bool fixed_point_;
503};
504
505/// Transitively visits all *reachable*, [*closed*](@ref Def::is_closed) mutables in the World.
506/// * Select with `elide_empty` whether you want to visit trivial mutables without body.
507/// * Set `schedule` if the mutables should be scheduled to ensure a correct order of dependencies.
508/// * If you are only interested in specific mutables, you can pass this to @p M.
509/// @see @ref phases_closed_mut_phase
510template<class M = Def>
511class ClosedMutPhase : public Phase {
512public:
513 ClosedMutPhase(World& world, std::string name, bool elide_empty, bool schedule = false)
514 : Phase(world, std::move(name))
515 , elide_empty_(elide_empty)
516 , schedule_(schedule) {}
518 : Phase(world, annex)
519 , elide_empty_(elide_empty)
520 , schedule_(schedule) {}
521
522 bool elide_empty() const { return elide_empty_; }
523 bool schedule() const { return schedule_; }
524
525protected:
526 void start() override {
527 world().template for_each<M>(elide_empty(), [this](M* mut) { root_ = mut, visit(mut); }, schedule());
528 }
529 virtual void visit(M*) = 0;
530 M* root() const { return root_; }
531
532private:
533 const bool elide_empty_;
534 const bool schedule_;
535 M* root_;
536};
537
538/// Like ClosedMutPhase but computes a Nest for each NestPhase::visit.
539/// @see @ref phases_nest_phase
540template<class M = Def>
541class NestPhase : public ClosedMutPhase<M> {
542public:
543 NestPhase(World& world, std::string name, bool elide_empty, bool schedule = false)
544 : ClosedMutPhase<M>(world, std::move(name), elide_empty, schedule) {}
547
548 const Nest& nest() const { return *nest_; }
549 virtual void visit(const Nest&) = 0;
550
551private:
552 void visit(M* mut) final {
553 Nest nest(mut);
554 nest_ = &nest;
555 visit(nest);
556 }
557
558 const Nest* nest_;
559};
560
561} // namespace mim
Traverses the current World using Rewriter infrastructure while staying in the same world.
Definition phase.h:130
virtual void prepare()
Run before the main analysis.
Definition phase.h:266
size_t version() const
Bumped on every observable lattice change; snapshot it around a code region to detect changes.
Definition phase.h:158
void start() override
Actual entry.
Definition phase.cpp:75
bool lattice_force(const Def *concr, const Def *abstr)
Non-monotone write of concr ↦ abstr into lattice() and map().
Definition phase.cpp:123
const Def * rewrite(const Def *) override
Rewrites def and then maps the result to its repr().
Definition phase.cpp:154
const Def * rewrite_imm_Proxy(const Proxy *proxy) override
By default: ignore Proxys.
Definition phase.h:271
Analysis(World &world, std::string name)
Definition phase.h:134
void make_dense()
Forces whole-World rounds unconditionally.
Definition phase.h:156
const Def * lookup(const Def *) override
Lookup old_def by searching in reverse through the stack of maps.
Definition phase.cpp:118
const Def * rewrite_mut(Def *) final
Schedules mut for a breadth-first visit of its dependencies and records mut -> mut.
Definition phase.cpp:159
void pin_imm(const Def *def, DefPred auto pinnable)
Definition phase.h:239
virtual void leave()
Called after curr_mut() has been completely dealt with.
Definition phase.h:292
void taint(Def *mut)
Additionally schedules mut for the next sparse round.
Definition phase.h:247
void touch()
Observable analysis information changed: records curr_mut() as dirty - the seed set of the next spars...
Definition phase.h:253
const Def * lattice(const Def *def) const
Definition phase.h:168
bool is_top(const Def *def) const
Definition phase.h:188
void pin_imm(DefSet &visited, const Def *def, DefPred auto pinnable)
pins every def in def's immutable subgraph for which pinnable holds.
Definition phase.h:231
virtual void rewrite_external(Def *)
Definition phase.cpp:116
bool lattice_erase(const Def *concr)
Non-monotone removal of concr from lattice(), so no later sparse round replays it.
Definition phase.h:212
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
const auto & lattice() const
The whole map; used e.g. to diff two fixed-point runs.
Definition phase.h:165
static bool is_singleton(const Def *def)
The sole inhabitant of a singleton type is ⊤ and shared by all binders of that type,...
Definition phase.h:199
bool pin(const Def *def)
Monotonically forces def to ⊤ (keep as is).
Definition phase.h:227
Analysis(World &world, flags_t annex)
Definition phase.h:137
const Def * repr(const Def *def) const
The representative of def: follows def ↦ lattice(def) to the end of its chain.
Definition phase.h:181
World & world()
Definition phase.h:58
A (possibly paramterized) Array.
Definition tuple.h:305
Removes unreachable and dead code by rebuilding the whole World into a new one and swapping them afte...
Definition phase.h:471
RWPhase(World &world, std::string name, Analysis *analysis=nullptr)
Definition phase.h:334
M * root() const
Definition phase.h:530
void start() override
Actual entry.
Definition phase.h:526
ClosedMutPhase(World &world, flags_t annex, bool elide_empty, bool schedule=false)
Definition phase.h:517
bool schedule() const
Definition phase.h:523
bool elide_empty() const
Definition phase.h:522
virtual void visit(M *)=0
ClosedMutPhase(World &world, std::string name, bool elide_empty, bool schedule=false)
Definition phase.h:513
Base class for all Defs.
Definition def.h:289
Defs deps() const noexcept
Definition def.cpp:486
World & world() const noexcept
Definition def.h:1105
T * isa_mut() const
If this is mutable, it will cast constness away and perform a dynamic_cast to T.
Definition def.h:588
const Def * type() const noexcept
Yields the "raw" type of this Def (maybe nullptr).
Definition def.h:1119
Some "global" variables needed all over the place.
Definition driver.h:64
virtual void visit(const Nest &)=0
const Nest & nest() const
Definition phase.h:548
void visit(M *mut) final
Definition phase.h:552
NestPhase(World &world, std::string name, bool elide_empty, bool schedule=false)
Definition phase.h:543
NestPhase(World &world, flags_t annex, bool elide_empty, bool schedule=false)
Definition phase.h:545
Builds a nesting tree for all mutables/binders.
Definition nest.h:25
Organizes several Phases into a pipeline.
Definition phase.h:479
PhaseMan(World &world, flags_t annex)
Definition phase.h:483
bool fixed_point() const
Definition phase.h:493
auto & phases()
Definition phase.h:494
void start() final
Actual entry.
Definition phase.cpp:346
const auto & phases() const
Definition phase.h:495
void apply(bool, Phases &&)
Definition phase.cpp:322
A Phase performs one self-contained task over the whole World.
Definition phase.h:23
virtual std::unique_ptr< Phase > recreate()
Creates a new instance; needed by a fixed-point PhaseMan.
Definition phase.cpp:44
std::string name_
Definition phase.h:104
static void hook(Flags2Phases &phases)
Definition phase.h:51
friend class Analysis
Definition phase.h:106
void invalidate(bool todo=true)
Signals that another round of fixed-point iteration is required, either as part of.
Definition phase.h:79
flags_t annex() const
Definition phase.h:62
const fe::Log & log() const
Definition phase.h:60
Phase(World &world, std::string name)
Definition phase.h:27
static void run(Args &&... args)
Runs a single Phase.
Definition phase.h:89
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
virtual std::unique_ptr< Phase > take_resolved()
The Phase to use instead; nullptr means elide.
Definition phase.h:43
virtual void apply(Phase &)
Dito, but invoked by Phase::recreate.
Definition phase.h:36
Driver & driver()
Definition phase.h:59
const fe::Vector< std::string > & args()
Command-line arguments passed to this Phase's plugin via -X <plugin>:<arg>.
Definition phase.cpp:23
virtual void run()
Entry point and generates some debug output; invokes Phase::start.
Definition phase.cpp:51
static std::unique_ptr< Phase > create(const Flags2Phases &phases, const Def *def)
Definition phase.cpp:25
bool todo() const
Definition phase.h:71
std::string_view name() const
Definition phase.h:61
virtual void start()=0
Actual entry.
virtual void apply(const App *)
Invoked if your Phase has additional args.
Definition phase.h:35
virtual bool redirects() const
If true, Phase::create uses take_resolved().
Definition phase.h:42
virtual ~Phase()=default
World & world()
Definition phase.h:58
Used as intermediate value during optimizatinos such as Analysis.
Definition def.h:1065
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
virtual void finalize()
Run after all roots have been walked but still before the two worlds are swapped.
Definition phase.h:406
const Def * lookup(const Def *) override
Lookup old_def by searching in reverse through the stack of maps.
Definition phase.cpp:212
RWPhase(World &world, flags_t annex, Analysis *analysis=nullptr)
Definition phase.h:336
const Def * rewrite_imm_Tuple(const Tuple *) override
Definition phase.cpp:237
static bool splittable(const Def *type)
Does coerce() explode and reassemble values of type?
Definition phase.h:399
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:350
const Def * coerce(const Def *new_type, const Def *new_val)
Converts the new new_val into a value of the new new_type, where a checking position demands it.
Definition phase.cpp:264
Analysis * analysis()
Definition phase.h:342
RWPhase(World &world, std::string name, Analysis *analysis=nullptr)
Definition phase.h:334
void start() override
Actual entry.
Definition phase.cpp:184
bool is_top(const Def *old_def) const
Definition phase.h:355
const Def * rewrite_stub(Def *, Def *) override
Definition phase.cpp:250
World & world()=delete
Hides both and forbids direct access.
const Analysis * analysis() const
Definition phase.h:343
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
World & old_world()
Get old Defs from here.
Definition phase.h:378
virtual void rewrite_external(Def *)
Definition phase.cpp:218
const Def * rewrite_imm_App(const App *) override
Definition phase.cpp:223
An RWPhase that searches for a pattern and replaces it.
Definition phase.h:435
const Def * rewrite(const Def *def) final
Definition phase.h:446
virtual const Def * replace(const Def *def)=0
RWPhase(World &world, std::string name, Analysis *analysis=nullptr)
Definition phase.h:334
World & world()
replace() inspects and builds Defs of the old world; the RWPhase machinery carries the result over.
Definition phase.h:440
Recurseivly rebuilds part of a program into the provided World w.r.t. Rewriter::map.
Definition rewrite.h:21
World & world()
Definition rewrite.h:33
D * curr_mut() const
Definition rewrite.h:98
virtual const Def * rewrite_stub(Def *, Def *)
Definition rewrite.cpp:257
Rewriter(World &world)
Definition rewrite.cpp:16
virtual const Def * rewrite(const Def *)
Definition rewrite.cpp:62
A dependent tuple type.
Definition tuple.h:39
Data constructor for a Sigma.
Definition tuple.h:84
The World represents the whole program and manages creation of MimIR nodes (Defs).
Definition world.h:43
const Def * inhabitant(const Def *type)
The sole inhabitant of type, if it has exactly one, nullptr otherwise.
Definition world.h:342
Driver & driver()
Definition world.h:107
Used by Def::proj & friends.
Definition def.h:186
#define MIM_MUT_NODE(X)
Definition def.h:65
Definition ast.h:16
DefMap< const Def * > Def2Def
Definition def.h:95
u64 flags_t
Definition types.h:39
GIDSet< Def * > MutSet
Definition def.h:106
fe::Vector< const Def * > DefVec
Definition def.h:98
GIDSet< const Def * > DefSet
Definition def.h:94
std::deque< std::unique_ptr< Phase > > Phases
Definition phase.h:18
ankerl::unordered_dense::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:30
#define CODE_MUT(N)
Definition phase.h:288
static consteval flags_t base()
Definition plugin.h:276