diff options
Diffstat (limited to 'compiler/luci/export')
18 files changed, 2064 insertions, 0 deletions
diff --git a/compiler/luci/export/CMakeLists.txt b/compiler/luci/export/CMakeLists.txt new file mode 100644 index 000000000..e32eca366 --- /dev/null +++ b/compiler/luci/export/CMakeLists.txt @@ -0,0 +1,29 @@ +file(GLOB_RECURSE SOURCES "src/*.cpp") +# TODO enable tests +#file(GLOB_RECURSE TESTS "src/*.test.cpp") +#list(REMOVE_ITEM SOURCES ${TESTS}) + +add_library(luci_export SHARED ${SOURCES}) +target_include_directories(luci_export PRIVATE src) +target_include_directories(luci_export PUBLIC include) +target_link_libraries(luci_export PRIVATE luci_lang) +target_link_libraries(luci_export PRIVATE luci_service) +target_link_libraries(luci_export PRIVATE luci_pass) +target_link_libraries(luci_export PRIVATE mio_circle) +target_link_libraries(luci_export PRIVATE luci_log) +target_link_libraries(luci_export PRIVATE luci_logex) +target_link_libraries(luci_export PRIVATE nncc_common) +target_link_libraries(luci_export PRIVATE locop) +target_link_libraries(luci_export PRIVATE oops) +install(TARGETS luci_export DESTINATION lib) + +#if(NOT ENABLE_TEST) +# return() +#endif(NOT ENABLE_TEST) +# +#nnas_find_package(GTest REQUIRED) +# +#GTest_AddTest(luci_export_test ${TESTS}) +#target_include_directories(luci_export_test PRIVATE src) +#target_link_libraries(luci_export_test luci_export) +#target_link_libraries(luci_export_test oops) diff --git a/compiler/luci/export/README.md b/compiler/luci/export/README.md new file mode 100644 index 000000000..12b190a2f --- /dev/null +++ b/compiler/luci/export/README.md @@ -0,0 +1,3 @@ +# luci-export + +_luci-export_ provides exporting _loco_ graph of Circle IR to Circle model file diff --git a/compiler/luci/export/include/luci/CircleExporter.h b/compiler/luci/export/include/luci/CircleExporter.h new file mode 100644 index 000000000..0584c623c --- /dev/null +++ b/compiler/luci/export/include/luci/CircleExporter.h @@ -0,0 +1,64 @@ +/* + * 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 __LUCI_CIRCLEEXPORTER_H__ +#define __LUCI_CIRCLEEXPORTER_H__ + +#include <luci/IR/Module.h> + +#include <loco.h> + +#include <memory> + +namespace luci +{ + +class CircleExporter +{ +public: + // This contract class describes the interaction between a exporter and its client. + struct Contract + { + public: + virtual ~Contract() = default; + + public: // Client -> Exporter + // Input Graph (to be exported) + // Exporter expects a loco graph that consists of Circle nodes + virtual loco::Graph *graph(void) const = 0; + + // Input Module (to be exported) + // Exporter expects a luci module that consists of loco graphs + // TODO make this pure virtual + virtual luci::Module *module(void) const; + + public: // Exporter -> Client + // Exporter calls store for export data + // Notice: Please DO NOT STORE ptr and size when implementing this in Client + virtual bool store(const char *ptr, const size_t size) const = 0; + }; + +public: + explicit CircleExporter(); + +public: + // invoke(...) returns false on failure. + bool invoke(Contract *) const; +}; + +} // namespace luci + +#endif // __LUCI_CIRCLEEXPORTER_H__ diff --git a/compiler/luci/export/src/Check.h b/compiler/luci/export/src/Check.h new file mode 100644 index 000000000..e05ec904a --- /dev/null +++ b/compiler/luci/export/src/Check.h @@ -0,0 +1,35 @@ +/* + * 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 __CHECK_H__ +#define __CHECK_H__ + +#include <stdexcept> +#include <cassert> +#include <iostream> + +// TODO Add macro for Release version + +#define LUCI_ASSERT(condition, msg) \ + { \ + if (!(condition)) \ + { \ + std::cerr << "[assert failed] " << (msg) << ". " << std::endl; \ + assert((condition)); \ + } \ + } + +#endif // __CHECK_H__ diff --git a/compiler/luci/export/src/CircleExporter.cpp b/compiler/luci/export/src/CircleExporter.cpp new file mode 100644 index 000000000..125df7802 --- /dev/null +++ b/compiler/luci/export/src/CircleExporter.cpp @@ -0,0 +1,64 @@ +/* + * 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 "luci/CircleExporter.h" +#include "luci/IR/Module.h" +#include "CircleExporterImpl.h" + +#include <oops/InternalExn.h> + +#include <fstream> +#include <memory> + +namespace luci +{ + +// TODO remove this +Module *CircleExporter::Contract::module(void) const { return nullptr; } + +CircleExporter::CircleExporter() +{ + // NOTHING TO DO +} + +bool CircleExporter::invoke(Contract *contract) const +{ + auto module = contract->module(); + if (module != nullptr) + { + CircleExporterImpl impl(module); + + const char *ptr = impl.getBufferPointer(); + const size_t size = impl.getBufferSize(); + + // we just send one time + return contract->store(ptr, size); + } + + auto graph = contract->graph(); + if (graph == nullptr) + return false; + + CircleExporterImpl impl(graph); + + const char *ptr = impl.getBufferPointer(); + const size_t size = impl.getBufferSize(); + + // we just send one time + return contract->store(ptr, size); +} + +} // namespace luci diff --git a/compiler/luci/export/src/CircleExporterImpl.cpp b/compiler/luci/export/src/CircleExporterImpl.cpp new file mode 100644 index 000000000..81109ee62 --- /dev/null +++ b/compiler/luci/export/src/CircleExporterImpl.cpp @@ -0,0 +1,266 @@ +/* + * 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 "CircleExporterImpl.h" +#include "Optimize.h" +#include "CircleTensorExporter.h" +#include "CircleOperationExporter.h" +#include "CircleExporterUtils.h" + +#include <oops/InternalExn.h> +#include <mio/circle/schema_generated.h> +#include <flatbuffers/flatbuffers.h> + +#include <cassert> +#include <unordered_map> +#include <string> +#include <stdexcept> + +namespace +{ + +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 (pull->indexed() && pull->index() == index) + { + return pull; + } + } + } + return nullptr; +} + +luci::CircleOutput *output_node(loco::Graph *g, const loco::GraphOutputIndex &index) +{ + for (uint32_t n = 0; n < g->nodes()->size(); ++n) + { + if (auto push = dynamic_cast<luci::CircleOutput *>(g->nodes()->at(n))) + { + if (push->indexed() && push->index() == index) + { + return push; + } + } + } + return nullptr; +} + +void registerGraphInputTensors(loco::Graph *graph, luci::SubGraphContext &ctx) +{ + for (uint32_t n = 0; n < graph->inputs()->size(); ++n) + { + auto node = input_node(graph, n); + assert(node != nullptr); + ctx._inputs.push_back(luci::get_tensor_index(node)); + } +} + +void registerGraphOutputTensors(loco::Graph *graph, luci::SubGraphContext &ctx) +{ + for (uint32_t n = 0; n < graph->outputs()->size(); ++n) + { + auto push = output_node(graph, n); + assert(push != nullptr); + auto node = push->from(); + assert(node != nullptr); + ctx._outputs.push_back(luci::get_tensor_index(node)); + } +} + +} // namespace + +namespace +{ + +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) +{ + std::vector<Offset<OperatorCode>> operator_codes_vec(opcodes.size()); + for (auto it : opcodes) + { + uint32_t idx = it.second; + if (it.first.opcode != BuiltinOperator_CUSTOM) + { + operator_codes_vec[idx] = CreateOperatorCode(builder, it.first.opcode); + } + else // custom op + { + 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)); + } + } + return builder.CreateVector(operator_codes_vec); +} + +} // namespace + +namespace luci +{ + +using namespace circle; +using namespace flatbuffers; + +CircleExporterImpl::CircleExporterImpl(loco::Graph *graph) { exportGraph(graph); } +CircleExporterImpl::CircleExporterImpl(Module *module) { exportModule(module); } + +::flatbuffers::Offset<::circle::SubGraph> +CircleExporterImpl::exportSubgraph(SerializedGraphData &gd) +{ + auto tensors = _builder.CreateVector(gd._tensors); + auto inputs = _builder.CreateVector(gd._inputs); + auto outputs = _builder.CreateVector(gd._outputs); + auto operators = _builder.CreateVector(gd._operators); + auto df = gd._data_format; + auto subgraph = CreateSubGraph(_builder, tensors, inputs, outputs, operators, df); + return subgraph; +} + +void CircleExporterImpl::exportGraph(loco::Graph *graph) +{ + // do graph optimization + optimize(graph); + + _builder.Clear(); + + SerializedModelData md; + SerializedGraphData gd; + + // This version is taken from comment in fbs + constexpr uint32_t version = 0; + + // TODO set this value properly + gd._data_format = circle::DataFormat::DataFormat_CHANNELS_LAST; + + // prepare model data + prepareModelData(_builder, md); + + // parse graph into SerializedModelData structure + exportOpDefinedTensors(graph, _builder, md, gd); + + // NOTE Invoke these register functions only after each node is annotated with its tensor_index + registerGraphInputTensors(graph, gd); + registerGraphOutputTensors(graph, gd); + + exportNodes(graph, _builder, md, gd); + + // encode operator codes + auto operator_codes = + encodeOperatorCodes(_builder, md._operator_codes, md._custom_operator_codes); + + // Subgraphs + Offset<SubGraph> subgraph = exportSubgraph(gd); + auto subgraphs = _builder.CreateVector(std::vector<Offset<SubGraph>>{subgraph}); + + // Description + std::string description_str = "nnpackage"; + auto description = _builder.CreateString(description_str); + + // create array of buffers + auto buffers = _builder.CreateVector(md._buffers); + + // empty metadata + std::vector<int> metadata_buffer_vec; + auto metadata_buffer = _builder.CreateVector(metadata_buffer_vec); + + // Model + auto model_offset = CreateModel(_builder, version, operator_codes, subgraphs, description, + buffers, metadata_buffer); + FinishModelBuffer(_builder, model_offset); +} + +void CircleExporterImpl::exportModule(Module *module) +{ + assert(module->size() > 0); + // do graph optimization + + SerializedModelData md; + + _builder.Clear(); + + std::vector<flatbuffers::Offset<circle::SubGraph>> subgraph_vec; + + for (size_t g = 0; g < module->size(); ++g) + { + auto graph = module->graph(g); + + optimize(graph); + + SerializedGraphData gd; + + // TODO set this value properly + gd._data_format = circle::DataFormat::DataFormat_CHANNELS_LAST; + + // parse graph into SerializedModelData structure + exportOpDefinedTensors(graph, _builder, md, gd); + + // NOTE Invoke these register functions only after each node is annotated with its tensor_index + registerGraphInputTensors(graph, gd); + registerGraphOutputTensors(graph, gd); + + exportNodes(graph, _builder, md, gd); + + // Subgraphs + Offset<SubGraph> subgraph = exportSubgraph(gd); + subgraph_vec.push_back(subgraph); + } + + 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); + + // Description + std::string description_str = "nnpackage"; + auto description = _builder.CreateString(description_str); + + // create array of buffers + auto buffers = _builder.CreateVector(md._buffers); + + // empty metadata + std::vector<int> metadata_buffer_vec; + auto metadata_buffer = _builder.CreateVector(metadata_buffer_vec); + + // This version is taken from comment in fbs + constexpr uint32_t version = 0; + + // Model + auto model_offset = CreateModel(_builder, version, operator_codes, subgraphs, description, + buffers, metadata_buffer); + FinishModelBuffer(_builder, model_offset); +} + +const char *CircleExporterImpl::getBufferPointer() const +{ + return reinterpret_cast<const char *>(_builder.GetBufferPointer()); +} + +size_t CircleExporterImpl::getBufferSize() const { return _builder.GetSize(); } + +} // namespace luci diff --git a/compiler/luci/export/src/CircleExporterImpl.h b/compiler/luci/export/src/CircleExporterImpl.h new file mode 100644 index 000000000..e5d5b5a00 --- /dev/null +++ b/compiler/luci/export/src/CircleExporterImpl.h @@ -0,0 +1,82 @@ +/* + * 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 __CIRCLE_EXPORTER_IMPL_H__ +#define __CIRCLE_EXPORTER_IMPL_H__ + +#include "luci/CircleExporter.h" +#include "luci/IR/Module.h" + +#include "SerializedData.h" + +#include "SerializedData.h" + +#include <mio/circle/schema_generated.h> + +#include <loco.h> + +namespace luci +{ + +/** + * internal implementation of interface exporter class + */ +class CircleExporterImpl +{ +public: + CircleExporterImpl() = delete; + ~CircleExporterImpl() = default; + + explicit CircleExporterImpl(loco::Graph *graph); + explicit CircleExporterImpl(Module *module); + + /** + * @return pointer to buffer with serialized graph + */ + const char *getBufferPointer() const; + + /** + * @return size of buffer with serialized graph + */ + size_t getBufferSize() const; + +private: + /** + * @brief create Subgraph using data stored in SerializedGraphData + * @param gd information about serializer parts of model + * @return offset in buffer corresponding to serialized subgraph + */ + flatbuffers::Offset<circle::SubGraph> exportSubgraph(SerializedGraphData &gd); + + /** + * @brief root function that writes graph into internal buffer + * @param graph + */ + void exportGraph(loco::Graph *graph); + + /** + * @brief root function that writes Module into internal buffer + * @param module + */ + void exportModule(Module *module); + +private: + flatbuffers::FlatBufferBuilder _builder; +}; + +} // namespace luci + +#endif // __CIRCLE_EXPORTER_IMPL_H__ diff --git a/compiler/luci/export/src/CircleExporterUtils.cpp b/compiler/luci/export/src/CircleExporterUtils.cpp new file mode 100644 index 000000000..1272facb2 --- /dev/null +++ b/compiler/luci/export/src/CircleExporterUtils.cpp @@ -0,0 +1,169 @@ +/* + * 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 "CircleExporterUtils.h" + +#include <oops/InternalExn.h> + +#include <cassert> +#include <memory> + +namespace luci +{ + +circle::ActivationFunctionType to_circle_actfunc(luci::FusedActFunc func) +{ + switch (func) + { + case luci::FusedActFunc::NONE: + return circle::ActivationFunctionType_NONE; + case luci::FusedActFunc::RELU: + return circle::ActivationFunctionType_RELU; + case luci::FusedActFunc::RELU_N1_TO_1: + return circle::ActivationFunctionType_RELU_N1_TO_1; + case luci::FusedActFunc::RELU6: + return circle::ActivationFunctionType_RELU6; + default: + INTERNAL_EXN_V("trying to convert unsupported luci::FusedActFunc", oops::to_uint32(func)); + } +} + +circle::TensorType to_circle_tensortype(loco::DataType type) +{ + switch (type) + { + case loco::DataType::U8: + return circle::TensorType_UINT8; + + case loco::DataType::S8: + return circle::TensorType_INT8; + case loco::DataType::S16: + return circle::TensorType_INT16; + case loco::DataType::S32: + return circle::TensorType_INT32; + case loco::DataType::S64: + return circle::TensorType_INT64; + + case loco::DataType::FLOAT16: + return circle::TensorType_FLOAT16; + case loco::DataType::FLOAT32: + return circle::TensorType_FLOAT32; + + case loco::DataType::BOOL: + return circle::TensorType_BOOL; + + default: + INTERNAL_EXN_V("failed to convert unsupported loco::DataType", oops::to_uint32(type)); + } +} + +} // namespace luci + +namespace luci +{ + +uint32_t SerializedModelData::registerBuiltinOpcode(circle::BuiltinOperator builtin_code) +{ + auto it = _operator_codes.find(OpCode{builtin_code}); + if (it != _operator_codes.end()) + { + return it->second; + } + auto idx = static_cast<uint32_t>(_operator_codes.size()); + _operator_codes.emplace(OpCode{builtin_code}, idx); + return idx; +} + +uint32_t SerializedModelData::registerCustomOpcode(const std::string &custom_op) +{ + circle::BuiltinOperator custom_code = circle::BuiltinOperator_CUSTOM; + auto idx = registerBuiltinOpcode(custom_code); + _custom_operator_codes.emplace(OpCode{custom_code}, custom_op); + return idx; +} + +circle::Padding getOpPadding(const loco::Padding2D *pad, const loco::Stride<2> *stride, + const ShapeDescription &ifm, const ShapeDescription &ofm) +{ + // VALID padding + if (pad->top() == 0 && pad->bottom() == 0 && pad->left() == 0 && pad->right() == 0) + return circle::Padding_VALID; + + // SAME padding + // + // For same padding, by definition, following equation should hold: + // O = floor((I - 1) / S) + 1 + // where input size I, output size O, stride S + // + // NOTE input and output 'feature' map are shape of NHWC + bool same_padding_criterion_1 = + (static_cast<uint32_t>(ofm._dims[1]) == (ifm._dims[1] - 1) / stride->vertical() + 1) && + (static_cast<uint32_t>(ofm._dims[2]) == (ifm._dims[2] - 1) / stride->horizontal() + 1); + + // For same padding, rear padding is same or bigger than front padding by at most 1 + bool same_padding_criterion_2 = + (pad->top() <= pad->bottom()) && (pad->bottom() <= pad->top() + 1) && + (pad->left() <= pad->right()) && (pad->right() <= pad->left() + 1); + + if (same_padding_criterion_1 && same_padding_criterion_2) + return circle::Padding_SAME; + + INTERNAL_EXN("Unsupported padding criteria"); +} + +circle::Padding getOpPadding(const luci::Padding pad) +{ + if (pad == luci::Padding::VALID) + return circle::Padding_VALID; + if (pad == luci::Padding::SAME) + return circle::Padding_SAME; + + INTERNAL_EXN_V("Unsupported luci::Padding", oops::to_uint32(pad)); +} + +namespace +{ + +class CircleTensorIndexAnnotation final : public loco::NodeAnnotation +{ +public: + CircleTensorIndexAnnotation(const CircleTensorIndex &index) : _index{index} + { + // DO NOTHING + } + +public: + const CircleTensorIndex &index(void) const { return _index; } + +private: + CircleTensorIndex _index; +}; + +} // namespace + +void set_tensor_index(loco::Node *node, const CircleTensorIndex &tensor_id) +{ + assert(node->annot<CircleTensorIndexAnnotation>() == nullptr); + node->annot(std::make_unique<CircleTensorIndexAnnotation>(tensor_id)); +} + +CircleTensorIndex get_tensor_index(loco::Node *node) +{ + assert(node->annot<CircleTensorIndexAnnotation>() != nullptr); + return node->annot<CircleTensorIndexAnnotation>()->index(); +} + +} // namespace luci diff --git a/compiler/luci/export/src/CircleExporterUtils.h b/compiler/luci/export/src/CircleExporterUtils.h new file mode 100644 index 000000000..6b970fd3c --- /dev/null +++ b/compiler/luci/export/src/CircleExporterUtils.h @@ -0,0 +1,51 @@ +/* + * 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 __CIRCLE_EXPORTER_UTILS_H__ +#define __CIRCLE_EXPORTER_UTILS_H__ + +#include "SerializedData.h" + +#include <luci/IR/CircleNodes.h> +#include <luci/Service/ShapeDescription.h> + +#include <loco.h> + +#include <mio/circle/schema_generated.h> + +namespace luci +{ + +circle::ActivationFunctionType to_circle_actfunc(luci::FusedActFunc func); +circle::TensorType to_circle_tensortype(loco::DataType type); + +} // namespace luci + +namespace luci +{ + +circle::Padding getOpPadding(const loco::Padding2D *pad, const loco::Stride<2> *stride, + const ShapeDescription &ifm, const ShapeDescription &ofm); +circle::Padding getOpPadding(const luci::Padding pad); + +using CircleTensorIndex = int32_t; + +void set_tensor_index(loco::Node *node, const CircleTensorIndex &tensor_id); +CircleTensorIndex get_tensor_index(loco::Node *node); + +} // namespace luci + +#endif // __CIRCLE_EXPORTER_UTILS_H__ diff --git a/compiler/luci/export/src/CircleOperationExporter.cpp b/compiler/luci/export/src/CircleOperationExporter.cpp new file mode 100644 index 000000000..ad9c7fd4b --- /dev/null +++ b/compiler/luci/export/src/CircleOperationExporter.cpp @@ -0,0 +1,643 @@ +/* + * 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 "CircleOperationExporter.h" +#include "CircleExporterUtils.h" +#include "Check.h" + +#include <luci/IR/CircleNode.h> +#include <luci/IR/CircleNodes.h> +#include <luci/IR/CircleNodeVisitor.h> +#include <luci/Service/CircleShapeInference.h> + +#include <loco/IR/CanonicalNodeVisitor.h> +#include <oops/InternalExn.h> + +#include <flatbuffers/flexbuffers.h> + +using namespace flatbuffers; +using namespace circle; + +namespace +{ + +using namespace luci; + +class OperationExporter final : public luci::CircleNodeMutableVisitor<void>, + public loco::CanonicalNodeMutableVisitor<void> +{ +public: + OperationExporter(FlatBufferBuilder &fbb, SerializedModelData &m, SerializedGraphData &g) + : builder{fbb}, md{m}, gd{g} + { + // DO NOTHING + } + +public: + void visit(luci::CircleAbs *) final; + void visit(luci::CircleAdd *) final; + void visit(luci::CircleArgMax *) final; + void visit(luci::CircleAveragePool2D *) final; + void visit(luci::CircleBatchToSpaceND *) 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::CircleDepthwiseConv2D *) final; + void visit(luci::CircleDiv *) final; + void visit(luci::CircleExp *) final; + void visit(luci::CircleEqual *) final; + void visit(luci::CircleFullyConnected *) final; + void visit(luci::CircleLogicalNot *) final; + void visit(luci::CircleLogicalOr *) final; + void visit(luci::CircleMaximum *) final; + void visit(luci::CircleMaxPool2D *) final; + void visit(luci::CircleMean *) final; + void visit(luci::CircleMul *) final; + void visit(luci::CirclePack *) final; + void visit(luci::CirclePad *) final; + void visit(luci::CircleRelu *) final; + void visit(luci::CircleRelu6 *) final; + void visit(luci::CircleReshape *) final; + void visit(luci::CircleRsqrt *) final; + void visit(luci::CircleSoftmax *) final; + void visit(luci::CircleSqrt *) final; + void visit(luci::CircleSquaredDifference *) final; + void visit(luci::CircleSub *) final; + // TODO CircleTanh + void visit(luci::CircleTranspose *) final; + void visit(luci::CircleTransposeConv *) final; + // Circle only + void visit(luci::CircleInstanceNorm *) final; + // Virtual + void visit(luci::CircleInput *) final {} + void visit(luci::CircleOutput *) final {} + +private: + /** + * @brief Exports CircleMaxPool2D or CircleAveragePool2D + * + * @note CirclePool2D should be one of CircleMaxPool2D or CircleAveragePool2D + */ + template <class CirclePool2D> + void export_pool_2d(CirclePool2D *node, circle::BuiltinOperator builtin_op); + +private: + FlatBufferBuilder &builder; + SerializedModelData &md; + SerializedGraphData &gd; +}; + +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_AVERAGE_POOL_2D, + "Should be MaxPool or AvgPool"); + LUCI_ASSERT(node->padding() != luci::Padding::UNDEFINED, "Padding is not set"); + + uint32_t op_idx = md.registerBuiltinOpcode(builtin_op); + 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); + auto outputs = builder.CreateVector(outputs_vec); + + circle::Padding padding = getOpPadding(node->padding()); + + auto options = CreatePool2DOptions(builder, padding, node->stride()->w(), node->stride()->h(), + node->filter()->w(), node->filter()->h(), + to_circle_actfunc(node->fusedActivationFunction())); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_Pool2DOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(luci::CircleAbs *node) +{ + uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_ABS); + 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_AbsOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +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())}; + 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 = CreateAddOptions(builder, to_circle_actfunc(node->fusedActivationFunction())); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_AddOptions, 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())}; + 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()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(luci::CircleAveragePool2D *node) +{ + export_pool_2d<luci::CircleAveragePool2D>(node, circle::BuiltinOperator_AVERAGE_POOL_2D); +} + +void OperationExporter::visit(luci::CircleConcatenation *node) +{ + uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_CONCATENATION); + 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->numValues(); ++i) + inputs_vec.push_back(get_tensor_index(node->values(i))); + + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateConcatenationOptions(builder, node->axis(), + to_circle_actfunc(node->fusedActivationFunction())); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_ConcatenationOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +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); +} + +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); +} + +void OperationExporter::visit(luci::CircleCos *node) +{ + uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_COS); + + // 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()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(luci::CircleDepthwiseConv2D *node) +{ + uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_DEPTHWISE_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 = CreateDepthwiseConv2DOptions(builder, padding, node->stride()->w(), + node->stride()->h(), node->depthMultiplier(), + to_circle_actfunc(node->fusedActivationFunction())); + + // 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::CircleDiv *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); +} + +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); +} + +void OperationExporter::visit(luci::CircleFullyConnected *node) +{ + uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_FULLY_CONNECTED); + + // 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())); + + // 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::CircleLogicalNot *node) +{ + uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_LOGICAL_NOT); + + // 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 op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_LogicalNotOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(luci::CircleLogicalOr *node) +{ + uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_LOGICAL_OR); + + // 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); + + // 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::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); +} + +void OperationExporter::visit(luci::CircleMaxPool2D *node) +{ + export_pool_2d<luci::CircleMaxPool2D>(node, circle::BuiltinOperator_MAX_POOL_2D); +} + +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); +} + +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); +} + +void OperationExporter::visit(luci::CirclePack *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))}; + + for (uint32_t i = 0; i < node->values_count(); ++i) + inputs_vec.push_back(get_tensor_index(node->values(i))); + + 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::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); +} + +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); +} + +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); +} + +void OperationExporter::visit(luci::CircleReshape *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); + + // Create options. + 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); +} + +void OperationExporter::visit(luci::CircleRsqrt *node) +{ + uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_RSQRT); + 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 op_offset = CreateOperator(builder, op_idx, inputs, outputs); + gd._operators.push_back(op_offset); +} + +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))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateSoftmaxOptions(builder, node->beta()); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_SoftmaxOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(luci::CircleSqrt *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 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); +} + +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); +} + +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); +} + +// TODO CircleTanh + +void OperationExporter::visit(luci::CircleTranspose *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)}; + + 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()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(luci::CircleTransposeConv *node) +{ + uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_TRANSPOSE_CONV); + + // 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 op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_TransposeConvOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(luci::CircleInstanceNorm *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))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateInstanceNormOptions(builder, node->epsilon(), + to_circle_actfunc(node->fusedActivationFunction())); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_InstanceNormOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(luci::CircleEqual *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); + + auto options = CreateEqualOptions(builder); + + auto op_offset = CreateOperator(builder, opcode_idx, fb_inputs, fb_outputs, + circle::BuiltinOptions_EqualOptions, options.Union()); + + gd._operators.push_back(op_offset); +} + +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}; + circle_node->accept(&exporter); + } + else + { + INTERNAL_EXN("Node with unsupported dialect found"); + } +} + +} // namespace + +namespace luci +{ + +void exportNodes(loco::Graph *g, FlatBufferBuilder &builder, SerializedModelData &md, + SerializedGraphData &gd) +{ + for (auto node : loco::postorder_traversal(loco::output_nodes(g))) + { + exportNode(node, builder, md, gd); + } +} + +} // namespace luci diff --git a/compiler/luci/export/src/CircleOperationExporter.h b/compiler/luci/export/src/CircleOperationExporter.h new file mode 100644 index 000000000..de6abfc54 --- /dev/null +++ b/compiler/luci/export/src/CircleOperationExporter.h @@ -0,0 +1,37 @@ +/* + * 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 __CIRCLE_OPERATION_EXPORTER_H__ +#define __CIRCLE_OPERATION_EXPORTER_H__ + +#include "CircleExporterUtils.h" + +#include <loco/IR/Graph.h> + +namespace luci +{ + +/** + * @brief create Operators corresponding to model nodes + * @param nodes container with nodes + * @param gd information about serializer parts of model + */ +void exportNodes(loco::Graph *g, flatbuffers::FlatBufferBuilder &builder, SerializedModelData &md, + SerializedGraphData &gd); + +} // namespace luci + +#endif // __CIRCLE_OPERATION_EXPORTER_H__ diff --git a/compiler/luci/export/src/CircleTensorExporter.cpp b/compiler/luci/export/src/CircleTensorExporter.cpp new file mode 100644 index 000000000..ef9b9d7d9 --- /dev/null +++ b/compiler/luci/export/src/CircleTensorExporter.cpp @@ -0,0 +1,264 @@ +/* + * 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 "CircleTensorExporter.h" + +#include <luci/IR/CircleNodes.h> +#include <luci/IR/CircleNodeVisitor.h> +#include <luci/Service/CircleTypeInference.h> +#include <luci/Service/CircleShapeInference.h> +#include <luci/Log.h> + +#include <loco/IR/Algorithm.h> +#include <loco/IR/CanonicalNode.h> +#include <loco/IR/CanonicalNodeVisitor.h> +#include <loco/IR/DataTypeTraits.h> +#include <oops/InternalExn.h> + +using namespace circle; +using namespace flatbuffers; + +namespace +{ + +using namespace luci; + +class CircleTensoInfo +{ +public: + CircleTensoInfo() = default; + +public: + void name(const std::string &name) { _name = name; } + const std::string &name(void) const { return _name; } + +public: + const circle::TensorType &dtype(void) const { return _dtype; } + void dtype(const circle::TensorType &dtype) { _dtype = dtype; } + + const ShapeDescription &shape(void) const { return _shape; } + void shape(const ShapeDescription &shape) { _shape = shape; } + +public: + luci::CircleConst *content(void) const { return _content; } + void content(luci::CircleConst *c) { _content = c; } + + luci::CircleQuantParam *quantparam(void) const { return _quantparam; } + void quantparam(luci::CircleQuantParam *qp) { _quantparam = qp; } + +private: + std::string _name; + + circle::TensorType _dtype; + ShapeDescription _shape; + + luci::CircleConst *_content = nullptr; + luci::CircleQuantParam *_quantparam = nullptr; +}; + +using CircleTensorContext = std::vector<CircleTensoInfo>; + +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; } + + // Return false by default + bool visit(luci::CircleNode *) final { return false; } +}; + +void allocateCircleTensor(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); + std::string tensor_name = node->name(); + if (tensor_name.empty()) + tensor_name = "t_" + std::to_string(tensor_index); + INFO(l) << "[luci] Tensor for " << tensor_name << ": " << tensor_index << std::endl; + + CircleTensoInfo tensor_info; + + tensor_info.name(tensor_name); + tensor_info.dtype(TypeInference::get(node)); + tensor_info.shape(ShapeInference::get(node)); + + tensor_info.content(dynamic_cast<luci::CircleConst *>(node)); + tensor_info.quantparam(node->quantparam()); + + set_tensor_index(node, tensor_index); + + ctx.emplace_back(tensor_info); +} + +} // namespace + +namespace +{ + +flatbuffers::Offset<Vector<int32_t>> encodeShape(FlatBufferBuilder &builder, + const ShapeDescription &shape) +{ + assert(shape._rank_known && "unknown number of dimensions is not supported"); + return builder.CreateVector(shape._dims); +} + +flatbuffers::Offset<circle::Buffer> encodeOpBuffer(FlatBufferBuilder &builder) +{ + return CreateBuffer(builder); +} + +template <typename NodeT> +flatbuffers::Offset<circle::Buffer> encodeOpBuffer(FlatBufferBuilder &builder, NodeT *) +{ + return CreateBuffer(builder); +} + +template <loco::DataType DT> +flatbuffers::Offset<circle::Buffer> encodeOpBufferByDType(FlatBufferBuilder &builder, + luci::CircleConst *c) +{ + using NativeType = typename loco::DataTypeImpl<DT>::Type; + + std::vector<NativeType> raw_data; + const uint32_t size = c->size<DT>(); + raw_data.reserve(size); + for (uint32_t i = 0; i < size; ++i) + { + raw_data.push_back(c->at<DT>(i)); + } + const size_t raw_size = size * sizeof(NativeType); + auto array_offset = builder.CreateVector(reinterpret_cast<uint8_t *>(raw_data.data()), raw_size); + return CreateBuffer(builder, array_offset); +} + +template <> +flatbuffers::Offset<circle::Buffer> encodeOpBuffer(FlatBufferBuilder &builder, luci::CircleConst *c) +{ + // TODO use switch + if (c->dtype() == loco::DataType::FLOAT32) + { + 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); + } + + INTERNAL_EXN_V("Unsupported datatype", oops::to_uint32(c->dtype())); +} + +flatbuffers::Offset<circle::QuantizationParameters> +encodeQuantizationParameters(FlatBufferBuilder &builder, luci::CircleQuantParam *quantparam) +{ + if (quantparam == nullptr) + return 0; + + flatbuffers::Offset<flatbuffers::Vector<float>> min; + flatbuffers::Offset<flatbuffers::Vector<float>> max; + flatbuffers::Offset<flatbuffers::Vector<float>> scale; + flatbuffers::Offset<flatbuffers::Vector<int64_t>> zero_point; + if (quantparam->min.size() && quantparam->max.size()) + { + min = builder.CreateVector(quantparam->min); + max = builder.CreateVector(quantparam->max); + } + if (quantparam->scale.size() && quantparam->zerop.size()) + { + scale = builder.CreateVector(quantparam->scale); + zero_point = builder.CreateVector(quantparam->zerop); + } + return circle::CreateQuantizationParameters(builder, min, max, scale, zero_point); +} + +void exportOpDefinedTensor(const CircleTensoInfo &info, FlatBufferBuilder &builder, + SerializedModelData &md, SerializedGraphData &gd) +{ + // Create and register output tensor shape + auto shape_offset = encodeShape(builder, info.shape()); + + // encode and register output tensor buffer + auto buffer = + info.content() == nullptr ? encodeOpBuffer(builder) : encodeOpBuffer(builder, info.content()); + + auto quantparam = encodeQuantizationParameters(builder, info.quantparam()); + + auto buffer_id = static_cast<uint32_t>(md._buffers.size()); + md._buffers.push_back(buffer); + + auto name_offset = builder.CreateString(info.name()); + auto tensor_offset = CreateTensor(builder, shape_offset, info.dtype(), buffer_id, name_offset, + quantparam, /*is_variable*/ false); + gd._tensors.push_back(tensor_offset); +} + +} // namespace + +namespace luci +{ + +void prepareModelData(FlatBufferBuilder &builder, SerializedModelData &md) +{ + // add one empty buffer + // note: this follows TFLite + // note: there's a comment in tflite fbs file + // - Note the 0th entry of this array must be an empty buffer (sentinel). + // - This is a convention so that tensors without a buffer can provide 0 as + // - their buffer. + auto buffer = encodeOpBuffer(builder); + md._buffers.push_back(buffer); +} + +void exportOpDefinedTensors(loco::Graph *g, FlatBufferBuilder &builder, SerializedModelData &md, + SerializedGraphData &gd) +{ + CircleTensorContext tensor_ctx; + + for (auto node : loco::postorder_traversal(loco::output_nodes(g))) + { + CircleNode *circle_node = dynamic_cast<luci::CircleNode *>(node); + allocateCircleTensor(circle_node, tensor_ctx); + } + + for (const auto &tensor_info : tensor_ctx) + { + exportOpDefinedTensor(tensor_info, builder, md, gd); + } +} + +} // namespace luci diff --git a/compiler/luci/export/src/CircleTensorExporter.h b/compiler/luci/export/src/CircleTensorExporter.h new file mode 100644 index 000000000..f9d6107b4 --- /dev/null +++ b/compiler/luci/export/src/CircleTensorExporter.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 __CIRCLE_TENSOR_EXPORTER_H__ +#define __CIRCLE_TENSOR_EXPORTER_H__ + +#include "CircleExporterUtils.h" + +#include <loco/IR/Graph.h> + +#include <flatbuffers/flatbuffers.h> + +namespace luci +{ + +/** + * @brief one time preparation for SerializedModelData + */ +void prepareModelData(flatbuffers::FlatBufferBuilder &builder, SerializedModelData &md); + +/** + * @brief create Tensors corresponding to results of all nodes in graph + * @param computational graph + * @param gd information about serialized parts of model + */ +void exportOpDefinedTensors(loco::Graph *g, flatbuffers::FlatBufferBuilder &builder, + SerializedModelData &md, SerializedGraphData &gd); + +} // namespace luci + +#endif // __CIRCLE_TENSOR_EXPORTER_H__ diff --git a/compiler/luci/export/src/Optimize.cpp b/compiler/luci/export/src/Optimize.cpp new file mode 100644 index 000000000..6fa50b564 --- /dev/null +++ b/compiler/luci/export/src/Optimize.cpp @@ -0,0 +1,48 @@ +/* + * 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 "Optimize.h" +#include "ProgressReporter.h" + +#include <luci/Pass/ShapeInferencePass.h> +#include <luci/Pass/TypeInferencePass.h> + +#include <logo/Phase.h> + +#include <memory> + +namespace luci +{ + +void optimize(loco::Graph *g) +{ + logo::Phase phase; + { + // prepare type and shape before optimization + phase.emplace_back(std::make_unique<TypeInferencePass>()); + phase.emplace_back(std::make_unique<ShapeInferencePass>()); + + // TODO add more optimization passes (with a knob) + } + + logo::PhaseRunner<logo::PhaseStrategy::Restart> phase_runner{g}; + + ProgressReporter prog(g, logo::PhaseStrategy::Restart); + phase_runner.attach(&prog); + phase_runner.run(phase); +} + +} // namespace luci diff --git a/compiler/luci/export/src/Optimize.h b/compiler/luci/export/src/Optimize.h new file mode 100644 index 000000000..c3af7a04c --- /dev/null +++ b/compiler/luci/export/src/Optimize.h @@ -0,0 +1,33 @@ +/* + * 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 __OPTIMIZE_H__ +#define __OPTIMIZE_H__ + +#include <loco.h> + +namespace luci +{ + +/** + * @brief Run passes of graph transformations + * + */ +void optimize(loco::Graph *); + +} // namespace luci + +#endif // __OPTIMIZE_H__ diff --git a/compiler/luci/export/src/ProgressReporter.cpp b/compiler/luci/export/src/ProgressReporter.cpp new file mode 100644 index 000000000..ac9c3d9a8 --- /dev/null +++ b/compiler/luci/export/src/ProgressReporter.cpp @@ -0,0 +1,84 @@ +/* + * 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 "ProgressReporter.h" + +#include "luci/Log.h" +#include "luci/LogHelper.h" + +#include <logo/Phase.h> +#include <logo/Pass.h> + +#include <cassert> + +namespace +{ + +char to_char(bool b) { return b ? 'Y' : 'N'; } + +const char *to_str(logo::PhaseStrategy s) +{ + switch (s) + { + case logo::PhaseStrategy::Saturate: + return "Saturate"; + case logo::PhaseStrategy::Restart: + return "Restart"; + } + assert(false); + return ""; +} + +} // namespace + +namespace luci +{ + +void ProgressReporter::notify(const logo::PhaseEventInfo<logo::PhaseEvent::PhaseBegin> *) +{ + LOGGER(prime); + + INFO(prime) << "=============================================================="; + INFO(prime) << "luci::PhaseRunner<" << to_str(strategy()) << ">"; + INFO(prime) << "Initial graph"; + INFO(prime) << fmt(graph()); +} + +void ProgressReporter::notify(const logo::PhaseEventInfo<logo::PhaseEvent::PhaseEnd> *) +{ + LOGGER(prime); + + INFO(prime) << "luci::PhaseRunner<" << to_str(strategy()) << "> - done"; +} + +void ProgressReporter::notify(const logo::PhaseEventInfo<logo::PhaseEvent::PassBegin> *info) +{ + LOGGER(prime); + + INFO(prime) << "--------------------------------------------------------------"; + INFO(prime) << "Before " << logo::pass_name(info->pass()); +} + +void ProgressReporter::notify(const logo::PhaseEventInfo<logo::PhaseEvent::PassEnd> *info) +{ + LOGGER(prime); + + INFO(prime) << "After " << logo::pass_name(info->pass()) + << " (changed: " << to_char(info->changed()) << ")"; + INFO(prime) << fmt(graph()); +} + +} // namespace luci diff --git a/compiler/luci/export/src/ProgressReporter.h b/compiler/luci/export/src/ProgressReporter.h new file mode 100644 index 000000000..e91f42592 --- /dev/null +++ b/compiler/luci/export/src/ProgressReporter.h @@ -0,0 +1,53 @@ +/* + * 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 __PROGRESSREPORTER_H__ +#define __PROGRESSREPORTER_H__ + +#include <logo/Phase.h> + +#include <loco.h> + +namespace luci +{ + +class ProgressReporter : public logo::PhaseEventListener +{ +public: + ProgressReporter(loco::Graph *graph, logo::PhaseStrategy strategy) + : _graph{graph}, _strategy{strategy} + { + // DO NOTHING + } + +public: + void notify(const logo::PhaseEventInfo<logo::PhaseEvent::PhaseBegin> *) override; + void notify(const logo::PhaseEventInfo<logo::PhaseEvent::PhaseEnd> *) override; + void notify(const logo::PhaseEventInfo<logo::PhaseEvent::PassBegin> *) override; + void notify(const logo::PhaseEventInfo<logo::PhaseEvent::PassEnd> *) override; + +public: + loco::Graph *graph(void) const { return _graph; } + logo::PhaseStrategy strategy(void) const { return _strategy; } + +private: + loco::Graph *_graph; + logo::PhaseStrategy _strategy; +}; + +} // namespace luci + +#endif // __PROGRESSREPORTER_H__ diff --git a/compiler/luci/export/src/SerializedData.h b/compiler/luci/export/src/SerializedData.h new file mode 100644 index 000000000..84249653c --- /dev/null +++ b/compiler/luci/export/src/SerializedData.h @@ -0,0 +1,95 @@ +/* + * 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 __SERIALIZED_DATA_H__ +#define __SERIALIZED_DATA_H__ + +#include <mio/circle/schema_generated.h> + +#include <vector> + +#include <unordered_map> + +namespace luci +{ + +struct OpCode +{ + circle::BuiltinOperator opcode; + + bool operator==(const OpCode &rhs) const { return opcode == rhs.opcode; } +}; + +} // namespace luci + +namespace std +{ + +template <> struct hash<luci::OpCode> +{ + size_t operator()(const luci::OpCode &x) const { return hash<int>()(x.opcode); } +}; + +} // namespace std + +namespace luci +{ + +/** + * @breif Record the information of T/F Lite SubGraph and its mapping to loco + */ +struct SubGraphContext +{ + /// @brief SubGraph input tensor id + std::vector<int32_t> _inputs; + /// @brief SubGraph output tensor id + std::vector<int32_t> _outputs; + /// @DataFormat for SubGraph + circle::DataFormat _data_format{circle::DataFormat::DataFormat_CHANNELS_LAST}; +}; + +// Prerequisites for circle::Model object creation +struct SerializedModelData final +{ + SerializedModelData() = default; + 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; + + /** + * @brief if opcode is not registered in table of opcodes add it + * @param builtin_code + * @return idx of opcode in table of opcodes (see schema) + */ + uint32_t registerBuiltinOpcode(circle::BuiltinOperator builtin_code); + uint32_t registerCustomOpcode(const std::string &custom_op); +}; + +// Prerequisites for circle::Model object creation +struct SerializedGraphData final : public SubGraphContext +{ + SerializedGraphData() = default; + SerializedGraphData(const SerializedModelData &) = delete; + + std::vector<flatbuffers::Offset<circle::Operator>> _operators; + std::vector<flatbuffers::Offset<circle::Tensor>> _tensors; +}; + +} // namespace luci + +#endif // __SERIALIZED_DATA_H__ |