MimIR
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
tensor.h
Go to the documentation of this file.
1#pragma once
2
3#include <optional>
4
5#include <fe/worklist.h>
6
7#include <mim/driver.h>
8#include <mim/lam.h>
9#include <mim/plugin.h>
10#include <mim/tuple.h>
11#include <mim/world.h>
12
14
15namespace mim::plug::tensor {
16
17/// Is @p map the identity access map `λ x. x`?
18inline bool is_identity_map(const Def* map) {
19 auto lam = map->isa_mut<Lam>();
20 return lam && lam->is_set() && lam->body() == lam->var();
21}
22
23/// Recognizes the (rebuilt) `tensor_copy` combiner `(acc, ys) ↦ ys#0`: the result is exactly the
24/// single input element, so a map_reduce built on it is a pure re-indexed read of that input.
25inline bool is_copy_comb(const Def* comb) {
26 auto ret = Lam::isa_ret_arg(comb);
27 return ret && ret == comb->as_mut<Lam>()->var(2, 0)->proj(2, 1)->proj(1, 0);
28}
29
30/// Is `post` the (rebuilt) CPS identity `tensor.id`, i.e. a lam `(x, extras) ↦ x` that returns its
31/// first argument (and hence has no epilogue inputs)?
32inline bool is_identity_post(const Def* post) {
33 auto ret = Lam::isa_ret_arg(post);
34 return ret && ret == post->as_mut<Lam>()->var(2, 0)->proj(2, 0);
35}
36
37/// A pure re-indexed read: the source tensor, the access map into it (over the read's output
38/// coordinates), and the source's element type/rank/shape.
39struct PureRead {
40 const Def* src = nullptr;
41 const Def* map = nullptr;
42 const Def* T = nullptr;
43 const Def* R = nullptr;
44 const Def* S = nullptr;
45};
46
47/// If `value` is a pure re-indexed read — a copy-combiner map_reduce without reduction loops that
48/// writes its full loop domain through the identity output map (reshape/transpose/slice/flip/repeat
49/// lower to these) — returns its single access map and source.
50/// `fuse_tensor`'s read-through absorbs exactly these into the consuming op's access maps.
51inline std::optional<PureRead> is_pure_read(const Def* value) {
52 auto mr = Axm::isa<tensor::map_reduce_post>(value);
53 if (!mr) return {};
54 auto [nis_nps, meta, shapes, in_tys, comb_init, map_out, maps_all, is_all] = mr->uncurry_args<8>();
55
56 auto [nis, nps] = nis_nps->projs<2>();
57 // No reduction loops: the total loop count Rn equals the output rank Ro.
58 if (Lit::isa(nis) != 1 || Lit::isa(nps) != 0 || meta->proj(5, 2) != meta->proj(5, 3)) return {};
59 auto [So, Sr, sched] = shapes->projs<3>();
60 if (Sr != So) return {};
61 if (!is_identity_map(map_out)) return {};
62 auto [comb, init, post] = comb_init->projs<3>();
63 if (!is_copy_comb(comb) || !is_identity_post(post)) return {};
64
65 auto sole = [](const Def* d, u64 n, u64 i) { return d->proj(n, i)->proj(1, 0); };
66 return PureRead{sole(is_all, 2, 0), sole(maps_all, 2, 0), sole(in_tys, 6, 0), sole(in_tys, 6, 1),
67 sole(in_tys, 6, 2)};
68}
69
70/// The literal values of @p def's @p n projections, or nothing if one of them is not a literal.
71inline std::optional<fe::Vector<u64>> lit_projs(const Def* def, u64 n) {
72 auto res = fe::Vector<u64>(n);
73 for (u64 i = 0; i != n; ++i)
74 if (auto l = Lit::isa<u64>(def->proj(n, i)))
75 res[i] = *l;
76 else
77 return {};
78 return res;
79}
80
81/// @p perm as literals, if it is literal.
82inline std::optional<fe::Vector<u64>> lit_perm(const Def* perm) {
83 if (auto r = Lit::isa(perm->arity())) return lit_projs(perm, *r);
84 return {};
85}
86
87/// The permutation of a `transpose` app, if it is literal.
88inline std::optional<fe::Vector<u64>> transpose_perm(const App* app) {
89 return lit_perm(app->decurry()->decurry()->arg());
90}
91
92/// The input of a 2-D transpose `transpose (1, 0)`, or `nullptr`.
93inline const Def* isa_transpose_2d(const Def* def) {
94 if (auto app = Axm::isa<tensor::transpose>(def)) {
95 auto perm = app->decurry()->decurry()->arg();
96 if (Lit::isa(perm->arity()) == 2) {
97 auto [p0, p1] = perm->projs<2>();
98 if (Lit::isa(p0) == 1 && Lit::isa(p1) == 0) return app->arg();
99 }
100 }
101 return nullptr;
102}
103
104/// Counts the consumers of every def of @p world matched by @p pred.
105/// Tuples and packs are transparent argument wrappers, so a wrapped def is charged to the enclosing
106/// non-tuple consumer - a shared argument tuple charges each of its users, and a def used twice in one
107/// argument list counts twice.
108/// A phase whose world does not track uses needs this up front.
109DefMap<u64> count_consumers(const World& world, std::predicate<const Def*> auto pred) {
110 auto counts = DefMap<u64>();
111 auto charge = [&](this auto&& charge, const Def* d) -> void {
112 if (pred(d))
113 ++counts[d];
114 else if (d->isa<Tuple>() || d->isa<Pack>())
115 for (auto op : d->ops())
116 if (op) charge(op);
117 };
118
119 auto wl = fe::BFSWorklist<DefSet>();
120 wl.push(world.externals().muts());
121
122 while (!wl.empty()) {
123 auto def = wl.pop();
124 auto transparent = def->isa<Tuple>() || def->isa<Pack>();
125 for (auto op : def->ops())
126 if (op) {
127 if (!transparent) charge(op);
128 wl.push(op);
129 }
130 if (def->type()) wl.push(def->type());
131 }
132
133 return counts;
134}
135
136} // namespace mim::plug::tensor
const App * decurry() const
Returns App::callee again as App.
Definition lam.h:238
const Def * arg() const
Definition lam.h:251
static auto isa(const Def *def)
Definition axm.h:112
Base class for all Defs.
Definition def.h:289
bool is_set() const
Definition def.h:396
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
T * as_mut() const
Asserts that this is a mutable, casts constness away and performs a static_cast to T.
Definition def.h:597
A function.
Definition lam.h:91
static const Def * isa_ret_arg(const Def *d)
Yields the y of λ (x, ret) = ret y - the argument d's body hands to its Lam::ret_var.
Definition lam.cpp:46
static std::optional< T > isa(const Def *def)
Definition def.h:976
A (possibly paramterized) Tuple.
Definition tuple.h:332
Data constructor for a Sigma.
Definition tuple.h:84
auto muts() const
Definition world.h:191
The World represents the whole program and manages creation of MimIR nodes (Defs).
Definition world.h:43
const Externals & externals() const
Definition world.h:286
The tensor Plugin
Definition constraints.h:5
std::optional< fe::Vector< u64 > > lit_projs(const Def *def, u64 n)
The literal values of def's n projections, or nothing if one of them is not a literal.
Definition tensor.h:71
bool is_identity_post(const Def *post)
Is post the (rebuilt) CPS identity tensor.id, i.e.
Definition tensor.h:32
DefMap< u64 > count_consumers(const World &world, std::predicate< const Def * > auto pred)
Counts the consumers of every def of world matched by pred.
Definition tensor.h:109
std::optional< PureRead > is_pure_read(const Def *value)
If value is a pure re-indexed read — a copy-combiner map_reduce without reduction loops that writes i...
Definition tensor.h:51
const Def * isa_transpose_2d(const Def *def)
The input of a 2-D transpose transpose (1, 0), or nullptr.
Definition tensor.h:93
std::optional< fe::Vector< u64 > > transpose_perm(const App *app)
The permutation of a transpose app, if it is literal.
Definition tensor.h:88
std::optional< fe::Vector< u64 > > lit_perm(const Def *perm)
perm as literals, if it is literal.
Definition tensor.h:82
bool is_copy_comb(const Def *comb)
Recognizes the (rebuilt) tensor_copy combiner (acc, ys) ↦ ys#0: the result is exactly the single inpu...
Definition tensor.h:25
bool is_identity_map(const Def *map)
Is map the identity access map λ x. x?
Definition tensor.h:18
A pure re-indexed read: the source tensor, the access map into it (over the read's output coordinates...
Definition tensor.h:39
GIDMap< const Def *, To > DefMap
Definition def.h:93
uint64_t u64
Definition types.h:27