diff options
Diffstat (limited to 'compiler/luci/export/src')
-rw-r--r-- | compiler/luci/export/src/CircleExporterImpl.cpp | 59 | ||||
-rw-r--r-- | compiler/luci/export/src/CircleExporterUtils.cpp | 35 | ||||
-rw-r--r-- | compiler/luci/export/src/CircleExporterUtils.h | 1 | ||||
-rw-r--r-- | compiler/luci/export/src/CircleOperationExporter.cpp | 1205 | ||||
-rw-r--r-- | compiler/luci/export/src/CircleTensorExporter.cpp | 179 | ||||
-rw-r--r-- | compiler/luci/export/src/ProgressReporter.cpp | 28 | ||||
-rw-r--r-- | compiler/luci/export/src/SerializedData.h | 23 | ||||
-rw-r--r-- | compiler/luci/export/src/TypeBridge.cpp | 105 | ||||
-rw-r--r-- | compiler/luci/export/src/TypeBridge.h | 44 |
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__ |