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