#ifndef GEMMSTONE_DSL_IR_CODEGEN_SEND_HPP
#define GEMMSTONE_DSL_IR_CODEGEN_SEND_HPP
#include "dsl/ir/codegen/kernel.hpp"
#include "dsl/ir/codegen/register_scope.hpp"
#include "dsl/ir/send.hpp"
#include "ngen.hpp"
GEMMSTONE_NAMESPACE_START
namespace dsl {
namespace ir {
inline ngen::CacheSettingsLSC get_cache_settings(
const send_t &send, const hw_t &hw) {
auto ret = ngen::CacheSettingsLSC::Default;
bool is_load = send.is_load() || send.is_load_2d();
bool is_store = send.is_store() || send.is_store_2d();
bool is_prefetch = send.is_prefetch() || send.is_prefetch_2d();
switch (send.cache_hint) {
case send_cache_hint_t::undef:
switch (send.hw.ngen_hw()) {
case ngen::HW::XeHPG:
if (is_store && hw.systolic_support())
ret = ngen::CacheSettingsLSC::L1WB_L3WB;
break;
case ngen::HW::XeHPC:
if (is_store) {
ret = ngen::CacheSettingsLSC::L1UC_L3WB;
} else if (is_load || is_prefetch) {
ret = ngen::CacheSettingsLSC::L1C_L3C;
}
break;
case ngen::HW::XE3P_35_10:
case ngen::HW::XE3P_35_11:
case ngen::HW::XE3P_UNKNOWN:
if (is_store) {
ret = ngen::CacheSettingsLSC::L1UC_L2UC_L3WB;
} else if (is_load || is_prefetch) {
ret = ngen::CacheSettingsLSC::L1C_L2C_L3C;
}
break;
default: break;
}
break;
case send_cache_hint_t::load_once:
ret = ngen::CacheSettingsLSC::L1C_L3C;
break;
case send_cache_hint_t::hw_default:
ret = ngen::CacheSettingsLSC::Default;
break;
}
return ret;
}
template <typename DataSpecT, typename = void>
struct atomic_helper_t {
template <typename GeneratorT>
static void call(GeneratorT *, ngen::AtomicOp,
const ngen::InstructionModifier &, const DataSpecT &,
ngen::AddressBase, const ngen::RegData &, const ngen::RegData &) {
dsl_error() << "Unknown DataSpec: atomics are not supported.";
}
};
template <typename DataSpecT>
struct atomic_helper_t<DataSpecT,
typename std::enable_if<
std::is_same<DataSpecT, ngen::scattered_qword>::value
|| std::is_same<DataSpecT,
ngen::scattered_dword>::value>::type> {
template <typename GeneratorT>
static void call(GeneratorT *host, ngen::AtomicOp atomic_op,
const ngen::InstructionModifier &mod, const DataSpecT &spec,
ngen::AddressBase base, const ngen::RegData &addr,
const ngen::RegData &data) {
host->atomic(atomic_op, mod, spec, base, addr, data);
}
template <typename GeneratorT>
static void call(GeneratorT *host, ngen::AtomicOp atomic_op,
const ngen::InstructionModifier &mod, const ngen::RegData &dst,
const DataSpecT &spec, ngen::AddressBase base,
const ngen::RegData &addr, const ngen::RegData &data) {
host->atomic(atomic_op, mod, dst, spec, base, addr, data);
}
};
class send_impl_t {
public:
send_impl_t(const send_t &send) : send_(send) {}
template <typename GeneratorT, typename T>
void emit(GeneratorT *host, ngen_register_scope_t &scope,
const ngen::InstructionModifier &mod, const ngen::RegData &header,
const T &data) {
if (send_.is_2d()) {
emit_2d(host, mod, data, header);
return;
}
if (send_.is_lsc) {
emit_lsc(host, mod, data, header);
return;
}
auto address_base = to_address_base(send_.address);
int elems = send_.type.elems();
auto &t = send_.type;
if (t.is_byte())
emit_load_or_store(host, mod, ngen::scattered_byte(elems),
address_base, header, data);
else if (t.is_dword())
emit_load_or_store(host, mod, ngen::scattered_dword(elems),
address_base, header, data);
else if (t.is_qword())
emit_load_or_store(host, mod, ngen::scattered_qword(elems),
address_base, header, data);
else if (t.is_oword())
emit_load_or_store(host, mod, ngen::block_oword(elems),
address_base, header, data);
else if (t.is_hword())
emit_load_or_store(host, mod, ngen::block_hword(elems),
address_base, header, data);
else
dsl_error() << t;
}
template <typename GeneratorT, typename T>
void emit(GeneratorT *host, ngen_register_scope_t &scope,
const ngen::InstructionModifier &mod, const T &dst,
const ngen::RegData &header, const T &data) {
if (send_.type.is_dword())
emit_atomic_cmpwr(host, mod, dst,
ngen::scattered_dword(send_.type.elems()),
to_address_base(send_.address), header, data);
else if (send_.type.is_qword())
emit_atomic_cmpwr(host, mod, dst,
ngen::scattered_qword(send_.type.elems()),
to_address_base(send_.address), header, data);
else
dsl_error() << send_.type;
}
private:
template <typename GeneratorT, typename DataSpecT>
void emit_load_or_store(GeneratorT *host,
const ngen::InstructionModifier &mod, const DataSpecT &spec,
ngen::AddressBase base, const ngen::RegData &addr,
const ngen::RegData &data) {
if (send_.is_load()) {
host->load(mod, data, spec, base, addr);
} else if (send_.is_atomic()) {
atomic_helper_t<DataSpecT>::call(
host, to_atomic_op(send_.op), mod, spec, base, addr, data);
} else if (send_.is_store()) {
host->store(mod, spec, base, addr, data);
} else {
dsl_error() << "Can't emit send: " << send_;
}
}
template <typename GeneratorT, typename DataSpecT>
void emit_atomic_cmpwr(GeneratorT *host,
const ngen::InstructionModifier &mod, const ngen::RegData &dst,
const DataSpecT &spec, ngen::AddressBase base,
const ngen::RegData &addr, const ngen::RegData &data) {
atomic_helper_t<DataSpecT>::call(
host, ngen::AtomicOp::cmpwr, mod, dst, spec, base, addr, data);
}
template <typename GeneratorT>
void emit_lsc(GeneratorT *host, const ngen::InstructionModifier &mod,
const ngen::RegData &data, const ngen::RegData &header) {
auto get_lsc_type = [&](const type_t &type, bool is_block) {
if (!send_.is_block()) return type;
for (auto &t : {type_t::qword(), type_t::dword()}) {
if (type.size() % t.size() == 0) {
int elems = type.size() / t.size();
dsl_assert(is_pow2(elems));
dsl_assert(elems >= 1 && elems <= 64);
return t.with_elems(elems);
}
}
stub();
return type;
};
std::unique_ptr<ngen::DataSpecLSC> lsc_spec;
auto lsc_type = to_data_lsc(get_lsc_type(send_.type, send_.is_block()));
if (send_.is_scattered()) {
lsc_spec = make_unique<ngen::DataSpecLSC>(
ngen::scattered(lsc_type.first, lsc_type.second));
} else if (send_.is_block()) {
lsc_spec = make_unique<ngen::DataSpecLSC>(
ngen::block(lsc_type.first, lsc_type.second));
} else {
stub();
}
if (send_.is_slm()) {
if (send_.is_load()) {
host->load.slm(mod, data, *lsc_spec, host->SLM, header);
} else if (send_.is_store()) {
host->store.slm(mod, *lsc_spec, host->SLM, header, data);
} else {
stub();
}
} else if (send_.is_a64()) {
*lsc_spec |= get_cache_settings(send_, host->hw_info());
if (send_.is_load() || send_.is_prefetch()) {
if (host->hw_info() >= ngen::HW::XE3P_35_10)
*lsc_spec |= ngen::DataSpecLSC::createOverfetch();
host->load.ugm(mod, data, *lsc_spec, host->A64, header);
} else if (send_.is_store()) {
host->store.ugm(mod, *lsc_spec, host->A64, header, data);
} else if (send_.is_atomic()) {
host->atomic.ugm(to_atomic_op(send_.op), mod, *lsc_spec,
to_address_base(send_.address), header, data);
}
} else {
stub();
}
}
template <typename GeneratorT>
void emit_2d(GeneratorT *host, const ngen::InstructionModifier &mod,
const ngen::RegData &data, const ngen::RegData &header) {
auto &info = send_.block_2d_info;
ngen::DataSizeLSC data_size = ngen::DataSizeLSC::D8;
switch (send_.type.size()) {
case 1: data_size = ngen::DataSizeLSC::D8; break;
case 2: data_size = ngen::DataSizeLSC::D16; break;
case 4: data_size = ngen::DataSizeLSC::D32; break;
default: stub();
}
ngen::DataSpecLSC data_spec(data_size);
if (info.vnni) data_spec |= host->vnni;
if (info.transpose) data_spec |= host->transpose;
ngen::block_2d spec(data_spec, info.width, info.height, info.count);
spec |= get_cache_settings(send_, host->hw_info());
if (send_.is_load_2d() || send_.is_prefetch_2d()) {
host->load(mod, data, spec, host->A64, header);
} else if (send_.is_store_2d()) {
host->store(mod, spec, host->A64, header, data);
} else {
stub();
}
}
static std::pair<ngen::DataSizeLSC, int> to_data_lsc(const type_t &type) {
switch (type.base().size()) {
case 1: {
if (type.elems() == 1)
return std::make_pair(ngen::DataSizeLSC::D8U32, 1);
if (type.elems() == 2)
return std::make_pair(ngen::DataSizeLSC::D16U32, 1);
if (type.elems() == 4)
return std::make_pair(ngen::DataSizeLSC::D32, 1);
if (type.elems() == 8)
return std::make_pair(ngen::DataSizeLSC::D64, 1);
break;
}
case 2: {
if (type.elems() == 1)
return std::make_pair(ngen::DataSizeLSC::D16U32, 1);
if (type.elems() == 2)
return std::make_pair(ngen::DataSizeLSC::D32, 1);
if (type.elems() == 4)
return std::make_pair(ngen::DataSizeLSC::D64, 1);
break;
}
case 4: return std::make_pair(ngen::DataSizeLSC::D32, type.elems());
case 8: return std::make_pair(ngen::DataSizeLSC::D64, type.elems());
default: break;
}
stub();
return std::make_pair(ngen::DataSizeLSC::D8, 1);
}
static ngen::AddressBase to_address_base(send_address_t address) {
switch (address) {
case send_address_t::a64: return ngen::AddressBase::createA64(true);
case send_address_t::slm: return ngen::AddressBase::createSLM();
default: stub();
}
return ngen::AddressBase();
}
static ngen::AtomicOp to_atomic_op(send_op_t op) {
switch (op) {
case send_op_t::atomic_add: return ngen::AtomicOp::add;
case send_op_t::atomic_fadd: return ngen::AtomicOp::fadd;
case send_op_t::atomic_bfadd: return ngen::AtomicOp::bfadd;
case send_op_t::atomic_cmpwr: return ngen::AtomicOp::cmpwr;
default: stub();
}
return ngen::AtomicOp();
}
const send_t &send_;
};
} } GEMMSTONE_NAMESPACE_END
#endif