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 <>
76void copy_data<loco::DataType::STRING>(const VectorWrapper<uint8_t> &raw_data,
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 < num_elements)
135 {
136 idx++;
137 const_node->at<DT>(idx) = static_cast<T>(data[i]) >> 4;
138 }
139 }
140}
141
142} // namespace
143
144namespace luci
145{
146
148 GraphBuilderContext *context) const
149{
150 assert(tensor_index >= 0);
151 LOGGER(l);
152
153 auto graph = context->graph();
154 auto reader = context->reader();
155 const auto tensors = reader->tensors();
156 const auto const_tensor = tensors[tensor_index];
157 assert(const_tensor != nullptr);
158 if (const_tensor->is_variable())
159 {
160 // Create CircleVariable for variable
161 return nullptr;
162 }
163
164 const auto r_buffers = reader->buffers();
165 const auto c_buffer = const_tensor->buffer();
166 const auto r_buffer = r_buffers[c_buffer];
167 assert(r_buffer != nullptr);
168 if (r_buffer->offset() == 1 || r_buffer->size() == 1)
169 {
170 // NOTE this shouldn't happen
171 throw std::runtime_error("CircleConst: Circle file with invalid extended Buffer.");
172 }
173 // temporary buffer to provide raw data from file
174 // must have life time same or longer than 'buffer' variable
175 std::vector<uint8_t> temp_buffer;
176 luci::VectorWrapper<uint8_t> buffer(nullptr);
177 if (r_buffer->offset() > 1)
178 {
179 if (r_buffer->size() >= std::numeric_limits<uint32_t>::max())
180 {
181 // NOTE uint32_t limit is to match "uoffset_t flatbuffers::Vector::size()"
182 throw std::runtime_error("CircleConst: Circle file with invalid extended Buffer.");
183 }
184 uint32_t r_size = static_cast<uint32_t>(r_buffer->size());
185 // match binary level to flatbuffers::Vector
186 temp_buffer.resize(r_size + sizeof(uint32_t));
187
188 uint8_t *t_data = temp_buffer.data();
189 const uint8_t *f_data = reader->file_data(r_buffer->offset());
190 if (f_data == nullptr)
191 {
192 // NOTE this shouldn't happen
193 assert(false);
194 return nullptr;
195 }
196 memcpy(t_data, &r_size, sizeof(r_size));
197 t_data = t_data + sizeof(r_size);
198 if (r_buffer->offset() + r_buffer->size() > reader->file_size())
199 {
200 // NOTE this shouldn't happen
201 assert(false);
202 return nullptr;
203 }
204 memcpy(t_data, f_data, r_buffer->size());
205
206 using fbv_t = flatbuffers::Vector<uint8_t>;
207 const fbv_t *v_data = reinterpret_cast<const fbv_t *>(temp_buffer.data());
208 buffer = wrap(v_data);
209
210 context->ext_buffer(true);
211 }
212 else
213 {
214 buffer = wrap(r_buffer->data());
215 }
216 const auto const_dims = wrap(const_tensor->shape()); // in NHWC
217 if (const_dims.size() == 0 && buffer.empty())
218 {
219 // unknown shape tensor and scalar tensor
220 return nullptr;
221 }
222
223 // if tensor_index is used as output to some other operator, this is not a constant
224 auto tensoroutputs = context->tensoroutputs();
225 if (tensoroutputs->find(tensor_index))
226 {
227 // other operator output tensor
228 return nullptr;
229 }
230
231 uint32_t num_elements = 1;
232 for (uint32_t r = 0; r < const_dims.size(); ++r)
233 {
234 num_elements = num_elements * const_dims[r];
235 }
236
237 if (buffer.empty() && num_elements > 0)
238 {
239 // normal empty tensor
240 return nullptr;
241 }
242
243 auto const_node = graph->nodes()->create<CircleConst>();
244 copy_tensor_attributes(const_tensor, const_node);
246 INFO(l) << "[luci] NodeFinder const_node(" << tensor_index << ") -> " << const_node << " "
247 << const_dims << std::endl;
248 if (num_elements > 0)
249 {
250 switch (luci_datatype(const_tensor->type()))
251 {
252 case loco::DataType::FLOAT32:
253 copy_data<loco::DataType::FLOAT32>(buffer, num_elements, const_node);
254 break;
255
256 case loco::DataType::FLOAT16:
257 copy_data<loco::DataType::FLOAT16>(buffer, num_elements, const_node);
258 break;
259
260 case loco::DataType::U4:
261 copy_data_4<loco::DataType::U4>(buffer, num_elements, const_node);
262 break;
263
264 case loco::DataType::U8:
265 copy_data<loco::DataType::U8>(buffer, num_elements, const_node);
266 break;
267
268 case loco::DataType::S4:
269 copy_data_4<loco::DataType::S4>(buffer, num_elements, const_node);
270 break;
271
272 case loco::DataType::S8:
273 copy_data<loco::DataType::S8>(buffer, num_elements, const_node);
274 break;
275
276 case loco::DataType::S16:
277 copy_data<loco::DataType::S16>(buffer, num_elements, const_node);
278 break;
279
280 case loco::DataType::S32:
281 copy_data<loco::DataType::S32>(buffer, num_elements, const_node);
282 break;
283
284 case loco::DataType::S64:
285 copy_data<loco::DataType::S64>(buffer, num_elements, const_node);
286 break;
287
288 case loco::DataType::BOOL:
289 copy_data<loco::DataType::BOOL>(buffer, num_elements, const_node);
290 break;
291
292 case loco::DataType::STRING:
293 copy_data<loco::DataType::STRING>(buffer, num_elements, const_node);
294 break;
295
296 default:
297 throw oops::UserExn("Unsupported tensor type",
298 circle::EnumNameTensorType(const_tensor->type()));
299 }
300 }
301
302 return const_node;
303}
304
305} // 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:467
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.
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