#include <bitset>
#include "common/c_types_map.hpp"
#include "cpu/x64/jit_avx512_core_bf16cvt.hpp"
#include "cpu/x64/jit_generator.hpp"
#include "cpu/x64/jit_uni_resampling_kernel.hpp"
namespace dnnl {
namespace impl {
namespace cpu {
namespace x64 {
using namespace Xbyak;
using namespace format_tag;
using tag_kind = jit_memory_tag_kind_t;
#define GET_OFF(field) offsetof(jit_uni_resampling_args_t, field)
template <cpu_isa_t isa, typename Vmm>
jit_uni_resampling_kernel_t<isa, Vmm>::jit_uni_resampling_kernel_t(
const jit_resampling_conf_t &conf, const memory_desc_t *dst_md)
: jit_uni_resampling_kernel_base_t(conf)
, tail_size_(calculate_tail_size())
, io_(this, conf_.isa, {conf_.src_data_type, conf_.dst_data_type},
{can_movntps_be_used()},
io::io_tail_conf_t {simd_w_, tail_size_, k_tail_mask_,
vmm_tail_mask_.getIdx(), reg_tmp_},
io::io_emu_bf16_conf_t {vmm_bf16_emu_1_, vmm_bf16_emu_2_,
vmm_bf16_emu_3_, reg_tmp_, vmm_bf16_emu_4_},
create_saturation_vmm_map(),
io::io_gather_conf_t {simd_w_, k_full_mask_,
vmm_full_mask_.getIdx(), reg_tmp_, reg_tmp1_,
vmm_tmp_gather_.getIdx()}) {
if (conf_.with_postops) {
memory_desc_wrapper dst_d = memory_desc_wrapper(*dst_md);
static constexpr bool preserve_gpr = true;
static constexpr bool preserve_vmm = false;
static constexpr bool use_exact_tail_scalar_bcast = true;
const binary_injector::rhs_arg_static_params_t rhs_sp {
static_cast<size_t>(vmm_post_op_helper_.getIdx()), r14, r15,
r13, preserve_gpr, preserve_vmm,
GET_OFF(post_ops_binary_rhs_arg_vec), GET_OFF(dst_orig), dst_d,
tail_size_, k_tail_mask_, use_exact_tail_scalar_bcast};
const bcast_set_t accepted_broadcasts
= {broadcasting_strategy_t::scalar,
broadcasting_strategy_t::per_oc,
broadcasting_strategy_t::per_oc_spatial};
const binary_injector::static_params_t bsp {
reg_param, accepted_broadcasts, rhs_sp};
postops_injector_ = utils::make_unique<
injector::jit_uni_postops_injector_t<isa, Vmm>>(
this, conf_.post_ops, bsp);
std::tie(any_binary_postop_is_per_oc_bcast_type_,
any_binary_postop_is_per_oc_sp_bcast_type_)
= binary_injector_utils::bcast_strategies_present_tup(
conf_.post_ops.entry_, dst_d,
broadcasting_strategy_t::per_oc,
broadcasting_strategy_t::per_oc_spatial);
}
}
template <cpu_isa_t isa, typename Vmm>
bool jit_uni_resampling_kernel_t<isa, Vmm>::can_movntps_be_used() const {
const std::size_t alignment = simd_w_ * conf_.dst_dt_size;
assert(alignment > 0 && conf_.dst_dt_size > 0
&& "Incorrect output data type size.");
bool are_data_filling_register_fully = false;
switch (conf_.dst_data_type) {
case data_type::f32:
case data_type::s32: are_data_filling_register_fully = true; break;
case data_type::f16:
case data_type::bf16:
are_data_filling_register_fully = is_xmm_ ? false : true;
break;
case data_type::s8:
case data_type::u8:
are_data_filling_register_fully = is_zmm_ ? true : false;
break;
default: assert(!"Unsupported data type."); break;
}
bool can_use_movntps = false;
if (is_superset(conf_.isa, avx512_core) || conf_.dst_dt_size % 4 == 0)
can_use_movntps = conf_.is_data_size_bigger_than_L3
&& are_data_filling_register_fully
&& conf_.output_data_size % alignment == 0
&& (tail_size_ == 0 || conf_.tag_kind == tag_kind::blocked);
return can_use_movntps;
}
template <cpu_isa_t isa, typename Vmm>
std::size_t jit_uni_resampling_kernel_t<isa, Vmm>::calculate_tail_size() const {
std::size_t tail_size = 0;
if (conf_.tag_kind == tag_kind::nspc
|| conf_.tag_kind == tag_kind::blocked) {
tail_size = conf_.c % simd_w_;
} else if (conf_.tag_kind == tag_kind::ncsp) {
if (conf_.alg == alg_kind::resampling_nearest)
tail_size = conf_.ow % simd_w_;
else
tail_size = (conf_.od * conf_.oh * conf_.ow) % simd_w_;
} else
assert(!"Incorrect memory tag passed to resampling primitive.");
return tail_size;
}
template <cpu_isa_t isa, typename Vmm>
int jit_uni_resampling_kernel_t<isa, Vmm>::get_channels_to_compute_without_tail(
const bool is_tail_in_blocked_format) const {
assert(utils::one_of(conf_.tag_kind, tag_kind::blocked, tag_kind::nspc)
&& "Incorrect memory tag.");
int c_to_compute_without_tail = 0;
if (conf_.tag_kind == tag_kind::blocked && is_tail_in_blocked_format) {
c_to_compute_without_tail
= ((conf_.c % conf_.inner_stride) / simd_w_) * simd_w_;
} else
c_to_compute_without_tail = (conf_.inner_stride / simd_w_) * simd_w_;
return c_to_compute_without_tail;
}
template <cpu_isa_t isa, typename Vmm>
std::map<data_type_t, io::io_saturation_conf_t>
jit_uni_resampling_kernel_t<isa, Vmm>::create_saturation_vmm_map() const {
std::map<data_type_t, io::io_saturation_conf_t> saturation_map {};
if (conf_.is_saturation_needed)
saturation_map.emplace(conf_.dst_data_type,
io::io_saturation_conf_t {vmm_zero_saturation_.getIdx(),
vmm_saturation_ubound_.getIdx(), reg_tmp_});
return saturation_map;
}
template <cpu_isa_t isa, typename Vmm>
void jit_uni_resampling_kernel_t<isa,
Vmm>::get_params_for_linear_in_c_oriented_format() {
mov(reg_src_ftl_, ptr[reg_param + GET_OFF(src)]);
add(reg_src_ftl_, ptr[reg_param + GET_OFF(src_offset_front)]);
add(reg_src_ftl_, ptr[reg_param + GET_OFF(src_offset_top)]);
mov(reg_src_ftr_, reg_src_ftl_);
if (conf_.ndims == 4 || conf_.ndims == 5) {
uni_vbroadcastss(weight_top_, ptr[reg_param + GET_OFF(weight_top)]);
uni_vbroadcastss(
weight_bottom_, ptr[reg_param + GET_OFF(weight_bottom)]);
mov(reg_src_fbl_, ptr[reg_param + GET_OFF(src)]);
add(reg_src_fbl_, ptr[reg_param + GET_OFF(src_offset_front)]);
add(reg_src_fbl_, ptr[reg_param + GET_OFF(src_offset_bottom)]);
mov(reg_src_fbr_, reg_src_fbl_);
}
if (conf_.ndims == 5) {
uni_vbroadcastss(weight_front_, ptr[reg_param + GET_OFF(weight_front)]);
uni_vbroadcastss(weight_back_, ptr[reg_param + GET_OFF(weight_back)]);
mov(reg_src_btl_, ptr[reg_param + GET_OFF(src)]);
add(reg_src_btl_, ptr[reg_param + GET_OFF(src_offset_back)]);
add(reg_src_btl_, ptr[reg_param + GET_OFF(src_offset_top)]);
mov(reg_src_btr_, reg_src_btl_);
mov(reg_src_bbl_, ptr[reg_param + GET_OFF(src)]);
add(reg_src_bbl_, ptr[reg_param + GET_OFF(src_offset_back)]);
add(reg_src_bbl_, ptr[reg_param + GET_OFF(src_offset_bottom)]);
mov(reg_src_bbr_, reg_src_bbl_);
}
}
template <cpu_isa_t isa, typename Vmm>
void jit_uni_resampling_kernel_t<isa, Vmm>::preserve_zero_padding_in_post_ops(
const int data_idx) {
Vmm vmm_data(data_idx);
const Vmm vmm_zeros(vmm_tmp_.getIdx());
uni_vxorps(vmm_zeros, vmm_zeros, vmm_zeros);
if (is_superset(conf_.isa, avx512_core))
vblendmps(vmm_data | k_tail_mask_, vmm_zeros, vmm_data);
else {
std::bitset<8> tail_mask((1 << tail_size_) - 1);
tail_mask.flip();
uni_vblendps(vmm_data, vmm_data, vmm_zeros, tail_mask.to_ulong());
}
}
template <cpu_isa_t isa, typename Vmm>
void jit_uni_resampling_kernel_t<isa, Vmm>::apply_sum(
const int data_idx, const bool is_tail, const size_t offset) {
if (conf_.with_sum) {
assert(!conf_.sum_scales.empty()
&& "No scales for sum post operation.");
const auto sum_injector = [this, data_idx, is_tail, offset]() {
const Vmm vmm_prev_dst(vmm_tmp_.getIdx());
const Vmm vmm_dst(data_idx);
if (is_tail && conf_.tag_kind == tag_kind::blocked)
uni_vxorps(vmm_prev_dst, vmm_prev_dst, vmm_prev_dst);
io_.at(conf_.dst_data_type)
->load(ptr[reg_dst_ + offset], vmm_prev_dst, is_tail);
const float sum_scale = sum_scales_.front();
if (sum_scale == 1.f)
uni_vaddps(vmm_dst, vmm_dst, vmm_prev_dst);
else {
const Xmm xmm_sum_scale = Xmm(vmm_sum_scale_.getIdx());
if (conf_.alg == alg_kind::resampling_linear
&& conf_.ndims == 5)
push(reg_tmp1_);
mov(reg_tmp1_.cvt32(), float2int(sum_scale));
uni_vmovd(xmm_sum_scale, reg_tmp1_.cvt32());
if (conf_.alg == alg_kind::resampling_linear
&& conf_.ndims == 5)
pop(reg_tmp1_);
uni_vbroadcastss(vmm_sum_scale_, xmm_sum_scale);
uni_vfmadd231ps(vmm_dst, vmm_prev_dst, vmm_sum_scale_);
}
sum_scales_.push(sum_scale);
sum_scales_.pop();
};
postops_injector_->set_lambda_injector(
primitive_kind::sum, sum_injector);
}
}
template <cpu_isa_t isa, typename Vmm>
void jit_uni_resampling_kernel_t<isa, Vmm>::apply_postops(
const int data_idx, const bool is_tail, const size_t offset) {
binary_injector::rhs_arg_dynamic_params_t rhs_arg_params;
const bool is_preserving_zero_padding_needed
= is_tail && conf_.tag_kind == tag_kind::blocked;
const bool apply_rhs_binary = conf_.with_binary
&& (any_binary_postop_is_per_oc_bcast_type_
|| any_binary_postop_is_per_oc_sp_bcast_type_);
if (conf_.with_sum) apply_sum(data_idx, is_tail, offset);
if (apply_rhs_binary) {
rhs_arg_params.vmm_idx_to_out_reg.emplace(data_idx, reg_dst_);
rhs_arg_params.vmm_idx_to_out_elem_off_val.emplace(data_idx, offset);
if (is_tail) rhs_arg_params.vmm_tail_idx_.emplace(data_idx);
postops_injector_->compute_vector(data_idx, rhs_arg_params);
} else
postops_injector_->compute_vector(data_idx);
if (is_preserving_zero_padding_needed)
preserve_zero_padding_in_post_ops(data_idx);
}
template <cpu_isa_t isa, typename Vmm>
void jit_uni_resampling_kernel_t<isa, Vmm>::preserve_zero_padding(
const int c_to_compute_without_tail, const bool is_tail) {
const int c_to_compute_with_tail
= is_tail ? utils::rnd_up(tail_size_, simd_w_) : 0;
const int c_to_zeroing = conf_.inner_stride - c_to_compute_without_tail
- c_to_compute_with_tail;
if (c_to_zeroing > 0) {
assert(c_to_zeroing % simd_w_ == 0);
const Vmm vmm_zeros(vmm_tmp_.getIdx());
for (int c = 0; c < c_to_zeroing; c += simd_w_) {
uni_vxorps(vmm_zeros, vmm_zeros, vmm_zeros);
const auto dst_address = ptr[reg_dst_ + c * conf_.dst_dt_size];
io_.at(conf_.dst_data_type)->store(vmm_zeros, dst_address, false);
}
add(reg_dst_, c_to_zeroing * conf_.dst_dt_size);
}
}
template <cpu_isa_t isa, typename Vmm>
void jit_uni_resampling_kernel_t<isa, Vmm>::interpolate_c_oriented_format(
const c_oriented_generation_fn_t &generation_fn) {
const unsigned c_with_padding = utils::rnd_up(conf_.c, conf_.inner_stride);
const unsigned padding_size_to_preserve = c_with_padding - conf_.c;
if (padding_size_to_preserve > 0 && conf_.tag_kind == tag_kind::blocked) {
Label tail_label;
Label end_label;
cmp(reg_c_offset, utils::rnd_dn(conf_.c, conf_.inner_stride));
je(tail_label, T_NEAR);
generation_fn(false );
jmp(end_label, T_NEAR);
L(tail_label);
generation_fn(true );
L(end_label);
} else {
generation_fn(false );
}
}
template <cpu_isa_t isa, typename Vmm>
void jit_uni_resampling_kernel_t<isa, Vmm>::nearest_ncsp_format() {
const Reg64 ®_indices_h = reg_aux_src_0_;
const Reg64 ®_indices_w = reg_aux_src_1_;
const Reg64 ®_src_shifted = reg_aux_src_2_;
const Reg64 ®_oh = reg_tmp1_;
auto nearest_interpolation = ([&](bool is_tail) {
uni_vmovdqu(vmm_indices_, ptr[reg_indices_w]);
io_.at(conf_.src_data_type)
->gather(reg_src_shifted, vmm_indices_, vmm_src_, is_tail);
if (conf_.with_postops) apply_postops(vmm_src_.getIdx(), is_tail);
io_.at(conf_.dst_data_type)->store(vmm_src_, ptr[reg_dst_], is_tail);
});
mov(reg_indices_h, reg_indices_);
mov(reg_indices_w, reg_indices_);
add(reg_indices_w, conf_.oh * conf_.el_size_of_indices);
Label oh_loop_begin, oh_loop_end;
Label ow_loop_begin, ow_loop_end;
xor_(reg_oh, reg_oh);
L(oh_loop_begin);
{
cmp(reg_oh, conf_.oh);
jge(oh_loop_end, T_NEAR);
push(reg_oh);
mov(reg_work_, conf_.ow);
mov(reg_src_shifted, reg_src_);
xor_(reg_tmp_, reg_tmp_);
mov(reg_tmp_.cvt32(), dword[reg_indices_h]);
add(reg_src_shifted, reg_tmp_);
push(reg_indices_w);
L(ow_loop_begin);
{
cmp(reg_work_, simd_w_);
jl(ow_loop_end, T_NEAR);
nearest_interpolation(false);
add(reg_dst_, simd_w_ * conf_.dst_dt_size);
add(reg_indices_w, simd_w_ * conf_.el_size_of_indices);
sub(reg_work_, simd_w_);
jmp(ow_loop_begin, T_NEAR);
}
L(ow_loop_end);
if (tail_size_ > 0) {
nearest_interpolation(true);
add(reg_dst_, tail_size_ * conf_.dst_dt_size);
}
add(reg_indices_h, conf_.el_size_of_indices);
pop(reg_indices_w);
pop(reg_oh);
add(reg_oh, 1);
jmp(oh_loop_begin);
}
L(oh_loop_end);
}
template <cpu_isa_t isa, typename Vmm>
void jit_uni_resampling_kernel_t<isa, Vmm>::compute_nearest_c_interpolate(
const int c_to_compute_without_tail, const bool is_tail) {
const Reg64 ®_c = reg_tmp_;
const Reg64 ®_src_shifted = reg_aux_src_0_;
auto nearest_interpolation = [&](const bool is_tail) {
const bool load_and_store_with_tail
= is_tail && conf_.tag_kind == tag_kind::nspc;
io_.at(conf_.src_data_type)
->load(ptr[reg_src_shifted], vmm_src_,
load_and_store_with_tail);
if (conf_.with_postops) apply_postops(vmm_src_.getIdx(), is_tail);
io_.at(conf_.dst_data_type)
->store(vmm_src_, ptr[reg_dst_], load_and_store_with_tail);
};
Label c_loop_begin, c_loop_end;
xor_(reg_c, reg_c);
L(c_loop_begin);
{
cmp(reg_c, c_to_compute_without_tail);
je(c_loop_end, T_NEAR);
nearest_interpolation(false);
add(reg_src_shifted, simd_w_ * conf_.src_dt_size);
add(reg_dst_, simd_w_ * conf_.dst_dt_size);
add(reg_c, simd_w_);
jmp(c_loop_begin, T_NEAR);
}
L(c_loop_end);
if (is_tail) {
nearest_interpolation(true);
if (conf_.tag_kind == tag_kind::nspc)
add(reg_dst_, tail_size_ * conf_.dst_dt_size);
else if (conf_.tag_kind == tag_kind::blocked)
add(reg_dst_, simd_w_ * conf_.dst_dt_size);
}
}
template <cpu_isa_t isa, typename Vmm>
void jit_uni_resampling_kernel_t<isa,
Vmm>::compute_ne_xf16_nearest_c_interpolate(const int
c_to_compute_without_tail) {
const Reg64 ®_c = reg_tmp_;
const Reg64 ®_src_shifted = reg_aux_src_0_;
auto nearest_ne_xf16_interpolation = [&](const bool is_tail) {
io_.at(conf_.src_data_type)
->load_two_simdw_xf16(
ptr[reg_src_shifted], vmm_src_even_, vmm_src_odd_);
io_.at(conf_.src_data_type)
->merge_interleaved_to_plain(
vmm_src_even_, vmm_src_odd_, vmm_tmp_);
if (conf_.with_postops) {
apply_postops(vmm_src_even_.getIdx(), false);
apply_postops(
vmm_src_odd_.getIdx(), false, simd_w_ * conf_.dst_dt_size);
}
io_.at(conf_.dst_data_type)->store(vmm_src_even_, ptr[reg_dst_], false);
io_.at(conf_.dst_data_type)
->store(vmm_src_odd_,
ptr[reg_dst_ + simd_w_ * conf_.dst_dt_size], false);
};
Label c_loop_begin, c_loop_end;
xor_(reg_c, reg_c);
L(c_loop_begin);
{
cmp(reg_c, c_to_compute_without_tail);
je(c_loop_end, T_NEAR);
nearest_ne_xf16_interpolation(false);
add(reg_src_shifted, 2 * simd_w_ * conf_.src_dt_size);
add(reg_dst_, 2 * simd_w_ * conf_.dst_dt_size);
add(reg_c, 2 * simd_w_);
jmp(c_loop_begin, T_NEAR);
}
L(c_loop_end);
}
template <cpu_isa_t isa, typename Vmm>
void jit_uni_resampling_kernel_t<isa, Vmm>::nearest_c_oriented_format(
const bool is_tail_in_blocked_format) {
const bool has_ne_xf16_supported = isa == avx2_vnni_2
&& utils::one_of(
conf_.src_data_type, data_type::bf16, data_type::f16);
const int total_c_to_compute_without_tail
= get_channels_to_compute_without_tail(is_tail_in_blocked_format);
const int c_to_compute_ne_xf16_without_tail = has_ne_xf16_supported
? utils::rnd_dn(total_c_to_compute_without_tail, 2 * simd_w_)
: 0;
const int c_to_compute_without_tail = total_c_to_compute_without_tail
- c_to_compute_ne_xf16_without_tail;
const bool insert_tail_processsing_code
= (conf_.tag_kind == tag_kind::nspc && tail_size_ > 0)
|| is_tail_in_blocked_format;
const Reg64 ®_src_shifted = reg_aux_src_0_;
Label loop_begin, loop_end;
L(loop_begin);
{
cmp(reg_work_, 1);
jl(loop_end, T_NEAR);
mov(reg_src_shifted, reg_src_);
mov(reg_tmp1_.cvt32(), dword[reg_indices_]);
add(reg_src_shifted, reg_tmp1_);
if (has_ne_xf16_supported)
compute_ne_xf16_nearest_c_interpolate(
c_to_compute_ne_xf16_without_tail);
compute_nearest_c_interpolate(c_to_compute_without_tail, false);
if (insert_tail_processsing_code) {
if (tail_size_ > 0) compute_nearest_c_interpolate(0, true);
if (conf_.tag_kind == tag_kind::blocked)
preserve_zero_padding(total_c_to_compute_without_tail,
is_tail_in_blocked_format);
}
add(reg_indices_, conf_.el_size_of_indices);
dec(reg_work_);
jmp(loop_begin, T_NEAR);
}
L(loop_end);
}
template <cpu_isa_t isa, typename Vmm>
void jit_uni_resampling_kernel_t<isa, Vmm>::linear_ncsp_format() {
const unsigned indices_stride
= conf_.ow * conf_.oh * conf_.od * conf_.el_size_of_indices;
const unsigned weights_stride
= conf_.ow * conf_.oh * conf_.od * sizeof(float);
auto linear_interpolation = [&](const bool is_tail) {
const Vmm vmm_dst(vmm_idx(0));
for (unsigned i = 0; i < conf_.number_of_corners; i++) {
uni_vmovdqu(vmm_indices_, ptr[reg_indices_ + i * indices_stride]);
io_.at(conf_.src_data_type)
->gather(reg_src_, vmm_indices_, Vmm(vmm_idx(i)), is_tail);
}
uni_vmovups(vmm_weights_, ptr[reg_weights]);
uni_vmulps(vmm_dst, vmm_dst, vmm_weights_);
for (unsigned i = 1; i < conf_.number_of_corners; i++) {
uni_vmovups(vmm_weights_, ptr[reg_weights + i * weights_stride]);
uni_vfmadd231ps(vmm_dst, Vmm(vmm_idx(i)), vmm_weights_);
}
if (conf_.with_postops) apply_postops(vmm_idx(0), is_tail);
if (conf_.is_saturation_needed && conf_.ndims == 5
&& !is_superset(conf_.isa, avx512_core)) {
io_.init_saturate_f32({conf_.dst_data_type});
}
io_.at(conf_.dst_data_type)->store(vmm_dst, ptr[reg_dst_], is_tail);
};
Label loop_begin, loop_end;
L(loop_begin);
{
cmp(reg_work_, simd_w_);
jl(loop_end, T_NEAR);
linear_interpolation(false);
add(reg_dst_, simd_w_ * conf_.dst_dt_size);
add(reg_weights, simd_w_ * sizeof(float));
add(reg_indices_, simd_w_ * conf_.el_size_of_indices);
sub(reg_work_, simd_w_);
jmp(loop_begin, T_NEAR);
}
L(loop_end);
if (tail_size_ > 0) linear_interpolation(true);
}
template <cpu_isa_t isa, typename Vmm>
void jit_uni_resampling_kernel_t<isa,
Vmm>::compute_ne_xf16_linear_c_interpolate(const int
c_to_compute_without_tail) {
const Reg64 ®_c = reg_tmp_;
const std::vector<std::reference_wrapper<const Vmm>> src_vmms
= {src_ftl_even_, src_ftr_even_, src_fbl_even_, src_fbr_even_,
src_ftl_odd_, src_ftr_odd_, src_fbl_odd_, src_fbr_odd_};
assert(src_vmms.size() >= 2 * conf_.number_of_corners);
auto linear_interpolation = [&](const Reg64 ®_c, const bool is_tail) {
for (unsigned i = 0; i < conf_.number_of_corners; i += 2) {
const auto src_l_even = src_vmms[i].get();
const auto src_r_even = src_vmms[i + 1].get();
const auto src_l_odd = src_vmms[i + 4].get();
const auto src_r_odd = src_vmms[i + 5].get();
io_.at(conf_.src_data_type)
->load_two_simdw_xf16(
ptr[src_regs_[i].get()], src_l_even, src_l_odd);
io_.at(conf_.src_data_type)
->merge_interleaved_to_plain(
src_l_even, src_l_odd, vmm_tmp_);
io_.at(conf_.src_data_type)
->load_two_simdw_xf16(
ptr[src_regs_[i + 1].get()], src_r_even, src_r_odd);
io_.at(conf_.src_data_type)
->merge_interleaved_to_plain(
src_r_even, src_r_odd, vmm_tmp_);
uni_vmulps(src_l_even, src_l_even, weight_left_);
uni_vfmadd231ps(src_l_even, src_r_even, weight_right_);
uni_vmulps(src_l_odd, src_l_odd, weight_left_);
uni_vfmadd231ps(src_l_odd, src_r_odd, weight_right_);
}
if (conf_.ndims == 4) {
uni_vmulps(src_ftl_even_, src_ftl_even_, weight_top_);
uni_vfmadd231ps(src_ftl_even_, src_fbl_even_, weight_bottom_);
uni_vmulps(src_ftl_odd_, src_ftl_odd_, weight_top_);
uni_vfmadd231ps(src_ftl_odd_, src_fbl_odd_, weight_bottom_);
}
if (conf_.with_postops) {
apply_postops(src_ftl_even_.getIdx(), false);
apply_postops(
src_ftl_odd_.getIdx(), false, simd_w_ * conf_.dst_dt_size);
}
if (conf_.is_saturation_needed && conf_.ndims >= 4) {
push(reg_tmp_);
io_.init_saturate_f32({conf_.dst_data_type});
pop(reg_tmp_);
}
io_.at(conf_.dst_data_type)->store(src_ftl_even_, ptr[reg_dst_], false);
io_.at(conf_.dst_data_type)
->store(src_ftl_odd_,
ptr[reg_dst_ + simd_w_ * conf_.dst_dt_size], false);
};
Label c_loop_begin, c_loop_end;
xor_(reg_c, reg_c);
L(c_loop_begin);
{
cmp(reg_c, c_to_compute_without_tail);
je(c_loop_end, T_NEAR);
linear_interpolation(reg_c, false);
add(reg_dst_, 2 * simd_w_ * conf_.dst_dt_size);
for (unsigned i = 0; i < conf_.number_of_corners; i++)
add(src_regs_[i], 2 * simd_w_ * conf_.src_dt_size);
add(reg_c, 2 * simd_w_);
jmp(c_loop_begin, T_NEAR);
}
L(c_loop_end);
}
template <cpu_isa_t isa, typename Vmm>
void jit_uni_resampling_kernel_t<isa, Vmm>::compute_linear_c_interpolate(
const int c_to_compute_without_tail, const bool is_tail) {
const Reg64 ®_c = reg_tmp_;
const std::vector<std::reference_wrapper<const Vmm>> src_vmms
= {src_ftl_, src_ftr_, src_fbl_, src_fbr_, src_btl_, src_btr_,
src_bbl_, src_bbr_};
assert(src_vmms.size() >= conf_.number_of_corners);
auto linear_interpolation = [&](const Reg64 ®_c, const bool is_tail) {
const bool load_and_store_with_tail
= is_tail && conf_.tag_kind == tag_kind::nspc;
for (unsigned i = 0; i < conf_.number_of_corners; i++) {
io_.at(conf_.src_data_type)
->load(ptr[src_regs_[i].get()], src_vmms[i].get(),
load_and_store_with_tail);
}
uni_vmulps(src_ftl_, src_ftl_, weight_left_);
uni_vfmadd231ps(src_ftl_, src_ftr_, weight_right_);
if (conf_.ndims == 4 || conf_.ndims == 5) {
uni_vmulps(src_fbl_, src_fbl_, weight_left_);
uni_vfmadd231ps(src_fbl_, src_fbr_, weight_right_);
uni_vmulps(src_ftl_, src_ftl_, weight_top_);
uni_vfmadd231ps(src_ftl_, src_fbl_, weight_bottom_);
}
if (conf_.ndims == 5) {
uni_vmulps(src_btl_, src_btl_, weight_left_);
uni_vfmadd231ps(src_btl_, src_btr_, weight_right_);
uni_vmulps(src_bbl_, src_bbl_, weight_left_);
uni_vfmadd231ps(src_bbl_, src_bbr_, weight_right_);
uni_vmulps(src_btl_, src_btl_, weight_top_);
uni_vfmadd231ps(src_btl_, src_bbl_, weight_bottom_);
uni_vmulps(src_ftl_, src_ftl_, weight_front_);
uni_vfmadd231ps(src_ftl_, src_btl_, weight_back_);
}
if (conf_.with_postops) apply_postops(src_ftl_.getIdx(), is_tail);
if (conf_.is_saturation_needed && conf_.ndims == 5
&& !is_superset(conf_.isa, avx512_core)) {
push(reg_tmp_);
io_.init_saturate_f32({conf_.dst_data_type});
pop(reg_tmp_);
}
io_.at(conf_.dst_data_type)
->store(src_ftl_, ptr[reg_dst_], load_and_store_with_tail);
};
Label c_loop_begin, c_loop_end;
xor_(reg_c, reg_c);
L(c_loop_begin);
{
cmp(reg_c, c_to_compute_without_tail);
je(c_loop_end, T_NEAR);
linear_interpolation(reg_c, false);
add(reg_dst_, simd_w_ * conf_.dst_dt_size);
for (unsigned i = 0; i < conf_.number_of_corners; i++)
add(src_regs_[i], simd_w_ * conf_.src_dt_size);
add(reg_c, simd_w_);
jmp(c_loop_begin, T_NEAR);
}
L(c_loop_end);
if (is_tail) {
linear_interpolation(reg_c, true);
if (conf_.tag_kind == tag_kind::nspc)
add(reg_dst_, tail_size_ * conf_.dst_dt_size);
else if (conf_.tag_kind == tag_kind::blocked)
add(reg_dst_, simd_w_ * conf_.dst_dt_size);
}
}
template <cpu_isa_t isa, typename Vmm>
void jit_uni_resampling_kernel_t<isa, Vmm>::linear_c_oriented_format(
const bool is_tail_in_blocked_format) {
const bool has_ne_xf16_supported = isa == avx2_vnni_2 && conf_.ndims <= 4
&& utils::one_of(
conf_.src_data_type, data_type::bf16, data_type::f16);
const int total_c_to_compute_without_tail
= get_channels_to_compute_without_tail(is_tail_in_blocked_format);
const int c_to_compute_ne_xf16_without_tail = has_ne_xf16_supported
? utils::rnd_dn(total_c_to_compute_without_tail, 2 * simd_w_)
: 0;
const int c_to_compute_without_tail = total_c_to_compute_without_tail
- c_to_compute_ne_xf16_without_tail;
const bool insert_tail_processsing_code
= (conf_.tag_kind == tag_kind::nspc && tail_size_ > 0)
|| is_tail_in_blocked_format;
const Reg64 ®_index_left = reg_tmp_;
const Reg64 ®_index_right = reg_tmp_;
assert(src_regs_.size() >= conf_.number_of_corners);
xor_(reg_index_left, reg_index_left);
Label loop_begin, loop_end;
L(loop_begin);
{
cmp(reg_work_, 1);
jl(loop_end, T_NEAR);
for (unsigned i = 0; i < conf_.number_of_corners; i++) {
push(src_regs_[i]);
}
mov(reg_index_left.cvt32(), dword[reg_indices_]);
for (unsigned i = 0; i < conf_.number_of_corners / 2; i++) {
add(src_regs_[2 * i], reg_index_left);
}
mov(reg_index_right.cvt32(),
dword[reg_indices_ + conf_.el_size_of_indices]);
for (unsigned i = 0; i < conf_.number_of_corners / 2; i++) {
add(src_regs_[2 * i + 1], reg_index_right);
}
uni_vbroadcastss(weight_left_, ptr[reg_weights]);
uni_vbroadcastss(weight_right_, ptr[reg_weights + sizeof(float)]);
if (has_ne_xf16_supported)
compute_ne_xf16_linear_c_interpolate(
c_to_compute_ne_xf16_without_tail);
compute_linear_c_interpolate(c_to_compute_without_tail, false);
if (insert_tail_processsing_code) {
if (tail_size_ > 0) compute_linear_c_interpolate(0, true);
if (conf_.tag_kind == tag_kind::blocked)
preserve_zero_padding(total_c_to_compute_without_tail,
is_tail_in_blocked_format);
}
add(reg_indices_, 2 * conf_.el_size_of_indices);
add(reg_weights, 2 * sizeof(float));
for (unsigned i = 0; i < conf_.number_of_corners; i++) {
pop(src_regs_[(conf_.number_of_corners - 1) - i]);
}
dec(reg_work_);
jmp(loop_begin, T_NEAR);
}
L(loop_end);
}
template <cpu_isa_t isa, typename Vmm>
void jit_uni_resampling_kernel_t<isa, Vmm>::generate() {
preamble();
io_.init_bf16();
if (conf_.is_saturation_needed)
io_.init_saturate_f32({conf_.dst_data_type});
if (tail_size_ > 0
&& (conf_.tag_kind != tag_kind::blocked || conf_.with_postops))
io_.prepare_tail_mask();
if (is_superset(conf_.isa, avx2) && conf_.tag_kind == tag_kind::ncsp) {
io_.init_full_mask();
io_.prepare_full_mask();
}
mov(reg_dst_, ptr[reg_param + GET_OFF(dst)]);
mov(reg_work_, ptr[reg_param + GET_OFF(batch_of_sp_points_to_process)]);
mov(reg_indices_, ptr[reg_param + GET_OFF(indices)]);
mov(reg_c_offset, ptr[reg_param + GET_OFF(c_offset)]);
if (conf_.alg == alg_kind::resampling_nearest) {
mov(reg_src_, ptr[reg_param + GET_OFF(src)]);
if (conf_.tag_kind == tag_kind::ncsp) {
nearest_ncsp_format();
} else if (conf_.tag_kind == tag_kind::nspc
|| conf_.tag_kind == tag_kind::blocked) {
interpolate_c_oriented_format(
[&](const bool is_tail_in_blocked_format) {
nearest_c_oriented_format(is_tail_in_blocked_format);
});
}
} else if (conf_.alg == alg_kind::resampling_linear) {
mov(reg_weights, ptr[reg_param + GET_OFF(weights)]);
if (conf_.tag_kind == tag_kind::ncsp) {
mov(reg_src_, ptr[reg_param + GET_OFF(src)]);
linear_ncsp_format();
} else if (conf_.tag_kind == tag_kind::nspc
|| conf_.tag_kind == tag_kind::blocked) {
get_params_for_linear_in_c_oriented_format();
interpolate_c_oriented_format(
[&](const bool is_tail_in_blocked_format) {
linear_c_oriented_format(is_tail_in_blocked_format);
});
}
}
postamble();
if (conf_.with_eltwise && postops_injector_)
postops_injector_->prepare_table( true);
}
template struct jit_uni_resampling_kernel_t<avx512_core_fp16, Zmm>;
template struct jit_uni_resampling_kernel_t<avx512_core, Zmm>;
template struct jit_uni_resampling_kernel_t<avx512_core, Ymm>;
template struct jit_uni_resampling_kernel_t<avx2_vnni_2, Ymm>;
template struct jit_uni_resampling_kernel_t<avx2, Ymm>;
template struct jit_uni_resampling_kernel_t<avx, Ymm>;
template struct jit_uni_resampling_kernel_t<avx, Xmm>;
template struct jit_uni_resampling_kernel_t<sse41, Xmm>;
} } } }