ONE - On-device Neural Engine
Loading...
Searching...
No Matches
CircleTensorExporter.cpp
Go to the documentation of this file.
1/*
2 * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
18#include "CircleTypeInference.h"
19#include "ShapeInference.h"
20
21// TODO Fix include style
22#include "loco/IR/Algorithm.h"
26
27#include "Dialect/IR/TFLNodes.h"
28
29#include <oops/InternalExn.h>
30
31using namespace circle;
32using namespace flatbuffers;
33
34namespace
35{
36
37using namespace exo;
38using namespace exo::circle_detail;
39
40class TFLTensorInfo
41{
42public:
43 TFLTensorInfo() = default;
44
45public:
46 void name(const std::string &name) { _name = name; }
47 const std::string &name(void) const { return _name; }
48
49public:
50 const circle::TensorType &dtype(void) const { return _dtype; }
51 void dtype(const circle::TensorType &dtype) { _dtype = dtype; }
52
53 const ShapeDescription &shape(void) const { return _shape; }
54 void shape(const ShapeDescription &shape) { _shape = shape; }
55
56public:
57 locoex::TFLConst *tfl_content(void) const { return _tfl_content; }
58 void tfl_content(locoex::TFLConst *c) { _tfl_content = c; }
59
60private:
61 std::string _name;
62
63 circle::TensorType _dtype{circle::TensorType_FLOAT32};
64 ShapeDescription _shape{};
65
66 // TODO Find a better design
67 loco::ConstGen *_content = nullptr; // TODO deprecate
68 locoex::TFLConst *_tfl_content = nullptr;
69};
70
71using TFLTensorContext = std::vector<TFLTensorInfo>;
72
73struct NoOpDetector final : public loco::CanonicalNodeMutableVisitor<bool>
74{
75 bool visit(loco::BiasEncode *) final
76 {
77 // BiasEncode is always noop
78 return true;
79 }
80
81 bool visit(loco::FilterEncode *node) final
82 {
83 auto encoder = dynamic_cast<loco::PermutingEncoder<loco::Domain::Filter> *>(node->encoder());
84 if (encoder != nullptr)
85 {
86 auto perm = encoder->perm();
87 return isNHWC(perm);
88 }
89 return false;
90 }
91
92 bool visit(loco::FeatureEncode *node) final
93 {
94 auto encoder = dynamic_cast<loco::PermutingEncoder<loco::Domain::Feature> *>(node->encoder());
95 if (encoder != nullptr)
96 {
97 auto perm = encoder->perm();
98 return isNHWC(perm);
99 }
100 return false;
101 }
102
103 bool visit(loco::FeatureDecode *node) final
104 {
105 auto decoder = dynamic_cast<loco::PermutingDecoder<loco::Domain::Feature> *>(node->decoder());
106 if (decoder != nullptr)
107 {
108 auto perm = decoder->perm();
109 return isNHWC(perm);
110 }
111 return false;
112 }
113
114 // Return false by default
115 bool visit(loco::Node *) final { return false; }
116};
117
118bool isNoOp(loco::Node *node)
119{
120 if (auto canonical_node = dynamic_cast<loco::CanonicalNode *>(node))
121 {
122 NoOpDetector d;
123 return canonical_node->accept(&d);
124 }
125 return false;
126}
127
128void allocateCircleTensor(loco::Node *node, TFLTensorContext &ctx)
129{
130 if (isNoOp(node))
131 {
132 assert(node->arity() == 1 && node->arg(0) != nullptr);
133 set_tensor_index(node, get_tensor_index(node->arg(0)));
134 return;
135 }
136
137 auto tensor_index = static_cast<TFLTensorIndex>(ctx.size());
138 // TODO Use Graph-level metadata for Input & Output
139 auto tensor_name = "t_" + std::to_string(tensor_index);
140
141 TFLTensorInfo tensor_info;
142
143 tensor_info.name(tensor_name);
144 tensor_info.dtype(TypeInference::get(node));
145 tensor_info.shape(ShapeInference::get(node));
146
147 tensor_info.tfl_content(dynamic_cast<locoex::TFLConst *>(node));
148
149 set_tensor_index(node, tensor_index);
150
151 ctx.emplace_back(tensor_info);
152}
153
154} // namespace
155
156namespace
157{
158
160 const ShapeDescription &shape)
161{
162 assert(shape._rank_known && "unknown number of dimensions is not supported");
163 return builder.CreateVector(shape._dims);
164}
165
167{
168 return CreateBuffer(builder);
169}
170
171template <typename NodeT>
172flatbuffers::Offset<circle::Buffer> encodeOpBuffer(FlatBufferBuilder &builder, NodeT *)
173{
174 return CreateBuffer(builder);
175}
176
177template <loco::DataType DT>
178flatbuffers::Offset<circle::Buffer> encodeOpBufferByDType(FlatBufferBuilder &builder,
180{
181 using NativeType = typename loco::DataTypeImpl<DT>::Type;
182
183 std::vector<NativeType> raw_data;
184 const uint32_t size = c->size<DT>();
185 raw_data.reserve(size);
186 for (uint32_t i = 0; i < size; ++i)
187 {
188 raw_data.push_back(c->at<DT>(i));
189 }
190 const size_t raw_size = size * sizeof(NativeType);
191 auto array_offset = builder.CreateVector(reinterpret_cast<uint8_t *>(raw_data.data()), raw_size);
192 return CreateBuffer(builder, array_offset);
193}
194
195template <>
197{
198 if (c->dtype() == loco::DataType::FLOAT32)
199 {
200 return encodeOpBufferByDType<loco::DataType::FLOAT32>(builder, c);
201 }
202 else if (c->dtype() == loco::DataType::S32)
203 {
204 return encodeOpBufferByDType<loco::DataType::S32>(builder, c);
205 }
206
207 INTERNAL_EXN_V("Unsupported datatype", oops::to_uint32(c->dtype()));
208}
209
210} // namespace
211
212namespace exo
213{
214namespace circle_detail
215{
216
217void exportOpDefinedTensor(const TFLTensorInfo &info, FlatBufferBuilder &builder,
219{
220 // Create and register output tensor shape
221 auto shape_offset = encodeShape(builder, info.shape());
222
223 // encode and register output tensor buffer
224 auto buffer = info.tfl_content() == nullptr ? encodeOpBuffer(builder)
225 : encodeOpBuffer(builder, info.tfl_content());
226
227 auto buffer_id = static_cast<uint32_t>(gd._buffers.size());
228 gd._buffers.push_back(buffer);
229
230 auto name_offset = builder.CreateString(info.name());
231 auto tensor_offset = CreateTensor(builder, shape_offset, info.dtype(), buffer_id, name_offset,
232 /*quantization*/ 0, /*is_variable*/ false);
233 gd._tensors.push_back(tensor_offset);
234}
235
237{
238 TFLTensorContext tensor_ctx;
239
241 {
242 allocateCircleTensor(node, tensor_ctx);
243 }
244
245 // add one empty buffer
246 // note: this follows TFLite
247 // note: there's a comment in tflite fbs file
248 // - Note the 0th entry of this array must be an empty buffer (sentinel).
249 // - This is a convention so that tensors without a buffer can provide 0 as
250 // - their buffer.
251 auto buffer = encodeOpBuffer(builder);
252 gd._buffers.push_back(buffer);
253
254 for (const auto &tensor_info : tensor_ctx)
255 {
256 exportOpDefinedTensor(tensor_info, builder, gd);
257 }
258}
259
260} // namespace circle_detail
261} // namespace exo
#define INTERNAL_EXN_V(msg, val)
@ brief throw internal exception with message and value
Definition InternalExn.h:28
Helper class to hold data needed in creation of a FlatBuffer. To serialize data, you typically call o...
Offset< String > CreateString(const char *str, size_t len)
Store a string in the buffer, which can contain any binary data.
Offset< Vector< T > > CreateVector(const T *v, size_t len)
Serialize an array into a FlatBuffer vector.
Create a "Bias" from a "Tensor".
Definition Nodes.h:758
Create a value from constant byte array.
Definition Nodes.h:218
Create a tensor from a feature map.
Definition Nodes.h:399
Create a feature map from a tensor.
Definition Nodes.h:380
Create a filter from a tensor.
Definition Nodes.h:418
A neural network graph.
Definition Graph.h:161
Logical unit of computation.
Definition Node.h:54
virtual Node * arg(uint32_t N) const =0
Access N-th argument node.
virtual uint32_t arity(void) const =0
Return the number of arguments.
Class to build tensor data.
Definition TFLNodes.h:198
uint32_t size(void) const
Definition TFLNodes.cpp:28
const loco::DataTypeImpl< DT >::Type & at(uint32_t n) const
Definition TFLNodes.cpp:42
volatile const char info[]
void exportOpDefinedTensors(loco::Graph *g, FlatBufferBuilder &builder, SerializedModelData &gd)
create Tensors corresponding to results of all nodes in graph
void exportOpDefinedTensor(const TFLTensorInfo &info, FlatBufferBuilder &builder, SerializedModelData &gd)
TFLTensorIndex get_tensor_index(loco::Node *node)
void set_tensor_index(loco::Node *node, const TFLTensorIndex &tensor_id)
bool isNHWC(Permutation *perm)
std::vector< loco::Node * > postorder_traversal(const std::vector< loco::Node * > &roots)
Generate postorder traversal sequence starting from "roots".
Definition Algorithm.cpp:53
std::vector< Node * > output_nodes(Graph *)
Definition Graph.cpp:101
const char * tensor_name(const circle::Tensor *tensor)
uint32_t to_uint32(T a)
Definition InternalExn.h:33
int32_t size[5]
Definition Slice.cpp:35
std::vector< int32_t > _dims
static ShapeDescription get(loco::Node *node)
std::vector< flatbuffers::Offset< circle::Tensor > > _tensors
std::vector< flatbuffers::Offset< circle::Buffer > > _buffers
static circle::TensorType get(loco::Node *node)
virtual T visit(Node *)
Default fallback.
C++ scalar type corresponding to each DataType.