#include "cpu/aarch64/acl_utils.hpp"
#include <limits>
namespace dnnl {
namespace impl {
namespace cpu {
namespace aarch64 {
namespace acl_utils {
using namespace dnnl::impl::alg_kind;
using namespace data_type;
status_t safe_set_strides(arm_compute::Strides &strides, size_t dim, size_t val,
bool inc_dim = true) {
if (val > std::numeric_limits<uint32_t>::max()) {
return status::unimplemented;
}
strides.set(dim, val, inc_dim);
return status::success;
}
arm_compute::DataType get_acl_data_t(
const dnnl_data_type_t dt, const bool is_quantized) {
switch (dt) {
case bf16: return arm_compute::DataType::BFLOAT16;
case f32: return arm_compute::DataType::F32;
case s32: return arm_compute::DataType::S32;
case f16: return arm_compute::DataType::F16;
case s8:
if (is_quantized)
return arm_compute::DataType::QASYMM8_SIGNED;
else
return arm_compute::DataType::S8;
case u8:
if (is_quantized)
return arm_compute::DataType::QASYMM8;
else
return arm_compute::DataType::U8;
default: return arm_compute::DataType::UNKNOWN;
}
}
status_t convert_to_acl_act(alg_kind_t eltwise_alg, float alpha, float beta,
arm_compute::ActivationLayerInfo &act_info) {
using namespace arm_compute;
using act_func = ActivationLayerInfo::ActivationFunction;
switch (eltwise_alg) {
case eltwise_relu:
if (alpha == 0) {
act_info = ActivationLayerInfo(act_func::RELU, alpha, beta);
} else {
act_info = ActivationLayerInfo(
act_func::LEAKY_RELU, alpha, beta);
}
break;
case eltwise_tanh:
act_info = ActivationLayerInfo(act_func::TANH, 1.f, 1.f);
break;
case eltwise_elu:
act_info = ActivationLayerInfo(act_func::ELU, alpha, beta);
break;
case eltwise_square:
act_info = ActivationLayerInfo(act_func::SQUARE, alpha, beta);
break;
case eltwise_abs:
act_info = ActivationLayerInfo(act_func::ABS, alpha, beta);
break;
case eltwise_sqrt:
act_info = ActivationLayerInfo(act_func::SQRT, alpha, beta);
break;
case eltwise_linear:
act_info = ActivationLayerInfo(act_func::LINEAR, alpha, beta);
break;
case eltwise_soft_relu:
if (alpha == 1.f) {
act_info
= ActivationLayerInfo(act_func::SOFT_RELU, alpha, beta);
break;
} else {
return status::unimplemented;
}
case eltwise_logistic:
act_info = ActivationLayerInfo(act_func::LOGISTIC, alpha, beta);
break;
case eltwise_clip:
act_info = ActivationLayerInfo(
act_func::LU_BOUNDED_RELU, beta, alpha);
break;
case eltwise_gelu_erf:
act_info = ActivationLayerInfo(act_func::GELU);
break;
default: act_info = ActivationLayerInfo(); return status::unimplemented;
}
return status::success;
}
status_t convert_to_acl_act(
const eltwise_desc_t &ed, arm_compute::ActivationLayerInfo &act_info) {
return convert_to_acl_act(ed.alg_kind, ed.alpha, ed.beta, act_info);
}
status_t convert_to_acl_act(const post_ops_t::entry_t::eltwise_t &elt,
arm_compute::ActivationLayerInfo &act_info) {
return convert_to_acl_act(elt.alg, elt.alpha, elt.beta, act_info);
}
status_t tensor_info(arm_compute::TensorInfo &info, const memory_desc_t &md) {
const memory_desc_wrapper md_wrap(&md);
return tensor_info(info, md_wrap);
}
status_t tensor_info(
arm_compute::TensorInfo &info, const memory_desc_wrapper &md) {
if (!md.is_blocking_desc() || !md.is_dense() || !md.is_plain()
|| md.has_zero_dim())
return status::unimplemented;
arm_compute::TensorShape shape;
size_t acl_dim_i = 0;
for (int i = md.ndims() - 1; i >= 0; --i) {
shape.set(acl_dim_i, md.dims()[i]);
acl_dim_i++;
}
arm_compute::Strides strides_in_bytes;
const blocking_desc_t &blocking_desc = md.blocking_desc();
size_t acl_stride_i = 0;
for (int i = md.ndims() - 1; i >= 0; --i) {
CHECK(safe_set_strides(strides_in_bytes, acl_stride_i,
blocking_desc.strides[i] * md.data_type_size()));
++acl_stride_i;
}
arm_compute::DataType data_type = get_acl_data_t(md.data_type());
size_t num_channels = 1;
size_t offset_first_element_in_bytes = 0;
size_t total_size_in_bytes = md.size();
info.init(shape, num_channels, data_type, strides_in_bytes,
offset_first_element_in_bytes, total_size_in_bytes);
return status::success;
}
status_t insert_singleton_dimension(arm_compute::TensorInfo &ti, size_t dim_i) {
if (ti.num_dimensions() >= 6) return status::unimplemented;
arm_compute::TensorShape shape = ti.tensor_shape();
for (size_t old_i = 0, new_i = 0; old_i < ti.num_dimensions(); ++old_i) {
if (old_i == dim_i) {
shape.set(new_i, 1, false);
++new_i;
}
shape.set(new_i, ti.tensor_shape()[old_i], false);
++new_i;
}
arm_compute::Strides strides;
for (size_t old_i = 0, new_i = 0; old_i < ti.num_dimensions(); ++old_i) {
if (old_i == dim_i) {
CHECK(safe_set_strides(
strides, new_i, ti.strides_in_bytes()[old_i], false));
++new_i;
}
CHECK(safe_set_strides(
strides, new_i, ti.strides_in_bytes()[old_i], false));
++new_i;
}
ti.init(shape, ti.num_channels(), ti.data_type(), strides,
ti.offset_first_element_in_bytes(), ti.total_size());
return status::success;
}
int reorder_dimensions_by_stride(std::vector<memory_desc_t *> permuted_mds,
std::vector<const memory_desc_t *> mds) {
if (permuted_mds.size() != mds.size() || mds.empty()) return 0;
const dim_t ndims = mds[0]->ndims;
for (const auto &md : mds) {
if (md->ndims != ndims || md->format_kind != format_kind::blocked)
return 0;
}
int reordered_dims = 0;
std::vector<int> perm(ndims);
std::iota(perm.begin(), perm.end(), 0);
std::vector<dim_t> next_smallest_stride(mds.size(), 1);
for (dim_t d1 = ndims - 1; d1 >= 0; --d1) {
bool found_swap = false;
for (dim_t d2 = d1; d2 >= 0; --d2) {
found_swap = true;
for (size_t i = 0; i < mds.size(); i++) {
auto &md_strides = mds[i]->format_desc.blocking.strides;
bool can_swap = md_strides[perm[d2]] == next_smallest_stride[i]
|| mds[i]->dims[perm[d2]] == 1;
if (!can_swap) {
found_swap = false;
break;
}
}
if (found_swap) {
for (size_t i = 0; i < mds.size(); i++)
next_smallest_stride[i] *= mds[i]->dims[perm[d2]];
nstl::swap(perm[d2], perm[d1]);
++reordered_dims;
break;
}
}
if (!found_swap) break;
}
std::vector<int> invperm(ndims);
for (dim_t d = 0; d < ndims; ++d)
invperm[perm[d]] = d;
for (size_t i = 0; i < mds.size(); i++) {
memory_desc_permute_axes(*permuted_mds[i], *mds[i], invperm.data());
}
return reordered_dims;
}
status_t reorder_to_weight_format(arm_compute::TensorInfo &info,
memory_desc_t &md, arm_compute::WeightFormat wf, dim_t I_dim,
dim_t O_dim, const std::vector<dim_t> &spatial_dims,
const std::vector<dim_t> &batch_dims) {
md.format_kind = format_kind::blocked;
md.format_desc.blocking = blocking_desc_t {};
const int interleaved_by = arm_compute::interleave_by(wf);
const int block_by = arm_compute::block_by(wf);
md.format_desc.blocking.strides[I_dim] = interleaved_by * block_by;
md.padded_dims[I_dim] = utils::rnd_up(md.dims[I_dim], block_by);
dim_t ldb = interleaved_by * md.padded_dims[I_dim];
for (dim_t sd : spatial_dims) {
md.format_desc.blocking.strides[sd] = ldb;
ldb *= md.padded_dims[sd];
}
md.format_desc.blocking.strides[O_dim] = ldb;
md.padded_dims[O_dim] = utils::rnd_up(md.dims[O_dim], interleaved_by);
const dim_t innermost_batch_stride
= md.padded_dims[I_dim] * md.padded_dims[O_dim];
dim_t batch_stride = innermost_batch_stride;
for (dim_t bd : batch_dims) {
md.format_desc.blocking.strides[bd] = batch_stride;
batch_stride *= md.padded_dims[bd];
}
if (interleaved_by > 1) {
md.format_desc.blocking.inner_nblks = 1 + (block_by > 1);
md.format_desc.blocking.inner_idxs[0] = O_dim;
md.format_desc.blocking.inner_blks[0] = interleaved_by;
if (block_by > 1) {
md.format_desc.blocking.inner_idxs[1] = I_dim;
md.format_desc.blocking.inner_blks[1] = block_by;
}
}
if (arm_compute::is_fixed_format_fast_math(wf)) {
md.data_type = dnnl_bf16;
info.set_data_type(arm_compute::DataType::BFLOAT16);
}
info.set_data_layout(arm_compute::DataLayout::UNKNOWN);
arm_compute::Strides new_strides_in_bytes = info.strides_in_bytes();
CHECK(safe_set_strides(new_strides_in_bytes, 1, ldb * info.element_size()));
CHECK(safe_set_strides(new_strides_in_bytes, 2,
innermost_batch_stride * info.element_size()));
info.init(info.tensor_shape(), info.num_channels(), info.data_type(),
new_strides_in_bytes, info.offset_first_element_in_bytes(),
memory_desc_wrapper(md).size());
return status::success;
}
}
} } } }