#ifndef GPU_AMD_MIOPEN_CONVOLUTION_HPP
#define GPU_AMD_MIOPEN_CONVOLUTION_HPP
#include "common/c_types_map.hpp"
#include "common/primitive_desc.hpp"
#include "gpu/amd/engine.hpp"
#include "gpu/amd/miopen_convolution_impl.hpp"
#include "gpu/amd/miopen_convolution_pd.hpp"
#include "gpu/amd/sycl_hip_utils.hpp"
#include "gpu/gpu_primitive.hpp"
#include "xpu/sycl/memory_storage.hpp"
#include <miopen/miopen.h>
namespace dnnl {
namespace impl {
namespace gpu {
namespace amd {
struct miopen_convolution_fwd_t : public gpu::primitive_t {
using gpu::primitive_t::primitive_t;
struct pd_t : public miopen_convolution_fwd_pd_t {
using miopen_convolution_fwd_pd_t::miopen_convolution_fwd_pd_t;
pd_t(const pd_t &other)
: miopen_convolution_fwd_pd_t(other)
, impl_(other.impl_)
, dst_md_temp_(other.dst_md_temp_) {}
DECLARE_COMMON_PD_T("hip:miopen:any", miopen_convolution_fwd_t);
status_t init(impl::engine_t *engine) {
using namespace data_type;
using sm_t = primitive_attr_t::skip_mask_t;
const auto attr_skip_mask = sm_t::post_ops;
bool ok = utils::one_of(desc()->prop_kind,
prop_kind::forward_training, prop_kind::forward_inference);
ok = ok && attr()->has_default_values(attr_skip_mask);
ok = ok && attr_post_ops_ok(attr());
ok = ok
&& (utils::everyone_is(f32, src_md_.data_type,
weights_md_.data_type, dst_md_.data_type)
|| (utils::everyone_is(bf16, src_md_.data_type,
weights_md_.data_type, dst_md_.data_type))
|| (utils::everyone_is(f16, src_md_.data_type,
weights_md_.data_type, dst_md_.data_type))
|| (utils::everyone_is(s8, src_md_.data_type,
weights_md_.data_type, dst_md_.data_type)));
ok = ok
&& IMPLICATION(
utils::one_of(bf16, src_md_.data_type,
weights_md_.data_type, dst_md_.data_type),
!with_bias() && attr()->has_default_values());
ok = ok
&& (this->set_default_formats(src_md_, weights_md_, dst_md_,
&bias_md_, ndims())
== status::success);
ok = ok
&& IMPLICATION(
desc()->alg_kind == dnnl_convolution_winograd,
ndims() < 5 && src_md_.data_type != s8);
ok = ok
&& IMPLICATION(
src_md_.data_type == s8, check_s8_configuration())
&& IMPLICATION(with_bias(),
dst_md_.data_type == bias_md_.data_type);
ok = ok && memory_format_ok(&src_md_);
ok = ok && memory_format_ok(&weights_md_);
ok = ok && memory_format_ok(&dst_md_);
if (with_bias()) ok = ok && memory_format_ok(&bias_md_);
if (!ok) return status::unimplemented;
if (check_for_zero_dims()) return status::success;
if (!check_format()) return status::unimplemented;
const bool use_temp_dst = attr()->post_ops_.len() > 0;
if (use_temp_dst) {
dst_md_temp_ = dst_md_;
if (dst_md_.data_type == s8) { dst_md_temp_.data_type = f32; }
}
impl_.reset(new miopen_convolution_impl_fwd_t());
return impl_->init(engine, this, use_temp_dst);
}
bool check_format() const {
if (memory_desc_wrapper(src_md()).matches_one_of_tag(format_tag::a,
format_tag::ab, format_tag::abc, format_tag::abcd,
format_tag::abcde)
&& memory_desc_wrapper(weights_md())
.matches_one_of_tag(format_tag::a,
format_tag::ab, format_tag::abc,
format_tag::abcd, format_tag::abcde,
format_tag::oi, format_tag::oiw,
format_tag::oihw, format_tag::oidhw)
&& memory_desc_wrapper(dst_md()).matches_one_of_tag(
format_tag::a, format_tag::ab, format_tag::abc,
format_tag::abcd, format_tag::abcde)) {
return true;
}
else if (memory_desc_wrapper(src_md()).matches_one_of_tag(
format_tag::a, format_tag::ab, format_tag::acb,
format_tag::acdb, format_tag::acdeb)
&& memory_desc_wrapper(weights_md())
.matches_one_of_tag(format_tag::acb,
format_tag::acdb, format_tag::acdeb,
format_tag::owi, format_tag::ohwi,
format_tag::odhwi)
&& memory_desc_wrapper(dst_md()).matches_one_of_tag(
format_tag::a, format_tag::ab, format_tag::acb,
format_tag::acdb, format_tag::acdeb)) {
return true;
} else
return false;
}
bool with_scratchpad() const { return impl_->with_scratchpad(); }
std::shared_ptr<miopen_convolution_impl_base_t> impl_;
memory_desc_t dst_md_temp_;
bool use_temp_dst() const {
if (impl_.get()) return impl_->use_temp_dst();
return false;
}
private:
status_t set_default_formats(memory_desc_t &src_md,
memory_desc_t &weights_md, memory_desc_t &dst_md,
memory_desc_t *bias_md, int ndims) {
using namespace format_tag;
auto init_md
= [&](memory_desc_t &out_md, const memory_desc_t &in_md) {
format_tag_t md_tag;
if (memory_desc_matches_one_of_tag(in_md, ab, abc, abcd, abcde))
md_tag = utils::pick(ndims - 2, ab, abc, abcd, abcde);
else if (memory_desc_matches_one_of_tag(
in_md, acb, acdb, acdeb))
md_tag = utils::pick(ndims - 2, cba, cdba, cdeba);
else if (memory_desc_matches_one_of_tag(
in_md, ba, cba, cdba, cdeba))
md_tag = utils::pick(ndims - 2, ab, acb, acdb, acdeb);
else {
memory_desc_wrapper md_desc_wrapper(in_md);
return memory_desc_init_by_blocking_desc(
out_md, md_desc_wrapper.blocking_desc());
}
return memory_desc_init_by_tag(out_md, md_tag);
};
if (src_md.format_kind == format_kind::any
&& weights_md.format_kind == format_kind::any
&& dst_md.format_kind == format_kind::any) {
auto dat_tag = utils::pick(ndims - 3, ncw, nchw, ncdhw);
auto wei_tag = with_groups()
? utils::pick(ndims - 3, goiw, goihw, goidhw)
: utils::pick(ndims - 3, oiw, oihw, oidhw);
set_default_formats_common(dat_tag, wei_tag, dat_tag);
}
if (src_md.format_kind == format_kind::any
&& weights_md.format_kind == format_kind::any) {
CHECK(memory_desc_init_by_tag(
src_md, utils::pick(ndims - 2, nc, ncw, nchw, ncdhw)));
CHECK(memory_desc_init_by_tag(weights_md,
utils::pick(ndims - 2, oi, oiw, oihw, oidhw)));
} else if (src_md.format_kind == format_kind::any) {
CHECK(init_md(src_md, weights_md));
} else if (weights_md.format_kind == format_kind::any) {
CHECK(init_md(weights_md, src_md));
}
if (dst_md.format_kind == format_kind::any) {
CHECK(memory_desc_init_by_tag(dst_md, nc));
}
if (bias_md->format_kind == format_kind::any) {
CHECK(memory_desc_init_by_tag(*bias_md, x));
}
return status::success;
}
bool check_s8_configuration() const {
const auto check_nhwc
= [](const memory_desc_t &md, bool is_weights = false) {
miopenTensorLayout_t fmt;
get_format(&md, fmt, is_weights);
return fmt == miopenTensorNHWC;
};
return check_nhwc(src_md_) && check_nhwc(dst_md_)
&& check_nhwc(weights_md_, true)
&& (src_md_.dims[1] % 4) == 0 && (dst_md_.dims[1] % 4) == 0
&& ndims() < 5;
}
};
status_t init_temp_dst(impl::engine_t *engine) {
auto sycl_engine = utils::downcast<amd::engine_t *>(engine);
memory_storage_t *scratch_ptr = nullptr;
auto wrap = memory_desc_wrapper(pd()->dst_md_temp_);
CHECK(sycl_engine->create_memory_storage(
&scratch_ptr, memory_flags_t::alloc, wrap.size(), nullptr));
scratch_storage.reset(scratch_ptr);
CHECK(sycl_engine->create_memory_storage(
&scratch_ptr, memory_flags_t::alloc, wrap.size(), nullptr));
scratch_storage_2.reset(scratch_ptr);
return status::success;
}
virtual status_t init(impl::engine_t *engine) override {
const auto impl = pd()->impl_.get();
if (impl && impl->use_temp_dst()) { init_temp_dst(engine); }
return status::success;
}
status_t execute(const exec_ctx_t &ctx) const override {
if (pd()->check_for_zero_dims()) { return status::success; }
execute_convolution(ctx, pd()->with_bias(), pd()->with_scratchpad());
return status::success;
}
status_t execute_convolution(
const exec_ctx_t &ctx, bool with_bias, bool with_scratchpad) const;
private:
::sycl::buffer<uint8_t, 1> &buffer(memory_storage_t *mem_storage) const {
return utils::downcast<xpu::sycl::buffer_memory_storage_t *>(
mem_storage)
->buffer();
}
const pd_t *pd() const { return (const pd_t *)primitive_t::pd().get(); }
std::shared_ptr<memory_storage_t> scratch_storage;
std::shared_ptr<memory_storage_t> scratch_storage_2;
};
struct miopen_convolution_bwd_data_t : public gpu::primitive_t {
using gpu::primitive_t::primitive_t;
struct pd_t : public miopen_convolution_bwd_data_pd_t {
using miopen_convolution_bwd_data_pd_t::
miopen_convolution_bwd_data_pd_t;
DECLARE_COMMON_PD_T("hip:miopen:any", miopen_convolution_bwd_data_t);
status_t init(impl::engine_t *engine) {
using namespace data_type;
bool ok = desc()->prop_kind == prop_kind::backward_data;
ok = ok && this->set_default_formats();
ok = ok
&& (utils::everyone_is(f32, diff_src_md_.data_type,
weights_md_.data_type, diff_dst_md_.data_type)
|| utils::everyone_is(bf16, diff_src_md_.data_type,
weights_md_.data_type,
diff_dst_md_.data_type)
|| utils::everyone_is(f16, diff_src_md_.data_type,
weights_md_.data_type,
diff_dst_md_.data_type));
ok = ok
&& IMPLICATION(utils::one_of(bf16, diff_src_md_.data_type,
weights_md_.data_type,
diff_dst_md_.data_type),
!with_bias());
ok = ok
&& IMPLICATION(
desc()->alg_kind == dnnl_convolution_winograd,
ndims() < 5);
ok = ok && memory_format_ok(&diff_src_md_);
ok = ok && memory_format_ok(&weights_md_);
ok = ok && memory_format_ok(&diff_dst_md_);
if (with_bias()) {
ok = ok && memory_format_ok(&bias_md_);
ok = ok && bias_md_.data_type == diff_dst_md_.data_type;
}
if (!ok) return status::unimplemented;
if (check_for_zero_dims()) return status::success;
if (!check_format()) return status::unimplemented;
impl_.reset(new miopen_convolution_impl_bwd_data_t());
return impl_->init(engine, this);
}
bool check_format() const {
if (memory_desc_wrapper(diff_src_md())
.matches_one_of_tag(format_tag::a, format_tag::ab,
format_tag::abc, format_tag::abcd,
format_tag::abcde)
&& memory_desc_wrapper(weights_md())
.matches_one_of_tag(format_tag::a,
format_tag::ab, format_tag::abc,
format_tag::abcd, format_tag::abcde)
&& memory_desc_wrapper(diff_dst_md())
.matches_one_of_tag(format_tag::a,
format_tag::ab, format_tag::abc,
format_tag::abcd, format_tag::abcde)) {
return true;
}
else if (memory_desc_wrapper(diff_src_md())
.matches_one_of_tag(format_tag::a, format_tag::ab,
format_tag::acb, format_tag::acdb,
format_tag::acdeb)
&& memory_desc_wrapper(weights_md())
.matches_one_of_tag(format_tag::a,
format_tag::ab, format_tag::acb,
format_tag::acdb, format_tag::acdeb)
&& memory_desc_wrapper(diff_dst_md())
.matches_one_of_tag(format_tag::a,
format_tag::ab, format_tag::acb,
format_tag::acdb, format_tag::acdeb)) {
return true;
} else
return false;
}
std::shared_ptr<miopen_convolution_impl_base_t> impl_;
bool set_default_formats() {
using namespace format_tag;
auto dat_tag = utils::pick(ndims() - 3, ncw, nchw, ncdhw);
auto wei_tag = with_groups()
? utils::pick(ndims() - 3, goiw, goihw, goidhw)
: utils::pick(ndims() - 3, oiw, oihw, oidhw);
return set_default_formats_common(dat_tag, wei_tag, dat_tag);
}
bool with_scratchpad() const { return impl_->with_scratchpad(); }
bool support_bias() const override { return true; }
};
~miopen_convolution_bwd_data_t() {}
status_t execute(const exec_ctx_t &ctx) const override {
if (pd()->check_for_zero_dims()) { return status::success; }
return execute_convolution(
ctx, pd()->with_bias(), pd()->with_scratchpad());
}
status_t execute_convolution(
const exec_ctx_t &ctx, bool with_bias, bool with_scratchpad) const;
private:
const pd_t *pd() const { return (const pd_t *)primitive_t::pd().get(); }
};
struct miopen_convolution_bwd_weights_t : public gpu::primitive_t {
using gpu::primitive_t::primitive_t;
struct pd_t : public miopen_convolution_bwd_weights_pd_t {
using miopen_convolution_bwd_weights_pd_t::
miopen_convolution_bwd_weights_pd_t;
DECLARE_COMMON_PD_T("hip:miopen:any", miopen_convolution_bwd_weights_t);
status_t init(impl::engine_t *engine) {
using namespace data_type;
bool ok = desc()->prop_kind == prop_kind::backward_weights;
ok = ok && this->set_default_formats();
ok = ok
&& (utils::everyone_is(f32, src_md_.data_type,
diff_weights_md_.data_type,
diff_dst_md_.data_type)
|| utils::everyone_is(bf16, src_md_.data_type,
diff_weights_md_.data_type,
diff_dst_md_.data_type)
|| utils::everyone_is(f16, src_md_.data_type,
diff_weights_md_.data_type,
diff_dst_md_.data_type));
ok = ok
&& IMPLICATION(utils::one_of(bf16, src_md_.data_type,
diff_weights_md_.data_type,
diff_dst_md_.data_type),
!with_bias());
ok = ok
&& IMPLICATION(
desc()->alg_kind == dnnl_convolution_winograd,
ndims() < 5);
ok = ok && memory_format_ok(&src_md_);
ok = ok && memory_format_ok(&diff_weights_md_);
ok = ok && memory_format_ok(&diff_dst_md_);
if (with_bias()) {
ok = ok && memory_format_ok(&diff_bias_md_);
ok = ok && diff_bias_md_.data_type == diff_dst_md_.data_type;
}
if (!ok) return status::unimplemented;
impl_.reset(new miopen_convolution_impl_bwd_weights_t());
if (check_for_zero_dims()) { return impl_->init_zero_dims(this); };
if (!check_format()) return status::unimplemented;
return impl_->init(engine, this);
}
bool check_format() const {
if (memory_desc_wrapper(src_md()).matches_one_of_tag(format_tag::a,
format_tag::ab, format_tag::abc, format_tag::abcd,
format_tag::abcde)
&& memory_desc_wrapper(diff_weights_md())
.matches_one_of_tag(format_tag::a,
format_tag::ab, format_tag::abc,
format_tag::abcd, format_tag::abcde)
&& memory_desc_wrapper(diff_dst_md())
.matches_one_of_tag(format_tag::a,
format_tag::ab, format_tag::abc,
format_tag::abcd, format_tag::abcde)) {
return true;
}
else if (memory_desc_wrapper(src_md()).matches_one_of_tag(
format_tag::a, format_tag::ab, format_tag::acb,
format_tag::acdb, format_tag::acdeb)
&& memory_desc_wrapper(diff_weights_md())
.matches_one_of_tag(format_tag::a,
format_tag::ab, format_tag::acb,
format_tag::acdb, format_tag::acdeb)
&& memory_desc_wrapper(diff_dst_md())
.matches_one_of_tag(format_tag::a,
format_tag::ab, format_tag::acb,
format_tag::acdb, format_tag::acdeb)) {
return true;
} else
return false;
}
std::shared_ptr<miopen_convolution_impl_base_t> impl_;
bool set_default_formats() {
using namespace format_tag;
auto dat_tag = utils::pick(ndims() - 3, ncw, nchw, ncdhw);
auto wei_tag = with_groups()
? utils::pick(ndims() - 3, goiw, goihw, goidhw)
: utils::pick(ndims() - 3, oiw, oihw, oidhw);
return set_default_formats_common(dat_tag, wei_tag, dat_tag);
}
bool with_scratchpad() const { return impl_->with_scratchpad(); }
};
~miopen_convolution_bwd_weights_t() {}
status_t execute(const exec_ctx_t &ctx) const override {
if (pd()->check_for_zero_dims()) { return execute_zero_dims(ctx); }
return execute_convolution(
ctx, pd()->with_bias(), pd()->with_scratchpad());
}
status_t execute_convolution(
const exec_ctx_t &ctx, bool with_bias, bool with_scratchpad) const;
status_t execute_zero_dims(const exec_ctx_t &ctx) const;
private:
const pd_t *pd() const { return (const pd_t *)primitive_t::pd().get(); }
};
} } } }
#endif