MimIR 0.1
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
clos_conv.h
Go to the documentation of this file.
1#pragma once
2
3#include <queue>
4#include <vector>
5
6#include "mim/phase/phase.h"
7
9#include "mim/plug/mem/mem.h"
10
11namespace mim::plug::clos {
12
13/// Transitively compute free Def's on demand.
14/// This is used internally by ClosConv.
16public:
18 : world_(world)
19 , cur_pass_id(1)
20 , lam2nodes_() {}
21
22 /// FreeDefAna::run will compute free defs (FD) that appear in @p lam%s body.
23 /// Mutable Def%s are only considered free if they are annotated with Clos::freeBB or
24 /// Clos::fstclassBB.
25 /// Otherwise, we add a mut's free defs in order to build a closure for it.
26 /// Immutables containing mutables are broken up if necessary.
27 DefSet& run(Lam* lam);
28
29private:
30 /// @name analysis graph
31 /// @{
32 struct Node;
33 using NodeQueue = std::queue<Node*>;
34 using Nodes = std::vector<Node*>;
35
36 /// In order to avoid recomputing FDs sets, sets are computed for the subgraph reachable from mut and memorized.
37 /// `pass_id` determines if the node has been initialized.
38 struct Node {
39 Def* mut;
40 DefSet fvs;
41 Nodes preds;
42 Nodes succs;
43 unsigned pass_id; //
44
45 auto add_fvs(const Def* def) {
46 assert(!match<mem::M>(def->type()));
47 return fvs.emplace(def);
48 }
49 };
50 /// @}
51
52 /// Node is not initialized?
53 bool is_bot(Node* node) { return node->pass_id == 0; }
54
55 /// FD set for node is already present?
56 bool is_done(Node* node) { return !is_bot(node) && node->pass_id < cur_pass_id; }
57
58 /// Marks a node as done.
59 void mark(Node* node) { node->pass_id = cur_pass_id; }
60
61 /// Split a free Def. This may create more Nodes as more reachable nodes are discovered.
62 void split_fd(Node* node, const Def* fv, bool& is_init, NodeQueue& worklist);
63
64 std::pair<Node*, bool> build_node(Def* mut, NodeQueue& worklist);
65 void run(NodeQueue& worklist);
66
67 World& world() { return world_; }
68
69 World& world_;
70 unsigned cur_pass_id;
72};
73
74/// Performs *typed closure conversion*.
75/// This is based on the [Simply Typed Closure Conversion](https://dl.acm.org/doi/abs/10.1145/237721.237791).
76/// Closures are represented using tuples: `[Env: *, Cn [Env, Args..], Env]`.
77/// In general only *continuations* are converted.
78/// Different kind of Lam%s may be rewritten differently:
79/// - *returning continuations* ("functions"), *join-points* and *branches* are fully closure converted.
80/// - *return continuations* are not closure converted.
81/// - *first-class continuations* get a "dummy" closure, they still have free variables.
82///
83/// This pass relies on ClosConvPrep to introduce annotations for these cases.
84///
85/// Note: Since direct-style Def%s are not rewritten, this can lead to problems with certain Axiom%s:
86/// `ax : (B : *, int -> B) -> (int -> B)` won't be converted, possible arguments may.
87/// Further, there is no machinery to handle free variables in a Lam%s type; this may lead to
88/// problems with polymorphic functions.
89class ClosConv : public Phase {
90public:
92 : Phase(world, "clos_conv", true)
93 , fva_(world) {}
94
95 void start() override;
96
97private:
98 /// @name closure stubs
99 /// @{
100 struct Stub {
101 Lam* old_fn;
102 size_t num_fvs;
103 const Def* env;
104 Lam* fn;
105 };
106
107 Stub make_stub(const DefSet& fvs, Lam* lam, Def2Def& subst);
108 Stub make_stub(Lam* lam, Def2Def& subst);
109 /// @}
110
111 /// @name Recursively rewrite Def%s.
112 /// @{
113 void rewrite_body(Lam* lam, Def2Def& subst);
114 const Def* rewrite(const Def* old_def, Def2Def& subst);
115 Def* rewrite_mut(Def* mut, const Def* new_type, Def2Def& subst);
116 const Pi* rewrite_type_cn(const Pi*, Def2Def& subst);
117 const Def* type_clos(const Pi* pi, Def2Def& subst, const Def* ent_type = nullptr);
118 /// @}
119
120 FreeDefAna fva_;
121 DefMap<Stub> closures_;
122
123 // Muts that must be re rewritten uniformly across the whole module:
124 // Currently, this includes globals and closure types (for typechecking to go through).
125 // Such muts must not depend on defs that live inside the scope of a continuation!
126 Def2Def glob_muts_;
127
128 std::queue<const Def*> worklist_;
129};
130
131}; // namespace mim::plug::clos
Base class for all Defs.
Definition def.h:212
Ref type() const noexcept
Yields the raw type of this Def, i.e. maybe nullptr.
Definition def.h:241
A function.
Definition lam.h:105
As opposed to a Pass, a Phase does one thing at a time and does not mix with other Phases.
Definition phase.h:17
World & world()
Definition phase.h:27
A dependent function type.
Definition lam.h:11
The World represents the whole program and manages creation of MimIR nodes (Defs).
Definition world.h:33
Performs typed closure conversion.
Definition clos_conv.h:89
void start() override
Actual entry.
ClosConv(World &world)
Definition clos_conv.h:91
Transitively compute free Def's on demand.
Definition clos_conv.h:15
DefSet & run(Lam *lam)
FreeDefAna::run will compute free defs (FD) that appear in lams body.
The clos Plugin
Definition clos.h:7
DefMap< const Def * > Def2Def
Definition def.h:60
auto match(Ref def)
Definition axiom.h:112
GIDMap< const Def *, To > DefMap
Definition def.h:58
GIDSet< const Def * > DefSet
Definition def.h:59