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