MimIR
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
eta_conv.h
Go to the documentation of this file.
1#pragma once
2
3#include <fe/assert.h>
4
5#include "mim/phase.h"
6
7#include "mim/util/gid.h"
8
9namespace mim {
10
11/// Combined η-normalization: folds η-reduction and η-expansion into a single, idempotent phase.
12/// A Lam should appear either **only** in callee position (Known) or not (Unknown).
13/// A Lam that occurs in an unknown position more than once (Unknown_N) or in both positions (Both) is η-expanded
14/// (`g f -> g (λx.f x)`); a genuine η-redex `λx.f x` whose `f` does **not** want to be expanded is η-reduced.
15///
16/// The analysis is **wrapper-transparent**: a use of a wrapper `λx.f x` is counted as a use of `f` at the same
17/// position/multiplicity instead of counting `f` as Known (the wrapper's callee).
18/// This makes `f`'s classification identical whether `f` is bare or wrapped, so the canonical η-form is a genuine
19/// fixed point - the phase does not fight itself and can share one big `compile.phases tt` fixed-point loop with
20/// BetaRed and mem.seo without oscillating.
21class EtaConv : public InplaceRWPhase {
22public:
24 : InplaceRWPhase(world, "EtaConv") {}
27
28private:
29 enum Lattice : u8 {
30 None = 0,
31 Known = 1,
32 Unknown_1 = 2,
33 Unknown_N = 3,
34 Both = 4,
35 };
36
37 static Lattice join(Lattice l1, Lattice l2) {
38 if (l1 == Unknown_1 && l2 == Unknown_1) return Unknown_N;
39 if (l1 == l2) return l1;
40 if (l1 == None) return l2;
41 if (l2 == None) return l1;
42 if (l1 == Both || l2 == Both) return Both;
43 if (l1 == Known && (l2 == Unknown_1 || l2 == Unknown_N)) return Both;
44 if (l2 == Known && (l1 == Unknown_1 || l1 == Unknown_N)) return Both;
45 if (l1 == Unknown_1 && l2 == Unknown_N) return Unknown_N;
46 if (l2 == Unknown_1 && l1 == Unknown_N) return Unknown_N;
47 fe::unreachable();
48 }
49
50 Lattice lattice(const Lam* lam) {
51 if (auto i = lam2lattice_.find(lam); i != lam2lattice_.end()) return i->second;
52 return None;
53 }
54
55 static bool eta_expand(Lattice l) { return l != Known && l != Unknown_1 && l != None; }
56 bool eta_expand(const Lam* lam) { return eta_expand(lattice(lam)); }
57
58 /// Should a wrapper `λx.f x` be kept (because `f` wants to be expanded) instead of reduced?
59 bool keep_wrapper(const Def* f) {
60 auto lam = f->isa<Lam>();
61 return lam && eta_expand(lattice(lam));
62 }
63
64 /// Is @p lam a wrapper that is already in the shape a fresh Lam::eta_expand would produce here?
65 /// That means: it belongs to this one occurrence alone and carries the canonical `tt` filter.
66 /// Only then may we keep it - re-creating it would hand out a fresh identity on every run, so this phase would
67 /// never reach a fixed point in place.
68 bool is_canonical_wrapper(const Lam* lam) const {
69 auto i = wrapper_uses_.find(lam);
70 return i != wrapper_uses_.end() && i->second == 1 && lam->filter() == lam->world().lit_tt();
71 }
72
73 void join(const Lam* lam, Lattice l) {
74 if (auto [i, ins] = lam2lattice_.emplace(lam, l); !ins) i->second = join(i->second, l);
75 }
76
77 bool analyze() final;
78 void analyze(const Def*);
79 void visit(const Def*, Lattice);
80
81 /// An annex or external must keep its shape: neither η-reduce nor η-expand a root.
82 const Def* rewrite_root(const Def* def) final { return rewrite_no_eta(def); }
83 const Def* rewrite(const Def*) final;
84 const Def* rewrite_imm_App(const App*) final;
85 const Def* rewrite_imm_Var(const Var*) final;
86 /// η-reduce wrappers but never η-expand - used for callee (Known) positions, where expansion must not happen
87 /// but a wrapper `λx.f x` should still collapse to `f` (just as the standalone EtaRed did everywhere).
88 const Def* rewrite_no_exp(const Def* old_def);
89 const Def* rewrite_no_eta(const Def* old_def) { return Rewriter::rewrite(old_def); }
90
91 DefSet analyzed_;
92 GIDMap<const Lam*, Lattice> lam2lattice_;
93 GIDMap<const Lam*, u32> wrapper_uses_; ///< How many occurrences does a wrapper `λx.f x` serve?
94};
95
96} // namespace mim
Base class for all Defs.
Definition def.h:273
EtaConv(World &world)
Definition eta_conv.h:23
const Def * rewrite_imm_Var(const Var *) final
Definition eta_conv.cpp:83
const Def * rewrite(const Def *) final
Definition eta_conv.cpp:37
const Def * rewrite_imm_App(const App *) final
Definition eta_conv.cpp:78
EtaConv(World &world, flags_t annex)
Definition eta_conv.h:25
bool analyze() final
Runs the optional pre-analysis on Phase::world, typically to a fixed point, before rewriting begins.
Definition eta_conv.cpp:5
const Def * rewrite_root(const Def *def) final
An annex or external must keep its shape: neither η-reduce nor η-expand a root.
Definition eta_conv.h:82
InplaceRWPhase(World &world, std::string name, Analysis *analysis=nullptr)
Definition phase.h:489
World & world()
Definition phase.h:77
flags_t annex() const
Definition phase.h:81
virtual const Def * rewrite(const Def *)
Definition rewrite.cpp:55
A variable introduced by a binder (mutable).
Definition def.h:825
The World represents the whole program and manages creation of MimIR nodes (Defs).
Definition world.h:40
Definition ast.h:16
u64 flags_t
Definition types.h:39
absl::flat_hash_map< K, V, GIDHash< K > > GIDMap
Definition gid.h:24
GIDSet< const Def * > DefSet
Definition def.h:89
uint8_t u8
Definition types.h:27
@ Lam
Definition def.h:122