#include <cassert>
#include "common/bfloat16.hpp"
#include "common/c_types_map.hpp"
#include "cpu/x64/jit_generator.hpp"
#include "cpu/x64/shuffle/jit_uni_shuffle_kernel.hpp"
namespace dnnl {
namespace impl {
namespace cpu {
namespace x64 {
using namespace Xbyak;
#define GET_OFF(field) offsetof(jit_uni_shuffle_args_t, field)
static size_t get_padding_size(const jit_shuffle_conf_t &conf) {
const auto padding_tail_size = conf.c % conf.blk_size;
return (padding_tail_size) ? conf.blk_size - padding_tail_size : 0;
}
template <cpu_isa_t isa>
jit_uni_shuffle_kernel_t<isa>::jit_uni_shuffle_kernel_t(
const jit_shuffle_conf_t &conf)
: jit_generator_t(jit_name(), isa)
, conf_(conf)
, padding_size_(get_padding_size(conf)) {}
template <cpu_isa_t isa>
void jit_uni_shuffle_kernel_t<isa>::prepare_mask() {}
template <>
void jit_uni_shuffle_kernel_t<avx512_core>::prepare_mask() {
const size_t tail_mask = (1ULL << conf_.simd_tail) - 1ULL;
const Reg64 ®_tail = reg_tmp_;
mov(reg_tail.cvt32(), tail_mask);
kmovw(k_tail_mask_, reg_tail.cvt32());
}
template <>
void jit_uni_shuffle_kernel_t<avx>::prepare_mask() {
static constexpr uint32_t mask[16]
= {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0};
mov(reg_tmp_, reinterpret_cast<size_t>(&mask[8 - conf_.simd_tail]));
vmovups(vmm_tail_mask_, ptr[reg_tmp_]);
}
template <>
void jit_uni_shuffle_kernel_t<avx512_core>::emu_gather_data(
const Reg64 ®_src_addr, const int indices_idx, const int data_idx,
const bool is_tail) {
assert(conf_.data_type == data_type::bf16);
const Xmm xmm_tmp = Xmm(vmm_full_mask_.getIdx());
const Xmm xmm_dst = Xmm(vmm_tmp_.getIdx());
xor_(reg_tmp_, reg_tmp_);
mov(reg_tmp1_, reg_src_addr);
constexpr unsigned xmm_size_elem = 8; constexpr unsigned xmm_size_elem_half = xmm_size_elem / 2;
const unsigned number_of_xmms = is_tail
? utils::div_up(conf_.simd_tail, xmm_size_elem)
: utils::div_up(conf_.simd_w, xmm_size_elem);
for (unsigned i = 0; i < number_of_xmms; i++) {
const unsigned number_of_xmm_halfs = is_tail && i == number_of_xmms - 1
? utils::div_up(conf_.simd_tail,
xmm_size_elem_half + i * xmm_size_elem)
: 2;
for (unsigned j = 0; j < number_of_xmm_halfs; j++) {
const unsigned rem = conf_.simd_tail % xmm_size_elem_half;
const unsigned number_of_values_to_load = is_tail
&& i == number_of_xmms - 1
&& j == number_of_xmm_halfs - 1 && rem
? rem
: xmm_size_elem_half;
vextractf32x4(xmm_tmp, Zmm(indices_idx), j + i * 2);
for (unsigned k = 0; k < number_of_values_to_load; k++) {
vpextrd(reg_tmp_.cvt32(), xmm_tmp, k + j * xmm_size_elem_half);
add(reg_src_addr, reg_tmp_);
vpinsrw(xmm_dst, xmm_dst, ptr[reg_src_addr],
k + j * xmm_size_elem_half);
mov(reg_src_addr, reg_tmp1_);
}
}
vinsertf128(Ymm(data_idx), Ymm(data_idx), xmm_dst, i);
}
}
template <>
void jit_uni_shuffle_kernel_t<avx>::emu_gather_data(const Reg64 ®_src_addr,
const int indices_idx, const int data_idx, const bool is_tail) {
const Xmm xmm_tmp = Xmm(vmm_full_mask_.getIdx());
const Xmm xmm_dst = Xmm(vmm_tmp_.getIdx());
xor_(reg_tmp_, reg_tmp_);
mov(reg_tmp1_, reg_src_addr);
constexpr unsigned xmm_size_elem = 4;
const unsigned number_of_xmms = is_tail
? utils::div_up(conf_.simd_tail, xmm_size_elem)
: utils::div_up(conf_.simd_w, xmm_size_elem);
for (unsigned i = 0; i < number_of_xmms; i++) {
vextractf128(xmm_tmp, Ymm(indices_idx), i);
const unsigned number_of_values_to_load = i == number_of_xmms - 1
&& is_tail && conf_.simd_tail % xmm_size_elem != 0
? conf_.simd_tail % xmm_size_elem
: xmm_size_elem;
for (unsigned j = 0; j < number_of_values_to_load; j++) {
vpextrd(reg_tmp_.cvt32(), xmm_tmp, j);
add(reg_src_addr, reg_tmp_);
vpinsrd(xmm_dst, xmm_dst, ptr[reg_src_addr], j);
mov(reg_src_addr, reg_tmp1_);
}
vinsertf128(Ymm(data_idx), Ymm(data_idx), xmm_dst, i);
}
}
template <>
void jit_uni_shuffle_kernel_t<sse41>::emu_gather_data(const Reg64 ®_src_addr,
const int indices_idx, const int data_idx, const bool is_tail) {
xor_(reg_tmp_, reg_tmp_);
mov(reg_tmp1_, reg_src_addr);
constexpr unsigned xmm_size_elem = 4;
const unsigned number_of_values_to_load
= is_tail ? conf_.simd_tail : xmm_size_elem;
for (unsigned j = 0; j < number_of_values_to_load; j++) {
pextrd(reg_tmp_.cvt32(), Xmm(indices_idx), j);
add(reg_src_addr, reg_tmp_);
pinsrd(Xmm(data_idx), ptr[reg_src_addr], j);
mov(reg_src_addr, reg_tmp1_);
}
}
template <>
void jit_uni_shuffle_kernel_t<avx512_core>::gather_data(
const Reg64 ®_src_addr, const int indices_idx, const int data_idx,
const bool is_tail) {
if (conf_.dt_size == sizeof(float)) {
const Opmask &mask = is_tail ? k_tail_mask_ : k_full_mask_;
if (!is_tail) {
mov(reg_tmp_.cvt32(), (1ULL << conf_.simd_w) - 1ULL);
kmovw(k_full_mask_, reg_tmp_.cvt32());
}
vgatherdps(Vmm(data_idx) | mask, ptr[reg_src_addr + Vmm(indices_idx)]);
if (is_tail) prepare_mask();
} else {
emu_gather_data(reg_src_addr, indices_idx, data_idx, is_tail);
}
}
template <>
void jit_uni_shuffle_kernel_t<avx>::gather_data(const Reg64 ®_src_addr,
const int indices_idx, const int data_idx, const bool is_tail) {
if (conf_.isa == avx2 && conf_.dt_size == sizeof(float)) {
const Vmm &mask = is_tail ? vmm_tail_mask_ : vmm_full_mask_;
if (!is_tail) {
if (conf_.data_type == data_type::s32)
vpcmpeqw(vmm_full_mask_, vmm_full_mask_, vmm_full_mask_);
else
vcmpps(vmm_full_mask_, vmm_full_mask_, vmm_full_mask_,
_cmp_eq_oq);
}
if (conf_.data_type == data_type::s32)
vpgatherdd(
Vmm(data_idx), ptr[reg_src_addr + Vmm(indices_idx)], mask);
else
vgatherdps(
Vmm(data_idx), ptr[reg_src_addr + Vmm(indices_idx)], mask);
if (is_tail) prepare_mask();
} else {
emu_gather_data(reg_src_addr, indices_idx, data_idx, is_tail);
}
}
template <>
void jit_uni_shuffle_kernel_t<sse41>::gather_data(const Reg64 ®_src_addr,
const int indices_idx, const int data_idx, const bool is_tail) {
emu_gather_data(reg_src_addr, indices_idx, data_idx, is_tail);
}
template <>
void jit_uni_shuffle_kernel_t<avx512_core>::store_data(const int data_idx,
const Reg64 ®_dst_addr, const int offset, const bool is_tail) {
const auto extend_for_padding
= is_tail && padding_size_ + conf_.simd_tail >= conf_.simd_w;
if (conf_.data_type == data_type::bf16) {
const Ymm to_store_data = Ymm(data_idx);
const Ymm ymm_tmp = Ymm(vmm_tmp_.getIdx());
if (extend_for_padding) {
vmovdqu16(ymm_tmp | k_tail_mask_ | T_z, to_store_data);
vmovups(ptr[reg_dst_addr + offset], ymm_tmp);
} else {
if (is_tail)
vmovdqu16(ptr[reg_dst_addr + offset] | k_tail_mask_,
to_store_data);
else
vmovups(ptr[reg_dst_addr + offset], to_store_data);
}
} else {
if (extend_for_padding) {
vmovups(vmm_tmp_ | k_tail_mask_ | T_z, Vmm(data_idx));
vmovups(ptr[reg_dst_addr + offset], vmm_tmp_);
} else {
if (is_tail)
vmovups(ptr[reg_dst_addr + offset] | k_tail_mask_,
Vmm(data_idx));
else
vmovups(ptr[reg_dst_addr + offset], Vmm(data_idx));
}
}
append_zero_padding(reg_dst_, extend_for_padding);
}
template <>
void jit_uni_shuffle_kernel_t<avx>::store_data(const int data_idx,
const Reg64 ®_dst_addr, const int offset, const bool is_tail) {
const auto extend_for_padding
= is_tail && padding_size_ + conf_.simd_tail >= conf_.simd_w;
if (extend_for_padding) {
uni_vxorps(vmm_tmp_, vmm_tmp_, vmm_tmp_);
uni_vblendvps(vmm_tmp_, vmm_tmp_, Vmm(data_idx), vmm_tail_mask_);
vmovups(ptr[reg_dst_addr + offset], vmm_tmp_);
} else {
if (is_tail)
vmaskmovps(
ptr[reg_dst_addr + offset], vmm_tail_mask_, Vmm(data_idx));
else
vmovups(ptr[reg_dst_addr + offset], Vmm(data_idx));
}
append_zero_padding(reg_dst_, extend_for_padding);
}
template <>
void jit_uni_shuffle_kernel_t<sse41>::store_data(const int data_idx,
const Reg64 ®_dst_addr, const int offset, const bool is_tail) {
if (is_tail)
for (unsigned i = 0; i < conf_.simd_tail; i++) {
pextrd(ptr[reg_dst_addr + offset + i * conf_.dt_size],
Xmm(data_idx), i);
}
else
movups(ptr[reg_dst_addr + offset], Vmm(data_idx));
append_zero_padding(reg_dst_, false);
}
template <cpu_isa_t isa>
void jit_uni_shuffle_kernel_t<isa>::shuffle_blocked_format() {
const Reg64 ®_sp = reg_tmp2_;
const Reg64 ®_cb = reg_tmp3_;
const Reg64 ®_cb_loop_size = reg_tmp4_;
const Reg64 ®_blk_tail = reg_tmp5_;
const Reg64 ®_src_save = reg_tmp6_;
const int simd_in_blk = conf_.blk_size / conf_.simd_w;
const int simd_in_tail_blk
= utils::div_up(conf_.c % conf_.blk_size, conf_.simd_w);
const Vmm vmm_tmp[4] = {Vmm(5), Vmm(6), Vmm(7), Vmm(8)};
auto load_indices = ([&](bool is_blk_tail) {
const int simd_to_process
= is_blk_tail ? simd_in_tail_blk : simd_in_blk;
for (int i = 0; i < simd_to_process; ++i)
uni_vmovdqu(vmm_tmp[i],
ptr[reg_indices_
+ i * conf_.simd_w * conf_.el_size_of_indices]);
});
auto shuffle = ([&](bool is_blk_tail) {
const int simd_to_process
= is_blk_tail ? simd_in_tail_blk : simd_in_blk;
for (int i = 0; i < simd_to_process; ++i) {
const bool simd_tail_condition = is_blk_tail && conf_.simd_tail > 0
&& i == simd_to_process - 1;
uni_vmovups(vmm_indices_, vmm_tmp[i]);
gather_data(reg_src_, vmm_indices_.getIdx(), vmm_src_.getIdx(),
simd_tail_condition);
store_data(vmm_src_.getIdx(), reg_dst_,
i * conf_.simd_w * conf_.dt_size, simd_tail_condition);
}
});
mov(reg_cb_loop_size, ptr[reg_param + GET_OFF(cb_loop_size)]);
Label sp_loop_begin, sp_loop_end;
Label sp_tail_loop_begin, sp_tail_loop_end;
Label cb_loop_begin, cb_loop_end;
Label simd_loop_begin, simd_loop_end;
Label blk_tail_loop_begin, blk_tail_loop_end;
Label blk_tail_check_end;
Label no_tail;
xor_(reg_blk_tail, reg_blk_tail);
cmp(reg_cb_loop_size, conf_.blk_size);
je(no_tail, T_NEAR);
mov(reg_blk_tail, reg_cb_loop_size);
xor_(reg_cb_loop_size, reg_cb_loop_size);
L(no_tail);
xor_(reg_cb, reg_cb);
L(cb_loop_begin);
{
cmp(reg_cb, reg_cb_loop_size);
jge(cb_loop_end, T_NEAR);
load_indices(false);
mov(reg_src_save, reg_src_);
xor_(reg_sp, reg_sp);
L(sp_loop_begin);
{
cmp(reg_sp, conf_.sp_split_size);
jge(sp_loop_end, T_NEAR);
shuffle(false);
inc(reg_sp);
add(reg_src_, conf_.blk_size * conf_.dt_size);
add(reg_dst_, conf_.blk_size * conf_.dt_size);
jmp(sp_loop_begin);
}
L(sp_loop_end);
mov(reg_src_, reg_src_save);
add(reg_cb, conf_.blk_size);
add(reg_dst_,
conf_.blk_size * (conf_.sp - conf_.sp_split_size)
* conf_.dt_size);
add(reg_indices_, conf_.blk_size * conf_.el_size_of_indices);
jmp(cb_loop_begin);
}
L(cb_loop_end);
cmp(reg_blk_tail, 0);
je(blk_tail_check_end, T_NEAR);
load_indices(true);
xor_(reg_sp, reg_sp);
L(sp_tail_loop_begin);
{
cmp(reg_sp, conf_.sp_split_size);
jge(sp_tail_loop_end, T_NEAR);
shuffle(true);
inc(reg_sp);
add(reg_src_, conf_.blk_size * conf_.dt_size);
add(reg_dst_, conf_.blk_size * conf_.dt_size);
jmp(sp_tail_loop_begin);
}
L(sp_tail_loop_end);
L(blk_tail_check_end);
}
template <cpu_isa_t isa>
void jit_uni_shuffle_kernel_t<isa>::append_zero_padding(
const Reg64 ®_dst_addr, const bool extend_for_padding) {
static constexpr size_t reg64_size = 8;
const size_t simd_w_byte = conf_.simd_w * sizeof(float);
if (!padding_size_) return;
const auto padding_start
= (extend_for_padding) ? conf_.simd_w : conf_.c % conf_.blk_size;
const auto padding_end = (extend_for_padding)
? padding_size_ - (conf_.simd_w - conf_.simd_tail)
: padding_size_;
const auto off_start = padding_start * conf_.dt_size;
const auto padding_to_add = padding_end * conf_.dt_size;
if (!padding_to_add) return;
Label end;
unsigned int off = 0;
cmp(reg_padded_block, 0);
je(end, T_NEAR);
if (simd_w_byte <= padding_to_add) {
uni_vxorps(vmm_zero_, vmm_zero_, vmm_zero_);
for (; off + simd_w_byte < padding_to_add; off += simd_w_byte)
uni_vmovups(ptr[reg_dst_addr + off_start + off], vmm_zero_);
}
if (off != padding_to_add) {
xor_(reg_tmp_, reg_tmp_);
for (; off + reg64_size < padding_to_add; off += reg64_size)
mov(ptr[reg_dst_addr + off_start + off], reg_tmp_);
for (; off < padding_to_add; off++)
mov(ptr[reg_dst_addr + off_start + off], Reg8(reg_tmp_.getIdx()));
}
L(end);
}
template <cpu_isa_t isa>
void jit_uni_shuffle_kernel_t<isa>::generate() {
preamble();
#if defined(_WIN32)
xor_(rdi, rcx);
xor_(rcx, rdi);
xor_(rdi, rcx);
#endif
if (conf_.isa == avx2) {
uni_vxorps(vmm_full_mask_, vmm_full_mask_, vmm_full_mask_);
}
if (conf_.simd_tail > 0) prepare_mask();
mov(reg_indices_, ptr[reg_param + GET_OFF(input_off_ptr)]);
mov(reg_src_, ptr[reg_param + GET_OFF(src)]);
mov(reg_dst_, ptr[reg_param + GET_OFF(dst)]);
mov(reg_padded_block, ptr[reg_param + GET_OFF(is_padded_block)]);
shuffle_blocked_format();
postamble();
}
template struct jit_uni_shuffle_kernel_t<sse41>;
template struct jit_uni_shuffle_kernel_t<avx>;
template struct jit_uni_shuffle_kernel_t<avx512_core>;
#undef GET_OFF
} } } }