#include "gpu/intel/conv/jit/plan.hpp"
#include "gpu/intel/conv/jit/config.hpp"
#include "gpu/intel/jit/ir/gemm_schedule.hpp"
#include "gpu/intel/jit/ir/legacy.hpp"
#include "gpu/intel/jit/ir/reduce.hpp"
#include "gpu/intel/jit/ir/send_builder.hpp"
#include "gpu/intel/jit/ir/send_plan.hpp"
#include "gpu/intel/jit/ir/tensor.hpp"
namespace dnnl {
namespace impl {
namespace gpu {
namespace intel {
namespace conv {
namespace jit {
class dim_tile_t {
public:
const expr_t &grid_idx() const { return not_empty(grid_idx_); }
const expr_t &tg_idx() const { return not_empty(tg_idx_); }
const expr_t &loop_idx() const { return not_empty(loop_idx_); }
const expr_t &iter_idx() const { return not_empty(iter_idx_); }
void set_grid_idx(const expr_t &idx) { grid_idx_ = idx; }
void set_tg_idx(const expr_t &idx) { tg_idx_ = idx; }
void set_loop_idx(const expr_t &idx) { loop_idx_ = idx; }
void set_iter_idx(const expr_t &idx) { iter_idx_ = idx; }
private:
static const expr_t ¬_empty(const expr_t &v) {
gpu_assert(v) << "Queried empty index.";
return v;
}
expr_t grid_idx_;
expr_t tg_idx_;
expr_t loop_idx_;
expr_t iter_idx_;
};
static dim_tile_t create_tile(gemm_schedule_t &gemm_schedule,
const config_t &cfg, const expr_t &dim) {
dim_tile_t tile;
auto &name = dim.as<var_t>().name;
auto dim_id = pvar_t(name);
dim_t loop_dim = cfg.loop_dim(dim_id);
dim_t tg_dim = cfg.thread_group_dim(dim_id);
dim_t iter_dim = cfg.iter_dim(dim_id);
std::vector<dim_t> dims = {1, loop_dim, tg_dim, iter_dim};
dim_idx_t ndims = into<dim_idx_t>(dims.size());
std::vector<expr_t> idxs(ndims);
static const char *suffixes[]
= {"_grid_idx", "_loop_idx", "_tg_idx", "_iter_idx"};
auto &dim_name = dim.as<var_t>().name;
auto has_block = [&](int dim_idx) {
bool is_thr = (dim_idx == 1);
bool is_tg = (dim_idx == 2);
bool is_iter = (dim_idx == 3);
if (is_thr || is_iter) return true;
auto grid = is_tg ? get_thread_group_grid_dims(cfg)
: get_kernel_grid_dims(cfg);
for (auto &tile : grid)
for (auto &d : tile)
if (dim_name == d.str()) return true;
return false;
};
expr_t idx = dim;
for (int i = ndims - 1; i >= 1; i--) {
expr_t outer;
expr_t inner;
auto outer_name = (i == 1) ? dim_name + suffixes[i - 1] : std::string();
auto inner_name = dim_name + suffixes[i];
gemm_schedule.split(idx, dims[i], outer, inner, outer_name, inner_name);
if (has_block(i)) idxs[i] = std::move(inner);
idx = std::move(outer);
}
idxs[0] = std::move(idx);
tile.set_grid_idx(idxs[0]);
tile.set_loop_idx(idxs[1]);
tile.set_tg_idx(idxs[2]);
tile.set_iter_idx(idxs[3]);
return tile;
}
void bind_thread_group_grid_idx(const config_t &cfg,
gemm_schedule_t &gemm_schedule, const expr_t &var) {
auto grid_dims = get_thread_group_grid_dims(cfg);
int grid_id = -1;
for (auto &v : gemm_schedule.get_root_vars(var)) {
auto v_dim = pvar_t(v.as<var_t>().name);
for (int i = 0; i < 3; i++) {
if (grid_dims[i].has(v_dim)) {
gpu_assert(grid_id == -1 || grid_id == i);
grid_id = i;
}
}
}
gpu_assert(grid_id != -1);
gemm_schedule.bind(var, cfg.thread_group_grid().idx(grid_id));
}
void bind_kernel_grid(
gemm_schedule_t &gemm_schedule, const std::vector<expr_t> &vars) {
for (auto &v : vars) {
if (gemm_schedule.var_bound(v) == 1) continue;
auto root_vars = gemm_schedule.get_root_vars(v);
gpu_assert((int)root_vars.size() == 1);
auto v_dim = pvar_t(root_vars[0].as<var_t>().name);
auto dummy_grid_var
= gemm_schedule.kernel_grid_walk_order().grid_var(v_dim);
gemm_schedule.bind(v, dummy_grid_var);
}
}
void init_fwd(const config_t &cfg_, gemm_schedule_t &gemm_schedule,
view_t &src_view, view_t &wei_view, view_t &dst_view) {
auto &prb_ = cfg_.prb();
constraint_set_t init_cset;
auto &src_layout = cfg_.src_layout().compute();
auto &wei_layout = cfg_.wei_layout().compute();
auto &dst_layout = cfg_.dst_layout().compute();
auto mb = var_t::make(dsl::type_t::s32(), "mb");
auto ic = var_t::make(dsl::type_t::s32(), "ic");
auto oc = var_t::make(dsl::type_t::s32(), "oc");
auto kd = var_t::make(dsl::type_t::s32(), "kd");
auto kh = var_t::make(dsl::type_t::s32(), "kh");
auto kw = var_t::make(dsl::type_t::s32(), "kw");
auto g = var_t::make(dsl::type_t::s32(), "g");
expr_t ow, oh, od;
bool check_od = false;
bool check_oh = false;
bool check_ow = false;
od = var_t::make(dsl::type_t::s32(), "od");
oh = var_t::make(dsl::type_t::s32(), "oh");
ow = var_t::make(dsl::type_t::s32(), "ow");
check_ow = (prb_.ow < cfg_.padded_dim(pvars::ow));
expr_t id_mask, ih_mask, iw_mask;
expr_t od_mask, oh_mask, ow_mask;
bool check_kw = (prb_.kw < cfg_.padded_dim(pvars::kw));
bool check_iw = check_kw || check_ow
|| utils::need_src_or_dst_check(prb_.is_fwd, prb_.ow, prb_.iw,
prb_.kw, prb_.pw, prb_.sw, prb_.dw);
bool check_ih = check_oh
|| utils::need_src_or_dst_check(prb_.is_fwd, prb_.oh, prb_.ih,
prb_.kh, prb_.ph, prb_.sh, prb_.dh);
bool check_id = check_od
|| utils::need_src_or_dst_check(prb_.is_fwd, prb_.od, prb_.id,
prb_.kd, prb_.pd, prb_.sd, prb_.dd);
auto &x = view_t::placeholder_var();
if (check_id) id_mask = (x >= 0) & (x < prb_.id);
if (check_ih) ih_mask = (x >= 0) & (x < prb_.ih);
if (check_iw) iw_mask = (x >= 0) & (x < prb_.iw);
if (check_od) od_mask = (x >= 0) & (x < prb_.od);
if (check_oh) oh_mask = (x >= 0) & (x < prb_.oh);
if (check_ow) ow_mask = (x >= 0) & (x < prb_.ow);
src_view = view_t({mb, g, ic, od, oh, ow, kd, kh, kw}, 6);
src_view.set_vdim(mb, prb_.mb);
src_view.set_vdim(g, prb_.g);
src_view.set_vdim(ic, prb_.ic);
src_view.set_vdim(od, prb_.od);
src_view.set_vdim(oh, prb_.oh);
src_view.set_vdim(ow, prb_.ow);
src_view.set_vdim(kd, prb_.kd);
src_view.set_vdim(kh, prb_.kh);
src_view.set_vdim(kw, prb_.kw);
src_view.set_tdim(0, mb);
src_view.set_tdim(1, g);
src_view.set_tdim(2, ic);
src_view.set_tdim(3, od * prb_.sd - prb_.pd + kd * (1 + prb_.dd), id_mask);
src_view.set_tdim(4, oh * prb_.sh - prb_.ph + kh * (1 + prb_.dh), ih_mask);
src_view.set_tdim(5, ow * prb_.sw - prb_.pw + kw * (1 + prb_.dw), iw_mask);
src_view.set_tlayout(src_layout);
src_view.set_tmasks(cfg_.padded_dims().get().to_string_map());
wei_view = view_t({g, oc, ic, kd, kh, kw}, 6);
wei_view.set_vdim(g, prb_.g);
wei_view.set_vdim(oc, prb_.oc);
wei_view.set_vdim(ic, prb_.ic);
wei_view.set_vdim(kd, prb_.kd);
wei_view.set_vdim(kh, prb_.kh);
wei_view.set_vdim(kw, prb_.kw);
wei_view.set_tdim(0, g);
wei_view.set_tdim(1, oc);
wei_view.set_tdim(2, ic);
wei_view.set_tdim(3, kd);
wei_view.set_tdim(4, kh);
wei_view.set_tdim(5, kw);
wei_view.set_tlayout(wei_layout);
wei_view.set_tmasks(cfg_.padded_dims().get().to_string_map());
dst_view = view_t({mb, g, oc, od, oh, ow}, 6);
dst_view.set_vdim(mb, prb_.mb);
dst_view.set_vdim(g, prb_.g);
dst_view.set_vdim(oc, prb_.oc);
dst_view.set_vdim(od, prb_.od);
dst_view.set_vdim(oh, prb_.oh);
dst_view.set_vdim(ow, prb_.ow);
dst_view.set_tdim(0, mb);
dst_view.set_tdim(1, g);
dst_view.set_tdim(2, oc);
dst_view.set_tdim(3, od, od_mask);
dst_view.set_tdim(4, oh, oh_mask);
dst_view.set_tdim(5, ow, ow_mask);
dst_view.set_tlayout(dst_layout);
dst_view.set_tmasks(cfg_.padded_dims().get().to_string_map());
if (prb_.ab_swap_transpose) {
gemm_schedule.set_a_view(wei_view);
gemm_schedule.set_b_view(src_view);
gemm_schedule.set_n_vars({mb, od, oh, ow});
gemm_schedule.set_m_vars({oc});
} else {
gemm_schedule.set_a_view(src_view);
gemm_schedule.set_b_view(wei_view);
gemm_schedule.set_m_vars({mb, od, oh, ow});
gemm_schedule.set_n_vars({oc});
}
gemm_schedule.set_c_view(dst_view);
gemm_schedule.set_b_vars({g});
gemm_schedule.set_k_vars({ic, kd, kh, kw});
gemm_schedule.for_each_var([&](const expr_t &var) {
dim_t bound = cfg_.padded_dim(pvar_t(var.as<var_t>().name));
gemm_schedule.set_var_bound(var, bound);
});
auto g_tile = create_tile(gemm_schedule, cfg_, g);
auto oc_tile = create_tile(gemm_schedule, cfg_, oc);
auto mb_tile = create_tile(gemm_schedule, cfg_, mb);
auto ow_tile = create_tile(gemm_schedule, cfg_, ow);
auto ic_tile = create_tile(gemm_schedule, cfg_, ic);
auto kw_tile = create_tile(gemm_schedule, cfg_, kw);
auto mb_ow_tg_idx = gemm_schedule.fuse(mb_tile.tg_idx(), ow_tile.tg_idx());
std::vector<expr_t> kernel_grid_vars;
kernel_grid_vars.push_back(oc_tile.grid_idx());
kernel_grid_vars.push_back(std::move(od));
kernel_grid_vars.push_back(std::move(oh));
kernel_grid_vars.push_back(ow_tile.grid_idx());
kernel_grid_vars.push_back(g_tile.grid_idx());
kernel_grid_vars.push_back(mb_tile.grid_idx());
bind_kernel_grid(gemm_schedule, kernel_grid_vars);
bind_thread_group_grid_idx(cfg_, gemm_schedule, oc_tile.tg_idx());
bind_thread_group_grid_idx(cfg_, gemm_schedule, mb_ow_tg_idx);
bind_thread_group_grid_idx(cfg_, gemm_schedule, ic_tile.tg_idx());
gemm_schedule.tensorize(g_tile.iter_idx());
gemm_schedule.tensorize(oc_tile.iter_idx());
gemm_schedule.tensorize(mb_tile.iter_idx());
gemm_schedule.tensorize(ow_tile.iter_idx());
gemm_schedule.tensorize(kw_tile.iter_idx());
gemm_schedule.tensorize(ic_tile.iter_idx());
gemm_schedule.reorder({ic_tile.loop_idx(), std::move(kd), std::move(kh),
kw_tile.loop_idx(), oc_tile.tg_idx(), std::move(mb_ow_tg_idx),
ic_tile.tg_idx()});
}
void init_bwd_d(const config_t &cfg_, gemm_schedule_t &gemm_schedule,
view_t &dst_view, view_t &wei_view, view_t &src_view) {
auto &prb_ = cfg_.prb();
auto &src_layout = cfg_.src_layout().compute();
auto &wei_layout = cfg_.wei_layout().compute();
auto &dst_layout = cfg_.dst_layout().compute();
auto g = var_t::make(dsl::type_t::s32(), "g");
auto mb = var_t::make(dsl::type_t::s32(), "mb");
auto ic = var_t::make(dsl::type_t::s32(), "ic");
auto oc = var_t::make(dsl::type_t::s32(), "oc");
auto id = var_t::make(dsl::type_t::s32(), "id");
auto ih = var_t::make(dsl::type_t::s32(), "ih");
auto iw = var_t::make(dsl::type_t::s32(), "iw");
auto kd = var_t::make(dsl::type_t::s32(), "kd");
auto kh = var_t::make(dsl::type_t::s32(), "kh");
auto kw = var_t::make(dsl::type_t::s32(), "kw");
expr_t od_mask(true), oh_mask(true), ow_mask(true);
bool check_iw = (prb_.iw < cfg_.padded_dim(pvars::iw));
bool check_ow = check_iw
|| utils::need_src_or_dst_check(prb_.is_fwd, prb_.ow, prb_.iw,
prb_.kw, prb_.pw, prb_.sw, prb_.dw);
bool check_oh = utils::need_src_or_dst_check(
prb_.is_fwd, prb_.oh, prb_.ih, prb_.kh, prb_.ph, prb_.sh, prb_.dh);
bool check_od = utils::need_src_or_dst_check(
prb_.is_fwd, prb_.od, prb_.id, prb_.kd, prb_.pd, prb_.sd, prb_.dd);
auto &x = view_t::placeholder_var();
if (check_od) od_mask = (x >= 0) & (x < prb_.od);
if (check_oh) oh_mask = (x >= 0) & (x < prb_.oh);
if (check_ow) ow_mask = (x >= 0) & (x < prb_.ow);
std::function<expr_t(const expr_t &)> iw_mapping;
if (cfg_.bwd_d_optimize_kind() == bwd_d_optimize_kind_t::skip_strided_dhw) {
iw_mapping = [&](const expr_t &e) {
dim_t iw_tg_blk = cfg_.thread_group_dim(pvars::iw)
* cfg_.iter_dim(pvars::iw);
dim_t iw_bound = utils::rnd_up(prb_.iw, iw_tg_blk);
dim_t iw_same_mod_blk = ir_utils::safe_divide(iw_bound, prb_.sw);
return (e % iw_same_mod_blk) * prb_.sw + (e / iw_same_mod_blk);
};
} else {
iw_mapping = [](const expr_t &e) { return e; };
}
dst_view = view_t({mb, g, oc, id, ih, iw, kd, kh, kw}, 6);
dst_view.set_vdim(mb, prb_.mb);
dst_view.set_vdim(g, prb_.g);
dst_view.set_vdim(oc, prb_.oc);
dst_view.set_vdim(id, prb_.id);
dst_view.set_vdim(ih, prb_.ih);
dst_view.set_vdim(iw, prb_.iw);
dst_view.set_vdim(kd, prb_.kd);
dst_view.set_vdim(kh, prb_.kh);
dst_view.set_vdim(kw, prb_.kw);
dst_view.set_tdim(0, mb);
dst_view.set_tdim(1, g);
dst_view.set_tdim(2, oc);
auto od = id - kd * (1 + prb_.dd) + prb_.pd;
auto oh = ih - kh * (1 + prb_.dh) + prb_.ph;
auto ow = iw_mapping(iw) - kw * (1 + prb_.dw) + prb_.pw;
switch (cfg_.bwd_d_optimize_kind()) {
case bwd_d_optimize_kind_t::none:
case bwd_d_optimize_kind_t::skip_out_of_bound_w:
if (prb_.sd != 1) od_mask &= (od % prb_.sd == 0);
if (prb_.sh != 1) oh_mask &= (oh % prb_.sh == 0);
if (prb_.sw != 1) ow_mask &= (ow % prb_.sw == 0);
break;
case bwd_d_optimize_kind_t::skip_strided_dhw: break;
case bwd_d_optimize_kind_t::skip_strided_dh:
if (prb_.sw != 1) ow_mask &= (ow % prb_.sw == 0);
break;
default: gpu_error_not_expected();
}
dst_view.set_tdim(3, od / prb_.sd, od_mask);
dst_view.set_tdim(4, oh / prb_.sh, oh_mask);
dst_view.set_tdim(5, ow / prb_.sw, ow_mask);
dst_view.set_tlayout(dst_layout);
dst_view.set_tmasks(cfg_.padded_dims().get().to_string_map());
wei_view = view_t({g, oc, ic, kd, kh, kw}, 6);
wei_view.set_vdim(g, prb_.g);
wei_view.set_vdim(ic, prb_.ic);
wei_view.set_vdim(oc, prb_.oc);
wei_view.set_vdim(kd, prb_.kd);
wei_view.set_vdim(kh, prb_.kh);
wei_view.set_vdim(kw, prb_.kw);
wei_view.set_tdim(0, g);
wei_view.set_tdim(1, oc);
wei_view.set_tdim(2, ic);
wei_view.set_tdim(3, kd);
wei_view.set_tdim(4, kh);
wei_view.set_tdim(5, kw);
wei_view.set_tlayout(wei_layout);
wei_view.set_tmasks(cfg_.padded_dims().get().to_string_map());
src_view = view_t({mb, g, ic, id, ih, iw}, 6);
src_view.set_vdim(mb, prb_.mb);
src_view.set_vdim(g, prb_.g);
src_view.set_vdim(ic, prb_.ic);
src_view.set_vdim(id, prb_.id);
src_view.set_vdim(ih, prb_.ih);
src_view.set_vdim(iw, prb_.iw);
src_view.set_tdim(0, mb);
src_view.set_tdim(1, g);
src_view.set_tdim(2, ic);
src_view.set_tdim(3, id);
src_view.set_tdim(4, ih);
src_view.set_tdim(5, iw_mapping(iw));
src_view.set_tlayout(src_layout);
src_view.set_tmasks(cfg_.padded_dims().get().to_string_map());
if (prb_.ab_swap_transpose) {
gemm_schedule.set_a_view(wei_view);
gemm_schedule.set_b_view(dst_view);
} else {
gemm_schedule.set_a_view(dst_view);
gemm_schedule.set_b_view(wei_view);
}
gemm_schedule.set_c_view(src_view);
gemm_schedule.set_b_vars({g});
if (prb_.ab_swap_transpose) {
gemm_schedule.set_n_vars({mb, id, ih, iw});
gemm_schedule.set_m_vars({ic});
} else {
gemm_schedule.set_m_vars({mb, id, ih, iw});
gemm_schedule.set_n_vars({ic});
}
gemm_schedule.set_k_vars({oc, kd, kh, kw});
gemm_schedule.for_each_var([&](const expr_t &var) {
dim_t bound = cfg_.padded_dim(pvar_t(var.as<var_t>().name));
gemm_schedule.set_var_bound(var, bound);
});
auto g_tile = create_tile(gemm_schedule, cfg_, g);
auto ic_tile = create_tile(gemm_schedule, cfg_, ic);
auto mb_tile = create_tile(gemm_schedule, cfg_, mb);
auto iw_tile = create_tile(gemm_schedule, cfg_, iw);
auto oc_tile = create_tile(gemm_schedule, cfg_, oc);
std::vector<expr_t> kernel_grid_vars;
kernel_grid_vars.push_back(ic_tile.grid_idx());
kernel_grid_vars.push_back(id);
kernel_grid_vars.push_back(ih);
kernel_grid_vars.push_back(iw_tile.grid_idx());
kernel_grid_vars.push_back(g_tile.grid_idx());
kernel_grid_vars.push_back(mb_tile.grid_idx());
bind_kernel_grid(gemm_schedule, kernel_grid_vars);
auto mb_iw_tg_idx = gemm_schedule.fuse(mb_tile.tg_idx(), iw_tile.tg_idx());
bind_thread_group_grid_idx(cfg_, gemm_schedule, ic_tile.tg_idx());
bind_thread_group_grid_idx(cfg_, gemm_schedule, mb_iw_tg_idx);
bind_thread_group_grid_idx(cfg_, gemm_schedule, oc_tile.tg_idx());
gemm_schedule.tensorize(g_tile.iter_idx());
gemm_schedule.tensorize(ic_tile.iter_idx());
gemm_schedule.tensorize(mb_tile.iter_idx());
gemm_schedule.tensorize(iw_tile.iter_idx());
gemm_schedule.tensorize(oc_tile.iter_idx());
switch (cfg_.bwd_d_optimize_kind()) {
case bwd_d_optimize_kind_t::none:
gemm_schedule.reorder({oc_tile.loop_idx(), std::move(kd),
std::move(kh), std::move(kw)});
break;
case bwd_d_optimize_kind_t::skip_strided_dhw:
gemm_schedule.set_dynamic_bounds(
kw, (iw_mapping(iw) + prb_.pw) % prb_.sw, prb_.sw);
case bwd_d_optimize_kind_t::skip_strided_dh:
gemm_schedule.set_dynamic_bounds(
kd, (id + prb_.pd) % prb_.sd, prb_.sd);
gemm_schedule.set_dynamic_bounds(
kh, (ih + prb_.ph) % prb_.sh, prb_.sh);
gemm_schedule.reorder({std::move(kd), std::move(kh), std::move(kw),
oc_tile.loop_idx()});
break;
case bwd_d_optimize_kind_t::skip_out_of_bound_w:
gemm_schedule.set_dynamic_bounds(kw,
binary_op_t::make(op_kind_t::_max,
(iw_mapping(iw) + ((prb_.pw - prb_.ow) + 1))
/ (1 + prb_.dw),
0),
expr_t(1));
break;
default: gpu_error_not_expected();
}
}
void init_bwd_w(const config_t &cfg_, gemm_schedule_t &gemm_schedule,
view_t &src_view, view_t &dst_view, view_t &wei_view,
view_t &bia_view) {
auto &prb_ = cfg_.prb();
auto &src_layout = cfg_.src_layout().compute();
auto &wei_layout = cfg_.wei_layout().compute();
auto &dst_layout = cfg_.dst_layout().compute();
auto &bia_layout = cfg_.bia_layout().compute();
auto g = var_t::make(dsl::type_t::s32(), "g");
auto mb = var_t::make(dsl::type_t::s32(), "mb");
auto ic = var_t::make(dsl::type_t::s32(), "ic");
auto oc = var_t::make(dsl::type_t::s32(), "oc");
auto od = var_t::make(dsl::type_t::s32(), "od");
auto oh = var_t::make(dsl::type_t::s32(), "oh");
auto ow = var_t::make(dsl::type_t::s32(), "ow");
auto kd = var_t::make(dsl::type_t::s32(), "kd");
auto kh = var_t::make(dsl::type_t::s32(), "kh");
auto kw = var_t::make(dsl::type_t::s32(), "kw");
expr_t id_mask(true), ih_mask(true), iw_mask(true);
bool check_ow = (prb_.ow < cfg_.padded_dim(pvars::ow));
bool check_oh = (prb_.oh < cfg_.padded_dim(pvars::oh));
bool check_od = (prb_.od < cfg_.padded_dim(pvars::od));
bool check_kw = (prb_.kw < cfg_.padded_dim(pvars::kw));
bool check_iw = check_kw
|| utils::need_src_or_dst_check(true, prb_.ow, prb_.iw,
prb_.kw, prb_.pw, prb_.sw, prb_.dw);
bool check_ih = utils::need_src_or_dst_check(true, prb_.oh,
prb_.ih, prb_.kh, prb_.ph, prb_.sh, prb_.dh);
bool check_id = utils::need_src_or_dst_check(true, prb_.od,
prb_.id, prb_.kd, prb_.pd, prb_.sd, prb_.dd);
bool check_iw_min = check_iw;
bool check_ih_min = check_ih;
bool check_id_min = check_id;
bool check_iw_max = (check_iw || check_ow);
bool check_ih_max = (check_ih || check_oh);
bool check_id_max = (check_id || check_od);
auto &x = view_t::placeholder_var();
if (check_id_min) id_mask &= (x >= 0);
if (check_ih_min) ih_mask &= (x >= 0);
if (check_iw_min) iw_mask &= (x >= 0);
if (check_id_max) id_mask &= (x < prb_.id);
if (check_ih_max) ih_mask &= (x < prb_.ih);
if (check_iw_max) iw_mask &= (x < prb_.iw);
src_view = view_t({mb, g, ic, od, oh, ow, kw}, 6);
src_view.set_vdim(mb, prb_.mb);
src_view.set_vdim(g, prb_.g);
src_view.set_vdim(ic, prb_.ic);
src_view.set_vdim(od, prb_.od);
src_view.set_vdim(oh, prb_.oh);
src_view.set_vdim(ow, prb_.ow);
src_view.set_vdim(kw, prb_.kw);
src_view.set_tdim(0, mb);
src_view.set_tdim(1, g);
src_view.set_tdim(2, ic);
src_view.set_tdim(3, od * prb_.sd - prb_.pd + kd * (1 + prb_.dd), id_mask);
src_view.set_tdim(4, oh * prb_.sh - prb_.ph + kh * (1 + prb_.dh), ih_mask);
src_view.set_tdim(5, ow * prb_.sw - prb_.pw + kw * (1 + prb_.dw), iw_mask);
src_view.set_tlayout(src_layout);
src_view.set_tmasks(cfg_.padded_dims().get().to_string_map());
wei_view = view_t({g, oc, ic, kd, kh, kw}, 6);
wei_view.set_vdim(g, prb_.g);
wei_view.set_vdim(oc, prb_.oc);
wei_view.set_vdim(ic, prb_.ic);
wei_view.set_vdim(kd, prb_.kd);
wei_view.set_vdim(kh, prb_.kh);
wei_view.set_vdim(kw, prb_.kw);
wei_view.set_tdim(0, g);
wei_view.set_tdim(1, oc);
wei_view.set_tdim(2, ic);
wei_view.set_tdim(3, kd);
wei_view.set_tdim(4, kh);
wei_view.set_tdim(5, kw);
wei_view.set_tlayout(wei_layout);
wei_view.set_tmasks(cfg_.padded_dims().get().to_string_map());
dst_view = view_t({mb, g, oc, od, oh, ow}, 6);
dst_view.set_vdim(mb, prb_.mb);
dst_view.set_vdim(g, prb_.g);
dst_view.set_vdim(oc, prb_.oc);
dst_view.set_vdim(od, prb_.od);
dst_view.set_vdim(oh, prb_.oh);
dst_view.set_vdim(ow, prb_.ow);
dst_view.set_tdim(0, mb);
dst_view.set_tdim(1, g);
dst_view.set_tdim(2, oc);
dst_view.set_tdim(3, od);
dst_view.set_tdim(4, oh);
dst_view.set_tdim(5, ow);
dst_view.set_tlayout(dst_layout);
dst_view.set_tmasks(cfg_.padded_dims().get().to_string_map());
if (prb_.with_bias) {
bia_view = view_t({g, oc}, 2);
bia_view.set_vdim(g, prb_.g);
bia_view.set_vdim(oc, prb_.oc);
bia_view.set_tdim(0, g);
bia_view.set_tdim(1, oc);
bia_view.set_tlayout(bia_layout);
bia_view.set_tmasks(cfg_.padded_dims().get().to_string_map());
}
if (prb_.ab_swap_transpose) {
gemm_schedule.set_a_view(dst_view);
gemm_schedule.set_b_view(src_view);
} else {
gemm_schedule.set_a_view(src_view);
gemm_schedule.set_b_view(dst_view);
}
gemm_schedule.set_c_view(wei_view);
gemm_schedule.set_b_vars({g});
if (prb_.ab_swap_transpose) {
gemm_schedule.set_m_vars({oc});
gemm_schedule.set_n_vars({ic, kw});
} else {
gemm_schedule.set_m_vars({ic, kw});
gemm_schedule.set_n_vars({oc});
}
gemm_schedule.set_k_vars({mb, od, oh, ow});
gemm_schedule.for_each_var([&](const expr_t &var) {
dim_t bound = cfg_.padded_dim(pvar_t(var.as<var_t>().name));
gemm_schedule.set_var_bound(var, bound);
});
auto g_tile = create_tile(gemm_schedule, cfg_, g);
auto mb_tile = create_tile(gemm_schedule, cfg_, mb);
auto ic_tile = create_tile(gemm_schedule, cfg_, ic);
auto oc_tile = create_tile(gemm_schedule, cfg_, oc);
auto od_tile = create_tile(gemm_schedule, cfg_, od);
auto oh_tile = create_tile(gemm_schedule, cfg_, oh);
auto ow_tile = create_tile(gemm_schedule, cfg_, ow);
auto kw_tile = create_tile(gemm_schedule, cfg_, kw);
std::vector<expr_t> kernel_grid_vars;
kernel_grid_vars.push_back(oc_tile.grid_idx());
kernel_grid_vars.push_back(od_tile.grid_idx());
kernel_grid_vars.push_back(oh_tile.grid_idx());
kernel_grid_vars.push_back(ow_tile.grid_idx());
kernel_grid_vars.push_back(std::move(kd));
kernel_grid_vars.push_back(std::move(kh));
kernel_grid_vars.push_back(kw_tile.grid_idx());
kernel_grid_vars.push_back(ic_tile.grid_idx());
kernel_grid_vars.push_back(mb_tile.grid_idx());
kernel_grid_vars.push_back(g_tile.grid_idx());
bind_kernel_grid(gemm_schedule, kernel_grid_vars);
bind_thread_group_grid_idx(cfg_, gemm_schedule, oc_tile.tg_idx());
bind_thread_group_grid_idx(cfg_, gemm_schedule, ic_tile.tg_idx());
gemm_schedule.reorder({od_tile.loop_idx(), oh_tile.loop_idx(),
ow_tile.loop_idx(), mb_tile.loop_idx()});
gemm_schedule.unroll(mb_tile.loop_idx(), cfg_.unroll(pvars::mb));
gemm_schedule.unroll(ow_tile.loop_idx(), cfg_.unroll(pvars::ow));
gemm_schedule.tensorize(g_tile.iter_idx());
gemm_schedule.tensorize(oc_tile.iter_idx());
gemm_schedule.tensorize(ic_tile.iter_idx());
gemm_schedule.tensorize(mb_tile.iter_idx());
gemm_schedule.tensorize(ow_tile.iter_idx());
gemm_schedule.tensorize(kw_tile.iter_idx());
}
reorder_plan_t create_reorder_plan(
const dsl::hw_t &hw, const layout_t &src, const layout_t &dst) {
if (src.is_equal_normalized(dst)) return reorder_plan_t();
if ((src.type() == dst.type()
|| (src.type().is_f32() && dst.type().is_tf32()))
&& src.with(dst.type()).is_equal_normalized(dst))
return reorder_plan_t();
reorder_plan_t ret(hw);
ret.src = src;
ret.dst = dst;
return ret;
}
bool reorder_plan_t::can_split(int factor) const {
auto split_src = split(src, factor);
auto split_dst = split(dst, factor);
auto split_src_tile = split_src.tile();
auto split_dst_tile = split_dst.tile();
return ir_utils::is_equal(split_src_tile, split_dst_tile);
}
void reorder_plan_t::set_split(int factor) {
if (!*this) return;
gpu_assert(can_split(factor));
split_factor = factor;
}
stmt_t reorder_plan_t::create_stmt(
const expr_t &src_buf, const expr_t &dst_buf) const {
if (!*this) return stmt_t();
auto split_src = split(src, split_factor);
auto split_dst = split(dst, split_factor);
auto stmt = create_reorder_stmt(split_src, split_dst, src_buf, dst_buf);
return stmt;
}
dim_t reorder_plan_t::src_buf_size() const {
dim_t src_size = utils::div_up(size_bytes(src), split_factor);
return src_size;
}
int reorder_plan_t::estimate_regs() const {
if (!*this) return 0;
dim_t dst_size = utils::div_up(size_bytes(dst), split_factor);
return into<int>(utils::div_up(dst_size, grf_size()));
}
reduce_plan_t create_reduce_plan(const dsl::hw_t &hw, const layout_t &src,
const layout_t &dst, uint32_t mask) {
reduce_plan_t ret(hw);
ret.src = src;
ret.dst = dst;
ret.mask = mask;
return ret;
}
dim_t reduce_plan_t::dst_buf_size() const {
if (!*this) return 0;
dim_t dst_size = utils::div_up(size_bytes(dst), split_factor);
return utils::rnd_up(dst_size, grf_size());
}
bool reduce_plan_t::can_split(int factor) const {
if (!*this) return true;
auto split_src = split(src, factor);
if (split_src.is_empty()) return false;
for (dim_idx_t i = 0; i < src.ndims(); i++) {
if ((mask & (1 << i)) != 0 && split_src.elems(i) != src.elems(i))
return false;
}
return true;
}
void reduce_plan_t::set_split(int factor) {
if (!*this) return;
gpu_assert(can_split(factor));
split_factor = factor;
}
stmt_t reduce_plan_t::create_stmt(
const expr_t &src_buf, const expr_t &dst_buf) const {
if (!*this) return stmt_t();
auto stmt = create_reduce_stmt(src, dst, src_buf, dst_buf, tile_t(), mask);
return stmt;
}
int reduce_plan_t::estimate_regs() const {
if (!*this) return 0;
auto ret = dst_buf_size();
return into<int>(utils::div_up(ret, grf_size()));
}
std::string slm_plan_t::str() const {
ostringstream_t oss;
if (has_a()) {
oss << "a_layout: " << a_layout << std::endl;
oss << a_g2s_load.str("a_g2s_load") << std::endl;
if (a_reorder) oss << a_reorder.str("a_reorder") << std::endl;
oss << a_g2s_store.str("a_g2s_store") << std::endl;
}
if (has_b()) {
oss << "b_layout: " << b_layout << std::endl;
oss << b_g2s_load.str("b_g2s_load") << std::endl;
if (b_reorder) oss << b_reorder.str("b_reorder") << std::endl;
oss << b_g2s_store.str("b_g2s_store") << std::endl;
}
if (x_reduce) { oss << x_reduce.str("x_reduce") << std::endl; }
return add_indent("slm_plan", oss.str());
}
std::string prefetch_plan_t::str() const {
ostringstream_t oss;
if (a_prefetch) oss << a_prefetch.str("a") << std::endl;
if (b_prefetch) oss << b_prefetch.str("b") << std::endl;
return add_indent("prefetch", oss.str());
}
bool x2r_plan_t::can_split(abc_kind_t abc, int factor) const {
if (factor == 1) return true;
auto has_outer_block
= [](const layout_t &layout, dim_t block, const pvar_t &idx = {}) {
if (block == 1) return true;
if (layout.blocks().empty()) return false;
auto &b = layout.blocks().back();
if (!idx.is_undef() && b.idx != idx) return false;
if (b.size % block != 0) return false;
return true;
};
bool is_a = (abc == abc_kind_t::a);
auto &load = (is_a ? a_load : b_load);
auto &reorder = (is_a ? a_reorder : b_reorder);
auto &layout = (is_a ? a_layout : b_layout);
if (!has_outer_block(layout, factor)) return false;
auto &idx = layout.blocks().back().idx;
if (reorder && !has_outer_block(reorder.src, factor, idx)) return false;
if (!load.can_split(factor)) return false;
if (!x_reduce.can_split(factor)) return false;
return true;
}
void x2r_plan_t::set_split(abc_kind_t abc, int factor) {
gpu_assert(can_split(abc, factor));
a_load.set_split(1);
a_reorder.set_split(1);
b_load.set_split(1);
b_reorder.set_split(1);
x_reduce.set_split(1);
split_abc = abc;
split_factor = factor;
switch (abc) {
case abc_kind_t::a:
a_load.set_split(factor);
a_reorder.set_split(factor);
break;
case abc_kind_t::b:
b_load.set_split(factor);
b_reorder.set_split(factor);
x_reduce.set_split(factor);
break;
default: break;
}
}
int x2r_plan_t::estimate_regs(bool reuse_headers) const {
int a_size = a_load.reg_buf_size();
int b_size = b_load.reg_buf_size();
if (a_reorder) a_size += a_load.reg_buf_size();
if (b_reorder) b_size += b_load.reg_buf_size();
int ret = 0;
ret += utils::div_up(a_size, grf_size());
ret += utils::div_up(b_size, grf_size());
ret += a_load.estimate_regs(false, reuse_headers);
ret += b_load.estimate_regs(false, reuse_headers);
ret += x_reduce.estimate_regs();
ret += a_reorder.estimate_regs();
ret += b_reorder.estimate_regs();
return ret;
}
std::string x2r_plan_t::str() const {
ostringstream_t oss;
oss << a_load.str("a_load") << std::endl;
oss << b_load.str("b_load") << std::endl;
if (x_reduce) oss << x_reduce.str("x_reduce") << std::endl;
if (a_reorder) oss << a_reorder.str("a_reorder") << std::endl;
if (b_reorder) oss << b_reorder.str("b_reorder") << std::endl;
oss << "a_layout: " << a_layout << std::endl;
oss << "b_layout: " << b_layout << std::endl;
return add_indent("x2r_plan", oss.str());
}
int get_dpas_block_rcount(const layout_t &layout, const pvar_t &idx) {
if (layout.nblocks() < 2) return 1;
auto &b0 = layout[0];
if (b0.size * layout.type().size() > 32) return 1;
auto &b1 = layout[1];
if (b1.idx != idx) return 1;
int block_rcount = (int)b1.size;
int max_rcount = 8;
if (block_rcount % max_rcount == 0) return max_rcount;
return block_rcount;
}
bool fma_plan_t::can_split(abc_kind_t abc, int factor) const {
if (factor == 1) return true;
bool is_a = (abc == abc_kind_t::a);
bool is_m = is_a;
auto &layout = is_a ? a_layout : b_layout;
dim_idx_t mn_idx = is_a ? 1 : 2;
int dim = (int)layout.elems(mn_idx);
if (dim % factor != 0) return false;
int blk = is_m ? m_blk : n_blk;
if (blk > dim / factor) return false;
auto &blocks = layout.blocks();
if (blocks.empty()) return false;
auto &b = blocks.back();
if (b.idx.index() != mn_idx) return false;
if ((int)b.size % factor != 0) return false;
return true;
}
void fma_plan_t::set_split(abc_kind_t abc, int factor) {
gpu_assert(can_split(abc, factor));
split_abc = abc;
split_factor = factor;
if (abc == abc_kind_t::a
&& utils::one_of(fma_kind, fma_kind_t::dp4a, fma_kind_t::dpas,
fma_kind_t::dpasw)) {
auto blocks = a_layout.blocks();
blocks.back().size /= factor;
auto layout = layout_t(a_layout.type(), blocks);
m_blk = get_dpas_block_rcount(layout, 1);
}
}
int fma_plan_t::a_buf_size() const {
int a_size = into<int>(size_bytes(a_layout));
if (split_abc == abc_kind_t::a)
a_size = utils::div_up(a_size, split_factor);
return utils::rnd_up(a_size, grf_size());
}
int fma_plan_t::b_buf_size() const {
int b_size = into<int>(size_bytes(b_layout));
if (split_abc == abc_kind_t::b)
b_size = utils::div_up(b_size, split_factor);
return utils::rnd_up(b_size, grf_size());
}
int fma_plan_t::bmnk_split_idx(
bmnk_kind_t bmnk, int split_off, bool is_start) const {
dim_t B = a_layout.elems(0);
dim_t M = a_layout.elems(1);
dim_t N = b_layout.elems(2);
dim_t K = a_layout.elems(2);
int start[4] = {0, 0, 0, 0};
dim_t stop[4] = {B, M, N, K};
bool split_a = (split_abc == abc_kind_t::a);
bool split_b = (split_abc == abc_kind_t::b);
bool is_m = (bmnk == bmnk_kind_t::m);
bool is_n = (bmnk == bmnk_kind_t::n);
int factor = 1;
int off = 0;
if ((split_a && is_m) || (split_b && is_n)) {
factor = split_factor;
off = split_off;
}
int i0 = start[(int)bmnk];
int i1 = into<int>(stop[(int)bmnk]);
gpu_assert((i1 - i0) % factor == 0);
int step = (i1 - i0) / factor;
int idx = i0 + off * step;
return is_start ? idx : idx + step;
}
int fma_plan_t::bmnk_start_idx(bmnk_kind_t bmnk, int subtile_idx) const {
return bmnk_split_idx(bmnk, subtile_idx, true);
}
int fma_plan_t::bmnk_stop_idx(bmnk_kind_t bmnk, int subtile_idx) const {
return bmnk_split_idx(bmnk, subtile_idx, false);
}
stmt_t fma_plan_t::create_stmt(ir_context_t &ir_ctx, buffer_manager_t &buf_mgr,
const std::string &a, const std::string &b, const std::string &c,
int subtile_idx, dsl::type_t a_override, dsl::type_t b_override) const {
int c_buf_size = into<int>(size_bytes(c_layout, ir_ctx.grf_size()));
auto a_buf = buf_mgr.get(a);
auto b_buf = buf_mgr.get(b);
auto c_buf = buf_mgr.get(c, c_buf_size);
int b0 = bmnk_start_idx(bmnk_kind_t::b, subtile_idx);
int b1 = bmnk_stop_idx(bmnk_kind_t::b, subtile_idx);
int m0 = bmnk_start_idx(bmnk_kind_t::m, subtile_idx);
int m1 = bmnk_stop_idx(bmnk_kind_t::m, subtile_idx);
int n0 = bmnk_start_idx(bmnk_kind_t::n, subtile_idx);
int n1 = bmnk_stop_idx(bmnk_kind_t::n, subtile_idx);
int k0 = bmnk_start_idx(bmnk_kind_t::k, subtile_idx);
int k1 = bmnk_stop_idx(bmnk_kind_t::k, subtile_idx);
icoord_t a_idx(3);
icoord_t b_idx(3);
icoord_t c_idx(3);
auto fma_funcs = create_fma_funcs(ir_ctx.hw(), a_override, b_override);
stmt_t stmt;
for (int b = b0; b < b1; b += b_blk) {
a_idx[0] = b_idx[0] = c_idx[0] = b;
for (int k = k0; k < k1; k += k_blk) {
a_idx[2] = b_idx[1] = k;
for (int n = n0; n < n1; n += n_blk) {
b_idx[2] = c_idx[2] = n;
for (int m = m0; m < m1; m += m_blk) {
a_idx[1] = c_idx[1] = m;
dim_t a_off = offset_bytes<dim_t>(a_layout, a_idx);
dim_t b_off = offset_bytes<dim_t>(b_layout, b_idx);
dim_t c_off = offset_bytes<dim_t>(c_layout, c_idx);
a_off = a_off % a_buf_size();
b_off = b_off % b_buf_size();
stmt = stmt.append(create_fma_block(fma_funcs, a_buf[a_off],
b_buf[b_off], c_buf[c_off]));
}
}
}
}
return stmt;
}
stmt_t fma_plan_t::create_fma_block(const std::vector<func_t> &fmas,
const expr_t &a, const expr_t &b, const expr_t &c) {
bool is_dpas = fmas[0].is<dpas_t>();
auto src1 = a;
auto src2 = b;
auto dst = c;
if (is_dpas) std::swap(src1, src2);
if (!is_dpas) gpu_assert(fmas.size() == 1);
stmt_t ret;
for (auto &f : fmas) {
ret = ret.append(f.call({dst, dst, src1, src2}));
auto *dpas = f.as_ptr<dpas_t>();
if (is_dpas) {
src2 += dpas->src2_size();
dst += dpas->dst_size();
}
}
return ret;
}
std::vector<func_t> fma_plan_t::create_fma_funcs(const dsl::hw_t &hw,
dsl::type_t a_override, dsl::type_t b_override) const {
auto a = (a_override.is_undef()) ? a_layout : a_layout.with(a_override);
auto b = (b_override.is_undef()) ? b_layout : b_layout.with(b_override);
auto &c = c_layout;
std::vector<func_t> ret;
gpu_assert(a.type().bitsize() == a_layout.type().bitsize());
gpu_assert(b.type().bitsize() == b_layout.type().bitsize());
switch (fma_kind) {
case fma_kind_t::mad: {
int simd = max_bmn_blk();
int a_stride = is_a_broadcast() ? 0 : int(a[0].stride);
int b_stride = is_b_broadcast() ? 0 : int(b[0].stride);
auto mad = mad_t::make(
hw, c.type(), simd, a.type(), a_stride, b.type(), b_stride);
ret.push_back(mad);
break;
}
case fma_kind_t::dp4a:
case fma_kind_t::dpas:
case fma_kind_t::dpasw: {
const int max_rcount = 8;
int block_rcount = m_blk;
int simd = n_blk;
int sdepth = ir_utils::safe_divide(k_blk * a.type().size(), 4);
for (int r = 0; r < block_rcount;) {
int rcount = std::min(max_rcount, block_rcount - r);
auto dpas = dpas_t::make(false, simd,
into<uint8_t>(sdepth), into<uint8_t>(rcount), c.type(),
b.type(), a.type());
ret.push_back(dpas);
r += rcount;
}
break;
}
default: gpu_error_not_expected();
}
return ret;
}
int fma_plan_t::estimate_regs() const {
return into<int>(utils::div_up(size_bytes(c_layout), grf_size()));
}
std::string fma_plan_t::str() const {
ostringstream_t oss;
oss << "a: " << a_layout << std::endl;
oss << "b: " << b_layout << std::endl;
oss << "c: " << c_layout << std::endl;
oss << "c_prb: " << c_prb_layout << std::endl;
oss << "block: ";
int blocks[4] = {b_blk, m_blk, n_blk, k_blk};
for (int i = 0; i < 4; i++) {
if (blocks[i] == 1) continue;
oss << "bmnk"[i] << blocks[i];
}
return add_indent("fma", oss.str());
}
bool plan_t::can_split(abc_kind_t abc, int factor) const {
if (!fma.can_split(abc, factor)) return false;
if (!x2r.can_split(abc, factor)) return false;
if (zp.has_zp_src() && !zp.can_split(abc, factor)) return false;
return true;
}
void plan_t::set_split(abc_kind_t abc, int factor) {
gpu_assert(can_split(abc, factor));
split_abc = abc;
split_factor = factor;
x2r.set_split(abc, factor);
fma.set_split(abc, factor);
if (zp.has_zp_src()) zp.set_split(abc, factor);
}
bool plan_t::uses_2d_load(abc_kind_t abc) const {
auto &send_plan = ((abc == abc_kind_t::a) ? x2r.a_load : x2r.b_load);
return send_plan.send_params().hint_2d.enable;
}
grf_usage_t plan_t::grf_usage() const {
gpu_assert(reserved_regs != -1);
bool with_headers = !reuse_headers;
auto nregs = [&](dim_t size) {
return into<int>(utils::div_up(size, grf_size()));
};
int out_buf_regs = 0;
out_buf_regs += nregs(size_bytes(fma.c_layout));
out_buf_regs += nregs(slm.x_reduce.dst_buf_size());
out_buf_regs += nregs(x2r.x_reduce.dst_buf_size());
int gmem_load_buf_regs = 0;
gmem_load_buf_regs += slm.a_g2s_load.estimate_regs(
true, with_headers, reuse_headers);
gmem_load_buf_regs += slm.b_g2s_load.estimate_regs(
true, with_headers, reuse_headers);
bool use_a_slm = x2r.a_load.send_params().is_slm();
bool use_b_slm = x2r.b_load.send_params().is_slm();
int a_g2r_buf_regs = use_a_slm
? 0
: x2r.a_load.estimate_regs(true,
false,
false);
int b_g2r_buf_regs = use_b_slm
? 0
: x2r.b_load.estimate_regs(true,
false,
false);
if (x2r.a_reorder && x2r.b_reorder) {
gpu_assert(!use_a_slm && !use_b_slm);
gmem_load_buf_regs += std::max(a_g2r_buf_regs, b_g2r_buf_regs);
} else {
if (!x2r.a_load.send_params().is_slm())
gmem_load_buf_regs += a_g2r_buf_regs;
if (!x2r.b_load.send_params().is_slm())
gmem_load_buf_regs += b_g2r_buf_regs;
}
int gmem_load_regs = 0;
gmem_load_regs += prefetch.a_prefetch.estimate_regs(
false, with_headers, reuse_headers);
gmem_load_regs += prefetch.b_prefetch.estimate_regs(
false, with_headers, reuse_headers);
if (!use_a_slm)
gmem_load_regs += x2r.a_load.estimate_regs(
false, with_headers, reuse_headers);
if (!use_b_slm)
gmem_load_regs += x2r.b_load.estimate_regs(
false, with_headers, reuse_headers);
gmem_load_regs += gmem_load_buf_regs;
int slm_store_regs = 0;
slm_store_regs += slm.a_g2s_store.estimate_regs(
false, with_headers, reuse_headers);
slm_store_regs += slm.b_g2s_store.estimate_regs(
false, with_headers, reuse_headers);
int slm_load_regs = 0;
if (use_a_slm)
slm_load_regs += x2r.a_load.estimate_regs(
true, with_headers, reuse_headers);
if (use_b_slm)
slm_load_regs += x2r.b_load.estimate_regs(
true, with_headers, reuse_headers);
int reorder_regs = 0;
reorder_regs += slm.a_reorder.estimate_regs();
reorder_regs += slm.b_reorder.estimate_regs();
reorder_regs += x2r.a_reorder.estimate_regs();
reorder_regs += x2r.b_reorder.estimate_regs();
int reused_header_regs = 0;
if (reuse_headers) {
for (auto *sp : {&prefetch.a_prefetch, &prefetch.b_prefetch,
&x2r.a_load, &x2r.b_load}) {
reused_header_regs = std::max(
reused_header_regs, sp->estimate_regs(false, true, true));
}
}
int zp_regs = zp.estimate_regs();
grf_usage_t info(grf_size());
info.add(grf_usage_label_t::out_buf, out_buf_regs);
info.add(grf_usage_label_t::gmem_load, gmem_load_regs);
info.add(grf_usage_label_t::slm_store, slm_store_regs);
info.add(grf_usage_label_t::slm_load, slm_load_regs);
info.add(grf_usage_label_t::reorder, reorder_regs);
info.add(grf_usage_label_t::reused_headers, reused_header_regs);
info.add(grf_usage_label_t::reserved, reserved_regs);
info.add(grf_usage_label_t::zero_points, zp_regs);
return info;
}
void plan_t::reset() {
slm = slm_plan_t(hw);
prefetch = prefetch_plan_t(hw);
x2r = x2r_plan_t(hw);
fma = fma_plan_t(hw);
split_abc = abc_kind_t::undef;
split_factor = 1;
reuse_headers = false;
max_gmem_bufs = 1;
}
std::string plan_t::str() const {
using intel::jit::ir_utils::to_string;
using intel::jit::operator<<;
ostringstream_t oss;
if (slm) oss << slm << std::endl;
if (prefetch) oss << prefetch << std::endl;
oss << x2r << std::endl;
oss << fma << std::endl;
if (zp) oss << zp << std::endl;
oss << "a_can_split (2): " << to_string(can_split(abc_kind_t::a, 2))
<< std::endl;
oss << "a_can_split (4): " << to_string(can_split(abc_kind_t::a, 4))
<< std::endl;
oss << "b_can_split (2): " << to_string(can_split(abc_kind_t::b, 2))
<< std::endl;
oss << "b_can_split (4): " << to_string(can_split(abc_kind_t::b, 4))
<< std::endl;
if (split_abc != abc_kind_t::undef)
oss << "split: " << split_abc << split_factor << std::endl;
oss << "reuse_headers: " << to_string(reuse_headers) << std::endl;
oss << grf_usage() << std::endl;
return jit::add_indent("conv_plan", oss.str());
}
dsl::type_t get_accumulation_type(
const config_t &cfg, const dsl::type_t &a, const dsl::type_t &b) {
if (a.is_int()) return dsl::type_t::s32();
if (a.is_f64()) return dsl::type_t::f64();
if (cfg.fma_kind() == fma_kind_t::mad && a.is_f16() && b.is_f16()
&& !cfg.prb().is_bwd_w) {
return dsl::type_t::f16();
}
return dsl::type_t::f32();
}
layout_t make_with_block(const layout_t &base, const layout_t &inner) {
gpu_assert(base.type() == inner.type());
auto cur_tile = base.tile();
tile_t rem_tile;
for (auto &d : cur_tile)
rem_tile[d] = ir_utils::safe_divide(cur_tile.at(d), inner.elems(d));
auto ret = base.with(inner.blocks());
for (auto &b : base.blocks()) {
auto &d = cur_tile[b.idx];
auto &r = rem_tile[b.idx];
d = ir_utils::safe_divide(d, b.size);
if (r <= d) continue;
auto blk = ir_utils::safe_divide(r, d);
ret = ret.with_block({b.idx, blk});
r = ir_utils::safe_divide(r, blk);
}
for (auto &d : rem_tile)
gpu_assert(rem_tile[d] == 1);
return ret;
}
struct fma_layout_hint_t {
int vec_dim_idx = -1;
bool is_empty() const { return vec_dim_idx == -1; }
};
struct fma_context_t {
fma_context_t(const config_t &cfg)
: hw(cfg.hw())
, simd(cfg.simd())
, vec_size(cfg.vec_size())
, fma(cfg.fma_kind())
, a_type(to_ir(cfg.prb().a_data_type))
, b_type(to_ir(cfg.prb().b_data_type))
, acc_type(get_accumulation_type(cfg, a_type, b_type))
, is_src1_broadcast(!cfg.prb().is_dw)
, ab_swap_transpose_(cfg.prb().ab_swap_transpose) {}
fma_layout_hint_t &layout_hint(abc_kind_t abc) {
return (abc == abc_kind_t::a) ? a_layout_hint : b_layout_hint;
}
const fma_layout_hint_t &layout_hint(abc_kind_t abc) const {
return (abc == abc_kind_t::a) ? a_layout_hint : b_layout_hint;
}
layout_t maybe_retype_layout_for_mad(
bool is_a, const layout_t &layout) const {
if (layout.type().is_x8())
return make_strided(layout.with(dsl::type_t::s16()), 2);
if (a_type.is_f16() && acc_type.is_f16()) {
return layout.make_dense();
}
if (layout.type().is_bf16() && !hw.systolic_support())
return layout.with(dsl::type_t::f32()).make_dense();
if (a_type.is_bf16()) {
if (is_a && is_src1_broadcast)
return layout.with(dsl::type_t::f32()).make_dense();
if (is_a) return layout.make_dense();
return layout.with(dsl::type_t::f32()).make_dense();
}
bool is_a_xf8_or_xf16_or_xf4 = (a_type.is_fp4() || a_type.is_fp8()
|| a_type.is_bf16() || a_type.is_f16());
bool is_b_xf8_or_xf16_or_xf4 = (b_type.is_fp4() || b_type.is_fp8()
|| b_type.is_bf16() || b_type.is_f16());
if (is_a_xf8_or_xf16_or_xf4 || is_b_xf8_or_xf16_or_xf4) {
return layout.with(dsl::type_t::f32()).make_dense();
}
return layout;
}
layout_t get_fma_friendly_layout(abc_kind_t abc,
const bmnk_mapper_t &mapper, const layout_t &layout) const {
bool is_mad = (fma == fma_kind_t::mad);
bool is_dpas = is_dp_fma(fma);
bool is_a = (abc == abc_kind_t::a);
auto type = (is_a ? a_type : b_type);
bool cvt_f16 = ((hw < ngen::HW::XE3P_35_10 && layout.type().is_fp8())
|| (hw < ngen::HW::XE3P_35_11 && layout.type().is_fp4()));
int type_size = (cvt_f16 ? 2 : type.size());
if (is_dpas) {
int sdepth = 8;
int dword_size = 4;
std::vector<layout_block_t> blocks;
auto bmnks = get_bmnk_kinds(abc);
if (is_a) {
int k_blk = sdepth * dword_size / type_size;
blocks.emplace_back(1, k_blk);
} else {
int k_blk0 = dword_size / type_size;
int n_blk = simd;
int k_blk1 = sdepth;
blocks.emplace_back(0, k_blk0);
blocks.emplace_back(1, n_blk);
blocks.emplace_back(0, k_blk1);
}
auto bmnk_layout
= mapper.map_to_bmnk(abc, bmnks, layout).with(type);
auto fma_layout
= make_with_block(bmnk_layout, layout_t(type, blocks));
auto abc_layout
= mapper.map_from_bmnk(abc, bmnks, fma_layout, layout);
if (cvt_f16) return abc_layout.with(dsl::type_t::f16());
return abc_layout;
}
if (is_mad) {
auto ret = maybe_retype_layout_for_mad(is_a, layout);
auto &hint = layout_hint(abc);
if (hint.is_empty()) return ret;
std::vector<layout_block_t> blocks;
blocks.emplace_back(hint.vec_dim_idx, vec_size);
auto bmnks = get_bmnk_kinds(abc, true);
auto bmnk_layout = mapper.map_to_bmnk(abc, bmnks, ret);
auto fma_layout = make_with_block(
bmnk_layout, layout_t(ret.type(), blocks));
auto abc_layout = mapper.map_from_bmnk(abc, bmnks, fma_layout, ret);
if (layout.type().is_x8()) {
gpu_assert(abc_layout.type().is_s16());
abc_layout = make_strided(abc_layout, 2);
}
return abc_layout;
}
gpu_error_not_expected();
return layout;
}
layout_t get_fma_friendly_layout(abc_kind_t abc,
const bmnk_mapper_t &mapper, const view_t &view) const {
return get_fma_friendly_layout(
abc, mapper, view.create_dense_vlayout());
}
static int get_vec_idx(abc_kind_t abc, bmnk_kind_t bmnk) {
gpu_assert(utils::one_of(abc, abc_kind_t::a, abc_kind_t::b));
bool is_a = (abc == abc_kind_t::a);
switch (bmnk) {
case bmnk_kind_t::b: return 0;
case bmnk_kind_t::m: return is_a ? 1 : -1;
case bmnk_kind_t::n: return is_a ? -1 : 2;
default: gpu_error_not_expected();
}
return -1;
}
static std::vector<bmnk_kind_t> get_bmnk_kinds(
abc_kind_t abc, bool with_batch = false) {
std::vector<bmnk_kind_t> ret;
if (with_batch) ret.push_back(bmnk_kind_t::b);
switch (abc) {
case abc_kind_t::a:
ret.push_back(bmnk_kind_t::m);
ret.push_back(bmnk_kind_t::k);
break;
case abc_kind_t::b:
ret.push_back(bmnk_kind_t::k);
ret.push_back(bmnk_kind_t::n);
break;
default: gpu_error_not_expected();
}
return ret;
}
bool can_vectorize_by(
bmnk_kind_t bmnk, const layout_t &a, const layout_t &b) const {
int a_idx = get_vec_idx(abc_kind_t::a, bmnk);
int b_idx = get_vec_idx(abc_kind_t::b, bmnk);
return is_mad_compatible(a, b, a_idx, b_idx);
}
bool is_mad_compatible(const layout_t &a, const layout_t &b, int a_vec_idx,
int b_vec_idx) const {
auto is_blocked_by
= [](const layout_t &layout, const pvar_t &dim, int block) {
if (block == 1) return true;
if (layout.nblocks() == 0) return false;
auto check = [&](const layout_block_t &b) {
bool ok = true;
ok &= (b.idx == dim);
ok &= (b.size % block == 0);
bool stride_ok = (b.stride == 1)
|| (layout.type().is_x16() && b.stride == 2);
ok &= stride_ok;
return ok;
};
auto &b0 = layout[0];
if (check(b0)) return true;
if (layout.nblocks() > 1) {
auto &b1 = layout[1];
if (b1.stride == b0.stride && check(b1)) return true;
}
return false;
};
if (a_vec_idx != -1 && !is_blocked_by(a, a_vec_idx, vec_size))
return false;
if (b_vec_idx != -1 && !is_blocked_by(b, b_vec_idx, vec_size))
return false;
return true;
}
void set_layout_hints(const layout_t &a, const layout_t &b) {
a_layout_hint.vec_dim_idx = -1;
b_layout_hint.vec_dim_idx = -1;
for (auto bmnk : {bmnk_kind_t::b, bmnk_kind_t::n, bmnk_kind_t::m}) {
int a_idx = get_vec_idx(abc_kind_t::a, bmnk);
int b_idx = get_vec_idx(abc_kind_t::b, bmnk);
if (a_idx != -1 && a.elems(a_idx) % vec_size != 0) continue;
if (b_idx != -1 && b.elems(b_idx) % vec_size != 0) continue;
if (a_idx != -1) a_layout_hint.vec_dim_idx = a_idx;
if (b_idx != -1) b_layout_hint.vec_dim_idx = b_idx;
break;
}
}
dsl::hw_t hw;
int simd;
int vec_size;
fma_kind_t fma;
dsl::type_t a_type;
dsl::type_t b_type;
dsl::type_t acc_type;
bool is_src1_broadcast;
bool ab_swap_transpose_;
fma_layout_hint_t a_layout_hint;
fma_layout_hint_t b_layout_hint;
};
int slm_memory_bank_count(ngen::HW hw) {
switch (hw) {
case ngen::HW::XeHP: return 65;
case ngen::HW::XeHPG: return 32;
default: gpu_error_not_expected();
}
return 0;
}
int slm_memory_bank_granularity(ngen::HW hw) {
switch (hw) {
case ngen::HW::XeHP: return 4;
case ngen::HW::XeHPG: return 8;
default: gpu_error_not_expected();
}
return 0;
}
dim_t find_min_stride_without_conflicts(
const dsl::hw_t &hw, dim_t inner_bytes, dim_t dense_stride_bytes) {
int write_step = 64;
int stride_step = 16;
dim_t stride_beg = dense_stride_bytes;
dim_t stride_end = 2 * dense_stride_bytes;
const int slm_banks = slm_memory_bank_count(hw);
const int bank_granularity = slm_memory_bank_granularity(hw);
for (dim_t s = stride_beg; s < stride_end; s += stride_step) {
bool ok = true;
for (dim_t off0 = 0; off0 < inner_bytes; off0 += write_step) {
std::vector<bool> found(slm_banks, false);
for (dim_t off = off0; off < off0 + write_step;
off += bank_granularity) {
int bank0 = (off / bank_granularity) % slm_banks;
int bank1 = ((off + s) / bank_granularity) % slm_banks;
if (found[bank0]) {
ok = false;
break;
}
found[bank0] = true;
if (found[bank1]) {
ok = false;
break;
}
found[bank1] = true;
}
if (ok) return s;
}
}
gpu_warning() << "Couldn't find stride without conflicts for SLM padding.";
return dense_stride_bytes;
}
layout_t split_into_multi_blocks(
const layout_t &layout, const std::vector<dim_t> &multi_blocks) {
if (layout.is_empty()) return layout;
layout_t tmp(layout);
std::vector<dim_t> rem_elems = multi_blocks;
std::vector<dim_t> cur_elems(rem_elems.size(), 1);
for (auto &b : tmp.blocks()) {
for (int i = 0; i < int(rem_elems.size()); i++) {
auto &e = rem_elems[i];
if (e == 1) continue;
if (b.size > e) {
dim_t next_block = utils::max_div(b.size, e);
if (next_block == 1) return layout_t();
return split_into_multi_blocks(
tmp.split_block(b, next_block, b.size / next_block),
multi_blocks);
}
if (e % b.size != 0) return layout_t();
e /= b.size;
cur_elems[i] *= b.size;
break;
}
}
for (int i = 0; i < int(cur_elems.size()); i++) {
if (cur_elems[i] != multi_blocks[i]) { return layout_t(); }
}
return tmp;
}
layout_t pad_slm_layout(
const dsl::hw_t &hw, const layout_t &layout, const grid_info_t &grid) {
if (hw >= ngen::HW::XeHPC || hw <= ngen::HW::XeLP) return layout;
auto tg_dim0 = grid.dim(0);
auto tg_dim1 = grid.dim(1);
int type_size = layout.type().size();
if (layout.elems() % tg_dim0) return layout_t();
dim_t inner_block = layout.elems() / tg_dim0;
if ((inner_block * type_size) % tg_dim1 != 0) return layout_t();
dim_t per_thr_bytes = (inner_block * type_size) / tg_dim1;
std::vector<dim_t> multi_blocks = {inner_block, tg_dim0};
auto l = split_into_multi_blocks(layout, multi_blocks);
if (l.is_empty()) {
gpu_warning() << "Couldn't split layout for SLM padding.";
return layout;
}
auto padded_blocks = l.blocks();
stride_t stride;
dim_t remaining_elems = inner_block;
bool past_inner_block = remaining_elems == 1;
for (auto &b : padded_blocks) {
if (past_inner_block) {
if (stride.is_undefined()) {
dim_t stride_bytes = find_min_stride_without_conflicts(
hw, per_thr_bytes, dim_t(b.stride) * type_size);
gpu_assert(stride_bytes % type_size == 0);
stride = stride_bytes / type_size;
}
b.stride = stride;
stride = b.stride * b.size;
continue;
}
gpu_assert(remaining_elems % b.size == 0);
remaining_elems /= b.size;
if (remaining_elems == 1) past_inner_block = true;
}
return layout.with(padded_blocks);
}
layout_t get_slm_layout(const fma_context_t &fma_ctx, abc_kind_t abc,
const bmnk_mapper_t &mapper, const view_t &tg_view,
const grid_info_t &grid) {
auto layout = fma_ctx.get_fma_friendly_layout(abc, mapper, tg_view);
layout = pad_slm_layout(fma_ctx.hw, layout, grid);
return layout;
}
struct reduce_mask_t {
reduce_mask_t() = default;
reduce_mask_t(uint32_t mask) : enable(true), mask(mask) {}
explicit operator bool() const { return enable; }
bool has(int idx) const { return (mask & (1 << idx)) != 0; }
bool enable = false;
uint32_t mask;
};
bool do_reduce(const config_t &cfg, abc_kind_t abc) {
auto &prb = cfg.prb();
return ((abc == abc_kind_t::b && !prb.ab_swap_transpose)
|| (abc == abc_kind_t::a && prb.ab_swap_transpose))
&& prb.is_bwd_w && prb.with_bias;
}
reduce_mask_t reduce_mask(const config_t &cfg, abc_kind_t abc) {
if (!do_reduce(cfg, abc)) return reduce_mask_t();
return reduce_mask_t((1 << 1) | (1 << 2));
}
std::vector<size_t> get_reduce_dim_map(uint32_t mask, size_t &ndims) {
std::vector<size_t> ret(ndims, dim_idx::invalid);
size_t dst_idx = 0;
for (size_t i = 0; i < ndims; i++) {
if ((mask & (1 << i)) == 0) continue;
ret[i] = dst_idx++;
}
ndims = dst_idx;
return ret;
}
tile_coord_t to_reduce_tensor(
size_t ndims, const tile_coord_t &tile_coord, uint32_t mask) {
size_t reduce_ndims = ndims;
auto map = get_reduce_dim_map(mask, reduce_ndims);
tile_t reduce_dims;
coord_t reduce_start;
for (size_t i = 0; i < ndims; i++) {
if (map[i] == dim_idx::invalid) continue;
reduce_dims[map[i]] = tile_coord.tile.get(i);
reduce_start[map[i]] = tile_coord.coord.get(i);
}
return tile_coord_t(reduce_dims, reduce_start);
}
layout_t to_reduce_layout(
const config_t &cfg, const layout_t &layout, uint32_t mask) {
size_t reduce_ndims = layout.ndims();
auto map = get_reduce_dim_map(mask, reduce_ndims);
std::vector<layout_block_t> reduce_blocks;
for (auto &b : layout.blocks()) {
if (map[b.idx] == dim_idx::invalid) continue;
auto bb = b;
bb.idx = map[b.idx];
reduce_blocks.push_back(bb);
}
auto type = get_accumulation_type(cfg, layout.type(), layout.type());
return layout_t(type, reduce_blocks, 0, reduce_ndims).make_dense();
}
class direct_view_t {
public:
direct_view_t() = default;
direct_view_t(const view_t &view, bool active)
: view_(view), active_(active) {
if (!check_tdims(fused_tidx_)) return;
if (!check_masks()) return;
init_direct_view();
}
explicit operator bool() const {
return active_ && !direct_view_.is_empty();
}
const view_t &get() const { return direct_view_; }
layout_t transform(const layout_t &layout) const {
gpu_assert((bool)*this);
gpu_assert(fused_tidx_ != dim_idx::invalid);
std::vector<layout_block_t> blocks;
bool seen = false;
for (auto &b : layout.blocks()) {
if (b.size == 1) continue;
auto &tdim = view_.tdim(b.idx);
if (b.idx.index() != fused_tidx_) {
auto vb = b;
vb.idx = tdim.vidx(0);
blocks.push_back(vb);
continue;
}
if (seen) return layout_t();
seen = true;
for (dim_idx_t i = 0; i < tdim.nvargs(); i++) {
int vidx = tdim.vidx(i);
dim_t vstride = (dim_t)tdim.vstride(i);
auto vb = b;
vb.idx = vidx;
vb.size = view_.vdims()[vidx];
vb.stride = b.stride * vstride;
blocks.push_back(vb);
}
}
return layout_t(layout.type(), blocks, 0, view_.nvdims());
}
private:
bool check_tdims(dim_idx_t &fused_tidx) const {
int nfused = 0;
for (dim_idx_t tidx = 0; tidx < view_.ntdims(); tidx++) {
auto &tdim = view_.tdim(tidx);
if (tdim.is_identity()) continue;
int nvars = 0;
for (dim_idx_t i = 0; i < tdim.nvargs(); i++) {
auto vdim = view_.vdims()[tdim.vidx(i)];
if (vdim == 1) continue;
if (tdim.vstride(i).is_unknown()) return false;
nvars++;
}
if (nvars >= 2) {
nfused++;
fused_tidx = tidx;
}
}
if (nfused != 1) return false;
return true;
}
bool check_masks() const {
for (dim_idx_t tidx = 0; tidx < view_.ntdims(); tidx++) {
auto &tdim = view_.tdim(tidx);
for (auto &v : view_.vvars())
if (contains_object(tdim.mask(), v)) return false;
}
return true;
}
static std::vector<expr_t> create_vvars(int nvdims) {
static const int max_nvdims = 128;
static thread_local std::vector<expr_t> _vvars([] {
std::vector<expr_t> ret;
ret.reserve(max_nvdims);
for (int i = 0; i < max_nvdims; i++)
ret.push_back(var_t::make(
dsl::type_t::s32(), "_" + std::to_string(i)));
return ret;
}());
gpu_assert(nvdims <= max_nvdims) << "Too many dimensions: " << nvdims;
return std::vector<expr_t>(_vvars.begin(), _vvars.begin() + nvdims);
}
void init_direct_view() {
std::vector<dim_t> tdim_extents(view_.ntdims());
std::vector<expr_t> tdim_starts(view_.ntdims());
for (dim_idx_t tidx = 0; tidx < view_.ntdims(); tidx++) {
auto &tdim = view_.tdim(tidx);
auto &textent = tdim_extents[tidx];
auto &tstart = tdim_starts[tidx];
if (tdim.is_identity()) {
textent = view_.vdims()[tdim.vidx(0)];
tstart = view_.vstart()[tdim.vidx(0)];
continue;
}
textent = 1;
tstart = tdim.expr();
for (dim_idx_t i = 0; i < tdim.nvargs(); i++) {
auto vidx = tdim.vidx(i);
auto vdim = view_.vdims()[vidx];
textent += (dim_t)tdim.vstride(i) * (vdim - 1);
tstart = substitute(
tstart, view_.vvar(vidx), view_.vstart()[vidx]);
}
}
direct_view_ = view_t(create_vvars(view_.ntdims()), view_.ntdims());
direct_view_.set_tlayout(view_.tlayout());
for (dim_idx_t tidx = 0; tidx < view_.ntdims(); tidx++) {
auto &tdim = view_.tdim(tidx);
direct_view_.set_vdim(direct_view_.vvars()[tidx],
tdim_extents[tidx], tdim_starts[tidx]);
direct_view_.set_tdim(
tidx, direct_view_.vvars()[tidx], tdim.mask());
}
}
view_t view_;
dim_idx_t fused_tidx_ = -1;
view_t direct_view_;
bool active_ = false;
};
layout_t add_batch(const layout_t &layout) {
auto blocks = layout.blocks();
for (auto &b : blocks) {
b.idx = b.idx + 1;
}
return layout.with(blocks);
}
std::array<pvar_t, 2> dpas_dims(bool transpose) {
if (transpose) return {1, 0};
return {0, 1};
}
bool is_dpas_src1_compatible(int simd, bool transpose, const layout_t &layout) {
const int sdepth = 8;
auto &type = layout.type();
auto c_type = (type.is_int() ? dsl::type_t::s32() : dsl::type_t::f32());
auto func = dpas_t::make(
false, simd, sdepth, 1, c_type, type, type);
auto &dpas = func.as<dpas_t>();
auto src1_layout = dpas.a_layout(dpas_dims(transpose));
src1_layout = add_batch(src1_layout);
return src1_layout <= layout;
}
bool is_dpas_src2_compatible(int simd, bool transpose, const layout_t &layout) {
const int sdepth = 8;
auto &type = layout.type();
auto c_type = (type.is_int() ? dsl::type_t::s32() : dsl::type_t::f32());
auto func = dpas_t::make(
false, simd, sdepth, 1, c_type, type, type);
auto &dpas = func.as<dpas_t>();
auto src2_layout = dpas.b_layout(dpas_dims(transpose));
src2_layout = add_batch(src2_layout);
return src2_layout <= layout;
}
layout_t get_c_layout(const layout_t &a_layout, const layout_t &b_layout,
const layout_t &c_blk_layout) {
std::vector<layout_block_t> blocks;
const bmnk_kind_t a_bmnks[3]
= {bmnk_kind_t::b, bmnk_kind_t::m, bmnk_kind_t::k};
const bmnk_kind_t b_bmnks[3]
= {bmnk_kind_t::b, bmnk_kind_t::k, bmnk_kind_t::n};
for (auto &b : a_layout.blocks()) {
if (a_bmnks[b.idx] == bmnk_kind_t::k) continue;
blocks.push_back(b);
}
for (auto &b : b_layout.blocks()) {
if (utils::one_of(b_bmnks[b.idx], bmnk_kind_t::b, bmnk_kind_t::k))
continue;
blocks.push_back(b);
}
layout_t c_layout(c_blk_layout.type(), blocks, 0, c_blk_layout.ndims());
c_layout = make_with_block(c_layout, c_blk_layout);
return c_layout;
}
enum class plan_status_t {
success,
error,
ab_layout_vnni_mismatch,
ab_layout_k_blocks_mismatch,
invalid_fma_layout,
invalid_slm_send,
out_of_registers,
invalid_slm_k_slicing,
invalid_slm_layout,
invalid_direct_view,
invalid_c_layout,
};
#define PLAN_CHECK(status) \
do { \
if ((status) != plan_status_t::success) return (status); \
} while (false)
class plan_builder_t {
public:
plan_builder_t(config_t &cfg)
: cfg_(cfg)
, prb_(cfg.prb())
, plan_ptr_(get_plan())
, plan_(*plan_ptr_)
, gemm_schedule_(plan_.gemm_schedule)
, fma_ctx_(cfg)
, allow_slm_(cfg.hw() >= ngen::HW::XeLP) {}
status_t init_plan() {
plan_status_t status;
status = try_init_plan();
if (status == plan_status_t::success) return status::success;
switch (status) {
case plan_status_t::invalid_fma_layout:
for (auto abc : {abc_kind_t::a, abc_kind_t::b}) {
auto &hint = fma_ctx_.layout_hint(abc);
if (!hint.is_empty() && plan_.uses_2d_load(abc)) {
hint.vec_dim_idx = -1;
enable_send_2d(abc, false);
status = try_init_plan();
if (status == plan_status_t::success) break;
}
}
if (!fma_ctx_.a_layout_hint.is_empty()
|| !fma_ctx_.b_layout_hint.is_empty())
status = try_init_plan();
break;
case plan_status_t::ab_layout_vnni_mismatch:
for (auto abc : {abc_kind_t::a, abc_kind_t::b}) {
if (plan_.uses_2d_load(abc)) {
enable_send_2d(abc, false);
status = try_init_plan();
if (status == plan_status_t::success) break;
}
}
break;
default: break;
}
if (status == plan_status_t::success) return status::success;
if ((use_slm(abc_kind_t::a) || use_slm(abc_kind_t::b))
&& !cfg_.slm().is_overridden()) {
gpu_trace() << "Retry plan initialization without SLM";
enable_slm(false);
status = try_init_plan();
if (status == plan_status_t::success) return status::success;
}
if (a_direct_view_ || b_direct_view_) {
gpu_trace() << "Retry plan initialization without direct view";
enable_direct_view(false);
status = try_init_plan();
if (status == plan_status_t::success) return status::success;
}
return status::runtime_error;
}
private:
void set_plan() {
auto &slm = plan_.slm;
if (slm) {
int gmem_buf_size = 0;
gmem_buf_size += slm.a_g2s_load.estimate_regs(
true, false,
plan_.reuse_headers);
gmem_buf_size += slm.b_g2s_load.estimate_regs(
true, false,
plan_.reuse_headers);
int bound = cfg_.regs() - 5;
int free = std::max(0, bound - plan_.grf_usage().total());
plan_.max_gmem_bufs
= gmem_buf_size == 0 ? 0 : 1 + free / gmem_buf_size;
}
gpu_trace() << plan_;
cfg_.set_plan(plan_ptr_);
}
plan_status_t try_init_plan() {
plan_.reset();
plan_.reserved_regs = cfg_.reserved_regs();
PLAN_CHECK(init_x_g2r_direct_view(gemm_schedule_.a_tg_view(),
gemm_schedule_.a_thr_tile_coord(), a_direct_view_));
PLAN_CHECK(init_x_g2r_direct_view(gemm_schedule_.b_tg_view(),
gemm_schedule_.b_thr_tile_coord(), b_direct_view_));
PLAN_CHECK(init_slm_plan(plan_.slm));
PLAN_CHECK(init_prefetch_plan(plan_.prefetch));
PLAN_CHECK(init_x2r_plan(plan_.slm, plan_.x2r));
PLAN_CHECK(init_fma_plan(plan_.x2r, fma_ctx_, plan_.fma));
PLAN_CHECK(init_zp_plan(plan_.x2r, plan_.fma, plan_.zp));
if (cfg_.subtiles().is_env_overridden()) {
int a = cfg_.subtiles().a();
int b = cfg_.subtiles().b();
if (a > 1) plan_.set_split(abc_kind_t::a, a);
if (b > 1) plan_.set_split(abc_kind_t::b, b);
}
if (cfg_.pipeline().is_env_overridden()) {
plan_.reuse_headers = cfg_.pipeline().reuse_headers();
}
PLAN_CHECK(fixup_grf_usage(plan_));
auto k_tile = plan_.fma.bmnk_stop_idx(bmnk_kind_t::k, 0)
- plan_.fma.bmnk_start_idx(bmnk_kind_t::k, 0);
auto is_dpas = utils::one_of(
plan_.fma.fma_kind, fma_kind_t::dpas, fma_kind_t::dpasw);
auto k_blk_dpas_aligned
= plan_.fma.m_blk == 1 || plan_.fma.k_blk == k_tile;
if (cfg_.hw() >= ngen::HW::XE3P_35_10 && is_dpas
&& plan_.fma.m_blk % 2 != 0 && !k_blk_dpas_aligned)
return plan_status_t::invalid_fma_layout;
set_plan();
return plan_status_t::success;
}
plan_status_t fixup_grf_usage(plan_t &plan) const {
int tmp_regs = 5;
int bound = cfg_.regs() - tmp_regs;
if (plan.grf_usage().total() < bound) return plan_status_t::success;
if (cfg_.options().hw().large_grf_support() && cfg_.regs() <= 128
&& default_regs(cfg_) != 128)
return plan_status_t::out_of_registers;
plan_status_t status;
status = try_apply_ab_split(plan, bound);
if (status == plan_status_t::success) return plan_status_t::success;
status = try_reuse_headers(plan, bound);
if (status == plan_status_t::success) return plan_status_t::success;
status = try_drop_prefetch(plan, bound);
if (status == plan_status_t::success) return plan_status_t::success;
return plan_status_t::out_of_registers;
}
plan_status_t try_apply_ab_split(plan_t &plan, int bound) const {
if (cfg_.subtiles().is_env_overridden())
return plan_status_t::out_of_registers;
int min_regs = plan.grf_usage().total();
auto min_split = std::make_pair(abc_kind_t::undef, 1);
for (int factor : {2, 4}) {
for (abc_kind_t abc : {abc_kind_t::a, abc_kind_t::b}) {
if (plan.can_split(abc, factor)) {
plan.set_split(abc, factor);
int regs = plan.grf_usage().total();
if (regs < bound) return plan_status_t::success;
if (regs < min_regs) {
min_split = std::make_pair(abc, factor);
min_regs = regs;
}
}
}
}
auto abc = min_split.first;
auto factor = min_split.second;
plan.set_split(abc, factor);
return plan_status_t::out_of_registers;
}
plan_status_t try_reuse_headers(plan_t &plan, int bound) const {
if (cfg_.pipeline().is_env_overridden())
return plan_status_t::out_of_registers;
plan.reuse_headers = true;
if (plan.grf_usage().total() < bound) return plan_status_t::success;
return plan_status_t::out_of_registers;
}
plan_status_t try_drop_prefetch(plan_t &plan, int bound) const {
if (cfg_.prefetch().is_env_overridden())
return plan_status_t::out_of_registers;
auto &prefetch = plan.prefetch;
if (prefetch.a_prefetch) {
prefetch.a_prefetch = send_plan_t();
if (plan.grf_usage().total() < bound) return plan_status_t::success;
}
if (prefetch.b_prefetch) {
prefetch.b_prefetch = send_plan_t();
if (plan.grf_usage().total() < bound) return plan_status_t::success;
}
return plan_status_t::out_of_registers;
}
plan_status_t init_x_g2r_direct_view(const view_t &tg_view,
const tile_coord_t &thr_tile_coord,
direct_view_t &direct_view) const {
auto gmem_view = tg_view.create_sub_view(thr_tile_coord);
direct_view = direct_view_t(gmem_view, allow_direct_view_);
return plan_status_t::success;
}
bool use_prefetch(abc_kind_t abc) const {
auto &prb = cfg_.prb();
bool is_a = (abc == abc_kind_t::a);
if (cfg_.prefetch().is_overridden()) {
return is_a ? cfg_.prefetch().a() : cfg_.prefetch().b();
}
if (cfg_.hw() < ngen::HW::XeHPC) return false;
if (!cfg_.is_dpas_or_dpasw_fma()) return false;
if (is_a && !prb.is_bwd_d && !prb.ab_swap_transpose && is_small_ic(prb)
&& cfg_.is_dp_fma())
return false;
bmnk_dim_helper_t h(cfg_);
dim_t k_tg = h.thread_group_dim(pvars::k);
if (k_tg != 1) return false;
return true;
}
bool use_slm(abc_kind_t abc) const {
auto &prb = cfg_.prb();
bool is_a = (abc == abc_kind_t::a);
if (cfg_.bufs_hint() == 0) return false;
if (cfg_.slm().is_overridden()) {
return is_a ? cfg_.slm().a() : cfg_.slm().b();
}
if (prb.is_bwd_w && prb.with_bias && prb.ab_swap_transpose)
return false;
if (!allow_slm_) return false;
if (cfg_.hw() >= ngen::HW::XeHPC) return false;
auto &tg = cfg_.thread_group_grid();
int tg_idx = (is_a ? 0 : 1);
if (tg[tg_idx] == 1) return false;
auto &direct_view = (is_a ? a_direct_view_ : b_direct_view_);
if ((bool)direct_view) return false;
return true;
}
plan_status_t init_x_slm_plan(abc_kind_t abc, const view_t &tg_view,
layout_t &slm_layout, grid_info_t &grid, send_plan_t &g2s_load,
send_plan_t &g2s_store, reorder_plan_t &reorder,
reduce_mask_t reduce_mask = reduce_mask_t(),
reduce_plan_t *reduce = nullptr,
tile_coord_t *reduce_tile_coord = nullptr) const {
if (!use_slm(abc)) return plan_status_t::success;
auto &tg = cfg_.thread_group_grid();
slm_layout = get_slm_layout(
fma_ctx_, abc, gemm_schedule_.bmnk_mapper(), tg_view, tg);
if (slm_layout == layout_t()) return plan_status_t::invalid_slm_layout;
auto thr_tile_coord = split(slm_layout, tg, &grid);
auto abs_thr_tile_coord = tg_view.vtile_coord().sub(thr_tile_coord);
auto slm_thr_layout
= slm_layout.sub(thr_tile_coord.tile, thr_tile_coord.coord);
auto slm_thr_view = view_t(slm_thr_layout);
auto thr_view = tg_view.create_sub_view(thr_tile_coord);
auto load_params = get_send_params(cfg_.options(), send_op_t::load,
send_address_t::a64, abc, thr_view);
auto store_params = get_send_params(cfg_.options(), send_op_t::store,
send_address_t::slm, abc, slm_thr_view);
g2s_load = create_send_plan(cfg_.options(), thr_view, load_params);
g2s_store
= create_send_plan(cfg_.options(), slm_thr_view, store_params);
auto &src = g2s_load.reg_layout();
auto &dst = g2s_store.reg_layout();
reorder = create_reorder_plan(cfg_.hw(), src, dst);
if (reduce_mask && cfg_.allow_global_reduction()) {
*reduce_tile_coord = to_reduce_tensor(
src.ndims(), abs_thr_tile_coord, reduce_mask.mask);
auto reduce_layout = to_reduce_layout(cfg_, src, reduce_mask.mask);
*reduce = create_reduce_plan(
cfg_.hw(), src, reduce_layout, reduce_mask.mask);
}
if (g2s_store.is_scattered()) {
slm_layout = layout_t();
grid = grid_info_t();
g2s_load = send_plan_t();
g2s_store = send_plan_t();
reorder = reorder_plan_t();
if (reduce_mask) {
*reduce_tile_coord = tile_coord_t();
*reduce = reduce_plan_t();
}
}
return plan_status_t::success;
}
plan_status_t init_slm_plan(slm_plan_t &plan) const {
PLAN_CHECK(init_x_slm_plan(abc_kind_t::a, gemm_schedule_.a_tg_view(),
plan.a_layout, plan.a_grid, plan.a_g2s_load, plan.a_g2s_store,
plan.a_reorder, reduce_mask(cfg_, abc_kind_t::a),
&plan.x_reduce, &plan.x_reduce_tile_coord));
PLAN_CHECK(init_x_slm_plan(abc_kind_t::b, gemm_schedule_.b_tg_view(),
plan.b_layout, plan.b_grid, plan.b_g2s_load, plan.b_g2s_store,
plan.b_reorder, reduce_mask(cfg_, abc_kind_t::b),
&plan.x_reduce, &plan.x_reduce_tile_coord));
return plan_status_t::success;
}
void maybe_extend_prefetch_thread_view_to_256_bytes(
view_t &thr_view) const {
auto thr_layout = thr_view.create_pseudo_vlayout();
auto &blocks = thr_layout.blocks();
if (blocks.size() <= 1) return;
auto &b0 = blocks[0];
auto &b1 = blocks[1];
if (!b1.stride.is_fixed() || !b0.stride.is_fixed()) return;
auto inner_var = thr_view.vvars()[b0.idx];
bool is_block_strided
= (b0.stride == stride_t(1)) && (b1.stride > b0.size);
int type_size = thr_layout.type().size();
dim_t full_dim_size
= gemm_schedule_.a_view().vdims()[b0.idx] * type_size;
bool size_ge_256b = (full_dim_size >= 256);
dim_t b0_size = b0.size * type_size;
bool prefetch_lt_256b = (b0_size < 256);
bool is_inner_loop = gemm_schedule_.is_inner_loop(inner_var);
if (is_block_strided && size_ge_256b && prefetch_lt_256b
&& is_inner_loop) {
gpu_assert(thr_view.vdims()[b0.idx] == b0.size);
auto factor = static_cast<int>(256 / b0_size);
thr_view.set_vdim(inner_var, b0.size * factor,
thr_view.vstart()[b0.idx],
true);
}
}
plan_status_t init_x_prefetch_plan(abc_kind_t abc, const view_t &tg_view,
grid_info_t &grid, send_plan_t &prefetch) const {
if (!use_prefetch(abc)) return plan_status_t::success;
auto &tg = cfg_.thread_group_grid();
auto thr_view = tg_view.split(tg, &grid);
if (cfg_.hw() == ngen::HW::XE3P_35_11) {
maybe_extend_prefetch_thread_view_to_256_bytes(thr_view);
}
auto params = get_send_params(cfg_.options(), send_op_t::prefetch,
send_address_t::a64, fma_kind_t::undef, abc, thr_view,
gemm_schedule_);
prefetch = create_send_plan(cfg_.options(), thr_view, params);
return plan_status_t::success;
}
plan_status_t init_prefetch_plan(prefetch_plan_t &plan) const {
PLAN_CHECK(init_x_prefetch_plan(abc_kind_t::a,
gemm_schedule_.a_tg_view(), plan.a_grid, plan.a_prefetch));
PLAN_CHECK(init_x_prefetch_plan(abc_kind_t::b,
gemm_schedule_.b_tg_view(), plan.b_grid, plan.b_prefetch));
return plan_status_t::success;
}
plan_status_t init_x_s2r_plan(abc_kind_t abc, bool has_x_slm,
const layout_t &slm_layout, const tile_coord_t &thr_tile_coord,
const tile_coord_t &abs_thr_tile_coord, send_plan_t &load,
layout_t &layout, reduce_mask_t reduce_mask = reduce_mask_t(),
reduce_plan_t *reduce = nullptr,
tile_coord_t *reduce_tile_coord = nullptr) const {
if (!has_x_slm) return plan_status_t::success;
auto thr_view = view_t(slm_layout).create_sub_view(thr_tile_coord);
auto params = get_send_params(cfg_.options(), send_op_t::load,
send_address_t::slm, abc, thr_view);
load = create_send_plan(cfg_.options(), thr_view, params);
layout = load.reg_layout();
if (reduce_mask && !cfg_.allow_global_reduction()) {
*reduce_tile_coord = to_reduce_tensor(
layout.ndims(), abs_thr_tile_coord, reduce_mask.mask);
auto reduce_layout
= to_reduce_layout(cfg_, layout, reduce_mask.mask);
*reduce = create_reduce_plan(
cfg_.hw(), layout, reduce_layout, reduce_mask.mask);
}
if (load.is_scattered()) {
return plan_status_t::invalid_slm_send;
}
return plan_status_t::success;
}
plan_status_t init_x_g2r_plan(abc_kind_t abc, bool has_x_slm,
const view_t &tg_view, const tile_coord_t &thr_tile_coord,
const tile_coord_t &abs_thr_tile_coord, send_plan_t &load,
reorder_plan_t &reorder, layout_t &layout,
reduce_mask_t reduce_mask = reduce_mask_t(),
reduce_plan_t *reduce = nullptr,
tile_coord_t *reduce_tile_coord = nullptr) const {
if (has_x_slm) return plan_status_t::success;
auto gmem_view = tg_view.create_sub_view(thr_tile_coord);
auto &direct_view
= (abc == abc_kind_t::a ? a_direct_view_ : b_direct_view_);
const auto &load_view = direct_view ? direct_view.get() : gmem_view;
auto params = get_send_params(cfg_.options(), send_op_t::load,
send_address_t::a64, cfg_.fma_kind(), abc, load_view,
gemm_schedule_,
true);
load = create_send_plan(cfg_.options(), load_view, params);
auto reg_layout = load.reg_layout();
if (direct_view) {
reg_layout = direct_view.transform(reg_layout);
if (reg_layout.is_empty())
return plan_status_t::invalid_direct_view;
}
if (reduce_mask) {
gpu_assert(!direct_view);
*reduce_tile_coord = to_reduce_tensor(
reg_layout.ndims(), abs_thr_tile_coord, reduce_mask.mask);
auto reduce_layout
= to_reduce_layout(cfg_, reg_layout, reduce_mask.mask);
*reduce = create_reduce_plan(
cfg_.hw(), reg_layout, reduce_layout, reduce_mask.mask);
}
layout = fma_ctx_.get_fma_friendly_layout(
abc, gemm_schedule_.bmnk_mapper(), reg_layout);
auto &src = reg_layout;
auto &dst = layout;
reorder = create_reorder_plan(cfg_.hw(), src, dst);
return plan_status_t::success;
}
plan_status_t verify_2d() const {
auto &a = plan_.x2r.a_load.send_params().hint_2d;
auto &b = plan_.x2r.b_load.send_params().hint_2d;
int a_vnni_factor = a.enable ? a.vnni_permute_factor : 0;
int b_vnni_factor = b.enable ? b.vnni_permute_factor : 0;
if (a_vnni_factor != b_vnni_factor)
return plan_status_t::ab_layout_vnni_mismatch;
return plan_status_t::success;
}
plan_status_t verify_slm_k_slicing() const {
bmnk_dim_helper_t h(cfg_);
dim_t k_tg = h.thread_group_dim(pvars::k);
if (k_tg == 1) return plan_status_t::success;
auto l = plan_.fma.c_prb_layout;
size_t ndims = l.ndims();
auto blocks = l.blocks();
l = l.with_block({ndims, k_tg});
dim_t outer = 1;
auto rem_dims = l.tile();
for (int i = (int)blocks.size() - 1; i >= 0; i--) {
auto &b = blocks[i];
for (dim_t j = 2; j <= b.size; j++) {
if (b.size % j != 0) continue;
if (outer * j > k_tg) break;
if (outer * j == k_tg || j == b.size) {
rem_dims[b.idx] /= j;
outer *= j;
break;
}
}
}
if (outer != k_tg) return plan_status_t::invalid_slm_k_slicing;
if (plan_.hw < ngen::HW::XeHPG) {
auto l_sub = l.sub(tile_t(std::move(rem_dims)));
int bytes = l_sub.type().size();
stride_t stride = 1;
for (auto &b : l_sub.blocks()) {
if (b.stride != stride) break;
bytes *= (int)b.size;
stride *= b.size;
}
if (bytes % plan_.grf_size() != 0)
return plan_status_t::invalid_slm_k_slicing;
}
return plan_status_t::success;
}
plan_status_t fixup_k_blocks_order(layout_t &a, layout_t &b) const {
auto &bmnk_mapper = gemm_schedule_.bmnk_mapper();
object_map_t<expr_t, pvar_t> k_vars;
auto k_sub_layout = [&](abc_kind_t abc_kind, const layout_t &l) {
layout_t k_layout = layout_t(dsl::type_t::u8());
for (auto &b : l.blocks()) {
auto bmnk_kind = bmnk_mapper.bmnk_kind(abc_kind, b.idx);
if (bmnk_kind != bmnk_kind_t::k) continue;
auto &var = bmnk_mapper.var(abc_kind, b.idx);
auto ret = k_vars.emplace(var, k_vars.size());
k_layout = k_layout.with_block({ret.first->second, b.size});
}
return k_layout;
};
auto a_k = k_sub_layout(abc_kind_t::a, a);
auto b_k = k_sub_layout(abc_kind_t::b, b);
if (a_k.is_equal_normalized(b_k)) return plan_status_t::success;
if (cfg_.fma_kind() != fma_kind_t::mad)
return plan_status_t::ab_layout_k_blocks_mismatch;
if (a_k.nblocks() == 2 && b_k.nblocks() == 2) {
auto &a0 = a_k[0];
auto &a1 = a_k[1];
auto &b0 = b_k[0];
auto &b1 = b_k[1];
bool dims_ok = (a0.idx == b1.idx) && (a1.idx == b0.idx);
bool blocks_ok = (a0.size == b1.size) && (a1.size == b0.size);
if (dims_ok && blocks_ok) {
auto a_blocks = a.blocks();
constexpr size_t unset = -1;
size_t i0 = unset;
size_t i1 = unset;
for (size_t i = 0; i < a.nblocks(); i++) {
if (bmnk_mapper.bmnk_kind(abc_kind_t::a, a_blocks[i].idx)
== bmnk_kind_t::k) {
if (i0 == unset) {
i0 = i;
continue;
}
if (i1 == unset) {
i1 = i;
continue;
}
}
}
std::swap(a_blocks[i0], a_blocks[i1]);
a = a.with(a_blocks);
return plan_status_t::success;
}
}
return plan_status_t::ab_layout_k_blocks_mismatch;
}
plan_status_t init_x2r_plan(const slm_plan_t &slm, x2r_plan_t &plan) const {
PLAN_CHECK(init_x_s2r_plan(abc_kind_t::a, slm.has_a(), slm.a_layout,
gemm_schedule_.a_thr_tile_coord(),
gemm_schedule_.a_thr_tile_coord(false),
plan.a_load, plan.a_layout, reduce_mask(cfg_, abc_kind_t::a),
&plan.x_reduce, &plan.x_reduce_tile_coord));
PLAN_CHECK(init_x_s2r_plan(abc_kind_t::b, slm.has_b(), slm.b_layout,
gemm_schedule_.b_thr_tile_coord(),
gemm_schedule_.b_thr_tile_coord(false),
plan.b_load, plan.b_layout, reduce_mask(cfg_, abc_kind_t::b),
&plan.x_reduce, &plan.x_reduce_tile_coord));
PLAN_CHECK(init_x_g2r_plan(abc_kind_t::a, slm.has_a(),
gemm_schedule_.a_tg_view(), gemm_schedule_.a_thr_tile_coord(),
gemm_schedule_.a_thr_tile_coord(false),
plan.a_load, plan.a_reorder, plan.a_layout,
reduce_mask(cfg_, abc_kind_t::a), &plan.x_reduce,
&plan.x_reduce_tile_coord));
PLAN_CHECK(init_x_g2r_plan(abc_kind_t::b, slm.has_b(),
gemm_schedule_.b_tg_view(), gemm_schedule_.b_thr_tile_coord(),
gemm_schedule_.b_thr_tile_coord(false),
plan.b_load, plan.b_reorder, plan.b_layout,
reduce_mask(cfg_, abc_kind_t::b), &plan.x_reduce,
&plan.x_reduce_tile_coord));
PLAN_CHECK(verify_2d());
PLAN_CHECK(fixup_k_blocks_order(plan.a_layout, plan.b_layout));
return plan_status_t::success;
}
plan_status_t init_fma_plan(const x2r_plan_t &x2r, fma_context_t &fma_ctx,
fma_plan_t &plan) const {
auto &mapper = gemm_schedule_.bmnk_mapper();
auto a_layout = mapper.map_to_bmnk(abc_kind_t::a,
{bmnk_kind_t::b, bmnk_kind_t::m, bmnk_kind_t::k}, x2r.a_layout);
auto b_layout = mapper.map_to_bmnk(abc_kind_t::b,
{bmnk_kind_t::b, bmnk_kind_t::k, bmnk_kind_t::n}, x2r.b_layout);
auto fma_kind = cfg_.fma_kind();
int simd = cfg_.simd();
int vec_size = cfg_.vec_size();
int b_blk = 1;
int m_blk = 1;
int n_blk = 1;
int k_blk = 1;
auto &a_type = a_layout.type();
auto &b_type = b_layout.type();
auto c_type = get_accumulation_type(cfg_, a_type, b_type);
layout_t c_blk_layout(c_type, std::vector<dim_t>(3, 1));
switch (fma_kind) {
case fma_kind_t::dp4a:
case fma_kind_t::dpas:
case fma_kind_t::dpasw: {
const int sdepth = 8;
const int dword_size = 4;
gpu_assert(is_dpas_src1_compatible(
simd, true, b_layout));
gpu_assert(is_dpas_src2_compatible(
simd, true, a_layout));
gpu_assert(a_layout.type().size() == b_layout.type().size());
int ab_type_size = a_layout.type().size();
m_blk = get_dpas_block_rcount(a_layout, 1);
n_blk = simd;
k_blk = sdepth * dword_size / ab_type_size;
c_blk_layout = c_blk_layout.with_block({2, n_blk});
c_blk_layout = c_blk_layout.with_block({1, m_blk});
break;
}
case fma_kind_t::mad:
if (fma_ctx.can_vectorize_by(
bmnk_kind_t::b, a_layout, b_layout)) {
b_blk = vec_size;
c_blk_layout = c_blk_layout.with_block({0, vec_size});
} else if (fma_ctx.can_vectorize_by(
bmnk_kind_t::n, a_layout, b_layout)) {
n_blk = vec_size;
c_blk_layout = c_blk_layout.with_block({2, vec_size});
} else if (fma_ctx.can_vectorize_by(
bmnk_kind_t::m, a_layout, b_layout)) {
m_blk = vec_size;
c_blk_layout = c_blk_layout.with_block({1, vec_size});
} else {
fma_ctx.set_layout_hints(a_layout, b_layout);
return plan_status_t::invalid_fma_layout;
}
break;
default: gpu_error_not_expected();
}
auto c_layout = get_c_layout(a_layout, b_layout, c_blk_layout);
bmnk_block_mapper_t c_mapper(mapper);
c_mapper.push_blocks(abc_kind_t::a, x2r.a_layout.blocks());
for (auto &block : x2r.b_layout.blocks()) {
if (mapper.bmnk_kind(abc_kind_t::b, block.idx) == bmnk_kind_t::n)
c_mapper.push_block(abc_kind_t::b, block);
}
auto c_prb_layout = c_mapper.map_from_bmnk(abc_kind_t::c,
{bmnk_kind_t::b, bmnk_kind_t::m, bmnk_kind_t::n}, c_layout);
bool requires_fadd
= prb_.is_bwd_w && gemm_schedule_.with_kernel_grid_k_slicing();
if (!cfg_.hw().has_fp64_atomic_support() && requires_fadd
&& c_layout.elems() % 8 != 0 && c_type == dsl::type_t::f64()) {
return plan_status_t::invalid_c_layout;
}
plan.a_layout = std::move(a_layout);
plan.b_layout = std::move(b_layout);
plan.c_layout = std::move(c_layout);
plan.c_prb_layout = std::move(c_prb_layout);
plan.fma_kind = fma_kind;
plan.b_blk = b_blk;
plan.m_blk = m_blk;
plan.n_blk = n_blk;
plan.k_blk = k_blk;
PLAN_CHECK(verify_slm_k_slicing());
return plan_status_t::success;
}
plan_status_t init_zp_plan(const x2r_plan_t &x2r, const fma_plan_t &fma,
zp_plan_t &plan) const {
auto &prb = cfg_.prb();
auto b_tile_coord
= gemm_schedule_.b_thr_tile_coord(false);
int g_idx = 0;
int ic_idx = 2;
expr_t zp_off;
dim_t zp_g_dim, zp_ic_dim;
if (cfg_.zp_cfg().is_common_src_zero_point) {
zp_off = expr_t(0);
zp_g_dim = 1;
zp_ic_dim = 1;
} else {
auto &w_g = b_tile_coord.coord[g_idx];
auto &w_ic = b_tile_coord.coord[ic_idx];
zp_off = w_g * prb.ic + w_ic;
zp_g_dim = b_tile_coord.tile[g_idx];
zp_ic_dim = b_tile_coord.tile[ic_idx];
}
const auto src_zp_type = (cfg_.zp_cfg().do_src_compensation)
? cfg_.zp_cfg().src_zp_type
: dsl::type_t::s32();
layout_t zp_layout(
src_zp_type, std::vector<dim_t> {zp_g_dim, zp_ic_dim}, zp_off);
view_t zp_view(zp_layout);
layout_t zp_wei_layout(
cfg_.zp_cfg().wei_zp_type, std::vector<dim_t> {1, 1});
view_t zp_wei_view(zp_wei_layout);
plan.init(cfg_, x2r.a_load.send_params().hint_2d.enable, gemm_schedule_,
zp_view, zp_wei_view, x2r.a_layout, x2r.b_layout,
fma.c_prb_layout);
return plan_status_t::success;
}
std::shared_ptr<plan_t> get_plan() const {
auto plan_ptr = std::make_shared<plan_t>(cfg_.hw());
auto &plan = *plan_ptr;
auto &init_cset = plan.init_cset;
auto &gemm_schedule = plan.gemm_schedule;
gemm_schedule = gemm_schedule_t(init_cset, cfg_.kernel_grid(),
cfg_.thread_group_grid(), cfg_.walk_order());
view_t a_view;
view_t b_view;
view_t c_view;
if (prb_.is_fwd) {
init_fwd(cfg_, gemm_schedule, a_view, b_view, c_view);
} else if (prb_.is_bwd_d) {
init_bwd_d(cfg_, gemm_schedule, a_view, b_view, c_view);
} else if (prb_.is_bwd_w) {
init_bwd_w(
cfg_, gemm_schedule, a_view, b_view, c_view, plan.bia_view);
} else {
gpu_error_not_expected();
}
gemm_schedule.finalize();
if (cfg_.pipeline().is_overridden()) {
plan.reuse_headers = cfg_.pipeline().reuse_headers();
}
return plan_ptr;
}
send_params_t get_send_params(const dsl::kernel::options_t &options,
send_op_t op, send_address_t address, abc_kind_t abc,
const view_t &view) const {
auto params = jit::get_send_params(options, op, address, view);
bool allow_send_2d
= (abc == abc_kind_t::a ? allow_a_send_2d_ : allow_b_send_2d_);
if (!allow_send_2d) params.hint_2d.enable = false;
return params;
}
send_params_t get_send_params(const dsl::kernel::options_t &options,
send_op_t op, send_address_t address, fma_kind_t fma,
abc_kind_t abc, const view_t &view,
const gemm_schedule_t &gemm_schedule,
bool allow_2d_load = true) const {
auto params = jit::get_send_params(options, op, address, fma, abc, view,
gemm_schedule, allow_2d_load);
bool allow_send_2d
= (abc == abc_kind_t::a ? allow_a_send_2d_ : allow_b_send_2d_);
if (!allow_send_2d) params.hint_2d.enable = false;
return params;
}
void enable_direct_view(bool value) { allow_direct_view_ = value; }
void enable_send_2d(abc_kind_t abc, bool value) {
auto &allow_send_2d
= (abc == abc_kind_t::a ? allow_a_send_2d_ : allow_b_send_2d_);
allow_send_2d = value;
}
void enable_slm(bool value) { allow_slm_ = value; }
config_t &cfg_;
const problem_t &prb_;
std::shared_ptr<plan_t> plan_ptr_;
plan_t &plan_;
gemm_schedule_t &gemm_schedule_;
fma_context_t fma_ctx_;
direct_view_t a_direct_view_;
direct_view_t b_direct_view_;
bool allow_direct_view_ = true;
bool allow_a_send_2d_ = true;
bool allow_b_send_2d_ = true;
bool allow_slm_ = true;
};
status_t init_plan(config_t &cfg) {
plan_builder_t plan_builder(cfg);
return plan_builder.init_plan();
}
} } } } } }