diff --git a/compiler/circledump/driver/Driver.cpp b/compiler/circledump/driver/Driver.cpp index 5b0871a91ae..f5fd9f8268a 100644 --- a/compiler/circledump/driver/Driver.cpp +++ b/compiler/circledump/driver/Driver.cpp @@ -50,7 +50,11 @@ int entry(int argc, char **argv) std::cout << "Dump: " << circle_path << std::endl << std::endl; - std::cout << circlemodel << std::endl; + circledump::ModelEx modelex; + modelex.model = circlemodel; + modelex.rawdata = &modelData; + + std::cout << modelex << std::endl; return 0; } diff --git a/compiler/circledump/include/circledump/Dump.h b/compiler/circledump/include/circledump/Dump.h index 594209a5def..2d25a6178c6 100644 --- a/compiler/circledump/include/circledump/Dump.h +++ b/compiler/circledump/include/circledump/Dump.h @@ -24,10 +24,16 @@ namespace circledump { -void dump_model(std::ostream &os, const circle::Model *model); +struct ModelEx +{ + const circle::Model *model; + const std::vector *rawdata; +}; + +void dump_model(std::ostream &os, const circledump::ModelEx &model); } // namespace circledump -std::ostream &operator<<(std::ostream &os, const circle::Model *model); +std::ostream &operator<<(std::ostream &os, const circledump::ModelEx &model); #endif // __CIRCLEDUMP_DUMP_H__ diff --git a/compiler/circledump/src/Dump.cpp b/compiler/circledump/src/Dump.cpp index 166931648f8..b61949c4c40 100644 --- a/compiler/circledump/src/Dump.cpp +++ b/compiler/circledump/src/Dump.cpp @@ -341,9 +341,9 @@ void dump_sub_graph(std::ostream &os, mio::circle::Reader &reader) os << std::endl; } -void dump_model(std::ostream &os, const circle::Model *model) +void dump_model(std::ostream &os, const circle::Model *model, const std::vector *rawdata) { - mio::circle::Reader reader(model); + mio::circle::Reader reader(model, rawdata); uint32_t num_subgraph = reader.num_subgraph(); @@ -378,13 +378,17 @@ void dump_model(std::ostream &os, const circle::Model *model) os << std::endl; // dump buffer - os << "Buffers: B(index) (length) values, if any" << std::endl; + os << "Buffers: B(index) (length) values, if any; (length *) for ext_offset" << std::endl; for (uint32_t i = 0; i < buffers->size(); ++i) { + bool ext_offset; const uint8_t *buff_data; - size_t size = reader.buffer_info(i, &buff_data); + size_t size = reader.buffer_info(i, &buff_data, ext_offset); - os << "B(" << i << ") (" << size << ") "; + os << "B(" << i << ") (" << size; + if (ext_offset) + os << " *"; + os << ") "; if (buff_data != nullptr) { dump_buffer(os, buff_data, size, 16); @@ -460,8 +464,8 @@ void dump_model(std::ostream &os, const circle::Model *model) } // namespace circledump -std::ostream &operator<<(std::ostream &os, const circle::Model *model) +std::ostream &operator<<(std::ostream &os, const circledump::ModelEx &modelex) { - circledump::dump_model(os, model); + circledump::dump_model(os, modelex.model, modelex.rawdata); return os; } diff --git a/compiler/luci/export/include/luci/CircleExporter.h b/compiler/luci/export/include/luci/CircleExporter.h index 0584c623cfa..94e77b12f96 100644 --- a/compiler/luci/export/include/luci/CircleExporter.h +++ b/compiler/luci/export/include/luci/CircleExporter.h @@ -36,14 +36,10 @@ class CircleExporter 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; + virtual luci::Module *module(void) const = 0; public: // Exporter -> Client // Exporter calls store for export data diff --git a/compiler/luci/export/include/luci/CircleFileExpContract.h b/compiler/luci/export/include/luci/CircleFileExpContract.h index 8ef1b5e0cf6..a06ebe9963e 100644 --- a/compiler/luci/export/include/luci/CircleFileExpContract.h +++ b/compiler/luci/export/include/luci/CircleFileExpContract.h @@ -40,7 +40,6 @@ struct CircleFileExpContract : public luci::CircleExporter::Contract virtual ~CircleFileExpContract() = default; public: - loco::Graph *graph(void) const final { return nullptr; } luci::Module *module(void) const final { return _module; } public: diff --git a/compiler/luci/export/src/CircleExporter.cpp b/compiler/luci/export/src/CircleExporter.cpp index 125df780212..2e0d5c7de1b 100644 --- a/compiler/luci/export/src/CircleExporter.cpp +++ b/compiler/luci/export/src/CircleExporter.cpp @@ -26,9 +26,6 @@ namespace luci { -// TODO remove this -Module *CircleExporter::Contract::module(void) const { return nullptr; } - CircleExporter::CircleExporter() { // NOTHING TO DO @@ -48,17 +45,7 @@ bool CircleExporter::invoke(Contract *contract) const 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); + return false; } } // namespace luci diff --git a/compiler/luci/export/src/CircleExporter.test.cpp b/compiler/luci/export/src/CircleExporter.test.cpp index 5898f9d653e..c76d983972d 100644 --- a/compiler/luci/export/src/CircleExporter.test.cpp +++ b/compiler/luci/export/src/CircleExporter.test.cpp @@ -33,12 +33,12 @@ class SampleGraphContract : public luci::CircleExporter::Contract SampleGraphContract() : luci::CircleExporter::Contract(), _buffer(new std::vector) { // create needed entities - _g = loco::make_graph(); - auto graph_input = _g->inputs()->create(); - auto graph_output = _g->outputs()->create(); - input_node = _g->nodes()->create(); - output_node = _g->nodes()->create(); - relu_node = _g->nodes()->create(); + auto g = loco::make_graph(); + auto graph_input = g->inputs()->create(); + auto graph_output = g->outputs()->create(); + input_node = g->nodes()->create(); + output_node = g->nodes()->create(); + relu_node = g->nodes()->create(); // link nodes and link them to graph relu_node->features(input_node); @@ -57,9 +57,12 @@ class SampleGraphContract : public luci::CircleExporter::Contract graph_output->shape({1, 2, 3, 4}); graph_output->dtype(loco::DataType::FLOAT32); + + _m = std::unique_ptr{new luci::Module}; + _m->add(std::move(g)); } - loco::Graph *graph(void) const override { return _g.get(); } + luci::Module *module(void) const override { return _m.get(); } public: bool store(const char *ptr, const size_t size) const override @@ -77,7 +80,7 @@ class SampleGraphContract : public luci::CircleExporter::Contract luci::CircleRelu *relu_node; private: - std::unique_ptr _g; + std::unique_ptr _m; std::unique_ptr> _buffer; }; diff --git a/compiler/luci/export/src/CircleExporterImpl.cpp b/compiler/luci/export/src/CircleExporterImpl.cpp index 014ef45d71c..c8f2aa06529 100644 --- a/compiler/luci/export/src/CircleExporterImpl.cpp +++ b/compiler/luci/export/src/CircleExporterImpl.cpp @@ -15,14 +15,18 @@ */ #include "CircleExporterImpl.h" -#include "Optimize.h" #include "CircleExportMetadata.h" #include "CircleTensorExporter.h" #include "CircleOperationExporter.h" #include "CircleExporterUtils.h" +#include "ProgressReporter.h" #include +#include +#include +#include +#include #include #include #include @@ -100,13 +104,35 @@ encodeOperatorCodes(FlatBufferBuilder &builder, std::unordered_map()); + phase.emplace_back(std::make_unique()); + + // TODO add more optimization passes (with a knob) + } + + logo::PhaseRunner phase_runner{g}; + + luci::ProgressReporter prog(g, logo::PhaseStrategy::Restart); + phase_runner.attach(&prog); + phase_runner.run(phase); +} + +} // 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> @@ -121,61 +147,6 @@ CircleExporterImpl::exportSubgraph(SerializedGraphData &gd) 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; - - // set Subgraph name - gd._name = graph->name(); - - // 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); - - // Subgraphs - Offset subgraph = exportSubgraph(gd); - auto subgraphs = _builder.CreateVector(std::vector>{subgraph}); - - // Description - std::string description_str = "nnpackage"; - auto description = _builder.CreateString(description_str); - - // Metadata - auto metadata_vec = createCircleMetadataVector(_builder, md); - auto metadata = _builder.CreateVector(std::vector>(metadata_vec)); - - // create array of buffers - auto buffers = _builder.CreateVector(md._buffers); - - // Model - auto model_offset = CreateModel(_builder, version, operator_codes, subgraphs, description, - buffers, 0 /* metadata_buffer */, metadata); - FinishModelBuffer(_builder, model_offset); -} - void CircleExporterImpl::exportModule(Module *module) { assert(module->size() > 0); diff --git a/compiler/luci/export/src/CircleExporterImpl.h b/compiler/luci/export/src/CircleExporterImpl.h index 069f62afd4a..5911fadba43 100644 --- a/compiler/luci/export/src/CircleExporterImpl.h +++ b/compiler/luci/export/src/CircleExporterImpl.h @@ -38,7 +38,6 @@ class CircleExporterImpl CircleExporterImpl() = delete; ~CircleExporterImpl() = default; - explicit CircleExporterImpl(loco::Graph *graph); explicit CircleExporterImpl(Module *module); /** @@ -59,12 +58,6 @@ class CircleExporterImpl */ flatbuffers::Offset 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 diff --git a/compiler/luci/export/src/Optimize.cpp b/compiler/luci/export/src/Optimize.cpp deleted file mode 100644 index e59f15204a3..00000000000 --- a/compiler/luci/export/src/Optimize.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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 -#include - -#include - -#include - -namespace luci -{ - -void optimize(loco::Graph *g) -{ - logo::Phase phase; - { - // prepare type and shape before optimization - phase.emplace_back(std::make_unique()); - phase.emplace_back(std::make_unique()); - - // TODO add more optimization passes (with a knob) - } - - logo::PhaseRunner 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 deleted file mode 100644 index c3af7a04cbc..00000000000 --- a/compiler/luci/export/src/Optimize.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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 - -namespace luci -{ - -/** - * @brief Run passes of graph transformations - * - */ -void optimize(loco::Graph *); - -} // namespace luci - -#endif // __OPTIMIZE_H__ diff --git a/compiler/luci/import/include/luci/Import/CircleReader.h b/compiler/luci/import/include/luci/Import/CircleReader.h index 36e3cdf3c06..961d09023f5 100644 --- a/compiler/luci/import/include/luci/Import/CircleReader.h +++ b/compiler/luci/import/include/luci/Import/CircleReader.h @@ -115,11 +115,18 @@ class CircleReader public: bool parse(const circle::Model *model); + bool parse(const circle::Model *model, const uint8_t *data, size_t size); bool select_subgraph(uint32_t subgraph); +public: + // to access raw file data for Buffer outside of flatbuffer range + const uint8_t *file_data(uint64_t offset) const; + private: const circle::Model *_model{nullptr}; const circle::SubGraph *_current_subgraph{nullptr}; + const uint8_t *_file_data{nullptr}; + size_t _file_size{0}; }; } // namespace luci diff --git a/compiler/luci/import/include/luci/Importer.h b/compiler/luci/import/include/luci/Importer.h index f08ddcda781..0b99f0a7b63 100644 --- a/compiler/luci/import/include/luci/Importer.h +++ b/compiler/luci/import/include/luci/Importer.h @@ -41,12 +41,19 @@ class Importer final // DO NOTHING } + explicit Importer(const uint8_t *data, size_t size) : _file_data{data}, _file_size{size} + { + // DO NOTHING + } + public: - std::unique_ptr import(const circle::Model *model) const; + // std::unique_ptr import(const circle::Model *model) const; std::unique_ptr importModule(const circle::Model *model) const; private: const GraphBuilderSource *_source = nullptr; + const uint8_t *_file_data = nullptr; + size_t _file_size = 0; }; } // namespace luci diff --git a/compiler/luci/import/src/CircleReader.cpp b/compiler/luci/import/src/CircleReader.cpp index 9d05a1a538a..20a42f60685 100644 --- a/compiler/luci/import/src/CircleReader.cpp +++ b/compiler/luci/import/src/CircleReader.cpp @@ -331,6 +331,19 @@ bool CircleReader::parse(const circle::Model *model) return true; } +bool CircleReader::parse(const circle::Model *model, const uint8_t *data, size_t size) +{ + assert(model != nullptr); + + // for direct pointer access + _model = model; + + _file_data = data; + _file_size = size; + + return true; +} + bool CircleReader::select_subgraph(uint32_t sgindex) { if (num_subgraph() <= sgindex) @@ -349,6 +362,13 @@ bool CircleReader::select_subgraph(uint32_t sgindex) return true; } +const uint8_t *CircleReader::file_data(uint64_t offset) const +{ + assert(_file_data); + assert(_file_size); + return (_file_data == nullptr) ? nullptr : _file_data + offset; +} + template VectorWrapper::VectorWrapper(const flatbuffers::Vector *ptr) : _vector(ptr) { diff --git a/compiler/luci/import/src/Importer.cpp b/compiler/luci/import/src/Importer.cpp index 15de03df2be..8a78ac279d0 100644 --- a/compiler/luci/import/src/Importer.cpp +++ b/compiler/luci/import/src/Importer.cpp @@ -264,6 +264,7 @@ Importer::Importer() // DO NOTHING } +/* std::unique_ptr Importer::import(const circle::Model *model) const { auto graph = loco::make_graph(); @@ -300,6 +301,7 @@ std::unique_ptr Importer::import(const circle::Model *model) const return graph; } +*/ std::unique_ptr Importer::importModule(const circle::Model *model) const { @@ -314,7 +316,7 @@ std::unique_ptr Importer::importModule(const circle::Model *model) const } CircleReader reader; - if (!reader.parse(model)) + if (!reader.parse(model, _file_data, _file_size)) return nullptr; for (uint32_t g = 0; g < reader.num_subgraph(); ++g) diff --git a/compiler/luci/import/src/ImporterEx.cpp b/compiler/luci/import/src/ImporterEx.cpp index db585fd4df0..1547bc35541 100644 --- a/compiler/luci/import/src/ImporterEx.cpp +++ b/compiler/luci/import/src/ImporterEx.cpp @@ -40,7 +40,10 @@ std::unique_ptr ImporterEx::importVerifyModule(const std::string &input_ return nullptr; } - flatbuffers::Verifier verifier{reinterpret_cast(model_data.data()), model_data.size()}; + auto data_data = reinterpret_cast(model_data.data()); + auto data_size = model_data.size(); + + flatbuffers::Verifier verifier{data_data, data_size}; if (!circle::VerifyModelBuffer(verifier)) { std::cerr << "ERROR: Invalid input file '" << input_path << "'" << std::endl; @@ -54,7 +57,7 @@ std::unique_ptr ImporterEx::importVerifyModule(const std::string &input_ return nullptr; } - Importer importer; + Importer importer(data_data, data_size); return importer.importModule(circle_model); } diff --git a/compiler/luci/import/src/Nodes/CircleConst.cpp b/compiler/luci/import/src/Nodes/CircleConst.cpp index 189f4d897f4..be053e2a118 100644 --- a/compiler/luci/import/src/Nodes/CircleConst.cpp +++ b/compiler/luci/import/src/Nodes/CircleConst.cpp @@ -16,6 +16,8 @@ #include "luci/Import/Nodes/CircleConst.h" +#include "luci/Import/CircleReader.h" + #include #include @@ -27,6 +29,8 @@ #include #include +#include + namespace { @@ -156,8 +160,41 @@ CircleNode *CircleConstNodeBuilder::build(TensorIndex tensor_index, return nullptr; } - assert(reader->buffers()[const_tensor->buffer()] != nullptr); - const auto buffer = wrap(reader->buffers()[const_tensor->buffer()]->data()); + const auto r_buffers = reader->buffers(); + const auto c_buffer = const_tensor->buffer(); + assert(r_buffers[c_buffer] != nullptr); + const auto r_buffer = r_buffers[c_buffer]; + // temporary buffer to provide raw data from file + // must have life time same or longer than 'buffer' variable + std::vector temp_buffer; + // const auto buffer = wrap(r_buffer->data()); + luci::VectorWrapper buffer(nullptr); + if (r_buffer->offset() > 1) + { + uint32_t r_size = static_cast(r_buffer->size()); + // match binary level to flatbuffers::Vector + temp_buffer.resize(r_size + sizeof(uint32_t)); + + uint8_t *t_data = temp_buffer.data(); + const uint8_t *f_data = reader->file_data(r_buffer->offset()); + if (f_data == nullptr) + { + // NOTE this shouldn't happen + assert(false); + return nullptr; + } + memcpy(t_data, &r_size, sizeof(r_size)); + t_data = t_data + sizeof(r_size); + memcpy(t_data, f_data, r_buffer->size()); + + using fbv_t = flatbuffers::Vector; + const fbv_t *v_data = reinterpret_cast(temp_buffer.data()); + buffer = wrap(v_data); + } + else + { + buffer = wrap(r_buffer->data()); + } const auto const_dims = wrap(const_tensor->shape()); // in NHWC if (const_dims.size() == 0 && buffer.empty()) { diff --git a/compiler/luci/tester/src/ReadModule.cpp b/compiler/luci/tester/src/ReadModule.cpp index 87c1233f07f..dfc28c92df6 100644 --- a/compiler/luci/tester/src/ReadModule.cpp +++ b/compiler/luci/tester/src/ReadModule.cpp @@ -38,7 +38,8 @@ std::unique_ptr ReadModule(std::string &input_path) return nullptr; } - luci::Importer importer; + auto *data_data = reinterpret_cast(model_data.data()); + luci::Importer importer(data_data, model_data.size()); auto module = importer.importModule(circle_model); assert(module->size() > 0); diff --git a/compiler/luci/tester/src/WriteTester.cpp b/compiler/luci/tester/src/WriteTester.cpp index 0d3a1efa220..e5b31e16dc6 100644 --- a/compiler/luci/tester/src/WriteTester.cpp +++ b/compiler/luci/tester/src/WriteTester.cpp @@ -42,11 +42,6 @@ void show_error_message(const char *progname, std::ostream &os, const std::strin struct CircleExpContract : public luci::CircleExporter::Contract { public: - CircleExpContract(loco::Graph *graph, const std::string &filename) - : _graph(graph), _filepath(filename) - { - // NOTHING TO DO - } CircleExpContract(luci::Module *module, const std::string &filename) : _module(module), _filepath(filename) { @@ -55,15 +50,12 @@ struct CircleExpContract : public luci::CircleExporter::Contract virtual ~CircleExpContract() = default; public: - loco::Graph *graph(void) const final { return _graph; } - luci::Module *module(void) const final { return _module; } public: bool store(const char *ptr, const size_t size) const final; private: - loco::Graph *_graph{nullptr}; luci::Module *_module{nullptr}; const std::string _filepath; }; diff --git a/compiler/luci/tests/test.lst b/compiler/luci/tests/test.lst index a9ded24b6be..b791abe2d5c 100644 --- a/compiler/luci/tests/test.lst +++ b/compiler/luci/tests/test.lst @@ -37,6 +37,7 @@ addread(Conv2D_000) addread(Conv2D_001) addread(Conv2D_002) addread(Conv2D_003) +addread(Conv2D_006) addread(Conv2D_U8_000) addread(Conv2D_U8_001) addread(Cos_000) @@ -270,6 +271,7 @@ addwrite(Conv2D_000) addwrite(Conv2D_001) addwrite(Conv2D_002) addwrite(Conv2D_003) +addwrite(Conv2D_006) addwrite(Conv2D_U8_000) addwrite(Conv2D_U8_001) addwrite(Cos_000) diff --git a/compiler/mio-circle08/CMakeLists.txt b/compiler/mio-circle08/CMakeLists.txt index 03e449d6e81..cee15c96993 100644 --- a/compiler/mio-circle08/CMakeLists.txt +++ b/compiler/mio-circle08/CMakeLists.txt @@ -19,7 +19,7 @@ add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/schema.fbs" DEPENDS "${SCHEMA_FILE}" ) -FlatBuffers_Target(mio_circle08 +FlatBuffersMuteable_Target(mio_circle08 OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/gen/mio/circle" INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/gen" SCHEMA_DIR "${CMAKE_CURRENT_BINARY_DIR}" diff --git a/compiler/mio-circle08/include/mio_circle/Reader.h b/compiler/mio-circle08/include/mio_circle/Reader.h index 723668f264e..079ef27b77f 100644 --- a/compiler/mio-circle08/include/mio_circle/Reader.h +++ b/compiler/mio-circle08/include/mio_circle/Reader.h @@ -47,6 +47,7 @@ class Reader public: Reader(const ::circle::Model *model); + Reader(const ::circle::Model *model, const std::vector *rawdata); Reader() = delete; @@ -65,6 +66,7 @@ class Reader uint32_t num_subgraph() const { return _subgraphs->size(); } size_t buffer_info(uint32_t buf_idx, const uint8_t **buff_data); + size_t buffer_info(uint32_t buf_idx, const uint8_t **buff_data, bool &ext_offset); ::circle::BuiltinOperator builtin_code(const ::circle::Operator *op) const; std::string opcode_name(const ::circle::Operator *op) const; std::vector outputs(const ::circle::Operator *op) const; @@ -79,6 +81,8 @@ class Reader private: uint32_t _version; + const std::vector *_rawdata{nullptr}; + const CircleSubGraphs_t *_subgraphs{nullptr}; const CircleBuffers_t *_buffers{nullptr}; const CircleTensors_t *_tensors{nullptr}; diff --git a/compiler/mio-circle08/src/Reader.cpp b/compiler/mio-circle08/src/Reader.cpp index e4df6d04d54..b9f17725e01 100644 --- a/compiler/mio-circle08/src/Reader.cpp +++ b/compiler/mio-circle08/src/Reader.cpp @@ -17,6 +17,7 @@ #include "mio_circle/Reader.h" #include "mio_circle/Helper.h" +#include #include #include @@ -45,6 +46,28 @@ Reader::Reader(const ::circle::Model *model) } } +Reader::Reader(const ::circle::Model *model, const std::vector *rawdata) +{ + if (model == nullptr) + { + throw std::runtime_error("Invalid model"); + } + + _rawdata = rawdata; + + _version = model->version(); + _subgraphs = model->subgraphs(); + _buffers = model->buffers(); + _metadata = model->metadata(); + _signature_defs = model->signature_defs(); + + auto opcodes = model->operator_codes(); + for (const ::circle::OperatorCode *opcode : *opcodes) + { + _op_codes.push_back(opcode); + } +} + size_t Reader::buffer_info(uint32_t buf_idx, const uint8_t **buff_data) { if (buff_data != nullptr) @@ -73,6 +96,54 @@ size_t Reader::buffer_info(uint32_t buf_idx, const uint8_t **buff_data) return 0; } +size_t Reader::buffer_info(uint32_t buf_idx, const uint8_t **buff_data, bool &ext_offset) +{ + ext_offset = false; + + if (buff_data != nullptr) + { + *buff_data = nullptr; + } + + if (buf_idx == 0) + return 0; + + if (auto *buffer = (*_buffers)[buf_idx]) + { + auto buffer_offset = buffer->offset(); + if (buffer_offset > 1) + { + assert(_rawdata); // make debug break for invalid case + if (_rawdata == nullptr) + return 0; + + ext_offset = true; + *buff_data = reinterpret_cast(&_rawdata->at(buffer_offset)); + return buffer->size(); + } + else if (auto *array = buffer->data()) + { + if (size_t size = array->size()) + { + if (buff_data != nullptr) + { + *buff_data = reinterpret_cast(array->data()); + } + return size; + } + } + else + { + if (buffer->offset() == 1 && buffer->size() == 1) + { + std::cerr << "Buffer " << buf_idx << " has invalid offset/size." << std::endl; + } + } + } + + return 0; +} + ::circle::BuiltinOperator Reader::builtin_code(const ::circle::Operator *op) const { uint32_t index = op->opcode_index(); diff --git a/compiler/tflchef/tests/ext_offset/test.recipe b/compiler/tflchef/tests/ext_offset/test.recipe new file mode 100644 index 00000000000..fb94e9c7b26 --- /dev/null +++ b/compiler/tflchef/tests/ext_offset/test.recipe @@ -0,0 +1,44 @@ +operand { + name: "ifm" + type: FLOAT32 + shape { dim: 1 dim: 3 dim: 3 dim: 2 } +} +operand { + name: "ker" + type: FLOAT32 + shape { dim: 1 dim: 1 dim: 1 dim: 2 } + filler { + tag: "explicit" + arg: "1.1" + arg: "2.2" + } +} +operand { + name: "bias" + type: FLOAT32 + shape { dim: 1 } + filler { + tag: "constant" + arg: "3.3" + } +} +operand { + name: "ofm" + type: FLOAT32 + shape { dim: 1 dim: 3 dim: 3 dim: 1 } +} +operation { + type: "Conv2D" + conv2d_options { + padding: VALID + stride_w: 1 + stride_h: 1 + } + input: "ifm" + input: "ker" + input: "bias" + output: "ofm" +} +input: "ifm" +output: "ofm" +ext_offset: true diff --git a/compiler/tfldump/driver/Driver.cpp b/compiler/tfldump/driver/Driver.cpp index a3e748be1d1..1fb5725a6de 100644 --- a/compiler/tfldump/driver/Driver.cpp +++ b/compiler/tfldump/driver/Driver.cpp @@ -49,7 +49,11 @@ int entry(int argc, char **argv) std::cout << "Dump: " << tflite_path << std::endl << std::endl; - std::cout << tflmodel << std::endl; + tfldump::ModelEx modelex; + modelex.model = tflmodel; + modelex.rawdata = &modelData; + + std::cout << modelex << std::endl; return 0; } diff --git a/compiler/tfldump/include/tfldump/Dump.h b/compiler/tfldump/include/tfldump/Dump.h index af04bb1327d..c352967887b 100644 --- a/compiler/tfldump/include/tfldump/Dump.h +++ b/compiler/tfldump/include/tfldump/Dump.h @@ -24,9 +24,16 @@ namespace tfldump { -void dump_model(std::ostream &os, const tflite::Model *model); -} +struct ModelEx +{ + const tflite::Model *model; + const std::vector *rawdata; +}; + +void dump_model(std::ostream &os, const ModelEx &model); + +} // namespace tfldump -std::ostream &operator<<(std::ostream &os, const tflite::Model *model); +std::ostream &operator<<(std::ostream &os, const tfldump::ModelEx &model); #endif // __TFLDUMP_DUMP_H__ diff --git a/compiler/tfldump/src/Dump.cpp b/compiler/tfldump/src/Dump.cpp index 7139f9ca46c..548f5878b83 100644 --- a/compiler/tfldump/src/Dump.cpp +++ b/compiler/tfldump/src/Dump.cpp @@ -340,9 +340,9 @@ void dump_sub_graph(std::ostream &os, tflread::Reader &reader) os << std::endl; } -void dump_model(std::ostream &os, const tflite::Model *model) +void dump_model(std::ostream &os, const tflite::Model *model, const std::vector *rawdata) { - tflread::Reader reader(model); + tflread::Reader reader(model, rawdata); uint32_t num_subgraph = reader.num_subgraph(); @@ -376,13 +376,17 @@ void dump_model(std::ostream &os, const tflite::Model *model) os << std::endl; // dump buffer - os << "Buffers: B(index) (length) values, if any" << std::endl; + os << "Buffers: B(index) (length) values, if any; (length *) for ext_offset" << std::endl; for (uint32_t i = 0; i < buffers->size(); ++i) { + bool ext_offset = false; const uint8_t *buff_data; - size_t size = reader.buffer_info(i, &buff_data); + size_t size = reader.buffer_info(i, &buff_data, ext_offset); - os << "B(" << i << ") (" << size << ") "; + os << "B(" << i << ") (" << size; + if (ext_offset) + os << " *"; + os << ") "; if (buff_data != nullptr) { dump_buffer(os, buff_data, size, 16); @@ -450,8 +454,8 @@ void dump_model(std::ostream &os, const tflite::Model *model) } // namespace tfldump -std::ostream &operator<<(std::ostream &os, const tflite::Model *model) +std::ostream &operator<<(std::ostream &os, const tfldump::ModelEx &modelex) { - tfldump::dump_model(os, model); + tfldump::dump_model(os, modelex.model, modelex.rawdata); return os; } diff --git a/compiler/tfldump/src/Read.cpp b/compiler/tfldump/src/Read.cpp index f55d86dda5b..fe921c65d16 100644 --- a/compiler/tfldump/src/Read.cpp +++ b/compiler/tfldump/src/Read.cpp @@ -18,6 +18,7 @@ #include +#include #include #include @@ -39,6 +40,23 @@ Reader::Reader(const tflite::Model *model) } } +Reader::Reader(const tflite::Model *model, const std::vector *rawdata) +{ + _rawdata = rawdata; + + _version = model->version(); + _subgraphs = model->subgraphs(); + _buffers = model->buffers(); + _metadata = model->metadata(); + _signaturedefs = model->signature_defs(); + + auto opcodes = model->operator_codes(); + for (const ::tflite::OperatorCode *opcode : *opcodes) + { + _op_codes.push_back(opcode); + } +} + size_t Reader::buffer_info(uint32_t buf_idx, const uint8_t **buff_data) { *buff_data = nullptr; @@ -61,6 +79,47 @@ size_t Reader::buffer_info(uint32_t buf_idx, const uint8_t **buff_data) return 0; } +size_t Reader::buffer_info(uint32_t buf_idx, const uint8_t **buff_data, bool &ext_offset) +{ + *buff_data = nullptr; + ext_offset = false; + + if (buf_idx == 0) + return 0; + + if (auto *buffer = (*_buffers)[buf_idx]) + { + auto buffer_offset = buffer->offset(); + if (buffer->offset() > 1) + { + assert(_rawdata); // make debug break for invalid case + if (_rawdata == nullptr) + return 0; + + ext_offset = true; + *buff_data = reinterpret_cast(&_rawdata->at(buffer_offset)); + return buffer->size(); + } + else if (auto *array = buffer->data()) + { + if (size_t size = array->size()) + { + *buff_data = reinterpret_cast(array->data()); + return size; + } + } + else + { + if (buffer->offset() == 1 && buffer->size() == 1) + { + std::cerr << "Buffer " << buf_idx << " has invalid offset/size." << std::endl; + } + } + } + + return 0; +} + tflite::BuiltinOperator Reader::builtin_code(const tflite::Operator *op) const { uint32_t index = op->opcode_index(); diff --git a/compiler/tfldump/src/Read.h b/compiler/tfldump/src/Read.h index fb4d330e70d..974f60923ce 100644 --- a/compiler/tfldump/src/Read.h +++ b/compiler/tfldump/src/Read.h @@ -51,6 +51,7 @@ class Reader public: Reader(const tflite::Model *model); + Reader(const tflite::Model *model, const std::vector *rawdata); Reader() = delete; @@ -69,6 +70,7 @@ class Reader uint32_t num_subgraph() const { return _subgraphs->size(); } size_t buffer_info(uint32_t buf_idx, const uint8_t **buff_data); + size_t buffer_info(uint32_t buf_idx, const uint8_t **buff_data, bool &ext_offset); tflite::BuiltinOperator builtin_code(const tflite::Operator *op) const; std::string opcode_name(const tflite::Operator *op) const; @@ -80,6 +82,8 @@ class Reader private: uint32_t _version; + const std::vector *_rawdata{nullptr}; + const TFliteSubGraphs_t *_subgraphs{nullptr}; const TFliteBuffers_t *_buffers{nullptr}; const TFliteTensors_t *_tensors{nullptr}; diff --git a/compiler/tflite2circle/driver/Driver.cpp b/compiler/tflite2circle/driver/Driver.cpp index 6afe1b0f272..ab4b9566342 100644 --- a/compiler/tflite2circle/driver/Driver.cpp +++ b/compiler/tflite2circle/driver/Driver.cpp @@ -67,10 +67,12 @@ int entry(int argc, char **argv) auto flatbuffer_builder = std::make_unique(1024); // convert tflite to circle - tflite2circle::CircleModel circle_model{flatbuffer_builder}; + const std::vector &raw_data = tfl_model.raw_data(); + tflite2circle::CircleModel circle_model{flatbuffer_builder, raw_data}; circle_model.load_offsets(tfl_model.get_model()); circle_model.model_build(); + circle_model.finalize(); std::ofstream outfile{circle_path, std::ios::binary}; diff --git a/compiler/tflite2circle/include/CircleModel.h b/compiler/tflite2circle/include/CircleModel.h index 189cfaff23f..5e9f0335bed 100644 --- a/compiler/tflite2circle/include/CircleModel.h +++ b/compiler/tflite2circle/include/CircleModel.h @@ -55,6 +55,9 @@ struct MetaDataBufferLink using CIR = int32_t; }; +using BufferData = std::vector; +using MapBufferData = std::map; + template class Offset { private: @@ -68,6 +71,8 @@ template class Offset public: void set_signature_defs(const SignatureDefs *offset) { _tfl_signature_def_offsets = offset; } + void set_buffer_data_map(MapBufferData *map) { _buffer_data_map = map; } + void set_file_raw(const std::vector *raw) { _file_raw = raw; } public: void build(const TFLFlatBufVec *tflite_flatbuffer_vec); @@ -80,6 +85,9 @@ template class Offset CIRFlatBufVecOffset _circle_flatbuffer_vec_offset; // TODO revise this when Circle supports SignatureDef const SignatureDefs *_tfl_signature_def_offsets = nullptr; + // for extended buffer for size > 2G + const std::vector *_file_raw = nullptr; + MapBufferData *_buffer_data_map = nullptr; }; class CircleModel @@ -89,11 +97,12 @@ class CircleModel public: CircleModel(void) = delete; - CircleModel(FlatBufBuilder &fb); + CircleModel(FlatBufBuilder &fb, const std::vector &fr); public: void load_offsets(const tflite::Model *tfl_model); void model_build(void) const; + void finalize(void); const char *base(void) const; size_t size(void) const; @@ -101,10 +110,14 @@ class CircleModel uint32_t _version; Description _description; FlatBufBuilder &_fb; + const std::vector &_file_raw; std::unique_ptr> _operator_codes_offset; std::unique_ptr> _subGraphs_offset; std::unique_ptr> _buffers_offset; std::unique_ptr> _metadata_buffer_offset; + + MapBufferData _buffer_data_map; + std::string _fb_data_with_ext; }; } // namespace tflite2circle diff --git a/compiler/tflite2circle/include/TFLModel.h b/compiler/tflite2circle/include/TFLModel.h index 507667bb903..111a5b9fefc 100644 --- a/compiler/tflite2circle/include/TFLModel.h +++ b/compiler/tflite2circle/include/TFLModel.h @@ -38,6 +38,8 @@ class TFLModel public: const tflite::Model *get_model(void); + // NOTE TFLModel lifetime should be longer than users + const std::vector &raw_data(void) const { return _data; } public: bool verify_data(void); diff --git a/compiler/tflite2circle/src/CircleModel.cpp b/compiler/tflite2circle/src/CircleModel.cpp index c9465a0c615..5195f1cc291 100644 --- a/compiler/tflite2circle/src/CircleModel.cpp +++ b/compiler/tflite2circle/src/CircleModel.cpp @@ -43,15 +43,36 @@ template <> void Offset::build(const TFLFlatBufVec *tflite_flatbuffe for (auto it : *tflite_flatbuffer_vec) { flatbuffers::Offset> buffer_data; - if (it->data()) + const auto tflbuff_data = it->data(); + const auto tflbuff_offset = it->offset(); + const auto tflbuff_size = it->size(); + if (tflbuff_offset > 1) { - std::vector data_vec{it->data()->begin(), it->data()->end()}; - buffer_data = _fb->CreateVector(data_vec); + assert(_buffer_data_map && _file_raw); + if (_buffer_data_map && _file_raw) + { + int32_t buffer_index = buffers_vec.size(); + + auto *file_data_ptr = reinterpret_cast(_file_raw->data()) + tflbuff_offset; + std::vector buffer_data(file_data_ptr, file_data_ptr + tflbuff_size); + _buffer_data_map->emplace(buffer_index, buffer_data); + + auto buffer = circle::CreateBuffer(*_fb.get(), 0, 1, 1); + buffers_vec.emplace_back(buffer); + } + } + else + { + if (tflbuff_data) + { + std::vector data_vec{tflbuff_data->begin(), tflbuff_data->end()}; + buffer_data = _fb->CreateVector(data_vec); + } + circle::BufferBuilder circle_buffer_builder{*_fb}; + circle_buffer_builder.add_data(buffer_data); + auto circle_buffers = circle_buffer_builder.Finish(); + buffers_vec.emplace_back(circle_buffers); } - circle::BufferBuilder circle_buffer_builder{*_fb}; - circle_buffer_builder.add_data(buffer_data); - auto circle_buffers = circle_buffer_builder.Finish(); - buffers_vec.emplace_back(circle_buffers); } _circle_flatbuffer_vec_offset = _fb->CreateVector(buffers_vec); } @@ -376,8 +397,8 @@ template <> void Offset::build(const TFLFlatBufVec *tflite_fla _circle_flatbuffer_vec_offset = _fb->CreateVector(operator_code_vec); } -CircleModel::CircleModel(FlatBufBuilder &fb) - : _version{0}, _description{fb->CreateString("ONE-tflite2circle")}, _fb{fb} +CircleModel::CircleModel(FlatBufBuilder &fb, const std::vector &fr) + : _version{0}, _description{fb->CreateString("ONE-tflite2circle")}, _fb{fb}, _file_raw{fr} { // NOTHING TODO } @@ -390,6 +411,8 @@ void CircleModel::load_offsets(const tflite::Model *tfl_model) _metadata_buffer_offset = std::make_unique>(_fb); _subGraphs_offset->set_signature_defs(tfl_model->signature_defs()); + _buffers_offset->set_buffer_data_map(&_buffer_data_map); + _buffers_offset->set_file_raw(&_file_raw); _operator_codes_offset->build(tfl_model->operator_codes()); _subGraphs_offset->build(tfl_model->subgraphs()); @@ -412,11 +435,79 @@ void CircleModel::model_build(void) const circle::FinishModelBuffer(*_fb, model); } +void CircleModel::finalize(void) +{ + if (_buffer_data_map.empty()) + return; + + auto align16 = [](size_t &v) { + while (v % 16 != 0) + v++; + }; + + // get total memory for flatbuffer + all buffer_data + size_t result_size = _fb->GetSize(); + align16(result_size); + for (auto &it : _buffer_data_map) + { + BufferData &buffer_data = it.second; + result_size += buffer_data.size(); + align16(result_size); + } + align16(result_size); + result_size += 16; // for safety + + std::string result; + const char *buff_ptr = reinterpret_cast(_fb->GetBufferPointer()); + + auto padalign16 = [](std::string &str) { + while (str.size() % 16 != 0) + str += '\0'; + }; + + result.reserve(result_size); + result.append(buff_ptr, _fb->GetSize()); + + if (_buffer_data_map.size() > 0) + { + auto mutable_model = circle::GetMutableModel(result.data()); + auto mutable_buffers = mutable_model->mutable_buffers(); + + // pad to be 16 bytes aligned + padalign16(result); + for (auto &it : _buffer_data_map) + { + int32_t buffer_index = it.first; + BufferData &buffer_data = it.second; + uint64_t offset = result.size(); + uint64_t size = buffer_data.size(); + + circle::Buffer *mutable_buffer = mutable_buffers->GetMutableObject(buffer_index); + mutable_buffer->mutate_offset(offset); + mutable_buffer->mutate_size(size); + + result.append(buffer_data.begin(), buffer_data.end()); + padalign16(result); + } + padalign16(result); + } + + // use final result + _fb_data_with_ext = result; +} + const char *CircleModel::base(void) const { - return reinterpret_cast(_fb->GetBufferPointer()); + if (_buffer_data_map.empty()) + return reinterpret_cast(_fb->GetBufferPointer()); + return reinterpret_cast(_fb_data_with_ext.data()); } -size_t CircleModel::size(void) const { return _fb->GetSize(); } +size_t CircleModel::size(void) const +{ + if (_buffer_data_map.empty()) + return _fb->GetSize(); + return _fb_data_with_ext.size(); +} } // namespace tflite2circle diff --git a/res/TensorFlowLiteRecipes/Conv2D_006/test.recipe b/res/TensorFlowLiteRecipes/Conv2D_006/test.recipe new file mode 100644 index 00000000000..d4da00b9026 --- /dev/null +++ b/res/TensorFlowLiteRecipes/Conv2D_006/test.recipe @@ -0,0 +1,52 @@ +# test to store as buffer data to outside of flatbuffer + +operand { + name: "ifm" + type: FLOAT32 + shape { dim: 1 dim: 4 dim: 3 dim: 2 } +} +operand { + name: "ker" + type: FLOAT32 + shape { dim: 2 dim: 2 dim: 2 dim: 2 } + filler { + tag: "explicit" + arg: "1" arg: "2" arg: "-3" arg: "-4" + arg: "-5" arg: "6" arg: "-7" arg: "8" + arg: "4" arg: "-2" arg: "3" arg: "-1" + arg: "-8" arg: "-6" arg: "7" arg: "5" + } +} +operand { + name: "bias" + type: FLOAT32 + shape { dim: 2 } + filler { + tag: "explicit" + arg: "1" + arg: "2" + } +} +operand { + name: "ofm" + type: FLOAT32 + shape { dim: 1 dim: 2 dim: 2 dim: 2 } +} +operation { + type: "Conv2D" + conv2d_options { + padding: VALID + stride_w: 1 + stride_h: 2 + dilation_w_factor: 1 + dilation_h_factor: 1 + activation: RELU + } + input: "ifm" + input: "ker" + input: "bias" + output: "ofm" +} +input: "ifm" +output: "ofm" +ext_offset: true