#include "common/c_types_map.hpp"
#include "common/compiler_workarounds.hpp"
#include "common/dnnl_thread.hpp"
#include "common/nstl.hpp"
#include "common/primitive_desc_iterator.hpp"
#include "common/type_helpers.hpp"
#include "common/utils.hpp"
#include "cpu/x64/jit_brgemm_deconv.hpp"
namespace dnnl {
namespace impl {
namespace cpu {
namespace x64 {
namespace {
status_t weights_axes_permutation(
memory_desc_t *o_md, const memory_desc_t *i_md, bool with_groups) {
int perm[DNNL_MAX_NDIMS] {}; for (int d = 0; d < DNNL_MAX_NDIMS; ++d)
perm[d] = d;
nstl::swap(perm[0 + with_groups], perm[1 + with_groups]);
return memory_desc_permute_axes(*o_md, *i_md, perm);
}
status_t fwd_conv_desc_create(const deconvolution_desc_t *fwd_deconv_d,
convolution_desc_t *fwd_conv_d) {
const memory_desc_t &fwd_weights_md = fwd_deconv_d->weights_desc;
const int ndims_spatial = fwd_deconv_d->dst_desc.ndims - 2;
dims_t overflow_l;
dims_t overflow_r;
dim_t ks = 1;
for (int i = 0; i < ndims_spatial; i++) {
VDISPATCH_DECONVOLUTION_IC(fwd_deconv_d->strides[i] == 1,
VERBOSE_UNSUPPORTED_FEATURE,
"only unit strides are allowed for bwd-to-fwd conversion");
const dim_t K
= fwd_weights_md.dims[fwd_weights_md.ndims - ndims_spatial + i];
ks *= K;
const dim_t D = fwd_deconv_d->dilates[i];
const dim_t PL = fwd_deconv_d->padding[0][i]; const dim_t PR = fwd_deconv_d->padding[1][i]; constexpr dim_t S = 1;
overflow_l[i] = ((K - 1) * (D + 1) - PL) / S;
overflow_r[i] = ((K - 1) * (D + 1) - PR) / S;
}
const status_t desc_init_status = conv_desc_init(fwd_conv_d,
prop_kind::forward_training, alg_kind::convolution_direct,
&fwd_deconv_d->src_desc, &fwd_weights_md, &fwd_deconv_d->bias_desc,
&fwd_deconv_d->dst_desc, fwd_deconv_d->strides,
fwd_deconv_d->dilates, overflow_l, overflow_r);
VDISPATCH_DECONVOLUTION_IC(desc_init_status == status::success,
VERBOSE_PRIMITIVE_CREATION_FAIL, "fwd_conv");
const bool with_spatial_inversion = ks > 1;
if (with_spatial_inversion) {
fwd_conv_d->diff_src_desc = fwd_conv_d->src_desc;
fwd_conv_d->diff_dst_desc = fwd_conv_d->dst_desc;
}
fwd_conv_d->use_inversion = true;
return status::success;
}
status_t bwd_conv_desc_create(const deconvolution_desc_t *fwd_deconv_d,
convolution_desc_t *bwd_conv_d) {
const memory_desc_t *src_md, *dst_md, *deconv_weights_d;
memory_desc_t src_md_patched;
const auto src_dt = fwd_deconv_d->dst_desc.data_type;
VDISPATCH_DECONVOLUTION_IC(memory_desc_init_by_md_and_dt(src_md_patched,
fwd_deconv_d->dst_desc, src_dt)
== status::success,
VERBOSE_DESC_CREATION_FAIL, "memory");
src_md = &src_md_patched;
dst_md = &fwd_deconv_d->src_desc;
deconv_weights_d = &fwd_deconv_d->weights_desc;
memory_desc_t conv_weights_d;
const bool with_groups = deconv_weights_d->ndims == src_md->ndims + 1;
VDISPATCH_DECONVOLUTION_IC(weights_axes_permutation(&conv_weights_d,
deconv_weights_d, with_groups)
== status::success,
VERBOSE_DESC_CREATION_FAIL, "weights");
const status_t desc_init_status = conv_desc_init(bwd_conv_d,
prop_kind::backward_data, alg_kind::convolution_direct, src_md,
&conv_weights_d, &fwd_deconv_d->bias_desc, dst_md,
fwd_deconv_d->strides, fwd_deconv_d->dilates,
fwd_deconv_d->padding[0], fwd_deconv_d->padding[1]);
VDISPATCH_DECONVOLUTION_IC(desc_init_status == status::success,
VERBOSE_PRIMITIVE_CREATION_FAIL, "bwd_conv");
bwd_conv_d->src_desc = bwd_conv_d->diff_src_desc;
bwd_conv_d->dst_desc = bwd_conv_d->diff_dst_desc;
bwd_conv_d->use_inversion = true;
return status::success;
}
}
template <typename implementation_pd>
status_t check_embedded_impl_init(primitive_desc_iterator_t &it) {
const auto pd = dynamic_cast<implementation_pd *>((*it).get());
if (pd != nullptr) return status::success; return status::unimplemented;
}
template <cpu_isa_t isa>
status_t brgemm_deconvolution_fwd_t<isa>::pd_t::init(engine_t *engine) {
using namespace data_type;
using namespace utils;
using namespace format_tag;
using smask_t = primitive_attr_t::skip_mask_t;
const deconvolution_desc_t *fwd_deconv_d = desc();
const auto src_type = fwd_deconv_d->src_desc.data_type;
const auto dst_type = fwd_deconv_d->dst_desc.data_type;
const bool is_int8 = utils::one_of(src_type, s8, u8);
auto skip_mask = smask_t::post_ops | smask_t::sum_dt;
if (is_int8) skip_mask |= smask_t::scales | smask_t::zero_points;
VDISPATCH_DECONVOLUTION(is_fwd(), VERBOSE_BAD_PROPKIND);
VDISPATCH_DECONVOLUTION((desc()->alg_kind & alg_kind::deconvolution_direct),
VERBOSE_BAD_ALGORITHM);
VDISPATCH_DECONVOLUTION(attr()->has_default_values(skip_mask, dst_type),
VERBOSE_UNSUPPORTED_ATTR);
VDISPATCH_DECONVOLUTION(
attr()->post_ops_.check_sum_consistency(dst_type, is_int8),
VERBOSE_UNSUPPORTED_POSTOP);
VDISPATCH_DECONVOLUTION(attr_scales_ok(), VERBOSE_UNSUPPORTED_SCALES_CFG);
VDISPATCH_DECONVOLUTION(post_ops_ok(), VERBOSE_UNSUPPORTED_POSTOP);
VDISPATCH_DECONVOLUTION(zero_points_ok(), VERBOSE_UNSUPPORTED_ZP_CFG);
VDISPATCH_DECONVOLUTION(!has_zero_dim_memory(), VERBOSE_EMPTY_TENSOR, "");
VDISPATCH_DECONVOLUTION(
impl::is_dense_format_kind({src_md(0), diff_weights_md(0),
diff_weights_md(1), diff_dst_md(0), dst_md(0)}),
VERBOSE_UNSUPPORTED_SPARSE_CFG);
convolution_desc_t conv_d = convolution_desc_t();
assert(src_type != data_type::undef);
const int ndims_spatial = fwd_deconv_d->dst_desc.ndims - 2;
for (int i = 0; i < ndims_spatial; i++) {
if (fwd_deconv_d->strides[i] != 1) {
has_strides_ = true;
break;
}
}
if (has_strides_) {
CHECK(bwd_conv_desc_create(fwd_deconv_d, &conv_d));
primitive_desc_iterator_t it(engine,
reinterpret_cast<const op_desc_t *>(&conv_d), attr(), nullptr);
if (!it.is_initialized()) return status::out_of_memory;
while (++it != it.end()) {
conv_pd_ = *it;
if (check_embedded_impl_init<
typename brgemm_convolution_bwd_strided_t<isa>::pd_t>(
it)
== status::success)
break;
}
if (it == it.end())
VDISPATCH_DECONVOLUTION_IC(false,
"brgemm implementation not found for strided convolution");
} else {
CHECK(fwd_conv_desc_create(fwd_deconv_d, &conv_d));
primitive_desc_iterator_t it(engine,
reinterpret_cast<const op_desc_t *>(&conv_d), attr(), nullptr);
if (!it.is_initialized()) return status::out_of_memory;
while (++it != it.end()) {
conv_pd_ = *it;
if (check_embedded_impl_init<
typename brgemm_1x1_convolution_fwd_t<isa>::pd_t>(it)
== status::success)
break;
if (check_embedded_impl_init<
typename brgemm_convolution_fwd_t<isa>::pd_t>(it)
== status::success)
break;
}
if (it == it.end())
VDISPATCH_DECONVOLUTION_IC(false,
"brgemm implementation not found for strided convolution");
}
if (weights_md_.format_kind == format_kind::any) {
if (has_strides_) {
const status_t desc_init_status = weights_axes_permutation(
&weights_md_, conv_pd_->weights_md(), with_groups());
VDISPATCH_DECONVOLUTION_IC(desc_init_status == status::success,
VERBOSE_DESC_CREATION_FAIL, "weights");
const bool is_signed_input = src_type == s8;
const bool scale_adjust_required = is_signed_input
&& !isa_has_s8s8(isa) && !isa_has_int8_vnni(isa);
if (scale_adjust_required)
weights_md_.extra.flags = 0 | memory_extra_flags::scale_adjust;
} else
weights_md_ = *conv_pd_->weights_md();
}
if (src_md_.format_kind == format_kind::any) {
if (has_strides_)
src_md_ = *conv_pd_->diff_dst_md();
else
src_md_ = *conv_pd_->src_md();
}
if (dst_md_.format_kind == format_kind::any) {
if (has_strides_)
dst_md_ = *conv_pd_->diff_src_md();
else
dst_md_ = *conv_pd_->dst_md();
}
attr_.set_default_formats(&dst_md_);
if (bias_md_.format_kind == format_kind::any)
CHECK(memory_desc_init_by_tag(bias_md_, x));
init_name();
auto scratchpad = scratchpad_registry().registrar();
scratchpad.book(memory_tracking::names::key_nested,
conv_pd_->scratchpad_registry());
return status::success;
}
template <cpu_isa_t isa>
status_t brgemm_deconvolution_fwd_t<isa>::init(engine_t *engine) {
return pd()->conv_pd_->create_primitive(conv_p_, engine);
}
template <cpu_isa_t isa>
status_t brgemm_deconvolution_fwd_t<isa>::execute(const exec_ctx_t &ctx) const {
const auto &args = ctx.args();
exec_args_t conv_args(args);
if (pd()->has_strides_) {
conv_args[DNNL_ARG_DIFF_SRC] = args.at(DNNL_ARG_DST);
conv_args[DNNL_ARG_DIFF_DST] = args.at(DNNL_ARG_SRC);
conv_args.erase(DNNL_ARG_DST);
conv_args.erase(DNNL_ARG_SRC);
}
exec_ctx_t conv_ctx(ctx, std::move(conv_args));
auto *nested_grantor = create_nested_grantor(ctx.get_scratchpad_grantor(),
memory_tracking::names::key_nested,
conv_p_->pd()->scratchpad_registry());
conv_ctx.set_scratchpad_grantor(nested_grantor);
return conv_p_->execute(conv_ctx);
}
template struct brgemm_deconvolution_fwd_t<avx2>;
template struct brgemm_deconvolution_fwd_t<avx2_vnni>;
template struct brgemm_deconvolution_fwd_t<avx2_vnni_2>;
template struct brgemm_deconvolution_fwd_t<avx512_core>;
template struct brgemm_deconvolution_fwd_t<avx512_core_vnni>;
template struct brgemm_deconvolution_fwd_t<avx512_core_bf16>;
template struct brgemm_deconvolution_fwd_t<avx512_core_fp16>;
template struct brgemm_deconvolution_fwd_t<avx10_2>;
template struct brgemm_deconvolution_fwd_t<avx512_core_amx>;
template struct brgemm_deconvolution_fwd_t<avx512_core_amx_fp16>;
template struct brgemm_deconvolution_fwd_t<avx10_2_amx_2>;
} } } }