#ifndef CPU_X64_BRGEMM_JIT_BRDGMM_KERNEL_HPP
#define CPU_X64_BRGEMM_JIT_BRDGMM_KERNEL_HPP
#include "common/c_types_map.hpp"
#include "common/nstl.hpp"
#include "common/type_helpers.hpp"
#include "common/utils.hpp"
#include "cpu/x64/brgemm/brgemm_types.hpp"
#include "cpu/x64/cpu_barrier.hpp"
#include "cpu/x64/injectors/jit_uni_postops_injector.hpp"
#include "cpu/x64/jit_avx512_core_bf16cvt.hpp"
#include "cpu/x64/jit_generator.hpp"
namespace dnnl {
namespace impl {
namespace cpu {
namespace x64 {
template <typename Wmm>
struct jit_brdgmm_kernel_base_t : public jit_base_brgemm_kernel_t {
jit_brdgmm_kernel_base_t(const brgemm_desc_t &abrd);
DECLARE_CPU_JIT_AUX_FUNCTIONS(jit_brdgmm_kernel_base_t)
brgemm_desc_t brg;
static bool is_fast_vnni_int8(const brgemm_desc_t &brg) {
return brg.is_dgmm && brg.is_int8 && brg.isa_impl == avx512_core_vnni
&& brg.ldb_tail == 0;
}
struct vmm_allocator_helper_t {
vmm_allocator_helper_t(const brgemm_desc_t &brg)
: aux_vmm_count_(0)
, vmm_tmp_count_(0)
, compute_vmm_base_idx_(-1)
, compute_vmm_count_((grouped_bs(brg) ? 0 : 1)
+ (!is_fma_embd(brg))
* brg.ld_block2 ) , idx_vmm_a_(-1)
, idx_vmm_b_(-1)
, idx_vmm_permute_(-1)
, idx_vmm_shift_(-1)
, idx_vmm_zp_comp_(-1)
, idx_vmm_bcast_(-1)
, idx_vmm_s8s8_comp_(-1) {
if (brg.with_sum || brg.with_wei_scales) vmm_tmp_count_ = 2;
if (is_fast_vnni_int8(brg)) idx_vmm_permute_ = aux_vmm_count_++;
if (brg.req_s8s8_compensation) {
idx_vmm_shift_ = aux_vmm_count_;
idx_vmm_s8s8_comp_ = aux_vmm_count_++;
assert(idx_vmm_shift_ == idx_vmm_s8s8_comp_);
}
const bool compute_src_zp
= brg.zp_type_a != brgemm_broadcast_t::none;
if (compute_src_zp) {
idx_vmm_zp_comp_ = aux_vmm_count_++;
if (!is_superset(brg.isa_impl, avx512_core))
idx_vmm_bcast_ = aux_vmm_count_++;
} else if (brg.with_sum
&& (!is_superset(brg.isa_impl, avx512_core))) {
const bool p_sum_scale_reg_set = brg.sum_scale != 1.f;
if (p_sum_scale_reg_set)
idx_vmm_bcast_
= aux_vmm_count_++; }
compute_vmm_base_idx_ = aux_vmm_count_;
idx_vmm_a_ = compute_vmm_base_idx_;
const int max_m = brg.bd_block2 + brg.bs_group - 1;
const int max_n = brg.ld_block2;
idx_vmm_b_
= vmm_a_idx(brg, max_m - 1, max_n - 1) + !is_fma_embd(brg);
}
int vnni_substep(const brgemm_desc_t &brg) const {
return brg.isa_impl == avx2_vnni_2 && brg.is_xf16() ? 2 : 1;
}
int vmm_a_idx(const brgemm_desc_t &brg, int m, int n) const {
const auto idx_offset = grouped_bs(brg)
? (m * brg.ld_block2 + n) * vnni_substep(brg)
: 0;
return idx_vmm_a_ + idx_offset;
}
int get_compute_vmm_count() { return compute_vmm_count_; }
int get_aux_vmm_count() {
return nstl::max(vmm_tmp_count_, aux_vmm_count_);
}
int get_idx_vmm_a() {
assert(idx_vmm_a_ >= 0);
return idx_vmm_a_;
}
int get_idx_vmm_b() {
assert(idx_vmm_b_ >= 0);
return idx_vmm_b_;
}
int get_idx_vmm_permute() {
assert(idx_vmm_permute_ >= 0);
return idx_vmm_permute_;
}
int get_idx_vmm_shift() {
assert(idx_vmm_shift_ >= 0);
return idx_vmm_shift_;
}
int get_idx_vmm_zp_comp() {
assert(idx_vmm_zp_comp_ >= 0);
return idx_vmm_zp_comp_;
}
int get_idx_vmm_bcast() {
assert(idx_vmm_bcast_ >= 0);
return idx_vmm_bcast_;
}
int get_idx_vmm_s8s8_comp() {
assert(idx_vmm_s8s8_comp_ >= 0);
return idx_vmm_s8s8_comp_;
}
private:
int aux_vmm_count_;
int vmm_tmp_count_;
int compute_vmm_base_idx_;
int compute_vmm_count_;
int idx_vmm_a_;
int idx_vmm_b_;
int idx_vmm_permute_;
int idx_vmm_shift_;
int idx_vmm_zp_comp_;
int idx_vmm_bcast_;
int idx_vmm_s8s8_comp_;
};
static int get_aux_vmm_count(const brgemm_desc_t &brg) {
auto vmm_alloc = vmm_allocator_helper_t(brg);
return vmm_alloc.get_aux_vmm_count();
}
static int get_compute_vmm_count(const brgemm_desc_t &brg) {
auto vmm_alloc = vmm_allocator_helper_t(brg);
return vmm_alloc.get_compute_vmm_count();
}
const brgemm_desc_t &get_brg() const override { return brg; }
private:
using Vmm =
typename utils::conditional<std::is_same<Wmm, Xbyak::Tmm>::value,
Xbyak::Zmm, Wmm>::type;
using Vmm_low_t = typename vreg_traits_t<Vmm>::Vmm_lower_t;
using po_injector_t = injector::jit_uni_postops_injector_base_t<Vmm>;
std::unique_ptr<po_injector_t> postops_injector_;
std::unique_ptr<bf16_emulation_t> bf16_emu_;
Xbyak::Label permute_index_table;
enum class compute_pad_kernel_t { s8s8_kernel, zero_point_kernel };
injector_utils::registry_scratchpad_t regscratchpad_ {*this, brg.isa_impl};
using reg64_t = const Xbyak::Reg64;
const reg64_t param1 = abi_param1;
const reg64_t reg_A = abi_not_param1;
const reg64_t reg_B = r8;
const injector_utils::reg64_savable_t reg_aux_batch_addr {
regscratchpad_, r15};
const reg64_t reg_BS = rsi;
const reg64_t reg_BS_loop = r12;
const reg64_t reg_aux_M = r13;
const reg64_t reg_aux_D = rbx;
const injector_utils::reg64_savable_t reg_aux_C {
regscratchpad_, rdx, rdx, may_use_rbp()};
const reg64_t reg_aux_A = r10;
const reg64_t reg_aux_B = abi_param1;
const injector_utils::reg64_savable_t reg_aux1_A {
regscratchpad_, abi_not_param1}; const injector_utils::reg64_savable_t reg_aux1_B {
regscratchpad_, r8}; const reg64_t reg_a_offset = r9;
const reg64_t reg_aux_N = r11;
const reg64_t reg_aux_A_vpad_top = r14;
const injector_utils::reg64_savable_t reg_aux_A_vpad_bottom {
regscratchpad_, rdx, rbp, may_use_rbp()};
const reg64_t reg_table_base = rax;
const reg64_t reg_tmp = reg_table_base;
const reg64_t reg_total_padding = reg_table_base;
const injector_utils::reg64_savable_t reg_aux_bias {regscratchpad_, rax};
const injector_utils::reg64_savable_t reg_aux_src_scales {
regscratchpad_, rax};
const injector_utils::reg64_savable_t reg_aux_wei_scales {
regscratchpad_, rax};
const injector_utils::reg64_savable_t reg_aux_dst_scales {
regscratchpad_, rax, r22};
const injector_utils::reg64_savable_t reg_dst_zero_point {
regscratchpad_, rax, r23};
const injector_utils::reg64_savable_t reg_src_zero_point {
regscratchpad_, rax, r24};
const injector_utils::reg64_savable_t reg_zp_compensation {
regscratchpad_, r12, r25};
const injector_utils::reg64_savable_t reg_binary_params {
regscratchpad_, abi_param1};
const injector_utils::reg64_savable_t reg_ptr_sum_scale {
regscratchpad_, r14, r27};
const injector_utils::reg64_savable_t reg_ptr_sum_zp {
regscratchpad_, rax, r28};
const injector_utils::reg64_savable_t reg_s8s8_comp {
regscratchpad_, r14, r29};
const reg64_t reg_aux_src_zp = rax;
const reg64_t reg_aux_zp_comp = r12;
const reg64_t reg_aux_s8s8_comp = r14;
Xbyak::Opmask k_mask = Xbyak::Opmask(2);
Xbyak::Opmask k_tail_mask = Xbyak::Opmask(3);
Xbyak::Opmask kblend_mask = Xbyak::Opmask(4);
reg64_t bf16_emu_scratch = reg_table_base;
Xbyak::Zmm bf16_emu_reserv_1 = Xbyak::Zmm(0);
Xbyak::Zmm bf16_emu_reserv_2 = Xbyak::Zmm(1);
Xbyak::Zmm bf16_emu_reserv_3 = Xbyak::Zmm(2);
Xbyak::Zmm bf16_emu_reserv_4 = Xbyak::Zmm(3);
const int simd_w_;
const int max_vmms_;
const bool compute_dst_zp_, compute_src_zp_;
const bool is_src_zp_bcast_;
const bool compute_compensation_; const bool has_vpad_; const bool has_bpad_;
vmm_allocator_helper_t vmm_alloc;
bool with_binary_non_scalar_bcast_ = false;
inline int M() { return brg.bcast_dim; }
inline int N() { return brg.load_dim; }
inline int m_block2() { return brg.bd_block2; }
inline int nb_m_block2() { return brg.bdb2; }
inline int m_block2_tail() { return brg.bdb2_tail; }
inline int n_block1() { return brg.ld_block; }
inline int nb_n_block1() { return brg.ldb; }
inline int n_block1_tail() { return brg.ldb_tail; }
inline int n_block2() { return brg.ld_block2; }
inline int nb_n_block2() { return brg.ldb2; }
inline int n_block2_tail() { return brg.ldb2_tail; }
int tail_length() { return n_block1_tail() % simd_w_; }
inline int bs_group() const { return brg.bs_group; }
static bool grouped_bs(const brgemm_desc_t &brg) {
return brg.bs_group > 1;
}
inline bool grouped_bs() const { return grouped_bs(brg); }
static bool is_fma_embd(const brgemm_desc_t &brg) {
return grouped_bs(brg)
? false
: brg.is_f32 && is_superset(brg.isa_impl, avx512_core);
}
bool is_fma_embd() { return is_fma_embd(brg); }
bool is_fast_vnni_int8() { return is_fast_vnni_int8(brg); }
bool is_slow_bf16_vnni() {
return brg.is_bf16 && mayiuse(avx512_core_amx);
}
bool req_vmm_reload() { return brg.is_bf16_emu; }
bool assign_data_vmm_once() { return !req_vmm_reload(); }
int vnni_substep() { return vmm_alloc.vnni_substep(brg); }
int get_substep_simd(int n_i, int v_i, bool has_n_tail) {
const int last_n_block_sz
= n_block2_tail() > 0 ? n_block2_tail() : n_block2();
if (has_n_tail && n_i + 1 == last_n_block_sz) {
return nstl::min(simd_w_, n_block1_tail() - v_i * simd_w_);
} else {
return simd_w_;
}
}
Vmm vmm_a(int m, int n) {
const auto idx_a = vmm_alloc.vmm_a_idx(brg, m, n);
assert(idx_a < (vmm_alloc.get_idx_vmm_b() + is_fma_embd()));
return Vmm(idx_a);
}
Vmm vmm_b(int bi = 0) { return Vmm(vmm_alloc.get_idx_vmm_b() + bi); }
Vmm accm(int m_blocks, int n_blocks, int m, int n, int vnni_idx) {
assert(m_blocks <= m_block2() && m < m_blocks);
assert(n_blocks <= n_block2() && n < n_blocks);
const int accm_start = max_vmms_ - m_blocks * n_blocks * vnni_substep();
const int accm_rel_idx
= m * n_blocks * vnni_substep() + n * vnni_substep() + vnni_idx;
const int idx = accm_start + accm_rel_idx;
assert(idx < max_vmms_ && idx > vmm_b(0).getIdx());
return Vmm(idx);
}
Vmm vmm_permute() { return Vmm(vmm_alloc.get_idx_vmm_permute()); }
Vmm vmm_shift() { return Vmm(vmm_alloc.get_idx_vmm_shift());
}
Vmm vmm_s8s8_comp() { return Vmm(vmm_alloc.get_idx_vmm_s8s8_comp()); }
Vmm vmm_zp_comp() { return Vmm(vmm_alloc.get_idx_vmm_zp_comp()); }
Vmm vmm_bcast() { return Vmm(vmm_alloc.get_idx_vmm_bcast()); }
Vmm vmm_tmp(int i) {
const int idx
= max_vmms_ - m_block2() * n_block2() * vnni_substep() - 1 - i;
assert(idx > (is_fast_vnni_int8() - 1));
return Vmm(idx);
}
template <typename U>
U maybe_mask(const U umm_in, bool mask_flag, bool store);
void init_masks();
void read_params();
void load_permute_vmm();
void load_accumulators(int m_blocks, int n_blocks);
void restore_A_B_matrices();
void set_A_B_matrices();
void advance_A_B_matrices();
void load_a(Vmm vmma, int m_i, int n_i, int v_i, bool has_n_tail);
void load_b(
Vmm vmmb, int n_i, int v_i, bool has_n_tail, bool wei_zp = false);
void comp_dot_product(compute_pad_kernel_t kernel_type, Vmm vmm_acc,
Vmm vmmb, int n,
bool is_tail_block); void pad_comp_kernel(compute_pad_kernel_t kernel_type, int m_blocks,
int n_blocks, int padding, const Xbyak::Reg64 reg_pad,
const std::function<int(int)> &get_mi, bool has_tail = false);
void vertical_pad_kernel(int m_blocks, int n_blocks, bool has_tail);
void batch_pad_kernel(int m_blocks, int n_blocks, bool has_tail = false);
void brdgmm_microkernel(int m_blocks, int n_blocks, bool has_top_padding,
bool has_bottom_padding, bool has_tail, int shift_a);
void compute_loop();
void get_batch_padding_info();
void get_vertical_padding_info(const int m_blocks);
void call_brdgmm_microkernel(const int m_blocks, const int n_blocks,
bool has_n_tail, int shift_a);
void batch_loop(const int m_blocks, const int n_blocks, bool has_n_tail);
void cvt2ps(data_type_t type_in, const Vmm vmm_in, const Xbyak::Operand &op,
bool mask_flag, bool store);
void apply_post_ops(int m_blocks, int n_blocks, bool has_n_tail);
void maybe_transpose_interleaved_vnni_to_plain(
int m_blocks, int n_blocks, bool has_n_tail);
void load_src_zp();
void compute_int8_compensation(int m_blocks, int n_blocks, bool has_n_tail);
void store_accumulators(int m_blocks, int n_blocks, bool has_n_tail);
void store_accumulators_without_post_ops(
int m_blocks, int n_blocks, bool has_n_tail);
void store_accumulators_apply_post_ops(
int m_blocks, int n_blocks, bool has_n_tail);
bool check_effective_padding() { return has_vpad_ && M() > m_block2(); }
int oc_logical_offset(int n) { return n * n_block1(); }
int A_offset(int m, int n) {
return brg.typesize_A * (m * brg.LDA + n * n_block1());
}
int B_offset(int n) { return brg.typesize_B * n * n_block1(); }
int C_offset(int m, int n, int v) {
return brg.typesize_C * (m * brg.LDC + n * n_block1() + v * simd_w_);
}
int D_offset(int m, int n, int v) {
return brg.typesize_D * (m * brg.LDD + n * n_block1() + v * simd_w_);
}
int bias_offset(int n, int v) {
return brg.typesize_bias * (n * n_block1() + v * simd_w_);
}
int wei_scales_offset(int n, int v) {
return sizeof(float) * brg.is_oc_scale * (n * n_block1() + v * simd_w_);
}
size_t comp_offset(int n) { return sizeof(int32_t) * n * n_block1(); }
void generate() override;
};
} } } }
#endif