summaryrefslogtreecommitdiff
path: root/compiler/luci/export/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/luci/export/src')
-rw-r--r--compiler/luci/export/src/CircleExporterImpl.cpp59
-rw-r--r--compiler/luci/export/src/CircleExporterUtils.cpp35
-rw-r--r--compiler/luci/export/src/CircleExporterUtils.h1
-rw-r--r--compiler/luci/export/src/CircleOperationExporter.cpp1205
-rw-r--r--compiler/luci/export/src/CircleTensorExporter.cpp179
-rw-r--r--compiler/luci/export/src/ProgressReporter.cpp28
-rw-r--r--compiler/luci/export/src/SerializedData.h23
-rw-r--r--compiler/luci/export/src/TypeBridge.cpp105
-rw-r--r--compiler/luci/export/src/TypeBridge.h44
9 files changed, 1301 insertions, 378 deletions
diff --git a/compiler/luci/export/src/CircleExporterImpl.cpp b/compiler/luci/export/src/CircleExporterImpl.cpp
index 81109ee62..860cebf6e 100644
--- a/compiler/luci/export/src/CircleExporterImpl.cpp
+++ b/compiler/luci/export/src/CircleExporterImpl.cpp
@@ -16,6 +16,7 @@
#include "CircleExporterImpl.h"
#include "Optimize.h"
+#include "TypeBridge.h"
#include "CircleTensorExporter.h"
#include "CircleOperationExporter.h"
#include "CircleExporterUtils.h"
@@ -36,11 +37,11 @@ luci::CircleInput *input_node(loco::Graph *g, const loco::GraphInputIndex &index
{
for (uint32_t n = 0; n < g->nodes()->size(); ++n)
{
- if (auto pull = dynamic_cast<luci::CircleInput *>(g->nodes()->at(n)))
+ if (auto input = dynamic_cast<luci::CircleInput *>(g->nodes()->at(n)))
{
- if (pull->indexed() && pull->index() == index)
+ if (input->indexed() && input->index() == index)
{
- return pull;
+ return input;
}
}
}
@@ -51,11 +52,11 @@ luci::CircleOutput *output_node(loco::Graph *g, const loco::GraphOutputIndex &in
{
for (uint32_t n = 0; n < g->nodes()->size(); ++n)
{
- if (auto push = dynamic_cast<luci::CircleOutput *>(g->nodes()->at(n)))
+ if (auto output = dynamic_cast<luci::CircleOutput *>(g->nodes()->at(n)))
{
- if (push->indexed() && push->index() == index)
+ if (output->indexed() && output->index() == index)
{
- return push;
+ return output;
}
}
}
@@ -80,6 +81,13 @@ void registerGraphOutputTensors(loco::Graph *graph, luci::SubGraphContext &ctx)
assert(push != nullptr);
auto node = push->from();
assert(node != nullptr);
+
+ // Do not export CircleOutput when it's input is CircleOutputExclude
+ if (dynamic_cast<luci::CircleOutputExclude *>(push->from()) != nullptr)
+ {
+ continue;
+ }
+
ctx._outputs.push_back(luci::get_tensor_index(node));
}
}
@@ -93,8 +101,7 @@ using namespace circle;
using namespace flatbuffers;
Offset<Vector<Offset<OperatorCode>>>
-encodeOperatorCodes(FlatBufferBuilder &builder, std::unordered_map<luci::OpCode, uint32_t> &opcodes,
- std::unordered_map<luci::OpCode, std::string> &custom_opcodes)
+encodeOperatorCodes(FlatBufferBuilder &builder, std::unordered_map<luci::OpCode, uint32_t> &opcodes)
{
std::vector<Offset<OperatorCode>> operator_codes_vec(opcodes.size());
for (auto it : opcodes)
@@ -102,19 +109,15 @@ encodeOperatorCodes(FlatBufferBuilder &builder, std::unordered_map<luci::OpCode,
uint32_t idx = it.second;
if (it.first.opcode != BuiltinOperator_CUSTOM)
{
- operator_codes_vec[idx] = CreateOperatorCode(builder, it.first.opcode);
+ operator_codes_vec[idx] = CreateOperatorCode(builder, it.first.opcode, 0, it.first.version);
}
- else // custom op
+ else
{
- auto opCode = it.first;
- auto custom_code = custom_opcodes.find(opCode);
- if (custom_code == custom_opcodes.end())
- INTERNAL_EXN("Cannot find code for customop even though opcode is BuiltinOperator_CUSTOM");
-
operator_codes_vec[idx] =
- CreateOperatorCode(builder, it.first.opcode, builder.CreateString(custom_code->second));
+ CreateOperatorCode(builder, it.first.opcode, builder.CreateString(it.first.custom_code));
}
}
+
return builder.CreateVector(operator_codes_vec);
}
@@ -136,8 +139,9 @@ CircleExporterImpl::exportSubgraph(SerializedGraphData &gd)
auto inputs = _builder.CreateVector(gd._inputs);
auto outputs = _builder.CreateVector(gd._outputs);
auto operators = _builder.CreateVector(gd._operators);
+ auto name = _builder.CreateString(gd._name);
auto df = gd._data_format;
- auto subgraph = CreateSubGraph(_builder, tensors, inputs, outputs, operators, df);
+ auto subgraph = CreateSubGraph(_builder, tensors, inputs, outputs, operators, name, df);
return subgraph;
}
@@ -146,6 +150,9 @@ void CircleExporterImpl::exportGraph(loco::Graph *graph)
// do graph optimization
optimize(graph);
+ // copy shape/dtype inference data to CircleNode
+ copy_shape_dtype(graph);
+
_builder.Clear();
SerializedModelData md;
@@ -154,6 +161,9 @@ void CircleExporterImpl::exportGraph(loco::Graph *graph)
// This version is taken from comment in fbs
constexpr uint32_t version = 0;
+ // set Subgraph name
+ gd._name = graph->name();
+
// TODO set this value properly
gd._data_format = circle::DataFormat::DataFormat_CHANNELS_LAST;
@@ -170,8 +180,7 @@ void CircleExporterImpl::exportGraph(loco::Graph *graph)
exportNodes(graph, _builder, md, gd);
// encode operator codes
- auto operator_codes =
- encodeOperatorCodes(_builder, md._operator_codes, md._custom_operator_codes);
+ auto operator_codes = encodeOperatorCodes(_builder, md._operator_codes);
// Subgraphs
Offset<SubGraph> subgraph = exportSubgraph(gd);
@@ -203,6 +212,9 @@ void CircleExporterImpl::exportModule(Module *module)
_builder.Clear();
+ // prepare model data
+ prepareModelData(_builder, md);
+
std::vector<flatbuffers::Offset<circle::SubGraph>> subgraph_vec;
for (size_t g = 0; g < module->size(); ++g)
@@ -211,8 +223,14 @@ void CircleExporterImpl::exportModule(Module *module)
optimize(graph);
+ // copy shape/dtype inference data to CircleNode
+ copy_shape_dtype(graph);
+
SerializedGraphData gd;
+ // set Subgraph name
+ gd._name = graph->name();
+
// TODO set this value properly
gd._data_format = circle::DataFormat::DataFormat_CHANNELS_LAST;
@@ -233,8 +251,7 @@ void CircleExporterImpl::exportModule(Module *module)
auto subgraphs = _builder.CreateVector(std::vector<Offset<SubGraph>>{subgraph_vec});
// encode operator codes
- auto operator_codes =
- encodeOperatorCodes(_builder, md._operator_codes, md._custom_operator_codes);
+ auto operator_codes = encodeOperatorCodes(_builder, md._operator_codes);
// Description
std::string description_str = "nnpackage";
diff --git a/compiler/luci/export/src/CircleExporterUtils.cpp b/compiler/luci/export/src/CircleExporterUtils.cpp
index 1272facb2..f097e71c5 100644
--- a/compiler/luci/export/src/CircleExporterUtils.cpp
+++ b/compiler/luci/export/src/CircleExporterUtils.cpp
@@ -70,28 +70,49 @@ circle::TensorType to_circle_tensortype(loco::DataType type)
}
}
+circle::MirrorPadMode to_circle_mirrorpadmode(luci::MirrorPadMode mode)
+{
+ switch (mode)
+ {
+ case luci::MirrorPadMode::REFLECT:
+ return circle::MirrorPadMode::MirrorPadMode_REFLECT;
+ case luci::MirrorPadMode::SYMMETRIC:
+ return circle::MirrorPadMode::MirrorPadMode_SYMMETRIC;
+ default:
+ INTERNAL_EXN_V("trying to convert unsupported luci::MirrorPadMode", oops::to_uint32(mode));
+ }
+}
+
} // namespace luci
namespace luci
{
-uint32_t SerializedModelData::registerBuiltinOpcode(circle::BuiltinOperator builtin_code)
+uint32_t SerializedModelData::registerBuiltinOpcode(circle::BuiltinOperator builtin_code,
+ const int32_t op_version)
{
- auto it = _operator_codes.find(OpCode{builtin_code});
+ assert(op_version > 0);
+
+ auto it = _operator_codes.find(OpCode{builtin_code, "", op_version});
if (it != _operator_codes.end())
{
return it->second;
}
auto idx = static_cast<uint32_t>(_operator_codes.size());
- _operator_codes.emplace(OpCode{builtin_code}, idx);
+ _operator_codes.emplace(OpCode{builtin_code, "", op_version}, idx);
return idx;
}
-uint32_t SerializedModelData::registerCustomOpcode(const std::string &custom_op)
+uint32_t SerializedModelData::registerCustomOpcode(const std::string &custom_code)
{
- circle::BuiltinOperator custom_code = circle::BuiltinOperator_CUSTOM;
- auto idx = registerBuiltinOpcode(custom_code);
- _custom_operator_codes.emplace(OpCode{custom_code}, custom_op);
+ const circle::BuiltinOperator builtin_code = circle::BuiltinOperator_CUSTOM;
+ auto it = _operator_codes.find(OpCode{builtin_code, custom_code});
+ if (it != _operator_codes.end())
+ {
+ return it->second;
+ }
+ auto idx = static_cast<uint32_t>(_operator_codes.size());
+ _operator_codes.emplace(OpCode{builtin_code, custom_code}, idx);
return idx;
}
diff --git a/compiler/luci/export/src/CircleExporterUtils.h b/compiler/luci/export/src/CircleExporterUtils.h
index 6b970fd3c..f9ce6d2bf 100644
--- a/compiler/luci/export/src/CircleExporterUtils.h
+++ b/compiler/luci/export/src/CircleExporterUtils.h
@@ -31,6 +31,7 @@ namespace luci
circle::ActivationFunctionType to_circle_actfunc(luci::FusedActFunc func);
circle::TensorType to_circle_tensortype(loco::DataType type);
+circle::MirrorPadMode to_circle_mirrorpadmode(luci::MirrorPadMode mode);
} // namespace luci
diff --git a/compiler/luci/export/src/CircleOperationExporter.cpp b/compiler/luci/export/src/CircleOperationExporter.cpp
index ad9c7fd4b..3c01b676f 100644
--- a/compiler/luci/export/src/CircleOperationExporter.cpp
+++ b/compiler/luci/export/src/CircleOperationExporter.cpp
@@ -22,6 +22,8 @@
#include <luci/IR/CircleNodes.h>
#include <luci/IR/CircleNodeVisitor.h>
#include <luci/Service/CircleShapeInference.h>
+#include <luci/UserSettings.h>
+#include <luci/Log.h>
#include <loco/IR/CanonicalNodeVisitor.h>
#include <oops/InternalExn.h>
@@ -49,42 +51,125 @@ public:
public:
void visit(luci::CircleAbs *) final;
void visit(luci::CircleAdd *) final;
+ void visit(luci::CircleAddN *) final;
void visit(luci::CircleArgMax *) final;
+ void visit(luci::CircleArgMin *) final;
void visit(luci::CircleAveragePool2D *) final;
+ void visit(luci::CircleBatchMatMul *) final;
void visit(luci::CircleBatchToSpaceND *) final;
+ void visit(luci::CircleCast *) final;
+ void visit(luci::CircleCeil *) final;
void visit(luci::CircleConcatenation *) final;
void visit(luci::CircleConst *) final{/* skip, everything is done in exportOpDefinedTensors */};
void visit(luci::CircleConv2D *) final;
void visit(luci::CircleCos *) final;
+ void visit(luci::CircleCustom *) final;
+ void visit(luci::CircleDepthToSpace *) final;
void visit(luci::CircleDepthwiseConv2D *) final;
void visit(luci::CircleDiv *) final;
- void visit(luci::CircleExp *) final;
+ void visit(luci::CircleElu *) final;
void visit(luci::CircleEqual *) final;
+ void visit(luci::CircleExp *) final;
+ void visit(luci::CircleExpandDims *) final;
+ void visit(luci::CircleFill *) final;
+ void visit(luci::CircleFloor *) final;
+ void visit(luci::CircleFloorDiv *) final;
+ void visit(luci::CircleFloorMod *) final;
void visit(luci::CircleFullyConnected *) final;
+ void visit(luci::CircleGather *) final;
+ void visit(luci::CircleGatherNd *) final;
+ void visit(luci::CircleGreater *) final;
+ void visit(luci::CircleGreaterEqual *) final;
+ void visit(luci::CircleIf *) final;
+ void visit(luci::CircleL2Normalize *) final;
+ void visit(luci::CircleL2Pool2D *) final;
+ void visit(luci::CircleLeakyRelu *) final;
+ void visit(luci::CircleLess *) final;
+ void visit(luci::CircleLessEqual *) final;
+ void visit(luci::CircleLocalResponseNormalization *) final;
+ void visit(luci::CircleLog *) final;
+ void visit(luci::CircleLogicalAnd *) final;
void visit(luci::CircleLogicalNot *) final;
void visit(luci::CircleLogicalOr *) final;
+ void visit(luci::CircleLogistic *) final;
+ void visit(luci::CircleLogSoftmax *) final;
+ void visit(luci::CircleMatrixDiag *) final;
+ void visit(luci::CircleMatrixSetDiag *) final;
void visit(luci::CircleMaximum *) final;
void visit(luci::CircleMaxPool2D *) final;
void visit(luci::CircleMean *) final;
+ void visit(luci::CircleMinimum *) final;
+ void visit(luci::CircleMirrorPad *) final;
void visit(luci::CircleMul *) final;
+ void visit(luci::CircleNeg *) final;
+ void visit(luci::CircleNotEqual *) final;
+ void visit(luci::CircleOneHot *) final;
void visit(luci::CirclePack *) final;
void visit(luci::CirclePad *) final;
+ void visit(luci::CirclePow *) final;
+ void visit(luci::CirclePRelu *) final;
+ void visit(luci::CircleRange *) final;
+ void visit(luci::CircleRank *) final;
+ void visit(luci::CircleReduceAny *) final;
+ void visit(luci::CircleReduceMax *) final;
+ void visit(luci::CircleReduceMin *) final;
+ void visit(luci::CircleReduceProd *) final;
void visit(luci::CircleRelu *) final;
void visit(luci::CircleRelu6 *) final;
+ void visit(luci::CircleReluN1To1 *) final;
void visit(luci::CircleReshape *) final;
+ void visit(luci::CircleResizeBilinear *) final;
+ void visit(luci::CircleResizeNearestNeighbor *) final;
+ void visit(luci::CircleReverseSequence *) final;
+ void visit(luci::CircleReverseV2 *) final;
+ void visit(luci::CircleRound *) final;
void visit(luci::CircleRsqrt *) final;
+ void visit(luci::CircleScatterNd *) final;
+ void visit(luci::CircleSegmentSum *) final;
+ void visit(luci::CircleSelect *) final;
+ void visit(luci::CircleSelectV2 *) final;
+ void visit(luci::CircleShape *) final;
+ void visit(luci::CircleSin *) final;
+ void visit(luci::CircleSlice *) final;
void visit(luci::CircleSoftmax *) final;
+ void visit(luci::CircleSpaceToBatchND *) final;
+ void visit(luci::CircleSpaceToDepth *) final;
+ void visit(luci::CircleSparseToDense *) final;
+ void visit(luci::CircleSplit *) final;
+ void visit(luci::CircleSplitV *) final;
void visit(luci::CircleSqrt *) final;
+ void visit(luci::CircleSquare *) final;
void visit(luci::CircleSquaredDifference *) final;
+ void visit(luci::CircleSqueeze *) final;
+ void visit(luci::CircleStridedSlice *) final;
void visit(luci::CircleSub *) final;
- // TODO CircleTanh
+ void visit(luci::CircleSum *) final;
+ void visit(luci::CircleTanh *) final;
+ void visit(luci::CircleTile *) final;
+ void visit(luci::CircleTopKV2 *) final;
void visit(luci::CircleTranspose *) final;
void visit(luci::CircleTransposeConv *) final;
+ void visit(luci::CircleUnpack *) final;
+ void visit(luci::CircleWhere *) final;
+ void visit(luci::CircleWhile *) final;
+ void visit(luci::CircleZerosLike *) final;
// Circle only
+ void visit(luci::CircleBCQFullyConnected *) final;
+ void visit(luci::CircleBCQGather *) final;
void visit(luci::CircleInstanceNorm *) final;
// Virtual
void visit(luci::CircleInput *) final {}
void visit(luci::CircleOutput *) final {}
+ void visit(luci::CircleOutputDummy *) final {}
+ void visit(luci::CircleOutputExclude *) final {}
+ // Virtual for multiple-outputs
+ void visit(luci::CircleCustomOut *) final {}
+ void visit(luci::CircleIfOut *) final {}
+ void visit(luci::CircleSplitOut *) final {}
+ void visit(luci::CircleSplitVOut *) final {}
+ void visit(luci::CircleTopKV2Out *) final {}
+ void visit(luci::CircleUnpackOut *) final {}
+ void visit(luci::CircleWhileOut *) final {}
private:
/**
@@ -95,6 +180,17 @@ private:
template <class CirclePool2D>
void export_pool_2d(CirclePool2D *node, circle::BuiltinOperator builtin_op);
+ /**
+ * @brief export simple nodes
+ */
+ void export_simple(loco::Node *node, circle::BuiltinOperator bop, circle::BuiltinOptions bot,
+ flatbuffers::Offset<void> options_offset);
+
+ /**
+ * @brief export simple nodes having void options
+ */
+ void export_simple(loco::Node *node, circle::BuiltinOperator bop);
+
private:
FlatBufferBuilder &builder;
SerializedModelData &md;
@@ -105,11 +201,12 @@ template <class CirclePool2D>
void OperationExporter::export_pool_2d(CirclePool2D *node, circle::BuiltinOperator builtin_op)
{
LUCI_ASSERT(builtin_op == circle::BuiltinOperator_MAX_POOL_2D ||
+ builtin_op == circle::BuiltinOperator_L2_POOL_2D ||
builtin_op == circle::BuiltinOperator_AVERAGE_POOL_2D,
- "Should be MaxPool or AvgPool");
+ "Should be L2Pool, MaxPool or AvgPool");
LUCI_ASSERT(node->padding() != luci::Padding::UNDEFINED, "Padding is not set");
- uint32_t op_idx = md.registerBuiltinOpcode(builtin_op);
+ uint32_t op_idx = md.registerBuiltinOpcode(builtin_op, node->op_version());
std::vector<int32_t> inputs_vec{get_tensor_index(node->value())};
std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
auto inputs = builder.CreateVector(inputs_vec);
@@ -125,54 +222,122 @@ void OperationExporter::export_pool_2d(CirclePool2D *node, circle::BuiltinOperat
gd._operators.push_back(op_offset);
}
-void OperationExporter::visit(luci::CircleAbs *node)
+void OperationExporter::export_simple(loco::Node *node, circle::BuiltinOperator bop,
+ circle::BuiltinOptions bot,
+ flatbuffers::Offset<void> options_offset)
{
- uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_ABS);
- std::vector<int32_t> inputs_vec{get_tensor_index(node->x())};
+ uint32_t op_idx =
+ md.registerBuiltinOpcode(bop, loco::must_cast<luci::CircleNode *>(node)->op_version());
+ std::vector<int32_t> inputs_vec;
+ std::vector<int32_t> outputs_vec{get_tensor_index(node)};
+ for (uint32_t i = 0; i < node->arity(); ++i)
+ inputs_vec.push_back(get_tensor_index(node->arg(i)));
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, bot, options_offset);
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::export_simple(loco::Node *node, circle::BuiltinOperator bop)
+{
+ uint32_t op_idx =
+ md.registerBuiltinOpcode(bop, loco::must_cast<luci::CircleNode *>(node)->op_version());
+ std::vector<int32_t> inputs_vec;
std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ for (uint32_t i = 0; i < node->arity(); ++i)
+ inputs_vec.push_back(get_tensor_index(node->arg(i)));
auto inputs = builder.CreateVector(inputs_vec);
auto outputs = builder.CreateVector(outputs_vec);
- auto options = CreateAbsOptions(builder);
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
- circle::BuiltinOptions_AbsOptions, options.Union());
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs);
gd._operators.push_back(op_offset);
}
+void OperationExporter::visit(luci::CircleAbs *node)
+{
+ export_simple(node, circle::BuiltinOperator_ABS, circle::BuiltinOptions_AbsOptions,
+ CreateAbsOptions(builder).Union());
+}
+
void OperationExporter::visit(luci::CircleAdd *node)
{
- uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_ADD);
- std::vector<int32_t> inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())};
+ export_simple(
+ node, circle::BuiltinOperator_ADD, circle::BuiltinOptions_AddOptions,
+ CreateAddOptions(builder, to_circle_actfunc(node->fusedActivationFunction())).Union());
+}
+
+void OperationExporter::visit(luci::CircleAddN *node)
+{
+ uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_ADD_N, node->op_version());
+ std::vector<int32_t> inputs_vec;
std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+
+ for (uint32_t i = 0; i < node->arity(); ++i)
+ inputs_vec.push_back(get_tensor_index(node->inputs(i)));
+
auto inputs = builder.CreateVector(inputs_vec);
auto outputs = builder.CreateVector(outputs_vec);
- auto options = CreateAddOptions(builder, to_circle_actfunc(node->fusedActivationFunction()));
+ auto options = CreateAddNOptions(builder);
auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
- circle::BuiltinOptions_AddOptions, options.Union());
+ circle::BuiltinOptions_AddNOptions, options.Union());
gd._operators.push_back(op_offset);
}
void OperationExporter::visit(luci::CircleArgMax *node)
{
- uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_ARG_MAX);
- std::vector<int32_t> inputs_vec{get_tensor_index(node->input()),
- get_tensor_index(node->dimension())};
+ export_simple(node, circle::BuiltinOperator_ARG_MAX, circle::BuiltinOptions_ArgMaxOptions,
+ CreateArgMaxOptions(builder, to_circle_tensortype(node->output_type())).Union());
+}
+
+void OperationExporter::visit(luci::CircleArgMin *node)
+{
+ export_simple(node, circle::BuiltinOperator_ARG_MIN, circle::BuiltinOptions_ArgMinOptions,
+ CreateArgMinOptions(builder, to_circle_tensortype(node->output_type())).Union());
+}
+
+void OperationExporter::visit(luci::CircleAveragePool2D *node)
+{
+ export_pool_2d<luci::CircleAveragePool2D>(node, circle::BuiltinOperator_AVERAGE_POOL_2D);
+}
+
+void OperationExporter::visit(luci::CircleBatchMatMul *node)
+{
+ export_simple(node, circle::BuiltinOperator_BATCH_MATMUL,
+ circle::BuiltinOptions_BatchMatMulOptions,
+ CreateBatchMatMulOptions(builder, node->adj_x(), node->adj_y()).Union());
+}
+
+void OperationExporter::visit(luci::CircleCast *node)
+{
+ uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_CAST, node->op_version());
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->x())};
std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
auto inputs = builder.CreateVector(inputs_vec);
auto outputs = builder.CreateVector(outputs_vec);
- auto options = CreateArgMaxOptions(builder, to_circle_tensortype(node->output_type()));
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
- circle::BuiltinOptions_ArgMaxOptions, options.Union());
+
+ flatbuffers::Offset<Operator> op_offset;
+ if (node->out_data_type() != loco::DataType::Unknown)
+ {
+ auto options = CreateCastOptions(builder, to_circle_tensortype(node->in_data_type()),
+ to_circle_tensortype(node->out_data_type()));
+ op_offset = CreateOperator(builder, op_idx, inputs, outputs, circle::BuiltinOptions_CastOptions,
+ options.Union());
+ }
+ else
+ {
+ op_offset = CreateOperator(builder, op_idx, inputs, outputs);
+ }
gd._operators.push_back(op_offset);
}
-void OperationExporter::visit(luci::CircleAveragePool2D *node)
+void OperationExporter::visit(luci::CircleCeil *node)
{
- export_pool_2d<luci::CircleAveragePool2D>(node, circle::BuiltinOperator_AVERAGE_POOL_2D);
+ export_simple(node, circle::BuiltinOperator_CEIL);
}
void OperationExporter::visit(luci::CircleConcatenation *node)
{
- uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_CONCATENATION);
+ uint32_t op_idx =
+ md.registerBuiltinOpcode(circle::BuiltinOperator_CONCATENATION, node->op_version());
std::vector<int32_t> inputs_vec;
std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
@@ -190,169 +355,304 @@ void OperationExporter::visit(luci::CircleConcatenation *node)
void OperationExporter::visit(luci::CircleBatchToSpaceND *node)
{
- uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_BATCH_TO_SPACE_ND);
- std::vector<int32_t> inputs_vec{get_tensor_index(node->input()),
- get_tensor_index(node->block_shape()),
- get_tensor_index(node->crops())};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
-
- auto inputs = builder.CreateVector(inputs_vec);
- auto outputs = builder.CreateVector(outputs_vec);
- auto options = CreateBatchToSpaceNDOptions(builder);
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
- circle::BuiltinOptions_BatchToSpaceNDOptions, options.Union());
- gd._operators.push_back(op_offset);
+ export_simple(node, circle::BuiltinOperator_BATCH_TO_SPACE_ND,
+ circle::BuiltinOptions_BatchToSpaceNDOptions,
+ CreateBatchToSpaceNDOptions(builder).Union());
}
void OperationExporter::visit(luci::CircleConv2D *node)
{
- uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_CONV_2D);
-
- // Make input, output and options for operator
- std::vector<int32_t> inputs_vec{get_tensor_index(node->input()), get_tensor_index(node->filter()),
- get_tensor_index(node->bias())};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
- auto inputs = builder.CreateVector(inputs_vec);
- auto outputs = builder.CreateVector(outputs_vec);
- circle::Padding padding = getOpPadding(node->padding());
- auto options = CreateConv2DOptions(builder, padding, node->stride()->w(), node->stride()->h(),
- to_circle_actfunc(node->fusedActivationFunction()));
-
- // Make CONV_2D operator
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
- circle::BuiltinOptions_Conv2DOptions, options.Union());
- gd._operators.push_back(op_offset);
+ export_simple(node, circle::BuiltinOperator_CONV_2D, circle::BuiltinOptions_Conv2DOptions,
+ CreateConv2DOptions(builder, getOpPadding(node->padding()), node->stride()->w(),
+ node->stride()->h(),
+ to_circle_actfunc(node->fusedActivationFunction()),
+ node->dilation()->w(), node->dilation()->h())
+ .Union());
}
void OperationExporter::visit(luci::CircleCos *node)
{
- uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_COS);
+ export_simple(node, circle::BuiltinOperator_COS, circle::BuiltinOptions_CosOptions,
+ CreateCosOptions(builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleCustom *node)
+{
+ auto custom_outputs = loco::succs(node);
+
+ uint32_t op_idx = md.registerCustomOpcode(node->custom_code());
+ std::vector<int32_t> inputs_vec;
+ std::vector<int32_t> outputs_vec;
+
+ for (uint32_t index = 0; index < node->numInputs(); index++)
+ {
+ inputs_vec.push_back(get_tensor_index(node->inputs(index)));
+ }
+ for (uint32_t index = 0; index < custom_outputs.size(); index++)
+ {
+ // store in order of index
+ bool found = false;
+ for (auto out : custom_outputs)
+ {
+ auto custom_out = loco::must_cast<luci::CircleCustomOut *>(out);
+ if (custom_out->index() == static_cast<int32_t>(index))
+ {
+ outputs_vec.push_back(get_tensor_index(custom_out));
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ INTERNAL_EXN("Invalid Custom output");
+ }
+ }
- // Make input, output and options for operator
- std::vector<int32_t> inputs_vec{get_tensor_index(node->x())};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
auto inputs = builder.CreateVector(inputs_vec);
auto outputs = builder.CreateVector(outputs_vec);
- auto options = CreateCosOptions(builder);
-
- // Make COS operator
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
- circle::BuiltinOptions_CosOptions, options.Union());
+ flatbuffers::Offset<flatbuffers::Vector<uint8_t>> circle_custom_options;
+ std::vector<uint8_t> custom_options_vec{node->custom_options().begin(),
+ node->custom_options().end()};
+ circle_custom_options = builder.CreateVector(custom_options_vec);
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, circle::BuiltinOptions_NONE,
+ flatbuffers::Offset<void>(), circle_custom_options);
gd._operators.push_back(op_offset);
}
+void OperationExporter::visit(luci::CircleDepthToSpace *node)
+{
+ export_simple(node, circle::BuiltinOperator_DEPTH_TO_SPACE,
+ circle::BuiltinOptions_DepthToSpaceOptions,
+ CreateDepthToSpaceOptions(builder, node->block_size()).Union());
+}
+
void OperationExporter::visit(luci::CircleDepthwiseConv2D *node)
{
- uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_DEPTHWISE_CONV_2D);
+ export_simple(node, circle::BuiltinOperator_DEPTHWISE_CONV_2D,
+ circle::BuiltinOptions_DepthwiseConv2DOptions,
+ CreateDepthwiseConv2DOptions(builder, getOpPadding(node->padding()),
+ node->stride()->w(), node->stride()->h(),
+ node->depthMultiplier(),
+ to_circle_actfunc(node->fusedActivationFunction()),
+ node->dilation()->w(), node->dilation()->h())
+ .Union());
+}
- // Make input, output and options for operator
- std::vector<int32_t> inputs_vec{get_tensor_index(node->input()), get_tensor_index(node->filter()),
- get_tensor_index(node->bias())};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
- auto inputs = builder.CreateVector(inputs_vec);
- auto outputs = builder.CreateVector(outputs_vec);
- circle::Padding padding = getOpPadding(node->padding());
- auto options = CreateDepthwiseConv2DOptions(builder, padding, node->stride()->w(),
- node->stride()->h(), node->depthMultiplier(),
- to_circle_actfunc(node->fusedActivationFunction()));
+void OperationExporter::visit(luci::CircleDiv *node)
+{
+ export_simple(
+ node, circle::BuiltinOperator_DIV, circle::BuiltinOptions_DivOptions,
+ CreateDivOptions(builder, to_circle_actfunc(node->fusedActivationFunction())).Union());
+}
- // Make DEPTHWISE_CONV_2D operator
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
- circle::BuiltinOptions_DepthwiseConv2DOptions, options.Union());
- gd._operators.push_back(op_offset);
+void OperationExporter::visit(luci::CircleElu *node)
+{
+ export_simple(node, circle::BuiltinOperator_ELU);
}
-void OperationExporter::visit(luci::CircleDiv *node)
+void OperationExporter::visit(luci::CircleEqual *node)
{
- uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_DIV);
- std::vector<int32_t> inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
- auto inputs = builder.CreateVector(inputs_vec);
- auto outputs = builder.CreateVector(outputs_vec);
- auto options = CreateDivOptions(builder, to_circle_actfunc(node->fusedActivationFunction()));
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
- circle::BuiltinOptions_DivOptions, options.Union());
- gd._operators.push_back(op_offset);
+ export_simple(node, circle::BuiltinOperator_EQUAL, circle::BuiltinOptions_EqualOptions,
+ CreateEqualOptions(builder).Union());
}
void OperationExporter::visit(luci::CircleExp *node)
{
- uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_EXP);
- std::vector<int32_t> inputs_vec{get_tensor_index(node->x())};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
- auto inputs = builder.CreateVector(inputs_vec);
- auto outputs = builder.CreateVector(outputs_vec);
- auto options = CreateAbsOptions(builder);
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
- circle::BuiltinOptions_ExpOptions, options.Union());
- gd._operators.push_back(op_offset);
+ export_simple(node, circle::BuiltinOperator_EXP, circle::BuiltinOptions_ExpOptions,
+ CreateExpOptions(builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleExpandDims *node)
+{
+ export_simple(node, circle::BuiltinOperator_EXPAND_DIMS, circle::BuiltinOptions_ExpandDimsOptions,
+ CreateExpandDimsOptions(builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleFill *node)
+{
+ export_simple(node, circle::BuiltinOperator_FILL, circle::BuiltinOptions_FillOptions,
+ CreateFillOptions(builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleFloor *node)
+{
+ export_simple(node, circle::BuiltinOperator_FLOOR);
+}
+
+void OperationExporter::visit(luci::CircleFloorDiv *node)
+{
+ export_simple(node, circle::BuiltinOperator_FLOOR_DIV, circle::BuiltinOptions_FloorDivOptions,
+ CreateFloorDivOptions(builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleFloorMod *node)
+{
+ export_simple(node, circle::BuiltinOperator_FLOOR_MOD, circle::BuiltinOptions_FloorModOptions,
+ CreateFloorModOptions(builder).Union());
}
void OperationExporter::visit(luci::CircleFullyConnected *node)
{
- uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_FULLY_CONNECTED);
+ export_simple(
+ node, circle::BuiltinOperator_FULLY_CONNECTED, circle::BuiltinOptions_FullyConnectedOptions,
+ CreateFullyConnectedOptions(builder, to_circle_actfunc(node->fusedActivationFunction()))
+ .Union());
+}
- // Make input, output and options for operator
- std::vector<int32_t> inputs_vec{get_tensor_index(node->input()),
- get_tensor_index(node->weights()),
- get_tensor_index(node->bias())};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
- auto inputs = builder.CreateVector(inputs_vec);
- auto outputs = builder.CreateVector(outputs_vec);
- auto options =
- CreateFullyConnectedOptions(builder, to_circle_actfunc(node->fusedActivationFunction()));
+void OperationExporter::visit(luci::CircleGather *node)
+{
+ export_simple(node, circle::BuiltinOperator_GATHER, circle::BuiltinOptions_GatherOptions,
+ CreateGatherOptions(builder, node->axis()).Union());
+}
- // Make FULLY_CONNECTED operator
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
- circle::BuiltinOptions_FullyConnectedOptions, options.Union());
- gd._operators.push_back(op_offset);
+void OperationExporter::visit(luci::CircleGatherNd *node)
+{
+ export_simple(node, circle::BuiltinOperator_GATHER_ND, circle::BuiltinOptions_GatherNdOptions,
+ CreateGatherNdOptions(builder).Union());
}
-void OperationExporter::visit(luci::CircleLogicalNot *node)
+void OperationExporter::visit(luci::CircleGreater *node)
{
- uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_LOGICAL_NOT);
+ export_simple(node, circle::BuiltinOperator_GREATER, circle::BuiltinOptions_GreaterOptions,
+ CreateGreaterOptions(builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleGreaterEqual *node)
+{
+ export_simple(node, circle::BuiltinOperator_GREATER_EQUAL,
+ circle::BuiltinOptions_GreaterEqualOptions,
+ CreateGreaterEqualOptions(builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleIf *node)
+{
+ auto if_outs = loco::succs(node);
+ assert(if_outs.size() == node->output_count());
+
+ uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_IF, node->op_version());
+ std::vector<int32_t> inputs_vec;
+ std::vector<int32_t> outputs_vec;
+
+ inputs_vec.push_back(get_tensor_index(node->cond()));
+ for (uint32_t idx = 0; idx < node->input_count(); ++idx)
+ inputs_vec.push_back(get_tensor_index(node->input(idx)));
+
+ for (uint32_t idx = 0; idx < node->output_count(); ++idx)
+ {
+ // store in order of index
+ bool found = false;
+ for (auto out : if_outs)
+ {
+ auto if_out = loco::must_cast<luci::CircleIfOut *>(out);
+ if (if_out->index() == static_cast<int32_t>(idx))
+ {
+ outputs_vec.push_back(get_tensor_index(if_out));
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ INTERNAL_EXN("Invalid CircleIf output");
+ }
+ }
- // Make input, output and options for operator
- std::vector<int32_t> inputs_vec{get_tensor_index(node->x())};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
auto inputs = builder.CreateVector(inputs_vec);
auto outputs = builder.CreateVector(outputs_vec);
- auto options = CreateLogicalNotOptions(builder);
-
- // Make LOGICAL_NOT operator
+ auto options = CreateIfOptions(builder, node->then_branch(), node->else_branch());
auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
- circle::BuiltinOptions_LogicalNotOptions, options.Union());
+ circle::BuiltinOptions_IfOptions, options.Union());
gd._operators.push_back(op_offset);
}
+void OperationExporter::visit(luci::CircleL2Normalize *node)
+{
+ export_simple(
+ node, circle::BuiltinOperator_L2_NORMALIZATION, circle::BuiltinOptions_L2NormOptions,
+ CreateL2NormOptions(builder, to_circle_actfunc(node->fusedActivationFunction())).Union());
+}
+
+void OperationExporter::visit(luci::CircleL2Pool2D *node)
+{
+ export_pool_2d<luci::CircleL2Pool2D>(node, circle::BuiltinOperator_L2_POOL_2D);
+}
+
+void OperationExporter::visit(luci::CircleLeakyRelu *node)
+{
+ export_simple(node, circle::BuiltinOperator_LEAKY_RELU, circle::BuiltinOptions_LeakyReluOptions,
+ CreateLeakyReluOptions(builder, node->alpha()).Union());
+}
+
+void OperationExporter::visit(luci::CircleLess *node)
+{
+ export_simple(node, circle::BuiltinOperator_LESS, circle::BuiltinOptions_LessOptions,
+ CreateLessOptions(builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleLessEqual *node)
+{
+ export_simple(node, circle::BuiltinOperator_LESS_EQUAL, circle::BuiltinOptions_LessEqualOptions,
+ CreateLessEqualOptions(builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleLocalResponseNormalization *node)
+{
+ export_simple(node, circle::BuiltinOperator_LOCAL_RESPONSE_NORMALIZATION,
+ circle::BuiltinOptions_LocalResponseNormalizationOptions,
+ CreateLocalResponseNormalizationOptions(builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleLog *node)
+{
+ export_simple(node, circle::BuiltinOperator_LOG);
+}
+
+void OperationExporter::visit(luci::CircleLogicalAnd *node)
+{
+ export_simple(node, circle::BuiltinOperator_LOGICAL_AND, circle::BuiltinOptions_LogicalAndOptions,
+ CreateLogicalAndOptions(builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleLogicalNot *node)
+{
+ export_simple(node, circle::BuiltinOperator_LOGICAL_NOT, circle::BuiltinOptions_LogicalNotOptions,
+ CreateLogicalNotOptions(builder).Union());
+}
+
void OperationExporter::visit(luci::CircleLogicalOr *node)
{
- uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_LOGICAL_OR);
+ export_simple(node, circle::BuiltinOperator_LOGICAL_OR, circle::BuiltinOptions_LogicalOrOptions,
+ CreateLogicalOrOptions(builder).Union());
+}
- // Make input, output and options for operator
- std::vector<int32_t> inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
- auto inputs = builder.CreateVector(inputs_vec);
- auto outputs = builder.CreateVector(outputs_vec);
- auto options = CreateLogicalOrOptions(builder);
+void OperationExporter::visit(luci::CircleLogistic *node)
+{
+ export_simple(node, circle::BuiltinOperator_LOGISTIC);
+}
- // Make LOGICAL_OR operator
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
- circle::BuiltinOptions_LogicalOrOptions, options.Union());
- gd._operators.push_back(op_offset);
+void OperationExporter::visit(luci::CircleLogSoftmax *node)
+{
+ export_simple(node, circle::BuiltinOperator_LOG_SOFTMAX, circle::BuiltinOptions_LogSoftmaxOptions,
+ CreateLogSoftmaxOptions(builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleMatrixDiag *node)
+{
+ export_simple(node, circle::BuiltinOperator_MATRIX_DIAG, circle::BuiltinOptions_MatrixDiagOptions,
+ CreateMatrixDiagOptions(builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleMatrixSetDiag *node)
+{
+ export_simple(node, circle::BuiltinOperator_MATRIX_SET_DIAG,
+ circle::BuiltinOptions_MatrixSetDiagOptions,
+ CreateMatrixSetDiagOptions(builder).Union());
}
void OperationExporter::visit(luci::CircleMaximum *node)
{
- uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_MAXIMUM);
- std::vector<int32_t> inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
- auto inputs = builder.CreateVector(inputs_vec);
- auto outputs = builder.CreateVector(outputs_vec);
- auto options = CreateMaximumMinimumOptions(builder);
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
- circle::BuiltinOptions_MaximumMinimumOptions, options.Union());
- gd._operators.push_back(op_offset);
+ export_simple(node, circle::BuiltinOperator_MAXIMUM, circle::BuiltinOptions_MaximumMinimumOptions,
+ CreateMaximumMinimumOptions(builder).Union());
}
void OperationExporter::visit(luci::CircleMaxPool2D *node)
@@ -362,259 +662,568 @@ void OperationExporter::visit(luci::CircleMaxPool2D *node)
void OperationExporter::visit(luci::CircleMean *node)
{
- uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_MEAN);
- std::vector<int32_t> inputs_vec{get_tensor_index(node->input()),
- get_tensor_index(node->reduction_indices())};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
- auto inputs = builder.CreateVector(inputs_vec);
- auto outputs = builder.CreateVector(outputs_vec);
- auto options = CreateReducerOptions(builder, node->keep_dims());
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
- circle::BuiltinOptions_ReducerOptions, options.Union());
- gd._operators.push_back(op_offset);
+ export_simple(node, circle::BuiltinOperator_MEAN, circle::BuiltinOptions_ReducerOptions,
+ CreateReducerOptions(builder, node->keep_dims()).Union());
+}
+
+void OperationExporter::visit(luci::CircleMinimum *node)
+{
+ export_simple(node, circle::BuiltinOperator_MINIMUM, circle::BuiltinOptions_MaximumMinimumOptions,
+ CreateMaximumMinimumOptions(builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleMirrorPad *node)
+{
+ export_simple(node, circle::BuiltinOperator_MIRROR_PAD, circle::BuiltinOptions_MirrorPadOptions,
+ CreateMirrorPadOptions(builder, to_circle_mirrorpadmode(node->mode())).Union());
}
void OperationExporter::visit(luci::CircleMul *node)
{
- uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_MUL);
- std::vector<int32_t> inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
- auto inputs = builder.CreateVector(inputs_vec);
- auto outputs = builder.CreateVector(outputs_vec);
- auto options = CreateMulOptions(builder, to_circle_actfunc(node->fusedActivationFunction()));
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
- circle::BuiltinOptions_MulOptions, options.Union());
- gd._operators.push_back(op_offset);
+ export_simple(
+ node, circle::BuiltinOperator_MUL, circle::BuiltinOptions_MulOptions,
+ CreateMulOptions(builder, to_circle_actfunc(node->fusedActivationFunction())).Union());
}
-void OperationExporter::visit(luci::CirclePack *node)
+void OperationExporter::visit(luci::CircleNeg *node)
{
- uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_PACK);
- std::vector<int32_t> inputs_vec;
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ export_simple(node, circle::BuiltinOperator_NEG, circle::BuiltinOptions_NegOptions,
+ CreateNegOptions(builder).Union());
+}
- for (uint32_t i = 0; i < node->values_count(); ++i)
- inputs_vec.push_back(get_tensor_index(node->values(i)));
+void OperationExporter::visit(luci::CircleNotEqual *node)
+{
+ export_simple(node, circle::BuiltinOperator_NOT_EQUAL, circle::BuiltinOptions_NotEqualOptions,
+ CreateNotEqualOptions(builder).Union());
+}
- auto inputs = builder.CreateVector(inputs_vec);
- auto outputs = builder.CreateVector(outputs_vec);
- auto options = CreatePackOptions(builder, node->values_count(), node->axis());
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
- circle::BuiltinOptions_PackOptions, options.Union());
- gd._operators.push_back(op_offset);
+void OperationExporter::visit(luci::CircleOneHot *node)
+{
+ export_simple(node, circle::BuiltinOperator_ONE_HOT, circle::BuiltinOptions_OneHotOptions,
+ CreateOneHotOptions(builder, node->axis()).Union());
+}
+
+void OperationExporter::visit(luci::CirclePack *node)
+{
+ export_simple(node, circle::BuiltinOperator_PACK, circle::BuiltinOptions_PackOptions,
+ CreatePackOptions(builder, node->values_count(), node->axis()).Union());
}
void OperationExporter::visit(luci::CirclePad *node)
{
- uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_PAD);
- std::vector<int32_t> inputs_vec{get_tensor_index(node->input()),
- get_tensor_index(node->paddings())};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
- auto inputs = builder.CreateVector(inputs_vec);
- auto outputs = builder.CreateVector(outputs_vec);
- auto options = CreatePadOptions(builder);
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
- circle::BuiltinOptions_PadOptions, options.Union());
- gd._operators.push_back(op_offset);
+ export_simple(node, circle::BuiltinOperator_PAD, circle::BuiltinOptions_PadOptions,
+ CreatePadOptions(builder).Union());
+}
+
+void OperationExporter::visit(luci::CirclePow *node)
+{
+ export_simple(node, circle::BuiltinOperator_POW, circle::BuiltinOptions_PowOptions,
+ CreatePowOptions(builder).Union());
+}
+
+void OperationExporter::visit(luci::CirclePRelu *node)
+{
+ export_simple(node, circle::BuiltinOperator_PRELU);
+}
+
+void OperationExporter::visit(luci::CircleRange *node)
+{
+ export_simple(node, circle::BuiltinOperator_RANGE, circle::BuiltinOptions_RangeOptions,
+ CreateRangeOptions(builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleRank *node)
+{
+ export_simple(node, circle::BuiltinOperator_RANK, circle::BuiltinOptions_RankOptions,
+ CreateRankOptions(builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleReduceAny *node)
+{
+ export_simple(node, circle::BuiltinOperator_REDUCE_ANY, circle::BuiltinOptions_ReducerOptions,
+ CreateReducerOptions(builder, node->keep_dims()).Union());
+}
+
+void OperationExporter::visit(luci::CircleReduceMax *node)
+{
+ export_simple(node, circle::BuiltinOperator_REDUCE_MAX, circle::BuiltinOptions_ReducerOptions,
+ CreateReducerOptions(builder, node->keep_dims()).Union());
+}
+
+void OperationExporter::visit(luci::CircleReduceMin *node)
+{
+ export_simple(node, circle::BuiltinOperator_REDUCE_MIN, circle::BuiltinOptions_ReducerOptions,
+ CreateReducerOptions(builder, node->keep_dims()).Union());
+}
+
+void OperationExporter::visit(luci::CircleReduceProd *node)
+{
+ export_simple(node, circle::BuiltinOperator_REDUCE_PROD, circle::BuiltinOptions_ReducerOptions,
+ CreateReducerOptions(builder, node->keep_dims()).Union());
}
void OperationExporter::visit(luci::CircleRelu *node)
{
- uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_RELU);
- std::vector<int32_t> inputs_vec{get_tensor_index(node->features())};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
- auto inputs = builder.CreateVector(inputs_vec);
- auto outputs = builder.CreateVector(outputs_vec);
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs);
- gd._operators.push_back(op_offset);
+ export_simple(node, circle::BuiltinOperator_RELU);
}
void OperationExporter::visit(luci::CircleRelu6 *node)
{
- uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_RELU6);
- std::vector<int32_t> inputs_vec{get_tensor_index(node->features())};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
- auto inputs = builder.CreateVector(inputs_vec);
- auto outputs = builder.CreateVector(outputs_vec);
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs);
- gd._operators.push_back(op_offset);
+ export_simple(node, circle::BuiltinOperator_RELU6);
}
-void OperationExporter::visit(luci::CircleReshape *node)
+void OperationExporter::visit(luci::CircleReluN1To1 *node)
{
- uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_RESHAPE);
-
- // Create inputs and outputs.
- std::vector<int32_t> inputs_vec{get_tensor_index(node->tensor()),
- get_tensor_index(node->shape())};
- std::vector<int32_t> outputs_vec{get_tensor_index(node)};
- auto inputs = builder.CreateVector(inputs_vec);
- auto outputs = builder.CreateVector(outputs_vec);
+ export_simple(node, circle::BuiltinOperator_RELU_N1_TO_1);
+}
- // Create options.
+void OperationExporter::visit(luci::CircleReshape *node)
+{
auto new_shape = builder.CreateVector<int32_t>(
node->newShape()->rank(), [node](size_t i) { return node->newShape()->dim(i); });
- auto options = CreateReshapeOptions(builder, new_shape);
- // Create the operator.
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
- circle::BuiltinOptions_ReshapeOptions, options.Union());
- gd._operators.push_back(op_offset);
+ export_simple(node, circle::BuiltinOperator_RESHAPE, circle::BuiltinOptions_ReshapeOptions,
+ CreateReshapeOptions(builder, new_shape).Union());
}
-void OperationExporter::visit(luci::CircleRsqrt *node)
+void OperationExporter::visit(luci::CircleResizeBilinear *node)
{
- uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_RSQRT);
- std::vector<int32_t> inputs_vec{get_tensor_index(node->x())};
+ export_simple(
+ node, circle::BuiltinOperator_RESIZE_BILINEAR, circle::BuiltinOptions_ResizeBilinearOptions,
+ CreateResizeBilinearOptions(builder, node->align_corners(), node->half_pixel_centers())
+ .Union());
+}
+
+void OperationExporter::visit(luci::CircleResizeNearestNeighbor *node)
+{
+ export_simple(node, circle::BuiltinOperator_RESIZE_NEAREST_NEIGHBOR,
+ circle::BuiltinOptions_ResizeNearestNeighborOptions,
+ CreateResizeNearestNeighborOptions(builder, node->align_corners()).Union());
+}
+
+void OperationExporter::visit(luci::CircleReverseSequence *node)
+{
+ export_simple(
+ node, circle::BuiltinOperator_REVERSE_SEQUENCE, circle::BuiltinOptions_ReverseSequenceOptions,
+ CreateReverseSequenceOptions(builder, node->seq_axis(), node->batch_axis()).Union());
+}
+
+void OperationExporter::visit(luci::CircleReverseV2 *node)
+{
+ uint32_t op_idx =
+ md.registerBuiltinOpcode(circle::BuiltinOperator_REVERSE_V2, node->op_version());
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->tensor()), get_tensor_index(node->axis())};
std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
auto inputs = builder.CreateVector(inputs_vec);
auto outputs = builder.CreateVector(outputs_vec);
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs);
+ auto options = CreateReverseV2Options(builder);
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_ReverseSequenceOptions, options.Union());
gd._operators.push_back(op_offset);
}
+void OperationExporter::visit(luci::CircleRound *node)
+{
+ export_simple(node, circle::BuiltinOperator_ROUND);
+}
+
+void OperationExporter::visit(luci::CircleRsqrt *node)
+{
+ export_simple(node, circle::BuiltinOperator_RSQRT);
+}
+
+void OperationExporter::visit(luci::CircleScatterNd *node)
+{
+ export_simple(node, circle::BuiltinOperator_SCATTER_ND, circle::BuiltinOptions_ScatterNdOptions,
+ CreateScatterNdOptions(builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleSegmentSum *node)
+{
+ export_simple(node, circle::BuiltinOperator_SEGMENT_SUM, circle::BuiltinOptions_SegmentSumOptions,
+ CreateSegmentSumOptions(builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleSelect *node)
+{
+ export_simple(node, circle::BuiltinOperator_SELECT, circle::BuiltinOptions_SelectOptions,
+ CreateSelectOptions(builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleSelectV2 *node)
+{
+ export_simple(node, circle::BuiltinOperator_SELECT_V2, circle::BuiltinOptions_SelectV2Options,
+ CreateSelectV2Options(builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleShape *node)
+{
+ export_simple(node, circle::BuiltinOperator_SHAPE, circle::BuiltinOptions_ShapeOptions,
+ CreateShapeOptions(builder, to_circle_tensortype(node->out_type())).Union());
+}
+
+void OperationExporter::visit(luci::CircleSin *node)
+{
+ export_simple(node, circle::BuiltinOperator_SIN);
+}
+
+void OperationExporter::visit(luci::CircleSlice *node)
+{
+ export_simple(node, circle::BuiltinOperator_SLICE, circle::BuiltinOptions_SliceOptions,
+ CreateSliceOptions(builder).Union());
+}
+
void OperationExporter::visit(luci::CircleSoftmax *node)
{
- uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_SOFTMAX);
- std::vector<int32_t> inputs_vec{get_tensor_index(node->logits())};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ export_simple(node, circle::BuiltinOperator_SOFTMAX, circle::BuiltinOptions_SoftmaxOptions,
+ CreateSoftmaxOptions(builder, node->beta()).Union());
+}
+
+void OperationExporter::visit(luci::CircleSpaceToBatchND *node)
+{
+ export_simple(node, circle::BuiltinOperator_SPACE_TO_BATCH_ND,
+ circle::BuiltinOptions_SpaceToBatchNDOptions,
+ CreateSpaceToBatchNDOptions(builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleSpaceToDepth *node)
+{
+ export_simple(node, circle::BuiltinOperator_SPACE_TO_DEPTH,
+ circle::BuiltinOptions_SpaceToDepthOptions,
+ CreateSpaceToDepthOptions(builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleSparseToDense *node)
+{
+ export_simple(node, circle::BuiltinOperator_SPARSE_TO_DENSE,
+ circle::BuiltinOptions_SparseToDenseOptions,
+ CreateSparseToDenseOptions(builder, node->validate_indices()).Union());
+}
+
+void OperationExporter::visit(luci::CircleSplit *node)
+{
+ auto split_outs = loco::succs(node);
+ assert(int32_t(split_outs.size()) == node->num_split());
+
+ uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_SPLIT, node->op_version());
+ // NOTE BuiltinOperator_SPLIT input is placed at second position
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->split_dim()),
+ get_tensor_index(node->input())};
+ std::vector<int32_t> outputs_vec;
+
+ for (int32_t index = 0; index < node->num_split(); index++)
+ {
+ // store in order of index
+ bool found = false;
+ for (auto out : split_outs)
+ {
+ auto split_out = loco::must_cast<luci::CircleSplitOut *>(out);
+ if (split_out->index() == index)
+ {
+ outputs_vec.push_back(get_tensor_index(split_out));
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ INTERNAL_EXN("Invalid Split output");
+ }
+ }
+
auto inputs = builder.CreateVector(inputs_vec);
auto outputs = builder.CreateVector(outputs_vec);
- auto options = CreateSoftmaxOptions(builder, node->beta());
+ auto options = CreateSplitOptions(builder, node->num_split());
auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
- circle::BuiltinOptions_SoftmaxOptions, options.Union());
+ circle::BuiltinOptions_SplitOptions, options.Union());
gd._operators.push_back(op_offset);
}
-void OperationExporter::visit(luci::CircleSqrt *node)
+void OperationExporter::visit(luci::CircleSplitV *node)
{
- uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_SQRT);
- std::vector<int32_t> inputs_vec{get_tensor_index(node->x())};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto split_outs = loco::succs(node);
+ assert(int32_t(split_outs.size()) == node->num_split());
+
+ uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_SPLIT_V, node->op_version());
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->input()),
+ get_tensor_index(node->size_splits()),
+ get_tensor_index(node->split_dim())};
+ std::vector<int32_t> outputs_vec;
+
+ for (int32_t index = 0; index < node->num_split(); index++)
+ {
+ // store in order of index
+ bool found = false;
+ for (auto out : split_outs)
+ {
+ auto split_out = loco::must_cast<luci::CircleSplitVOut *>(out);
+ if (split_out->index() == index)
+ {
+ outputs_vec.push_back(get_tensor_index(split_out));
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ INTERNAL_EXN("Invalid SplitV output");
+ }
+ }
+
auto inputs = builder.CreateVector(inputs_vec);
auto outputs = builder.CreateVector(outputs_vec);
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs);
+ auto options = CreateSplitVOptions(builder, node->num_split());
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_SplitVOptions, options.Union());
gd._operators.push_back(op_offset);
}
+void OperationExporter::visit(luci::CircleSqrt *node)
+{
+ export_simple(node, circle::BuiltinOperator_SQRT);
+}
+
+void OperationExporter::visit(luci::CircleSquare *node)
+{
+ export_simple(node, circle::BuiltinOperator_SQUARE, circle::BuiltinOptions_SquareOptions,
+ CreateSquareOptions(builder).Union());
+}
+
void OperationExporter::visit(luci::CircleSquaredDifference *node)
{
- uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_SQUARED_DIFFERENCE);
- std::vector<int32_t> inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
- auto inputs = builder.CreateVector(inputs_vec);
- auto outputs = builder.CreateVector(outputs_vec);
- auto options = CreateSquaredDifferenceOptions(builder);
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
- circle::BuiltinOptions_SquaredDifferenceOptions, options.Union());
- gd._operators.push_back(op_offset);
+ export_simple(node, circle::BuiltinOperator_SQUARED_DIFFERENCE,
+ circle::BuiltinOptions_SquaredDifferenceOptions,
+ CreateSquaredDifferenceOptions(builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleSqueeze *node)
+{
+ auto squeeze_dims = builder.CreateVector<int32_t>(node->squeeze_dims());
+ export_simple(node, circle::BuiltinOperator_SQUEEZE, circle::BuiltinOptions_SqueezeOptions,
+ CreateSqueezeOptions(builder, squeeze_dims).Union());
+}
+
+void OperationExporter::visit(luci::CircleStridedSlice *node)
+{
+ export_simple(node, circle::BuiltinOperator_STRIDED_SLICE,
+ circle::BuiltinOptions_StridedSliceOptions,
+ CreateStridedSliceOptions(builder, node->begin_mask(), node->end_mask(),
+ node->ellipsis_mask(), node->new_axis_mask(),
+ node->shrink_axis_mask())
+ .Union());
}
void OperationExporter::visit(luci::CircleSub *node)
{
- uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_SUB);
- std::vector<int32_t> inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
- auto inputs = builder.CreateVector(inputs_vec);
- auto outputs = builder.CreateVector(outputs_vec);
- auto options = CreateSubOptions(builder, to_circle_actfunc(node->fusedActivationFunction()));
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
- circle::BuiltinOptions_SubOptions, options.Union());
- gd._operators.push_back(op_offset);
+ export_simple(
+ node, circle::BuiltinOperator_SUB, circle::BuiltinOptions_SubOptions,
+ CreateSubOptions(builder, to_circle_actfunc(node->fusedActivationFunction())).Union());
}
-// TODO CircleTanh
+void OperationExporter::visit(luci::CircleSum *node)
+{
+ export_simple(node, circle::BuiltinOperator_SUM, circle::BuiltinOptions_ReducerOptions,
+ CreateReducerOptions(builder, node->keep_dims()).Union());
+}
-void OperationExporter::visit(luci::CircleTranspose *node)
+void OperationExporter::visit(luci::CircleTanh *node)
{
- uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_TRANSPOSE);
- std::vector<int32_t> inputs_vec{get_tensor_index(node->arg(0)), get_tensor_index(node->arg(1))};
- std::vector<int32_t> outputs_vec{get_tensor_index(node)};
+ export_simple(node, circle::BuiltinOperator_TANH);
+}
+
+void OperationExporter::visit(luci::CircleTile *node)
+{
+ export_simple(node, circle::BuiltinOperator_TILE, circle::BuiltinOptions_TileOptions,
+ CreateTileOptions(builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleTopKV2 *node)
+{
+ auto topkv2_outs = loco::succs(node);
+ int outs_count = int32_t(topkv2_outs.size());
+ assert(outs_count == 2);
+
+ uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_TOPK_V2, node->op_version());
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->input()), get_tensor_index(node->k())};
+ std::vector<int32_t> outputs_vec;
+
+ for (int32_t index = 0; index < outs_count; index++)
+ {
+ // store in order of index
+ bool found = false;
+ for (auto out : topkv2_outs)
+ {
+ auto topkv2_out = loco::must_cast<luci::CircleTopKV2Out *>(out);
+ if (topkv2_out->index() == index)
+ {
+ outputs_vec.push_back(get_tensor_index(topkv2_out));
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ INTERNAL_EXN("Invalid TopKV2 output");
+ }
+ }
auto inputs = builder.CreateVector(inputs_vec);
auto outputs = builder.CreateVector(outputs_vec);
- auto options = CreateTransposeOptions(builder);
-
- auto op_offset =
- CreateOperator(builder, op_idx, inputs, outputs,
- circle::BuiltinOptions::BuiltinOptions_TransposeOptions, options.Union());
+ auto options = CreateTopKV2Options(builder);
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_TopKV2Options, options.Union());
gd._operators.push_back(op_offset);
}
+void OperationExporter::visit(luci::CircleTranspose *node)
+{
+ export_simple(node, circle::BuiltinOperator_TRANSPOSE, circle::BuiltinOptions_TransposeOptions,
+ CreateTransposeOptions(builder).Union());
+}
+
void OperationExporter::visit(luci::CircleTransposeConv *node)
{
- uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_TRANSPOSE_CONV);
+ export_simple(node, circle::BuiltinOperator_TRANSPOSE_CONV,
+ circle::BuiltinOptions_TransposeConvOptions,
+ CreateTransposeConvOptions(builder, getOpPadding(node->padding()),
+ node->stride()->w(), node->stride()->h())
+ .Union());
+}
+
+void OperationExporter::visit(luci::CircleUnpack *node)
+{
+ LOGGER(l);
+ auto settings = luci::UserSettings::settings();
+
+ auto unpack_outs = loco::succs(node);
+ // NOTE real models may not use all of the outputs
+ if (static_cast<int32_t>(unpack_outs.size()) != node->num())
+ {
+ if (settings->get(luci::UserSettings::Key::DisableValidation))
+ {
+ WARN(l) << "Warning: export Unpack(" << node->name() << ") 'num' not same as outputs";
+ }
+ else
+ assert(false);
+ }
+
+ uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_UNPACK, node->op_version());
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->value())};
+ std::vector<int32_t> outputs_vec;
+
+ for (int32_t index = 0; index < node->num(); index++)
+ {
+ // store in order of index
+ bool found = false;
+ for (auto out : unpack_outs)
+ {
+ auto unpack_out = loco::must_cast<luci::CircleUnpackOut *>(out);
+ if (unpack_out->index() == index)
+ {
+ outputs_vec.push_back(get_tensor_index(unpack_out));
+ found = true;
+ break;
+ }
+ }
+ // NOTE real models may not use all of the outputs
+ if (!found)
+ {
+ if (settings->get(luci::UserSettings::Key::DisableValidation))
+ {
+ WARN(l) << "Warning: export Unpack(" << node->name() << ") output " << index << " not used";
+ }
+ else
+ assert(false);
+ }
+ }
- // Make input, output and options for operator
- std::vector<int32_t> inputs_vec{get_tensor_index(node->inputSizes()),
- get_tensor_index(node->filter()),
- get_tensor_index(node->outBackprop())};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
auto inputs = builder.CreateVector(inputs_vec);
auto outputs = builder.CreateVector(outputs_vec);
- circle::Padding padding = getOpPadding(node->padding());
- auto options =
- CreateTransposeConvOptions(builder, padding, node->stride()->w(), node->stride()->h());
-
- // Make TRANSPOSE_CONV operator
+ auto options = CreateUnpackOptions(builder, node->num(), node->axis());
auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
- circle::BuiltinOptions_TransposeConvOptions, options.Union());
+ circle::BuiltinOptions_UnpackOptions, options.Union());
gd._operators.push_back(op_offset);
}
-void OperationExporter::visit(luci::CircleInstanceNorm *node)
+void OperationExporter::visit(luci::CircleWhere *node)
{
- uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_INSTANCE_NORM);
- std::vector<int32_t> inputs_vec{get_tensor_index(node->input()), get_tensor_index(node->gamma()),
- get_tensor_index(node->beta())};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ export_simple(node, circle::BuiltinOperator_WHERE, circle::BuiltinOptions_WhereOptions,
+ CreateWhereOptions(builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleWhile *node)
+{
+ auto while_outs = loco::succs(node);
+ assert(while_outs.size() == node->output_count());
+
+ uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_WHILE, node->op_version());
+ std::vector<int32_t> inputs_vec;
+ std::vector<int32_t> outputs_vec;
+
+ for (uint32_t idx = 0; idx < node->input_count(); ++idx)
+ inputs_vec.push_back(get_tensor_index(node->input(idx)));
+
+ for (uint32_t idx = 0; idx < node->output_count(); ++idx)
+ {
+ // store in order of index
+ bool found = false;
+ for (auto out : while_outs)
+ {
+ auto while_out = loco::must_cast<luci::CircleWhileOut *>(out);
+ if (while_out->index() == static_cast<int32_t>(idx))
+ {
+ outputs_vec.push_back(get_tensor_index(while_out));
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ INTERNAL_EXN("Invalid CircleWhile output");
+ }
+ }
+
auto inputs = builder.CreateVector(inputs_vec);
auto outputs = builder.CreateVector(outputs_vec);
- auto options = CreateInstanceNormOptions(builder, node->epsilon(),
- to_circle_actfunc(node->fusedActivationFunction()));
+ auto options = CreateWhileOptions(builder, node->cond_branch(), node->body_branch());
auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
- circle::BuiltinOptions_InstanceNormOptions, options.Union());
+ circle::BuiltinOptions_WhileOptions, options.Union());
gd._operators.push_back(op_offset);
}
-void OperationExporter::visit(luci::CircleEqual *node)
+void OperationExporter::visit(luci::CircleZerosLike *node)
{
- uint32_t opcode_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_EQUAL);
- std::vector<int32_t> inputs{get_tensor_index(node->x()), get_tensor_index(node->y())};
- std::vector<int32_t> outputs{get_tensor_index(node)};
-
- auto fb_inputs = builder.CreateVector(inputs);
- auto fb_outputs = builder.CreateVector(outputs);
+ export_simple(node, circle::BuiltinOperator_ZEROS_LIKE, circle::BuiltinOptions_ZerosLikeOptions,
+ CreateZerosLikeOptions(builder).Union());
+}
- auto options = CreateEqualOptions(builder);
+void OperationExporter::visit(luci::CircleBCQFullyConnected *node)
+{
+ export_simple(node, circle::BuiltinOperator_BCQ_FULLY_CONNECTED,
+ circle::BuiltinOptions_BCQFullyConnectedOptions,
+ CreateBCQFullyConnectedOptions(builder, node->weights_hidden_size(),
+ to_circle_actfunc(node->fusedActivationFunction()))
+ .Union());
+}
- auto op_offset = CreateOperator(builder, opcode_idx, fb_inputs, fb_outputs,
- circle::BuiltinOptions_EqualOptions, options.Union());
+void OperationExporter::visit(luci::CircleBCQGather *node)
+{
+ export_simple(node, circle::BuiltinOperator_BCQ_GATHER, circle::BuiltinOptions_BCQGatherOptions,
+ CreateBCQGatherOptions(builder, node->input_hidden_size(), node->axis()).Union());
+}
- gd._operators.push_back(op_offset);
+void OperationExporter::visit(luci::CircleInstanceNorm *node)
+{
+ export_simple(node, circle::BuiltinOperator_INSTANCE_NORM,
+ circle::BuiltinOptions_InstanceNormOptions,
+ CreateInstanceNormOptions(builder, node->epsilon(),
+ to_circle_actfunc(node->fusedActivationFunction()))
+ .Union());
}
void exportNode(loco::Node *node, flatbuffers::FlatBufferBuilder &builder, SerializedModelData &md,
SerializedGraphData &gd)
{
- // TODO Use explicit tagging to prevent possible mistake
- auto isNoOp = [](loco::Node *node) {
- // If there is only one input and the TensorIndex for the input is same
- // as the TensorIndex of the output then this node is just a dummy node
- if (node->arity() == 1)
- {
- assert(node->arg(0) != nullptr);
- return get_tensor_index(node) == get_tensor_index(node->arg(0));
- }
- return false;
- };
-
- if (isNoOp(node))
- {
- // Skip if a given node is marked as NoOp (op with no effect) before
- return;
- }
-
if (auto circle_node = dynamic_cast<luci::CircleNode *>(node))
{
OperationExporter exporter{builder, md, gd};
diff --git a/compiler/luci/export/src/CircleTensorExporter.cpp b/compiler/luci/export/src/CircleTensorExporter.cpp
index ef9b9d7d9..5cad3920b 100644
--- a/compiler/luci/export/src/CircleTensorExporter.cpp
+++ b/compiler/luci/export/src/CircleTensorExporter.cpp
@@ -15,6 +15,7 @@
*/
#include "CircleTensorExporter.h"
+#include "TypeBridge.h"
#include <luci/IR/CircleNodes.h>
#include <luci/IR/CircleNodeVisitor.h>
@@ -52,6 +53,9 @@ public:
const ShapeDescription &shape(void) const { return _shape; }
void shape(const ShapeDescription &shape) { _shape = shape; }
+ luci::ShapeStatus shape_status(void) const { return _shape_status; }
+ void shape_status(luci::ShapeStatus ss) { _shape_status = ss; }
+
public:
luci::CircleConst *content(void) const { return _content; }
void content(luci::CircleConst *c) { _content = c; }
@@ -62,8 +66,9 @@ public:
private:
std::string _name;
- circle::TensorType _dtype;
- ShapeDescription _shape;
+ circle::TensorType _dtype{circle::TensorType_FLOAT32};
+ ShapeDescription _shape{};
+ luci::ShapeStatus _shape_status{luci::ShapeStatus::UNDEFINED};
luci::CircleConst *_content = nullptr;
luci::CircleQuantParam *_quantparam = nullptr;
@@ -76,30 +81,16 @@ struct NoOpDetector final : public luci::CircleNodeMutableVisitor<bool>
// Input is Virtual but does produce a Tensor
// Output is Virtual that does not produce any Tensor
bool visit(luci::CircleOutput *) final { return true; }
+ bool visit(luci::CircleOutputExclude *) final { return true; }
// Return false by default
bool visit(luci::CircleNode *) final { return false; }
};
-void allocateCircleTensor(CircleNode *node, CircleTensorContext &ctx)
+void allocateCircleTensorInfo(CircleNode *node, CircleTensorContext &ctx)
{
LOGGER(l);
- auto isNoOp = [](loco::Node *node) {
- if (auto circle_node = dynamic_cast<luci::CircleNode *>(node))
- {
- NoOpDetector d;
- return circle_node->accept(&d);
- }
- return false;
- };
-
- if (isNoOp(node))
- {
- set_tensor_index(node, get_tensor_index(node->arg(0)));
- return;
- }
-
auto tensor_index = static_cast<CircleTensorIndex>(ctx.size());
// TODO Use Graph-level metadata for Input & Output
// auto tensor_name = "t_" + std::to_string(tensor_index);
@@ -111,8 +102,10 @@ void allocateCircleTensor(CircleNode *node, CircleTensorContext &ctx)
CircleTensoInfo tensor_info;
tensor_info.name(tensor_name);
- tensor_info.dtype(TypeInference::get(node));
- tensor_info.shape(ShapeInference::get(node));
+ tensor_info.dtype(to_circle_tensortype(luci::node_dtype(node)));
+ if (node->shape_status() == ShapeStatus::VALID)
+ tensor_info.shape(to_shape_description(luci::node_shape(node)));
+ tensor_info.shape_status(node->shape_status());
tensor_info.content(dynamic_cast<luci::CircleConst *>(node));
tensor_info.quantparam(node->quantparam());
@@ -122,6 +115,108 @@ void allocateCircleTensor(CircleNode *node, CircleTensorContext &ctx)
ctx.emplace_back(tensor_info);
}
+class MultiOutputDetector final : public luci::CircleNodeMutableVisitor<bool>
+{
+public:
+ MultiOutputDetector(CircleTensorContext &ctx) : _ctx(ctx) {}
+
+private:
+ void store_outputs(luci::CircleNode *node, uint32_t count)
+ {
+ auto outs = loco::succs(node);
+ assert(outs.size() == count);
+ (void)count; // for unused variable error in release build
+ for (auto out : outs)
+ {
+ auto circle_out = loco::must_cast<luci::CircleNode *>(out);
+ allocateCircleTensorInfo(circle_out, _ctx);
+ }
+ set_tensor_index(node, -1);
+ }
+
+public:
+ bool visit(luci::CircleIfOut *) final { return true; }
+ bool visit(luci::CircleSplitOut *) final { return true; }
+ bool visit(luci::CircleSplitVOut *) final { return true; }
+ bool visit(luci::CircleTopKV2Out *) final { return true; }
+ bool visit(luci::CircleUnpackOut *) final { return true; }
+ bool visit(luci::CircleWhileOut *) final { return true; }
+
+ bool visit(luci::CircleIf *node) final
+ {
+ store_outputs(node, node->output_count());
+ return true;
+ }
+
+ bool visit(luci::CircleSplit *node) final
+ {
+ store_outputs(node, uint32_t(node->num_split()));
+ return true;
+ }
+
+ bool visit(luci::CircleSplitV *node) final
+ {
+ store_outputs(node, uint32_t(node->num_split()));
+ return true;
+ }
+
+ bool visit(luci::CircleTopKV2 *node) final
+ {
+ store_outputs(node, 2);
+ return true;
+ }
+
+ bool visit(luci::CircleUnpack *node) final
+ {
+ store_outputs(node, node->num());
+ return true;
+ }
+
+ bool visit(luci::CircleWhile *node) final
+ {
+ store_outputs(node, node->output_count());
+ return true;
+ }
+
+ // Return false by default
+ bool visit(luci::CircleNode *) final { return false; }
+
+private:
+ CircleTensorContext &_ctx;
+};
+
+void allocateCircleTensor(CircleNode *node, CircleTensorContext &ctx)
+{
+ if (node == nullptr)
+ throw std::runtime_error("allocateCIrcleTensor Failed : node is nullptr");
+
+ auto isNoOp = [](loco::Node *node) {
+ if (auto circle_node = dynamic_cast<luci::CircleNode *>(node))
+ {
+ NoOpDetector d;
+ return circle_node->accept(&d);
+ }
+ return false;
+ };
+
+ if (isNoOp(node))
+ {
+ set_tensor_index(node, -1);
+ return;
+ }
+
+ // TODO revise this when loco supports multiple outputs
+ // NOTE this will store all virtual output tensors and skip for the real node
+ if (auto circle_node = dynamic_cast<luci::CircleNode *>(node))
+ {
+ MultiOutputDetector d(ctx);
+ if (circle_node->accept(&d))
+ return;
+ }
+
+ allocateCircleTensorInfo(node, ctx);
+}
+
} // namespace
namespace
@@ -166,18 +261,22 @@ flatbuffers::Offset<circle::Buffer> encodeOpBufferByDType(FlatBufferBuilder &bui
template <>
flatbuffers::Offset<circle::Buffer> encodeOpBuffer(FlatBufferBuilder &builder, luci::CircleConst *c)
{
- // TODO use switch
- if (c->dtype() == loco::DataType::FLOAT32)
+ switch (c->dtype())
{
- return encodeOpBufferByDType<loco::DataType::FLOAT32>(builder, c);
- }
- else if (c->dtype() == loco::DataType::S32)
- {
- return encodeOpBufferByDType<loco::DataType::S32>(builder, c);
- }
- else if (c->dtype() == loco::DataType::U8)
- {
- return encodeOpBufferByDType<loco::DataType::U8>(builder, c);
+ case loco::DataType::FLOAT32:
+ return encodeOpBufferByDType<loco::DataType::FLOAT32>(builder, c);
+ case loco::DataType::S16:
+ return encodeOpBufferByDType<loco::DataType::S16>(builder, c);
+ case loco::DataType::S32:
+ return encodeOpBufferByDType<loco::DataType::S32>(builder, c);
+ case loco::DataType::S64:
+ return encodeOpBufferByDType<loco::DataType::S64>(builder, c);
+ case loco::DataType::U8:
+ return encodeOpBufferByDType<loco::DataType::U8>(builder, c);
+ case loco::DataType::BOOL:
+ return encodeOpBufferByDType<loco::DataType::BOOL>(builder, c);
+ default:
+ break;
}
INTERNAL_EXN_V("Unsupported datatype", oops::to_uint32(c->dtype()));
@@ -210,7 +309,9 @@ void exportOpDefinedTensor(const CircleTensoInfo &info, FlatBufferBuilder &build
SerializedModelData &md, SerializedGraphData &gd)
{
// Create and register output tensor shape
- auto shape_offset = encodeShape(builder, info.shape());
+ flatbuffers::Offset<Vector<int32_t>> shape_offset;
+ if (info.shape_status() == ShapeStatus::VALID)
+ shape_offset = encodeShape(builder, info.shape());
// encode and register output tensor buffer
auto buffer =
@@ -249,9 +350,21 @@ void exportOpDefinedTensors(loco::Graph *g, FlatBufferBuilder &builder, Serializ
{
CircleTensorContext tensor_ctx;
+ // NOTE There may exist dangle CircleInput that is not visited with postorder_traversal()
+ // All dangle CircleOutput should be visited by postorder_traversal()
+ auto nodes = g->nodes();
+ for (uint32_t n = 0; n < nodes->size(); ++n)
+ {
+ auto node = dynamic_cast<luci::CircleInput *>(nodes->at(n));
+ if (node != nullptr)
+ allocateCircleTensor(node, tensor_ctx);
+ }
+
for (auto node : loco::postorder_traversal(loco::output_nodes(g)))
{
- CircleNode *circle_node = dynamic_cast<luci::CircleNode *>(node);
+ CircleNode *circle_node = loco::must_cast<luci::CircleNode *>(node);
+ if (dynamic_cast<const luci::CircleInput *>(circle_node) != nullptr)
+ continue;
allocateCircleTensor(circle_node, tensor_ctx);
}
diff --git a/compiler/luci/export/src/ProgressReporter.cpp b/compiler/luci/export/src/ProgressReporter.cpp
index ac9c3d9a8..216bd3f2a 100644
--- a/compiler/luci/export/src/ProgressReporter.cpp
+++ b/compiler/luci/export/src/ProgressReporter.cpp
@@ -49,36 +49,36 @@ namespace luci
void ProgressReporter::notify(const logo::PhaseEventInfo<logo::PhaseEvent::PhaseBegin> *)
{
- LOGGER(prime);
+ LOGGER(l);
- INFO(prime) << "==============================================================";
- INFO(prime) << "luci::PhaseRunner<" << to_str(strategy()) << ">";
- INFO(prime) << "Initial graph";
- INFO(prime) << fmt(graph());
+ VERBOSE(l, 4) << "==============================================================";
+ VERBOSE(l, 4) << "luci::PhaseRunner<" << to_str(strategy()) << ">";
+ VERBOSE(l, 4) << "Initial graph";
+ VERBOSE(l, 4) << fmt(graph());
}
void ProgressReporter::notify(const logo::PhaseEventInfo<logo::PhaseEvent::PhaseEnd> *)
{
- LOGGER(prime);
+ LOGGER(l);
- INFO(prime) << "luci::PhaseRunner<" << to_str(strategy()) << "> - done";
+ VERBOSE(l, 4) << "luci::PhaseRunner<" << to_str(strategy()) << "> - done";
}
void ProgressReporter::notify(const logo::PhaseEventInfo<logo::PhaseEvent::PassBegin> *info)
{
- LOGGER(prime);
+ LOGGER(l);
- INFO(prime) << "--------------------------------------------------------------";
- INFO(prime) << "Before " << logo::pass_name(info->pass());
+ VERBOSE(l, 4) << "--------------------------------------------------------------";
+ VERBOSE(l, 4) << "Before " << logo::pass_name(info->pass());
}
void ProgressReporter::notify(const logo::PhaseEventInfo<logo::PhaseEvent::PassEnd> *info)
{
- LOGGER(prime);
+ LOGGER(l);
- INFO(prime) << "After " << logo::pass_name(info->pass())
- << " (changed: " << to_char(info->changed()) << ")";
- INFO(prime) << fmt(graph());
+ VERBOSE(l, 4) << "After " << logo::pass_name(info->pass())
+ << " (changed: " << to_char(info->changed()) << ")";
+ VERBOSE(l, 4) << fmt(graph());
}
} // namespace luci
diff --git a/compiler/luci/export/src/SerializedData.h b/compiler/luci/export/src/SerializedData.h
index 84249653c..251daa0ea 100644
--- a/compiler/luci/export/src/SerializedData.h
+++ b/compiler/luci/export/src/SerializedData.h
@@ -29,8 +29,20 @@ namespace luci
struct OpCode
{
circle::BuiltinOperator opcode;
-
- bool operator==(const OpCode &rhs) const { return opcode == rhs.opcode; }
+ std::string custom_code{""};
+ int32_t version = 1;
+
+ bool operator==(const OpCode &rhs) const
+ {
+ if (opcode == circle::BuiltinOperator_CUSTOM)
+ {
+ return custom_code == rhs.custom_code;
+ }
+ else
+ {
+ return opcode == rhs.opcode;
+ }
+ }
};
} // namespace luci
@@ -53,11 +65,13 @@ namespace luci
*/
struct SubGraphContext
{
+ /// @brief SubGraph name
+ std::string _name;
/// @brief SubGraph input tensor id
std::vector<int32_t> _inputs;
/// @brief SubGraph output tensor id
std::vector<int32_t> _outputs;
- /// @DataFormat for SubGraph
+ /// @brief DataFormat for SubGraph
circle::DataFormat _data_format{circle::DataFormat::DataFormat_CHANNELS_LAST};
};
@@ -68,7 +82,6 @@ struct SerializedModelData final
SerializedModelData(const SerializedModelData &) = delete;
std::unordered_map<OpCode, uint32_t> _operator_codes;
- std::unordered_map<OpCode, std::string> _custom_operator_codes;
std::vector<flatbuffers::Offset<circle::Buffer>> _buffers;
/**
@@ -76,7 +89,7 @@ struct SerializedModelData final
* @param builtin_code
* @return idx of opcode in table of opcodes (see schema)
*/
- uint32_t registerBuiltinOpcode(circle::BuiltinOperator builtin_code);
+ uint32_t registerBuiltinOpcode(circle::BuiltinOperator builtin_code, const int32_t op_version);
uint32_t registerCustomOpcode(const std::string &custom_op);
};
diff --git a/compiler/luci/export/src/TypeBridge.cpp b/compiler/luci/export/src/TypeBridge.cpp
new file mode 100644
index 000000000..9ccd52376
--- /dev/null
+++ b/compiler/luci/export/src/TypeBridge.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TypeBridge.h"
+
+#include "CircleExporterUtils.h"
+
+#include <luci/IR/CircleNodes.h>
+#include <luci/IR/CircleNodeVisitor.h>
+#include <luci/Service/CircleTypeInference.h>
+#include <luci/Service/CircleShapeInference.h>
+
+#include <loco/Service/TypeInference.h>
+#include <loco/Service/ShapeInference.h>
+
+namespace
+{
+
+/**
+ * @brief CopySelector will return condition of copy shape/type inference to node
+ */
+struct CopySelector final : public luci::CircleNodeVisitor<bool>
+{
+ // return false(don't copy) for nodes that provides shape/type from nature
+ bool visit(const luci::CircleInput *) final { return false; }
+ bool visit(const luci::CircleConst *) final { return false; }
+
+ // default is copy attributes
+ bool visit(const luci::CircleNode *) { return true; }
+};
+
+} // namespace
+
+namespace luci
+{
+
+loco::TensorShape node_shape(CircleNode *node)
+{
+ loco::TensorShape shape;
+
+ shape.rank(node->rank());
+ for (uint32_t r = 0; r < node->rank(); ++r)
+ {
+ shape.dim(r) = loco::Dimension(node->dim(r).value());
+ }
+ return shape;
+}
+
+loco::DataType node_dtype(CircleNode *node) { return node->dtype(); }
+
+void copy_shape_dtype(loco::Graph *graph)
+{
+ /**
+ * @note We will iterate all the nodes in the graph to include dangle nodes
+ */
+ auto nodes = graph->nodes();
+ for (uint32_t n = 0; n < nodes->size(); ++n)
+ {
+ auto node = loco::must_cast<luci::CircleNode *>(nodes->at(n));
+
+ CopySelector cs;
+ if (node->accept(&cs))
+ {
+ // NOTE not all nodes have infered shape/dtype: multiple outs may not be
+ // visited when outputs are not used
+ // TODO fix shape inference traversal
+ // NOTE when loco supports multiple outputs in nature this issue should be
+ // resolved also
+
+ if (loco::dtype_known(node))
+ {
+ node->dtype(loco::dtype_get(node));
+ }
+
+ if (loco::shape_known(node))
+ {
+ auto shape = loco::shape_get(node).as<loco::TensorShape>();
+ node->rank(shape.rank());
+ for (uint32_t r = 0; r < shape.rank(); ++r)
+ {
+ node->dim(r) = loco::Dimension(shape.dim(r).value());
+ }
+
+ // ShapeStatus should be update only when the status was UNDEFINED
+ if (node->shape_status() == ShapeStatus::UNDEFINED)
+ node->shape_status(ShapeStatus::VALID);
+ }
+ }
+ }
+}
+
+} // namespace luci
diff --git a/compiler/luci/export/src/TypeBridge.h b/compiler/luci/export/src/TypeBridge.h
new file mode 100644
index 000000000..a63fbce54
--- /dev/null
+++ b/compiler/luci/export/src/TypeBridge.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TYPE_BRIDGE_H__
+#define __TYPE_BRIDGE_H__
+
+#include <luci/IR/CircleNode.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+/**
+ * @brief node_shape() will return loco::TensorShape of CircleNode
+ */
+loco::TensorShape node_shape(CircleNode *node);
+
+/**
+ * @brief node_dtype() will return loco::DataType of CircleNode
+ */
+loco::DataType node_dtype(CircleNode *node);
+
+/**
+ * @brief copy_shape_dtype() will copy shape and dtype inference data to CircleNode
+ */
+void copy_shape_dtype(loco::Graph *graph);
+
+} // namespace luci
+
+#endif // __TYPE_BRIDGE_H__