#include "gemmstone/../../dsl/ir/codegen/extensions.hpp"
#include "gemmstone/../../dsl/ir/codegen/reorder.hpp"
#include "gpu/intel/jit/eltwise_injector.hpp"
#include "gpu/intel/jit/ir/post_ops.hpp"
namespace dnnl {
namespace impl {
namespace gpu {
namespace intel {
namespace jit {
template <typename ngen_generator_t>
void eltwise(ngen_generator_t &host, ir::ngen_register_scope_t &scope,
const dsl::hw_t &hw, const eltwise_t &func,
const std::vector<ir::ngen_operand_t> &args) {
int elems = to_cpp<int>(hw, eltwise_t::arg_elems(args));
auto &data_op = eltwise_t::arg_data(args);
const auto &data_rd = data_op.reg_buf_data();
eltwise_injector_f32_t<typename ngen_generator_t::RootCodeGenerator> inj(
&host, func.alg_kind, func.alpha, func.beta, func.scale);
auto scratch = scope.alloc_range(inj.preferred_scratch_regs());
inj.set_scratch(scratch);
inj.prepare();
int grf_size = ngen::GRF::bytes(hw);
int f_size = sizeof(float);
int step = 2 * grf_size / f_size;
auto do_eltwise = [&](const ir::reg_buf_data_t &r, const int count) {
if (func.alg_kind == alg_kind::eltwise_stochastic_round) {
gpu_assert(args.size() == 3);
const auto &seed = args[2].reg_buf_data();
inj.compute(ngen::GRFRange(r.base(), count),
seed.reg_data().getBase(), seed.reg_data().getOffset(),
func.dst_dt);
} else if (func.alg_kind == alg_kind::eltwise_mx_scale) {
gpu_assert(args.size() == 3);
const auto &scales_dst = args[2].reg_buf_data();
inj.compute(ngen::GRFRange(r.base(), count),
scales_dst.reg_data().getBase(),
scales_dst.reg_data().getOffset(), func.dst_dt);
} else {
inj.compute(ngen::GRFRange(r.base(), count));
}
};
for (int i = 0; i < elems; i += step) {
step = std::min(step, elems - i);
step = utils::rnd_down_pow2(step);
int cur_elems = step;
auto rd = data_rd.format(i, ngen::DataType::f);
if ((cur_elems * f_size) % grf_size != 0 || rd.byte_offset() != 0) {
auto &ra = scope.register_allocator();
int full_regs = utils::div_up(cur_elems * f_size, grf_size);
int full_elems = full_regs * (grf_size / f_size);
ir::reg_buf_data_t rbd(
ir::reg_buf_t(ra.hardware(), ra.alloc_range(full_regs)));
auto tmp = rbd.format(0, full_elems, 1, ngen::DataType::f);
emit_reorder_1d_tile(
&host, ra, hw.systolic_support(), cur_elems, rd, 1, tmp, 1);
do_eltwise(tmp, full_regs);
emit_reorder_1d_tile(
&host, ra, hw.systolic_support(), cur_elems, tmp, 1, rd, 1);
rbd.reg_buf().release(ra);
} else {
do_eltwise(rd, cur_elems * f_size / grf_size);
}
}
}
template <typename ngen_generator_t>
void handler(ngen_generator_t &host, const object_t &obj,
ir::codegen_extension_iface_t &ext_iface) {
auto &options = ext_iface.options();
if (obj.is<func_call_t>()) {
auto &call = obj.as<func_call_t>();
if (call.func.is<eltwise_t>()) {
ir::ngen_register_scope_t scope(ext_iface.allocator());
auto args = ext_iface.evaluate(call.args, scope);
eltwise(host, scope, options.hw(), call.func.as<eltwise_t>(), args);
return;
}
}
gpu_error_not_expected() << "Unknown object " << obj;
}
void extension_handler(
const object_t &obj, ir::codegen_extension_iface_t &ext_iface) {
auto host = ext_iface.root_code_generator();
#ifdef NGEN_ASM
if (host.info == typeid(ngen::AsmCodeGenerator))
return handler(*static_cast<ngen::AsmCodeGenerator *>(host.ptr), obj,
ext_iface);
#endif
#define GPU_HW_CASE(hw) \
return handler(*static_cast<ngen::BinaryCodeGenerator<(hw)> *>(host.ptr), \
obj, ext_iface);
GPU_HW_SWITCH(host.hw);
#undef GPU_HW_CASE
gpu_error_not_expected() << "Unknown nGEN code generator";
}
} } } } }
namespace gemmstone {
namespace dsl {
namespace kernel {
codegen_extension_handler_t default_extension_handler
= &dnnl::impl::gpu::intel::jit::extension_handler;
}
} }