#include "cpu/x64/rnn/jit_diff_weights_peephole.hpp"
#include "common/c_types_map.hpp"
#include "cpu/rnn/rnn_utils.hpp"
namespace dnnl {
namespace impl {
namespace cpu {
namespace x64 {
jit_diff_weights_peephole_t::jit_diff_weights_peephole_t(
const rnn_utils::rnn_conf_t &rnn, const dim_t dhc_block_size)
: jit_generator_t(jit_name())
, c_states_dt_(rnn.src_iter_c_dt)
, scratch_dt_(utils::map(true, data_type::f32, rnn.is_bf16_conf(),
data_type::bf16, rnn.is_f16_conf(), data_type::f16))
, dst_dt_(data_type::f32)
, compute_block_size_(dhc_block_size)
, tail_size_(dhc_block_size % simd_w_)
, io_(this,
rnn.is_f16_conf() ? avx512_core_fp16
: mayiuse(avx512_core_bf16) ? avx512_core_bf16
: avx512_core,
{c_states_dt_, scratch_dt_, dst_dt_}, {},
io::io_tail_conf_t {static_cast<std::size_t>(simd_w_),
static_cast<std::size_t>(tail_size_), tail_opmask_, 0,
reg_tmp_}) {}
void jit_diff_weights_peephole_t::generate() {
preamble();
load_addresses();
init();
compute_loop();
postamble();
}
#define PARAM_OFF(x) offsetof(jit_diff_weights_peephole_t::call_params_t, x)
void jit_diff_weights_peephole_t::load_addresses() {
mov(reg_c_states_, ptr[abi_param1 + PARAM_OFF(c_states)]);
mov(reg_scratch_gates_, ptr[abi_param1 + PARAM_OFF(scratch_gates)]);
mov(reg_dst_, ptr[abi_param1 + PARAM_OFF(dst)]);
}
#undef PARAM_OFF
void jit_diff_weights_peephole_t::init() {
if (tail_size_) { io_.prepare_tail_mask(); }
}
void jit_diff_weights_peephole_t::compute_loop() {
Xbyak::Label unroll_loop, unroll_loop_tail;
mov(loop_cnt_, compute_block_size_);
xor_(reg_offset_, reg_offset_);
const size_t offt_max = max_unrolling * simd_w_;
const size_t full_unroling_steps = compute_block_size_ / offt_max;
if (full_unroling_steps) {
L(unroll_loop);
{
cmp(loop_cnt_, offt_max);
jl(unroll_loop_tail, T_NEAR);
compute_dst(max_unrolling, false );
sub(loop_cnt_, offt_max);
add(reg_offset_, offt_max);
jmp(unroll_loop);
}
}
const size_t full_blocks_left = (compute_block_size_ - tail_size_
- (full_unroling_steps * offt_max))
/ simd_w_;
L(unroll_loop_tail);
{
if (full_blocks_left) {
compute_dst(full_blocks_left, false );
if (tail_size_) {
const size_t offt = full_blocks_left * simd_w_;
add(reg_offset_, offt);
}
}
if (tail_size_) { compute_dst(1u , true ); }
}
}
void jit_diff_weights_peephole_t::compute_dst(
size_t unrolling_factor, bool tail) {
static constexpr dim_t number_vmm_single_compute = 3;
const auto get_compute_zmm = [=](size_t base_idx, size_t unroll_group) {
return Xbyak::Zmm(base_idx + unroll_group * number_vmm_single_compute);
};
const auto get_addr = [&](const Xbyak::Reg64 ®_base, const dim_t offt,
const data_type_t dt) {
const auto dt_size = types::data_type_size(dt);
return ptr[reg_base + reg_offset_ * dt_size + offt * dt_size];
};
static constexpr size_t dst_idx = 0;
static constexpr size_t scratch_idx = 1;
static constexpr size_t c_states_idx = 2;
const auto io_dst = io_.at(dst_dt_);
const auto io_scratch = io_.at(scratch_dt_);
const auto io_c_states = io_.at(c_states_dt_);
for (size_t unroll_group = 0; unroll_group < unrolling_factor;
++unroll_group) {
const auto dst_zmm = get_compute_zmm(dst_idx, unroll_group);
const auto scratch_zmm = get_compute_zmm(scratch_idx, unroll_group);
const auto c_states_zmm = get_compute_zmm(c_states_idx, unroll_group);
const auto unroll_offset = unroll_group * simd_w_;
const auto dst_addr = get_addr(reg_dst_, unroll_offset, dst_dt_);
io_dst->load(dst_addr, dst_zmm, tail);
io_scratch->load(
get_addr(reg_scratch_gates_, unroll_offset, scratch_dt_),
scratch_zmm, tail);
io_c_states->load(get_addr(reg_c_states_, unroll_offset, c_states_dt_),
c_states_zmm, tail);
const auto dst_zmm_masked = tail ? dst_zmm | tail_opmask_ : dst_zmm;
uni_vfmadd231ps(dst_zmm_masked, scratch_zmm, c_states_zmm);
io_dst->store(dst_zmm, dst_addr, tail);
}
}
} } } }