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.constant n ↦ the `Nat` n reinterpreted as an `Idx 0`.
38 if (Axm::isa<affine::constant>(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()) {
64 if (Axm::isa<refly::check>(c)) fe::throwf("affine.op.mul called with non-constant second argument");
65 // `c` is a `Nat` constant; reinterpret it on the `Idx 0` carrier.
66 return w.call(core::wrap::mul, core::Mode::none, Defs{x, w.call<core::bitcast>(w.type_i64(), c)});
67 }
69 return div(core::div::udiv, x, w.call<core::bitcast>(w.type_i64(), c));
70 }
72 return div(core::div::urem, x, w.call<core::bitcast>(w.type_i64(), c));
73 }
75 auto c_idx = w.call<core::bitcast>(w.type_i64(), c);
76 // ceildiv(x, c) = (x + (c - 1)) / c (unsigned, on the Idx 0 carrier)
77 auto c_1 = w.call(core::wrap::sub, core::Mode::none, Defs{c_idx, w.lit(w.type_i64(), 1)});
78 return div(core::div::udiv, w.call(core::wrap::add, core::Mode::none, Defs{x, c_1}), c_idx);
79 }
80 }
81 }
82
83 // Row-major suffix-product strides of a shape `s` («n; Nat»): `strides#k = ∏_{j>k} s#j` (on `Idx 0`, via
84 // `%core.nat`).
85 auto strides = [&](const Def* s, size_t n) {
86 DefVec str(n);
87 if (n) str[n - 1] = w.lit_nat(1);
88 for (size_t k = n - 1; k-- != 0;)
89 str[k] = w.call(core::nat::mul, Defs{str[k + 1], s->proj(n, k + 1)});
90 for (size_t k = 0; k != n; ++k)
91 str[k] = w.call<core::bitcast>(w.type_i64(), str[k]);
92 return str;
93 };
94
95 // %affine.linearize (idxs, s) ↦ Σ_k idxs#k · strides#k (on the `Idx 0` carrier).
97 auto [idxs, s] = rewrite(app->arg())->projs<2>();
98 auto xs = idxs->projs();
99 auto str = strides(s, xs.size());
100 const Def* lin = w.lit(w.type_i64(), 0);
101 for (size_t k = 0; k != xs.size(); ++k) {
102 auto term = w.call(core::wrap::mul, core::Mode::none, Defs{xs[k], str[k]});
103 lin = w.call(core::wrap::add, core::Mode::none, Defs{lin, term});
104 }
105 return lin;
106 }
107
108 // %affine.delinearize (lin, s) ↦ (lin floordiv strides#d) mod s#d for each d (on the `Idx 0` carrier).
110 auto [lin, s] = rewrite(app->arg())->projs<2>();
111 auto m = s->num_projs();
112 auto str = strides(s, m);
113 return w.tuple(DefVec(m, [&](size_t d) {
114 auto q = div(core::div::udiv, lin, str[d]);
115 return div(core::div::urem, q, w.call<core::bitcast>(w.type_i64(), s->proj(m, d)));
116 }));
117 }
118
119 // %affine.map f idxs mem ↦ widen idxs to `Idx 0`, inline f (advancing the threaded mem through any div), and narrow
120 // each result back to its target `Idx (sout#j)`; returns `(mem', narrowed)`.
121 if (Axm::isa<affine::map>(app)) {
122 // Extract f/idxs/sout from the *old* callee; we inline f's body at this call site rather than rewriting it into
123 // a standalone lam, since its body may reference the threaded mem (from the divs) and would otherwise be open.
124 auto [mn, sinout, f, idxs] = app->callee()->as<App>()->uncurry_args<4>();
125 auto [sin, sout] = sinout->projs<2>();
126
127 auto _ = Restore(mem_);
128 auto mem = rewrite(app->arg()); // the `%affine.map`'s mem operand
129
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
133 auto f_lam = f->isa_mut<Lam>();
134
135 Lam* idx_map_lam = nullptr;
136 Lam* rw_idx_lam = nullptr;
137 if (auto idx_lam = lookup(f_lam)) {
138 if (auto idx_lam_mut = idx_lam->isa_mut<Lam>();
139 idx_lam_mut && idx_lam_mut->num_vars() == 2 && idx_lam_mut->var(0)->type() == mem->type())
140 // completely rewritten, great!
141 idx_map_lam = idx_lam->as_mut<Lam>();
142 else
143 // was rewritten as part of an annex, probably.. so we need to rewrite it again adding mem.
144 rw_idx_lam = idx_lam->as_mut<Lam>();
145 }
146 if (!idx_map_lam) {
147 auto lam_pi = rewrite(f_lam->type())->as<Pi>();
148
149 idx_map_lam = w.mut_lam(w.pi({mem->type(), lam_pi->dom()}, {mem->type(), lam_pi->codom()}));
150 map(f_lam, idx_map_lam);
151
152 push();
153 map(f_lam->var(), idx_map_lam->var(1));
154 for (size_t i = 0; i != f_lam->num_vars(); ++i)
155 map(f_lam->var(i), idx_map_lam->var(1)->proj(f_lam->num_vars(), i));
156 mem_ = idx_map_lam->var(0);
157
158 auto get_body = [&]() -> const Def* {
159 if (rw_idx_lam) return rw_idx_lam->reduce_body(idx_map_lam->var(1));
160 return rewrite(f_lam->body());
161 };
162 idx_map_lam->set(true, w.tuple({mem_, get_body()}))->set(f_lam->dbg());
163 pop();
164 }
165
166 auto outs = w.app(idx_map_lam, {mem, lifted});
167
168 auto sout_n = rewrite(sout);
169 auto narrowed = w.tuple(DefVec(sout_n->num_projs(), [&](size_t j) {
170 return w.call(core::conv::u, sout_n->proj(j), outs->proj(2, 1)->proj(j));
171 }));
172
173 return w.tuple({outs->proj(0), narrowed});
174 }
175
176 return RWPhase::rewrite_imm_App(app);
177}
178
179} // namespace mim::plug::affine::phase
const Def * callee() const
Definition lam.h:276
const Def * arg() const
Definition lam.h:285
static auto isa(const Def *def)
Definition axm.h:107
Base class for all Defs.
Definition def.h:261
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:635
T * as_mut() const
Asserts that this is a mutable, casts constness away and performs a static_cast to T.
Definition def.h:536
const Def * var(nat_t a, nat_t i) noexcept
Definition def.h:441
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:402
nat_t num_vars() noexcept
Definition def.h:441
A function.
Definition lam.h:110
const Def * reduce_body(const Def *arg) const
Definition lam.h:187
A dependent function type.
Definition lam.h:14
Pi * set(const Def *dom, const Def *codom)
Definition lam.h:76
World & new_world()
Create new Defs into this.
Definition phase.h:368
bool is_bootstrapping() const
Returns whether we are currently bootstrapping (rewriting annexes).
Definition phase.h:356
RAII guard that restores ref to its current value at the end of the scope.
Definition util.h:177
virtual void push()
Definition rewrite.h:38
virtual void pop()
Definition rewrite.h:39
virtual const Def * rewrite(const Def *)
Definition rewrite.cpp:56
virtual const Def * lookup(const Def *old_def)
Lookup old_def by searching in reverse through the stack of maps.
Definition rewrite.h:55
const Def * type_i64()
Definition world.h:612
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
View< const Def * > Defs
Definition def.h:78
Vector< const Def * > DefVec
Definition def.h:79