#ifndef GRAPH_BACKEND_DNNL_PASSES_UTILS_HPP
#define GRAPH_BACKEND_DNNL_PASSES_UTILS_HPP
#include <algorithm>
#include <functional>
#include <iostream>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include "graph/interface/c_types_map.hpp"
#include "graph/interface/graph.hpp"
#include "graph/interface/op.hpp"
#include "graph/interface/value.hpp"
#include "graph/utils/utils.hpp"
#include "graph/backend/dnnl/subgraph.hpp"
#include "graph/backend/dnnl/utils.hpp"
#include "oneapi/dnnl/dnnl.hpp"
#define VCHECK_UTILS(cond, status, msg, ...) \
VCONDCHECK(graph, create, check, utils, (cond), status, msg, ##__VA_ARGS__);
namespace dnnl {
namespace impl {
namespace graph {
namespace dnnl_impl {
class pass_pipeline_t {
public:
using pass_signature
= std::function<status_t(std::shared_ptr<subgraph_t> &)>;
pass_pipeline_t() = default;
pass_pipeline_t(const subgraph_visualizer_t &vis,
bool enable_validator = true, bool enable_visualizer = true)
: visualizer_(vis)
, is_layout_sensitive_(false)
, is_memory_sensitive_(false)
, enable_validator_(enable_validator)
, enable_visualizer_(enable_visualizer) {}
void reset_visualize_arg(
bool is_layout_sensitive, bool is_memory_sensitive) {
is_layout_sensitive_ = is_layout_sensitive;
is_memory_sensitive_ = is_memory_sensitive;
}
void add_pass(const pass_signature &apass, const std::string &name) {
passes_.emplace_back(apass);
names_.emplace_back(name);
is_layout_sensitives_.push_back(is_layout_sensitive_);
is_memory_sensitives_.push_back(is_memory_sensitive_);
}
status_t run(std::shared_ptr<subgraph_t> &sg) {
status_t ret;
for (size_t i = 0; i < passes_.size(); i++) {
ret = passes_[i](sg);
VCHECK_UTILS(ret == status::success, ret, "run pass %s failed",
names_[i].c_str());
if (enable_visualizer_) {
visualizer_.run(sg, names_[i], is_layout_sensitives_[i],
is_memory_sensitives_[i]);
}
if (enable_validator_) {
ret = validator_.run(sg);
VCHECK_UTILS(ret == status::success, ret,
"validation failed "
"after run pass %s",
names_[i].c_str());
}
}
return status::success;
}
private:
std::vector<pass_signature> passes_;
std::vector<std::string> names_;
std::vector<bool> is_layout_sensitives_;
std::vector<bool> is_memory_sensitives_;
subgraph_visualizer_t visualizer_;
subgraph_validator_t validator_;
bool is_layout_sensitive_;
bool is_memory_sensitive_;
bool enable_validator_;
bool enable_visualizer_;
};
#define BACKEND_DNNL_ADD_PASS(pipeline, pass) pipeline.add_pass(pass, #pass)
status_t set_given_inputs_outputs(std::shared_ptr<subgraph_t> &sg,
const std::vector<logical_tensor_t> &inputs,
const std::vector<logical_tensor_t> &outputs);
status_t set_given_inputs_outputs(std::vector<std::shared_ptr<op_t>> &subgraph,
const std::vector<logical_tensor_t> &inputs,
const std::vector<logical_tensor_t> &outputs);
void set_weight_bias_constant(std::shared_ptr<subgraph_t> &sg);
inline bool is_preprocess_op(op_t &op) {
static const std::set<op_kind_t> preprocess_ops = {
op_kind::_permute,
op_kind::_to_group,
op_kind::_from_group,
op_kind::_unsqueeze,
op_kind::_squeeze,
op_kind::_reshape,
op_kind::_transpose,
op_kind::_identity,
};
return preprocess_ops.count(op.get_kind()) != 0;
}
void merge_common_eltwise_attrs(
const std::shared_ptr<op_t> &org_op, std::shared_ptr<op_t> &new_op);
inline const std::map<op_kind_t, dnnl::algorithm> &get_eltwise_alg_map() {
static const std::map<op_kind_t, dnnl::algorithm> &eltwise_alg_map = {
{op_kind::Abs, dnnl::algorithm::eltwise_abs},
{op_kind::Clamp, dnnl::algorithm::eltwise_clip_v2},
{op_kind::Elu, dnnl::algorithm::eltwise_elu},
{op_kind::Exp, dnnl::algorithm::eltwise_exp},
{op_kind::GELU, dnnl::algorithm::eltwise_gelu_erf},
{op_kind::HardSigmoid, dnnl::algorithm::eltwise_hardsigmoid},
{op_kind::HardSwish, dnnl::algorithm::eltwise_hardswish},
{op_kind::LeakyReLU, dnnl::algorithm::eltwise_relu},
{op_kind::Log, dnnl::algorithm::eltwise_log},
{op_kind::Mish, dnnl::algorithm::eltwise_mish},
{op_kind::ReLU, dnnl::algorithm::eltwise_relu},
{op_kind::Round, dnnl::algorithm::eltwise_round},
{op_kind::Sigmoid, dnnl::algorithm::eltwise_logistic},
{op_kind::Sqrt, dnnl::algorithm::eltwise_sqrt},
{op_kind::Square, dnnl::algorithm::eltwise_square},
{op_kind::Tanh, dnnl::algorithm::eltwise_tanh},
{op_kind::AbsBackward, dnnl::algorithm::eltwise_abs},
{op_kind::ClampBackward, dnnl::algorithm::eltwise_clip_v2},
{op_kind::EluBackward, dnnl::algorithm::eltwise_elu},
{op_kind::GELUBackward, dnnl::algorithm::eltwise_gelu_erf},
{op_kind::HardSigmoidBackward,
dnnl::algorithm::eltwise_hardsigmoid},
{op_kind::HardSwishBackward, dnnl::algorithm::eltwise_hardswish},
{op_kind::MishBackward, dnnl::algorithm::eltwise_mish},
{op_kind::ReLUBackward, dnnl::algorithm::eltwise_relu},
{op_kind::SigmoidBackward, dnnl::algorithm::eltwise_logistic},
{op_kind::ReLUBackward, dnnl::algorithm::eltwise_relu},
{op_kind::SqrtBackward, dnnl::algorithm::eltwise_sqrt},
{op_kind::TanhBackward, dnnl::algorithm::eltwise_tanh},
};
return eltwise_alg_map;
}
inline dnnl::algorithm get_eltwise_alg(
const std::shared_ptr<op_t> &op, bool bwd) {
using algo = dnnl::algorithm;
const op_kind_t opk = op->get_kind();
const auto &map = get_eltwise_alg_map();
auto it = map.find(opk);
if (it == map.end()) {
assert(!"unexpected op kind");
return algo::undef;
} else {
auto alg = it->second;
if (opk == op_kind::GELU || opk == op_kind::GELUBackward) {
if (op->has_attr(op_attr::mode)
&& op->get_attr<std::string>(op_attr::mode)
== "gelu_tanh") {
alg = algo::eltwise_gelu_tanh;
}
}
if (bwd && op->has_attr(op_attr::use_dst)
&& op->get_attr<bool>(op_attr::use_dst)) {
switch (opk) {
case op_kind::ClampBackward:
alg = algo::eltwise_clip_v2_use_dst_for_bwd;
break;
case op_kind::EluBackward:
alg = algo::eltwise_elu_use_dst_for_bwd;
break;
case op_kind::ReLUBackward:
alg = algo::eltwise_relu_use_dst_for_bwd;
break;
case op_kind::SigmoidBackward:
alg = algo::eltwise_logistic_use_dst_for_bwd;
break;
case op_kind::SqrtBackward:
alg = algo::eltwise_sqrt_use_dst_for_bwd;
break;
case op_kind::TanhBackward:
alg = algo::eltwise_tanh_use_dst_for_bwd;
break;
default: break;
}
}
return alg;
}
}
inline const std::map<op_kind_t, dnnl::algorithm> &get_reduction_alg_map() {
static const std::map<op_kind_t, dnnl::algorithm> &reduction_alg_map = {
{op_kind::ReduceL1, dnnl::algorithm::reduction_norm_lp_power_p_sum},
{op_kind::ReduceL2, dnnl::algorithm::reduction_norm_lp_sum},
{op_kind::ReduceMax, dnnl::algorithm::reduction_max},
{op_kind::ReduceMean, dnnl::algorithm::reduction_mean},
{op_kind::ReduceMin, dnnl::algorithm::reduction_min},
{op_kind::ReduceProd, dnnl::algorithm::reduction_mul},
{op_kind::ReduceSum, dnnl::algorithm::reduction_sum},
};
return reduction_alg_map;
}
inline bool is_eltwise_kind(op_kind_t kind) {
static const std::set<op_kind_t> eltwise_kinds {
op_kind::Abs,
op_kind::Clamp,
op_kind::Elu,
op_kind::Exp,
op_kind::GELU,
op_kind::HardSigmoid,
op_kind::HardSwish,
op_kind::LeakyReLU,
op_kind::Log,
op_kind::Mish,
op_kind::ReLU,
op_kind::Round,
op_kind::Sigmoid,
op_kind::SoftPlus,
op_kind::Sqrt,
op_kind::Square,
op_kind::Tanh,
};
return eltwise_kinds.find(kind) != eltwise_kinds.end();
}
inline bool is_eltwise_bwd_kind(op_kind_t kind) {
static const std::set<op_kind_t> eltwise_bwd_kinds {
op_kind::AbsBackward,
op_kind::ClampBackward,
op_kind::EluBackward,
op_kind::GELUBackward,
op_kind::HardSigmoidBackward,
op_kind::HardSwishBackward,
op_kind::MishBackward,
op_kind::ReLUBackward,
op_kind::SigmoidBackward,
op_kind::SqrtBackward,
op_kind::TanhBackward,
};
return eltwise_bwd_kinds.find(kind) != eltwise_bwd_kinds.end();
}
inline bool is_binary_kind(op_kind_t kind) {
static const std::set<op_kind_t> binary_kinds = {
op_kind::Add,
op_kind::Subtract,
op_kind::Multiply,
op_kind::Divide,
op_kind::Minimum,
op_kind::Maximum,
};
return binary_kinds.find(kind) != binary_kinds.end();
}
inline bool is_reduction_kind(op_kind_t kind) {
static const std::set<op_kind_t> reduction_kinds = {
op_kind::ReduceL1,
op_kind::ReduceL2,
op_kind::ReduceMax,
op_kind::ReduceMean,
op_kind::ReduceMin,
op_kind::ReduceProd,
op_kind::ReduceSum,
};
return reduction_kinds.find(kind) != reduction_kinds.end();
}
std::vector<value_t *> get_constant_block_output_values(
const std::shared_ptr<subgraph_t> &sg);
status_t infer_shape(std::shared_ptr<subgraph_t> &sg);
const std::map<op_kind_t, dnnl::algorithm> &get_binary_alg_map();
bool binary_doable(
const std::vector<dim_t> &shape_0, const std::vector<dim_t> &shape_1);
bool prelu_doable(const std::vector<dim_t> &src_dims,
const std::vector<dim_t> &wei_dims, const std::string &data_format,
const bool per_channel_broadcast);
std::pair<bool, std::pair<size_t, int64_t>> shuffle_fusible(
const op_t *reshape0, op_t *reshape1, op_t *transpose);
bool post_binary_fusible(const op_t *base_op, const op_t *bin_op,
engine_kind_t ekind = engine_kind::cpu);
bool post_eltwise_fusible(
const op_t *base_op, const op_t *elt_op, graph::engine_kind_t ekind);
bool post_depthwise_conv_fusible(
const op_t *base_conv_op, const op_t *post_conv_op);
const std::unordered_map<op_kind_t, std::unordered_set<op_kind_t>> &
get_post_ops_fusible_map();
std::string kind2str(op_kind_t kind);
bool is_typecast(const op_t *op);
bool with_runtime_scales(
const std::shared_ptr<op_t> &op, bool is_input, size_t index);
bool with_runtime_zps(
const std::shared_ptr<op_t> &op, bool is_input, size_t index);
bool is_layout_reorder(const op_t *op);
std::shared_ptr<op_t> clone_mul_scales(const std::shared_ptr<op_t> &scale_op);
bool inverse_mul_scales(std::shared_ptr<op_t> &scale_op);
bool need_broadcast_for_inputs(
const std::shared_ptr<op_t> &op, size_t index1, size_t index2);
} } } }
#endif