#include "gpu/intel/binary/xe.hpp"
#include "gpu/intel/primitive_conf.hpp"
namespace dnnl {
namespace impl {
namespace gpu {
namespace intel {
namespace binary {
bool check_layout_constraints(const memory_desc_t *md) {
if (md->dims[1] % 16 != 0) { return false; }
for (int i = 2; i < md->ndims; i++) {
if (md->dims[i] != 1) { return false; }
}
return true;
}
bool check_mixed_layout(const memory_desc_wrapper &src0_d,
const memory_desc_wrapper &dst_d, const memory_desc_t *src1_md) {
using namespace dnnl::impl::format_tag;
format_tag_t src0_tag
= src0_d.matches_one_of_tag(aBc16b, aBcd16b, aBcde16b);
const memory_desc_wrapper src1_d(src1_md);
bool is_mixed = dst_d.matches_tag(src0_tag)
&& src1_d.matches_one_of_tag(abc, abcd, abcde) && src0_tag;
return (is_mixed && src1_md->dims[1] % 16 == 0) ? true : false;
}
bool check_broadcast(
const memory_desc_t *src0_md, const memory_desc_t *src1_md) {
for (int i = 0; i < src0_md->ndims; i++) {
if (src0_md->dims[i] != src1_md->dims[i]) { return false; }
}
return true;
}
status_t xe_t::pd_t::init_conf(impl::engine_t *engine) {
using namespace dnnl::impl::format_tag;
const memory_desc_wrapper src0_d(src_md(0));
const memory_desc_wrapper src1_d(src_md(1));
const memory_desc_wrapper dst_d(dst_md());
const memory_desc_wrapper src2_d
= is_ternary_op() ? memory_desc_wrapper(src_md(2)) : nullptr;
const int ndims = src0_d.ndims();
conf.src0_md_info = memory_desc_info_t::create(src0_d);
conf.src1_md_info = memory_desc_info_t::create(src1_d);
conf.dst_md_info = memory_desc_info_t::create(dst_d);
if (is_ternary_op()) {
conf.src2_md_info = memory_desc_info_t::create(src2_d);
} else {
conf.src2_md_info = memory_desc_info_t();
}
conf.attr_info = attr_info_t::create(attr());
conf.src0_data_type = src0_d.data_type();
conf.src1_data_type = src1_d.data_type();
conf.dst_data_type = dst_d.data_type();
conf.ndims = ndims;
conf.alg = desc()->alg_kind;
conf.is_tensor_op = is_tensor_op();
conf.is_dense = dst_d.is_dense();
conf.same_src_dt = (src0_d.data_type() == src1_d.data_type());
conf.is_same_md = (src0_d == dst_d) && (src1_d == dst_d);
conf.plain_to_ABcd4a4b = false;
conf.isXa16b = false;
conf.mb_block = 0;
conf.is_src1_broadcast = check_layout_constraints(src_md(1));
conf.is_src0_blocked = false;
conf.has_tail = 0;
for (int i = 0; i < MAX_NDIMS; ++i) {
VDISPATCH_BINARY_IC(!(i < ndims && src0_d.dims()[i] == 1
&& src0_d.dims()[i] != src1_d.dims()[i]),
VERBOSE_UNSUPPORTED_FEATURE, "broadcasted src0 dimensions");
conf.src1_bcast_dims[i]
= i < ndims ? into<int>(broadcast_dims()[i]) : 1;
}
if (conf.src1_bcast_dims[1] && !conf.src1_bcast_dims[ndims - 1]) {
conf.nvect = 1;
} else {
conf.nvect = 8;
while (dst_d.dims()[ndims - 1] % conf.nvect != 0) {
conf.nvect /= 2;
}
}
auto *intel_engine = utils::downcast<intel::engine_t *>(engine);
conf.dispatch = intel_engine->create_dispatch(dst_d.md_);
format_tag_t dst_tag = dst_d.matches_one_of_tag(nc, ncw, nchw, ncdhw);
conf.is_plain_layout = dst_tag;
conf.is_src0_blocked = check_mixed_layout(src0_d, dst_d, src_md(1));
bool is_src1_blocked = check_mixed_layout(src1_d, dst_d, src_md(0));
bool is_mixed_layout = check_broadcast(src_md(0), src_md(1))
&& (conf.is_src0_blocked || is_src1_blocked);
format_tag_t src0_16b
= src0_d.matches_one_of_tag(aBc16b, aBcd16b, aBcde16b);
bool is_16b = src1_d.matches_tag(src0_16b) && dst_d.matches_tag(src0_16b);
conf.isXa16b = src0_d.matches_one_of_tag(
ABcd32a16b, ABcde32a16b, ABcd16a16b, ABcde16a16b)
&& dst_d.matches_one_of_tag(
ABcd32a16b, ABcde32a16b, ABcd16a16b, ABcde16a16b)
&& src1_d.matches_one_of_tag(
ABcd32a16b, ABcde32a16b, ABcd16a16b, ABcde16a16b);
format_tag_t src_plain = src0_d.matches_one_of_tag(abcd, acdb);
const auto &padded_dims = dst_d.padded_dims();
bool plain_and_X4a4b = (src1_d.matches_tag(src_plain)
&& dst_d.matches_one_of_tag(ABcd4a4b) && src0_d.is_dense()
&& dst_d.is_dense(true) && padded_dims[3] % 16 == 0
&& dst_d.data_type() != dnnl_f32);
if (plain_and_X4a4b) {
dim_t blocks[MAX_NDIMS] = {1, 1, 1, 1, 1, 1};
auto &blk = dst_d.blocking_desc();
int b_block = into<int>(blk.inner_blks[blk.inner_nblks - 1]);
int sub_group_size = (b_block == 2 ? 8 : 16);
blocks[0] = 4;
blocks[1] = b_block;
int vect_dim = 3;
conf.nvect = 8;
for (int i = 0; i < MAX_NDIMS; ++i) {
auto dim_str = utils::format("D%d", i);
if (i < dst_d.ndims()) {
conf.dispatch.define_dim(dim_str, i, padded_dims[i], blocks[i]);
} else {
conf.dispatch.define_dim(dim_str, 1);
}
}
auto dim_str = utils::format("D%d", vect_dim);
CHECK(conf.dispatch.vectorize_dim(dim_str, sub_group_size));
conf.plain_to_ABcd4a4b = true;
} else if (conf.isXa16b) {
VDISPATCH_BINARY_IC(!is_broadcast(), VERBOSE_UNSUPPORTED_FEATURE,
"broadcasted dimensions");
conf.nvect = 8;
int channel_blk = 16;
const int vect_dim_size = 16;
const dim_t padded_channels = padded_dims[1];
conf.mb_block
= into<int>(dst_d.md_->format_desc.blocking.inner_blks[0]);
while (padded_channels % (vect_dim_size * channel_blk) != 0) {
channel_blk /= 2;
}
dim_t blocks[MAX_NDIMS] = {8, channel_blk, 1, 1, 1, 1};
for (int i = 0; i < MAX_NDIMS; ++i) {
auto dim_str = utils::format("D%d", i);
if (i < dst_d.ndims()) {
conf.dispatch.define_dim(dim_str, i, padded_dims[i], blocks[i]);
if (i == 1) {
CHECK(conf.dispatch.vectorize_dim(dim_str, vect_dim_size));
}
} else {
conf.dispatch.define_dim(dim_str, 1);
}
}
} else if ((conf.is_src0_blocked || is_16b) && conf.is_src1_broadcast) {
int idx = 0;
if (!is_16b) {
idx = 1;
conf.dispatch.define_dim_with_nesting_level(
"D0", ndims, dst_d.dims()[0], 1);
}
for (int i = idx; i < MAX_NDIMS; ++i) {
dim_t dim = i < ndims ? dst_d.dims()[i] : 1;
if (i == 1) {
conf.dispatch.define_dim(utils::format("D%d", i),
nstl::min(i, ndims - 1), dim, 1);
CHECK(conf.dispatch.vectorize_dim("D1", 16));
} else if (i == ndims - 1) {
conf.dispatch.define_dim(utils::format("D%d", i),
nstl::min(i, ndims - 1), dim, conf.nvect);
} else {
conf.dispatch.define_dim(utils::format("D%d", i),
nstl::min(i, ndims - 1), dim, 1);
}
}
} else if (is_mixed_layout) {
conf.nvect = 1;
int block_size = 16;
bool size_check = true;
if (dst_d.matches_tag(aBc16b) && dst_d.dims()[2] % 16 != 0) {
size_check = false;
} else if (dst_d.matches_tag(aBcd16b)
&& (dst_d.dims()[3] % 16 != 0 || dst_d.dims()[2] % 16 != 0)) {
size_check = false;
} else if (dst_d.matches_tag(aBcde16b)
&& (dst_d.dims()[4] % 16 != 0 || dst_d.dims()[3] % 16 != 0
|| dst_d.dims()[2] % 16 != 0)) {
size_check = false;
}
VDISPATCH_BINARY_IC(
size_check, VERBOSE_BLOCKING_FAIL, "bad dimensions");
for (int i = 0; i < MAX_NDIMS; ++i) {
dim_t dim = i < ndims ? dst_d.dims()[i] : 1;
if (i == 1) {
conf.dispatch.define_dim(utils::format("D%d", i),
nstl::min(i, ndims - 1), dim, 1);
CHECK(conf.dispatch.vectorize_dim("D1", block_size));
} else if (i == ndims - 1) {
conf.dispatch.define_dim(utils::format("D%d", i),
nstl::min(i, ndims - 1), dim, block_size);
} else {
conf.dispatch.define_dim(utils::format("D%d", i),
nstl::min(i, ndims - 1), dim, 1);
}
}
} else if (conf.is_plain_layout) {
VDISPATCH_BINARY_IC(
src0_d.matches_tag(dst_tag) && src1_d.matches_tag(dst_tag),
VERBOSE_UNSUPPORTED_TAG);
const int subgroup_size = 16;
const dim_t last_dim = dst_d.dims()[ndims - 1];
int rem = last_dim % subgroup_size;
dim_t rounded_last_dim = utils::rnd_up(last_dim, (dim_t)subgroup_size);
if (rem) { conf.has_tail = 1; }
bool all_dims_broadcast = true;
for (int i = 0; i < ndims; i++) {
if (src1_d.dims()[i] != 1) all_dims_broadcast = false;
}
VDISPATCH_BINARY_IC(!rem || all_dims_broadcast, VERBOSE_BAD_PARAM,
"tail processing fail");
conf.nvect = subgroup_size;
dim_t mixed_dim = 1;
for (int i = 0; i < (ndims - 1); ++i) {
mixed_dim *= dst_d.dims()[i];
}
VDISPATCH_BINARY_IC(!rem || (mixed_dim * last_dim) % subgroup_size != 0,
VERBOSE_BAD_PARAM, "bad tail processing fail");
mixed_dim *= rounded_last_dim;
while ((rounded_last_dim / subgroup_size) % conf.nvect != 0) {
--conf.nvect;
}
conf.dispatch.define_dim("MIXED_DIM", 0, mixed_dim, conf.nvect);
CHECK(conf.dispatch.vectorize_dim("MIXED_DIM", subgroup_size));
} else {
VDISPATCH_BINARY_IC(false, VERBOSE_UNSUPPORTED_TENSOR_LAYOUT, "");
}
conf.dispatch.generate();
return status::success;
}
status_t xe_t::pd_t::init_kernel_ctx(compute::kernel_ctx_t &kernel_ctx) const {
kernel_ctx.require_stateless_addressing(has_large_buffers());
kernel_ctx.define_int("BINARY_ALG", conf.alg);
kernel_ctx.define_int(
"IS_TERNARY", (conf.alg == alg_kind::binary_select) ? 1 : 0);
kernel_ctx.set_data_type(conf.src0_data_type);
kernel_ctx.define_int("SUB_GROUP_SIZE", 16);
kernel_ctx.define_int("NDIMS", conf.ndims);
kernel_ctx.define_int("IS_PLAIN_LAYOUT", conf.is_plain_layout);
kernel_ctx.define_int("PLAIN_TO_ABCD4AXB", conf.plain_to_ABcd4a4b);
kernel_ctx.define_int("IS_XA16B", conf.isXa16b);
kernel_ctx.define_int("MB_BLOCK", conf.mb_block);
kernel_ctx.define_int("HAS_TAIL", conf.has_tail);
if (conf.has_tail && conf.is_plain_layout) {
const memory_desc_wrapper dst_d(dst_md());
dim_t total_elems = 1;
for (int i = 0; i < dst_d.ndims(); i++) {
total_elems *= dst_d.dims()[i];
}
kernel_ctx.define_int("TOTAL_ELEMS", total_elems);
}
kernel_ctx.define_int("SAME_SRC_DT", conf.same_src_dt);
kernel_ctx.define_int("IS_SRC1_BROADCAST", conf.is_src1_broadcast);
kernel_ctx.define_int("IS_SRC0_BLOCKED", conf.is_src0_blocked);
kernel_ctx.define_int("BCAST_DIM0", conf.src1_bcast_dims[0]);
kernel_ctx.define_int("BCAST_DIM1", conf.src1_bcast_dims[1]);
kernel_ctx.define_int("BCAST_DIM2", conf.src1_bcast_dims[2]);
kernel_ctx.define_int("BCAST_DIM3", conf.src1_bcast_dims[3]);
kernel_ctx.define_int("BCAST_DIM4", conf.src1_bcast_dims[4]);
kernel_ctx.define_int("BCAST_DIM5", conf.src1_bcast_dims[5]);
kernel_ctx.define_int(
"BCAST_AT_INNERMOST_DIM", conf.src1_bcast_dims[conf.ndims - 1]);
kernel_ctx.define_int("NVECT", conf.nvect);
kernel_ctx.add_option("-Dcl_intel_subgroups_char");
kernel_ctx.add_option("-Dcl_intel_subgroups_uchar");
def_memory_desc_info(kernel_ctx, conf.src0_md_info, "SRC0");
def_memory_desc_info(kernel_ctx, conf.src1_md_info, "SRC1");
def_memory_desc_info(kernel_ctx, conf.dst_md_info, "DST");
if (conf.alg == alg_kind::binary_select) {
def_memory_desc_info(kernel_ctx, conf.src2_md_info, "SRC2");
}
CHECK(def_attr_info(
kernel_ctx, conf.attr_info, attr()->post_ops_, *dst_md()));
def_dispatch(kernel_ctx, conf.dispatch);
return status::success;
}
} } } } }