#ifndef GPU_INTEL_CONV_JIT_CONFIG_HPP
#define GPU_INTEL_CONV_JIT_CONFIG_HPP
#include <iostream>
#include "common/c_types_map.hpp"
#include "common/convolution_pd.hpp"
#include "gpu/intel/compute/utils.hpp"
#include "gpu/intel/conv/jit/key.hpp"
#include "gpu/intel/conv/jit/problem.hpp"
#include "gpu/intel/jit/ir/config.hpp"
#include "gpu/intel/jit/ir/hw.hpp"
#include "gpu/intel/jit/ir/legacy.hpp"
#include "gpu/intel/jit/ir/tensor_config.hpp"
#include "gpu/intel/jit/utils/utils.hpp"
namespace dnnl {
namespace impl {
namespace gpu {
namespace intel {
namespace conv {
namespace jit {
using namespace intel::jit;
class allow_global_reduction_param_t : public bool_param_t {
public:
allow_global_reduction_param_t() : bool_param_t(default_value) {}
std::string name() const override { return "global-reduction"; }
std::string desc() const override {
return "Whether global reduction via atomics is allowed.";
}
bool is_overridable() const override { return true; }
bool is_default() const override { return get() == default_value; }
static const bool default_value;
};
enum class bwd_d_optimize_kind_t {
undef,
none,
skip_out_of_bound_w,
skip_strided_dh,
skip_strided_dhw,
};
inline std::string to_string(bwd_d_optimize_kind_t kind) {
switch (kind) {
#define CASE(name) \
case bwd_d_optimize_kind_t::name: return #name
CASE(undef);
CASE(none);
CASE(skip_out_of_bound_w);
CASE(skip_strided_dh);
CASE(skip_strided_dhw);
#undef CASE
default: gpu_error_not_expected();
}
return "unknown";
}
inline bwd_d_optimize_kind_t to_bwd_d_optimize_kind(const std::string &s) {
#define CASE(name) \
if (s == #name) return bwd_d_optimize_kind_t::name
CASE(none);
CASE(skip_out_of_bound_w);
CASE(skip_strided_dh);
CASE(skip_strided_dhw);
#undef CASE
gpu_error_not_expected();
return bwd_d_optimize_kind_t::undef;
}
class bwd_d_optimize_kind_param_t
: public value_param_t<bwd_d_optimize_kind_t> {
public:
bwd_d_optimize_kind_param_t()
: value_param_t(bwd_d_optimize_kind_t::undef) {}
std::string name() const override { return "bwd-d-optimize"; }
std::string desc() const override {
return "Kind of special optimization for strided backward by data "
"convolution.";
}
bool is_undef() const override {
return get() == bwd_d_optimize_kind_t::undef;
}
bool is_overridable() const override { return true; }
bool is_default() const override { return get() == default_value; }
void set_from_str(const std::string &s) override {
value_ = to_bwd_d_optimize_kind(s);
}
std::string str() const override {
ostringstream_t oss;
oss << short_name() << "=" << to_string(value_);
return oss.str();
}
static const bwd_d_optimize_kind_t default_value;
};
class fma_kind_param_t : public value_param_t<fma_kind_t> {
public:
fma_kind_param_t() : value_param_t(fma_kind_t::undef) {}
std::string name() const override { return "fma"; }
std::string desc() const override { return "FMA kind."; }
bool is_overridable() const override { return true; }
bool is_default() const override { return false; }
void set_from_str(const std::string &s) override {
value_ = to_enum<fma_kind_t>(s);
}
std::string str() const override {
ostringstream_t oss;
oss << short_name() << "=" << to_string(value_);
return oss.str();
}
};
class pad_slm_param_t : public bool_param_t {
public:
pad_slm_param_t() : bool_param_t(default_value) {}
std::string name() const override { return "pad-slm"; }
std::string desc() const override {
return "Whether to pad SLM layout to avoid bank conflicts.";
}
bool is_overridable() const override { return true; }
bool is_default() const override { return get() == default_value; }
static const bool default_value;
};
class pipeline_param_t : public param_t {
public:
std::string name() const override { return "pipeline"; }
std::string short_name() const override { return "P"; }
std::string desc() const override { return "General pipeline parameters."; }
bool is_overridable() const override { return true; }
bool is_default() const override { return false; }
bool do_unroll() const { return do_unroll_; }
bool reuse_headers() const { return reuse_headers_; }
void set_from_str(const std::string &s) override {
do_unroll_ = false;
reuse_headers_ = false;
for (auto c : s) {
switch (c) {
case 'u': do_unroll_ = true; break;
case 'r': reuse_headers_ = true; break;
default: gpu_error_not_expected() << s;
}
}
}
void set(bool do_unroll, bool reuse_headers) {
do_unroll_ = do_unroll;
reuse_headers_ = reuse_headers;
}
std::string str() const override {
ostringstream_t oss;
oss << short_name() << "=";
if (do_unroll_) oss << "u";
if (reuse_headers_) oss << "r";
return oss.str();
}
private:
bool do_unroll_ = false;
bool reuse_headers_ = false;
};
class prb_param_t : public value_param_t<problem_t> {
public:
using value_param_t::value_param_t;
std::string name() const override { return "prb"; }
std::string desc() const override { return "Convolution problem."; }
bool is_overridable() const override { return false; }
void set_pd(const convolution_pd_t *pd) {
value_.conv_pd = pd;
value_.attr = pd->attr();
}
};
class prefetch_param_t : public param_t {
public:
std::string name() const override { return "prefetch"; }
std::string short_name() const override { return "p"; }
std::string desc() const override { return "Parameters for prefetching."; }
bool is_overridable() const override { return true; }
bool is_default() const override { return bufs_ == 0; }
int bufs() const { return bufs_; }
bool a() const { return a_; }
bool b() const { return b_; }
operator bool() const { return bufs_ > 0; }
void set_from_str(const std::string &s) override {
a_ = false;
b_ = false;
bool ab_set = false;
auto parts = gpu_utils::split(s, ".");
for (auto &p : parts) {
if (utils::one_of(p, "a", "b", "ab", "ba")) {
ab_set = true;
a_ = p.find("a") != std::string::npos;
b_ = p.find("b") != std::string::npos;
continue;
}
gpu_assert(p.size() >= 2) << p;
char name = p[0];
int value = std::stoi(p.substr(1));
switch (name) {
case 'x': bufs_ = value; break;
default: gpu_error_not_expected() << p;
}
}
if (!ab_set && bufs_ > 0) {
a_ = true;
b_ = true;
}
}
void set(int bufs, bool a, bool b) {
bufs_ = bufs;
a_ = a;
b_ = b;
}
std::string str() const override {
ostringstream_t oss;
oss << short_name() << "=";
oss << "x" << bufs_;
if (a_ != b_) oss << "." << (a_ ? "a" : "b");
return oss.str();
}
private:
int bufs_ = 0;
bool a_ = false;
bool b_ = false;
};
class slm_param_t : public param_t {
public:
std::string name() const override { return "slm"; }
std::string short_name() const override { return "s"; }
std::string desc() const override { return "SLM buffering parameters."; }
bool is_overridable() const override { return true; }
bool is_default() const override { return bufs_ == 0; }
int bufs() const { return bufs_; }
int gmem_bufs() const { return gmem_bufs_; }
int sync_version() const { return sync_version_; }
bool a() const { return a_; }
bool b() const { return b_; }
operator bool() const { return bufs() > 0; }
void set_from_str(const std::string &s) override {
a_ = false;
b_ = false;
bool ab_set = false;
auto parts = gpu_utils::split(s, ".");
for (auto &p : parts) {
if (utils::one_of(p, "a", "b", "ab", "ba")) {
ab_set = true;
a_ = p.find("a") != std::string::npos;
b_ = p.find("b") != std::string::npos;
continue;
}
gpu_assert(p.size() >= 2) << p;
char name = p[0];
int value = std::stoi(p.substr(1));
switch (name) {
case 'x': bufs_ = value; break;
case 'g': gmem_bufs_ = value; break;
case 'v': sync_version_ = value; break;
default: gpu_error_not_expected() << p;
}
}
if (!ab_set && bufs_ > 0) {
a_ = true;
b_ = true;
}
}
void set(int bufs, int gmem_bufs, bool a, bool b) {
bufs_ = bufs;
gmem_bufs_ = gmem_bufs;
a_ = a;
b_ = b;
}
void set_bufs(int bufs) { bufs_ = bufs; }
void set_gmem_bufs(int gmem_bufs) { gmem_bufs_ = gmem_bufs; }
std::string str() const override {
ostringstream_t oss;
oss << short_name() << "=";
oss << "x" << bufs_;
oss << ".g" << gmem_bufs_;
if (sync_version_ != -1) oss << ".v" << sync_version_;
if (a_ != b_) oss << "." << (a_ ? "a" : "b");
return oss.str();
}
private:
int bufs_ = 0;
int gmem_bufs_ = 0;
int sync_version_ = -1;
bool a_ = false;
bool b_ = false;
};
class subtiles_param_t : public param_t {
public:
std::string name() const override { return "subtiles"; }
std::string short_name() const override { return "S"; }
std::string desc() const override { return "Sub-iteration blocking."; }
bool is_overridable() const override { return true; }
bool is_default() const override { return (a_ == 1) && (b_ == 1); }
int a() const { return a_; }
int b() const { return b_; }
void set_from_str(const std::string &s) override {
a_ = 1;
b_ = 1;
for (auto &kv : ir_utils::to_string_int_pairs(s)) {
if (kv.first == "a") {
a_ = kv.second;
} else if (kv.first == "b") {
b_ = kv.second;
} else {
gpu_error_not_expected() << kv.first;
}
}
}
void set(int a, int b) {
a_ = a;
b_ = b;
}
void set_a(int a) { a_ = a; }
void set_b(int b) { b_ = b; }
std::string str() const override {
ostringstream_t oss;
oss << short_name() << "=";
if (a_ != 1) oss << "a" << a_;
if (b_ != 1) oss << "b" << b_;
return oss.str();
}
private:
int a_ = 1;
int b_ = 1;
};
class walk_order_param_t : public value_param_t<walk_order_t> {
public:
using value_param_t::value_param_t;
std::string name() const override { return "walk-order"; }
std::string desc() const override {
return "Kernel grid walk order (innermost -> outermost).";
}
bool is_overridable() const override { return true; }
bool is_default() const override { return false; }
void set_from_str(const std::string &s) override {
if (s.empty()) return;
value_ = make_walk_order(s);
}
std::string str() const override {
ostringstream_t oss;
oss << short_name() << "=" << value_;
return oss.str();
}
};
class wei_layout_param_t : public layout_param_t {
std::string name() const override { return "wei"; }
std::string desc() const override { return "Weights layout."; }
bool is_overridable() const override { return true; }
bool is_default() const override { return false; }
};
class bia_layout_param_t : public layout_param_t {
public:
std::string name() const override { return "bia"; }
std::string desc() const override { return "Bias layout."; }
bool is_overridable() const override { return true; }
bool is_default() const override { return false; }
};
namespace constants {
static const int max_slm_bufs = 3;
static const int reserved_regs_default = 16;
}
struct plan_t;
class tiler_t;
class config_t : public prim_config_t {
public:
#define DECL_PARAM(name) \
const name##_param_t &name##_param() const { \
gpu_assert(!name##_.is_undef()); \
(void)name##_init_; \
return name##_; \
} \
name##_param_t &name##_param() { return name##_; } \
const name##_param_t::value_t &name() const { \
gpu_assert(!name##_.is_undef()); \
return name##_.get(); \
} \
void set_##name(const name##_param_t::value_t &value) { \
name##_.set(value); \
}
#define DECL_PARAM2(name) \
const name##_param_t &name() const { \
(void)name##_init_; \
gpu_assert(!name##_.is_undef()); \
return name##_; \
} \
name##_param_t &name() { return name##_; }
DECL_PARAM(allow_global_reduction)
DECL_PARAM(bwd_d_optimize_kind)
DECL_PARAM(fma_kind)
DECL_PARAM(pad_slm)
DECL_PARAM(prb)
DECL_PARAM(walk_order)
DECL_PARAM2(pipeline)
DECL_PARAM2(prefetch)
DECL_PARAM2(slm)
DECL_PARAM2(subtiles)
DECL_PARAM2(unroll)
DECL_PARAM2(wei_layout)
DECL_PARAM2(bia_layout)
#undef DECL_PARAM
#undef DECL_PARAM2
std::string str() const override;
const std::vector<pvar_t> &index_dims() const override {
return jit::index_dims(prb().prop_kind());
}
tile_t shape(bool pad) const override;
std::string blocking_brief_str() const;
key_t key() const;
int pad_block(const pvar_t &d) const override;
int unroll(const pvar_t &d) const { return into<int>(unroll()(d)); }
int reserved_regs() const;
const dsl::hw_t &hw() const { return options().hw(); }
bool is_ge_xe_hpc() const { return hw() >= ngen::HW::XeHPC; }
int grf_size() const { return hw().grf_size(); }
int regs() const { return options().regs(); }
int simd() const { return options().simd(); }
int vec_size() const { return vec_size_; }
bool is_dp_fma() const { return jit::is_dp_fma(fma_kind()); }
bool is_dpas_or_dpasw_fma() const {
return utils::one_of(fma_kind(), fma_kind_t::dpas, fma_kind_t::dpasw);
}
const layout_param_t &a_layout() const {
return prb().pick_a<const layout_param_t &>(
src_layout(), wei_layout(), dst_layout());
}
const layout_param_t &b_layout() const {
return prb().pick_b<const layout_param_t &>(
src_layout(), wei_layout(), dst_layout());
}
compute::nd_range_t nd_range() const {
compute::range_t gws = compute::range_t::empty();
compute::range_t lws = compute::range_t::empty();
for (int i = 0; i < into<int>(compute::range_t::max_ndims); i++) {
lws[i] = thread_group_grid().dim(i) * (i == 0 ? simd() : 1);
gws[i] = kernel_grid().dim(i) * lws[i];
}
return compute::nd_range_t(gws, lws);
}
void set_pd(const pd_t *pd) { prb_.set_pd(pd); }
void set_regs(int regs) {
auto tmp = options();
tmp.set_regs(regs);
set_options(tmp);
}
void set_simd(int simd) {
auto tmp = options();
tmp.set_simd(simd);
set_options(tmp);
}
void set_require_dpas(bool value) {
auto tmp = options();
tmp.set_require_dpas(value);
set_options(tmp);
}
void set_vec_size(int vec_size) { vec_size_ = vec_size; }
void set_tiler(const std::shared_ptr<tiler_t> &tiler);
const tiler_t &tiler() const;
tiler_t &tiler();
void set_plan(const std::shared_ptr<plan_t> &plan);
const plan_t &plan() const;
bool can_skip_wei_zero_out() const;
bool can_skip_bia_zero_out() const;
blocking_params_t params(
int bufs_hint = blocking_params_t::bufs_hint_undef) const {
blocking_t blocking;
for (auto &d : index_dims()) {
dim_t loop = loop_dim(d);
dim_t tg = thread_group_dim(d);
dim_t iter = iter_dim(d);
if (loop != 1) blocking.set_loop(d, loop);
if (tg != 1) blocking.set_thread_group(d, tg);
if (iter != 1) blocking.set_iter(d, iter);
}
blocking.set_simd(vec_size());
blocking_params_t ret(blocking, bufs_hint);
ret.set_id(params_id_);
return ret;
}
private:
int vec_size_ = 0;
std::shared_ptr<plan_t> plan_;
std::shared_ptr<tiler_t> tiler_;
#define INIT_PARAM(name) \
name##_param_t name##_; \
param_init_t name##_init_ \
= register_param([](const container_config_t *c) { \
return &((const config_t *)c)->name##_; \
});
INIT_PARAM(allow_global_reduction)
INIT_PARAM(bwd_d_optimize_kind)
INIT_PARAM(fma_kind)
INIT_PARAM(pad_slm)
INIT_PARAM(prb)
INIT_PARAM(pipeline)
INIT_PARAM(prefetch)
INIT_PARAM(slm)
INIT_PARAM(subtiles)
INIT_PARAM(unroll)
INIT_PARAM(wei_layout)
INIT_PARAM(bia_layout)
INIT_PARAM(walk_order)
#undef INIT_PARAM
};
class bmnk_dim_helper_t {
public:
bmnk_dim_helper_t(const config_t &cfg) {
auto &prb = cfg.prb();
gemm_iter_ = to_gemm(cfg.iter_dims().get(), prb),
gemm_thread_group_ = to_gemm(cfg.thread_group_dims().get(), prb);
gemm_loop_ = to_gemm(cfg.loop_dims().get(), prb);
}
dim_t iter_dim(const pvar_t &d) const { return gemm_iter_.get(d, 1); }
dim_t thread_group_dim(const pvar_t &d) const {
return gemm_thread_group_.get(d, 1);
}
dim_t loop_dim(const pvar_t &d) const { return gemm_loop_.get(d, 1); }
private:
tile_t gemm_iter_;
tile_t gemm_thread_group_;
tile_t gemm_loop_;
};
status_t init_pd_time_cfg(const problem_t &prb, config_t &cfg,
impl::engine_t *engine, pd_t *pd, primitive_attr_t *attr);
status_t init_cfg(config_t &cfg, const primitive_t *prim);
void init_regs(config_t &cfg);
int slm_bufs_hint(const problem_t &prb, dim_t m_tg, dim_t n_tg,
bool do_src_zp_compensation, bool enable_a, bool enable_b,
bool do_unroll);
tensor_config_t get_tensor_config(
const config_t &cfg, const memory_desc_t *zp_src);
bool is_small(const dsl::type_t &type, dim_t elems);
int estimate_register_count(const config_t &cfg);
int default_regs(const config_t &cfg);
void init_kernel_grid(config_t &cfg);
void init_walk_order(config_t &cfg);
void init_thread_group_grid(config_t &cfg);
void prepare_zp_precompute(const problem_t &prb, dim_t *idhw, dim_t *odhw,
dim_t *pdhw, dim_t *ddhw);
std::array<tile_t, 3> get_kernel_grid_dims(const config_t &cfg);
std::array<tile_t, 3> get_thread_group_grid_dims(const config_t &cfg);
} } } } } }
#endif