MimIR
0.5-dev
MimIR is my Intermediate Representation
Toggle main menu visibility
Loading...
Searching...
No Matches
clos_conv.h
Go to the documentation of this file.
1
#pragma once
2
3
#include <queue>
4
5
#include <
mim/phase.h
>
6
7
#include <
mim/plug/mem/mem.h
>
8
9
#include "
mim/plug/clos/clos.h
"
10
11
namespace
mim::plug::clos::phase
{
12
13
/// Computes, on demand and with memoization, the free Def%s that a Lam must capture in its closure environment.
14
///
15
/// A referenced nested mutable does not become a free def itself; instead it contributes *its* free defs, since it
16
/// will be closure-converted too.
17
/// This makes the free-def sets of (mutually) recursive Lam%s inter-dependent, so they are solved to a fixed point
18
/// over a little dependency graph: an edge `pred → node` means `node` must include all of `pred`'s free defs.
19
///
20
/// Mutables are only treated as free defs directly if annotated with clos::anno::free_bb / clos::anno::fstclass_bb;
21
/// immutables carrying free vars are broken down to their relevant leaves.
22
class
FreeDefAna
{
23
public
:
24
FreeDefAna
(
World
& world)
25
: world_(world) {}
26
27
/// Returns the free defs @p lam has to capture; see the class description.
28
const
DefSet
&
run
(
Lam
* lam);
29
30
private
:
31
/// A memoized free-def set together with its dependency edges in the fixed-point graph.
32
struct
Node
{
33
Def
* mut;
34
DefSet
fvs;
35
fe::Vector<Node*> preds;
///< Nodes whose free defs must flow into this one.
36
fe::Vector<Node*> succs;
///< Reverse of preds; used to re-enqueue dependents when fvs grow.
37
unsigned
pass = 0;
///< 0 = uninitialized; otherwise the pass that last touched this node.
38
39
auto
add_fvs(
const
Def
* def) {
40
assert(!
Axm::isa<mem::M>
(def->
type
()));
41
return
fvs.emplace(def);
42
}
43
};
44
45
using
NodeQueue = std::queue<Node*>;
46
47
bool
is_bot(
Node
* node)
const
{
return
node->pass == 0; }
///< Not yet initialized?
48
bool
is_done(
Node
* node)
const
{
return
!is_bot(node) && node->pass < cur_pass_; }
///< Settled in an earlier pass?
49
void
mark(Node* node) { node->pass = cur_pass_; }
50
51
/// Classifies a free def of @p node, either adding it as a captured fv or spawning/linking a predecessor Node.
52
/// @p spawned_pred is set if this created a fresh (uninitialized) predecessor Node.
53
void
classify(Node* node,
const
Def* fd,
bool
& spawned_pred, NodeQueue& worklist);
54
55
std::pair<Node*, bool> build_node(Def* mut, NodeQueue& worklist);
56
void
propagate(NodeQueue& worklist);
57
58
World& world() {
return
world_; }
59
60
World& world_;
61
unsigned
cur_pass_ = 1;
62
DefMap<std::unique_ptr<Node>
> lam2node_;
63
};
64
65
/// Performs *typed closure conversion*, rebuilding the old world into a new one.
66
/// This is based on the [Simply Typed Closure Conversion](https://dl.acm.org/doi/abs/10.1145/237721.237791).
67
/// Closures are represented using tuples: `[Env: *, Cn [Env, Args..], Env]`.
68
/// In general only *continuations* are converted; different kinds of Lam%s are treated differently:
69
/// - *returning continuations* ("functions"), *join-points* and *branches* are fully closure converted.
70
/// - *return continuations* are not closure converted.
71
/// - *first-class continuations* get a "dummy" closure; they still have free variables.
72
///
73
/// This phase relies on ClosConvPrep to introduce annotations for these cases.
74
///
75
/// A converted Lam is first stubbed (ClosConv::make_stub) and packed at each use site, while its body is
76
/// enqueued and rewritten later (ClosConv::rewrite_body) in its own fresh substitution scope.
77
/// This isolation is essential: inside a closure's body its free defs are replaced by projections of *its own*
78
/// environment, and those substitutions must not leak into the enclosing scope where the closure was packed.
79
///
80
/// @note Direct-style Def%s are not rewritten, which can be a problem for certain Axm%s such as
81
/// `ax : (B : *, int → B) → (int → B)`.
82
/// There is also no machinery for free variables in a Lam's type, which may break polymorphic functions.
83
class
ClosConv
:
public
RWPhase
{
84
public
:
85
using
RWPhase::RWPhase
;
86
87
private
:
88
/// A closure-converted Lam: the code part @p fn capturing the free defs @p fvs of @p old_fn.
89
struct
Stub {
90
Lam
* old_fn;
91
/// The captured free defs.
92
/// Do not recover them from the env tuple's ops: World::tuple may normalize it (η-reduction back to the
93
/// underlying def, unary tuple, uniform Pack).
94
DefVec
fvs;
95
Lam
* fn;
96
};
97
98
/// @name Rewrite hooks
99
///@{
100
void
finalize
()
final
;
101
const
Def
*
rewrite_imm_Pi
(
const
Pi
*)
final
;
102
const
Def
*
rewrite_mut_Pi
(
Pi
*)
final
;
103
const
Def
*
rewrite_mut_Lam
(
Lam
*)
final
;
104
const
Def
*
rewrite_imm_App
(
const
App
*)
final
;
105
const
Def
*
rewrite_imm_Extract
(
const
Extract
*)
final
;
106
///@}
107
108
/// Handles the `clos.anno.{returning,free_bb,fstclass_bb}` wrappers; returns `nullptr` if @p a is none of these.
109
const
Def
* rewrite_anno(
Axm::IsA<anno, App>
a);
110
111
Stub make_stub(
const
DefSet
& fvs,
Lam
* old_lam);
112
Stub make_stub(
Lam
* old_lam);
113
void
rewrite_body(
const
Stub&);
114
115
/// Builds the closure type for the `Cn` @p pi; with @p env_type, the bare code `Cn` instead of the Sigma.
116
const
Def
* clos_type_of(
const
Pi
* pi,
const
Def
* env_type =
nullptr
);
117
/// Rewrites a return continuation's type: stays a plain `Cn`, but its domains are closure-converted.
118
const
Pi
* rewrite_ret_cn(
const
Pi
*);
119
/// A telescope's Cn keeps its shape; only its return continuation's domain is rewritten as rewrite_ret_cn() does.
120
const
Pi
* rewrite_telescope_cn(
const
Pi
*);
121
122
FreeDefAna
fva_{
old_world
()};
123
DefMap<Stub>
closures_;
///< old_fn *and* new fn ↦ Stub.
124
125
/// Closure types must be rewritten uniformly across the whole module.
126
/// They must not depend on defs living inside the scope of a continuation.
127
Def2Def
glob_muts_;
128
129
std::queue<Lam*> body_worklist_;
///< New fns whose bodies are yet to be rewritten.
130
};
131
132
}
// namespace mim::plug::clos::phase
mim::App
Definition
lam.h:215
mim::Axm::IsA
Definition
axm.h:74
mim::Axm::isa
static auto isa(const Def *def)
Definition
axm.h:112
mim::Def
Base class for all Defs.
Definition
def.h:289
mim::Def::type
const Def * type() const noexcept
Yields the "raw" type of this Def (maybe nullptr).
Definition
def.h:1119
mim::Extract
Extracts from a Sigma or Array-typed Extract::tuple the element at position Extract::index.
Definition
tuple.h:360
mim::Lam
A function.
Definition
lam.h:91
mim::Pi
A dependent function type.
Definition
lam.h:14
mim::RWPhase::RWPhase
RWPhase(World &world, std::string name, Analysis *analysis=nullptr)
Definition
phase.h:334
mim::RWPhase::old_world
World & old_world()
Get old Defs from here.
Definition
phase.h:378
mim::World
The World represents the whole program and manages creation of MimIR nodes (Defs).
Definition
world.h:43
mim::plug::clos::phase::ClosConv
Performs typed closure conversion, rebuilding the old world into a new one.
Definition
clos_conv.h:83
mim::plug::clos::phase::ClosConv::rewrite_imm_Extract
const Def * rewrite_imm_Extract(const Extract *) final
Definition
clos_conv.cpp:203
mim::plug::clos::phase::ClosConv::rewrite_mut_Pi
const Def * rewrite_mut_Pi(Pi *) final
Definition
clos_conv.cpp:143
mim::plug::clos::phase::ClosConv::rewrite_imm_App
const Def * rewrite_imm_App(const App *) final
Definition
clos_conv.cpp:162
mim::plug::clos::phase::ClosConv::RWPhase
RWPhase(World &world, std::string name, Analysis *analysis=nullptr)
Definition
phase.h:334
mim::plug::clos::phase::ClosConv::rewrite_imm_Pi
const Def * rewrite_imm_Pi(const Pi *) final
Definition
clos_conv.cpp:137
mim::plug::clos::phase::ClosConv::finalize
void finalize() final
Run after all roots have been walked but still before the two worlds are swapped.
Definition
clos_conv.cpp:120
mim::plug::clos::phase::ClosConv::rewrite_mut_Lam
const Def * rewrite_mut_Lam(Lam *) final
Definition
clos_conv.cpp:148
mim::plug::clos::phase::FreeDefAna
Computes, on demand and with memoization, the free Defs that a Lam must capture in its closure enviro...
Definition
clos_conv.h:22
mim::plug::clos::phase::FreeDefAna::FreeDefAna
FreeDefAna(World &world)
Definition
clos_conv.h:24
mim::plug::clos::phase::FreeDefAna::run
const DefSet & run(Lam *lam)
Returns the free defs lam has to capture; see the class description.
Definition
clos_conv.cpp:106
clos.h
mem.h
mim::plug::clos::phase
Definition
branch_clos_elim.h:5
mim::Def2Def
DefMap< const Def * > Def2Def
Definition
def.h:95
mim::DefMap
GIDMap< const Def *, To > DefMap
Definition
def.h:93
mim::DefVec
fe::Vector< const Def * > DefVec
Definition
def.h:98
mim::DefSet
GIDSet< const Def * > DefSet
Definition
def.h:94
mim::Node
Node
Definition
def.h:123
phase.h
include
mim
plug
clos
phase
clos_conv.h
Generated by
1.18.0