22const Def* LowerMapReduce::rec_broadcast(
const Def* s_in,
const Def* s_out,
const Def* input,
u64 r,
u64 i) {
25 if (i == r)
return input;
27 auto s_in_ri = s_in->proj(r, i), s_out_ri = s_out->proj(r, i);
28 log().d(
"broadcast dimension {} of {}: {} → {}, input = {}: {}", i, r, s_in_ri, s_out_ri, input, input->type());
30 if (s_in_ri == s_out_ri) {
33 [&](
size_t j) {
return rec_broadcast(s_in, s_out, input->proj(*s_in_lit, j), r, i + 1); });
34 return w.tuple(inputs);
38 log().w(
"dimension {} has equal but non-literal extent: {}", i, s_in_ri);
43 if (
auto s_in_lit =
Lit::isa<u64>(s_in_ri); s_in_lit && *s_in_lit == 1) {
44 log().d(
"dimension {}: packing the size-1 input to {}", i, s_out_ri);
45 return w.pack(s_out_ri, rec_broadcast(s_in, s_out, input, r, i + 1));
48 log().w(
"cannot broadcast dimension {}: {} → {}", i, s_in_ri, s_out_ri);
52const Def* LowerMapReduce::lower_broadcast(
const App* app) {
56 auto [s_in, s_out, input] = arg->projs<3>();
57 auto callee =
c->as<
App>();
58 auto [T,
r] = callee->args<2>();
59 log().d(
"lower broadcast: input = {}: {}, T = {}, r = {}, s_in = {}, s_out = {}", input, input->type(), T, r, s_in,
64 log().w(
"rank {} of {} is not known at lowering time", r, app);
68 if (s_in == s_out)
return input;
70 auto result = rec_broadcast(s_in, s_out, input, *r_nat, 0);
71 log().d(
"broadcast result: {}", result);
75const Def* LowerMapReduce::lower_map_reduce(
const App* app) {
94 auto inputs =
rewrite(app->arg());
98 auto Ro = mr.Ro, Sr = mr.Sr, So = mr.So;
100 auto ranks = mr.ranks();
102 log().w(
"rank counts (nis/nps/Ro/Rn) of {} are not known at lowering time", app);
105 auto [nis_nat, nps_nat, ro, rr] = *ranks;
106 auto nloops = ranks->nloops();
107 auto n =
w.lit_nat(nloops);
111 log().w(
"the input ranks of {} are not known at lowering time", app);
118 auto affine_map = [&](
const Def*
f,
const Def* m,
const Def* n,
const Def*
sin,
const Def* sout,
const Def* idxs) {
123 auto fun =
w.mut_fun(inputs->type(), type)->set(
"mapRed");
125 auto call =
w.app(ds_fun, inputs)->set(
"call");
127 auto [new_inputs, cont] = fun->vars<2>();
128 auto [new_is, new_post_is] = new_inputs->set(
"is")->projs<2>();
129 auto sr = Sr->projs(nloops);
132 const Def* acc =
w.bot(cont->type()->as<Pi>()->dom());
133 auto current_mut = fun;
136 auto wb_matrix = acc;
142 auto write_back =
w.mut_con(mr.To)->set(
"writeBack");
143 auto element_final = write_back->var();
144 DefVec wb_iters = out_iters;
145 for (
u64 j = 0; j < rr; ++j)
146 wb_iters.emplace_back(
w.call(
core::conv::u, sr[ro + j],
w.lit_i64(0)));
147 auto write_coords = affine_map(mr.map_out, Ro, n, Sr, So,
w.tuple(wb_iters));
150 DefVec post_elements(nps_nat, [&](
size_t j) {
151 auto sps_j = mr.Sps->proj(nps_nat, j);
153 = affine_map(mr.post_maps->proj(nps_nat, j), mr.Rps->proj(nps_nat, j), Ro, So, sps_j, write_coords);
154 return w.extract(new_post_is->proj(nps_nat, j), coords);
157 auto after_post =
w.mut_con(mr.Tp)->set(
"afterPost");
158 after_post->app(
true, cont,
w.insert(wb_matrix, write_coords, after_post->var()));
159 write_back->app(
true, mr.post, {w.tuple({element_final, w.tuple(post_elements)}), after_post});
166 auto element_acc = acc;
169 DefVec iters_v = out_iters;
171 iters_v.emplace_back(red);
172 auto iters =
w.tuple(iters_v);
175 DefVec input_elements(nis_nat, [&](
size_t i) {
176 auto sis_i = mr.Sis->proj(nis_nat, i);
177 auto coords = affine_map(mr.maps->proj(nis_nat, i), mr.Ris->proj(nis_nat, i), n, Sr, sis_i, iters);
178 return w.extract(new_is->proj(nis_nat, i), coords);
181 mr.comb->set(
"comb");
182 mr.post->set(
"post");
183 current_mut->app(
true, mr.comb, {w.tuple({element_acc, w.tuple(input_elements)}), cont});
185 }
catch (
const fe::Error::Bail&) {
throw; }
catch (
const std::exception& e) {
186 fe::throwf(
"failed to lower `tensor.map_reduce`: {}",
e.what());
190const Def* LowerMapReduce::build_pointwise(
const Def* inputs,
194 std::function<
const Def*(
Defs,
const Def*)> compute) {
195 auto&
w = new_world();
197 auto fun =
w.mut_fun(inputs->type(), type)->set(
"pointwise");
199 auto call =
w.app(ds_fun, inputs)->set(
"call");
201 auto [new_inputs, cont] = fun->vars<2>();
202 new_inputs->set(
"is");
205 const Def* acc =
w.bot(cont->type()->as<Pi>()->dom());
206 auto current_mut = fun;
207 auto so = So->projs(ro);
209 auto wb_matrix = acc;
213 auto element = compute(out_iters, new_inputs);
214 current_mut->app(
true, cont,
w.insert(wb_matrix,
w.tuple(write_coords), element));
218const Def* LowerMapReduce::lower_generate(
const App* app) {
219 auto&
w = new_world();
220 auto c = rewrite(app->callee())->as<
App>();
221 auto body = rewrite(app->arg());
222 auto type = rewrite(app->type());
224 auto [meta, s_out] =
c->uncurry_args<2>();
225 auto [T,
r] = meta->projs<2>();
228 log().w(
"rank {} of {} is not known at lowering time", r, app);
235 if (!
type->isa<Arr>()) {
236 DefVec zeros(rn, [&](
size_t) {
return w.lit_i64(0); });
237 return w.app(body,
w.tuple(zeros));
240 auto unit =
w.tuple();
241 auto compute = [&](
Defs out_iters,
const Def*) {
return w.call(body, out_iters); };
245const Def* LowerMapReduce::lower_pad(
const App* app) {
246 auto&
w = new_world();
247 auto c = rewrite(app->callee())->as<
App>();
248 auto args = rewrite(app->arg());
249 auto type = rewrite(app->type());
252 auto [Tr, s_in, params] =
c->uncurry_args<3>();
253 auto [T,
r] = Tr->projs<2>();
254 auto [
mode, lo, hi] = params->projs<3>();
258 if (!r_l || !mode_l) {
259 log().w(
"rank/mode of {} is not known at lowering time", app);
263 auto mode_nat = *mode_l;
266 auto out_arr =
type->isa<
Arr>();
267 if (!out_arr || out_arr->shape().rank() != rn) {
268 log().w(
"padded shape of {} is not a statically known rank-{} array", app, rn);
271 auto s_out = *out_arr->shape();
273 auto compute = [&](
Defs out_iters,
const Def* new_inputs) ->
const Def* {
274 auto [input, value] = new_inputs->projs<2>();
276 auto elem =
w.extract(input,
w.tuple(coords));
277 return valid ?
w.select(valid, elem, value) : elem;
283const Def* LowerMapReduce::lower_concat(
const App* app) {
284 auto&
w = new_world();
285 auto c = rewrite(app->callee())->as<
App>();
286 auto args = rewrite(app->arg());
287 auto type = rewrite(app->type());
290 auto [TnisR, ax, Sis] =
c->uncurry_args<3>();
291 auto [T, nis,
r] = TnisR->projs<3>();
296 if (!nis_l || !r_l || !ax_l) {
297 log().w(
"nis/r/ax of {} are not known at lowering time", app);
300 auto nisn = *nis_l, rn = *r_l, axn = *ax_l;
304 log().w(
"an extent of {} along the concat axis is not known at lowering time", app);
309 auto sum_ax = std::accumulate(exts->begin(), exts->end(),
u64(0));
310 DefVec so(rn, [&](
size_t d) {
return d == axn ?
w.lit_nat(sum_ax) : Sis->proj(nisn, 0)->proj(rn, d); });
311 auto s_out =
w.tuple(so);
313 auto compute = [&](
Defs out_iters,
const Def* new_inputs) ->
const Def* {
315 auto read = [&](
u64 i,
const Def* ax_idx) ->
const Def* {
316 auto Sis_i = Sis->proj(nisn, i);
317 DefVec coords(rn, [&](
size_t d) {
318 return w.call(
core::conv::u, Sis_i->proj(rn, d), d == axn ? ax_idx : out_iters[d]);
320 return w.extract(new_inputs->proj(nisn, i),
w.tuple(coords));
328const Def* LowerMapReduce::lower_gather(
const App* app) {
329 auto&
w = new_world();
330 auto c = rewrite(app->callee())->as<
App>();
331 auto args = rewrite(app->arg());
332 auto type = rewrite(app->type());
334 auto [Tr, shapes, dim] =
c->uncurry_args<3>();
335 auto [T,
r] = Tr->projs<2>();
336 auto [s_src, s_idx] = shapes->projs<2>();
339 if (!r_l || !dim_l)
return nullptr;
346 auto compute = [&](
Defs out_indices,
const Def* inputs) ->
const Def* {
348 return w.call(element,
Defs{s_src, s_idx}, dim, out_indices, inputs);
353const Def* LowerMapReduce::lower_scatter(
const App* app) {
354 auto&
w = new_world();
355 auto c = rewrite(app->callee())->as<
App>();
356 auto args = rewrite(app->arg());
357 auto type = rewrite(app->type());
359 auto [Tr, shapes, dim] =
c->uncurry_args<3>();
360 auto [T,
r] = Tr->projs<2>();
361 auto [s_src, s_idx, s_updates] = shapes->projs<3>();
364 if (!r_l || !dim_l)
return nullptr;
371 auto fun =
w.mut_fun(args->type(), type)->set(
"scatter");
373 auto call =
w.app(ds_fun, args)->set(
"call");
375 auto [iiu, cont] = fun->vars<2>();
376 auto [input, index, updates] = iiu->projs<3>();
379 auto dims = s_idx->projs(rn);
383 auto next =
w.call(step,
Defs{s_src, s_idx, s_updates}, dim, visit_indices,
Defs{acc, index, updates});
384 current->app(
true, cont, next);
393 if (
auto res = lower_broadcast(bc))
return res;
395 if (
auto res = lower_map_reduce(mr))
return res;
397 if (
auto res = lower_generate(
generate))
return res;
399 if (
auto res = lower_pad(
pad))
return res;
401 if (
auto res = lower_concat(cat))
return res;
403 if (
auto res = lower_gather(
gather))
return res;
405 if (
auto res = lower_scatter(
scatter))
return res;
static auto isa(const Def *def)
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.
Def * set(size_t i, const Def *)
Successively set from left to right.
static std::optional< T > isa(const Def *def)
const fe::Log & log() const
World & new_world()
Create new Defs into this.
const Def * rewrite_imm_App(const App *) override
virtual const Def * rewrite(const Def *)
const Def * rewrite_imm_App(const App *) final
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...
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',...
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 ...
DefVec to_idxs(World &w, Defs extents, Defs iters)
Converts the raw i64 loop counters iters into indices Idx extents[d].
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,...
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,...
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 ...
const Def * op_cps2ds_dep(const Def *k)
bool check_scatter_shape_constraints(const App *app, const Def *rank, const Def *dim, const Def *src_shape, const Def *idx_shape, const Def *updates_shape)
Checks statically decidable scatter constraints, blaming app for a violation.
bool check_gather_shape_constraints(const App *app, const Def *rank, const Def *dim, const Def *src_shape, const Def *idx_shape)
Checks statically decidable gather constraints, blaming app for a violation.
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.
gather_pointwise_elem_impl
fe::View< const Def * > Defs
fe::Vector< const Def * > DefVec
static MapRedPost of(const App *callee)