diff options
Diffstat (limited to 'compiler/luci/export/src/CircleExporterImpl.cpp')
-rw-r--r-- | compiler/luci/export/src/CircleExporterImpl.cpp | 266 |
1 files changed, 266 insertions, 0 deletions
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 |