MimIR
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
scalarize.h
Go to the documentation of this file.
1#pragma once
2
3#include <fe/bitset.h>
4
5#include "mim/phase.h"
6
7namespace mim {
8
9/// Perform Scalarization (= Argument simplification).
10/// This means that, e.g.,
11/// ```
12/// f := λ (x_1: [T_1, T_2], .., x_n: T_n).E
13/// ```
14/// will be transformed to
15/// ```
16/// f' := λ (y_1: T_1, y_2: T_2, .. y_n: T_n).E[x_1 \ (y_1, y_2); ..; x_n \ y_n]
17/// ```
18///
19/// The transformation is **type-directed**:
20/// the decision to flatten is made per continuation *type* (immutable `Cn`) - not per Lam.
21/// Every producer and consumer of such a type is reshaped in the same sweep:
22/// the Pi itself, all Lam%s of that type, and all App%s through it - no matter whether the callee is a mutable Lam,
23/// a Var (higher-order parameter), a branch tuple, or a value loaded back from memory.
24/// Hence, a Lam may escape (be stored, passed as argument) and still get its signature flattened.
25///
26/// A Pi is *pinned* (left untouched) if
27/// * it is reachable from an annex (normalizers and backends rely on its exact shape),
28/// * an Axm application's signature dictates it - what the Axm consumes and produces,
29/// plus a *bare* function argument's type (`autodiff.ad f`) - except for subtrees that are
30/// merely substituted in via (type) arguments (`T` in `mem.store T`), which stay flattenable,
31/// * it occurs inside an *interface* Lam's signature (external, annex, or unset declaration) -
32/// only such a Lam's own top-level Pi stays flattenable (it may be shared with internal values;
33/// rewrite_mut_Lam() preserves the interface's top level by hand),
34/// * it types a value inside a dependently-typed aggregate (a typed closure),
35/// * an App connects a dom and an arg whose types are alpha-equivalent yet *distinct* defs, or
36/// * one of its parameters is Extract%ed / Insert%ed via a **non-constant** index
37/// (splitting would only force the body to reassemble the tuple);
38/// this is tracked per parameter via a keep-bitmask.
39///
40/// The phase flattens **one level** of a Pi's (thresholded) domain per run.
41/// Because it is scheduled inside a fixed-point pipeline (`compile.phases tt (...)`),
42/// re-running it converges to a full flatten; each run that peels calls invalidate().
43///
44/// Flattening respects Flags::scalarize_threshold via the thresholded projection helpers
45/// (Def::num_tprojs, Pi::tdom, App::targ): a parameter is only expanded if its arity is
46/// below the threshold.
47/// It will not flatten mutable @p Sigma%s or @p Arr%ays (their vars have no static arity).
48class Scalarize : public RWPhase {
49private:
50 /// Optimistic fixed-point analysis: every immutable `Cn` is assumed flattenable
51 /// until proven otherwise (see the pinning rules above).
52 class Analysis : public mim::Analysis {
53 public:
55 : mim::Analysis(world, "Scalarize::Analysis") {}
56
57 /// Per-parameter expand mask for @p type; a set bit marks a parameter to be flattened one level.
58 /// An **empty** mask means "leave untouched".
59 /// Cheap; recomputed on demand from the (post-fixed-point) lattice.
60 fe::Bitset plan(const Def* type) const;
61
62 private:
63 const Def* rewrite(const Def* old) final;
64
65 void inspect(const Def* def);
66 /// Marks parameter @p dom of @p pi as *keep whole* by OR-ing bit @p dom into a per-Pi bitmask stored
67 /// in lattice() under @p pi.
68 /// We store the fact via lattice_force() - **not** via lattice(concr, abstr)/pin():
69 /// growing the mask swaps one Nat literal for another - non-monotone in terms of Def%s.
70 /// As with any lattice write, the bitmask also lands in the rewriter map(),
71 /// short-circuiting later rewrite(pi) calls to the Nat.
72 /// That is harmless for this same-World Analysis:
73 /// drain() discards rewrite results, and inspect() always receives the old def.
74 /// A fresh bit invalidate()s.
75 void keep(const Pi* pi, size_t dom);
76 void pin_tree(const Def* def); ///< pin%s every flattenable Pi nested in @p def%'s type tree.
77 /// As above, but skips defs already in @p visited - seed it to exempt subtrees from pinning.
78 void pin_tree(const Def* def, DefSet& visited);
79 bool kept(const Pi* pi, size_t dom) const; ///< Is parameter @p dom of @p pi kept whole?
80 };
81
82public:
84 : RWPhase(world, annex, &analysis_)
85 , analysis_(world) {}
86
87private:
88 const Def* rewrite_imm_Pi(const Pi*) final;
89 const Def* rewrite_mut_Lam(Lam*) final;
90 const Def* rewrite_imm_App(const App*) final;
91
92 /// Flattens @p app%'s arguments one level according to @p mask.
93 DefVec flatten_args(const App* app, const fe::Bitset& mask);
94
95 Analysis analysis_;
96};
97
98} // namespace mim
Traverses the current World using Rewriter infrastructure while staying in the same world.
Definition phase.h:151
Base class for all Defs.
Definition def.h:273
A function.
Definition lam.h:113
friend class Analysis
Definition phase.h:125
flags_t annex() const
Definition phase.h:81
A dependent function type.
Definition lam.h:14
RWPhase(World &world, std::string name, Analysis *analysis=nullptr)
Definition phase.h:431
World & world()=delete
Hides both and forbids direct access.
virtual const Def * rewrite(const Def *)
Definition rewrite.cpp:55
const Def * rewrite_imm_Pi(const Pi *) final
const Def * rewrite_imm_App(const App *) final
Scalarize(World &world, flags_t annex)
Definition scalarize.h:83
const Def * rewrite_mut_Lam(Lam *) final
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
fe::Vector< const Def * > DefVec
Definition def.h:93
GIDSet< const Def * > DefSet
Definition def.h:89