MimIR
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
lower.cpp
Go to the documentation of this file.
2
3#include <ranges>
4
5#include <mim/def.h>
6#include <mim/lam.h>
7
9
11
12const Def* Lower::read_through(const Def* base, Defs index) {
13 auto& w = new_world();
14
15 const Def* input;
16 const Def* s_in;
17 const Def* s_out;
18 const Def* rank;
19 bool wraps; // a repeat reads `idx mod s_in` per axis, a broadcast only ever reads a size-1 axis at 0
20
21 if (auto rep = Axm::isa<tensor::repeat>(base)) {
22 auto [Tr, in, out] = rep->callee()->as<App>()->uncurry_args<3>();
23 input = rep->arg(), s_in = in, s_out = out, rank = Tr->proj(2, 1), wraps = true;
24 } else if (auto bc = Axm::isa<tensor::broadcast>(base)) {
25 auto [in, out, i] = bc->args<3>();
26 auto [_, r] = bc->callee()->as<App>()->args<2>();
27 input = i, s_in = in, s_out = out, rank = r, wraps = false;
28 } else {
29 return nullptr;
30 }
31
32 auto r = Lit::isa(rank);
33 // An index reaching past the shape op's own axes continues into an array element type - not ours to answer.
34 if (!r || index.size() > *r) return nullptr;
35
36 auto new_index = DefVec(*r);
37 for (u64 d = 0, i = 0; d != *r; ++d) {
38 auto in_d = s_in->proj(*r, d);
39 auto out_d = s_out->proj(*r, d);
40 // A size-1 output axis folds out of the array type, so the chain carries no index for it.
41 auto idx_d = Lit::isa(out_d) == 1 ? w.lit_idx(1, 0) : (i < index.size() ? rewrite(index[i++]) : nullptr);
42 if (!idx_d) return nullptr;
43
44 if (in_d == out_d)
45 new_index[d] = idx_d;
46 else if (Lit::isa(in_d) == 1)
47 new_index[d] = w.lit_idx(1, 0);
48 else if (auto e = Lit::isa(in_d), x = Lit::isa(idx_d); wraps && e && x)
49 new_index[d] = w.lit_idx(*e, *x % *e);
50 else
51 return nullptr;
52 }
53
54 return w.extract(rewrite(input), w.tuple(new_index));
55}
56
57const Def* Lower::rewrite_imm_Extract(const Extract* extract) {
58 auto base = Axm::peel<tensor::buf>(extract->tuple());
60 auto index = Shape(extract->index());
61 if (auto r = index.rank())
62 if (auto res = read_through(base, DefVec(*r, [&](size_t i) { return index[i]; }))) return res;
63 }
64 return RWPhase::rewrite_imm_Extract(extract);
65}
66
67const Def* Lower::fastest_axis_2(const App* app, const Def* rank) {
68 auto& w = new_world();
69 auto b = rewrite(app->arg()->proj(2, 1));
70 return w.app(w.app(w.annex<tensor::fastest_axis>(), b->type()), {rank, b});
71}
72
73const Def* Lower::lower_via_impl(const App* app, const Def* impl) {
74 auto& w = new_world();
75
76 // The curry chain, innermost App first — hence re-applied in reverse.
77 auto args = DefVec();
78 for (const App* h = app; h; h = h->callee()->isa<App>())
79 args.emplace_back(rewrite(h->arg()));
80
81 // The `_impl` is a `lam`, so applying it triggers beta-reduction. Each `_impl`
82 // body references the `_impl` variants of its dependencies directly, so the
83 // chain bottoms out at the low-level axioms (`map_reduce`, …) in one go.
84 for (auto a : args | std::views::reverse)
85 impl = w.app(impl, a);
86 return impl;
87}
88
89const Def* Lower::rewrite_imm_App(const App* app) {
90 auto& w = new_world();
91
92 if (Axm::isa<tensor::broadcast_in_dim>(app)) return lower_via_impl(app, impl<tensor::broadcast_in_dim_impl>());
93 if (Axm::isa<tensor::transpose>(app)) return lower_via_impl(app, impl<tensor::transpose_impl>());
94 if (Axm::isa<tensor::map>(app)) return lower_via_impl(app, impl<tensor::map_impl>());
95 if (Axm::isa<tensor::unary>(app)) return lower_via_impl(app, impl<tensor::unary_impl>());
96 if (Axm::isa<tensor::binary>(app)) return lower_via_impl(app, impl<tensor::binary_impl>());
97 if (Axm::isa<tensor::select>(app)) return lower_via_impl(app, impl<tensor::select_impl>());
98 if (Axm::isa<tensor::repeat>(app)) return lower_via_impl(app, impl<tensor::repeat_impl>());
99 if (Axm::isa<tensor::reshape>(app)) return lower_via_impl(app, impl<tensor::reshape_impl>());
100 if (Axm::isa<tensor::slice>(app)) return lower_via_impl(app, impl<tensor::slice_impl>());
101 if (Axm::isa<tensor::flip>(app)) return lower_via_impl(app, impl<tensor::flip_impl>());
102 if (Axm::isa<tensor::conv>(app)) return lower_via_impl(app, impl<tensor::conv_impl>());
103 if (Axm::isa<tensor::conv_grouped>(app)) return lower_via_impl(app, impl<tensor::conv_grouped_impl>());
104 if (Axm::isa<tensor::pool>(app)) return lower_via_impl(app, impl<tensor::pool_impl>());
105
106 // The dot family's `_impl`s take a leading `fastest_2` with no axiom counterpart — the
107 // `tensor.fastest_axis` reflection of the right operand, pre-applied here at the staging
108 // point where that operand is concrete (see tensor.dot_product_impl for the decision).
110 return lower_via_impl(app, w.app(impl<tensor::product_2d_impl>(), fastest_axis_2(app, w.lit_nat(2))));
111 if (Axm::isa<tensor::bmm>(app))
112 return lower_via_impl(app, w.app(impl<tensor::bmm_impl>(), fastest_axis_2(app, w.lit_nat(3))));
114 // The curry chain, outermost app first: [a, b] {s1 s2} [c1, c2, b1, b2] {nc nb} {r1 r2};
115 // the right operand's rank is {r1 r2}#1.
116 auto groups = app->callee()->as<App>()->callee()->as<App>()->callee()->as<App>();
117 auto r2 = groups->callee()->as<App>()->arg()->proj(2, 1);
118 return lower_via_impl(app, w.app(impl<tensor::dot_product_impl>(), fastest_axis_2(app, rewrite(r2))));
119 }
120
121 return RWPhase::rewrite_imm_App(app);
122}
123
124} // namespace mim::plug::tensor::phase
const Def * callee() const
Definition lam.h:237
const Def * arg() const
Definition lam.h:251
static const Def * peel(const Def *def)
Peel off all Id Axms from def.
Definition axm.h:149
static auto isa(const Def *def)
Definition axm.h:112
Base class for all Defs.
Definition def.h:289
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:647
Extracts from a Sigma or Array-typed Extract::tuple the element at position Extract::index.
Definition tuple.h:360
const Def * tuple() const
Definition tuple.h:370
const Def * index() const
Definition tuple.h:371
static std::optional< T > isa(const Def *def)
Definition def.h:976
const fe::Vector< std::string > & args()
Command-line arguments passed to this Phase's plugin via -X <plugin>:<arg>.
Definition phase.cpp:23
World & new_world()
Create new Defs into this.
Definition phase.h:379
const Def * rewrite_imm_App(const App *) override
Definition phase.cpp:223
virtual const Def * rewrite_imm_Extract(const Extract *)
virtual const Def * rewrite(const Def *)
Definition rewrite.cpp:62
constexpr Shape() noexcept=default
const Def * rewrite_imm_App(const App *) final
Definition lower.cpp:89
const Def * rewrite_imm_Extract(const Extract *) final
Definition lower.cpp:57
fe::View< const Def * > Defs
Definition def.h:96
fe::Vector< const Def * > DefVec
Definition def.h:98
uint64_t u64
Definition types.h:27
@ App
Definition def.h:125