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 // Validate STRING offsets as non-negative, monotonic, and bounded within data buffer
100 for (uint32_t i = 0; i < offsets.size(); ++i)
101 {
102 if (offsets[i] < 0)
103 {
104 throw std::runtime_error("String offset is negative");
105 }
106 if (i > 0 && offsets[i] < offsets[i - 1])
107 {
108 throw std::runtime_error("String offsets are not monotonic");
109 }
110 if (offsets[i] > static_cast<int32_t>(raw_data.size()))
111 {
112 throw std::runtime_error("String offset is out of bounds");
113 }
114 }
115
116 const_node->size<loco::DataType::STRING>(num_elements);
117 for (uint32_t i = 0; i < num_elements; ++i)
118 {
119 int32_t start = offsets[i];
120 int32_t next = offsets[i + 1];
121
122 std::string value(data + start, next - start);
123 const_node->at<loco::DataType::STRING>(i) = value;
124 }
125}
126
127// NOTE copy_data for S4, U4.
128// this method will unpack two 4bit elements, packed in 8bit,
129// to two 8bit elements, having values -8~7, for S4 and 0~15 for U4.
130template <loco::DataType DT>
131void copy_data_4(const VectorWrapper<uint8_t> &raw_data, uint32_t num_elements,
132 CircleConst *const_node)
133{
134 using T = typename loco::DataTypeImpl<DT>::Type;
135
136 // TODO support sparse?
137 assert(const_node->sparsityparam() == nullptr);
138 if (const_node->sparsityparam())
139 return;
140
141 uint32_t raw_size = (num_elements + 1) / 2;
142 assert(raw_data.size() == raw_size);
143
144 const uint8_t *data = raw_data.data();
145 const_node->size<DT>(num_elements);
146 for (uint32_t i = 0; i < raw_size; ++i)
147 {
148 uint32_t idx = i * 2;
149 // for S4, 1bit for sign, 3bit for value
150 const_node->at<DT>(idx) = static_cast<T>(data[i] << 4) >> 4;
151 if (idx + 1 < num_elements)
152 {
153 const_node->at<DT>(idx + 1) = static_cast<T>(data[i]) >> 4;
154 }
155 }
156}
157
158} // namespace
159
160namespace luci
161{
162
164 GraphBuilderContext *context) const
165{
166 assert(tensor_index >= 0);
167 LOGGER(l);
168
169 auto graph = context->graph();
170 auto reader = context->reader();
171 const auto tensors = reader->tensors();
172 const auto const_tensor = tensors[tensor_index];
173 assert(const_tensor != nullptr);
174 if (const_tensor->is_variable())
175 {
176 // Create CircleVariable for variable
177 return nullptr;
178 }
179
180 const auto r_buffers = reader->buffers();
181 const auto c_buffer = const_tensor->buffer();
182 const auto r_buffer = r_buffers[c_buffer];
183 assert(r_buffer != nullptr);
184 if (r_buffer->offset() == 1 || r_buffer->size() == 1)
185 {
186 // NOTE this shouldn't happen
187 throw std::runtime_error("CircleConst: Circle file with invalid extended Buffer.");
188 }
189 // temporary buffer to provide raw data from file
190 // must have life time same or longer than 'buffer' variable
191 std::vector<uint8_t> temp_buffer;
192 luci::VectorWrapper<uint8_t> buffer(nullptr);
193 if (r_buffer->offset() > 1)
194 {
195 if (r_buffer->size() >= std::numeric_limits<uint32_t>::max())
196 {
197 // NOTE uint32_t limit is to match "uoffset_t flatbuffers::Vector::size()"
198 throw std::runtime_error("CircleConst: Circle file with invalid extended Buffer.");
199 }
200 uint32_t r_size = static_cast<uint32_t>(r_buffer->size());
201 // match binary level to flatbuffers::Vector
202 temp_buffer.resize(r_size + sizeof(uint32_t));
203
204 uint8_t *t_data = temp_buffer.data();
205 const uint8_t *f_data = reader->file_data(r_buffer->offset());
206 if (f_data == nullptr)
207 {
208 // NOTE this shouldn't happen
209 assert(false);
210 return nullptr;
211 }
212 memcpy(t_data, &r_size, sizeof(r_size));
213 t_data = t_data + sizeof(r_size);
214 if (r_buffer->offset() + r_buffer->size() > reader->file_size())
215 {
216 // NOTE this shouldn't happen
217 assert(false);
218 return nullptr;
219 }
220 memcpy(t_data, f_data, r_buffer->size());
221
223 const fbv_t *v_data = reinterpret_cast<const fbv_t *>(temp_buffer.data());
224 buffer = wrap(v_data);
225
226 context->ext_buffer(true);
227 }
228 else
229 {
230 buffer = wrap(r_buffer->data());
231 }
232 const auto const_dims = wrap(const_tensor->shape()); // in NHWC
233 if (const_dims.size() == 0 && buffer.empty())
234 {
235 // unknown shape tensor and scalar tensor
236 return nullptr;
237 }
238
239 // if tensor_index is used as output to some other operator, this is not a constant
240 auto tensoroutputs = context->tensoroutputs();
241 if (tensoroutputs->find(tensor_index))
242 {
243 // other operator output tensor
244 return nullptr;
245 }
246
247 uint32_t num_elements = 1;
248 for (uint32_t r = 0; r < const_dims.size(); ++r)
249 {
250 num_elements = num_elements * const_dims[r];
251 }
252
253 if (buffer.empty() && num_elements > 0)
254 {
255 // normal empty tensor
256 return nullptr;
257 }
258
259 auto const_node = graph->nodes()->create<CircleConst>();
262 INFO(l) << "[luci] NodeFinder const_node(" << tensor_index << ") -> " << const_node << " "
263 << const_dims << std::endl;
264 if (num_elements > 0)
265 {
266 switch (luci_datatype(const_tensor->type()))
267 {
268 case loco::DataType::FLOAT32:
269 copy_data<loco::DataType::FLOAT32>(buffer, num_elements, const_node);
270 break;
271
272 case loco::DataType::FLOAT16:
273 copy_data<loco::DataType::FLOAT16>(buffer, num_elements, const_node);
274 break;
275
276 case loco::DataType::U4:
277 copy_data_4<loco::DataType::U4>(buffer, num_elements, const_node);
278 break;
279
280 case loco::DataType::U8:
281 copy_data<loco::DataType::U8>(buffer, num_elements, const_node);
282 break;
283
284 case loco::DataType::S4:
285 copy_data_4<loco::DataType::S4>(buffer, num_elements, const_node);
286 break;
287
288 case loco::DataType::S8:
289 copy_data<loco::DataType::S8>(buffer, num_elements, const_node);
290 break;
291
292 case loco::DataType::S16:
293 copy_data<loco::DataType::S16>(buffer, num_elements, const_node);
294 break;
295
296 case loco::DataType::S32:
297 copy_data<loco::DataType::S32>(buffer, num_elements, const_node);
298 break;
299
300 case loco::DataType::S64:
301 copy_data<loco::DataType::S64>(buffer, num_elements, const_node);
302 break;
303
304 case loco::DataType::BOOL:
305 copy_data<loco::DataType::BOOL>(buffer, num_elements, const_node);
306 break;
307
308 case loco::DataType::STRING:
309 copy_data<loco::DataType::STRING>(buffer, num_elements, const_node);
310 break;
311
312 default:
313 throw oops::UserExn("Unsupported tensor type",
314 circle::EnumNameTensorType(const_tensor->type()));
315 }
316 }
317
318 return const_node;
319}
320
321} // namespace luci
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)
std::ostream & operator<<(std::ostream &s, const Index &idx)
Definition Index.cpp:36
#define LOGGER(name)
#define INFO(name)
C++ scalar type corresponding to each DataType.
SparsityParam * sparsityparam(void) const
ShapeStatus shape_status(void) const