ONE - On-device Neural Engine
Loading...
Searching...
No Matches
CircleConst.cpp
Go to the documentation of this file.
1/*
2 * Copyright (c) 2020 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
20
22#include <luci/Log.h>
23
24#include <loco.h>
25#include <oops/UserExn.h>
26
27#include <cassert>
28#include <limits>
29#include <ostream>
30#include <string>
31#include <vector>
32
33#include <string.h>
34
35namespace
36{
37
38std::ostream &operator<<(std::ostream &os, const luci::VectorWrapper<int32_t> &vect)
39{
40 uint32_t seq = 0;
41 for (const auto &v : vect)
42 {
43 if (seq)
44 os << ", ";
45 os << v;
46 seq++;
47 }
48 return os;
49}
50
51using namespace luci;
52
53template <loco::DataType DT>
54void copy_data(const VectorWrapper<uint8_t> &raw_data, uint32_t num_elements,
55 CircleConst *const_node)
56{
57 using T = typename loco::DataTypeImpl<DT>::Type;
58
59 // TODO calculate the exact buffer size of sparse tensor
60 if (const_node->sparsityparam())
61 {
62 num_elements = raw_data.size() / sizeof(T);
63 }
64
65 assert(raw_data.size() == num_elements * sizeof(T));
66 const auto *data = reinterpret_cast<const T *>(raw_data.data());
67
68 const_node->size<DT>(num_elements);
69 for (uint32_t i = 0; i < num_elements; ++i)
70 {
71 const_node->at<DT>(i) = data[i];
72 }
73}
74
75template <>
77 uint32_t num_elements, CircleConst *const_node)
78{
79 assert(const_node->sparsityparam() == nullptr);
80
81 const auto *data = reinterpret_cast<const char *>(raw_data.data());
82 const auto *i32d = reinterpret_cast<const int32_t *>(raw_data.data());
83
84 // de-serialize string data
85 // int32_t count
86 // int32_t offsets[count + 1]
87 // string values[count]
88 assert(static_cast<uint32_t>(*i32d) == num_elements);
89 i32d++; // skip count
90
91 std::vector<int32_t> offsets;
92 offsets.push_back(*i32d++);
93 for (uint32_t i = 0; i < num_elements; ++i)
94 {
95 offsets.push_back(*i32d++);
96 }
97 assert(offsets.size() == num_elements + 1);
98
99 const_node->size<loco::DataType::STRING>(num_elements);
100 for (uint32_t i = 0; i < num_elements; ++i)
101 {
102 int32_t start = offsets[i];
103 int32_t next = offsets[i + 1];
104
105 std::string value(data + start, next - start);
106 const_node->at<loco::DataType::STRING>(i) = value;
107 }
108}
109
110// NOTE copy_data for S4, U4.
111// this method will unpack two 4bit elements, packed in 8bit,
112// to two 8bit elements, having values -8~7, for S4 and 0~15 for U4.
113template <loco::DataType DT>
114void copy_data_4(const VectorWrapper<uint8_t> &raw_data, uint32_t num_elements,
115 CircleConst *const_node)
116{
117 using T = typename loco::DataTypeImpl<DT>::Type;
118
119 // TODO support sparse?
120 assert(const_node->sparsityparam() == nullptr);
121 if (const_node->sparsityparam())
122 return;
123
124 uint32_t raw_size = (num_elements + 1) / 2;
125 assert(raw_data.size() == raw_size);
126
127 const uint8_t *data = raw_data.data();
128 const_node->size<DT>(num_elements);
129 for (uint32_t i = 0; i < raw_size; ++i)
130 {
131 uint32_t idx = i * 2;
132 // for S4, 1bit for sign, 3bit for value
133 const_node->at<DT>(idx) = static_cast<T>(data[i] << 4) >> 4;
134 if (idx + 1 < num_elements)
135 {
136 const_node->at<DT>(idx + 1) = static_cast<T>(data[i]) >> 4;
137 }
138 }
139}
140
141} // namespace
142
143namespace luci
144{
145
147 GraphBuilderContext *context) const
148{
149 assert(tensor_index >= 0);
150 LOGGER(l);
151
152 auto graph = context->graph();
153 auto reader = context->reader();
154 const auto tensors = reader->tensors();
155 const auto const_tensor = tensors[tensor_index];
156 assert(const_tensor != nullptr);
157 if (const_tensor->is_variable())
158 {
159 // Create CircleVariable for variable
160 return nullptr;
161 }
162
163 const auto r_buffers = reader->buffers();
164 const auto c_buffer = const_tensor->buffer();
165 const auto r_buffer = r_buffers[c_buffer];
166 assert(r_buffer != nullptr);
167 if (r_buffer->offset() == 1 || r_buffer->size() == 1)
168 {
169 // NOTE this shouldn't happen
170 throw std::runtime_error("CircleConst: Circle file with invalid extended Buffer.");
171 }
172 // temporary buffer to provide raw data from file
173 // must have life time same or longer than 'buffer' variable
174 std::vector<uint8_t> temp_buffer;
175 luci::VectorWrapper<uint8_t> buffer(nullptr);
176 if (r_buffer->offset() > 1)
177 {
178 if (r_buffer->size() >= std::numeric_limits<uint32_t>::max())
179 {
180 // NOTE uint32_t limit is to match "uoffset_t flatbuffers::Vector::size()"
181 throw std::runtime_error("CircleConst: Circle file with invalid extended Buffer.");
182 }
183 uint32_t r_size = static_cast<uint32_t>(r_buffer->size());
184 // match binary level to flatbuffers::Vector
185 temp_buffer.resize(r_size + sizeof(uint32_t));
186
187 uint8_t *t_data = temp_buffer.data();
188 const uint8_t *f_data = reader->file_data(r_buffer->offset());
189 if (f_data == nullptr)
190 {
191 // NOTE this shouldn't happen
192 assert(false);
193 return nullptr;
194 }
195 memcpy(t_data, &r_size, sizeof(r_size));
196 t_data = t_data + sizeof(r_size);
197 if (r_buffer->offset() + r_buffer->size() > reader->file_size())
198 {
199 // NOTE this shouldn't happen
200 assert(false);
201 return nullptr;
202 }
203 memcpy(t_data, f_data, r_buffer->size());
204
206 const fbv_t *v_data = reinterpret_cast<const fbv_t *>(temp_buffer.data());
207 buffer = wrap(v_data);
208
209 context->ext_buffer(true);
210 }
211 else
212 {
213 buffer = wrap(r_buffer->data());
214 }
215 const auto const_dims = wrap(const_tensor->shape()); // in NHWC
216 if (const_dims.size() == 0 && buffer.empty())
217 {
218 // unknown shape tensor and scalar tensor
219 return nullptr;
220 }
221
222 // if tensor_index is used as output to some other operator, this is not a constant
223 auto tensoroutputs = context->tensoroutputs();
224 if (tensoroutputs->find(tensor_index))
225 {
226 // other operator output tensor
227 return nullptr;
228 }
229
230 uint32_t num_elements = 1;
231 for (uint32_t r = 0; r < const_dims.size(); ++r)
232 {
233 num_elements = num_elements * const_dims[r];
234 }
235
236 if (buffer.empty() && num_elements > 0)
237 {
238 // normal empty tensor
239 return nullptr;
240 }
241
242 auto const_node = graph->nodes()->create<CircleConst>();
245 INFO(l) << "[luci] NodeFinder const_node(" << tensor_index << ") -> " << const_node << " "
246 << const_dims << std::endl;
247 if (num_elements > 0)
248 {
249 switch (luci_datatype(const_tensor->type()))
250 {
251 case loco::DataType::FLOAT32:
252 copy_data<loco::DataType::FLOAT32>(buffer, num_elements, const_node);
253 break;
254
255 case loco::DataType::FLOAT16:
256 copy_data<loco::DataType::FLOAT16>(buffer, num_elements, const_node);
257 break;
258
259 case loco::DataType::U4:
260 copy_data_4<loco::DataType::U4>(buffer, num_elements, const_node);
261 break;
262
263 case loco::DataType::U8:
264 copy_data<loco::DataType::U8>(buffer, num_elements, const_node);
265 break;
266
267 case loco::DataType::S4:
268 copy_data_4<loco::DataType::S4>(buffer, num_elements, const_node);
269 break;
270
271 case loco::DataType::S8:
272 copy_data<loco::DataType::S8>(buffer, num_elements, const_node);
273 break;
274
275 case loco::DataType::S16:
276 copy_data<loco::DataType::S16>(buffer, num_elements, const_node);
277 break;
278
279 case loco::DataType::S32:
280 copy_data<loco::DataType::S32>(buffer, num_elements, const_node);
281 break;
282
283 case loco::DataType::S64:
284 copy_data<loco::DataType::S64>(buffer, num_elements, const_node);
285 break;
286
287 case loco::DataType::BOOL:
288 copy_data<loco::DataType::BOOL>(buffer, num_elements, const_node);
289 break;
290
291 case loco::DataType::STRING:
292 copy_data<loco::DataType::STRING>(buffer, num_elements, const_node);
293 break;
294
295 default:
296 throw oops::UserExn("Unsupported tensor type",
297 circle::EnumNameTensorType(const_tensor->type()));
298 }
299 }
300
301 return const_node;
302}
303
304} // namespace luci
#define LOGGER(name)
Definition Log.h:65
#define INFO(name)
Definition Log.h:68
std::ostream & operator<<(std::ostream &os, const circledump::ModelEx &model)
Definition Dump.cpp:477
Class to build tensor data.
Definition CircleConst.h:35
const loco::DataTypeImpl< DT >::Type & at(uint32_t n) const
uint32_t size(void) const
CircleNode * build(TensorIndex tensor_index, GraphBuilderContext *ctx) const final
CircleTensors tensors() const
Class to store context to build loco graph IR from TensorFlow.
IndexTensorOutputs * tensoroutputs()
Wrapper to use flatbuffers::Vector pointer as std::vector entity.
uint32_t size() const
const T * data() const
Exception to user.
Definition UserExn.h:42
const T * data(const std::vector< T, Alloc > &v)
int32_t TensorIndex
loco::DataType luci_datatype(circle::TensorType type)
void copy_tensor_attributes(const circle::Tensor *tensor, CircleNode *node)
Copy common tensor attributes such as name, type, etc. to node.
T must_cast(loco::Node *node)
VectorWrapper< T > wrap(const flatbuffers::Vector< T > *vec)
uint32_t num_elements(const Shape &shape)
The number of elements of a feature map of a given shape.
Definition Shape.h:59
C++ scalar type corresponding to each DataType.
SparsityParam * sparsityparam(void) const
ShapeStatus shape_status(void) const