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..48413c7a636 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 large" << std::endl; for (uint32_t i = 0; i < buffers->size(); ++i) { + bool is_large; const uint8_t *buff_data; - size_t size = reader.buffer_info(i, &buff_data); + size_t size = reader.buffer_info(i, &buff_data, is_large); - os << "B(" << i << ") (" << size << ") "; + os << "B(" << i << ") (" << size; + if (is_large) + 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/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/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..f06e1eed05d 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 &is_large); ::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..26594c85eb1 100644 --- a/compiler/mio-circle08/src/Reader.cpp +++ b/compiler/mio-circle08/src/Reader.cpp @@ -45,6 +45,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 +95,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 &is_large) +{ + is_large = 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); + if (_rawdata == nullptr) + return 0; + + is_large = 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; + } + } + } + + return 0; +} + ::circle::BuiltinOperator Reader::builtin_code(const ::circle::Operator *op) const { uint32_t index = op->opcode_index(); diff --git a/compiler/tflchef/core/src/ModelChef.cpp b/compiler/tflchef/core/src/ModelChef.cpp index ccb77af2b5d..af7255195dc 100644 --- a/compiler/tflchef/core/src/ModelChef.cpp +++ b/compiler/tflchef/core/src/ModelChef.cpp @@ -362,16 +362,26 @@ template void ModelChef::cook_operands(const T &graph) sparse_uint8.emplace_back(arr[b]); } } - auto data = _flatbuffer_builder->CreateVector(sparse_uint8); - - // Create Buffer - tflite::BufferBuilder buffer_builder{*_flatbuffer_builder}; - buffer_builder.add_data(data); - auto buffer = buffer_builder.Finish(); + if (_ext_offset) + { + buffer_index = _buffer_vec.size(); + _buffer_data_map[buffer_index] = sparse_uint8; - // Update Buffer Index & Vector - buffer_index = _buffer_vec.size(); - _buffer_vec.emplace_back(buffer); + auto buffer = tflite::CreateBuffer(*_flatbuffer_builder, 0, 1, 1); + _buffer_vec.emplace_back(buffer); + } + else + { + auto data = _flatbuffer_builder->CreateVector(sparse_uint8); + // Create Buffer + tflite::BufferBuilder buffer_builder{*_flatbuffer_builder}; + buffer_builder.add_data(data); + auto buffer = buffer_builder.Finish(); + + // Update Buffer Index & Vector + buffer_index = _buffer_vec.size(); + _buffer_vec.emplace_back(buffer); + } // save SparsityParameters auto traversal_order = _flatbuffer_builder->CreateVector(traversal_order_vec); @@ -405,16 +415,27 @@ template void ModelChef::cook_operands(const T &graph) sparse_uint8.emplace_back(arr[b]); } } - auto data = _flatbuffer_builder->CreateVector(sparse_uint8); + if (_ext_offset) + { + buffer_index = _buffer_vec.size(); + _buffer_data_map[buffer_index] = sparse_uint8; - // Create Buffer - tflite::BufferBuilder buffer_builder{*_flatbuffer_builder}; - buffer_builder.add_data(data); - auto buffer = buffer_builder.Finish(); + auto buffer = tflite::CreateBuffer(*_flatbuffer_builder, 0, 1, 1); + _buffer_vec.emplace_back(buffer); + } + else + { + auto data = _flatbuffer_builder->CreateVector(sparse_uint8); - // Update Buffer Index & Vector - buffer_index = _buffer_vec.size(); - _buffer_vec.emplace_back(buffer); + // Create Buffer + tflite::BufferBuilder buffer_builder{*_flatbuffer_builder}; + buffer_builder.add_data(data); + auto buffer = buffer_builder.Finish(); + + // Update Buffer Index & Vector + buffer_index = _buffer_vec.size(); + _buffer_vec.emplace_back(buffer); + } // save SparsityParameters auto traversal_order = _flatbuffer_builder->CreateVector(traversal_order_vec); @@ -454,16 +475,27 @@ template void ModelChef::cook_operands(const T &graph) data_vec = data_packed; } - auto data = _flatbuffer_builder->CreateVector(data_vec); + if (_ext_offset) + { + buffer_index = _buffer_vec.size(); + _buffer_data_map[buffer_index] = data_vec; - // Create Buffer - tflite::BufferBuilder buffer_builder{*_flatbuffer_builder}; - buffer_builder.add_data(data); - auto buffer = buffer_builder.Finish(); + auto buffer = tflite::CreateBuffer(*_flatbuffer_builder, 0, 1, 1); + _buffer_vec.emplace_back(buffer); + } + else + { + auto data = _flatbuffer_builder->CreateVector(data_vec); - // Update Buffer Index & Vector - buffer_index = _buffer_vec.size(); - _buffer_vec.emplace_back(buffer); + // Create Buffer + tflite::BufferBuilder buffer_builder{*_flatbuffer_builder}; + buffer_builder.add_data(data); + auto buffer = buffer_builder.Finish(); + + // Update Buffer Index & Vector + buffer_index = _buffer_vec.size(); + _buffer_vec.emplace_back(buffer); + } } } else @@ -951,6 +983,9 @@ bool ModelChef::finalize_ext_buffer(void) void ModelChef::cook(const ::tflchef::ModelRecipe &model_recipe) { + // use Custom/Buffer offset + _ext_offset = model_recipe.has_ext_offset() ? model_recipe.ext_offset() : false; + prepare_initial_buffer(); gather_operator_codes(model_recipe); diff --git a/compiler/tflchef/proto/tflchef.proto b/compiler/tflchef/proto/tflchef.proto index e4ae5d9b65b..59995f13e71 100644 --- a/compiler/tflchef/proto/tflchef.proto +++ b/compiler/tflchef/proto/tflchef.proto @@ -719,4 +719,6 @@ message ModelRecipe { optional uint32 version = 6 [default = 1]; repeated Graph graph = 7; repeated SignatureDef signature_def = 8; + // store to external and use (Buffer) offset + optional bool ext_offset = 9 [default = false]; } 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..34e0293ff01 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 large" << std::endl; for (uint32_t i = 0; i < buffers->size(); ++i) { + bool is_large; const uint8_t *buff_data; - size_t size = reader.buffer_info(i, &buff_data); + size_t size = reader.buffer_info(i, &buff_data, is_large); - os << "B(" << i << ") (" << size << ") "; + os << "B(" << i << ") (" << size; + if (is_large) + 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..d1b0899ae19 100644 --- a/compiler/tfldump/src/Read.cpp +++ b/compiler/tfldump/src/Read.cpp @@ -18,14 +18,17 @@ #include +#include #include #include namespace tflread { -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(); @@ -39,16 +42,24 @@ Reader::Reader(const tflite::Model *model) } } -size_t Reader::buffer_info(uint32_t buf_idx, const uint8_t **buff_data) +size_t Reader::buffer_info(uint32_t buf_idx, const uint8_t **buff_data, bool &is_large) { *buff_data = nullptr; + is_large = false; if (buf_idx == 0) return 0; if (auto *buffer = (*_buffers)[buf_idx]) { - if (auto *array = buffer->data()) + auto buffer_offset = buffer->offset(); + if (buffer->offset() > 1) + { + is_large = true; + *buff_data = reinterpret_cast(&_rawdata->at(buffer_offset)); + return buffer->size(); + } + else if (auto *array = buffer->data()) { if (size_t size = array->size()) { @@ -56,6 +67,13 @@ size_t Reader::buffer_info(uint32_t buf_idx, const uint8_t **buff_data) return size; } } + else + { + if (buffer->offset() == 1 && buffer->size() == 1) + { + std::cerr << "Buffer " << buf_idx << " is invalid large buffer." << std::endl; + } + } } return 0; diff --git a/compiler/tfldump/src/Read.h b/compiler/tfldump/src/Read.h index fb4d330e70d..22be0b2424a 100644 --- a/compiler/tfldump/src/Read.h +++ b/compiler/tfldump/src/Read.h @@ -50,7 +50,7 @@ class Reader using TFliteSignatureDef_t = flatbuffers::Vector>; public: - Reader(const tflite::Model *model); + Reader(const tflite::Model *model, const std::vector *rawdata); Reader() = delete; @@ -68,7 +68,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 &is_large); tflite::BuiltinOperator builtin_code(const tflite::Operator *op) const; std::string opcode_name(const tflite::Operator *op) const; @@ -80,6 +80,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