#ifndef GPU_INTEL_COMPUTE_KERNEL_ARG_LIST_HPP
#define GPU_INTEL_COMPUTE_KERNEL_ARG_LIST_HPP
#include <cassert>
#include <cstddef>
#include <string>
#include <type_traits>
#include "common/bfloat16.hpp"
#include "common/float16.hpp"
#include "common/host_scalar_memory_storage.hpp"
#include "common/memory_storage.hpp"
#include "common/nstl.hpp"
namespace dnnl {
namespace impl {
namespace gpu {
namespace intel {
namespace compute {
enum class kernel_arg_kind_t {
undef,
global,
local,
scalar,
};
enum class scalar_type_t {
undef,
_char,
_f4_e2m1,
_f4_e3m0,
_hfloat8,
_bfloat8,
_bfloat16,
_float,
_double,
_half,
_int,
_int4,
_long,
_short,
_uchar,
_uint,
_uint4,
_ulong,
_ushort,
_zero_pad_mask_t,
_int64x2_t,
_int64x3_t,
_int64x4_t,
_int64x5_t,
_int64x6_t,
_dispatch_gws_rt_params_t,
};
inline std::string to_string(scalar_type_t type) {
#define CASE(x) \
case scalar_type_t::x: return #x
switch (type) {
CASE(undef);
CASE(_char);
CASE(_f4_e2m1);
CASE(_f4_e3m0);
CASE(_hfloat8);
CASE(_bfloat8);
CASE(_bfloat16);
CASE(_float);
CASE(_double);
CASE(_half);
CASE(_int);
CASE(_int4);
CASE(_long);
CASE(_short);
CASE(_uchar);
CASE(_uint);
CASE(_uint4);
CASE(_ulong);
CASE(_ushort);
CASE(_zero_pad_mask_t);
CASE(_int64x2_t);
CASE(_int64x3_t);
CASE(_int64x4_t);
CASE(_int64x5_t);
CASE(_int64x6_t);
CASE(_dispatch_gws_rt_params_t);
}
return "unexpected";
#undef CASE
}
template <typename T>
struct scalar_type_traits_t {};
template <>
struct scalar_type_traits_t<float8_e8m0_t> {
static const auto type = scalar_type_t::_uchar;
};
template <>
struct scalar_type_traits_t<float8_e4m3_t> {
static const auto type = scalar_type_t::_hfloat8;
};
template <>
struct scalar_type_traits_t<float8_e5m2_t> {
static const auto type = scalar_type_t::_bfloat8;
};
template <>
struct scalar_type_traits_t<float16_t> {
static const auto type = scalar_type_t::_half;
};
template <>
struct scalar_type_traits_t<bfloat16_t> {
static const auto type = scalar_type_t::_bfloat16;
};
template <>
struct scalar_type_traits_t<float> {
static const auto type = scalar_type_t::_float;
};
template <>
struct scalar_type_traits_t<double> {
static const auto type = scalar_type_t::_double;
};
template <>
struct scalar_type_traits_t<uint8_t> {
static const auto type = scalar_type_t::_uchar;
};
template <>
struct scalar_type_traits_t<uint16_t> {
static const auto type = scalar_type_t::_ushort;
};
template <>
struct scalar_type_traits_t<uint32_t> {
static const auto type = scalar_type_t::_uint;
};
template <>
struct scalar_type_traits_t<uint64_t> {
static const auto type = scalar_type_t::_ulong;
};
template <>
struct scalar_type_traits_t<int8_t> {
static const auto type = scalar_type_t::_char;
};
template <>
struct scalar_type_traits_t<int16_t> {
static const auto type = scalar_type_t::_short;
};
template <>
struct scalar_type_traits_t<int32_t> {
static const auto type = scalar_type_t::_int;
};
template <>
struct scalar_type_traits_t<int64_t> {
static const auto type = scalar_type_t::_long;
};
class kernel_arg_t {
public:
kernel_arg_kind_t kind() const { return kind_; }
scalar_type_t scalar_type() const { return scalar_type_; }
size_t size() const { return size_; }
bool is_global() const { return kind() == kernel_arg_kind_t::global; }
bool is_local() const { return kind() == kernel_arg_kind_t::local; }
kernel_arg_t &set_value(const memory_storage_t &storage) {
kind_ = kernel_arg_kind_t::global;
size_ = 0;
value_ = static_cast<const void *>(&storage);
return *this;
}
template <typename T>
kernel_arg_t &set_value(const T &value, void *&data_pool) {
assert(size_ <= sizeof(T));
if (value_ == nullptr) {
assert(data_pool != nullptr);
size_ = sizeof(T);
data_pool = utils::align_ptr(data_pool, alignof(T));
value_ = data_pool;
data_pool = static_cast<char *>(data_pool) + size_;
}
kind_ = kernel_arg_kind_t::scalar;
scalar_type_ = scalar_type_traits_t<T>::type;
new (const_cast<void *>(value_)) T(value);
return *this;
}
kernel_arg_t &set_value(size_t size, std::nullptr_t) {
kind_ = kernel_arg_kind_t::local;
size_ = size;
value_ = nullptr;
return *this;
}
const void *value() const {
assert(kind() != kernel_arg_kind_t::undef);
return value_;
}
template <typename T>
T as() const {
assert(kind() == kernel_arg_kind_t::scalar);
assert(scalar_type() == scalar_type_traits_t<T>::type);
return *(const T *)value();
}
private:
kernel_arg_kind_t kind_ = kernel_arg_kind_t::undef;
scalar_type_t scalar_type_ = scalar_type_t::undef;
size_t size_ = 0;
const void *value_ = nullptr;
};
class kernel_arg_list_t {
public:
kernel_arg_list_t() { args_.reserve(512); }
~kernel_arg_list_t() = default;
#define APPEND_STORED_SCALAR_VALUE(stype, vtype) \
case data_type::stype: { \
vtype value = 0; \
status_t status \
= host_storage->get_scalar_value(&value, sizeof(value)); \
assert(status == status::success); \
if (status != status::success) return; \
append(value); \
break; \
}
void append(const memory_storage_t &storage) {
if (!storage.is_host_scalar()) {
args_.emplace_back();
args_.back().set_value(storage);
return;
}
auto *host_storage
= utils::downcast<const host_scalar_memory_storage_t *>(
&storage);
switch ((int)host_storage->data_type()) {
APPEND_STORED_SCALAR_VALUE(f16, float16_t)
APPEND_STORED_SCALAR_VALUE(bf16, bfloat16_t)
APPEND_STORED_SCALAR_VALUE(f32, float)
APPEND_STORED_SCALAR_VALUE(s32, int32_t)
APPEND_STORED_SCALAR_VALUE(s64, int64_t)
APPEND_STORED_SCALAR_VALUE(s8, int8_t)
APPEND_STORED_SCALAR_VALUE(u8, uint8_t)
default:
assert(!"Support for requested data type is missing for "
"host-side scalars");
}
}
#undef APPEND_STORED_SCALAR_VALUE
template <class T>
void append(const T &value) {
args_.emplace_back();
args_.back().set_value(value, unused_storage);
assert(unused_storage
<= reinterpret_cast<char *>(&scalar_storage_) + storage_size);
}
void append(size_t size, std::nullptr_t) {
args_.emplace_back();
args_.back().set_value(size, nullptr);
}
#define SET_STORED_SCALAR_VALUE(stype, vtype) \
case data_type::stype: { \
vtype value = 0; \
status_t status \
= host_storage->get_scalar_value(&value, sizeof(value)); \
assert(status == status::success); \
if (status != status::success) return; \
set(index, value); \
break; \
}
void set(int index, const memory_storage_t &storage) {
if (!storage.is_host_scalar()) {
assert(index < storage_size);
if ((index + 1) > nargs()) { args_.resize(index + 1); };
args_[index].set_value(storage);
return;
}
auto *host_storage
= utils::downcast<const host_scalar_memory_storage_t *>(
&storage);
switch ((int)host_storage->data_type()) {
SET_STORED_SCALAR_VALUE(f8_e4m3, float8_e4m3_t)
SET_STORED_SCALAR_VALUE(f8_e5m2, float8_e5m2_t)
SET_STORED_SCALAR_VALUE(e8m0, float8_e8m0_t)
SET_STORED_SCALAR_VALUE(f16, float16_t)
SET_STORED_SCALAR_VALUE(bf16, bfloat16_t)
SET_STORED_SCALAR_VALUE(f32, float)
SET_STORED_SCALAR_VALUE(s32, int32_t)
SET_STORED_SCALAR_VALUE(s64, int64_t)
SET_STORED_SCALAR_VALUE(s8, int8_t)
SET_STORED_SCALAR_VALUE(u8, uint8_t)
default:
assert(!"Support for requested data type is missing for "
"host-side scalars");
}
}
#undef SET_STORED_SCALAR_VALUE
template <class T>
void set(int index, const T &value) {
assert(index < storage_size);
if ((index + 1) > nargs()) { args_.resize(index + 1); };
args_[index].set_value(value, unused_storage);
assert(unused_storage
<= reinterpret_cast<char *>(&scalar_storage_) + storage_size);
}
void set(int index, size_t size, std::nullptr_t) {
assert(index < storage_size);
if ((index + 1) > nargs()) { args_.resize(index + 1); };
args_[index].set_value(size, nullptr);
}
int nargs() const { return static_cast<int>(args_.size()); }
const kernel_arg_t &get(int index) const {
assert(index < nargs());
return args_[index];
}
const memory_storage_t &get_memory_storage(int index) const {
assert(args_[index].kind() == kernel_arg_kind_t::global);
return *static_cast<const memory_storage_t *>(args_[index].value());
}
private:
static constexpr int storage_size = 2048;
static constexpr int storage_alignment = 8;
std::vector<kernel_arg_t> args_;
typename std::aligned_storage<storage_size, storage_alignment>::type
scalar_storage_;
void *unused_storage = &scalar_storage_;
kernel_arg_list_t(const kernel_arg_list_t &) = delete;
kernel_arg_list_t(kernel_arg_list_t &&) = delete;
kernel_arg_list_t &operator=(const kernel_arg_list_t &) = delete;
kernel_arg_list_t &operator=(kernel_arg_list_t &&) = delete;
};
template <typename T>
void set_scalar_arg_cvt(kernel_arg_list_t &arg_list, int index, T scalar,
scalar_type_t requested_type) {
if (scalar_type_traits_t<T>::type == requested_type) {
arg_list.set(index, scalar);
return;
}
switch (requested_type) {
case scalar_type_t::_half:
arg_list.set(index, (float16_t)scalar);
break;
case scalar_type_t::_double: arg_list.set(index, (double)scalar); break;
case scalar_type_t::_uchar: arg_list.set(index, (uint8_t)scalar); break;
case scalar_type_t::_char: arg_list.set(index, (int8_t)scalar); break;
default: assert(!"Cannot convert scalar to the requested type.");
}
}
} } } } }
#endif