MimIR
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
seo.h
Go to the documentation of this file.
1#pragma once
2
3#include <fe/worklist.h>
4
5#include <mim/def.h>
6#include <mim/phase.h>
7#include <mim/tuple.h>
8
9#include <mim/util/gid.h>
10
11namespace mim::plug::mem::phase {
12
13/// Symbolic Expression Optimization.
14/// Based on [SSA Translation Is an Abstract Interpretation](https://dl.acm.org/doi/10.1145/3571258).
15/// In addition:
16/// * propagates whole expressions - not just constants
17/// * optimistically combines φs/Vars that are already present with those being constructed through SSA translation
18/// * since abstract domain is a MimIR expression, stack slots themselves can be propagated etc.
19/// * contifies for free: a continuation parameter that all call sites agree on is propagated away
20///
21/// Additional papers worth reading:
22/// * [Constant propagation with conditional branches](https://dl.acm.org/doi/pdf/10.1145/103135.103136)
23/// * [Detecting equality of variables in programs](https://dl.acm.org/doi/10.1145/73560.73561)
24/// * [Combining analyses, combining optimizations](https://dl.acm.org/doi/pdf/10.1145/201059.201061)
25/// * [Simple and efficient construction of static single assignment
26/// form](https://dl.acm.org/doi/10.1007/978-3-642-37051-9_6)
27/// * [Contification using dominators](https://dl.acm.org/doi/10.1145/507635.507639)
28///
29/// Due to MimIR's sea of node structure a number of other optimizations kick in such as arithmetic simplifications and
30/// code motion.
31///
32/// Lattice per Lam::var:
33/// ```
34/// ⊤ ← Keep as is
35/// |
36/// Bundle ← Vars that (horizontally) behave the same build a single congruence class
37/// |
38/// Expr ← Whole expression is propagated (vertically) through var
39/// |
40/// ⊥
41/// ```
42/// A var that has reached ⊤ for propagation but still awaits GVN bundling is marked with a dedicated Proxy sentinel.
43class SEO : public RWPhase {
44private:
45 using Super = mim::RWPhase;
46
47 class Analysis : public mim::Analysis {
48 public:
49 using Super = mim::Analysis;
50
52 : mim::Analysis(world, "SEO::Analyzer") {}
53
54 void reset() final;
55
56 const LamSet& unknowns() const { return unknowns_; }
57
58 // SSA
59 const auto& slots() const { return slots_; }
60 const Def* lam2sloxy2val(Lam* lam, const Def* sloxy);
61 /// Can *every* recorded call site of @p lam supply a value for @p sloxy?
62 /// Only then may build_args() find an argument for a phi threaded through @p lam.
63 bool can_supply(Lam* lam, const Def* sloxy);
64
65 private:
66 // SCCP
67 const Proxy* mk_sccp_top(const Def* var);
68 const Def* sccp_join(Lam*, const Def*, const Def*);
69
70 /// Applies @p known to @p abstr_targs (one per tvar): propagates phis, runs SCCP + GVN, and sets the vars.
71 const Def* apply_known(Lam* known, Defs abstr_targs);
72 /// An App that merely records "@p callee applied to these abstract args"; never emitted code.
73 const Def* abstract_app(const Def* callee, const Def* arg);
74
75 // GVN
76 const Proxy* mk_bundle(Lam* lam, const Def* var, Defs bundle_vars);
77 void gvn_bundle(Lam*, Defs, Defs, fe::Span<const Def*>);
78 void gvn_split(Lam*, Defs, fe::Span<const Def*>, fe::Span<const Def*>);
79
80 // SSA
81 /// Grows lam2live_ by closing @p lam's free Var%s and @p abstr_args over the lattice and slot2stored_.
82 void live(Lam* lam, Defs abstr_args);
83 using VarWorklist = fe::DFSWorklist<GIDSet<const Var*>>;
84 /// The slot Var%s reachable from @p todo through the lattice and slot2stored_.
85 Vars reachable_slots(VarWorklist& todo);
86 /// Like free_vars() but skips mem-typed parts, which cannot carry a slot's ptr.
87 Vars non_mem_free_vars(const Def* def);
88 /// A direct-style call may access every slot reachable from the non-mem parts of its abstract callee and arg.
89 void pin_reachable_slots(const Def* abstr_callee, const Def* abstr_arg);
90 void pin_slot(const App* slot);
91 /// Writes `concr ↦ abstr` resolved against @p sloxy, which pin_slot erases again.
92 void resolve(const Def* sloxy, const Def* concr, const Def* abstr);
93 void propagate_phis(Lam*, DefVec& vars, DefVec& abstr_args);
94 const Def* sloxy2val(const Def* sloxy) { return lam2sloxy2val(curr_mut<Lam>(), sloxy); }
95 const Def* sloxy2val(const Def* sloxy, const Def* val) { return lam2sloxy2val_[curr_mut<Lam>()][sloxy] = val; }
96 const Def* rewrite_imm_App(const App*) final;
97 void leave() final;
98
99 // post-processing analysis to find sloxies that must be set to top
100 void finalize() final;
101 void analyze(const Def*);
102
103 // local (reset between iterations)
104 LamMap<Def2Def> lam2sloxy2val_;
105 DefSet visited_;
106 DefSet first_;
107 DefMap<Vars> non_mem_fvs_;
108
109 // global (kept between iterations)
110 DefSet slots_; // actually slot ptrs
111 VarMap<const App*> var2slot_; // slot continuation Var ↦ its slot
112 VarMap<Vars> slot2stored_; // free Var%s of the values stored behind a slot
113 VarMap<DefSet> slot2resolved_; // promoted leas and loaded values whose lattice entry depends on the slot
114 LamMap<Vars> lam2live_; // slot Var%s a Lam may access; only grows
115 LamSet unknowns_; // Lam%s reached as a *value*; their signature must stay untouched
116 LamMap<MutSet> lam2callers_; // all muts that apply a Lam; tainted when the Lam's abstract vars change
117 };
118
119public:
121 : RWPhase(world, annex, &analysis_)
122 , analysis_(world) {}
123
124private:
125 const Def* rewrite_imm_App(const App*) final;
126 const Def* rewrite_imm_Var(const Var*) final;
127 const Def* rewrite_mut_Lam(Lam*) final;
128
129 /// A live phi for a Lam: the @p sloxy it stands for, the @p phi proxy, and its abstract @p val.
130 struct Phi {
131 const Def* sloxy;
132 const Def* phi;
133 const Def* val;
134 };
135
136 /// The new signature of an old Lam: the old Lam's tvars sieved, with Sig::phis appended as further candidates.
137 /// So Sieve::num_new is the new Lam's arity, Sieve::operator[] the new index of an old tvar,
138 /// and Sieve::cat the new index of a phi.
139 struct Sig {
140 fe::Vector<Phi> phis; ///< Its live phis.
141 Sieve sieve{0}; ///< @see Sig
142 bool todo = false; ///< Does the old Lam need a new signature at all?
143 };
144
145 bool analyze() final;
146
147 /// Does @p old_ptr point (via literal mem.lea%s) into a promoted slot? Then yields its sloxy.
148 const Def* isa_promoted(const Def* old_ptr) const;
149 /// The (memoized) new signature of @p old_lam.
150 const Sig& sig_of(Lam* old_lam);
151 /// The new spelling of @p old_lam's var: kept projections become new vars, dropped ones their
152 /// propagated value (⊥ for a promoted slot). The single source of truth for both build_lam() and
153 /// rewrite_imm_Var(), and hence well-defined independent of build order.
154 const Def* var_of(Lam* old_lam);
155 /// Builds (and caches) the new Lam for @p old_lam with propagated vars removed and kept phis appended.
156 Lam* build_lam(Lam* old_lam);
157 /// Builds the argument list for a jump to @p old_lam (with the given @p old_targs, one per tvar)
158 /// matching the signature built by build_lam().
159 DefVec build_args(Lam* old_lam, Defs old_targs);
160
161 Analysis analysis_;
162 Lam2Lam lam_old2new_;
163 Lam2Lam lam_new2old_;
164 GIDNodeMap<Lam*, Sig> lam2sig_; // a Sig& outlives nested rewrites
165 DefVec sloxies_; // the eliminated sloxies; every Lam's phi candidates
166};
167
168} // namespace mim::plug::mem::phase
Traverses the current World using Rewriter infrastructure while staying in the same world.
Definition phase.h:130
virtual void leave()
Called after curr_mut() has been completely dealt with.
Definition phase.h:292
Base class for all Defs.
Definition def.h:289
A function.
Definition lam.h:91
friend class Analysis
Definition phase.h:106
flags_t annex() const
Definition phase.h:62
bool todo() const
Definition phase.h:71
Used as intermediate value during optimizatinos such as Analysis.
Definition def.h:1065
Rebuilds old_world() into new_world() and then swaps them.
Definition phase.h:330
virtual void finalize()
Run after all roots have been walked but still before the two worlds are swapped.
Definition phase.h:406
RWPhase(World &world, std::string name, Analysis *analysis=nullptr)
Definition phase.h:334
World & world()=delete
Hides both and forbids direct access.
D * curr_mut() const
Definition rewrite.h:98
void reset()
Definition rewrite.cpp:23
Filters the components of an aggregate and keeps track of where the survivors end up.
Definition tuple.h:116
A variable introduced by a binder (mutable).
Definition def.h:857
The World represents the whole program and manages creation of MimIR nodes (Defs).
Definition world.h:43
bool analyze() final
Runs the optional pre-analysis on old_world(), typically to a fixed point, before rewriting begins.
Definition seo.cpp:551
const Def * rewrite_imm_Var(const Var *) final
Definition seo.cpp:653
const Def * rewrite_imm_App(const App *) final
Definition seo.cpp:589
const Def * rewrite_mut_Lam(Lam *) final
Definition seo.cpp:645
SEO(World &world, flags_t annex)
Definition seo.h:120
GIDSet< Lam * > LamSet
Definition lam.h:211
u64 flags_t
Definition types.h:39
fe::View< const Def * > Defs
Definition def.h:96
GIDMap< const Var *, To > VarMap
Definition def.h:114
GIDMap< const Def *, To > DefMap
Definition def.h:93
LamMap< Lam * > Lam2Lam
Definition lam.h:212
GIDMap< Lam *, To > LamMap
Definition lam.h:210
fe::Vector< const Def * > DefVec
Definition def.h:98
GIDSet< const Def * > DefSet
Definition def.h:94
fe::Patricia< const Var, DefKey >::Set Vars
Definition def.h:115
ankerl::unordered_dense::segmented_map< K, V, GIDHash< K > > GIDNodeMap
Definition gid.h:25