#include <assert.h>
#include <math.h>
#include "common/c_types_map.hpp"
#include "common/dnnl_thread.hpp"
#include "common/reorder.hpp"
#include "common/type_helpers.hpp"
#include "cpu/cpu_primitive.hpp"
#include "cpu/x64/injectors/jit_uni_postops_injector.hpp"
#include "cpu/x64/jit_avx512_core_bf16cvt.hpp"
#include "cpu/x64/jit_generator.hpp"
#include "cpu/x64/jit_uni_layer_normalization.hpp"
#include "cpu/x64/utils/jit_io_helper.hpp"
namespace dnnl {
namespace impl {
namespace cpu {
namespace x64 {
using namespace memory_tracking::names;
using namespace data_type;
using namespace Xbyak;
static cpu_isa_t get_supported_isa() {
if (mayiuse(avx512_core)) return avx512_core;
if (mayiuse(avx2)) return avx2;
return isa_undef;
}
cpu_isa_t get_io_isa(cpu_isa_t isa, bool has_f16, bool has_bf16) {
if (has_f16 || has_bf16)
return is_superset(isa, avx512_core)
? (has_f16 ? avx512_core_fp16
: mayiuse(avx512_core_bf16) ? avx512_core_bf16
: avx512_core)
: avx2_vnni_2;
else
return isa;
}
static bcast_set_t get_supported_bcast_strategies(int ndims) {
assert(ndims > 1 && ndims <= 5);
bcast_set_t set {broadcasting_strategy_t::scalar};
switch (ndims) {
case 2: set.insert(broadcasting_strategy_t::per_oc); break;
case 3:
case 4:
case 5: set.insert(broadcasting_strategy_t::per_w); break;
default: assert(!"Unsupported ndims");
}
return set;
}
template <cpu_isa_t isa>
struct jit_stat_and_data_base_kernel_t : stat_and_data_kernel_t,
public jit_generator_t {
DECLARE_CPU_JIT_AUX_FUNCTIONS(jit_lnorm_stat_and_data_kernel_t);
void operator()(const void *src, void *dst, const float *scale,
const float *shift, float *mean, float *var, const void *src_scales,
const void *dst_scales, const void *post_ops_binary_rhs_arg_vec,
const size_t block_size) const override {
ker_args_t args;
args.src = src;
args.dst = dst;
args.scale = scale;
args.shift = shift;
args.mean = mean;
args.var = var;
args.src_scales = src_scales;
args.dst_scales = dst_scales;
args.block_size
= block_size * C_ * types::data_type_size(src_d_.data_type());
args.eps = eps_;
args.post_ops_binary_rhs_arg_vec = post_ops_binary_rhs_arg_vec;
jit_generator_t::operator()(&args);
}
status_t create_kernel() override {
return jit_generator_t::create_kernel();
}
jit_stat_and_data_base_kernel_t(const layer_normalization_pd_t *pd)
: stat_and_data_kernel_t(pd)
, jit_generator_t(jit_name(), isa)
, src_d_(pd_->src_md())
, dst_d_(pd_->dst_md())
, simd_w_(vlen / sizeof(float))
, C_(pd_->norm_axis())
, axis_simd_full_(C_ / simd_w_)
, axis_simd_tail_(C_ % simd_w_)
, use_scale_(pd_->use_scale())
, use_shift_(pd_->use_shift())
, save_stats_(pd_->is_training())
, calculate_stats_(!pd_->stats_are_src())
, eps_(pd_->desc()->layer_norm_epsilon)
, has_ne_convert_src_xf16_(isa == avx2 && mayiuse(avx2_vnni_2)
&& utils::one_of(
src_d_.data_type(), data_type::f16, data_type::bf16))
, skip_mean_(pd_->skip_mean()) {
const auto &post_ops = pd_->attr()->post_ops_;
with_postops_ = post_ops.len() != 0;
with_binary_ = post_ops.find(primitive_kind::binary) != -1;
with_eltwise_ = post_ops.find(primitive_kind::eltwise) != -1;
const auto &attr_scales = pd_->attr()->scales_;
with_src_scales_ = !attr_scales.has_default_values(DNNL_ARG_SRC);
with_dst_scales_ = !attr_scales.has_default_values(DNNL_ARG_DST);
io::io_conf_t io_conf;
io::io_tail_conf_t io_tail_conf(simd_w_, axis_simd_tail_,
tail_opmask_idx, vmm_tail_mask.getIdx(), reg_tmp);
io::io_emu_bf16_conf_t io_bf16_conf(bf16_emu_zmm_1_idx,
bf16_emu_zmm_2_idx, bf16_emu_zmm_3_idx, reg_tmp,
bf16_emu_zmm_4_idx);
io::io_saturation_conf_t io_saturation_conf(
vmm_zero.getIdx(), vmm_saturation_ubound.getIdx(), reg_tmp);
const auto io_isa = get_io_isa(isa,
utils::one_of(f16, src_d_.data_type(), dst_d_.data_type()),
utils::one_of(bf16, src_d_.data_type(), dst_d_.data_type()));
io_ = io::jit_io_multi_dt_helper_t<Vmm>(this, io_isa,
{src_d_.data_type(), dst_d_.data_type(), f32 },
io_conf, io_tail_conf, io_bf16_conf,
{{dst_d_.data_type(), io_saturation_conf}});
}
protected:
static constexpr int unroll_factor_ = 4;
using Vmm = typename cpu_isa_traits_t<isa>::Vmm;
const AddressFrame &vmmword = (isa == sse41) ? xword
: (isa == avx2) ? yword
: zword;
const int vlen = cpu_isa_traits_t<isa>::vlen;
struct ker_args_t {
const void *src;
void *dst;
const float *scale;
const float *shift;
const float *mean;
const float *var;
const void *src_scales;
const void *dst_scales;
const void *post_ops_binary_rhs_arg_vec;
size_t block_size;
float eps;
};
io::jit_io_multi_dt_helper_t<Vmm> io_;
const memory_desc_wrapper src_d_, dst_d_;
const size_t simd_w_;
const dim_t C_;
const dim_t axis_simd_full_;
const dim_t axis_simd_tail_;
const bool use_scale_;
const bool use_shift_;
const bool save_stats_;
const bool calculate_stats_;
const float eps_;
const bool has_ne_convert_src_xf16_;
const bool skip_mean_;
bool with_postops_ = false;
bool with_binary_ = false;
bool with_eltwise_ = false;
bool with_src_scales_ = false;
bool with_dst_scales_ = false;
std::unique_ptr<injector::jit_uni_postops_injector_t<isa>>
postops_injector_;
const Reg64 reg_param = abi_param1;
const Reg64 reg_src = rdx;
const Reg64 reg_dst = rax;
const Reg64 reg_mean = rbx;
const Reg64 reg_scale = r8;
const Reg64 reg_block_end = r9;
const Reg64 reg_eps = r10;
const Reg64 reg_tmp = r11;
const Reg64 reg_shift = r12;
const Reg64 reg_var = r13;
const Reg64 reg_src_scales = r14;
const Reg64 reg_dst_scales = r15;
const Vmm vmm_tail_mask = Vmm(0);
const Vmm vmm_zero = Vmm(4); const Vmm vmm_saturation_ubound
= Vmm(5); const Vmm vmm_qscale = Vmm(6); const Vmm vmm_scale = Vmm(7); const Vmm vmm_shift = Vmm(8); const Vmm vmm_ones = Vmm(9);
const Vmm vmm_eps = Vmm(10);
const Vmm vmm_c = Vmm(11);
const Vmm vmm_mean = Vmm(12);
const Vmm vmm_inv_sqrtvar = Vmm(13);
const Vmm vmm_dst = Vmm(14);
const Vmm vmm_tmp = Vmm(15);
const Xmm xmm_tmp = Xmm(15);
const Vmm vmm_dst_even = vmm_dst;
const Vmm vmm_dst_odd = Vmm(3);
const int bf16_emu_zmm_1_idx = 28;
const int bf16_emu_zmm_2_idx = 29;
const int bf16_emu_zmm_3_idx = 30;
const int bf16_emu_zmm_4_idx = 31;
const int tail_opmask_idx = 1;
Opmask tail_opmask = Opmask(tail_opmask_idx);
const int elt_inj_opmask_idx = 2;
const Xbyak::Reg64 reg_po_injector_helper_ = r14;
Opmask elt_inj_opmask = Opmask(elt_inj_opmask_idx);
Address src_ptr(size_t offt = 0) {
return vmmword[reg_src + offt * src_d_.data_type_size()];
}
Address dst_ptr(size_t offt = 0) {
return vmmword[reg_dst + offt * dst_d_.data_type_size()];
}
Address mean_ptr(size_t offt = 0) {
return vmmword[reg_mean + offt * sizeof(float)];
}
Address var_ptr(size_t offt = 0) {
return vmmword[reg_var + offt * sizeof(float)];
}
Address scale_ptr(size_t offt = 0) {
return vmmword[reg_scale + offt * sizeof(float)];
}
Address shift_ptr(size_t offt = 0) {
return vmmword[reg_shift + offt * sizeof(float)];
}
virtual void reduce(Vmm vmm_src, Vmm vmm_tmp) = 0;
void uni_vsubps_maybe_tail(const Vmm &x1, const Vmm &x2, const bool tail) {
if (!tail)
uni_vsubps(x1, x1, x2);
else {
if (is_superset(isa, avx512_core))
uni_vsubps(x1 | Opmask(tail_opmask_idx) | T_z, x1, x2);
else if (is_superset(isa, sse41)) {
uni_vpxor(vmm_tmp, vmm_tmp, vmm_tmp);
uni_vblendvps(vmm_tmp, vmm_tmp, x2, vmm_tail_mask);
uni_vsubps(x1, x1, vmm_tmp);
}
}
}
template <typename F>
void compute_ne_convert_xf16(Vmm vmm_stat, F op) {
bool need_tail = false;
int base_idx = 1;
uni_vpxor(Vmm(base_idx), Vmm(base_idx), Vmm(base_idx));
if (axis_simd_full_ > 0) {
const int unroll
= axis_simd_full_ >= unroll_factor_ ? unroll_factor_ : 1;
assert(math::is_pow2(unroll));
for (int i = base_idx + 1; i < base_idx + unroll; i++)
uni_vpxor(Vmm(i), Vmm(i), Vmm(i));
for (int i = 0; i < axis_simd_full_ / unroll; i++)
for (int j = base_idx; j < base_idx + unroll; j += 2) {
const bool can_load_two_simdw = base_idx + unroll - j >= 2;
if (!can_load_two_simdw)
io_[src_d_.data_type()]->load(
src_ptr((i * unroll + j - base_idx) * simd_w_),
Vmm(j + unroll), need_tail);
else
io_[src_d_.data_type()]->load_two_simdw_xf16(
src_ptr((i * unroll + j - base_idx) * simd_w_),
Vmm(j + unroll), Vmm(j + 1 + unroll));
op(Vmm(j), Vmm(j + unroll), need_tail);
if (can_load_two_simdw)
op(Vmm(j + 1), Vmm(j + 1 + unroll), need_tail);
}
int n = unroll;
while (n > 1) {
for (int j = base_idx; j < base_idx + n / 2; j++)
uni_vaddps(Vmm(j), Vmm(j), Vmm(j + n / 2));
n = n / 2;
}
for (int i = utils::rnd_dn(axis_simd_full_, unroll);
i < axis_simd_full_; i += 2) {
const bool can_load_two_simdw = axis_simd_full_ - i >= 2;
if (!can_load_two_simdw)
io_[src_d_.data_type()]->load(
src_ptr(i * simd_w_), Vmm(base_idx + 1), need_tail);
else
io_[src_d_.data_type()]->load_two_simdw_xf16(
src_ptr(i * simd_w_), Vmm(base_idx + 1),
Vmm(base_idx + 2));
op(Vmm(base_idx), Vmm(base_idx + 1), need_tail);
if (can_load_two_simdw)
op(Vmm(base_idx), Vmm(base_idx + 2), need_tail);
}
}
if (axis_simd_tail_ > 0) {
need_tail = true;
io_[src_d_.data_type()]->load(src_ptr(axis_simd_full_ * simd_w_),
Vmm(base_idx + 1), need_tail);
op(Vmm(base_idx), Vmm(base_idx + 1), need_tail);
}
reduce(Vmm(base_idx), Vmm(base_idx + 1));
uni_vdivps(Vmm(base_idx), Vmm(base_idx), vmm_c, vmm_tmp);
uni_vmovups(vmm_stat, Vmm(base_idx));
}
template <typename F>
void compute(Vmm vmm_stat, F op) {
bool need_tail = false;
int base_idx = 1;
uni_vpxor(Vmm(base_idx), Vmm(base_idx), Vmm(base_idx));
if (axis_simd_full_ > 0) {
const int unroll
= axis_simd_full_ >= unroll_factor_ ? unroll_factor_ : 1;
assert(math::is_pow2(unroll));
for (int i = base_idx + 1; i < base_idx + unroll; i++)
uni_vpxor(Vmm(i), Vmm(i), Vmm(i));
for (int i = 0; i < axis_simd_full_ / unroll; i++)
for (int j = base_idx; j < base_idx + unroll; j++) {
io_[src_d_.data_type()]->load(
src_ptr((i * unroll + j - base_idx) * simd_w_),
Vmm(j + unroll), need_tail);
op(Vmm(j), Vmm(j + unroll), need_tail);
}
int n = unroll;
while (n > 1) {
for (int j = base_idx; j < base_idx + n / 2; j++)
uni_vaddps(Vmm(j), Vmm(j), Vmm(j + n / 2));
n = n / 2;
}
for (int i = utils::rnd_dn(axis_simd_full_, unroll);
i < axis_simd_full_; i++) {
io_[src_d_.data_type()]->load(
src_ptr(i * simd_w_), Vmm(base_idx + 1), need_tail);
op(Vmm(base_idx), Vmm(base_idx + 1), need_tail);
}
}
if (axis_simd_tail_ > 0) {
need_tail = true;
io_[src_d_.data_type()]->load(src_ptr(axis_simd_full_ * simd_w_),
Vmm(base_idx + 1), need_tail);
op(Vmm(base_idx), Vmm(base_idx + 1), need_tail);
}
reduce(Vmm(base_idx), Vmm(base_idx + 1));
uni_vdivps(Vmm(base_idx), Vmm(base_idx), vmm_c, vmm_tmp);
uni_vmovups(vmm_stat, Vmm(base_idx));
}
void compute_mean() {
if (has_ne_convert_src_xf16_)
compute_ne_convert_xf16(
vmm_mean, [&](Vmm vmm_dst, Vmm vmm_src, bool need_tail) {
uni_vaddps(vmm_dst, vmm_dst, vmm_src);
});
else
compute(vmm_mean, [&](Vmm vmm_dst, Vmm vmm_src, bool need_tail) {
uni_vaddps(vmm_dst, vmm_dst, vmm_src);
});
if (save_stats_) uni_vmovss(ptr[reg_mean], Xmm(vmm_mean.getIdx()));
}
void compute_var() {
auto compute_var_lambda
= [&](Vmm vmm_dst, Vmm vmm_src, bool need_tail) {
if (!skip_mean_) {
uni_vsubps_maybe_tail(vmm_src, vmm_mean, need_tail);
}
uni_vfmadd231ps(vmm_dst, vmm_src, vmm_src);
};
if (has_ne_convert_src_xf16_)
compute_ne_convert_xf16(vmm_inv_sqrtvar, compute_var_lambda);
else
compute(vmm_inv_sqrtvar, compute_var_lambda);
if (save_stats_)
uni_vmovss(ptr[reg_var], Xmm(vmm_inv_sqrtvar.getIdx()));
}
void calculate_ne_convert_xf16_dst_body(
size_t offt_elems, bool tail = false) {
io_[src_d_.data_type()]->load_two_simdw_xf16(
src_ptr(offt_elems), vmm_dst_even, vmm_dst_odd);
io_[src_d_.data_type()]->merge_interleaved_to_plain(
vmm_dst_even, vmm_dst_odd, vmm_tmp);
for (int j = 0; j < 2; j++) {
const auto vmm_dst = j == 0 ? vmm_dst_even : vmm_dst_odd;
if (use_scale_)
io_[f32]->load(
scale_ptr(offt_elems + j * simd_w_), vmm_scale, tail);
if (use_shift_)
io_[f32]->load(
shift_ptr(offt_elems + j * simd_w_), vmm_shift, tail);
if (!skip_mean_) uni_vsubps(vmm_dst, vmm_dst, vmm_mean);
uni_vmulps(vmm_dst, vmm_dst, vmm_inv_sqrtvar);
if (use_scale_ && use_shift_)
uni_vfmadd213ps(vmm_dst, vmm_scale, vmm_shift);
else {
if (use_scale_) uni_vmulps(vmm_dst, vmm_dst, vmm_scale);
if (use_shift_) uni_vaddps(vmm_dst, vmm_dst, vmm_shift);
}
if (with_src_scales_) {
uni_vbroadcastss(vmm_qscale, ptr[reg_src_scales]);
uni_vmulps(vmm_dst, vmm_dst, vmm_qscale);
}
if (with_postops_) {
binary_injector::rhs_arg_dynamic_params_t rhs_arg_params;
if (with_binary_) {
rhs_arg_params.vmm_idx_to_out_addr.emplace(
vmm_dst.getIdx(), dst_ptr());
rhs_arg_params.vmm_idx_to_out_elem_off_val.emplace(
vmm_dst.getIdx(),
(offt_elems + j * simd_w_)
* dst_d_.data_type_size());
if (tail)
rhs_arg_params.vmm_tail_idx_.emplace(vmm_dst.getIdx());
}
postops_injector_->compute_vector(
vmm_dst.getIdx(), rhs_arg_params);
}
if (with_dst_scales_) {
uni_vbroadcastss(vmm_qscale, ptr[reg_dst_scales]);
uni_vmulps(vmm_dst, vmm_dst, vmm_qscale);
}
io_[dst_d_.data_type()]->store(
vmm_dst, dst_ptr(offt_elems + j * simd_w_), tail);
}
}
void calculate_dst_body(size_t offt_elems, bool tail = false) {
if (use_scale_) {
io_[f32]->load(scale_ptr(offt_elems), vmm_scale, tail);
}
if (use_shift_) {
io_[f32]->load(shift_ptr(offt_elems), vmm_shift, tail);
}
io_[src_d_.data_type()]->load(src_ptr(offt_elems), vmm_dst, tail);
if (!skip_mean_) uni_vsubps(vmm_dst, vmm_dst, vmm_mean);
uni_vmulps(vmm_dst, vmm_dst, vmm_inv_sqrtvar);
if (use_scale_ && use_shift_)
uni_vfmadd213ps(vmm_dst, vmm_scale, vmm_shift);
else {
if (use_scale_) uni_vmulps(vmm_dst, vmm_dst, vmm_scale);
if (use_shift_) uni_vaddps(vmm_dst, vmm_dst, vmm_shift);
}
if (with_src_scales_) {
uni_vbroadcastss(vmm_qscale, ptr[reg_src_scales]);
uni_vmulps(vmm_dst, vmm_dst, vmm_qscale);
}
if (with_postops_) {
binary_injector::rhs_arg_dynamic_params_t rhs_arg_params;
if (with_binary_) {
rhs_arg_params.vmm_idx_to_out_addr.emplace(
vmm_dst.getIdx(), dst_ptr());
rhs_arg_params.vmm_idx_to_out_elem_off_val.emplace(
vmm_dst.getIdx(), offt_elems * dst_d_.data_type_size());
if (tail)
rhs_arg_params.vmm_tail_idx_.emplace(vmm_dst.getIdx());
}
postops_injector_->compute_vector(vmm_dst.getIdx(), rhs_arg_params);
}
if (with_dst_scales_) {
uni_vbroadcastss(vmm_qscale, ptr[reg_dst_scales]);
uni_vmulps(vmm_dst, vmm_dst, vmm_qscale);
}
io_[dst_d_.data_type()]->store(vmm_dst, dst_ptr(offt_elems), tail);
}
void calculate_dst() {
if (has_ne_convert_src_xf16_) {
for (int i = 0; i < axis_simd_full_; i += 2) {
const bool can_load_two_simdw = axis_simd_full_ - i >= 2;
if (can_load_two_simdw)
calculate_ne_convert_xf16_dst_body(i * simd_w_);
else
calculate_dst_body(i * simd_w_);
}
} else {
for (int i = 0; i < axis_simd_full_; i++)
calculate_dst_body(i * simd_w_);
}
if (axis_simd_tail_)
calculate_dst_body(axis_simd_full_ * simd_w_, true);
}
void generate() override {
const size_t c_src_size
= C_ * types::data_type_size(src_d_.data_type());
const size_t c_dst_size
= C_ * types::data_type_size(dst_d_.data_type());
static const size_t float_size = types::data_type_size(f32);
#define PARAM_OFF(x) offsetof(ker_args_t, x)
if (with_postops_) {
static constexpr bool preserve_gpr = true;
static constexpr bool preserve_vmm = true;
static constexpr bool use_exact_tail_scalar_bcast = true;
static const std::size_t tmp_vmm_injector = this->vmm_tmp.getIdx();
const eltwise_injector::static_params_t esp(true ,
reg_po_injector_helper_, elt_inj_opmask, true ,
false );
const binary_injector::rhs_arg_static_params_t rhs_sp {
tmp_vmm_injector, this->r14, this->r15, this->r13,
preserve_gpr, preserve_vmm,
PARAM_OFF(post_ops_binary_rhs_arg_vec), PARAM_OFF(dst),
dst_d_, static_cast<size_t>(axis_simd_tail_), tail_opmask,
use_exact_tail_scalar_bcast};
const binary_injector::static_params_t bsp {reg_param,
get_supported_bcast_strategies(dst_d_.ndims()), rhs_sp};
postops_injector_ = utils::make_unique<
injector::jit_uni_postops_injector_t<isa>>(
this, pd_->attr()->post_ops_, bsp, esp);
}
preamble();
io_.init_bf16();
if (axis_simd_tail_) io_.prepare_tail_mask();
mov(reg_src, ptr[reg_param + PARAM_OFF(src)]);
mov(reg_dst, ptr[reg_param + PARAM_OFF(dst)]);
mov(reg_scale, ptr[reg_param + PARAM_OFF(scale)]);
mov(reg_shift, ptr[reg_param + PARAM_OFF(shift)]);
mov(reg_mean, ptr[reg_param + PARAM_OFF(mean)]);
mov(reg_var, ptr[reg_param + PARAM_OFF(var)]);
mov(reg_src_scales, ptr[reg_param + PARAM_OFF(src_scales)]);
mov(reg_dst_scales, ptr[reg_param + PARAM_OFF(dst_scales)]);
mov(reg_block_end, ptr[reg_param + PARAM_OFF(block_size)]);
mov(reg_eps, ptr[reg_param + PARAM_OFF(eps)]);
#undef PARAM_OFF
uni_vmovq(xmm_tmp, reg_eps);
uni_vbroadcastss(vmm_eps, xmm_tmp);
mov(reg_tmp, float2int(1.f));
uni_vmovq(xmm_tmp, reg_tmp);
uni_vbroadcastss(vmm_ones, xmm_tmp);
mov(reg_tmp, float2int(C_));
uni_vmovq(xmm_tmp, reg_tmp);
uni_vbroadcastss(vmm_c, xmm_tmp);
add(reg_block_end, reg_src);
Label unroll_loop, end;
L(unroll_loop);
{
cmp(reg_block_end, reg_src);
jle(end, T_NEAR);
if (calculate_stats_) {
if (!skip_mean_) { compute_mean(); }
compute_var();
} else {
if (!skip_mean_) {
uni_vmovss(xmm_tmp, dword[reg_mean]);
uni_vbroadcastss(vmm_mean, xmm_tmp);
}
uni_vmovss(xmm_tmp, dword[reg_var]);
uni_vbroadcastss(vmm_inv_sqrtvar, xmm_tmp);
}
uni_vaddps(vmm_inv_sqrtvar, vmm_inv_sqrtvar, vmm_eps);
uni_vsqrtps(vmm_inv_sqrtvar, vmm_inv_sqrtvar);
uni_vdivps(vmm_inv_sqrtvar, vmm_ones, vmm_inv_sqrtvar, vmm_tmp);
io_.init_saturate_f32({dst_d_.data_type()});
calculate_dst();
add(reg_src, c_src_size);
add(reg_dst, c_dst_size);
add(reg_mean, float_size);
add(reg_var, float_size);
jmp(unroll_loop);
}
L(end);
postamble();
if (with_eltwise_ && postops_injector_)
postops_injector_->prepare_table( true);
}
};
template <cpu_isa_t isa>
struct jit_stat_and_data_kernel_t;
template <>
struct jit_stat_and_data_kernel_t<avx512_core>
: public jit_stat_and_data_base_kernel_t<avx512_core> {
using jit_stat_and_data_base_kernel_t::jit_stat_and_data_base_kernel_t;
void reduce(Vmm vmm_src, Vmm vmm_tmp) override {
vshuff32x4(vmm_tmp, vmm_src, vmm_src, 0x4E); vaddps(vmm_src, vmm_src, vmm_tmp);
vshuff32x4(vmm_tmp, vmm_src, vmm_src, 0xB1); vaddps(vmm_src, vmm_src, vmm_tmp);
vshufps(vmm_tmp, vmm_src, vmm_src, 0x4E); vaddps(vmm_src, vmm_src, vmm_tmp);
vshufps(vmm_tmp, vmm_src, vmm_src, 0xB1); vaddps(vmm_src, vmm_src, vmm_tmp);
}
};
template <>
struct jit_stat_and_data_kernel_t<avx2>
: jit_stat_and_data_base_kernel_t<avx2> {
using jit_stat_and_data_base_kernel_t::jit_stat_and_data_base_kernel_t;
void reduce(Vmm vmm_src, Vmm vmm_tmp) override {
vperm2f128(vmm_tmp, vmm_src, vmm_src, 0x1); vaddps(vmm_src, vmm_src, vmm_tmp);
vshufps(vmm_tmp, vmm_src, vmm_src, 0x4E); vaddps(vmm_src, vmm_src, vmm_tmp);
vshufps(vmm_tmp, vmm_src, vmm_src, 0xB1); vaddps(vmm_src, vmm_src, vmm_tmp);
}
};
template <>
struct jit_stat_and_data_kernel_t<sse41>
: jit_stat_and_data_base_kernel_t<sse41> {
using jit_stat_and_data_base_kernel_t::jit_stat_and_data_base_kernel_t;
void reduce(Vmm vmm_src, Vmm vmm_tmp) override {
uni_vmovups(vmm_tmp, vmm_src);
shufps(vmm_tmp, vmm_tmp, 0x4E); uni_vaddps(vmm_src, vmm_src, vmm_tmp);
uni_vmovups(vmm_tmp, vmm_src);
shufps(vmm_tmp, vmm_tmp, 0xB1); uni_vaddps(vmm_src, vmm_src, vmm_tmp);
}
};
stat_and_data_kernel_t *stat_and_data_kernel_t::create(
const layer_normalization_pd_t *pd) {
if (mayiuse(avx512_core)) {
return new jit_stat_and_data_kernel_t<avx512_core>(pd);
} else if (mayiuse(avx2)) {
return new jit_stat_and_data_kernel_t<avx2>(pd);
} else if (mayiuse(sse41)) {
return new jit_stat_and_data_kernel_t<sse41>(pd);
} else {
assert(!"kernel is empty.");
return nullptr;
}
}
template <cpu_isa_t isa>
struct jit_diff_ss_kernel_t : diff_ss_kernel_t, public jit_generator_t {
DECLARE_CPU_JIT_AUX_FUNCTIONS(jit_lnorm_diff_ss_kernel_t);
void operator()(const void *src, const void *diff_dst, float *diff_scale,
float *diff_shift, const float *mean, const float *var,
float *const inv_sqrtvar, const size_t block_size) const override {
ker_args_t args;
args.src = src;
args.diff_dst = diff_dst;
args.diff_scale = diff_scale;
args.diff_shift = diff_shift;
args.mean = mean;
for (size_t i = 0; i < block_size; i++) {
#ifdef __INTEL_COMPILER
const volatile float denom = sqrtf(var[i] + eps_);
#else
const float denom = sqrtf(var[i] + eps_);
#endif
inv_sqrtvar[i] = 1.f / denom;
}
args.inv_sqrtvar = inv_sqrtvar;
args.block_size
= block_size * C_ * types::data_type_size(src_d_.data_type());
jit_generator_t::operator()(&args);
}
status_t create_kernel() override {
return jit_generator_t::create_kernel();
}
jit_diff_ss_kernel_t(const layer_normalization_pd_t *pd)
: diff_ss_kernel_t(pd)
, jit_generator_t(jit_name())
, src_d_(pd_->src_md())
, d_dst_d_(pd_->diff_dst_md())
, simd_w_(vlen / sizeof(float))
, C_(pd_->norm_axis())
, axis_simd_full_(C_ / simd_w_)
, axis_simd_tail_(C_ % simd_w_)
, eps_(pd_->desc()->layer_norm_epsilon)
, skip_mean_(pd_->skip_mean()) {
io::io_conf_t io_conf;
io::io_tail_conf_t io_tail_conf(simd_w_, axis_simd_tail_,
tail_opmask_idx, vmm_tail_mask.getIdx(), reg_tmp);
io::io_emu_bf16_conf_t io_bf16_conf(bf16_emu_zmm_1_idx,
bf16_emu_zmm_2_idx, bf16_emu_zmm_3_idx, reg_tmp,
bf16_emu_zmm_4_idx);
const auto io_isa = get_io_isa(isa,
utils::one_of(f16, src_d_.data_type(), d_dst_d_.data_type()),
utils::one_of(bf16, src_d_.data_type(), d_dst_d_.data_type()));
io_ = io::jit_io_multi_dt_helper_t<Vmm>(this, io_isa,
{src_d_.data_type(), d_dst_d_.data_type(), f32 },
io_conf, io_tail_conf, io_bf16_conf);
}
protected:
using Vmm = typename cpu_isa_traits_t<isa>::Vmm;
const AddressFrame &vmmword = (isa == sse41) ? xword
: (isa == avx2) ? yword
: zword;
const int vlen = cpu_isa_traits_t<isa>::vlen;
struct ker_args_t {
const void *src;
const void *diff_dst;
float *diff_scale;
float *diff_shift;
const float *mean;
const float *inv_sqrtvar;
size_t block_size;
};
io::jit_io_multi_dt_helper_t<Vmm> io_;
const memory_desc_wrapper src_d_, d_dst_d_;
const size_t simd_w_;
const dim_t C_;
const dim_t axis_simd_full_;
const dim_t axis_simd_tail_;
const float eps_;
const bool skip_mean_;
const Reg64 reg_param = abi_param1;
const Reg64 reg_src = rdx;
const Reg64 reg_diff_dst = rax;
const Reg64 reg_mean = rbx;
const Reg64 reg_diff_scale = r8;
const Reg64 reg_block_end = r9;
const Reg64 reg_tmp = r11;
const Reg64 reg_diff_shift = r12;
const Reg64 reg_inv_sqrtvar = r13;
const Vmm vmm_tail_mask = Vmm(0);
const Xmm xmm_tmp = Xmm(9);
const Vmm vmm_inv_sqrtvar = Vmm(10);
const Vmm vmm_ddst = Vmm(11);
const Vmm vmm_dscale = Vmm(12);
const Vmm vmm_dshift = Vmm(13);
const Vmm vmm_src = Vmm(14);
const Vmm vmm_mean = Vmm(15);
const int bf16_emu_zmm_1_idx = 28;
const int bf16_emu_zmm_2_idx = 29;
const int bf16_emu_zmm_3_idx = 30;
const int bf16_emu_zmm_4_idx = 31;
const int tail_opmask_idx = 1;
Address src_ptr(size_t offt = 0) {
return vmmword[reg_src + offt * src_d_.data_type_size()];
}
Address d_dst_ptr(size_t offt = 0) {
return vmmword[reg_diff_dst + offt * d_dst_d_.data_type_size()];
}
Address d_scale_ptr(size_t offt = 0) {
return vmmword[reg_diff_scale + offt * sizeof(float)];
}
Address d_shift_ptr(size_t offt = 0) {
return vmmword[reg_diff_shift + offt * sizeof(float)];
}
void calculate_diff_scale_shift(size_t offt_elems, bool tail = false) {
io_[d_dst_d_.data_type()]->load(d_dst_ptr(offt_elems), vmm_ddst, tail);
io_[f32]->load(d_scale_ptr(offt_elems), vmm_dscale, tail);
io_[f32]->load(d_shift_ptr(offt_elems), vmm_dshift, tail);
io_[src_d_.data_type()]->load(src_ptr(offt_elems), vmm_src, tail);
uni_vaddps(vmm_dshift, vmm_dshift, vmm_ddst);
if (!skip_mean_) uni_vsubps(vmm_src, vmm_src, vmm_mean);
uni_vmulps(vmm_src, vmm_src, vmm_inv_sqrtvar);
uni_vfmadd231ps(vmm_dscale, vmm_src, vmm_ddst);
io_[f32]->store(vmm_dscale, d_scale_ptr(offt_elems), tail);
io_[f32]->store(vmm_dshift, d_shift_ptr(offt_elems), tail);
}
void generate() override {
const size_t c_src_size
= C_ * types::data_type_size(src_d_.data_type());
const size_t c_ddst_size
= C_ * types::data_type_size(d_dst_d_.data_type());
static const size_t float_size = types::data_type_size(f32);
preamble();
io_.init_bf16();
if (axis_simd_tail_) io_.prepare_tail_mask();
#define PARAM_OFF(x) offsetof(ker_args_t, x)
mov(reg_src, ptr[reg_param + PARAM_OFF(src)]);
mov(reg_diff_dst, ptr[reg_param + PARAM_OFF(diff_dst)]);
mov(reg_diff_scale, ptr[reg_param + PARAM_OFF(diff_scale)]);
mov(reg_diff_shift, ptr[reg_param + PARAM_OFF(diff_shift)]);
mov(reg_mean, ptr[reg_param + PARAM_OFF(mean)]);
mov(reg_inv_sqrtvar, ptr[reg_param + PARAM_OFF(inv_sqrtvar)]);
mov(reg_block_end, ptr[reg_param + PARAM_OFF(block_size)]);
#undef PARAM_OFF
add(reg_block_end, reg_src);
Label unroll_loop, end;
L(unroll_loop);
{
cmp(reg_block_end, reg_src);
jle(end, T_NEAR);
if (!skip_mean_) {
uni_vmovss(xmm_tmp, dword[reg_mean]);
uni_vbroadcastss(vmm_mean, xmm_tmp);
}
uni_vmovss(xmm_tmp, dword[reg_inv_sqrtvar]);
uni_vbroadcastss(vmm_inv_sqrtvar, xmm_tmp);
for (int i = 0; i < axis_simd_full_; i++)
calculate_diff_scale_shift(i * simd_w_);
if (axis_simd_tail_)
calculate_diff_scale_shift(axis_simd_full_ * simd_w_, true);
add(reg_src, c_src_size);
add(reg_diff_dst, c_ddst_size);
add(reg_mean, float_size);
add(reg_inv_sqrtvar, float_size);
jmp(unroll_loop);
}
L(end);
postamble();
}
};
diff_ss_kernel_t *diff_ss_kernel_t::create(const layer_normalization_pd_t *pd) {
if (mayiuse(avx512_core)) {
return new jit_diff_ss_kernel_t<avx512_core>(pd);
} else if (mayiuse(avx2)) {
return new jit_diff_ss_kernel_t<avx2>(pd);
} else {
assert(!"kernel is empty.");
return nullptr;
}
}
template <cpu_isa_t isa>
struct jit_diff_data_base_kernel_t : diff_data_kernel_t,
public jit_generator_t {
DECLARE_CPU_JIT_AUX_FUNCTIONS(jit_lnorm_diff_data_kernel_t);
void operator()(const void *src, const void *diff_dst, void *diff_src,
const float *ss, const float *mean, float *const inv_sqrtvar,
const size_t block_size) const override {
ker_args_t args;
args.src = src;
args.diff_dst = diff_dst;
args.diff_src = diff_src;
args.ss = ss;
args.mean = mean;
args.inv_sqrtvar = inv_sqrtvar;
args.block_size
= block_size * C_ * types::data_type_size(src_d_.data_type());
jit_generator_t::operator()(&args);
}
status_t create_kernel() override {
return jit_generator_t::create_kernel();
}
jit_diff_data_base_kernel_t(const layer_normalization_pd_t *pd)
: diff_data_kernel_t(pd)
, jit_generator_t(jit_name())
, src_d_(pd_->src_md())
, d_dst_d_(pd_->diff_dst_md())
, d_src_d_(pd_->diff_src_md())
, simd_w_(vlen / sizeof(float))
, C_(pd_->norm_axis())
, axis_simd_full_(C_ / simd_w_)
, axis_simd_tail_(C_ % simd_w_)
, use_scale_(pd_->use_scale())
, use_shift_(pd_->use_shift())
, calculate_diff_stats_(!pd_->stats_are_src())
, skip_mean_(pd_->skip_mean()) {
io::io_conf_t io_conf;
io::io_tail_conf_t io_tail_conf(simd_w_, axis_simd_tail_,
tail_opmask_idx, vmm_tail_mask.getIdx(), reg_tmp);
io::io_emu_bf16_conf_t io_bf16_conf(bf16_emu_zmm_1_idx,
bf16_emu_zmm_2_idx, bf16_emu_zmm_3_idx, reg_tmp,
bf16_emu_zmm_4_idx);
const auto io_isa = get_io_isa(isa,
utils::one_of(f16, src_d_.data_type(), d_dst_d_.data_type(),
d_src_d_.data_type()),
utils::one_of(bf16, src_d_.data_type(), d_dst_d_.data_type(),
d_src_d_.data_type()));
io_ = io::jit_io_multi_dt_helper_t<Vmm>(this, io_isa,
{src_d_.data_type(), d_dst_d_.data_type(), d_src_d_.data_type(),
f32 },
io_conf, io_tail_conf, io_bf16_conf);
}
protected:
static constexpr int unroll_factor_ = 4;
using Vmm = typename cpu_isa_traits_t<isa>::Vmm;
const AddressFrame &vmmword = (isa == sse41) ? xword
: (isa == avx2) ? yword
: zword;
const int vlen = cpu_isa_traits_t<isa>::vlen;
struct ker_args_t {
const void *src;
const void *diff_dst;
void *diff_src;
const float *ss;
const float *mean;
const float *inv_sqrtvar;
size_t block_size;
};
io::jit_io_multi_dt_helper_t<Vmm> io_;
const memory_desc_wrapper src_d_, d_dst_d_, d_src_d_;
const size_t simd_w_;
const dim_t C_;
const dim_t axis_simd_full_;
const dim_t axis_simd_tail_;
const bool use_scale_;
const bool use_shift_;
const bool calculate_diff_stats_;
const bool skip_mean_;
const Reg64 reg_param = abi_param1;
const Reg64 reg_src = rdx;
const Reg64 reg_diff_dst = rax;
const Reg64 reg_diff_src = r14;
const Reg64 reg_mean = rbx;
const Reg64 reg_inv_sqrtvar = r13;
const Reg64 reg_scale = r8;
const Reg64 reg_tmp = r11;
const Reg64 reg_dd_scale = r10;
const Reg64 reg_dd_scale_x = r12;
const Reg64 reg_block_end = r9;
const Vmm vmm_tail_mask = Vmm(0);
const Vmm vmm_C = Vmm(7);
const Vmm vmm_scale = Vmm(8);
const Xmm xmm_tmp = Xmm(9);
const Vmm vmm_tmp = Vmm(9);
const Vmm vmm_inv_sqrtvar = Vmm(10);
const Vmm vmm_dsrc = Vmm(11);
const Vmm vmm_dd_scale_x = Vmm(12);
const Vmm vmm_dd_scale = Vmm(13);
const Vmm vmm_src = Vmm(14);
const Vmm vmm_mean = Vmm(15);
const int bf16_emu_zmm_1_idx = 28;
const int bf16_emu_zmm_2_idx = 29;
const int bf16_emu_zmm_3_idx = 30;
const int bf16_emu_zmm_4_idx = 31;
const int tail_opmask_idx = 1;
Address src_ptr(size_t offt = 0) {
return vmmword[reg_src + offt * src_d_.data_type_size()];
}
Address d_dst_ptr(size_t offt = 0) {
return vmmword[reg_diff_dst + offt * d_dst_d_.data_type_size()];
}
Address d_src_ptr(size_t offt = 0) {
return vmmword[reg_diff_src + offt * d_src_d_.data_type_size()];
}
Address scale_ptr(size_t offt = 0) {
return vmmword[reg_scale + offt * sizeof(float)];
}
virtual void reduce(Vmm vmm_src, Vmm vmm_tmp) = 0;
void compute_dd_scales(size_t offt_elems, bool tail = false) {
Vmm vmm_ddst = vmm_dsrc;
io_[d_dst_d_.data_type()]->load(d_dst_ptr(offt_elems), vmm_ddst, tail);
if (use_scale_) {
io_[f32]->load(scale_ptr(offt_elems), vmm_scale, tail);
uni_vmulps(vmm_ddst, vmm_ddst, vmm_scale);
}
io_[src_d_.data_type()]->load(src_ptr(offt_elems), vmm_src, tail);
uni_vaddps(vmm_dd_scale, vmm_dd_scale, vmm_ddst);
if (!skip_mean_) { uni_vsubps(vmm_src, vmm_src, vmm_mean); }
uni_vfmadd231ps(vmm_dd_scale_x, vmm_ddst, vmm_src);
}
void compute_diff_src(size_t offt_elems, bool tail = false) {
Vmm vmm_ddst = vmm_dsrc;
io_[d_dst_d_.data_type()]->load(d_dst_ptr(offt_elems), vmm_ddst, tail);
if (use_scale_) {
io_[f32]->load(scale_ptr(offt_elems), vmm_scale, tail);
uni_vmulps(vmm_dsrc, vmm_dsrc, vmm_scale);
}
if (calculate_diff_stats_) {
io_[src_d_.data_type()]->load(src_ptr(offt_elems), vmm_src, tail);
if (!skip_mean_) { uni_vsubps(vmm_src, vmm_src, vmm_mean); }
uni_vmulps(vmm_src, vmm_src, vmm_inv_sqrtvar);
uni_vfmadd213ps(vmm_src, vmm_dd_scale_x, vmm_dd_scale);
uni_vdivps(vmm_src, vmm_src, vmm_C);
uni_vsubps(vmm_dsrc, vmm_dsrc, vmm_src);
}
uni_vmulps(vmm_dsrc, vmm_dsrc, vmm_inv_sqrtvar);
io_[d_src_d_.data_type()]->store(vmm_dsrc, d_src_ptr(offt_elems), tail);
}
void generate() override {
const size_t c_src_size
= C_ * types::data_type_size(src_d_.data_type());
const size_t c_ddst_size
= C_ * types::data_type_size(d_dst_d_.data_type());
const size_t c_dsrc_size
= C_ * types::data_type_size(d_src_d_.data_type());
static const size_t float_size = types::data_type_size(f32);
preamble();
io_.init_bf16();
if (axis_simd_tail_) io_.prepare_tail_mask();
#define PARAM_OFF(x) offsetof(ker_args_t, x)
mov(reg_src, ptr[reg_param + PARAM_OFF(src)]);
mov(reg_diff_dst, ptr[reg_param + PARAM_OFF(diff_dst)]);
mov(reg_diff_src, ptr[reg_param + PARAM_OFF(diff_src)]);
mov(reg_scale, ptr[reg_param + PARAM_OFF(ss)]);
if (calculate_diff_stats_ && !skip_mean_) {
mov(reg_mean, ptr[reg_param + PARAM_OFF(mean)]);
}
mov(reg_inv_sqrtvar, ptr[reg_param + PARAM_OFF(inv_sqrtvar)]);
mov(reg_block_end, ptr[reg_param + PARAM_OFF(block_size)]);
#undef PARAM_OFF
mov(reg_tmp, float2int(C_));
uni_vmovq(xmm_tmp, reg_tmp);
uni_vbroadcastss(vmm_C, xmm_tmp);
add(reg_block_end, reg_src);
Label unroll_loop, end;
L(unroll_loop);
{
cmp(reg_block_end, reg_src);
jle(end, T_NEAR);
uni_vmovss(xmm_tmp, dword[reg_inv_sqrtvar]);
uni_vbroadcastss(vmm_inv_sqrtvar, xmm_tmp);
if (calculate_diff_stats_) {
if (!skip_mean_) {
uni_vmovss(xmm_tmp, dword[reg_mean]);
uni_vbroadcastss(vmm_mean, xmm_tmp);
}
uni_vpxor(vmm_dd_scale, vmm_dd_scale, vmm_dd_scale);
uni_vpxor(vmm_dd_scale_x, vmm_dd_scale_x, vmm_dd_scale_x);
for (int i = 0; i < axis_simd_full_; i++)
compute_dd_scales(i * simd_w_);
if (axis_simd_tail_)
compute_dd_scales(axis_simd_full_ * simd_w_, true);
reduce(vmm_dd_scale, vmm_tmp);
reduce(vmm_dd_scale_x, vmm_tmp);
uni_vmulps(vmm_dd_scale_x, vmm_dd_scale_x, vmm_inv_sqrtvar);
}
for (int i = 0; i < axis_simd_full_; i++)
compute_diff_src(i * simd_w_);
if (axis_simd_tail_)
compute_diff_src(axis_simd_full_ * simd_w_, true);
add(reg_src, c_src_size);
add(reg_diff_dst, c_ddst_size);
add(reg_diff_src, c_dsrc_size);
if (calculate_diff_stats_ && !skip_mean_) add(reg_mean, float_size);
add(reg_inv_sqrtvar, float_size);
jmp(unroll_loop);
}
L(end);
postamble();
}
};
template <cpu_isa_t isa>
struct jit_diff_data_kernel_t;
template <>
struct jit_diff_data_kernel_t<avx512_core>
: public jit_diff_data_base_kernel_t<avx512_core> {
using jit_diff_data_base_kernel_t::jit_diff_data_base_kernel_t;
void reduce(Vmm vmm_src, Vmm vmm_tmp) override {
vshuff32x4(vmm_tmp, vmm_src, vmm_src, 0x4E); vaddps(vmm_src, vmm_src, vmm_tmp);
vshuff32x4(vmm_tmp, vmm_src, vmm_src, 0xB1); vaddps(vmm_src, vmm_src, vmm_tmp);
vshufps(vmm_tmp, vmm_src, vmm_src, 0x4E); vaddps(vmm_src, vmm_src, vmm_tmp);
vshufps(vmm_tmp, vmm_src, vmm_src, 0xB1); vaddps(vmm_src, vmm_src, vmm_tmp);
}
};
template <>
struct jit_diff_data_kernel_t<avx2> : public jit_diff_data_base_kernel_t<avx2> {
using jit_diff_data_base_kernel_t::jit_diff_data_base_kernel_t;
void reduce(Vmm vmm_src, Vmm vmm_tmp) override {
vperm2f128(vmm_tmp, vmm_src, vmm_src, 0x1); vaddps(vmm_src, vmm_src, vmm_tmp);
vshufps(vmm_tmp, vmm_src, vmm_src, 0x4E); vaddps(vmm_src, vmm_src, vmm_tmp);
vshufps(vmm_tmp, vmm_src, vmm_src, 0xB1); vaddps(vmm_src, vmm_src, vmm_tmp);
}
};
diff_data_kernel_t *diff_data_kernel_t::create(
const layer_normalization_pd_t *pd) {
if (mayiuse(avx512_core)) {
return new jit_diff_data_kernel_t<avx512_core>(pd);
} else if (mayiuse(avx2)) {
return new jit_diff_data_kernel_t<avx2>(pd);
} else {
assert(!"kernel is empty.");
return nullptr;
}
}
status_t jit_uni_layer_normalization_fwd_t::pd_t::init(engine_t *engine) {
using namespace data_type;
using skip_mask_t = primitive_attr_t::skip_mask_t;
const memory_desc_wrapper src_d(src_md());
VDISPATCH_LNORM(is_fwd(), VERBOSE_BAD_PROPKIND);
VDISPATCH_LNORM(!has_zero_dim_memory(), VERBOSE_EMPTY_TENSOR, "src");
VDISPATCH_LNORM(utils::one_of(src_md()->data_type, f32, bf16, f16, s8, u8),
VERBOSE_UNSUPPORTED_DT);
VDISPATCH_LNORM(utils::one_of(dst_md()->data_type, f32, bf16, f16, s8, u8),
VERBOSE_UNSUPPORTED_DT);
VDISPATCH_LNORM(IMPLICATION(utils::one_of(bf16, src_md()->data_type,
dst_md()->data_type),
mayiuse(avx512_core) || mayiuse(avx2_vnni_2)),
VERBOSE_ISA_DT_MISMATCH);
VDISPATCH_LNORM(IMPLICATION(utils::one_of(f16, src_md()->data_type,
dst_md()->data_type),
mayiuse(avx512_core_fp16) || mayiuse(avx2_vnni_2)),
VERBOSE_ISA_DT_MISMATCH);
VDISPATCH_LNORM(stat_md()->data_type == f32, VERBOSE_UNSUPPORTED_DT);
VDISPATCH_LNORM(check_scale_shift_data_type(), VERBOSE_UNSUPPORTED_FEATURE,
"unsupported scale or shift data type");
VDISPATCH_LNORM(attr()->has_default_values(
skip_mask_t::scales | skip_mask_t::post_ops),
VERBOSE_UNSUPPORTED_ATTR);
VDISPATCH_LNORM(attr_scales_ok(), VERBOSE_UNSUPPORTED_SCALES_CFG);
VDISPATCH_LNORM(set_default_formats_common(), VERBOSE_UNSUPPORTED_TAG);
VDISPATCH_LNORM(src_d.is_blocking_desc(), VERBOSE_BLOCKING_FAIL,
"blocking descriptor fail");
VDISPATCH_LNORM(src_d.blocking_desc().strides[ndims() - 1] == 1,
VERBOSE_BLOCKING_FAIL, "bad stride value");
VDISPATCH_LNORM(impl::is_dense_format_kind({src_md(), dst_md()}),
VERBOSE_UNSUPPORTED_SPARSE_CFG);
auto post_ops_ok = [&]() -> bool {
const std::vector<injector::post_op_type> accepted_post_ops
= {injector::eltwise, injector::binary, injector::sum};
const memory_desc_wrapper dst_d(dst_md());
injector::post_ops_ok_args_t post_ops_args(get_supported_isa(),
accepted_post_ops, attr()->post_ops_, &dst_d, true, true, true,
true, get_supported_bcast_strategies(dst_d.ndims()));
return injector::post_ops_ok(post_ops_args)
&& !binary_injector::
any_binary_postop_rhs_with_ternary_scalar_bcast(
attr()->post_ops_, dst_d);
};
VDISPATCH_LNORM(attr_.set_default_formats(dst_md(0)) == status::success,
VERBOSE_UNSUPPORTED_POSTOP);
VDISPATCH_LNORM(post_ops_ok(), VERBOSE_UNSUPPORTED_POSTOP);
VDISPATCH_LNORM(fill_compatible_stats_md(*src_md(), reordered_stat_md_)
== status::success,
VERBOSE_INCONSISTENT_MDS, "src", "stat");
if (reordered_stat_md_ != *stat_md() && !stats_are_tmp()) {
CHECK(reorder_primitive_desc_create(reorder_pd_, engine,
stats_are_src() ? stat_md() : &reordered_stat_md_,
stats_are_src() ? &reordered_stat_md_ : stat_md()));
}
nthr_ = dnnl_get_max_threads();
init_scratchpad();
return status::success;
}
status_t jit_uni_layer_normalization_fwd_t::execute_forward(
const exec_ctx_t &ctx) const {
const auto &scratchpad = ctx.get_scratchpad_grantor();
const auto src = CTX_IN_MEM(const void *, DNNL_ARG_SRC);
auto dst = CTX_OUT_MEM(void *, DNNL_ARG_DST);
auto scale = CTX_IN_MEM(const float *, DNNL_ARG_SCALE);
auto shift = CTX_IN_MEM(const float *, DNNL_ARG_SHIFT);
bool skip_mean = pd()->skip_mean();
float *mean, *variance;
if (pd()->use_tmp_stats()) {
mean = skip_mean ? nullptr
: scratchpad.template get<float>(key_lnorm_tmp_mean);
variance = scratchpad.template get<float>(key_lnorm_tmp_var);
} else {
mean = pd()->stats_are_src()
? const_cast<float *>(CTX_IN_MEM(const float *, DNNL_ARG_MEAN))
: CTX_OUT_MEM(float *, DNNL_ARG_MEAN);
variance = pd()->stats_are_src()
? const_cast<float *>(
CTX_IN_MEM(const float *, DNNL_ARG_VARIANCE))
: CTX_OUT_MEM(float *, DNNL_ARG_VARIANCE);
}
const void *src_scales
= CTX_IN_MEM(const void *, DNNL_ARG_ATTR_SCALES | DNNL_ARG_SRC);
const void *dst_scales
= CTX_IN_MEM(const void *, DNNL_ARG_ATTR_SCALES | DNNL_ARG_DST);
const auto post_ops_binary_rhs_arg_vec
= binary_injector::prepare_binary_args(
pd()->attr()->post_ops_, ctx);
const memory_desc_wrapper src_d(pd()->src_md());
const memory_desc_wrapper dst_d(pd()->dst_md());
const dim_t N = pd()->across_axis();
const dim_t C_padded = src_d.padded_dims()[pd()->ndims() - 1];
parallel(pd()->nthr_,
[= COMPAT_THIS_CAPTURE](const int ithr, const int nthr) {
dim_t N_start = 0, N_end = 0;
balance211(N, nthr, ithr, N_start, N_end);
const char *const __restrict src_ptr
= reinterpret_cast<const char *>(src)
+ N_start * C_padded * src_d.data_type_size();
char *const __restrict dst_ptr = reinterpret_cast<char *>(dst)
+ N_start * C_padded * dst_d.data_type_size();
const int block_size = N_end - N_start;
float *mean_ptr = skip_mean ? nullptr : &mean[N_start];
float *dst_scales_inv_ptr = nullptr;
if (!pd()->attr()->scales_.has_default_values(DNNL_ARG_DST)) {
const float *dst_scales_ptr
= static_cast<const float *>(dst_scales);
dst_scales_inv_ptr
= scratchpad.template get<float>(key_lnorm_dst_scales)
+ ithr;
dst_scales_inv_ptr[0] = 1.f / dst_scales_ptr[0];
}
(*stat_and_data_kernel_)(src_ptr, dst_ptr, scale, shift, mean_ptr,
&variance[N_start], src_scales, dst_scales_inv_ptr,
post_ops_binary_rhs_arg_vec.data(), block_size);
});
return status::success;
}
status_t jit_uni_layer_normalization_bwd_t::execute_backward(
const exec_ctx_t &ctx) const {
status_t status = status::success;
const auto &scratchpad = ctx.get_scratchpad_grantor();
auto src = CTX_IN_MEM(const void *, DNNL_ARG_SRC);
auto diff_dst = CTX_IN_MEM(const void *, DNNL_ARG_DIFF_DST);
auto scale = CTX_IN_MEM(float *, DNNL_ARG_SCALE);
auto diff_src = CTX_OUT_CLEAN_MEM(void *, DNNL_ARG_DIFF_SRC, status);
auto diff_scale = CTX_OUT_CLEAN_MEM(float *, DNNL_ARG_DIFF_SCALE, status);
CHECK(status);
auto diff_shift = CTX_OUT_CLEAN_MEM(float *, DNNL_ARG_DIFF_SHIFT, status);
CHECK(status);
bool skip_mean = pd()->skip_mean();
const float *mean, *variance;
if (pd()->use_tmp_stats()) {
mean = skip_mean ? nullptr
: scratchpad.template get<float>(key_lnorm_tmp_mean);
variance = scratchpad.template get<float>(key_lnorm_tmp_var);
} else {
mean = CTX_IN_MEM(const float *, DNNL_ARG_MEAN);
variance = CTX_IN_MEM(const float *, DNNL_ARG_VARIANCE);
}
float *const inv_sqrtvar
= scratchpad.template get<float>(key_lnorm_inv_sqrtvar);
const memory_desc_wrapper src_d(pd()->src_md());
const memory_desc_wrapper diff_dst_d(pd()->diff_dst_md());
const memory_desc_wrapper diff_src_d(pd()->diff_src_md());
const dim_t N = pd()->across_axis();
const dim_t C = pd()->norm_axis();
const dim_t C_padded = src_d.padded_dims()[pd()->ndims() - 1];
float *reduce = scratchpad.template get<float>(key_lnorm_reduction);
if (diff_scale == nullptr)
diff_scale = scratchpad.template get<float>(key_lnorm_tmp_diff_ss);
if (diff_shift == nullptr) {
diff_shift = scratchpad.template get<float>(key_lnorm_tmp_diff_ss);
}
const int max_nthr = pd()->nthr_;
parallel(max_nthr, [= COMPAT_THIS_CAPTURE](int ithr, int nthr) {
dim_t N_start = 0, N_end = 0;
balance211(N, nthr, ithr, N_start, N_end);
const int block_size = N_end - N_start;
const char *const __restrict src_ptr
= reinterpret_cast<const char *>(src)
+ N_start * C_padded * src_d.data_type_size();
const char *const __restrict diff_dst_ptr
= reinterpret_cast<const char *>(diff_dst)
+ N_start * C_padded * diff_dst_d.data_type_size();
float *my_diff_gamma = reduce + C * ithr;
float *my_diff_beta = reduce + C * nthr + C * ithr;
for (dim_t c = 0; c < C; c++) {
my_diff_gamma[c] = 0.;
my_diff_beta[c] = 0.;
}
const float *mean_ptr = skip_mean ? nullptr : &mean[N_start];
(*diff_ss_kernel_)(src_ptr, diff_dst_ptr, my_diff_gamma, my_diff_beta,
mean_ptr, &variance[N_start], &inv_sqrtvar[N_start],
block_size);
});
parallel_nd(C, [=](dim_t c) {
float diff_gamma = 0, diff_beta = 0;
for (dim_t n = 0; n < max_nthr; n++) {
diff_gamma += reduce[C * n + c];
diff_beta += reduce[C * max_nthr + C * n + c];
}
diff_scale[c] = diff_gamma;
diff_shift[c] = diff_beta;
});
parallel(max_nthr, [= COMPAT_THIS_CAPTURE](int ithr, int nthr) {
dim_t N_start = 0, N_end = 0;
balance211(N, nthr, ithr, N_start, N_end);
const int block_size = N_end - N_start;
const char *const __restrict src_ptr
= reinterpret_cast<const char *>(src)
+ N_start * C_padded * src_d.data_type_size();
const char *const __restrict diff_dst_ptr
= reinterpret_cast<const char *>(diff_dst)
+ N_start * C_padded * diff_dst_d.data_type_size();
char *const __restrict diff_src_ptr = reinterpret_cast<char *>(diff_src)
+ N_start * C_padded * diff_src_d.data_type_size();
const float *mean_ptr = skip_mean ? nullptr : &mean[N_start];
(*diff_data_kernel_)(src_ptr, diff_dst_ptr, diff_src_ptr, scale,
mean_ptr, &inv_sqrtvar[N_start], block_size);
});
return status::success;
}
} } } }