28bool contains_gpu_init(
const Def* def,
DefSet& seen) {
29 if (
auto [_, ins] = seen.emplace(def); !ins)
return false;
31 for (
auto d : def->deps())
32 if (contains_gpu_init(d, seen))
return true;
37class GlobalMem :
public Rewriter {
39 GlobalMem(Lam* old_lam, Lam* new_lam)
40 : Rewriter(old_lam->world())
43 map(old_lam->var(), new_lam->var());
47 const Def* rewrite(
const Def* old_def)
final {
48 if (
auto mut = old_def->isa_mut(); mut && !old_lam_->nests(mut))
return mut;
57Lam* rebuild_lam_global_mem(Lam* lam,
const Def* Tout, Sym name) {
58 auto&
w = lam->world();
62 auto nv = lam->num_vars();
64 auto [_, Tin, extra_ty] = lam->var(nv, 0)->type()->projs<3>();
65 new_lam =
w.mut_con(
Defs{
w.sigma({global_ty, Tin, extra_ty}),
w.cn({global_ty, Tout})})->
set(name);
67 auto Tin = lam->var(nv, 1)->type();
68 auto extra_ty = lam->var(nv, 2)->type();
69 new_lam =
w.mut_con(
Defs{global_ty, Tin, extra_ty,
w.cn({global_ty, Tout})})->
set(name);
71 new_lam->set(
true, GlobalMem(lam, new_lam).rewrite(lam->body()));
76std::pair<const Def*, DefVec> unflatten_index(World& w,
const Def* flat,
const Vector<nat_t>& dims,
const Def* mem) {
78 if (m == 0)
return {mem, {}};
80 auto s_out =
w.tuple(
DefVec(m, [&](
size_t d) {
return w.lit_nat(dims[d]); }));
82 auto f =
w.mut_lam(index,
w.arr(m, index))->set(
"unflatten");
87 return {mem2, coords->projs(m)};
94InputDesc extract_input_desc(
nat_t n,
const Def* Rs,
const Def* Ss,
const Def* Ts,
const Def* maps) {
96 for (
nat_t i = 0; i != n; ++i) {
97 desc.rs[i] = Rs->proj(n, i);
98 desc.ss[i] = Ss->proj(n, i);
99 desc.ts[i] = Ts->proj(n, i);
100 desc.maps[i] = maps->proj(n, i);
111Inputs alloc_copy_inputs(World& w,
const Def* m0,
const Def* m1,
Defs ris,
Defs sis,
Defs tis,
const Def* inputs) {
113 for (
size_t i = 0; i != ris.size(); ++i) {
115 {m0, m1, inputs->proj(ris.size(), i)});
121 return {m0, m1, dptrs};
124std::pair<const Def*, const Def*> alloc_output(World& w,
const Def* m1,
const Def* elem_ty,
const Def* So) {
129 nat_t n_groups, n_items, total;
132Grid grid_layout(
const Vector<nat_t>& out_dims) {
134 for (
auto d : out_dims)
136 nat_t n_items = std::min<nat_t>(total, 1024);
137 nat_t n_groups = (total + n_items - 1) / n_items;
138 return {n_groups, n_items, total};
143 DefVec rs, ss, dptrs, maps;
144 nat_t n()
const {
return dptrs.size(); }
148Lam* build_kernel(World& w,
151 const Vector<nat_t>& out_dims,
159 const Mapped& post_ins,
165 auto nps = post_ins.n();
166 auto ro = out_dims.size();
167 auto nloops_nat = ro + rr;
168 auto n =
w.lit_nat(nloops_nat);
175 DefVec arg_tys(nis + nps + 1);
176 for (
size_t i = 0; i != nis; ++i)
177 arg_tys[i] = ins.dptrs[i]->type();
178 for (
size_t j = 0; j != nps; ++j)
179 arg_tys[nis + j] = post_ins.dptrs[j]->type();
180 arg_tys[nis + nps] = out_dptr->type();
183 =
w.mut_con(
Defs{global_ty, shared_ty, const_ty, local_ty,
w.type_idx(grid.n_groups),
w.type_idx(grid.n_items),
184 w.sigma(
Defs{}),
w.sigma(arg_tys),
w.cn({global_ty, shared_ty, const_ty, local_ty})})
185 ->
set(
"mapReduceKernel");
186 auto [k_global, k_shared, k_const, k_local, group_id, item_id, k_shared_ptrs, k_args, k_ret] = kernel->vars<9>();
189 for (
size_t i = 0; i != nis; ++i)
190 k_dptrs[i] = k_args->proj(nis + nps + 1, i);
192 for (
size_t j = 0; j != nps; ++j)
193 k_post_dptrs[j] = k_args->proj(nis + nps + 1, nis + j);
194 auto k_out_dptr = k_args->proj(nis + nps + 1, nis + nps);
196 auto group_i64 = grid.n_groups == 1 ?
w.lit_i64(0) :
w.call(
core::conv::u,
w.lit_nat_0(), group_id);
197 auto item_i64 = grid.n_items == 1 ?
w.lit_i64(0) :
w.call(
core::conv::u,
w.lit_nat_0(), item_id);
203 auto early_return =
w.mut_con(
w.sigma(
Defs{}))->set(
"outOfRange");
204 early_return->app(
true, k_ret,
Defs{k_global, k_shared, k_const, k_local});
205 auto body =
w.mut_con(
w.sigma(
Defs{}))->set(
"inRange");
206 kernel->branch(
true, in_range, body, early_return);
208 auto write_back =
w.mut_con(
Defs{global_ty, To})->
set(
"writeBack");
209 auto [wb_mem, acc_final] = write_back->vars<2>();
210 auto [wb_mem2, wb_coords] = unflatten_index(w, flat, out_dims, wb_mem);
211 DefVec wb_idx = wb_coords;
212 for (
size_t j = 0; j != rr; ++j)
213 wb_idx.push_back(
w.call(
core::conv::u, Sr->proj(nloops_nat, ro + j),
w.lit_i64(0)));
218 for (
size_t j = 0; j != nps; ++j) {
219 auto [pc_mem, pcoords]
223 auto [rd_mem, rd_val]
227 post_elems[j] = rd_val;
230 auto after_post =
w.mut_con(
Defs{global_ty, Tp})->
set(
"afterPost");
231 auto [post_mem, elem_post] = after_post->vars<2>();
234 after_post->app(
true, k_ret,
Defs{final_mem, k_shared, k_const, k_local});
235 btensor::app_cps(write_back, global_post, {pcur, acc_final,
w.tuple(post_elems)}, after_post);
237 const Def* acc =
w.tuple({k_global,
init});
238 const Def* cont = write_back;
239 Lam* current_mut = body;
240 auto sr = Sr->projs(nloops_nat);
243 auto [red_mem, elem_acc] = acc->projs<2>();
245 auto [body_mem, body_coords] = unflatten_index(w, flat, out_dims, red_mem);
246 DefVec iters_v = body_coords;
247 iters_v.insert(iters_v.end(), red_iters.begin(), red_iters.end());
248 auto iters =
w.tuple(iters_v);
252 for (
size_t i = 0; i != nis; ++i) {
253 auto [mc_mem, coords]
256 auto [rd_mem, rd_val]
259 input_elems[i] = rd_val;
262 btensor::app_cps(current_mut, global_comb, {cur, elem_acc,
w.tuple(input_elems)}, cont);
267Lam* build_teardown(World& w,
278 auto mem_ty =
w.call<
mem::M>(0);
280 auto after_launch =
w.mut_con(
Defs{mem_ty, global_ty, const_ty})->
set(
"afterLaunch");
281 auto [post_mem, post_global, post_const] = after_launch->vars<3>();
285 auto [cb_mem, cb_global] = copy_back->projs<2>();
287 auto cur_global = cb_global;
288 for (
auto dptr : dptrs)
290 for (
auto dptr : post_dptrs)
295 after_launch->app(
true, cont,
Defs{final_mem, host_buf});
304 = std::ranges::any_of(
old_world().externals().muts(), [&](
auto def) {
return contains_gpu_init(def, seen); });
306 log().w(
"not lowering any map-reduce operations to GPU: the program already contains an explicit `gpu.init`");
317const Def* LowerMapReduce::lower_map_reduce_post(
const App* app) {
323 auto ranks = mr.ranks();
325 log().w(
"{} doesn't have lowering-time known rank counts (nis/nps/Ro/Rn)", app);
328 auto [nis_n, nps_n, ro, rr] = *ranks;
330 Vector<nat_t> out_dims(ro);
332 for (
nat_t d = 0; d != ro; ++d) {
335 log().w(
"{} doesn't have a lowering-time known output (grid) shape", app);
341 if (out_total == 0) {
342 log().w(
"{} has a zero-sized output, skipping GPU lowering", app);
346 auto comb_lam = mr.comb->isa_mut<Lam>();
347 auto post_lam = mr.post->isa_mut<Lam>();
348 if (!comb_lam || !post_lam) {
349 log().w(
"{} doesn't have a lowering-time known combiner/epilogue", app);
354 auto [fun_mem, host_buf, new_inputs, new_post_ins] = fun->var(2, 0)->projs<4>();
355 auto cont = fun->var(2, 1);
357 auto [h_mem, h_global, h_const] =
w.app(
w.annex<
gpu::auto_init>(), fun_mem)->projs<3>();
359 auto in_desc = extract_input_desc(nis_n, mr.Ris, mr.Sis, mr.Tis, mr.maps);
360 auto inputs = alloc_copy_inputs(w, h_mem, h_global, in_desc.rs, in_desc.ss, in_desc.ts, new_inputs);
362 auto post_desc = extract_input_desc(nps_n, mr.Rps, mr.Sps, mr.Tps, mr.post_maps);
364 = alloc_copy_inputs(w, inputs.mem, inputs.global, post_desc.rs, post_desc.ss, post_desc.ts, new_post_ins);
366 auto [out_global, out_dptr] = alloc_output(w, post_inputs.global, mr.Tp, mr.So);
368 auto global_comb = rebuild_lam_global_mem(comb_lam, mr.To,
w.sym(
"combGlobal"));
369 auto global_post = rebuild_lam_global_mem(post_lam, mr.Tp,
w.sym(
"postGlobal"));
371 auto grid = grid_layout(out_dims);
373 auto kernel = build_kernel(w, mr.Ro, rr, out_dims, Sr, mr.So,
374 Mapped{in_desc.rs, in_desc.ss, inputs.dptrs, in_desc.maps}, mr.To, mr.map_out, mr.init,
375 global_comb, Mapped{post_desc.rs, post_desc.ss, post_inputs.dptrs, post_desc.maps},
376 global_post, mr.Tp, out_dptr, grid);
378 DefVec kernel_args = inputs.dptrs;
379 kernel_args.insert(kernel_args.end(), post_inputs.dptrs.begin(), post_inputs.dptrs.end());
380 kernel_args.push_back(out_dptr);
381 DefVec kernel_arg_tys(kernel_args.size(), [&](
size_t i) { return kernel_args[i]->type(); });
385 w.lit_ff(),
w.tuple()});
390 = build_teardown(w, mr.Ro, mr.So, mr.Tp, inputs.dptrs, post_inputs.dptrs, out_dptr, host_buf, cont);
391 auto launch_call =
w.app(
launch,
Defs{
w.tuple({post_inputs.mem, out_global, h_const}), after_launch});
392 fun->set(
true, launch_call);
const Def * callee() const
static auto isa(const Def *def)
auto projs(Projector auto f) const
Splits this Def via Def::projections into an Array (if A == std::dynamic_extent) or std::array (other...
const Def * type() const noexcept
Yields the "raw" type of this Def (maybe nullptr).
static std::optional< T > isa(const Def *def)
const fe::Log & log() const
World & new_world()
Create new Defs into this.
void start() override
Actual entry.
World & old_world()
Get old Defs from here.
const Def * rewrite_imm_App(const App *) override
virtual const Def * rewrite(const Def *)
constexpr Shape() noexcept=default
void start() final
Skips the whole phase if the program already contains an explicit gpu.init.
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',...
DefVec to_idxs(World &w, Defs extents, Defs iters)
Converts the raw i64 loop counters iters into indices Idx extents[d].
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.
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.
const Def * op_lea_tuple(const Def *ptr, const Def *tuple)
Successively offsets ptr by each component of the index tuple, peeling one array dimension per mem....
fe::View< const Def * > Defs
fe::Vector< const Def * > DefVec
GIDSet< const Def * > DefSet
static MapRedPost of(const App *callee)