MimIR
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
btensor.h
Go to the documentation of this file.
1#pragma once
2
3#include <optional>
4
5#include <mim/lam.h>
6#include <mim/tuple.h>
7#include <mim/world.h>
8
11#include <mim/plug/core/core.h>
12#include <mim/plug/cps/cps.h>
13#include <mim/plug/mem/mem.h>
14
16
18
19/// The curried meta arguments of a `tensor.map_reduce_post` or `btensor.map_reduce_post` callee (same layout).
20struct MapRedPost {
22 const Def *nis, *nps, *To, *Tp, *Ro, *Rn, *TSched, *So, *Sr, *sched;
23 const Def *Tis, *Ris, *Sis, *Tps, *Rps, *Sps, *comb, *init, *post, *maps, *post_maps;
24
25 static MapRedPost of(const App* callee) {
26 auto [nis_nps, meta, shapes, in_tys, comb_init, map_out, maps_all] = callee->uncurry_args<7>();
27 auto [nis, nps] = nis_nps->projs<2>();
28 auto [To, Tp, Ro, Rn, TSched] = meta->projs<5>();
29 auto [So, Sr, sched] = shapes->projs<3>();
30 auto [Tis, Ris, Sis, Tps, Rps, Sps] = in_tys->projs<6>();
31 auto [comb, init, post] = comb_init->projs<3>();
32 auto [maps, post_maps] = maps_all->projs<2>();
34 Tp, Ro, Rn, TSched, So, Sr, sched, Tis, Ris, Sis,
36 }
37
38 struct Ranks {
40 u64 nloops() const { return ro + rr; }
41 };
42
43 /// The literal input counts and loop ranks; `std::nullopt` unless all are literal and `Rn >= Ro`.
44 std::optional<Ranks> ranks() const {
45 auto nis_l = Lit::isa<u64>(nis);
46 auto nps_l = Lit::isa<u64>(nps);
47 auto ro_l = Lit::isa<u64>(Ro);
48 auto rn_l = Lit::isa<u64>(Rn);
49 if (!nis_l || !nps_l || !ro_l || !rn_l || *rn_l < *ro_l) return {};
50 return Ranks{*nis_l, *nps_l, *ro_l, *rn_l - *ro_l};
51 }
52};
53
54/// `affine.map @(m, n) @(s_in, s_out) f idxs as mem` with the address space `as` of @p mem ↦ `[mem', coords]`.
55inline const Def* op_affine_map(const Def* f,
56 const Def* m,
57 const Def* n,
58 const Def* s_in,
59 const Def* s_out,
60 const Def* idxs,
61 const Def* mem) {
62 auto& w = mem->world();
63 auto a = w.app(w.annex<affine::map>(), Defs{m, n});
64 a = w.app(a, Defs{s_in, s_out});
65 a = w.app(a, f);
66 a = w.app(a, idxs);
67 a = w.app(a, mem->type()->as<App>()->arg());
68 return w.app(a, mem);
69}
70
71/// Calls the combiner/epilogue @p f from @p caller with @p args and the continuation @p ret.
72/// Scalarize may have flattened @p f's domain `Cn [[args], Cn ret]` to `Cn [args…, Cn ret]`; the call matches either.
73inline void app_cps(Lam* caller, const Def* f, DefVec args, const Def* ret) {
74 auto& w = caller->world();
75 if (f->type()->as<Pi>()->dom()->num_projs() == args.size() + 1) {
76 args.emplace_back(ret);
77 caller->app(true, f, w.tuple(args));
78 } else {
79 caller->app(true, f, w.tuple({w.tuple(args), ret}));
80 }
81}
82
83/// A fresh `fun: Fn arg->type() → codom` named @p name, spliced into direct style as `cps.cps2ds_dep fun arg`.
84/// @returns `{fun, call}`.
85inline std::pair<Lam*, const Def*> ds_splice(const Def* arg, const Def* codom, std::string name) {
86 auto& w = arg->world();
87 auto fun = w.mut_fun(arg->type(), codom)->set(std::move(name));
88 auto call = w.app(cps::op_cps2ds_dep(fun), arg);
89 return {fun, call};
90}
91
92/// Converts the raw i64 loop counters @p iters into indices `Idx extents[d]`.
93inline DefVec to_idxs(World& w, Defs extents, Defs iters) {
94 return DefVec(iters.size(), [&](size_t d) { return w.call(core::conv::u, extents[d], iters[d]); });
95}
96
97/// The raw i64 loop counters @p iters as a buffer index into @p shape, with its literal size-1 axes folded away.
98inline const Def* folded_idx(const Def* shape, Defs iters) {
99 auto& w = shape->world();
100 return *Shape(w, to_idxs(w, shape->projs(iters.size()), iters)).fold(shape);
101}
102
103/// Clamps the i64 @p x into `[0, hi]`.
104inline const Def* clamp(const Def* x, const Def* hi) {
105 auto& w = x->world();
106 return w.call(core::extrema::smax, Defs{w.lit_i64(0), w.call(core::extrema::smin, Defs{x, hi})});
107}
108
109/// The read coordinates `o#d − lo#d` of a `pad` into `Idx s_in#d` - clamped to the nearest edge if @p replicate, or
110/// else zeroed out of range - together with the all-axes in-range flag (`nullptr` if @p replicate).
111inline std::pair<DefVec, const Def*> pad_coords(Defs iters, const Def* lo, const Def* s_in, bool replicate) {
112 auto& w = lo->world();
113 auto i64 = w.type_i64();
114 auto rn = iters.size();
115 DefVec coords(rn);
116 DefVec valid;
117 for (u64 d = 0; d < rn; ++d) {
118 auto lo_d = w.call<core::bitcast>(i64, lo->proj(rn, d));
119 auto sin_d = w.call<core::bitcast>(i64, s_in->proj(rn, d));
120 auto in_d = w.call(core::wrap::sub, core::Mode::none, Defs{iters[d], lo_d});
121 const Def* idx_i64;
122 if (replicate) {
123 idx_i64 = clamp(in_d, w.call(core::wrap::sub, core::Mode::none, Defs{sin_d, w.lit_i64(1)}));
124 } else { // a single unsigned `<` covers both bounds since underflow wraps high
125 auto v_d = w.call(core::icmp::ul, Defs{in_d, sin_d});
126 valid.push_back(v_d);
127 idx_i64 = w.select(v_d, in_d, w.lit_i64(0));
128 }
129 coords[d] = w.call(core::conv::u, s_in->proj(rn, d), idx_i64);
130 }
131 if (replicate) return {coords, nullptr};
132 auto all_valid = valid.empty() ? w.lit_tt() : valid[0];
133 for (u64 d = 1; d < valid.size(); ++d)
134 all_valid = w.call(core::bit2::and_, w.lit_nat(2), w.tuple({all_valid, valid[d]}));
135 return {coords, all_valid};
136}
137
138/// The literal extents of the `nis` input shapes @p Sis along the concat axis @p ax; `std::nullopt` if one is not
139/// literal.
140inline std::optional<fe::Vector<u64>> concat_extents(const Def* Sis, u64 nis, u64 r, u64 ax) {
141 auto exts = fe::Vector<u64>(nis);
142 for (u64 i = 0; i < nis; ++i)
143 if (auto e = Lit::isa<u64>(Sis->proj(nis, i)->proj(r, ax)))
144 exts[i] = *e;
145 else
146 return {};
147 return exts;
148}
149
150/// The concat cell at the i64 axis coordinate @p o_ax: `read(i, loc)` of the input `i` owning it - the highest one
151/// whose prefix offset is `≤ o_ax` - at the axis coordinate `loc` clamped into that input.
152inline const Def* concat_select(const Def* o_ax, const fe::Vector<u64>& exts, auto read) {
153 auto& w = o_ax->world();
154 auto off = u64(0);
155 auto read_i = [&](u64 i) {
156 auto loc = w.call(core::wrap::sub, core::Mode::none, Defs{o_ax, w.lit_i64(off)});
157 return read(i, clamp(loc, w.lit_i64(exts[i] - 1)));
158 };
159 auto result = read_i(0);
160 for (u64 i = 1; i < exts.size(); ++i) {
161 off += exts[i - 1];
162 auto cond = w.call(core::icmp::uge, Defs{o_ax, w.lit_i64(off)});
163 result = w.select(cond, read_i(i), result);
164 }
165 return result;
166}
167
168/// A `[mem, dst, ins] → [mem, Buf]` fun spliced via `cps.cps2ds_dep` that loops over @p s_out carrying
169/// `[mem, dst]` and writes `compute`'s element at the loop's (identity) output coordinates.
170/// `compute(iters, ins, mem)` receives the raw i64 loop counters, the fun's inputs var, and the current mem; it
171/// returns `{mem', elem}`.
172inline const Def* build_pointwise(const Def* result_ty, // [mem.M 0, buffer.Buf (r, s_out, T)]
173 const Def* mem,
174 const Def* dst,
175 const Def* ins,
176 const Def* s_out,
177 u64 rn,
178 const std::string& name,
179 auto compute) {
180 auto& w = mem->world();
181 auto [fun, call] = ds_splice(w.tuple({mem, dst, ins}), result_ty, name);
182 auto [fun_mem, out_buf, fun_ins] = fun->var(2, 0)->projs<3>();
183 const Def* cont = fun->var(2, 1);
184 const Def* acc = w.tuple({fun_mem, out_buf});
185 auto cur = fun;
186 auto iters = affine::counting_nest(cur, cont, acc, s_out->projs(rn), name);
187 auto [loop_mem, loop_buf] = acc->projs<2>();
188
189 std::pair<const Def*, const Def*> el = compute(iters, fun_ins, loop_mem);
190 auto [el_mem, elem] = el;
191 auto wr_mem = buffer::op_write(el_mem, loop_buf, folded_idx(s_out, iters), elem)->proj(2, 0);
192 cur->app(true, cont, w.tuple({wr_mem, loop_buf}));
193 return call;
194}
195
196} // namespace mim::plug::btensor
static auto uncurry_args(const Def *def)
Definition lam.h:274
const Def * arg() const
Definition lam.h:251
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
World & world() const noexcept
Definition def.h:1105
const Def * var(nat_t a, nat_t i) noexcept
Definition def.h:492
auto projs(Projector auto f) const
Splits this Def via Def::projections into an Array (if A == std::dynamic_extent) or std::array (other...
Definition def.h:457
const Def * type() const noexcept
Yields the "raw" type of this Def (maybe nullptr).
Definition def.h:1119
nat_t num_projs() const
Yields Def::arity(), if it is a Lit, or 1 otherwise.
Definition def.h:1140
A function.
Definition lam.h:91
Lam * app(Filter filter, const Def *callee, const Def *arg)
Set body to an App of callee and arg.
Definition lam.cpp:35
static std::optional< T > isa(const Def *def)
Definition def.h:976
A dependent function type.
Definition lam.h:14
const Def * dom() const
Definition lam.h:38
constexpr Shape() noexcept=default
The World represents the whole program and manages creation of MimIR nodes (Defs).
Definition world.h:43
DefVec counting_nest(Lam *&cur, const Def *&exit, const Def *&acc, Defs dims, std::string_view name)
Nests one counting loop per bound of dims inside cur, threading cur, exit and acc down to the innermo...
Definition affine.h:24
The btensor Plugin
Definition btensor.h:17
const Def * op_affine_map(const Def *f, const Def *m, const Def *n, const Def *s_in, const Def *s_out, const Def *idxs, const Def *mem)
affine.map @(m, n) @(s_in, s_out) f idxs as mem with the address space as of mem ↦ [mem',...
Definition btensor.h:55
std::optional< fe::Vector< u64 > > concat_extents(const Def *Sis, u64 nis, u64 r, u64 ax)
The literal extents of the nis input shapes Sis along the concat axis ax; std::nullopt if one is not ...
Definition btensor.h:140
DefVec to_idxs(World &w, Defs extents, Defs iters)
Converts the raw i64 loop counters iters into indices Idx extents[d].
Definition btensor.h:93
const Def * build_pointwise(const Def *result_ty, const Def *mem, const Def *dst, const Def *ins, const Def *s_out, u64 rn, const std::string &name, auto compute)
A [mem, dst, ins] → [mem, Buf] fun spliced via cps.cps2ds_dep that loops over s_out carrying [mem,...
Definition btensor.h:172
const Def * clamp(const Def *x, const Def *hi)
Clamps the i64 x into [0, hi].
Definition btensor.h:104
std::pair< DefVec, const Def * > pad_coords(Defs iters, const Def *lo, const Def *s_in, bool replicate)
The read coordinates o#d − lo#d of a pad into Idx s_in#d - clamped to the nearest edge if replicate,...
Definition btensor.h:111
const Def * folded_idx(const Def *shape, Defs iters)
The raw i64 loop counters iters as a buffer index into shape, with its literal size-1 axes folded awa...
Definition btensor.h:98
const Def * concat_select(const Def *o_ax, const fe::Vector< u64 > &exts, auto read)
The concat cell at the i64 axis coordinate o_ax: read(i, loc) of the input i owning it - the highest ...
Definition btensor.h:152
std::pair< Lam *, const Def * > ds_splice(const Def *arg, const Def *codom, std::string name)
A fresh fun: Fn arg->type() → codom named name, spliced into direct style as cps.cps2ds_dep fun arg.
Definition btensor.h:85
void app_cps(Lam *caller, const Def *f, DefVec args, const Def *ret)
Calls the combiner/epilogue f from caller with args and the continuation ret.
Definition btensor.h:73
const Def * op_write(const Def *r, const Def *s, const Def *T, const Def *mem, const Def *buf, const Def *idx, const Def *val)
buffer.write (r, s, T) (mem, buf, idx, val) ↦ [mem.M 0, buffer.Buf (r, s, T)].
Definition buffer.h:30
@ none
Wrap around.
Definition core.h:16
const Def * op_cps2ds_dep(const Def *k)
Definition cps.h:16
The mem Plugin
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
The curried meta arguments of a tensor.map_reduce_post or btensor.map_reduce_post callee (same layout...
Definition btensor.h:20
static MapRedPost of(const App *callee)
Definition btensor.h:25
std::optional< Ranks > ranks() const
The literal input counts and loop ranks; std::nullopt unless all are literal and Rn >= Ro.
Definition btensor.h:44