MimIR
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
emitter.h
Go to the documentation of this file.
1#pragma once
2
3#include "mim/def.h"
4#include "mim/phase.h"
5#include "mim/schedule.h"
6#include "mim/world.h"
7
8namespace mim {
9
10template<class Value, class Type, class BB, class Child>
11class Emitter : public NestPhase<Lam> {
12private:
13 constexpr const Child& child() const { return *static_cast<const Child*>(this); }
14 constexpr Child& child() { return *static_cast<Child*>(this); }
15
16 /// Internal wrapper for Emitter::emit that schedules @p def and invokes `child().emit_bb`.
17 Value emit_(const Def* def) {
18 auto place = scheduler_.smart(curr_lam_, def);
19 auto& bb = lam2bb_[place->mut()->template as<Lam>()];
20 return child().emit_bb(bb, def);
21 }
22
23public:
24 fe::Tab tab = fe::Tab::spaces();
25
26protected:
27 Emitter(World& world, std::string name, std::ostream& ostream, bool schedule = false)
28 : NestPhase<Lam>(world, std::move(name), false, schedule)
29 , ostream_(ostream) {}
30
31 virtual bool direct_style() { return false; }
32
33 std::ostream& ostream() const { return ostream_; }
34
35 /// Recursively emits code.
36 /// `mem`-typed @p def%s return sth that is `!child().is_valid(value)`.
37 /// This variant asserts in this case.
38 Value emit(const Def* def) {
39 auto res = emit_unsafe(def);
40 assert(child().is_valid(res));
41 return res;
42 }
43
44 /// As above but returning `!child().is_valid(value)` is permitted.
45 Value emit_unsafe(const Def* def) {
46 if (auto i = globals_.find(def); i != globals_.end()) return i->second;
47 if (auto i = locals_.find(def); i != locals_.end()) return i->second;
48
49 auto val = emit_(def);
50 return locals_[def] = val;
51 }
52
53 void visit(const Nest& nest) override {
54 if (!root()->is_set()) {
55 child().emit_imported(root());
56 return;
57 }
58
59 schedule_ = Scheduler::schedule(nest); // cached; Child::finalize needs the very same schedule
60 const auto& muts = schedule_;
61
62 // make sure that we don't need to rehash later on
63 for (auto mut : muts)
64 if (auto lam = mut->isa<Lam>()) lam2bb_.try_emplace(lam, BB());
65 auto old_size = lam2bb_.size();
66
67 if (!child().direct_style()) assert(root()->ret_var());
68
69 auto fct = child().prepare();
70
71 Scheduler new_scheduler(nest);
72 swap(scheduler_, new_scheduler);
73
74 for (auto mut : muts) {
75 if (auto lam = mut->isa<Lam>()) {
76 curr_lam_ = lam;
77 if (!child().direct_style()) assert(lam == root() || Lam::isa_basicblock(lam));
78 child().emit_epilogue(lam);
79 }
80 }
81
82 child().finalize();
83 locals_.clear();
84 assert_unused(lam2bb_.size() == old_size && "really make sure we didn't trigger a rehash");
85 // A BB never crosses a function boundary: Nest::contains is `def->has_free_vars_in(vars())`,
86 // so a *closed* Lam is never a member of another Lam's Nest - and it cannot belong to two Nests either,
87 // since a Lam free in the vars of two closed Lams would make the outer one open.
88 // Every `BB&` handed out by emit_ died with the calls above, so clearing here is safe.
89 // Without it, Child::finalize re-walks the BBs of all previously emitted functions - O(n²) in program size.
90 lam2bb_.clear();
91 }
92
93 /// The Scheduler::schedule of the function currently being emitted; see Emitter::visit.
94 const Scheduler::Schedule& schedule() const { return schedule_; }
95
96 Lam* curr_lam_ = nullptr;
97 std::ostream& ostream_;
104};
105
106} // namespace mim
Lam * root() const
Definition phase.h:624
Base class for all Defs.
Definition def.h:273
const Scheduler::Schedule & schedule() const
Definition emitter.h:94
Value emit(const Def *def)
Recursively emits code.
Definition emitter.h:38
virtual bool direct_style()
Definition emitter.h:31
Emitter(World &world, std::string name, std::ostream &ostream, bool schedule=false)
Definition emitter.h:27
Scheduler scheduler_
Definition emitter.h:98
Lam * curr_lam_
Definition emitter.h:96
LamMap< BB > lam2bb_
Definition emitter.h:103
void visit(const Nest &nest) override
Definition emitter.h:53
A function.
Definition lam.h:113
static const Lam * isa_basicblock(const Def *d)
Definition lam.h:145
const Nest & nest() const
Definition phase.h:642
NestPhase(World &world, std::string name, bool elide_empty, bool schedule=false)
Definition phase.h:637
Builds a nesting tree for all mutables/binders.
Definition nest.h:31
std::string_view name() const
Definition phase.h:80
World & world()
Definition phase.h:77
const Nest::Node * smart(Def *curr, const Def *)
Definition schedule.cpp:75
fe::Vector< Def * > Schedule
Definition schedule.h:79
static Schedule schedule(const Nest &)
Definition schedule.cpp:121
The World represents the whole program and manages creation of MimIR nodes (Defs).
Definition world.h:40
Definition ast.h:16
GIDMap< const Def *, To > DefMap
Definition def.h:88
GIDMap< Lam *, To > LamMap
Definition lam.h:219