MimIR
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
pass.h
Go to the documentation of this file.
1#pragma once
2
3#include <memory>
4#include <stack>
5#include <typeindex>
6
7#include <fe/assert.h>
8#include <fe/cast.h>
9
10#include "mim/world.h"
11
12namespace mim {
13
14class Pass;
15class PassMan;
16using Passes = std::deque<std::unique_ptr<Pass>>;
17
18/// @name Undo
19/// Used by FPPass::analyze to indicate where to backtrack to.
20///@{
21using undo_t = size_t;
22static constexpr undo_t No_Undo = std::numeric_limits<undo_t>::max();
23///@}
24
25/// Common base for Phase and Pass.
26class Stage : public fe::RuntimeCast<Stage> {
27public:
28 /// @name Construction & Destruction
29 ///@{
30 Stage(World& world, std::string name)
31 : world_(world)
32 , name_(std::move(name)) {}
34
35 virtual ~Stage() = default;
36 virtual std::unique_ptr<Stage> recreate(); ///< Creates a new instance; needed by a fixed-point PhaseMan.
37 virtual void apply(const App*) {} ///< Invoked if your Stage has additional args.
38 virtual void apply(Stage&) {} ///< Dito, but invoked by Stage::recreate.
39
40 /// @name Redirection
41 /// A Stage may resolve to a *different* Stage (or to nothing) after Stage::apply.
42 /// This is used by the `%%compile.named_*` stages that resolve a string to another plugin's annex.
43 ///@{
44 virtual bool redirects() const { return false; } ///< If `true`, Stage::create uses take_resolved().
45 virtual std::unique_ptr<Stage> take_resolved() {
46 return {};
47 } ///< The Stage to use instead; `nullptr` means *elide*.
48 ///@}
49
50 static std::unique_ptr<Stage> create(const Flags2Stages& stages, const Def* def) {
51 auto& world = def->world();
52 auto p_def = App::uncurry_callee(def);
53 world.DLOG("apply stage: `{}`", p_def);
54
55 if (auto axm = p_def->isa<Axm>())
56 if (auto i = stages.find(axm->flags()); i != stages.end()) {
57 auto stage = i->second(world);
58 if (stage) {
59 stage->apply(def->isa<App>());
60 if (stage->redirects()) return stage->take_resolved();
61 }
62 return stage;
63 } else
64 error("stage `{}` not found", axm->sym());
65 else
66 error("unsupported callee for a stage: `{}`", p_def);
67 }
68
69 template<class A, class P>
70 static void hook(Flags2Stages& stages) {
71 assert_emplace(stages, 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
84private:
85 World& world_;
86 flags_t annex_ = 0;
87
88protected:
89 std::string name_;
90};
91
92/// All Pass%es that want to be registered in the PassMan must implement this interface.
93/// * Inherit from RWPass if your pass does **not** need state and a fixed-point iteration.
94/// * Inherit from FPPass if you **do** need state and a fixed-point.
95/// * If you do not rely on interaction between different Pass%es, consider using Phase instead.
96class Pass : public Stage {
97public:
98 /// @name Construction & Destruction
99 ///@{
100 Pass(World& world, std::string name)
101 : Stage(world, name) {}
104
105 virtual void init(PassMan*);
106 ///@}
107
108 /// @name Getters
109 ///@{
110 PassMan& man() { return *man_; }
111 const PassMan& man() const { return *man_; }
112 size_t index() const { return index_; }
113 ///@}
114
115 /// @name Rewrite Hook for the PassMan
116 /// Rewrites an *immutable* @p def within PassMan::curr_mut.
117 /// @returns the replacement.
118 ///@{
119 virtual const Def* rewrite(const Def* def) { return def; }
120 virtual const Def* rewrite(const Var* var) { return var; }
121 virtual const Def* rewrite(const Proxy* proxy) { return proxy; }
122 ///@}
123
124 /// @name Analyze Hook for the PassMan
125 /// Invoked after the PassMan has finished Pass::rewrite%ing PassMan::curr_mut to analyze the Def.
126 /// Will only be invoked if Pass::fixed_point() yields `true` - which will be the case for FPPass%es.
127 /// @returns mim::No_Undo or the state to roll back to.
128 ///@{
129 virtual undo_t analyze(const Def*) { return No_Undo; }
130 virtual undo_t analyze(const Var*) { return No_Undo; }
131 virtual undo_t analyze(const Proxy*) { return No_Undo; }
132 ///@}
133
134 /// @name Further Hooks for the PassMan
135 ///@{
136 virtual bool fixed_point() const { return false; }
137
138 /// Should the PassMan even consider this pass?
139 virtual bool inspect() const = 0;
140
141 /// Invoked just before Pass::rewrite%ing PassMan::curr_mut's body.
142 /// @note This is invoked when seeing the *inside* of a mutable the first time.
143 /// This is often too late, as you usually want to do something when you see a mutable the first time from the
144 /// *outside*. This means that this PassMan::curr_mut has already been encountered elsewhere. Otherwise, we wouldn't
145 /// have seen PassMan::curr_mut to begin with (unless it is Def::is_external).
146 virtual void enter() {}
147
148 /// Invoked **once** before entering the main rewrite loop.
149 virtual void prepare() {}
150 ///@}
151
152 /// @name proxy
153 ///@{
154 const Proxy* proxy(const Def* type, Defs ops, u32 tag = 0) { return world().proxy(type, ops, index(), tag); }
155 /// Check whether given @p def is a Proxy whose Proxy::pass matches this Pass's @p IPass::index.
156 const Proxy* isa_proxy(const Def* def, u32 tag = 0) {
157 if (auto proxy = def->isa<Proxy>(); proxy != nullptr && proxy->pass() == index() && proxy->tag() == tag)
158 return proxy;
159 return nullptr;
160 }
161 const Proxy* as_proxy(const Def* def, u32 tag = 0) {
162 auto proxy = def->as<Proxy>();
163 assert_unused(proxy->pass() == index() && proxy->tag() == tag);
164 return proxy;
165 }
166 ///@}
167
168private:
169 /// @name Memory Management
170 ///@{
171 virtual void* alloc() { return nullptr; } ///< Default constructor.
172 virtual void* copy(const void*) { return nullptr; } ///< Copy constructor.
173 virtual void dealloc(void*) {} ///< Destructor.
174 ///@}
175
176 PassMan* man_ = nullptr;
177 size_t index_;
178
179 friend class PassMan;
180};
181
182/// An optimizer that combines several optimizations in an optimal way.
183/// This is loosely based upon:
184/// "Composing dataflow analyses and transformations" by Lerner, Grove, Chambers.
185class PassMan : public Pass {
186public:
189
190 void apply(Passes&&);
191 void apply(const App* app) final;
192 void apply(Stage& stage) final { apply(std::move(static_cast<PassMan&>(stage).passes_)); }
193 void init(PassMan*) final { fe::unreachable(); }
194
195 bool inspect() const final { fe::unreachable(); }
196
197 /// @name Getters
198 ///@{
199 bool empty() const { return passes_.empty(); }
200 const auto& passes() const { return passes_; }
201 bool fixed_point() const { return fixed_point_; }
202 Def* curr_mut() const { return curr_mut_; }
203 ///@}
204
205 /// @name Create and run Passes
206 ///@{
207 void run(); ///< Run all registered passes on the whole World.
208
209 Pass* find(std::type_index key) {
210 if (auto i = registry_.find(key); i != registry_.end()) return i->second;
211 return nullptr;
212 }
213
214 template<class P>
215 P* find() {
216 if (auto pass = find(std::type_index(typeid(P)))) return static_cast<P*>(pass);
217 return nullptr;
218 }
219
220 void add(std::unique_ptr<Pass>&& pass) {
221 fixed_point_ |= pass->fixed_point();
222 auto p = pass.get();
223 auto type_idx = std::type_index(typeid(*p));
224 if (auto pass = find(type_idx)) error("already added `{}`", pass->name());
225 registry_.emplace(type_idx, p);
226 passes_.emplace_back(std::move(pass));
227 }
228 ///@}
229
230private:
231 /// @name State
232 ///@{
233 struct State {
234 State() = default;
235 State(const State&) = delete;
236 State(State&&) = delete;
237 State& operator=(State) = delete;
238 State(size_t num)
239 : data(num) {}
240
241 Def* curr_mut = nullptr;
242 DefVec old_ops;
243 std::stack<Def*> stack;
244 MutMap<undo_t> mut2visit;
245 Vector<void*> data;
246 Def2Def old2new;
247 DefSet analyzed;
248 };
249
250 void push_state();
251 void pop_states(undo_t undo);
252 State& curr_state() {
253 assert(!states_.empty());
254 return states_.back();
255 }
256 const State& curr_state() const {
257 assert(!states_.empty());
258 return states_.back();
259 }
260 undo_t curr_undo() const { return states_.size() - 1; }
261 ///@}
262
263 /// @name rewriting
264 ///@{
265 const Def* rewrite(const Def*);
266
267 const Def* map(const Def* old_def, const Def* new_def) {
268 curr_state().old2new[old_def] = new_def;
269 curr_state().old2new.emplace(new_def, new_def);
270 return new_def;
271 }
272
273 std::optional<const Def*> lookup(const Def* old_def) {
274 for (auto& state : states_ | std::views::reverse)
275 if (auto i = state.old2new.find(old_def); i != state.old2new.end()) return i->second;
276 return {};
277 }
278 ///@}
279
280 /// @name analyze
281 ///@{
282 undo_t analyze(const Def*);
283 bool analyzed(const Def* def) {
284 for (auto& state : states_ | std::views::reverse)
285 if (state.analyzed.contains(def)) return true;
286 curr_state().analyzed.emplace(def);
287 return false;
288 }
289 ///@}
290
291 Passes passes_;
292 absl::flat_hash_map<std::type_index, Pass*> registry_;
293 std::deque<State> states_;
294 Def* curr_mut_ = nullptr;
295 bool fixed_point_ = false;
296 bool proxy_ = false;
297
298 template<class P, class M>
299 friend class FPPass;
300};
301
302/// Inherit from this class using [CRTP](https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern),
303/// if your Pass does **not** need state and a fixed-point iteration.
304/// If you are only interested in specific mutables, you can pass this to @p M.
305template<class P, class M = Def>
306class RWPass : public Pass {
307public:
308 RWPass(World& world, std::string name)
309 : Pass(world, std::move(name)) {}
312
313 bool inspect() const override {
314 if constexpr (std::is_same<M, Def>::value)
315 return man().curr_mut();
316 else
317 return man().curr_mut()->template isa<M>();
318 }
319
320 M* curr_mut() const {
321 if constexpr (std::is_same<M, Def>::value)
322 return man().curr_mut();
323 else
324 return man().curr_mut()->template as<M>();
325 }
326};
327
328/// Inherit from this class using [CRTP](https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern),
329/// if you **do** need a Pass with a state and a fixed-point.
330template<class P, class M = Def>
331class FPPass : public RWPass<P, M> {
332public:
334 using Data = std::tuple<>; ///< Default.
335
336 FPPass(World& world, std::string name)
337 : Super(world, std::move(name)) {}
340
341 bool fixed_point() const override { return true; }
342
343protected:
344 /// @name State-related Getters
345 ///@{
346 const auto& states() const { return Super::man().states_; }
347 auto& states() { return Super::man().states_; }
348 auto& data() {
349 assert(!states().empty());
350 return *static_cast<typename P::Data*>(states().back().data[Super::index()]);
351 }
352 template<size_t I>
353 auto& data() {
354 return std::get<I>(data());
355 }
356 /// Use this for your convenience if `P::Data` is a map.
357 template<class K>
358 auto& data(const K& key) {
359 return data()[key];
360 }
361 /// Use this for your convenience if `P::Data<I>` is a map.
362 template<size_t I, class K>
363 auto& data(const K& key) {
364 return data<I>()[key];
365 }
366 ///@}
367
368 /// @name undo
369 ///@{
370 undo_t curr_undo() const { return Super::man().curr_undo(); } ///< Current undo point.
371
372 /// Retrieves the point to backtrack to just **before** @p mut was seen the very first time.
373 undo_t undo_visit(Def* mut) const {
374 const auto& mut2visit = Super::man().curr_state().mut2visit;
375 if (auto i = mut2visit.find(mut); i != mut2visit.end()) return i->second;
376 return No_Undo;
377 }
378
379 /// Retrieves the point to backtrack to just **before** rewriting @p mut%'s body.
380 undo_t undo_enter(Def* mut) const {
381 for (auto i = states().size(); i-- != 0;)
382 if (states()[i].curr_mut == mut) return i;
383 return No_Undo;
384 }
385 ///@}
386
387private:
388 /// @name Memory Management for State
389 ///@{
390 void* alloc() override { return new typename P::Data(); }
391 void* copy(const void* p) override { return new typename P::Data(*static_cast<const typename P::Data*>(p)); }
392 void dealloc(void* state) override { delete static_cast<typename P::Data*>(state); }
393 ///@}
394};
395
396} // namespace mim
const Def * uncurry_callee() const
Definition lam.h:327
Definition axm.h:9
Base class for all Defs.
Definition def.h:246
World & world() const noexcept
Definition def.cpp:444
Some "global" variables needed all over the place.
Definition driver.h:19
void dealloc(void *state) override
Destructor.
Definition pass.h:392
auto & data()
Definition pass.h:353
auto & data()
Definition pass.h:348
RWPass< P, M > Super
Definition pass.h:333
undo_t undo_visit(Def *mut) const
Retrieves the point to backtrack to just before mut was seen the very first time.
Definition pass.h:373
FPPass(World &world, flags_t annex)
Definition pass.h:338
auto & data(const K &key)
Use this for your convenience if P::Data is a map.
Definition pass.h:358
FPPass(World &world, std::string name)
Definition pass.h:336
void * copy(const void *p) override
Copy constructor.
Definition pass.h:391
undo_t curr_undo() const
Current undo point.
Definition pass.h:370
const auto & states() const
Definition pass.h:346
bool fixed_point() const override
Definition pass.h:341
std::tuple<> Data
Default.
Definition pass.h:334
void * alloc() override
Default constructor.
Definition pass.h:390
auto & data(const K &key)
Use this for your convenience if P::Data<I> is a map.
Definition pass.h:363
auto & states()
Definition pass.h:347
undo_t undo_enter(Def *mut) const
Retrieves the point to backtrack to just before rewriting mut's body.
Definition pass.h:380
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
An optimizer that combines several optimizations in an optimal way.
Definition pass.h:185
P * find()
Definition pass.h:215
void init(PassMan *) final
Definition pass.h:193
Def * curr_mut() const
Definition pass.h:202
void apply(Passes &&)
Definition pass.cpp:36
Pass * find(std::type_index key)
Definition pass.h:209
void apply(Stage &stage) final
Dito, but invoked by Stage::recreate.
Definition pass.h:192
bool inspect() const final
Should the PassMan even consider this pass?
Definition pass.h:195
friend class FPPass
Definition pass.h:299
void run()
Run all registered passes on the whole World.
Definition pass.cpp:81
bool empty() const
Definition pass.h:199
void add(std::unique_ptr< Pass > &&pass)
Definition pass.h:220
PassMan(World &world, flags_t annex)
Definition pass.h:187
const auto & passes() const
Definition pass.h:200
bool fixed_point() const
Definition pass.h:201
All Passes that want to be registered in the PassMan must implement this interface.
Definition pass.h:96
virtual void enter()
Invoked just before Pass::rewriteing PassMan::curr_mut's body.
Definition pass.h:146
const Proxy * proxy(const Def *type, Defs ops, u32 tag=0)
Definition pass.h:154
virtual undo_t analyze(const Proxy *)
Definition pass.h:131
size_t index() const
Definition pass.h:112
const PassMan & man() const
Definition pass.h:111
virtual void * alloc()
Default constructor.
Definition pass.h:171
virtual const Def * rewrite(const Var *var)
Definition pass.h:120
virtual void dealloc(void *)
Destructor.
Definition pass.h:173
virtual const Def * rewrite(const Proxy *proxy)
Definition pass.h:121
virtual undo_t analyze(const Var *)
Definition pass.h:130
Pass(World &world, std::string name)
Definition pass.h:100
virtual void init(PassMan *)
Definition pass.cpp:30
virtual undo_t analyze(const Def *)
Definition pass.h:129
virtual void prepare()
Invoked once before entering the main rewrite loop.
Definition pass.h:149
virtual void * copy(const void *)
Copy constructor.
Definition pass.h:172
virtual bool fixed_point() const
Definition pass.h:136
Pass(World &world, flags_t annex)
Definition pass.h:102
virtual bool inspect() const =0
Should the PassMan even consider this pass?
PassMan & man()
Definition pass.h:110
virtual const Def * rewrite(const Def *def)
Definition pass.h:119
friend class PassMan
Definition pass.h:179
const Proxy * isa_proxy(const Def *def, u32 tag=0)
Check whether given def is a Proxy whose Proxy::pass matches this Pass's IPass::index.
Definition pass.h:156
const Proxy * as_proxy(const Def *def, u32 tag=0)
Definition pass.h:161
RWPass(World &world, std::string name)
Definition pass.h:308
bool inspect() const override
Should the PassMan even consider this pass?
Definition pass.h:313
M * curr_mut() const
Definition pass.h:320
RWPass(World &world, flags_t annex)
Definition pass.h:310
Common base for Phase and Pass.
Definition pass.h:26
World & world()
Definition pass.h:77
virtual std::unique_ptr< Stage > recreate()
Creates a new instance; needed by a fixed-point PhaseMan.
Definition pass.cpp:23
virtual std::unique_ptr< Stage > take_resolved()
The Stage to use instead; nullptr means elide.
Definition pass.h:45
std::string name_
Definition pass.h:89
virtual bool redirects() const
If true, Stage::create uses take_resolved().
Definition pass.h:44
virtual void apply(const App *)
Invoked if your Stage has additional args.
Definition pass.h:37
std::string_view name() const
Definition pass.h:80
virtual ~Stage()=default
static void hook(Flags2Stages &stages)
Definition pass.h:70
virtual void apply(Stage &)
Dito, but invoked by Stage::recreate.
Definition pass.h:38
Stage(World &world, std::string name)
Definition pass.h:30
Log & log() const
Definition pass.h:79
Driver & driver()
Definition pass.h:78
static std::unique_ptr< Stage > create(const Flags2Stages &stages, const Def *def)
Definition pass.h:50
flags_t annex() const
Definition pass.h:81
A variable introduced by a binder (mutable).
Definition def.h:714
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
const Proxy * proxy(const Def *type, Defs ops, u32 index, u32 tag)
Definition world.h:342
Definition ast.h:14
View< const Def * > Defs
Definition def.h:78
DefMap< const Def * > Def2Def
Definition def.h:77
Vector< const Def * > DefVec
Definition def.h:79
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
std::deque< std::unique_ptr< Pass > > Passes
Definition pass.h:16
absl::flat_hash_map< flags_t, std::function< std::unique_ptr< Stage >(World &)> > Flags2Stages
Maps an an axiom of a Stage to a function that creates one.
Definition plugin.h:25
GIDSet< const Def * > DefSet
Definition def.h:76
size_t undo_t
Definition pass.h:21
uint32_t u32
Definition types.h:27
void error(std::format_string< Args... > fmt, Args &&... args)
Wraps std::format to throw T with a formatted message.
Definition dbg.h:17
static constexpr undo_t No_Undo
Definition pass.h:22
Definition span.h:122
static consteval flags_t base()
Definition plugin.h:150