MimIR
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
lower_index.cpp
Go to the documentation of this file.
2
3#include <mim/def.h>
4#include <mim/lam.h>
5#include <mim/tuple.h>
6
8#include <mim/plug/mem/mem.h>
10
12
14
15const Def* LowerIndex::rewrite(const Def* def) {
16 // The opaque affine index type lowers to the wide `Idx 0` (i64) carrier.
17 if (Axm::isa<affine::Index>(def)) return new_world().type_i64();
18 return RWPhase::rewrite(def);
19}
20
22 if (is_bootstrapping()) return RWPhase::rewrite_imm_App(app);
23
24 auto& w = new_world();
25
26 // Emits `core.div.<op> (mem_, (x, c))` on the `Idx 0` carrier, advancing the threaded mem and yielding the value.
27 auto div = [&](core::div op, const Def* x, const Def* c) -> const Def* {
28 auto [m, v] = w.call(op, Defs{mem_, w.tuple({x, c})})->projs<2>();
29 mem_ = m;
30 return v;
31 };
32
33 // The affine index algebra is computed on the wide `Idx 0` carrier with wrap-around (`Mode::none`) arithmetic, so
34 // that negation/subtraction are correct via two's complement; the boundary `affine.map` casts in/out with
35 // `core.conv.u`.
36
37 // affine.lit n ↦ the `Nat` n reinterpreted as an `Idx 0`.
38 if (Axm::isa<affine::lit>(app)) return w.call<core::bitcast>(w.type_i64(), rewrite(app->arg()));
39
40 if (auto op = Axm::isa<affine::op>(app)) {
41 switch (op.id()) {
42 case affine::op::add: {
43 auto [a, b] = rewrite(app->arg())->projs<2>();
44 return w.call(core::wrap::add, core::Mode::none, Defs{a, b});
45 }
46 case affine::op::sub: {
47 auto [a, b] = rewrite(app->arg())->projs<2>();
48 return w.call(core::wrap::sub, core::Mode::none, Defs{a, b});
49 }
50 case affine::op::neg: {
51 auto a = rewrite(app->arg());
52 return w.call(core::wrap::sub, core::Mode::none, Defs{w.lit(w.type_i64(), 0), a});
53 }
54 case affine::op::mul: {
55 fe::throwf("`affine.op.mul` should have been rewritten to `affine.semiop.mul` and then to `core.mul`");
56 }
57 }
58 }
59
60 if (auto semiop = Axm::isa<affine::semiop>(app)) {
61 auto [x, c] = rewrite(app->arg())->projs<2>();
62 switch (semiop.id()) {
65 fe::throwf("`affine.semiop.mul` called with non-constant second argument");
66 // `c` is a `Nat` constant; reinterpret it on the `Idx 0` carrier.
67 return w.call(core::wrap::mul, core::Mode::none, Defs{x, w.call<core::bitcast>(w.type_i64(), c)});
68 }
70 return div(core::div::udiv, x, w.call<core::bitcast>(w.type_i64(), c));
71 }
73 return div(core::div::urem, x, w.call<core::bitcast>(w.type_i64(), c));
74 }
76 auto c_idx = w.call<core::bitcast>(w.type_i64(), c);
77 // ceildiv(x, c) = (x + (c - 1)) / c (unsigned, on the Idx 0 carrier)
78 auto c_1 = w.call(core::wrap::sub, core::Mode::none, Defs{c_idx, w.lit(w.type_i64(), 1)});
79 return div(core::div::udiv, w.call(core::wrap::add, core::Mode::none, Defs{x, c_1}), c_idx);
80 }
81 }
82 }
83
84 // Row-major suffix-product strides of a shape `s` («n; Nat»): `strides#k = ∏_{j>k} s#j` (on `Idx 0`, via
85 // `core.nat`).
86 auto strides = [&](const Def* s, size_t n) {
87 DefVec str(n);
88 if (n) str[n - 1] = w.lit_nat(1);
89 for (size_t k = n - 1; k-- != 0;)
90 str[k] = w.call(core::nat::mul, Defs{str[k + 1], s->proj(n, k + 1)});
91 for (size_t k = 0; k != n; ++k)
92 str[k] = w.call<core::bitcast>(w.type_i64(), str[k]);
93 return str;
94 };
95
96 // affine.linearize (idxs, s) ↦ Σ_k idxs#k · strides#k (on the `Idx 0` carrier).
98 auto [idxs, s] = rewrite(app->arg())->projs<2>();
99 auto xs = idxs->projs();
100 auto str = strides(s, xs.size());
101 const Def* lin = w.lit(w.type_i64(), 0);
102 for (size_t k = 0; k != xs.size(); ++k) {
103 auto term = w.call(core::wrap::mul, core::Mode::none, Defs{xs[k], str[k]});
104 lin = w.call(core::wrap::add, core::Mode::none, Defs{lin, term});
105 }
106 return lin;
107 }
108
109 // affine.delinearize (lin, s) ↦ (lin floordiv strides#d) mod s#d for each d (on the `Idx 0` carrier).
111 auto [lin, s] = rewrite(app->arg())->projs<2>();
112 auto m = s->num_projs();
113 auto str = strides(s, m);
114 return w.tuple(DefVec(m, [&](size_t d) {
115 auto q = div(core::div::udiv, lin, str[d]);
116 return div(core::div::urem, q, w.call<core::bitcast>(w.type_i64(), s->proj(m, d)));
117 }));
118 }
119
120 // affine.map f idxs mem ↦ widen idxs to `Idx 0`, inline f (advancing the threaded mem through any div), and narrow
121 // each result back to its target `Idx (sout#j)`; returns `(mem', narrowed)`.
122 if (Axm::isa<affine::map>(app)) {
123 // Extract f/idxs/sout from the *old* callee; we inline f's body at this call site rather than rewriting it into
124 // a standalone lam, since its body may reference the threaded mem (from the divs) and would otherwise be open.
125 auto [mn, sinout, f, idxs, _] = app->callee()->as<App>()->uncurry_args<5>();
126 auto [sin, sout] = sinout->projs<2>();
127
128 auto __ = fe::Restore(mem_);
129 auto mem = rewrite(app->arg()); // the `affine.map`'s mem operand
130 auto ins = rewrite(idxs)->projs();
131 auto lifted = w.tuple(DefVec(ins.size(), [&](size_t i) { return w.call(core::conv::u, w.lit_i64(), ins[i]); }));
132 auto f_lam = f->isa_mut<Lam>();
133
134 Lam* idx_map_lam = nullptr;
135 Lam* rw_idx_lam = nullptr;
136 if (auto idx_lam = lookup(f_lam)) {
137 if (auto idx_lam_mut = idx_lam->isa_mut<Lam>();
138 idx_lam_mut && idx_lam_mut->num_vars() == 2 && idx_lam_mut->var(0)->type() == mem->type())
139 // completely rewritten, great!
140 idx_map_lam = idx_lam->as_mut<Lam>();
141 else
142 // was rewritten as part of an annex, probably.. so we need to rewrite it again adding mem.
143 rw_idx_lam = idx_lam->as_mut<Lam>();
144 }
145 if (!idx_map_lam) {
146 auto lam_pi = rewrite(f_lam->type())->as<Pi>();
147
148 idx_map_lam = w.mut_lam(w.pi({mem->type(), lam_pi->dom()}, {mem->type(), lam_pi->codom()}));
149 map(f_lam, idx_map_lam);
150
151 push();
152 map(f_lam->var(), idx_map_lam->var(1));
153 for (size_t i = 0; i != f_lam->num_vars(); ++i)
154 map(f_lam->var(i), idx_map_lam->var(1)->proj(f_lam->num_vars(), i));
155 mem_ = idx_map_lam->var(0);
156
157 auto get_body = [&]() -> const Def* {
158 if (rw_idx_lam) return rw_idx_lam->reduce_body(idx_map_lam->var(1));
159 return rewrite(f_lam->body());
160 };
161 idx_map_lam->set(true, w.tuple({mem_, get_body()}))->set(f_lam->dbg_key());
162 pop();
163 }
164
165 auto outs = w.app(idx_map_lam, {mem, lifted});
166
167 auto sout_n = rewrite(sout);
168 auto narrowed = w.tuple(DefVec(sout_n->num_projs(), [&](size_t j) {
169 return w.call(core::conv::u, sout_n->proj(j), outs->proj(2, 1)->proj(j));
170 }));
171
172 return w.tuple({outs->proj(0), narrowed});
173 }
174
175 return RWPhase::rewrite_imm_App(app);
176}
177
178} // namespace mim::plug::affine::phase
const Def * callee() const
Definition lam.h:275
const Def * arg() const
Definition lam.h:284
static auto isa(const Def *def)
Definition axm.h:112
Base class for all Defs.
Definition def.h:273
const Def * proj(nat_t a, nat_t i) const
Similar to World::extract while assuming an arity of a, but also works on Sigmas and Arrays.
Definition def.cpp:623
T * as_mut() const
Asserts that this is a mutable, casts constness away and performs a static_cast to T.
Definition def.h:589
const Def * var(nat_t a, nat_t i) noexcept
Definition def.h:479
auto projs(F f) const
Splits this Def via Def::projections into an Array (if A == std::dynamic_extent) or std::array (other...
Definition def.h:440
nat_t num_vars() noexcept
Definition def.h:479
A function.
Definition lam.h:113
const Def * reduce_body(const Def *arg) const
Definition lam.h:196
A dependent function type.
Definition lam.h:14
Pi * set(const Def *dom, const Def *codom)
Definition lam.h:87
bool is_bootstrapping() const
Returns whether we are currently bootstrapping (rewriting annexes).
Definition phase.h:403
World & new_world()
Create new Defs into this.
Definition phase.h:452
virtual void push()
Definition rewrite.h:40
virtual const Def * map(const Def *old_def, const Def *new_def)
Definition rewrite.h:47
virtual void pop()
Definition rewrite.h:41
virtual const Def * rewrite(const Def *)
Definition rewrite.cpp:55
virtual const Def * lookup(const Def *old_def)
Lookup old_def by searching in reverse through the stack of maps.
Definition rewrite.h:62
const Def * type_i64()
Definition world.h:634
const Def * rewrite_imm_App(const App *) final
const Def * rewrite(const Def *) final
@ none
Wrap around.
Definition core.h:16
The mem Plugin
Definition mem.h:11
fe::View< const Def * > Defs
Definition def.h:91
fe::Vector< const Def * > DefVec
Definition def.h:93