MimIR
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
phase.cpp
Go to the documentation of this file.
1#include "mim/phase.h"
2
3#include <algorithm>
4#include <memory>
5#include <utility>
6
7#include <fe/bitset.h>
8
9#include "mim/driver.h"
10#include "mim/flags.h"
11
12namespace mim {
13
14/*
15 * Phase
16 */
17
19 : world_(world)
20 , annex_(annex)
21 , name_(world.annex(annex)->sym()) {}
22
23const fe::Vector<std::string>& Phase::args() { return driver().args(Annex::demangle(Annex::flags2plugin(annex_))); }
24
25std::unique_ptr<Phase> Phase::recreate() {
26 auto ctor = driver().phase(annex());
27 auto ptr = (*ctor)(world());
28 ptr->apply(*this);
29 return ptr;
30}
31
32void Phase::run() {
33 auto profiling = driver().flags().profile != Flags::Profile::None;
34 if (profiling) driver().profiler().start(name());
35 world().verify().log().i("🚀 launch phase `{}`", name());
36 start();
37 world().verify().log().i("🏁 finish phase `{}`", name());
38 if (profiling) driver().profiler().stop();
39}
40
41void Phase::profile_count(std::string_view key, uint64_t n) {
42 if (driver().flags().profile != Flags::Profile::None) driver().profiler().count(key, n);
43}
44
45/*
46 * Analyzer
47 */
48
50 old2news_.clear();
51 worklist_.clear();
52 push();
53 todo_ = false;
54 bootstrapping_ = true; // every full round walks the annexes again - and that walk *is* the bootstrapping half
55}
56
58 curr_sparse_ = !dense_ && !nonlocal_ && !dirty_.empty();
59 nonlocal_ = false;
60 auto seeds = fe::Vector<Def*>(dirty_.begin(), dirty_.end());
61 dirty_.clear();
62
63 prepare();
64
65 if (curr_sparse_) {
66 bootstrapping_ = false; // a sparse round re-drains program muts - it has no annex half
67 log().v("sparse round: re-drain {} dirty muts", seeds.size());
68 std::ranges::sort(seeds, GIDLt<Def*>()); // MutSet iteration order is nondeterministic
69
70 // Pre-install what earlier rounds substituted, so this round prunes everything they already settled -
71 // except for mutables, whose map entry doubles as the per-round "already scheduled" marker and would
72 // suppress their drain. Pruning skips rewrite hooks, which is why only a sparse round does it: the
73 // full round that certifies the fixed point always re-derives from the program.
74 for (auto [concr, abstr] : lattice_)
75 if (!concr->isa_mut()) map(concr, abstr);
76
77 for (auto mut : seeds)
78 rewrite(mut);
79 drain();
80 } else {
81 for (const auto& [flags, e] : world().annexes())
82 rewrite_annex(flags, e.sym, e.def);
83 drain();
84
85 bootstrapping_ = false;
86
87 for (auto mut : world().externals().muts())
89 drain();
90
91 finalize();
92 }
93
94 profile_count(curr_sparse_ ? "rounds.sparse" : "rounds.full");
95 profile_count("muts.drained", std::exchange(num_drained_, size_t(0)));
96
97 // A quiet sparse round only certifies the muts it visited:
98 // force one full round; the fixed point counts only if that one stays quiet, too.
99 if (curr_sparse_ && !todo()) invalidate();
100}
101
102void Analysis::rewrite_annex(flags_t, Sym, const Def* def) { rewrite(def); }
104
105const Def* Analysis::repr_(const Def* slow, const Def* fast) const {
106 while (slow != fast) {
107 auto next = follow(fast);
108 if (!next) return fast;
109 fast = follow(next);
110 if (!fast) return next;
111 slow = follow(slow);
112 assert(slow && "slow lags fast, so fast already traversed slow's successor");
113 }
114
115 auto res = slow;
116 for (auto def = follow(slow); def != slow; def = follow(def))
117 if (def->gid() < res->gid()) res = def;
118 return res;
119}
120
121const Def* Analysis::rewrite(const Def* def) {
122 if (def->isa_mut()) return Rewriter::rewrite(def);
123 return repr(Rewriter::rewrite(def));
124}
125
127 if (lookup(mut)) return mut; // already scheduled this round
128 map(mut, mut);
129 worklist_.emplace_back(mut);
130 return mut;
131}
132
133void Analysis::drain() {
134 while (!worklist_.empty()) {
135 auto mut = worklist_.front();
136 worklist_.pop_front();
137 ++num_drained_;
138
139 auto _ = enter(mut);
140 log().d("enter {}", mut);
141 for (auto d : mut->deps())
142 rewrite(d);
143 leave();
144 }
145}
146
147/*
148 * RWBase
149 */
150
152 auto max_iters = driver().flags().max_fp_iters;
153 bool todo = true;
154 for (uint32_t i = 0; todo; ++i) {
155 if (i >= max_iters) fe::throwf("phase `{}` did not reach a fixed point after {} iterations", name(), max_iters);
156 log().v("iteration {}", i);
157 todo = analyze();
158 }
159
160 // Count the Def%s each half of the walk creates.
161 // For an RWPhase the annex half is a fixed tax proportional to the loaded plugins' annex graph - not to the
162 // program - which is exactly why an InplaceRWPhase skips it by default.
163 auto gid = Rewriter::world().curr_gid();
164 if (rewrite_annexes())
165 for (const auto& [flags, e] : Phase::world().annexes())
166 rewrite_annex(flags, e.sym, e.def);
167 profile_count("rw.defs.annex", Rewriter::world().curr_gid() - gid);
168
169 bootstrapping_ = false;
170
171 gid = Rewriter::world().curr_gid();
172 // mutate(): an in-place rewrite_external may re-externalize, which would invalidate a live iterator.
173 for (auto mut : Phase::world().externals().mutate())
174 rewrite_external(mut);
175 finalize(); // inside the span: work deferred by the root walk belongs to the root walk
176 profile_count("rw.defs.external", Rewriter::world().curr_gid() - gid);
177}
178
180 if (analysis_) {
181 analysis_->reset();
182 analysis_->run();
183 return analysis_->todo();
184 }
185
186 return false;
187}
188
189/*
190 * RWPhase
191 */
192
196}
197
199 if (auto e = fe::lookup(new_world().annexes().flags2entry(), flags)) return e->def;
200 auto& e = old_world().annexes().flags2entry().at(flags);
201 return new_world().annexes().attach(flags, e.sym, rewrite_root(e.def));
202}
203
204void RWPhase::rewrite_annex(flags_t f, Sym sym, const Def* def) {
205 auto new_def = rewrite_root(def);
206 if (!new_world().annexes().flags2entry().contains(f)) new_world().annexes().attach(f, sym, new_def);
207}
208
210 auto new_mut = rewrite_root(old_mut)->as_mut();
211 if (old_mut->is_external()) new_mut->externalize();
212}
213
214/*
215 * InplaceRWPhase
216 */
217
218void InplaceRWPhase::rewrite_annex(flags_t flags, Sym, const Def* def) {
219 if (auto new_def = rewrite_root(def); new_def != def) {
220 world().annexes().reattach(flags, new_def);
221 invalidate();
222 }
223}
224
226 auto new_def = rewrite_root(old_mut);
227 if (new_def == old_mut) return;
228
229 // The rewrite replaced the external itself; carry the external flag over.
230 old_mut->internalize();
231 new_def->as_mut()->externalize();
232 invalidate();
233}
234
236 if (auto hole = mut->isa<Hole>()) {
237 auto [last, op] = hole->find();
238 return op ? rewrite(op) : last; // an unresolved Hole stays as is
239 }
240
241 // A mutable's identity is tied to its type, so if the rewrite changes the type, we cannot keep it: fall back to
242 // an RWPhase-style rebuild - Rewriter::rewrite_mut stubs a fresh mutable (in this very World) and maps onto it.
243 if (auto type = mut->type(); type && rewrite(type) != type) {
244 profile_count("inplace.muts.rebuilt");
245 invalidate();
246 return Rewriter::rewrite_mut(mut);
247 }
248
249 map(mut, mut); // keep the identity; doubles as the cycle breaker for recursive mutables
250 if (!mut->is_set()) return mut;
251
252 auto _ = enter(mut);
253 auto new_ops = rewrite(mut->ops());
254 if (!std::ranges::equal(new_ops, mut->ops())) {
255 mut->unset()->set(new_ops);
256 profile_count("inplace.muts.reset");
257 invalidate();
258 }
259
260 return mut;
261}
262
263/*
264 * PhaseMan
265 */
266
267void PhaseMan::apply(bool fp, Phases&& phases) {
268 fixed_point_ = fp;
269 phases_ = std::move(phases);
270 name_ += fixed_point_ ? " tt" : " ff";
271}
272
273void PhaseMan::apply(const App* app) {
274 auto [fp, args] = app->uncurry_args<2>();
275
276 auto phases = Phases();
277 for (auto arg : args->projs())
278 if (auto phase = create(driver().phases(), arg)) phases.emplace_back(std::move(phase));
279
280 apply(Lit::as<bool>(fp), std::move(phases));
281}
282
283void PhaseMan::apply(Phase& phase) {
284 auto& man = static_cast<PhaseMan&>(phase);
285 Phases new_phases;
286 for (auto& old_phase : man.phases())
287 new_phases.emplace_back(std::unique_ptr<Phase>(static_cast<Phase*>(old_phase->recreate().release())));
288 apply(man.fixed_point(), std::move(new_phases));
289}
290
292 auto max_iters = driver().flags().max_fp_iters;
293 auto n = phases().size();
294 // A phase's run is a deterministic function of the World's content.
295 // So a phase only needs to run (again) if the World (may have) changed since its last quiet run.
296 auto all = fe::Bitset(n, true);
297 auto stale = all;
298 auto ran = fe::Bitset(n, false);
299
300 for (uint32_t iter = 0; stale.any(); ++iter) {
301 if (iter >= max_iters)
302 fe::throwf("phase `{}` did not reach a fixed point after {} iterations", name(), max_iters);
303 if (fixed_point()) log().v("🔄 fixed-point iteration {}", iter);
304
305 bool todo = false;
306 for (size_t i = 0; i != n; ++i) {
307 auto& phase = phases()[i];
308 if (!stale.test(i)) {
309 log().v("skip `{}`: World unchanged since its last quiet run", phase->name());
310 profile_count("phases.skipped");
311 continue;
312 }
313
314 if (ran.test(i)) { // re-runs need a fresh instance
315 auto new_phase = std::unique_ptr<Phase>(static_cast<Phase*>(phase->recreate().release()));
316 swap(new_phase, phase);
317 }
318
319 phase->run();
320 ran.set(i);
321 stale.clear(i);
322
323 if (phase->todo()) {
324 todo = true;
325 // The World changed: everyone - including this phase itself - gets another look.
326 stale = all;
327 }
328 }
329
330 todo &= fixed_point();
332 if (!fixed_point()) break;
333 }
334}
335
336} // namespace mim
virtual void prepare()
Run before the main analysis.
Definition phase.h:314
void start() override
Actual entry.
Definition phase.cpp:57
const Def * rewrite(const Def *) override
Rewrites def and then maps the result to its repr().
Definition phase.cpp:121
virtual void leave()
Called after curr_mut() has been completely dealt with.
Definition phase.h:334
virtual void rewrite_annex(flags_t, Sym, const Def *)
Definition phase.cpp:102
virtual void rewrite_external(Def *)
Definition phase.cpp:103
virtual void reset()
Clears the rewriter map and resets Phase::todo() and is_bootstrapping() for the next fixed-point iter...
Definition phase.cpp:49
virtual void finalize()
Run after the main analysis - only in full rounds, so it always sees the complete abstract World.
Definition phase.h:316
const Def * repr(const Def *def) const
The representative of def: follows def ↦ lattice(def) to the end of its chain.
Definition phase.h:206
Def * rewrite_mut(Def *) override
Schedules mut for a breadth-first visit of its dependencies and records mut -> mut.
Definition phase.cpp:126
World & world()
Definition phase.h:77
static auto uncurry_args(const Def *def)
Definition lam.h:272
Base class for all Defs.
Definition def.h:313
bool is_set() const
Definition def.h:418
Def * set(size_t i, const Def *)
Successively set from left to right.
Definition def.cpp:203
T * as_mut() const
Asserts that this is a mutable, casts constness away and performs a static_cast to T.
Definition def.h:635
constexpr auto ops() const noexcept
Definition def.h:396
T * isa_mut() const
If this is mutable, it will cast constness away and perform a dynamic_cast to T.
Definition def.h:626
const Def * type() const noexcept
Yields the "raw" type of this Def (maybe nullptr).
Definition def.h:1186
bool is_external() const noexcept
Definition def.h:599
void internalize()
Definition def.cpp:620
Def * unset()
Unsets all Def::ops; works even, if not set at all or only partially set.
Definition def.cpp:220
Flags & flags()
Definition driver.h:77
const fe::Vector< std::string > & args(std::string_view plugin) const
Yields an empty fe::Vector if plugin has none.
Definition driver.cpp:214
fe::Profiler & profiler()
Definition driver.h:81
auto phase(flags_t flags)
Definition driver.h:200
This node is a hole in the IR that is inferred by its context later on.
Definition check.h:16
const Def * rewrite_mut(Def *mut) override
Definition phase.cpp:235
void rewrite_annex(flags_t, Sym, const Def *) override
Definition phase.cpp:218
void rewrite_external(Def *) override
Definition phase.cpp:225
World & world()
Definition phase.h:77
static T as(const Def *def)
Definition def.h:1019
PhaseMan(World &world, flags_t annex)
Definition phase.h:619
bool fixed_point() const
Definition phase.h:629
auto & phases()
Definition phase.h:630
void start() final
Actual entry.
Definition phase.cpp:291
void apply(bool, Phases &&)
Definition phase.cpp:267
virtual std::unique_ptr< Phase > recreate()
Creates a new instance; needed by a fixed-point PhaseMan.
Definition phase.cpp:25
std::string name_
Definition phase.h:123
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
const fe::Log & log() const
Definition phase.h:79
Phase(World &world, std::string name)
Definition phase.h:29
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 fe::Profiler counter key of the current run; no-op unless profiling is enabled.
Definition phase.cpp:41
Driver & driver()
Definition phase.h:78
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:32
bool todo() const
Definition phase.h:90
std::string_view name() const
Definition phase.h:80
virtual void start()=0
Actual entry.
World & world()
Definition phase.h:77
virtual void finalize()
Run after all roots have been walked - but for an RWPhase still before the two worlds are swapped.
Definition phase.h:449
virtual void rewrite_external(Def *)=0
void start() override
Actual entry.
Definition phase.cpp:151
virtual bool rewrite_annexes() const =0
virtual const Def * rewrite_root(const Def *def)
Rewrites a root - i.e. an annex or an external.
Definition phase.h:445
virtual bool analyze()
Runs the optional pre-analysis on Phase::world, typically to a fixed point, before rewriting begins.
Definition phase.cpp:179
virtual void rewrite_annex(flags_t, Sym, const Def *)=0
World & new_world()
Create new Defs into this.
Definition phase.h:487
void rewrite_annex(flags_t, Sym, const Def *) override
Definition phase.cpp:204
void start() override
RWBase::start() and then swaps the two worlds.
Definition phase.cpp:193
const Def * annex()
Definition phase.h:492
void rewrite_external(Def *) override
Definition phase.cpp:209
World & old_world()
Get old Defs from here.
Definition phase.h:486
friend void swap(Rewriter &rw1, Rewriter &rw2) noexcept
Definition rewrite.h:90
World & world()
Definition rewrite.h:32
virtual const Def * rewrite_mut(Def *)
Definition rewrite.cpp:63
virtual void push()
Definition rewrite.h:37
virtual const Def * map(const Def *old_def, const Def *new_def)
Definition rewrite.h:44
fe::Vector< Def2Def > old2news_
Definition rewrite.h:106
virtual const Def * rewrite(const Def *)
Definition rewrite.cpp:42
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:59
const Def * reattach(flags_t flags, const Def *def)
Overwrites the Def of an already attach()ed annex, keeping its Sym.
Definition world.h:251
const Def * attach(flags_t, Sym, const Def *)
Definition world.cpp:122
auto & flags2entry()
An annex's flags map to its full name and its Def.
Definition world.h:227
The World represents the whole program and manages creation of MimIR nodes (Defs).
Definition world.h:43
u32 curr_gid() const
Manage global identifier - a unique number for each Def.
Definition world.h:117
World & verify()
Verifies that all externals() and annexes() are Def::is_closed(), if MIM_ENABLE_CHECKS.
Definition world.cpp:1048
const fe::Log & log() const
Log via log().e("...", args) etc.; owned by the Driver.
Definition world.cpp:190
Annexes & annexes()
Definition world.h:286
Definition ast.h:16
u64 flags_t
Definition types.h:39
std::deque< std::unique_ptr< Phase > > Phases
Definition phase.h:20
static std::string demangle(plugin_t plugin)
Reverts an Axm::mangled plugin back to its name; never longer than Annex::Max_Plugin_Size.
Definition plugin.cpp:33
static constexpr plugin_t flags2plugin(flags_t f)
Definition plugin.h:264
@ None
No profiling.
Definition flags.h:14
uint32_t max_fp_iters
Definition flags.h:21
Profile profile
Definition flags.h:28