#include "common/c_types_map.hpp"
#include "common/convolution_pd.hpp"
#include "common/memory.hpp"
#include "common/memory_tracking.hpp"
#include "common/nstl.hpp"
#include "common/type_helpers.hpp"
#include "common/utils.hpp"
#include "cpu/platform.hpp"
#include "cpu/x64/injectors/injector_utils.hpp"
#include "cpu/x64/injectors/jit_uni_binary_injector.hpp"
#include "cpu/x64/injectors/jit_uni_eltwise_injector.hpp"
#include "cpu/x64/jit_uni_x8s8s32x_conv_kernel.hpp"
#define GET_OFF(field) offsetof(jit_conv_args_t, field)
namespace dnnl {
namespace impl {
namespace cpu {
namespace x64 {
using namespace dnnl::impl::memory_tracking::names;
using namespace dnnl::impl::utils;
using namespace Xbyak;
using namespace injector_utils;
namespace {
void pick_loop_order(jit_conv_conf_t &jcp) {
jcp.loop_order = loop_cwgn;
if (jcp.ngroups > 1) {
jcp.loop_order = loop_ngcw;
if (jcp.mb < jcp.nthr)
jcp.loop_order = jcp.ndims == 3 ? loop_nwcg : loop_nhwcg;
} else if (jcp.mb >= jcp.nthr && jcp.ic_without_padding <= 8) {
jcp.loop_order = loop_ngcw;
}
}
}
template <cpu_isa_t isa, typename Vmm>
jit_uni_x8s8s32x_fwd_kernel_vmm_t<isa, Vmm>::jit_uni_x8s8s32x_fwd_kernel_vmm_t(
const jit_conv_conf_t &ajcp, const primitive_attr_t &attr,
const memory_desc_t &dst_md)
: jit_generator_t(jit_name(), isa), jcp(ajcp), attr_(attr) {
if (jcp.with_eltwise || jcp.with_binary || jcp.with_sum) {
using namespace binary_injector;
static constexpr bool preserve_gpr = true;
static constexpr bool preserve_vmm = false;
static constexpr size_t helper_vmm_idx = 15;
const size_t block_tail
= (jcp.is_depthwise ? jcp.ch_block : jcp.oc_block)
% isa_simd_width_;
const size_t tail_size = block_tail
? block_tail
: (jcp.is_depthwise ? jcp.ngroups : jcp.oc_without_padding)
% isa_simd_width_;
const rhs_arg_static_params_t rhs_arg_static_params {helper_vmm_idx,
r13, r14, r15, preserve_gpr, preserve_vmm,
GET_OFF(post_ops_binary_rhs_arg_vec), GET_OFF(dst_orig),
memory_desc_wrapper(dst_md), tail_size, true};
const static_params_t static_params {
this->param1, rhs_arg_static_params};
postops_injector_
= utils::make_unique<injector::jit_uni_postops_injector_t<isa>>(
this, jcp.post_ops, static_params);
}
}
template <cpu_isa_t isa, typename Vmm>
void jit_uni_x8s8s32x_fwd_kernel_vmm_t<isa, Vmm>::prepare_output(int ur_w) {
int nb_oc_block
= jcp.is_depthwise ? jcp.nb_ch_blocking : jcp.nb_oc_blocking;
for (int k = 0; k < nb_oc_block; ++k)
for (int j = 0; j < ur_w; ++j) {
Vmm vmm = vmm_out(j, k);
uni_vpxor(vmm, vmm, vmm);
}
if (jcp.signed_input) {
auto xmm_shift = Xbyak::Xmm(vmm_shift.getIdx());
if (jcp.is_depthwise)
mov(reg_scratch, 128);
else
mov(reg_scratch, 0x80808080);
uni_vmovq(xmm_shift, reg_scratch);
uni_vpbroadcastd(vmm_shift, xmm_shift);
}
}
template <cpu_isa_t isa, typename Vmm>
void jit_uni_x8s8s32x_fwd_kernel_vmm_t<isa, Vmm>::cvt2ps(data_type_t type_in,
const Vmm &vmm_in, const Reg64 ®, int offset, int load_size) {
load_data(type_in, vmm_in, reg, offset, load_size);
if (type_in != data_type::f32) uni_vcvtdq2ps(vmm_in, vmm_in);
}
template <typename F>
void iterate(const int nb_oc_block, const int ur_w,
const bool last_oc_block_flag, const bool force_masking, const F &f) {
for (int k = 0; k < nb_oc_block; k++) {
const bool mask_flag
= force_masking || (last_oc_block_flag && k == nb_oc_block - 1);
for (int j = 0; j < ur_w; j++)
f(mask_flag, k, j);
}
}
template <typename F>
void iterate(const int nb_oc_block, const int ur_w,
const bool last_oc_block_flag, const F &f) {
iterate(nb_oc_block, ur_w, last_oc_block_flag, false, f);
}
template <typename F>
void iterate(const int nb_oc_block, const int ur_w, const F &f) {
iterate(nb_oc_block, ur_w, false, false, f);
}
template <cpu_isa_t isa, typename Vmm>
void jit_uni_x8s8s32x_fwd_kernel_vmm_t<isa, Vmm>::apply_sum(
const int nb_oc_block, const int ur_w, const bool last_oc_block_flag,
const int oc_block, const float *p_sum_scale, const int32_t *p_sum_zp) {
if (jcp.with_sum) {
assert(!utils::any_null(p_sum_scale, p_sum_zp)
&& "p_sum_scale or p_sum_zp = nullptr");
const float sum_scale = *p_sum_scale;
const int32_t sum_zp = *p_sum_zp;
const auto sum_injector_lam
= [this, oc_block, sum_scale, sum_zp](
const bool mask_flag, const int k, const int j) {
const int aux_output_offset = jcp.typesize_out
* (k * oc_block + j * jcp.oc_without_padding * jcp.ngroups);
cvt2ps(jcp.sum_dt, vmm_prev_dst, reg_out, aux_output_offset,
mask_flag ? get_tail_size() : get_blocking_size());
const Vmm vmm = vmm_out(j, k);
if (sum_zp != 0) {
uni_vbroadcastss(vmm_tmp, ptr[reg_ptr_sum_zp]);
uni_vcvtdq2ps(vmm_tmp, vmm_tmp);
uni_vsubps(vmm_prev_dst, vmm_prev_dst, vmm_tmp);
}
if (sum_scale == 1.f)
uni_vaddps(vmm, vmm, vmm_prev_dst);
else {
uni_vbroadcastss(vmm_tmp, ptr[reg_ptr_sum_scale]);
uni_vfmadd231ps(vmm, vmm_prev_dst, vmm_tmp);
}
};
const auto sum_injector
= [nb_oc_block, ur_w, last_oc_block_flag, sum_injector_lam]() {
iterate(nb_oc_block, ur_w, last_oc_block_flag, sum_injector_lam);
};
if (*p_sum_scale != 1.f)
mov(reg_ptr_sum_scale, reinterpret_cast<size_t>(p_sum_scale));
if (*p_sum_zp != 0)
mov(reg_ptr_sum_zp, reinterpret_cast<size_t>(p_sum_zp));
postops_injector_->set_lambda_injector(
primitive_kind::sum, sum_injector);
}
}
template <cpu_isa_t isa, typename Vmm>
void jit_uni_x8s8s32x_fwd_kernel_vmm_t<isa, Vmm>::apply_postops(
const int nb_oc_block, const int ur_w, const bool last_oc_block_flag,
const int oc_block, const float *p_sum_scale, const int32_t *p_sum_zp) {
if (jcp.with_eltwise || jcp.with_binary || jcp.with_sum) {
if (jcp.with_sum && *p_sum_zp != 0) push(reg_ptr_sum_zp);
apply_sum(nb_oc_block, ur_w, last_oc_block_flag, oc_block, p_sum_scale,
p_sum_zp);
vmm_index_set_t vmm_idxs;
if (jcp.with_binary) {
binary_injector::rhs_arg_dynamic_params_t rhs_arg_params;
const bool oc_blk_is_smaller_than_vmm = oc_block < isa_simd_width_;
iterate(nb_oc_block, ur_w, last_oc_block_flag,
oc_blk_is_smaller_than_vmm,
[&](const bool mask_flag, const int k, const int j) {
const size_t aux_output_offset = jcp.typesize_out
* (k * oc_block
+ j * jcp.oc_without_padding * jcp.ngroups);
const auto vmm_idx = vmm_out_idx(j, k);
vmm_idxs.emplace(vmm_idx);
rhs_arg_params.vmm_idx_to_out_reg.emplace(vmm_idx, reg_out);
rhs_arg_params.vmm_idx_to_out_elem_off_val.emplace(
vmm_idx, aux_output_offset);
if (mask_flag) rhs_arg_params.vmm_tail_idx_.emplace(vmm_idx);
});
postops_injector_->compute_vector_range(vmm_idxs, rhs_arg_params);
} else {
iterate(nb_oc_block, ur_w,
[&](const bool, const int k, const int j) {
vmm_idxs.emplace(vmm_out_idx(j, k));
});
postops_injector_->compute_vector_range(vmm_idxs);
}
if (jcp.with_sum && *p_sum_zp != 0) pop(reg_ptr_sum_zp);
}
}
template <cpu_isa_t isa, typename Vmm>
void jit_uni_x8s8s32x_fwd_kernel_vmm_t<isa, Vmm>::store_output(
int ur_w, bool last_oc_block_flag) {
int nb_oc_block
= jcp.is_depthwise ? jcp.nb_ch_blocking : jcp.nb_oc_blocking;
int oc_block = jcp.is_depthwise ? jcp.ch_block : jcp.oc_block;
mov(reg_bias, ptr[param1 + GET_OFF(bias)]);
if (jcp.signed_input)
mov(reg_compensation, ptr[param1 + GET_OFF(compensation)]);
if (jcp.src_zero_point) {
mov(reg_zp_compensation, ptr[param1 + GET_OFF(zp_compensation)]);
mov(reg_src_zero_point, ptr[param1 + GET_OFF(src_zero_point)]);
uni_vpbroadcastd(vmm_zp, ptr[reg_src_zero_point]);
}
const auto &p = attr_.post_ops_;
const int sum_idx = p.find(primitive_kind::sum);
const float *p_sum_scale = nullptr;
const int32_t *p_sum_zp = nullptr;
if (sum_idx != -1) {
const auto &p_entry = p.entry_[sum_idx];
p_sum_scale = &p_entry.sum.scale;
p_sum_zp = &p_entry.sum.zero_point;
}
for (int k = 0; k < nb_oc_block; ++k) {
const bool mask_flag = last_oc_block_flag && k == nb_oc_block - 1;
const int load_size = mask_flag ? get_tail_size() : get_blocking_size();
if (jcp.signed_input) {
const int comp_offset = sizeof(int32_t) * k * oc_block;
load_data(data_type::s32, vmm_comp, reg_compensation, comp_offset,
load_size);
}
if (jcp.src_zero_point) {
const int zp_offset = sizeof(int32_t) * k * oc_block;
load_data(data_type::s32, vmm_zp_comp, reg_zp_compensation,
zp_offset, load_size);
uni_vpmulld(vmm_zp_comp, vmm_zp_comp, vmm_zp);
}
bool is_vmm_scales_set = false;
if (jcp.with_src_scales) {
mov(reg_src_scales, ptr[param1 + GET_OFF(src_scales)]);
uni_vbroadcastss(vmm_scales, ptr[reg_src_scales]);
is_vmm_scales_set = true;
}
if (jcp.with_wei_scales) {
mov(reg_wei_scales, ptr[param1 + GET_OFF(wei_scales)]);
if (!jcp.is_oc_scale) {
uni_vbroadcastss(vmm_scales_tmp, ptr[reg_wei_scales]);
} else {
int scale_offset
= jcp.is_oc_scale * (sizeof(float) * k * oc_block);
if (mask_flag) {
load_data(data_type::s32, vmm_scales_tmp, reg_wei_scales,
scale_offset, get_tail_size());
} else {
uni_vmovups(
vmm_scales_tmp, ptr[reg_wei_scales + scale_offset]);
}
}
if (is_vmm_scales_set) {
uni_vmulps(vmm_scales, vmm_scales, vmm_scales_tmp);
} else {
uni_vmovups(vmm_scales, vmm_scales_tmp);
}
is_vmm_scales_set = true;
}
if (jcp.wei_adj_scale != 1.f) {
mov(reg_scale_adjust, float2int(1.f / jcp.wei_adj_scale));
auto vmm_scale_adjust = vmm_scales_tmp;
auto xmm_scale_adjust = Xmm(vmm_scale_adjust.getIdx());
uni_vmovq(xmm_scale_adjust, reg_scale_adjust);
uni_vbroadcastss(vmm_scale_adjust, xmm_scale_adjust);
if (is_vmm_scales_set) {
uni_vmulps(vmm_scales, vmm_scales, vmm_scale_adjust);
} else {
uni_vmovups(vmm_scales, vmm_scale_adjust);
}
is_vmm_scales_set = true;
}
if (jcp.with_bias) {
int bias_offset = jcp.typesize_bia * k * oc_block;
cvt2ps(jcp.bia_dt, vmm_bias, reg_bias, bias_offset, load_size);
}
for (int j = 0; j < ur_w; ++j) {
const Vmm vmm = vmm_out(j, k);
if (jcp.signed_input) uni_vpaddd(vmm, vmm, vmm_comp);
if (jcp.src_zero_point) uni_vpaddd(vmm, vmm, vmm_zp_comp);
uni_vcvtdq2ps(vmm, vmm);
if (is_vmm_scales_set) uni_vmulps(vmm, vmm, vmm_scales);
if (jcp.with_bias) uni_vaddps(vmm, vmm, vmm_bias);
}
}
apply_postops(nb_oc_block, ur_w, last_oc_block_flag, oc_block, p_sum_scale,
p_sum_zp);
if (jcp.with_dst_scales) {
mov(reg_dst_scales, ptr[param1 + GET_OFF(dst_scales)]);
uni_vbroadcastss(vmm_dst_scales, ptr[reg_dst_scales]);
for (int k = 0; k < nb_oc_block; k++) {
for (int j = 0; j < ur_w; j++) {
const Vmm vmm = vmm_out(j, k);
uni_vmulps(vmm, vmm, vmm_dst_scales);
}
}
}
if (jcp.dst_zero_point) {
mov(reg_dst_zero_point, ptr[param1 + GET_OFF(dst_zero_point)]);
uni_vpbroadcastd(vmm_zp, ptr[reg_dst_zero_point]);
uni_vcvtdq2ps(vmm_zp, vmm_zp);
for (int k = 0; k < nb_oc_block; k++) {
for (int j = 0; j < ur_w; j++) {
const Vmm vmm = vmm_out(j, k);
uni_vaddps(vmm, vmm, vmm_zp);
}
}
}
if (jcp.dst_dt == data_type::u8) {
uni_vpxor(vmm_zero, vmm_zero, vmm_zero);
for (int k = 0; k < nb_oc_block; ++k) {
for (int j = 0; j < ur_w; ++j) {
Vmm vmm = vmm_out(j, k);
uni_vmaxps(vmm, vmm, vmm_zero);
}
}
}
if (utils::one_of(
jcp.dst_dt, data_type::u8, data_type::s8, data_type::s32)) {
float saturation_ubound = types::max_value<float>(jcp.dst_dt);
Xmm xmm_saturation(vmm_saturation.getIdx());
mov(reg_ptr_saturation_ubound, float2int(saturation_ubound));
uni_vmovq(xmm_saturation, reg_ptr_saturation_ubound);
uni_vbroadcastss(vmm_saturation, xmm_saturation);
for (int k = 0; k < nb_oc_block; ++k) {
for (int j = 0; j < ur_w; ++j) {
Vmm vmm = vmm_out(j, k);
uni_vminps(vmm, vmm, vmm_saturation);
}
}
}
if (utils::one_of(jcp.dst_dt, data_type::u8, data_type::s8, data_type::s32))
for (int k = 0; k < nb_oc_block; ++k)
for (int j = 0; j < ur_w; ++j) {
Vmm vmm = vmm_out(j, k);
uni_vcvtps2dq(vmm, vmm);
}
for (int k = 0; k < nb_oc_block; ++k) {
const bool mask_flag = last_oc_block_flag && k == nb_oc_block - 1;
for (int j = 0; j < ur_w; ++j) {
Vmm r_vmm = vmm_out(j, k);
int aux_output_offset = jcp.typesize_out
* (k * oc_block + j * jcp.oc_without_padding * jcp.ngroups);
store_data(jcp.dst_dt, r_vmm, reg_out, aux_output_offset,
mask_flag ? get_tail_size() : get_blocking_size());
}
}
}
template <cpu_isa_t isa, typename Vmm>
void jit_uni_x8s8s32x_fwd_kernel_vmm_t<isa, Vmm>::compute_ker_dw(int ur_w,
int pad_l, int pad_r, ic_block_t last_ic_block_flag, bool h_padded) {
if (!(utils::one_of(isa, avx2) && std::is_same<Vmm, Xbyak::Ymm>::value)
&& !(utils::one_of(isa, sse41)
&& std::is_same<Vmm, Xbyak::Xmm>::value))
assert(!"invalid group blocking for depthwise convolution");
const bool compute_kernel = IMPLICATION(h_padded, jcp.signed_input);
if (jcp.src_zero_point) {
push(aux_reg_ker_d);
mov(reg_src_zero_point, ptr[param1 + GET_OFF(src_zero_point)]);
uni_vpbroadcastd(vmm_zp, ptr[reg_src_zero_point]);
}
auto input_spatial_index = [this, pad_l](int oi, int ki) {
return (ki * (jcp.dilate_w + 1) + oi * jcp.stride_w - pad_l);
};
auto input_offset2 = [this](int ii, int ci) {
if (jcp.is_fused_conv)
return jcp.typesize_in
* (ii * jcp.dw_conv_buffer_oc + ci * jcp.ch_block);
else
return jcp.typesize_in * (ii * jcp.ngroups + ci * jcp.ch_block);
};
auto input_offset3 = [this, input_offset2, input_spatial_index](
int oi, int ci, int ki) {
return jcp.typesize_in * input_offset2(input_spatial_index(oi, ki), ci);
};
auto kernel_offset = [this](int ci, int ki) {
return jcp.typesize_in * ((ci * jcp.kh * jcp.kw + ki) * jcp.ch_block);
};
auto compute = [this](Vmm vreg_acc, Vmm vreg_wei, Vmm vreg_src) {
if (jcp.has_vnni) {
vpdpbusd(vreg_acc, vreg_src, vreg_wei, VexEncoding);
} else {
uni_vpmaddwd(vmm_dw_tmp, vreg_src, vreg_wei);
uni_vpaddd(vreg_acc, vreg_acc, vmm_dw_tmp);
}
};
int ii_start = 0;
int ii_end = -1;
if (jcp.is_resrc_depthwise && !h_padded) {
bool first = true;
for (int ki = 0; ki < jcp.kw; ++ki) {
int oi_start = get_ow_start(ki, pad_l);
int oi_end = get_ow_end(ur_w, ki, pad_r);
for (int oi = oi_start; oi < oi_end; ++oi) {
int ii = input_spatial_index(oi, ki);
if (first || ii < ii_start) ii_start = ii;
if (first || ii > ii_end) ii_end = ii;
first = false;
}
}
}
for (int ci = 0; ci < jcp.nb_ch_blocking; ++ci) {
const bool mask_flag = last_ic_block_flag != no_last_block
&& ci == jcp.nb_ch_blocking - 1;
if (jcp.is_resrc_depthwise && !h_padded) {
for (int ii = ii_start; ii <= ii_end; ++ii) {
int aux_input_offset = input_offset2(ii, ci);
const Vmm vmm_inp_tmp = vmm_inp(ii, jcp.nb_ch_blocking);
load_data(data_type::u8, vmm_inp_tmp, aux_reg_inp,
aux_input_offset,
mask_flag ? get_tail_size() : get_blocking_size());
if (jcp.signed_input)
uni_vpaddb(vmm_inp_tmp, vmm_inp_tmp, vmm_shift);
}
}
for (int ki = 0; ki < jcp.kw; ++ki) {
int aux_kernel_offset = kernel_offset(ci, ki);
int oi_start = get_ow_start(ki, pad_l);
int oi_end = get_ow_end(ur_w, ki, pad_r);
if (compute_kernel) {
uni_vpmovsxbd(vmm_wei, ptr[aux_reg_ker + aux_kernel_offset]);
if (h_padded) {
assert(jcp.signed_input);
for (int oi = 0; oi < ur_w; ++oi)
compute(vmm_out(oi, ci), vmm_wei, vmm_shift);
} else {
int start = jcp.signed_input ? 0 : oi_start;
int end = jcp.signed_input ? ur_w : oi_end;
for (int oi = start; oi < end; ++oi) {
if (oi >= oi_start && oi < oi_end) {
if (jcp.is_resrc_depthwise) {
int ii = input_spatial_index(oi, ki);
vmm_dw_src = vmm_inp(ii, jcp.nb_ch_blocking);
} else {
int aux_input_offset
= input_offset3(oi, ci, ki);
load_data(data_type::u8, vmm_dw_src,
aux_reg_inp, aux_input_offset,
mask_flag ? get_tail_size()
: get_blocking_size());
if (jcp.signed_input)
uni_vpaddb(
vmm_dw_src, vmm_dw_src, vmm_shift);
}
compute(vmm_out(oi, ci), vmm_wei, vmm_dw_src);
} else {
assert(jcp.signed_input);
compute(vmm_out(oi, ci), vmm_wei, vmm_shift);
}
}
}
}
if (jcp.src_zero_point) {
if (!compute_kernel) {
uni_vpmovsxbd(
vmm_wei, ptr[aux_reg_ker + aux_kernel_offset]);
} for (int oi = 0; oi < ur_w; oi++) {
if (oi < oi_start || oi >= oi_end || h_padded) {
uni_vpmulld(vmm_zp_dw_tmp, vmm_wei, vmm_zp);
uni_vpaddd(vmm_out(oi, ci), vmm_out(oi, ci),
vmm_zp_dw_tmp);
}
}
}
}
}
if (jcp.src_zero_point) pop(aux_reg_ker_d);
}
template <cpu_isa_t isa, typename Vmm>
void jit_uni_x8s8s32x_fwd_kernel_vmm_t<isa, Vmm>::compute_ker(int ur_w,
int pad_l, int pad_r, ic_block_t last_ic_block_flag, bool h_padded) {
if (jcp.is_depthwise)
return compute_ker_dw(ur_w, pad_l, pad_r, last_ic_block_flag, h_padded);
int kw = jcp.kw;
int stride_w = jcp.stride_w;
int ic_block = jcp.ic_block;
int oc_block = jcp.oc_block;
int ch_block_all = jcp.ch_block * ic_block * oc_block;
int nb_oc_block = jcp.nb_oc_blocking;
const bool compute_kernel = IMPLICATION(h_padded, jcp.signed_input);
assert(IMPLICATION(h_padded, jcp.src_zero_point || jcp.signed_input));
if (jcp.src_zero_point) {
push(aux_reg_ker_d);
mov(reg_src_zero_point, ptr[param1 + GET_OFF(src_zero_point)]);
}
auto input_offset = [this, stride_w, pad_l](int oi, int ic, int ki) {
return jcp.typesize_in
* ((ki * (jcp.dilate_w + 1) + oi * stride_w - pad_l)
* jcp.ic_without_padding * jcp.ngroups
+ ic_sub_step * ic);
};
auto kernel_offset
= [this, ch_block_all, oc_block](int ii, int ic, int ki) {
return jcp.typesize_in
* ((ii * jcp.nb_ic * jcp.kd * jcp.kh * jcp.kw + ki)
* ch_block_all
+ ic_sub_step * ic * oc_block);
};
auto compute = [this](Vmm vreg_acc, Vmm vreg_wei, Vmm vreg_src) {
if (jcp.has_vnni) {
vpdpbusd(vreg_acc, vreg_src, vreg_wei, VexEncoding);
} else {
uni_vpmaddubsw(vmm_tmp, vreg_src, vreg_wei);
uni_vpmaddwd(vmm_tmp, vmm_tmp, vmm_one);
uni_vpaddd(vreg_acc, vreg_acc, vmm_tmp);
}
};
for (int ki = 0; ki < kw; ++ki) {
const int ow_start = get_ow_start(ki, pad_l);
const int ow_end = get_ow_end(ur_w, ki, pad_r);
const int ic_tail_size = jcp.ic_without_padding % ic_sub_step;
const int _start = jcp.signed_input ? 0 : ow_start;
const int _end = jcp.signed_input ? ur_w : ow_end;
const int icb = (last_ic_block_flag != no_last_block)
? div_up((jcp.ic_without_padding % ic_block), ic_sub_step)
: ic_block / ic_sub_step;
if (compute_kernel) {
for (int ic = 0; ic < icb; ++ic) {
if (h_padded) {
if (ic == 0) {
const Vmm inp = vmm_inp(0, nb_oc_block);
uni_vmovups(inp, vmm_shift);
}
} else {
for (int jj = _start; jj < _end; ++jj) {
int aux_input_offset = input_offset(jj, ic, ki);
if (jj >= ow_start && jj < ow_end) {
const bool need_partial_ic_bcast = true
&& last_ic_block_flag == last_sp_block
&& ic_tail_size != 0 && ic == icb - 1;
if (need_partial_ic_bcast) {
const auto inp_bcastd_vmm
= vmm_inp(jj, nb_oc_block);
const auto inp_bcastd
= Xmm(inp_bcastd_vmm.getIdx());
load_bytes(inp_bcastd_vmm, aux_reg_inp,
aux_input_offset, ic_tail_size);
uni_vpbroadcastd(
vmm_inp(jj, nb_oc_block), inp_bcastd);
} else {
uni_vpbroadcastd(vmm_inp(jj, nb_oc_block),
ptr[aux_reg_inp + aux_input_offset]);
}
if (jcp.signed_input)
uni_vpaddb(vmm_inp(jj, nb_oc_block),
vmm_inp(jj, nb_oc_block), vmm_shift);
} else {
if (jcp.signed_input && ic == 0) {
const Vmm inp = vmm_inp(jj, nb_oc_block);
uni_vmovups(inp, vmm_shift);
}
}
}
}
for (int ii = 0; ii < nb_oc_block; ++ii) {
const int aux_kernel_offset = kernel_offset(ii, ic, ki);
uni_vmovdqu(vmm_wei, ptr[aux_reg_ker + aux_kernel_offset]);
for (int jj = _start; jj < _end; ++jj) {
const Vmm inp = vmm_inp(h_padded ? 0 : jj, nb_oc_block);
compute(vmm_out(jj, ii), vmm_wei, inp);
}
}
}
}
if (jcp.src_zero_point) {
uni_vpbroadcastd(vmm_zp, ptr[reg_src_zero_point]);
const Vmm vmm_wacc = vmm_inp(0, nb_oc_block);
for (int jj = 0; jj < ur_w; jj++) {
if (jj < ow_start || jj >= ow_end || h_padded) {
for (int ii = 0; ii < nb_oc_block; ii++) {
uni_vpxor(vmm_tmp, vmm_tmp, vmm_tmp);
for (int ic = 0; ic < icb; ic++) {
const int aux_kernel_offset
= kernel_offset(ii, ic, ki);
if (jcp.has_vnni) {
vpdpbusd(vmm_tmp, vmm_zp_one,
ptr[aux_reg_ker + aux_kernel_offset],
VexEncoding);
} else {
uni_vpmaddubsw(vmm_wacc, vmm_zp_one,
ptr[aux_reg_ker + aux_kernel_offset]);
uni_vpmaddwd(vmm_wacc, vmm_wacc, vmm_one);
uni_vpaddd(vmm_tmp, vmm_tmp, vmm_wacc);
}
}
uni_vpmulld(vmm_tmp, vmm_tmp, vmm_zp);
uni_vpaddd(vmm_out(jj, ii), vmm_out(jj, ii), vmm_tmp);
}
}
}
}
}
if (jcp.src_zero_point) pop(aux_reg_ker_d);
}
template <cpu_isa_t isa, typename Vmm>
void jit_uni_x8s8s32x_fwd_kernel_vmm_t<isa, Vmm>::kh_loop(
int ur_w, int pad_l, int pad_r, ic_block_t last_ic_block_flag) {
Label kd_label, kh_label, skip_kd_loop, skip_kh_loop;
Label f_overflow_label, no_f_overflow_label, d_h_f_overflow_label,
t_overflow_label, no_t_overflow_label, b_overflow_label,
no_b_overflow_label, back_overflow_label, no_back_overflow_label,
d_h_back_overflow_label;
int ch_block_all = jcp.ch_block * jcp.ic_block * jcp.oc_block;
int shift_kernel_ptr = jcp.typesize_in * jcp.kw * ch_block_all;
int shift_input_ptr
= jcp.typesize_in * jcp.iw * jcp.ic_without_padding * jcp.ngroups;
if (jcp.src_zero_point && !jcp.is_depthwise) {
const auto xmm_one = Xbyak::Xmm(vmm_zp_one.getIdx());
mov(reg_scratch, 0x01010101);
uni_vmovq(xmm_one, reg_scratch);
uni_vpbroadcastd(vmm_zp_one, xmm_one);
}
if (jcp.ndims == 5) {
mov(aux_reg_ker_d, reg_ker);
mov(aux_reg_inp_d, reg_inp);
if (jcp.signed_input || jcp.src_zero_point) {
mov(reg_ki, ptr[param1 + GET_OFF(f_overflow)]);
cmp(reg_ki, 0);
je(no_f_overflow_label, T_NEAR);
L(f_overflow_label);
{
mov(aux_reg_ker, aux_reg_ker_d);
mov(reg_kj, jcp.kh);
L(d_h_f_overflow_label);
{
compute_ker(ur_w, pad_l, pad_r, last_ic_block_flag, true);
add(aux_reg_ker, shift_kernel_ptr);
dec(reg_kj);
jne(d_h_f_overflow_label);
}
add(aux_reg_ker_d, shift_kernel_ptr * jcp.kh);
dec(reg_ki);
jne(f_overflow_label);
}
L(no_f_overflow_label);
}
mov(reg_ki, ptr[param1 + GET_OFF(kd_padding)]);
if ((jcp.signed_input || jcp.src_zero_point) || (jcp.dilate_d >= jcp.id)
|| (!(jcp.signed_input || jcp.src_zero_point)
&& (jcp.kd - 1) * (jcp.dilate_d + 1)
< nstl::max(jcp.f_pad, jcp.back_pad))) {
cmp(reg_ki, 0);
je(skip_kd_loop, T_NEAR);
}
L(kd_label);
mov(aux_reg_inp, aux_reg_inp_d);
mov(aux_reg_ker, aux_reg_ker_d);
} else {
if (jcp.is_fused_conv) {
mov(aux_reg_inp_buffer_ptr, reg_inp_buffer_ptr);
} else {
mov(aux_reg_inp, reg_inp);
}
mov(aux_reg_ker, reg_ker);
}
if ((jcp.signed_input || jcp.src_zero_point) && jcp.ndims > 3) {
mov(reg_overflow, ptr[param1 + GET_OFF(t_overflow)]);
cmp(reg_overflow, 0);
je(no_t_overflow_label, T_NEAR);
L(t_overflow_label);
{
compute_ker(ur_w, pad_l, pad_r, last_ic_block_flag, true);
add(aux_reg_ker, shift_kernel_ptr);
dec(reg_overflow);
cmp(reg_overflow, 0);
jg(t_overflow_label, T_NEAR);
}
L(no_t_overflow_label);
}
mov(reg_kj, ptr[param1 + GET_OFF(kh_padding)]);
if ((jcp.signed_input || jcp.src_zero_point) || (jcp.dilate_h >= jcp.ih)
|| (!(jcp.signed_input || jcp.src_zero_point)
&& (jcp.kh - 1) * (jcp.dilate_h + 1)
< nstl::max(jcp.t_pad, jcp.b_pad))) {
cmp(reg_kj, 0);
je(skip_kh_loop, T_NEAR);
}
L(kh_label);
{
if (jcp.is_fused_conv) {
mov(aux_reg_inp, ptr[aux_reg_inp_buffer_ptr]);
add(aux_reg_inp, reg_inp);
}
compute_ker(ur_w, pad_l, pad_r, last_ic_block_flag, false);
add(aux_reg_ker, shift_kernel_ptr);
if (jcp.is_fused_conv) {
add(aux_reg_inp_buffer_ptr, sizeof(void *));
} else {
add(aux_reg_inp, shift_input_ptr * (jcp.dilate_h + 1));
}
dec(reg_kj);
cmp(reg_kj, 0);
jg(kh_label, T_NEAR);
}
L(skip_kh_loop);
if ((jcp.signed_input || jcp.src_zero_point) && jcp.ndims > 3) {
mov(reg_overflow, ptr[param1 + GET_OFF(b_overflow)]);
cmp(reg_overflow, 0);
je(no_b_overflow_label, T_NEAR);
L(b_overflow_label);
{
compute_ker(ur_w, pad_l, pad_r, last_ic_block_flag, true);
add(aux_reg_ker, shift_kernel_ptr);
dec(reg_overflow);
cmp(reg_overflow, 0);
jg(b_overflow_label, T_NEAR);
}
L(no_b_overflow_label);
}
if (jcp.ndims == 5) {
add(aux_reg_inp_d, shift_input_ptr * jcp.ih * (jcp.dilate_d + 1));
add(aux_reg_ker_d, shift_kernel_ptr * jcp.kh);
dec(reg_ki);
jne(kd_label, T_NEAR);
L(skip_kd_loop);
if (jcp.signed_input || jcp.src_zero_point) {
mov(reg_ki, ptr[param1 + GET_OFF(back_overflow)]);
cmp(reg_ki, 0);
je(no_back_overflow_label, T_NEAR);
L(back_overflow_label);
{
mov(aux_reg_ker, aux_reg_ker_d);
mov(reg_kj, jcp.kh);
L(d_h_back_overflow_label);
{
compute_ker(ur_w, pad_l, pad_r, last_ic_block_flag, true);
add(aux_reg_ker, shift_kernel_ptr);
dec(reg_kj);
jne(d_h_back_overflow_label);
}
add(aux_reg_ker_d, shift_kernel_ptr * jcp.kh);
dec(reg_ki);
jne(back_overflow_label);
}
L(no_back_overflow_label);
}
}
}
template <cpu_isa_t isa, typename Vmm>
void jit_uni_x8s8s32x_fwd_kernel_vmm_t<isa, Vmm>::icb_loop(
int ur_w, int pad_l, int pad_r, bool is_last_sp_block) {
prepare_output(ur_w);
Label icb_label;
mov(reg_icb, jcp.nb_ic);
L(icb_label);
const bool do_icb_loop
= jcp.is_depthwise ? jcp.nb_ch > jcp.nb_ch_blocking : jcp.nb_ic > 1;
if (jcp.ngroups % jcp.ch_block != 0 || jcp.ic_without_padding != jcp.ic) {
Label common_ker, end_ker;
if (do_icb_loop) {
if (jcp.is_depthwise)
cmp(reg_oc_blocks, jcp.nb_ch - jcp.nb_ch_blocking);
else
cmp(reg_icb, 1); jne(common_ker, T_NEAR);
}
kh_loop(ur_w, pad_l, pad_r,
is_last_sp_block ? last_sp_block : last_ic_block);
if (do_icb_loop) {
jmp(end_ker, T_NEAR);
L(common_ker);
kh_loop(ur_w, pad_l, pad_r, no_last_block);
L(end_ker);
}
} else {
kh_loop(ur_w, pad_l, pad_r, no_last_block);
}
if (do_icb_loop) {
int inp_step = jcp.ic_block;
const size_t ker_step = (size_t)jcp.kd * jcp.kh * jcp.kw * jcp.oc_block
* jcp.ic_block;
add(reg_inp, jcp.typesize_in * inp_step);
safe_add(reg_ker, jcp.typesize_in * ker_step, reg_ker_long_offt);
dec(reg_icb);
cmp(reg_icb, 0);
jg(icb_label, T_NEAR);
sub(reg_inp, jcp.typesize_in * inp_step * jcp.nb_ic);
safe_sub(reg_ker, jcp.typesize_in * ker_step * jcp.nb_ic,
reg_ker_long_offt);
}
if (jcp.ngroups % jcp.ch_block != 0 || jcp.oc_without_padding != jcp.oc) {
Label common_store, end_store;
if (jcp.is_depthwise)
cmp(reg_oc_blocks, jcp.nb_ch - jcp.nb_ch_blocking);
else
cmp(reg_oc_blocks, jcp.nb_oc - jcp.nb_oc_blocking);
jne(common_store, T_NEAR);
store_output(ur_w, true); jmp(end_store, T_NEAR);
L(common_store);
store_output(ur_w, false);
L(end_store);
} else {
store_output(ur_w, false);
}
}
template <cpu_isa_t isa, typename Vmm>
void jit_uni_x8s8s32x_fwd_kernel_vmm_t<isa, Vmm>::generate() {
Label permute_index_table;
int in_ic_shift = jcp.is_fused_conv ? jcp.dw_conv_buffer_oc
: jcp.ic_without_padding * jcp.ngroups;
const int urw_inp_stride = jcp.ur_w * jcp.stride_w;
const int n_urw_l_pad = nstl::min(
div_up(nstl::max(0, jcp.l_pad), urw_inp_stride), jcp.ow / jcp.ur_w);
const int inp_shift_pad = nstl::max(0,
jcp.typesize_in * (n_urw_l_pad * urw_inp_stride - jcp.l_pad)
* in_ic_shift);
int inp_shift = jcp.typesize_in * (jcp.ur_w * jcp.stride_w * in_ic_shift);
int out_shift = jcp.typesize_out
* (jcp.ur_w * jcp.oc_without_padding * jcp.ngroups);
preamble();
if (jcp.is_depthwise) {
const bool is_zero_point = jcp.src_zero_point || jcp.dst_zero_point;
int idx = ker_max_reg + 1 - jcp.max_regs_ur - 2 * is_zero_point;
if (!jcp.is_resrc_depthwise) vmm_dw_src = Vmm(--idx);
if (!jcp.has_vnni) vmm_dw_tmp = Vmm(--idx);
if (jcp.signed_input) {
--idx; }
assert(IMPLICATION(
!is_zero_point, idx == ker_max_reg - ker_dw_reg_base_idx));
}
if (!jcp.is_depthwise && (!jcp.has_vnni)) {
auto vmm_one_128 = Xbyak::Xmm(vmm_one.getIdx());
mov(reg_scratch, 0x10001);
uni_vmovq(vmm_one_128, reg_scratch);
uni_vpbroadcastd(vmm_one, vmm_one_128);
}
if (jcp.is_fused_conv) {
mov(reg_inp_buffer_ptr, ptr[param1 + GET_OFF(src)]);
xor_(reg_inp, reg_inp);
} else {
mov(reg_inp, ptr[param1 + GET_OFF(src)]);
}
mov(reg_out, ptr[param1 + GET_OFF(dst)]);
mov(reg_ker, ptr[param1 + GET_OFF(filt)]);
if (jcp.ngroups % jcp.ch_block != 0 || jcp.oc_without_padding != jcp.oc) {
mov(reg_oc_blocks, ptr[param1 + GET_OFF(oc_blocks)]);
}
const int extended_filter_size
= calculate_extended_filter_size(jcp.kw, jcp.dilate_w);
const int r_pad = nstl::max(0, jcp.r_pad);
const int ow_with_no_rpad = 1
+ (jcp.iw + jcp.l_pad + nstl::min(0, jcp.r_pad)
- extended_filter_size)
/ jcp.stride_w;
const int n_urw_per_ow_block = jcp.ow_block / jcp.ur_w;
const int max_safe_iw = nstl::max(0,
jcp.iw
- div_up(static_cast<int>(ic_sub_step),
jcp.ic_without_padding));
const int max_safe_ow = jcp.ic_without_padding % ic_sub_step == 0
? jcp.ow
: (max_safe_iw + jcp.l_pad - extended_filter_size) / jcp.stride_w;
Label middle_block_label, done_compute;
std::vector<Label> ow_block_jmp_table;
int r_pad_fall_through_ow_block = 0;
int r_pad_fall_through_n_urw = 0;
if (jcp.nb_ow > 1) {
int n_l_pad_labels = 0;
int n_labels = 0;
int cur_ow = 0;
n_l_pad_labels = div_up(n_urw_l_pad, n_urw_per_ow_block);
n_labels = n_l_pad_labels;
cur_ow += n_urw_l_pad * jcp.ur_w;
int n_urw_middle_block_loop = 0;
int cur_r_pad = nstl::max(0,
calculate_end_padding(jcp.l_pad, cur_ow + jcp.ur_w, jcp.iw,
jcp.stride_w, extended_filter_size));
if (cur_ow + jcp.ur_w <= jcp.ow && cur_r_pad == 0) {
n_urw_middle_block_loop
= nstl::max(0,
nstl::min(ow_with_no_rpad, max_safe_ow) - cur_ow)
/ jcp.ur_w;
cur_ow += n_urw_middle_block_loop * jcp.ur_w;
}
r_pad_fall_through_n_urw = (cur_ow / jcp.ur_w) % n_urw_per_ow_block;
r_pad_fall_through_ow_block = cur_ow / (n_urw_per_ow_block * jcp.ur_w);
if (cur_ow + jcp.ur_w <= jcp.ow) {
if (r_pad_fall_through_n_urw == 0) ++n_labels;
const int n_urw_r_pad_region = (jcp.ow - cur_ow) / jcp.ur_w;
n_labels += nstl::max(0,
div_up(r_pad_fall_through_n_urw + n_urw_r_pad_region,
n_urw_per_ow_block)
- 1);
}
if (jcp.ur_w_tail != 0) {
if (jcp.ow % jcp.ow_block == jcp.ur_w_tail) ++n_labels;
}
ow_block_jmp_table.resize(n_labels);
Label ow_block_jmp_table_label;
if (!ow_block_jmp_table.empty())
mov(reg_jmp_tbl_base, ow_block_jmp_table_label);
mov(reg_oi, n_urw_per_ow_block);
mov(reg_owb, ptr[param1 + GET_OFF(owb)]);
if (jcp.l_pad > 0) {
Label middle_or_rpad_check;
cmp(reg_owb, n_l_pad_labels);
jge(middle_or_rpad_check, T_NEAR);
jmp(ptr[reg_jmp_tbl_base + reg_owb * sizeof(void *)]);
L(middle_or_rpad_check);
const int inp_shift_pad_middle_block = -1 * jcp.typesize_in
* nstl::min(jcp.l_pad, n_urw_l_pad * urw_inp_stride)
* in_ic_shift;
add(reg_inp, inp_shift_pad_middle_block);
}
if (r_pad_fall_through_n_urw != 0) {
mov(reg_scratch, r_pad_fall_through_n_urw);
cmp(reg_owb, r_pad_fall_through_ow_block);
cmove(reg_oi, reg_scratch);
if (n_urw_middle_block_loop > 0) {
sub(reg_owb, r_pad_fall_through_ow_block);
jle(middle_block_label, T_NEAR);
dec(reg_owb);
} else {
sub(reg_owb, r_pad_fall_through_ow_block + 1);
}
} else {
sub(reg_owb, r_pad_fall_through_ow_block);
if (n_urw_middle_block_loop) jl(middle_block_label, T_NEAR);
}
if (!ow_block_jmp_table.empty())
jmp(ptr[reg_jmp_tbl_base + reg_owb * sizeof(void *)
+ n_l_pad_labels * sizeof(void *)]);
if (!ow_block_jmp_table.empty()) {
align(8);
L(ow_block_jmp_table_label);
{
for (size_t i = 0; i < ow_block_jmp_table.size(); ++i) {
putL(ow_block_jmp_table[i]);
}
}
}
}
int cur_ow = 0;
int cur_n_oi = 0; int label_cntr = 0;
int cur_l_pad = 0;
if (jcp.l_pad > 0) {
for (cur_l_pad = jcp.l_pad;
cur_l_pad > 0 && cur_ow + jcp.ur_w <= jcp.ow;
cur_l_pad -= urw_inp_stride) {
if (jcp.nb_ow > 1 && cur_n_oi == 0) {
const dim_t inp_lpad_region_shift
= static_cast<dim_t>(-label_cntr) * jcp.ow_block
* jcp.stride_w * in_ic_shift;
L(ow_block_jmp_table[label_cntr++]);
add(reg_inp, inp_lpad_region_shift);
}
cur_ow += jcp.ur_w;
int cur_r_pad = nstl::max(0,
calculate_end_padding(jcp.l_pad, cur_ow, jcp.iw,
jcp.stride_w, extended_filter_size));
icb_loop(jcp.ur_w, cur_l_pad, cur_r_pad, cur_ow > max_safe_ow);
add(reg_out, out_shift);
dec(reg_oi);
if (jcp.nb_ow > 1 && ++cur_n_oi == n_urw_per_ow_block) {
jmp(done_compute, T_NEAR);
cur_n_oi = 0;
}
}
if (jcp.nb_ow == 1 || cur_n_oi != 0) {
add(reg_inp, inp_shift_pad);
}
}
{
int cur_r_pad = nstl::max(0,
calculate_end_padding(jcp.l_pad, cur_ow + jcp.ur_w, jcp.iw,
jcp.stride_w, extended_filter_size));
if (cur_r_pad == 0 && cur_ow + jcp.ur_w <= jcp.ow) {
int n_oi_middle_block_loop
= nstl::max(0,
nstl::min(ow_with_no_rpad, max_safe_ow) - cur_ow)
/ jcp.ur_w;
if (jcp.nb_ow == 1 && n_oi_middle_block_loop > 1)
mov(reg_oi, n_oi_middle_block_loop);
L(middle_block_label);
if (n_oi_middle_block_loop > 0) {
icb_loop(jcp.ur_w, 0, 0, false);
add(reg_inp, inp_shift);
add(reg_out, out_shift);
if (n_oi_middle_block_loop > 1) {
dec(reg_oi);
jg(middle_block_label, T_NEAR);
}
}
cur_ow += n_oi_middle_block_loop * jcp.ur_w;
cur_n_oi = (cur_n_oi + n_oi_middle_block_loop) % n_urw_per_ow_block;
}
}
if (cur_ow + jcp.ur_w <= jcp.ow) {
if (jcp.nb_ow > 1) {
if (cur_n_oi == 0) {
jmp(done_compute, T_NEAR);
} else {
mov(reg_owb, ptr[param1 + GET_OFF(owb)]);
cmp(reg_owb, r_pad_fall_through_ow_block);
jne(done_compute, T_NEAR);
}
}
while (cur_ow + jcp.ur_w <= jcp.ow) {
if (jcp.nb_ow > 1 && cur_n_oi == 0) {
L(ow_block_jmp_table[label_cntr++]);
}
cur_ow += jcp.ur_w;
int cur_r_pad = calculate_end_padding(jcp.l_pad, cur_ow, jcp.iw,
jcp.stride_w, extended_filter_size);
assert(cur_r_pad > 0 || cur_ow > max_safe_ow); icb_loop(jcp.ur_w, 0, cur_r_pad, cur_ow > max_safe_ow);
add(reg_inp, inp_shift);
add(reg_out, out_shift);
if (jcp.nb_ow > 1 && ++cur_n_oi == n_urw_per_ow_block) {
jmp(done_compute, T_NEAR);
cur_n_oi = 0;
}
}
}
if (jcp.ur_w_tail != 0) {
if (jcp.nb_ow > 1) {
if (cur_n_oi == 0) {
jmp(done_compute, T_NEAR);
L(ow_block_jmp_table[label_cntr++]);
} else {
mov(reg_owb, ptr[param1 + GET_OFF(owb)]);
cmp(reg_owb, jcp.nb_ow - 1); jne(done_compute, T_NEAR);
}
}
icb_loop(jcp.ur_w_tail, nstl::max(0, cur_l_pad), r_pad, true);
}
L(done_compute);
assert(ow_block_jmp_table.size() == static_cast<size_t>(label_cntr));
postamble();
if (jcp.with_eltwise)
postops_injector_->prepare_table( true);
}
template <cpu_isa_t isa>
status_t jit_uni_x8s8s32x_fwd_kernel_t<isa>::init_conf(jit_conv_conf_t &jcp,
const convolution_desc_t &cd, memory_desc_t &src_md,
memory_desc_t &weights_md, memory_desc_t &dst_md,
memory_desc_t &bias_md, primitive_attr_t &attr, int nthreads) {
using namespace prop_kind;
const memory_desc_wrapper src_d(&src_md);
const memory_desc_wrapper weights_d(&weights_md);
const memory_desc_wrapper dst_d(&dst_md);
const memory_desc_wrapper bias_d(&bias_md);
VDISPATCH_CONV_IC(!has_large_size(cd, src_d, weights_d, dst_d),
VERBOSE_BAD_PARAM, "large size is not supported");
const bool with_groups = weights_d.ndims() == src_d.ndims() + 1;
const int ndims = src_d.ndims();
const bool is_1d = ndims == 3;
const bool is_2d = ndims == 4;
const bool is_3d = ndims == 5;
const bool is_avx2 = isa == avx2;
assert(is_1d || is_2d || is_3d);
if (!mayiuse(isa)) return status::unimplemented;
const bool dt_ok = one_of(src_d.data_type(), data_type::u8, data_type::s8)
&& weights_d.data_type() == data_type::s8
&& one_of(dst_d.data_type(), data_type::f32, data_type::s32,
data_type::s8, data_type::u8);
VDISPATCH_CONV_IC(dt_ok, VERBOSE_UNSUPPORTED_DT_CFG);
jcp = zero<decltype(jcp)>();
jcp.nthr = nthreads;
jcp.ndims = ndims;
jcp.prop_kind = cd.prop_kind;
jcp.ngroups = with_groups ? weights_d.dims()[0] : 1;
jcp.mb = src_d.dims()[0];
jcp.oc = dst_d.dims()[1] / jcp.ngroups;
jcp.oc_without_padding = jcp.oc;
jcp.ic = src_d.dims()[1] / jcp.ngroups;
jcp.ic_without_padding = jcp.ic;
jcp.id = is_3d ? src_d.dims()[2] : 1;
jcp.ih = is_1d ? 1 : src_d.dims()[ndims - 2];
jcp.iw = src_d.dims()[ndims - 1];
jcp.od = is_3d ? dst_d.dims()[2] : 1;
jcp.oh = is_1d ? 1 : dst_d.dims()[ndims - 2];
jcp.ow = dst_d.dims()[ndims - 1];
jcp.kd = is_3d ? weights_d.dims()[with_groups + 2] : 1;
jcp.kh = is_1d ? 1 : weights_d.dims()[with_groups + ndims - 2];
jcp.kw = weights_d.dims()[with_groups + ndims - 1];
jcp.f_pad = is_3d ? cd.padding[0][0] : 0;
jcp.t_pad = is_1d ? 0 : cd.padding[0][ndims - 4];
jcp.l_pad = cd.padding[0][ndims - 3];
jcp.stride_d = is_3d ? cd.strides[0] : 1;
jcp.stride_h = is_1d ? 1 : cd.strides[ndims - 4];
jcp.stride_w = cd.strides[ndims - 3];
jcp.with_bias = cd.bias_desc.format_kind != format_kind::undef;
jcp.ur_h = 1;
jcp.dilate_d = is_3d ? cd.dilates[0] : 0;
jcp.dilate_h = is_1d ? 0 : cd.dilates[ndims - 4];
jcp.dilate_w = cd.dilates[ndims - 3];
int ext_kw = calculate_extended_filter_size(jcp.kw, jcp.dilate_w);
int ext_kh = calculate_extended_filter_size(jcp.kh, jcp.dilate_h);
int ext_kd = calculate_extended_filter_size(jcp.kd, jcp.dilate_d);
jcp.r_pad = calculate_end_padding(
jcp.l_pad, jcp.ow, jcp.iw, jcp.stride_w, ext_kw);
jcp.b_pad = calculate_end_padding(
jcp.t_pad, jcp.oh, jcp.ih, jcp.stride_h, ext_kh);
jcp.back_pad = calculate_end_padding(
jcp.f_pad, jcp.od, jcp.id, jcp.stride_d, ext_kd);
jcp.has_vnni = mayiuse(avx2_vnni);
jcp.signed_input = src_d.data_type() == data_type::s8;
jcp.is_depthwise = true && with_groups && everyone_is(1, jcp.ic, jcp.oc);
jcp.is_oc_scale = attr.scales_.get_mask(DNNL_ARG_WEIGHTS) > 0;
jcp.with_src_scales = !attr.scales_.get(DNNL_ARG_SRC).has_default_values();
jcp.with_wei_scales
= !attr.scales_.get(DNNL_ARG_WEIGHTS).has_default_values();
jcp.with_dst_scales = !attr.scales_.get(DNNL_ARG_DST).has_default_values();
const auto &zp = attr.zero_points_;
jcp.dst_zero_point = !zp.has_default_values(DNNL_ARG_DST);
jcp.src_zero_point = !zp.has_default_values(DNNL_ARG_SRC);
jcp.zp_src_is_common
= zp.get_mask(DNNL_ARG_SRC) == 0; assert(IMPLICATION(jcp.src_zero_point, jcp.zp_src_is_common));
VDISPATCH_CONV_IC(
!((jcp.dst_zero_point || jcp.src_zero_point) && jcp.is_fused_conv),
VERBOSE_UNSUPPORTED_FEATURE,
"fused depthwise convolution does not support zero-point");
VDISPATCH_CONV_IC(!(is_3d && jcp.is_depthwise), VERBOSE_UNSUPPORTED_FEATURE,
"unsupported depthwise implementation for 3D convolution");
if (jcp.is_depthwise) {
jcp.ch_block = is_avx2 ? 8 : 4;
jcp.ic_block = 1;
jcp.oc_block = 1;
} else {
jcp.ch_block = 1;
jcp.ic_block = is_avx2 ? 8 : 4;
jcp.oc_block = is_avx2 ? 8 : 4;
if (jcp.ngroups == 1) {
jcp.oc = rnd_up(jcp.oc, jcp.oc_block);
jcp.ic = rnd_up(jcp.ic, jcp.ic_block);
} else if (jcp.ngroups != 1
&& ((jcp.ic % jcp.ic_block != 0)
|| (jcp.oc % jcp.oc_block != 0))) {
jcp.oc_block = jcp.ic_block = 4;
}
VDISPATCH_CONV_IC(
!(jcp.ic % jcp.ic_block != 0 || jcp.oc % jcp.oc_block != 0),
VERBOSE_BLOCKING_FAIL, "bad blocking parameters");
}
jcp.is_resrc_depthwise = true && jcp.is_depthwise && jcp.stride_w < jcp.kw
&& jcp.kw < 4 && jcp.dilate_w == 0;
if (jcp.is_depthwise) {
jcp.max_regs_ur = 14 - !jcp.is_resrc_depthwise - jcp.signed_input
+ (jcp.has_vnni);
} else {
jcp.max_regs_ur = jcp.has_vnni ? 15 - jcp.signed_input : 12;
}
if (jcp.src_zero_point || jcp.dst_zero_point) jcp.max_regs_ur = 9;
auto set_or_check_wei_format = [&]() {
using namespace format_tag;
using namespace memory_extra_flags;
const int c_mask = 0x1,
g_mask = 0x3; format_tag_t wei_tag;
if (jcp.ic_block == 8 || jcp.ch_block == 8) {
if (is_1d) {
wei_tag = with_groups ? jcp.is_depthwise ? Goiw8g : gOIw2i8o4i
: OIw2i8o4i;
} else if (is_2d) {
wei_tag = with_groups ? jcp.is_depthwise ? Goihw8g : gOIhw2i8o4i
: OIhw2i8o4i;
} else {
wei_tag = with_groups ? gOIdhw2i8o4i : OIdhw2i8o4i;
}
} else {
if (is_avx2) {
assert(with_groups && jcp.ic_block == 4);
wei_tag = is_3d ? gOIdhw4o4i : is_2d ? gOIhw4o4i : gOIw4o4i;
} else {
if (is_1d) {
wei_tag = with_groups ? jcp.is_depthwise ? Goiw4g : gOIw4o4i
: OIw4o4i;
} else if (is_2d) {
wei_tag = with_groups
? jcp.is_depthwise ? Goihw4g : gOIhw4o4i
: OIhw4o4i;
} else {
wei_tag = with_groups ? gOIdhw4o4i : OIdhw4o4i;
}
}
}
memory_desc_t want_wei_md = weights_md;
CHECK_BOOL(memory_desc_init_by_tag(want_wei_md, wei_tag));
if (jcp.signed_input) {
want_wei_md.extra.flags = 0
| memory_extra_flags::compensation_conv_s8s8
| memory_extra_flags::scale_adjust;
want_wei_md.extra.compensation_mask
= (with_groups && !jcp.is_depthwise) ? g_mask : c_mask;
want_wei_md.extra.scale_adjust = (jcp.has_vnni) ? 1.f : 0.5f;
}
if (jcp.src_zero_point) {
want_wei_md.extra.flags |= compensation_conv_asymmetric_src;
want_wei_md.extra.asymm_compensation_mask
= (with_groups && !jcp.is_depthwise) ? g_mask : c_mask;
}
if (weights_md.format_kind == format_kind::any) {
weights_md = want_wei_md;
return true;
}
return weights_md == want_wei_md;
};
VDISPATCH_CONV_IC(
set_or_check_wei_format(), VERBOSE_UNSUPPORTED_TAG_S, "weights");
format_tag_t dat_tag = utils::pick(
ndims - 3, format_tag::nwc, format_tag::nhwc, format_tag::ndhwc);
if (src_d.format_kind() == format_kind::any) {
CHECK(memory_desc_init_by_tag(src_md, dat_tag));
jcp.src_tag = dat_tag;
} else {
jcp.src_tag = src_d.matches_one_of_tag(dat_tag);
}
VDISPATCH_CONV_IC(jcp.src_tag == dat_tag, VERBOSE_UNSUPPORTED_TAG_S, "src");
if (dst_d.format_kind() == format_kind::any) {
CHECK(memory_desc_init_by_tag(dst_md, dat_tag));
jcp.dst_tag = dat_tag;
} else {
jcp.dst_tag = dst_d.matches_one_of_tag(dat_tag);
}
VDISPATCH_CONV_IC(jcp.dst_tag == dat_tag, VERBOSE_UNSUPPORTED_TAG_S, "dst");
if (jcp.with_bias) {
if (bias_d.format_kind() == format_kind::any)
CHECK(memory_desc_init_by_tag(bias_md, format_tag::x));
}
jcp.bia_dt = jcp.with_bias ? cd.bias_desc.data_type : data_type::undef;
jcp.dst_dt = cd.dst_desc.data_type;
CHECK(attr.set_default_formats(&dst_md));
const auto &post_ops = attr.post_ops_;
const int eltwise_ind = post_ops.find(primitive_kind::eltwise);
jcp.with_eltwise = eltwise_ind != -1;
const int binary_ind = post_ops.find(primitive_kind::binary);
const int prelu_ind = post_ops.find(primitive_kind::prelu);
jcp.with_binary = !everyone_is(-1, binary_ind, prelu_ind);
const int sum_ind = post_ops.find(primitive_kind::sum);
jcp.with_sum = sum_ind != -1;
jcp.sum_dt = post_ops.get_sum_dt(jcp.dst_dt);
jcp.post_ops = post_ops;
using namespace injector;
const bool post_ops_ok_ = post_ops_ok(post_ops_ok_args_t(isa,
{eltwise, binary, sum}, jcp.post_ops, &dst_d, false, false, false));
VDISPATCH_CONV_IC(post_ops_ok_, VERBOSE_UNSUPPORTED_POSTOP);
jcp.typesize_in = types::data_type_size(src_d.data_type());
jcp.typesize_out = types::data_type_size(dst_d.data_type());
jcp.typesize_bia
= jcp.with_bias ? types::data_type_size(bias_d.data_type()) : 0;
jcp.nb_ch = div_up(jcp.ngroups, jcp.ch_block);
jcp.nb_ic = jcp.ic / jcp.ic_block;
jcp.nb_oc = jcp.oc / jcp.oc_block;
int nb_ch_blocking = 4;
for (; nb_ch_blocking > 1; --nb_ch_blocking)
if (jcp.nb_ch % nb_ch_blocking == 0) break;
jcp.nb_ch_blocking = jcp.is_depthwise ? nb_ch_blocking : 1;
auto is_oc_blocking_ok = [&](int block) {
int ur_w = nstl::min(jcp.ow, jcp.max_regs_ur / (block + 1));
return jcp.nb_oc % block == 0 && jcp.l_pad <= ur_w
&& jcp.ow % ur_w != 1;
};
int max_threading_nb_oc_chunk = 4;
jcp.nb_oc_blocking_thr_chunk
= nstl::min(max_threading_nb_oc_chunk, jcp.nb_oc);
for (; jcp.nb_oc_blocking_thr_chunk > 1; --jcp.nb_oc_blocking_thr_chunk) {
if (is_oc_blocking_ok(jcp.nb_oc_blocking_thr_chunk)) break;
}
auto get_thr_eff = [&](int nb_ow, int nthr) {
int base_work_amount = jcp.mb * jcp.nb_ch * jcp.od * jcp.oh
* (jcp.nb_oc / jcp.nb_oc_blocking_thr_chunk);
auto work_amount = base_work_amount * nb_ow;
return float(work_amount) / rnd_up(work_amount, nthr);
};
auto get_ow_block = [&jcp, get_thr_eff](int ur_w, int nthr) {
int res_ow_block = jcp.ow;
float best_thr_eff = get_thr_eff(1, nthr);
float thr_eff;
int max_nb_ow = div_up(jcp.ow, ur_w);
for (int nb_ow = 1; nb_ow <= max_nb_ow; nb_ow++) {
int ow_block
= nstl::min(rnd_up(div_up(jcp.ow, nb_ow), ur_w), jcp.ow);
if (ow_block < jcp.nb_oc_blocking_thr_chunk * jcp.oc_block
&& best_thr_eff > 0.8f)
break;
if (div_up(jcp.ow, ow_block) != nb_ow) continue;
thr_eff = get_thr_eff(nb_ow, nthr);
if (ow_block >= ur_w && thr_eff > 1.1f * best_thr_eff) {
res_ow_block = ow_block;
best_thr_eff = thr_eff;
}
if (best_thr_eff > 0.9f) break;
}
return res_ow_block;
};
jcp.nb_oc_blocking = jcp.nb_oc_blocking_thr_chunk;
if (jcp.is_resrc_depthwise)
jcp.ur_w = (jcp.max_regs_ur - jcp.kw + jcp.stride_w)
/ (jcp.nb_ch_blocking + jcp.stride_w);
else
jcp.ur_w = jcp.max_regs_ur
/ (jcp.is_depthwise ? jcp.nb_ch_blocking
: jcp.nb_oc_blocking + 1);
if (jcp.ow < jcp.ur_w) jcp.ur_w = jcp.ow;
jcp.ur_w_tail = jcp.ow % jcp.ur_w;
jcp.ow_block = jcp.ow;
jcp.ow_block = get_ow_block(jcp.ur_w, jcp.nthr);
jcp.nb_ow = div_up(jcp.ow, jcp.ow_block);
float thr_eff = get_thr_eff(jcp.nb_ow, jcp.nthr);
size_t wei_size
= sizeof(float) * jcp.ic * jcp.oc * jcp.kh * jcp.kw * jcp.kd;
size_t out_size = static_cast<size_t>(jcp.mb) * jcp.typesize_out * jcp.oc
* jcp.oh * jcp.ow * jcp.od;
size_t inp_size = static_cast<size_t>(jcp.mb) * jcp.typesize_in * jcp.ic
* jcp.ih * jcp.iw * jcp.id;
size_t total_size = jcp.ngroups * (wei_size + out_size + inp_size);
const unsigned int L1_cache_size = platform::get_per_core_cache_size(1);
if (jcp.ngroups < jcp.nthr && (total_size < L1_cache_size)) {
int ow_block = jcp.ow_block;
float best_thr_eff = thr_eff;
float eff = -1.0f;
int end_nthr = with_groups ? jcp.ngroups : 1;
for (int nthr = jcp.nthr / 2; nthr >= end_nthr; nthr--) {
ow_block = get_ow_block(jcp.ur_w, nthr);
eff = get_thr_eff(div_up(jcp.ow, ow_block), nthr);
if (eff > 1.1f * best_thr_eff) {
best_thr_eff = eff;
jcp.ow_block = ow_block;
jcp.nb_ow = div_up(jcp.ow, jcp.ow_block);
jcp.nthr = jcp.aligned_threads = nthr;
if (best_thr_eff > 0.9f) break;
}
}
}
bool args_ok = true && jcp.oc % jcp.oc_block == 0
&& IMPLICATION(!jcp.is_1stconv, jcp.ic % jcp.ic_block == 0);
VDISPATCH_CONV_IC(
args_ok, VERBOSE_BLOCKING_FAIL, "bad blocking dimensions");
pick_loop_order(jcp);
jcp.nb_ic_L2 = jcp.nb_ic;
jcp.wei_adj_scale
= (weights_d.extra().flags & memory_extra_flags::scale_adjust)
? weights_d.extra().scale_adjust
: 1.f;
return status::success;
}
template <cpu_isa_t isa>
void jit_uni_x8s8s32x_fwd_kernel_t<isa>::init_scratchpad(
memory_tracking::registrar_t &scratchpad, const jit_conv_conf_t &jcp,
const primitive_attr_t &attr) {
if (jcp.with_dst_scales) {
const size_t n_dst_scales = static_cast<size_t>(
jcp.is_depthwise ? jcp.ngroups : jcp.nthr);
scratchpad.book(
key_conv_dst_scales, n_dst_scales * sizeof(float), 4096);
}
}
template struct jit_uni_x8s8s32x_fwd_kernel_vmm_t<avx2, Ymm>;
template struct jit_uni_x8s8s32x_fwd_kernel_vmm_t<avx2, Xmm>;
template struct jit_uni_x8s8s32x_fwd_kernel_vmm_t<sse41, Xmm>;
template struct jit_uni_x8s8s32x_fwd_kernel_t<avx2>;
template struct jit_uni_x8s8s32x_fwd_kernel_t<sse41>;
} } } }