MimIR 0.1
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
pass.h
Go to the documentation of this file.
1#pragma once
2
3#include <stack>
4#include <typeindex>
5
6#include "mim/world.h"
7
8namespace mim {
9
10class PassMan;
11/// @name Undo
12/// Used by FPPass::analyze to indicate where to backtrack to.
13///@{
14using undo_t = size_t;
15static constexpr undo_t No_Undo = std::numeric_limits<undo_t>::max();
16///@}
17
18/// All Pass%es that want to be registered in the PassMan must implement this interface.
19/// * Inherit from RWPass if your pass does **not** need state and a fixed-point iteration.
20/// * Inherit from FPPass if you **do** need state and a fixed-point.
21/// * If you do not rely on interaction between different Pass%es, consider using Phase instead.
22class Pass {
23public:
24 Pass(PassMan&, std::string_view name);
25 virtual ~Pass() = default;
26
27 /// @name Getters
28 ///@{
29 World& world();
30 PassMan& man() { return man_; }
31 const PassMan& man() const { return man_; }
32 std::string_view name() const { return name_; }
33 size_t index() const { return index_; }
34 ///@}
35
36 /// @name Rewrite Hook for the PassMan
37 /// Rewrites an *immutable* @p def within PassMan::curr_mut.
38 /// @returns the replacement.
39 ///@{
40 virtual const Def* rewrite(const Def* def) { return def; }
41 virtual const Def* rewrite(const Var* var) { return var; }
42 virtual const Def* rewrite(const Proxy* proxy) { return proxy; }
43 ///@}
44
45 /// @name Analyze Hook for the PassMan
46 /// Invoked after the PassMan has finished Pass::rewrite%ing PassMan::curr_mut to analyze the Def.
47 /// Will only be invoked if Pass::fixed_point() yields `true` - which will be the case for FPPass%es.
48 /// @returns mim::No_Undo or the state to roll back to.
49 ///@{
50 virtual undo_t analyze(const Def*) { return No_Undo; }
51 virtual undo_t analyze(const Var*) { return No_Undo; }
52 virtual undo_t analyze(const Proxy*) { return No_Undo; }
53 ///@}
54
55 /// @name Further Hooks for the PassMan
56 ///@{
57 virtual bool fixed_point() const { return false; }
58
59 /// Should the PassMan even consider this pass?
60 virtual bool inspect() const = 0;
61
62 /// Invoked just before Pass::rewrite%ing PassMan::curr_mut's body.
63 /// @note This is invoked when seeing the *inside* of a mutable the first time.
64 /// This is often too late, as you usually want to do something when you see a mutable the first time from the
65 /// *outside*. This means that this PassMan::curr_mut has already been encountered elsewhere. Otherwise, we wouldn't
66 /// have seen PassMan::curr_mut to begin with (unless it is Def::is_external).
67 virtual void enter() {}
68
69 /// Invoked **once** before entering the main rewrite loop.
70 virtual void prepare() {}
71 ///@}
72
73 /// @name proxy
74 ///@{
75 const Proxy* proxy(const Def* type, Defs ops, u32 tag = 0) { return world().proxy(type, index(), tag, ops); }
76 /// Check whether given @p def is a Proxy whose Proxy::pass matches this Pass's @p IPass::index.
77 const Proxy* isa_proxy(const Def* def, u32 tag = 0) {
78 if (auto proxy = def->isa<Proxy>(); proxy != nullptr && proxy->pass() == index() && proxy->tag() == tag)
79 return proxy;
80 return nullptr;
81 }
82 const Proxy* as_proxy(const Def* def, u32 tag = 0) {
83 auto proxy = def->as<Proxy>();
84 assert_unused(proxy->pass() == index() && proxy->tag() == tag);
85 return proxy;
86 }
87 ///@}
88
89private:
90 /// @name Memory Management
91 ///@{
92 virtual void* alloc() { return nullptr; } ///< Default constructor.
93 virtual void* copy(const void*) { return nullptr; } ///< Copy constructor.
94 virtual void dealloc(void*) {} ///< Destructor.
95 ///@}
96
97 PassMan& man_;
98 std::string name_;
99 size_t index_;
100
101 friend class PassMan;
102};
103
104/// An optimizer that combines several optimizations in an optimal way.
105/// This is loosely based upon:
106/// "Composing dataflow analyses and transformations" by Lerner, Grove, Chambers.
107class PassMan {
108public:
110 : world_(world) {}
111
112 /// @name Getters
113 ///@{
114 World& world() const { return world_; }
115 const auto& passes() const { return passes_; }
116 bool fixed_point() const { return fixed_point_; }
117 Def* curr_mut() const { return curr_mut_; }
118 ///@}
119
120 /// @name Create and run Passes
121 ///@{
122 void run(); ///< Run all registered passes on the whole World.
123
124 /// Add a pass to this PassMan.
125 /// If a pass of the same class has been added already, returns the earlier added instance.
126 template<class P, class... Args>
127 P* add(Args&&... args) {
128 auto key = std::type_index(typeid(P));
129 if (auto it = registry_.find(key); it != registry_.end()) return static_cast<P*>(it->second);
130 auto p = std::make_unique<P>(*this, std::forward<Args>(args)...);
131 auto res = p.get();
132 fixed_point_ |= res->fixed_point();
133 passes_.emplace_back(std::move(p));
134 registry_.emplace(key, res);
135 return res;
136 }
137
138 /// Runs a single Pass.
139 template<class P, class... Args>
140 static void run(World& world, Args&&... args) {
141 PassMan man(world);
142 man.add<P>(std::forward<Args>(args)...);
143 man.run();
144 }
145 ///@}
146
147private:
148 /// @name State
149 ///@{
150 struct State {
151 State() = default;
152 State(const State&) = delete;
153 State(State&&) = delete;
154 State& operator=(State) = delete;
155 State(size_t num)
156 : data(num) {}
157
158 Def* curr_mut = nullptr;
159 DefVec old_ops;
160 std::stack<Def*> stack;
161 MutMap<undo_t> mut2visit;
162 Vector<void*> data;
163 Def2Def old2new;
164 DefSet analyzed;
165 };
166
167 void push_state();
168 void pop_states(undo_t undo);
169 State& curr_state() {
170 assert(!states_.empty());
171 return states_.back();
172 }
173 const State& curr_state() const {
174 assert(!states_.empty());
175 return states_.back();
176 }
177 undo_t curr_undo() const { return states_.size() - 1; }
178 ///@}
179
180 /// @name rewriting
181 ///@{
182 const Def* rewrite(const Def*);
183
184 const Def* map(const Def* old_def, const Def* new_def) {
185 curr_state().old2new[old_def] = new_def;
186 curr_state().old2new.emplace(new_def, new_def);
187 return new_def;
188 }
189
190 std::optional<const Def*> lookup(const Def* old_def) {
191 for (auto& state : states_ | std::ranges::views::reverse)
192 if (auto i = state.old2new.find(old_def); i != state.old2new.end()) return i->second;
193 return {};
194 }
195 ///@}
196
197 /// @name analyze
198 ///@{
199 undo_t analyze(const Def*);
200 bool analyzed(const Def* def) {
201 for (auto& state : states_ | std::ranges::views::reverse)
202 if (state.analyzed.contains(def)) return true;
203 curr_state().analyzed.emplace(def);
204 return false;
205 }
206 ///@}
207
208 World& world_;
209 std::deque<std::unique_ptr<Pass>> passes_;
210 absl::flat_hash_map<std::type_index, Pass*> registry_;
211 std::deque<State> states_;
212 Def* curr_mut_ = nullptr;
213 bool fixed_point_ = false;
214 bool proxy_ = false;
215
216 template<class P, class M>
217 friend class FPPass;
218};
219
220/// Inherit from this class using [CRTP](https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern),
221/// if your Pass does **not** need state and a fixed-point iteration.
222/// If you a are only interested in specific mutables, you can pass this to @p M.
223template<class P, class M = Def>
224class RWPass : public Pass {
225public:
226 RWPass(PassMan& man, std::string_view name)
227 : Pass(man, name) {}
228
229 bool inspect() const override {
230 if constexpr (std::is_same<M, Def>::value)
231 return man().curr_mut();
232 else
233 return man().curr_mut()->template isa<M>();
234 }
235
236 M* curr_mut() const {
237 if constexpr (std::is_same<M, Def>::value)
238 return man().curr_mut();
239 else
240 return man().curr_mut()->template as<M>();
241 }
242};
243
244/// Inherit from this class using [CRTP](https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern),
245/// if you **do** need a Pass with a state and a fixed-point.
246template<class P, class M = Def>
247class FPPass : public RWPass<P, M> {
248public:
250 using Data = std::tuple<>; ///< Default.
251
252 FPPass(PassMan& man, std::string_view name)
253 : Super(man, name) {}
254
255 bool fixed_point() const override { return true; }
256
257protected:
258 /// @name State-related Getters
259 ///@{
260 const auto& states() const { return Super::man().states_; }
261 auto& states() { return Super::man().states_; }
262 auto& data() {
263 assert(!states().empty());
264 return *static_cast<typename P::Data*>(states().back().data[Super::index()]);
265 }
266 template<size_t I>
267 auto& data() {
268 return std::get<I>(data());
269 }
270 /// Use this for your convenience if `P::Data` is a map.
271 template<class K>
272 auto& data(const K& key) {
273 return data()[key];
274 }
275 /// Use this for your convenience if `P::Data<I>` is a map.
276 template<size_t I, class K>
277 auto& data(const K& key) {
278 return data<I>()[key];
279 }
280 ///@}
281
282 /// @name undo
283 ///@{
284 undo_t curr_undo() const { return Super::man().curr_undo(); } ///< Current undo point.
285
286 /// Retrieves the point to backtrack to just **before** @p mut was seen the very first time.
287 undo_t undo_visit(Def* mut) const {
288 const auto& mut2visit = Super::man().curr_state().mut2visit;
289 if (auto i = mut2visit.find(mut); i != mut2visit.end()) return i->second;
290 return No_Undo;
291 }
292
293 /// Retrieves the point to backtrack to just **before** rewriting @p mut%'s body.
294 undo_t undo_enter(Def* mut) const {
295 for (auto i = states().size(); i-- != 0;)
296 if (states()[i].curr_mut == mut) return i;
297 return No_Undo;
298 }
299 ///@}
300
301private:
302 /// @name Memory Management for State
303 ///@{
304 void* alloc() override { return new typename P::Data(); }
305 void* copy(const void* p) override { return new typename P::Data(*static_cast<const typename P::Data*>(p)); }
306 void dealloc(void* state) override { delete static_cast<typename P::Data*>(state); }
307 ///@}
308};
309
310inline World& Pass::world() { return man().world(); }
311
312} // namespace mim
Base class for all Defs.
Definition def.h:216
void dealloc(void *state) override
Destructor.
Definition pass.h:306
auto & data()
Definition pass.h:267
auto & data()
Definition pass.h:262
RWPass< P, M > Super
Definition pass.h:249
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:287
auto & data(const K &key)
Use this for your convenience if P::Data is a map.
Definition pass.h:272
FPPass(PassMan &man, std::string_view name)
Definition pass.h:252
void * copy(const void *p) override
Copy constructor.
Definition pass.h:305
undo_t curr_undo() const
Current undo point.
Definition pass.h:284
const auto & states() const
Definition pass.h:260
bool fixed_point() const override
Definition pass.h:255
std::tuple<> Data
Default.
Definition pass.h:250
void * alloc() override
Default constructor.
Definition pass.h:304
auto & data(const K &key)
Use this for your convenience if P::Data<I> is a map.
Definition pass.h:277
auto & states()
Definition pass.h:261
undo_t undo_enter(Def *mut) const
Retrieves the point to backtrack to just before rewriting mut's body.
Definition pass.h:294
An optimizer that combines several optimizations in an optimal way.
Definition pass.h:107
Def * curr_mut() const
Definition pass.h:117
friend class FPPass
Definition pass.h:217
void run()
Run all registered passes on the whole World.
Definition pass.cpp:44
static void run(World &world, Args &&... args)
Runs a single Pass.
Definition pass.h:140
PassMan(World &world)
Definition pass.h:109
World & world() const
Definition pass.h:114
const auto & passes() const
Definition pass.h:115
P * add(Args &&... args)
Add a pass to this PassMan.
Definition pass.h:127
bool fixed_point() const
Definition pass.h:116
virtual void enter()
Invoked just before Pass::rewriteing PassMan::curr_mut's body.
Definition pass.h:67
World & world()
Definition pass.h:310
const Proxy * proxy(const Def *type, Defs ops, u32 tag=0)
Definition pass.h:75
virtual undo_t analyze(const Proxy *)
Definition pass.h:52
size_t index() const
Definition pass.h:33
const PassMan & man() const
Definition pass.h:31
Pass(PassMan &, std::string_view name)
Definition pass.cpp:10
virtual void * alloc()
Default constructor.
Definition pass.h:92
virtual const Def * rewrite(const Var *var)
Definition pass.h:41
virtual void dealloc(void *)
Destructor.
Definition pass.h:94
virtual const Def * rewrite(const Proxy *proxy)
Definition pass.h:42
virtual undo_t analyze(const Var *)
Definition pass.h:51
virtual ~Pass()=default
virtual undo_t analyze(const Def *)
Definition pass.h:50
virtual void prepare()
Invoked once before entering the main rewrite loop.
Definition pass.h:70
virtual void * copy(const void *)
Copy constructor.
Definition pass.h:93
virtual bool fixed_point() const
Definition pass.h:57
virtual bool inspect() const =0
Should the PassMan even consider this pass?
PassMan & man()
Definition pass.h:30
virtual const Def * rewrite(const Def *def)
Definition pass.h:40
std::string_view name() const
Definition pass.h:32
friend class PassMan
Definition pass.h:101
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:77
const Proxy * as_proxy(const Def *def, u32 tag=0)
Definition pass.h:82
RWPass(PassMan &man, std::string_view name)
Definition pass.h:226
bool inspect() const override
Should the PassMan even consider this pass?
Definition pass.h:229
M * curr_mut() const
Definition pass.h:236
The World represents the whole program and manages creation of MimIR nodes (Defs).
Definition world.h:34
const Proxy * proxy(const Def *type, u32 index, u32 tag, Defs ops)
Definition world.h:225
#define M(S, D)
Definition ast.h:14
View< const Def * > Defs
Definition def.h:51
DefMap< const Def * > Def2Def
Definition def.h:50
Vector< const Def * > DefVec
Definition def.h:52
GIDSet< const Def * > DefSet
Definition def.h:49
size_t undo_t
Definition pass.h:14
uint32_t u32
Definition types.h:34
static constexpr undo_t No_Undo
Definition pass.h:15