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