1 /* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
2 
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6 
7     http://www.apache.org/licenses/LICENSE-2.0
8 
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15 
16 #include "tensorflow/lite/micro/micro_allocator.h"
17 
18 #include <cstddef>
19 #include <cstdint>
20 
21 #include "flatbuffers/flatbuffers.h"  // from @flatbuffers
22 #include "tensorflow/lite/c/common.h"
23 #include "tensorflow/lite/core/api/error_reporter.h"
24 #include "tensorflow/lite/core/api/flatbuffer_conversions.h"
25 #include "tensorflow/lite/core/api/op_resolver.h"
26 #include "tensorflow/lite/core/api/tensor_utils.h"
27 #include "tensorflow/lite/kernels/internal/compatibility.h"
28 #include "tensorflow/lite/micro/compatibility.h"
29 #include "tensorflow/lite/micro/memory_helpers.h"
30 #include "tensorflow/lite/micro/memory_planner/greedy_memory_planner.h"
31 #include "tensorflow/lite/micro/memory_planner/memory_planner.h"
32 #include "tensorflow/lite/micro/micro_error_reporter.h"
33 #include "tensorflow/lite/micro/simple_memory_allocator.h"
34 #include "tensorflow/lite/schema/schema_generated.h"
35 #include "tensorflow/lite/schema/schema_utils.h"
36 
37 namespace tflite {
38 
39 namespace {
40 
41 // Maximum number of scratch buffer requests per operator. Operator kernels that
42 // request more than this value will receive an exception.
43 constexpr size_t kMaxScratchBuffersPerOp = 12;
44 
45 // Sentinel value used as a placeholder to mark a ScratchBufferRequest request
46 // needs a node id assignment.
47 constexpr int kUnassignedScratchBufferRequestIndex = -1;
48 
49 // Used to hold information used during allocation calculations.
50 struct AllocationInfo {
51   size_t bytes;
52   void** output_ptr;
53   int first_created;
54   int last_used;
55   int32_t offline_offset;
56   bool needs_allocating;
57 };
58 
59 // We align tensor buffers to 16-byte boundaries, since this is a common
60 // requirement for SIMD extensions.
61 constexpr int kBufferAlignment = 16;
62 constexpr char kOfflineMemAllocMetadata[] = "OfflineMemoryAllocation";
63 const TfLiteIntArray kZeroLengthIntArray = {};
64 
65 class MicroBuiltinDataAllocator : public BuiltinDataAllocator {
66  public:
MicroBuiltinDataAllocator(SimpleMemoryAllocator * memory_allocator)67   explicit MicroBuiltinDataAllocator(SimpleMemoryAllocator* memory_allocator)
68       : memory_allocator_(memory_allocator) {}
69 
Allocate(size_t size,size_t alignment_hint)70   void* Allocate(size_t size, size_t alignment_hint) override {
71     return memory_allocator_->AllocateFromTail(size, alignment_hint);
72   }
Deallocate(void * data)73   void Deallocate(void* data) override {
74     // Do not deallocate, builtin data needs to be available for the life time
75     // of the model.
76   }
77 
78  private:
79   SimpleMemoryAllocator* memory_allocator_;
80 
81   TF_LITE_REMOVE_VIRTUAL_DELETE
82 };
83 
84 #if !defined(__clang__)
85 // Helper function to check flatbuffer metadata correctness. This function is
86 // not called by default. Hence it's not linked in to the final binary code.
CheckOfflinePlannedOffsets(const Model * model,ErrorReporter * error_reporter)87 TfLiteStatus CheckOfflinePlannedOffsets(const Model* model,
88                                         ErrorReporter* error_reporter) {
89   // Suppress compile warning for unused function
90   (void)CheckOfflinePlannedOffsets;
91 
92   if (model->metadata()) {
93     for (size_t i = 0; i < model->metadata()->size(); ++i) {
94       auto metadata = model->metadata()->Get(i);
95       if (strncmp(metadata->name()->c_str(), kOfflineMemAllocMetadata,
96                   strlen(kOfflineMemAllocMetadata)) == 0) {
97         auto* subgraphs = model->subgraphs();
98         const SubGraph* subgraph = (*subgraphs)[0];
99         const flatbuffers::Vector<flatbuffers::Offset<Tensor>>* tensors =
100             subgraph->tensors();
101         const flatbuffers::Vector<flatbuffers::Offset<Buffer>>* buffers =
102             model->buffers();
103         int nbr_tflite_tensors = tensors->size();
104         auto* buffer = (*buffers)[metadata->buffer()];
105         auto* array = buffer->data();
106         const uint32_t* metadata_buffer = (uint32_t*)array->data();
107         int version = metadata_buffer[0];
108         int subgraph_idx = metadata_buffer[1];
109         const int nbr_offline_offsets = metadata_buffer[2];
110 #ifndef TF_LITE_STRIP_ERROR_STRINGS
111         int* offline_planner_offsets = (int*)&metadata_buffer[3];
112 #endif
113 
114         TF_LITE_REPORT_ERROR(error_reporter, "==== Model metadata info: =====");
115         TF_LITE_REPORT_ERROR(error_reporter,
116                              "Offline planner metadata found, version %d, "
117                              "subgraph %d, nbr offline offsets %d",
118                              version, subgraph_idx, nbr_offline_offsets);
119         for (int j = 0; j < nbr_offline_offsets; ++j) {
120           TF_LITE_REPORT_ERROR(
121               error_reporter,
122               "Offline planner tensor index %d, offline offset: %d", j,
123               offline_planner_offsets[j]);
124         }
125 
126         if (version != 1) {
127           TF_LITE_REPORT_ERROR(error_reporter, "Version not supported! (%d)\n",
128                                version);
129           return kTfLiteError;
130         }
131         if (subgraph_idx != 0) {
132           TF_LITE_REPORT_ERROR(error_reporter,
133                                "Only 1 subgraph supported! Subgraph idx (%d)\n",
134                                subgraph_idx);
135           return kTfLiteError;
136         }
137         if (nbr_tflite_tensors != nbr_offline_offsets) {
138           TF_LITE_REPORT_ERROR(error_reporter,
139                                "Nbr of offline buffer offsets (%d) in metadata "
140                                "not equal nbr tensors (%d)\n",
141                                nbr_offline_offsets, nbr_tflite_tensors);
142           return kTfLiteError;
143         }
144       }
145     }
146   }
147   return kTfLiteOk;
148 }
149 #endif
150 
151 // A helper class to construct AllocationInfo array. This array contains the
152 // lifetime of tensors / scratch_buffer and will be used to calculate the memory
153 // plan. Methods need to be called in order from `Init`, `Add*`, to `Finish`.
154 class AllocationInfoBuilder {
155  public:
AllocationInfoBuilder(AllocationInfo * info,size_t tensor_count,size_t scratch_buffer_count,ErrorReporter * reporter)156   AllocationInfoBuilder(AllocationInfo* info, size_t tensor_count,
157                         size_t scratch_buffer_count, ErrorReporter* reporter)
158       : info_(info),
159         tensor_count_(tensor_count),
160         buffer_count_(scratch_buffer_count),
161         reporter_(reporter) {}
162 
163   // Check if model contains offline planned buffer offsets.
164   //  - If there's no metadata available, offline_planner_offsets is not set
165   //  - If there's metadata available, offline_planner_offsets will point to the
166   //    first offset in the metadata buffer list.
167   TfLiteStatus GetOfflinePlannedOffsets(
168       const Model* model, const int32_t** offline_planner_offsets);
169 
170   // Add allocaiton information for the tensors.
171   TfLiteStatus AddTensors(const SubGraph* subgraph,
172                           const int32_t* offline_offsets,
173                           TfLiteEvalTensor* eval_tensors);
174 
175   // Add allocation information for the scratch buffers.
176   TfLiteStatus AddScratchBuffers(
177       internal::ScratchBufferRequest* scratch_buffer_requests,
178       ScratchBufferHandle* scratch_buffer_handles);
179 
180   // Returns a pointer to the built AllocationInfo array.
Finish() const181   const AllocationInfo* Finish() const { return info_; }
182 
183  private:
184   AllocationInfo* info_ = nullptr;
185   size_t tensor_count_ = 0;
186   size_t buffer_count_ = 0;
187   ErrorReporter* reporter_ = nullptr;
188 };
189 
AddTensors(const SubGraph * subgraph,const int32_t * offline_offsets,TfLiteEvalTensor * eval_tensors)190 TfLiteStatus AllocationInfoBuilder::AddTensors(const SubGraph* subgraph,
191                                                const int32_t* offline_offsets,
192                                                TfLiteEvalTensor* eval_tensors) {
193   TFLITE_DCHECK(eval_tensors != nullptr);
194 
195   // Set up allocation info for all tensors.
196   for (size_t i = 0; i < tensor_count_; ++i) {
197     AllocationInfo* current = &info_[i];
198     current->output_ptr = &(eval_tensors[i].data.data);
199 
200     TF_LITE_ENSURE_STATUS(
201         TfLiteEvalTensorByteLength(&eval_tensors[i], &current->bytes));
202 
203     current->first_created = -1;
204     current->last_used = -1;
205     current->needs_allocating = (eval_tensors[i].data.data == nullptr) &&
206                                 (!subgraph->tensors()->Get(i)->is_variable());
207     if (offline_offsets) {
208       current->offline_offset = offline_offsets[i];
209     } else {
210       current->offline_offset = kOnlinePlannedBuffer;
211     }
212   }
213 
214   uint32_t operators_size = NumSubgraphOperators(subgraph);
215 
216   for (size_t i = 0; i < subgraph->inputs()->size(); ++i) {
217     const int tensor_index = subgraph->inputs()->Get(i);
218     AllocationInfo* current = &info_[tensor_index];
219     current->first_created = 0;
220   }
221 
222   // Mark all outputs as persistent to the end of the invocation.
223   for (size_t i = 0; i < subgraph->outputs()->size(); ++i) {
224     const int tensor_index = subgraph->outputs()->Get(i);
225     AllocationInfo* current = &info_[tensor_index];
226     current->last_used = operators_size - 1;
227   }
228 
229   // Figure out when the first and last use of each tensor is.
230   for (int i = (operators_size - 1); i >= 0; --i) {
231     const auto* op = subgraph->operators()->Get(i);
232     for (size_t n = 0; n < op->inputs()->size(); ++n) {
233       const int tensor_index = op->inputs()->Get(n);
234       AllocationInfo* current = &info_[tensor_index];
235       if (((current->last_used == -1) || (current->last_used < i))) {
236         current->last_used = i;
237       }
238     }
239     for (size_t n = 0; n < op->outputs()->size(); ++n) {
240       const int tensor_index = op->outputs()->Get(n);
241       AllocationInfo* current = &info_[tensor_index];
242       if ((current->first_created == -1) || (current->first_created > i)) {
243         current->first_created = i;
244       }
245     }
246   }
247   return kTfLiteOk;
248 }
249 
250 // Get offline tensors allocation plan. See
251 // micro/docs/memory_management.md for more info.
GetOfflinePlannedOffsets(const Model * model,const int32_t ** offline_planner_offsets)252 TfLiteStatus AllocationInfoBuilder::GetOfflinePlannedOffsets(
253     const Model* model, const int32_t** offline_planner_offsets) {
254   if (model->metadata()) {
255     for (size_t i = 0; i < model->metadata()->size(); ++i) {
256       auto metadata = model->metadata()->Get(i);
257       if (strncmp(metadata->name()->c_str(), kOfflineMemAllocMetadata,
258                   strlen(kOfflineMemAllocMetadata)) == 0) {
259         const flatbuffers::Vector<flatbuffers::Offset<Buffer>>* buffers =
260             model->buffers();
261         auto* buffer = (*buffers)[metadata->buffer()];
262         auto* array = buffer->data();
263         const uint32_t* metadata_buffer =
264             reinterpret_cast<const uint32_t*>(array->data());
265         const size_t nbr_tensors = static_cast<size_t>(metadata_buffer[2]);
266         *offline_planner_offsets =
267             reinterpret_cast<const int32_t*>(&metadata_buffer[3]);
268 
269         if (tensor_count_ != nbr_tensors) {
270           TF_LITE_REPORT_ERROR(reporter_,
271                                "Nbr of offline buffer offsets (%d) in metadata "
272                                "not equal nbr tensors (%d)\n",
273                                nbr_tensors, tensor_count_);
274           return kTfLiteError;
275         }
276       }
277     }
278   }
279   return kTfLiteOk;
280 }
281 
AddScratchBuffers(internal::ScratchBufferRequest * scratch_buffer_requests,ScratchBufferHandle * scratch_buffer_handles)282 TfLiteStatus AllocationInfoBuilder::AddScratchBuffers(
283     internal::ScratchBufferRequest* scratch_buffer_requests,
284     ScratchBufferHandle* scratch_buffer_handles) {
285   // Set up allocation info for buffers.
286   for (size_t i = tensor_count_; i < tensor_count_ + buffer_count_; ++i) {
287     internal::ScratchBufferRequest* current_request =
288         &(scratch_buffer_requests[i - tensor_count_]);
289     ScratchBufferHandle* current_handle =
290         &(scratch_buffer_handles[i - tensor_count_]);
291 
292     AllocationInfo* current = &info_[i];
293     current->output_ptr = reinterpret_cast<void**>(&current_handle->data);
294     current->bytes = current_request->bytes;
295     current->first_created = current_request->node_idx;
296     current->last_used = current_request->node_idx;
297     current->offline_offset = kOnlinePlannedBuffer;
298     current->needs_allocating = true;
299   }
300   return kTfLiteOk;
301 }
302 
CreatePlan(ErrorReporter * error_reporter,GreedyMemoryPlanner * planner,const AllocationInfo * allocation_info,size_t allocation_info_size)303 TfLiteStatus CreatePlan(ErrorReporter* error_reporter,
304                         GreedyMemoryPlanner* planner,
305                         const AllocationInfo* allocation_info,
306                         size_t allocation_info_size) {
307   // Add the tensors to our allocation plan.
308   for (size_t i = 0; i < allocation_info_size; ++i) {
309     const AllocationInfo* current = &allocation_info[i];
310     if (current->needs_allocating) {
311       size_t aligned_bytes_required =
312           AlignSizeUp(current->bytes, kBufferAlignment);
313       if (current->offline_offset == kOnlinePlannedBuffer) {
314         TF_LITE_ENSURE_STATUS(
315             planner->AddBuffer(error_reporter, aligned_bytes_required,
316                                current->first_created, current->last_used));
317       } else {
318         TF_LITE_ENSURE_STATUS(planner->AddBuffer(
319             error_reporter, aligned_bytes_required, current->first_created,
320             current->last_used, current->offline_offset));
321       }
322     }
323   }
324   return kTfLiteOk;
325 }
326 
CommitPlan(ErrorReporter * error_reporter,MemoryPlanner * planner,uint8_t * starting_point,const AllocationInfo * allocation_info,size_t allocation_info_size)327 TfLiteStatus CommitPlan(ErrorReporter* error_reporter, MemoryPlanner* planner,
328                         uint8_t* starting_point,
329                         const AllocationInfo* allocation_info,
330                         size_t allocation_info_size) {
331   // Figure out the actual memory addresses for each buffer, based on the plan.
332   int planner_index = 0;
333   for (size_t i = 0; i < allocation_info_size; ++i) {
334     const AllocationInfo* current = &allocation_info[i];
335     if (current->needs_allocating) {
336       int offset = -1;
337       TF_LITE_ENSURE_STATUS(
338           planner->GetOffsetForBuffer(error_reporter, planner_index, &offset));
339       *current->output_ptr = reinterpret_cast<void*>(starting_point + offset);
340       ++planner_index;
341     }
342   }
343   return kTfLiteOk;
344 }
345 }  // namespace
346 
347 namespace internal {
348 
349 // Handles architecture safe mapping of flatbuffer vectors to a TfLite*Array
350 // struct. Matching types are required (e.g. float and TfLiteFloatArray).
351 // Big-endian systems will always allocate dimension array data in the tail
352 // (persistent) section.
353 template <typename kFlatBufferVectorType, typename kTfLiteArrayType>
FlatBufferVectorToTfLiteTypeArray(SimpleMemoryAllocator * allocator,ErrorReporter * error_reporter,const flatbuffers::Vector<kFlatBufferVectorType> * flatbuffer_array,kTfLiteArrayType ** result)354 TfLiteStatus FlatBufferVectorToTfLiteTypeArray(
355     SimpleMemoryAllocator* allocator, ErrorReporter* error_reporter,
356     const flatbuffers::Vector<kFlatBufferVectorType>* flatbuffer_array,
357     kTfLiteArrayType** result) {
358   TFLITE_DCHECK(error_reporter != nullptr);
359   TFLITE_DCHECK(flatbuffer_array != nullptr);
360   // TODO(b/159668691): Consider adding type assertion or breaking this function
361   // into multiple functions for each type. std::is_same is c++11 and has a
362   // special updated constructor in c++17 that requires a string argument.
363   if (FLATBUFFERS_LITTLEENDIAN) {
364     // On little-endian machines, TfLite*Array happens to have the same memory
365     // layout as flatbuffers:Vector<kFlatBufferVectorType>, so we can
366     // reinterpret_cast the flatbuffer vector and avoid a copy and malloc.
367     *result = const_cast<kTfLiteArrayType*>(
368         reinterpret_cast<const kTfLiteArrayType*>(flatbuffer_array));
369   } else {
370     // Big-endian architecture can not use the same memory layout as
371     // flatbuffers::Vector<kFlatBufferVectorType>. Allocate from the tail and
372     // copy values from the flatbuffer into the newly allocated chunk.
373     kTfLiteArrayType* array = reinterpret_cast<kTfLiteArrayType*>(
374         allocator->SimpleMemoryAllocator::AllocateFromTail(
375             TfLiteIntArrayGetSizeInBytes(flatbuffer_array->size()),
376             alignof(kTfLiteArrayType)));
377     if (array == nullptr) {
378       TF_LITE_REPORT_ERROR(
379           error_reporter,
380           "Failed to allocate %d bytes of memory to copy an array.",
381           TfLiteIntArrayGetSizeInBytes(flatbuffer_array->size()));
382       return kTfLiteError;
383     }
384     array->size = flatbuffer_array->size();
385     for (int i = 0; i < array->size; ++i) {
386       array->data[i] = flatbuffer_array->Get(i);
387     }
388     *result = array;
389   }
390   return kTfLiteOk;
391 }
392 
393 // Returns a pointer to any buffer associated with the flatbuffer tensor. Can
394 // return nullptr if no buffer is found.
GetFlatbufferTensorBuffer(const tflite::Tensor & flatbuffer_tensor,const flatbuffers::Vector<flatbuffers::Offset<Buffer>> * buffers)395 void* GetFlatbufferTensorBuffer(
396     const tflite::Tensor& flatbuffer_tensor,
397     const flatbuffers::Vector<flatbuffers::Offset<Buffer>>* buffers) {
398   // We need to figure out where the actual contents of this tensor are stored
399   // in memory. We'll check to see if there's a serialized buffer (pretty much
400   // the same as a constant op in TensorFlow) associated with this tensor first,
401   // and if there is update the runtime structure to point to its location in
402   // memory.
403   // First see if there's any buffer information in the serialized tensor.
404   // TODO(b/170379532): Add better unit tests to validate flatbuffer values.
405   void* out_buffer = nullptr;
406   if (auto* buffer = (*buffers)[flatbuffer_tensor.buffer()]) {
407     // If we've found a buffer, does it have any data?
408     if (auto* array = buffer->data()) {
409       // If it has any data, is the data size larger than zero?
410       if (array->size()) {
411         // We've found a buffer with valid data, so update the runtime tensor
412         // data structure to point to it.
413         out_buffer = const_cast<void*>(static_cast<const void*>(array->data()));
414       }
415     }
416     // TODO(petewarden): It's not clear in what circumstances we could have a
417     // buffer in the serialized tensor, but it doesn't have any data in it. Is
418     // that a validly-generated file, and if so what does it mean, or is it an
419     // error condition? It would be good to tighten up the specification to make
420     // it less ambiguous.
421   }
422   return out_buffer;
423 }
424 
InitializeTfLiteTensorFromFlatbuffer(SimpleMemoryAllocator * allocator,bool allocate_temp,const tflite::Tensor & flatbuffer_tensor,const flatbuffers::Vector<flatbuffers::Offset<Buffer>> * buffers,ErrorReporter * error_reporter,TfLiteTensor * result)425 TfLiteStatus InitializeTfLiteTensorFromFlatbuffer(
426     SimpleMemoryAllocator* allocator, bool allocate_temp,
427     const tflite::Tensor& flatbuffer_tensor,
428     const flatbuffers::Vector<flatbuffers::Offset<Buffer>>* buffers,
429     ErrorReporter* error_reporter, TfLiteTensor* result) {
430   TFLITE_DCHECK(result != nullptr);
431 
432   *result = {};
433   // Make sure the serialized type is one we know how to deal with, and convert
434   // it from a flatbuffer enum into a constant used by the kernel C API.
435   TF_LITE_ENSURE_STATUS(ConvertTensorType(flatbuffer_tensor.type(),
436                                           &result->type, error_reporter));
437   // Make sure we remember if the serialized tensor is designated as a variable.
438   result->is_variable = flatbuffer_tensor.is_variable();
439 
440   result->data.data = GetFlatbufferTensorBuffer(flatbuffer_tensor, buffers);
441 
442   // TODO(petewarden): Some of these paths aren't getting enough testing
443   // coverage, so we should figure out some tests that exercise them.
444   if (result->data.data == nullptr) {
445     // The tensor contents haven't been set from a serialized buffer, so
446     // make a note that they will be allocated from memory. The actual
447     // allocation won't happen until later.
448     result->allocation_type = kTfLiteArenaRw;
449   } else {
450     // We set the data from a serialized buffer, so record tha.
451     result->allocation_type = kTfLiteMmapRo;
452   }
453 
454   // Figure out what the size in bytes of the buffer is and store it.
455   size_t type_size;
456   TF_LITE_ENSURE_STATUS(BytesRequiredForTensor(
457       flatbuffer_tensor, &result->bytes, &type_size, error_reporter));
458 
459   if (flatbuffer_tensor.shape() == nullptr) {
460     // flatbuffer_tensor.shape() can return a nullptr in the case of a scalar
461     // tensor.
462     result->dims = const_cast<TfLiteIntArray*>(&kZeroLengthIntArray);
463   } else {
464     // TFLM doesn't allow reshaping the tensor which requires dynamic memory
465     // allocation so it is safe to drop the const qualifier. In the future, if
466     // we really want to update the tensor shape, we can always pass in a new
467     // TfLiteIntArray - especially we have to do so if the dimension is
468     TF_LITE_ENSURE_STATUS(FlatBufferVectorToTfLiteTypeArray(
469         allocator, error_reporter, flatbuffer_tensor.shape(), &(result->dims)));
470   }
471 
472   // Copy the quantization information from the serialized data.
473   const auto* src_quantization = flatbuffer_tensor.quantization();
474   if (src_quantization && src_quantization->scale() &&
475       (src_quantization->scale()->size() > 0) &&
476       src_quantization->zero_point() &&
477       (src_quantization->zero_point()->size() > 0)) {
478     // Always populate the TfLiteTensor.params field, even if there are
479     // per-channel quantization parameters.
480     result->params.scale = src_quantization->scale()->Get(0);
481     // Note that the zero_point field in the FlatBuffers schema is a 64-bit
482     // integer, but the zero_point field in the TfLiteQuantizationParams struct
483     // is a 32-bit integer.
484     result->params.zero_point =
485         static_cast<int32_t>(src_quantization->zero_point()->Get(0));
486 
487     // Populate per-channel quantization params.
488     int channels = src_quantization->scale()->size();
489     TfLiteAffineQuantization* quantization =
490         allocate_temp
491             ? reinterpret_cast<TfLiteAffineQuantization*>(
492                   allocator->AllocateTemp(sizeof(TfLiteAffineQuantization),
493                                           alignof(TfLiteAffineQuantization)))
494             : reinterpret_cast<TfLiteAffineQuantization*>(
495                   allocator->AllocateFromTail(
496                       sizeof(TfLiteAffineQuantization),
497                       alignof(TfLiteAffineQuantization)));
498     if (quantization == nullptr) {
499       TF_LITE_REPORT_ERROR(error_reporter,
500                            "Unable to allocate TfLiteAffineQuantization.\n");
501       return kTfLiteError;
502     }
503 
504     // TODO(b/153688719): Reduce tail allocation by using a global zero-point
505     // buffer. This value can not be reused from the flatbuffer since the
506     // zero_point is stored as a int64_t.
507     quantization->zero_point =
508         allocate_temp
509             ? reinterpret_cast<TfLiteIntArray*>(allocator->AllocateTemp(
510                   TfLiteIntArrayGetSizeInBytes(channels),
511                   alignof(TfLiteIntArray)))
512             : reinterpret_cast<TfLiteIntArray*>(allocator->AllocateFromTail(
513                   TfLiteIntArrayGetSizeInBytes(channels),
514                   alignof(TfLiteIntArray)));
515     if (quantization->zero_point == nullptr) {
516       TF_LITE_REPORT_ERROR(error_reporter,
517                            "Unable to allocate quantization->zero_point.\n");
518       return kTfLiteError;
519     }
520 
521     TF_LITE_ENSURE_STATUS(FlatBufferVectorToTfLiteTypeArray(
522         allocator, error_reporter, src_quantization->scale(),
523         &quantization->scale));
524 
525     quantization->zero_point->size = channels;
526     int* zero_point_data = quantization->zero_point->data;
527     for (int i = 0; i < channels; i++) {
528       zero_point_data[i] = src_quantization->zero_point()->Get(i);
529     }
530     // TODO(rocky): Need to add a micro_allocator test case that fails when
531     // this is not copied:
532     quantization->quantized_dimension = src_quantization->quantized_dimension();
533 
534     result->quantization = {kTfLiteAffineQuantization, quantization};
535   }
536   return kTfLiteOk;
537 }
538 
InitializeTfLiteEvalTensorFromFlatbuffer(SimpleMemoryAllocator * allocator,const tflite::Tensor & flatbuffer_tensor,const flatbuffers::Vector<flatbuffers::Offset<Buffer>> * buffers,ErrorReporter * error_reporter,TfLiteEvalTensor * result)539 TfLiteStatus InitializeTfLiteEvalTensorFromFlatbuffer(
540     SimpleMemoryAllocator* allocator, const tflite::Tensor& flatbuffer_tensor,
541     const flatbuffers::Vector<flatbuffers::Offset<Buffer>>* buffers,
542     ErrorReporter* error_reporter, TfLiteEvalTensor* result) {
543   *result = {};
544   // Make sure the serialized type is one we know how to deal with, and convert
545   // it from a flatbuffer enum into a constant used by the kernel C API.
546   TF_LITE_ENSURE_STATUS(ConvertTensorType(flatbuffer_tensor.type(),
547                                           &result->type, error_reporter));
548 
549   result->data.data = GetFlatbufferTensorBuffer(flatbuffer_tensor, buffers);
550 
551   if (flatbuffer_tensor.shape() == nullptr) {
552     // flatbuffer_tensor.shape() can return a nullptr in the case of a scalar
553     // tensor.
554     result->dims = const_cast<TfLiteIntArray*>(&kZeroLengthIntArray);
555   } else {
556     TF_LITE_ENSURE_STATUS(FlatBufferVectorToTfLiteTypeArray(
557         allocator, error_reporter, flatbuffer_tensor.shape(), &(result->dims)));
558   }
559   return kTfLiteOk;
560 }
561 
562 }  // namespace internal
563 
MicroAllocator(SimpleMemoryAllocator * memory_allocator,ErrorReporter * error_reporter)564 MicroAllocator::MicroAllocator(SimpleMemoryAllocator* memory_allocator,
565                                ErrorReporter* error_reporter)
566     : memory_allocator_(memory_allocator),
567       error_reporter_(error_reporter),
568       model_is_allocating_(false) {}
569 
~MicroAllocator()570 MicroAllocator::~MicroAllocator() {}
571 
Create(uint8_t * tensor_arena,size_t arena_size,ErrorReporter * error_reporter)572 MicroAllocator* MicroAllocator::Create(uint8_t* tensor_arena, size_t arena_size,
573                                        ErrorReporter* error_reporter) {
574   uint8_t* aligned_arena = AlignPointerUp(tensor_arena, kBufferAlignment);
575   size_t aligned_arena_size = tensor_arena + arena_size - aligned_arena;
576   return Create(SimpleMemoryAllocator::Create(error_reporter, aligned_arena,
577                                               aligned_arena_size),
578                 error_reporter);
579 }
580 
Create(SimpleMemoryAllocator * memory_allocator,ErrorReporter * error_reporter)581 MicroAllocator* MicroAllocator::Create(SimpleMemoryAllocator* memory_allocator,
582                                        ErrorReporter* error_reporter) {
583   TFLITE_DCHECK(memory_allocator != nullptr);
584   TFLITE_DCHECK(error_reporter != nullptr);
585 
586   uint8_t* allocator_buffer = memory_allocator->AllocateFromTail(
587       sizeof(MicroAllocator), alignof(MicroAllocator));
588   MicroAllocator* allocator =
589       new (allocator_buffer) MicroAllocator(memory_allocator, error_reporter);
590   return allocator;
591 }
592 
StartModelAllocation(const Model * model)593 SubgraphAllocations* MicroAllocator::StartModelAllocation(const Model* model) {
594   TFLITE_DCHECK(model != nullptr);
595 
596   if (model_is_allocating_) {
597     TF_LITE_REPORT_ERROR(error_reporter_,
598                          "MicroAllocator: Model allocation started before "
599                          "finishing previously allocated model");
600     return nullptr;
601   }
602 
603   model_is_allocating_ = true;
604 
605   uint8_t* data_allocator_buffer = memory_allocator_->AllocateFromTail(
606       sizeof(MicroBuiltinDataAllocator), alignof(MicroBuiltinDataAllocator));
607   builtin_data_allocator_ =
608       new (data_allocator_buffer) MicroBuiltinDataAllocator(memory_allocator_);
609 
610   if (InitScratchBufferData() != kTfLiteOk) {
611     return nullptr;
612   }
613 
614   // Allocate struct to store eval tensors, nodes and registrations.
615   SubgraphAllocations* output = reinterpret_cast<SubgraphAllocations*>(
616       memory_allocator_->AllocateFromTail(
617           sizeof(SubgraphAllocations) * model->subgraphs()->size(),
618           alignof(SubgraphAllocations)));
619   if (output == nullptr) {
620     MicroPrintf("Failed to allocate memory for model metadata.");
621     return nullptr;
622   }
623 
624   if (AllocateTfLiteEvalTensors(model, output) != kTfLiteOk ||
625       AllocateNodeAndRegistrations(model, output) != kTfLiteOk) {
626     return nullptr;
627   }
628   return output;
629 }
630 
FinishModelAllocation(const Model * model,SubgraphAllocations * subgraph_allocations,ScratchBufferHandle ** scratch_buffer_handles)631 TfLiteStatus MicroAllocator::FinishModelAllocation(
632     const Model* model, SubgraphAllocations* subgraph_allocations,
633     ScratchBufferHandle** scratch_buffer_handles) {
634   if (!model_is_allocating_) {
635     TF_LITE_REPORT_ERROR(error_reporter_,
636                          "MicroAllocator: Model allocation finished before "
637                          "starting allocating model");
638     return kTfLiteError;
639   }
640 
641   // TODO(b/187993197): Track scratch buffers for each subgraph.
642   for (size_t subgraph_idx = 0; subgraph_idx < model->subgraphs()->size();
643        subgraph_idx++) {
644     const SubGraph* subgraph = model->subgraphs()->Get(subgraph_idx);
645     TFLITE_DCHECK(subgraph != nullptr);
646 
647     TF_LITE_ENSURE_STATUS(AllocateScratchBufferHandles(
648         scratch_buffer_handles, scratch_buffer_request_count_));
649     TF_LITE_ENSURE_STATUS(CommitStaticMemoryPlan(
650         model, subgraph_allocations[subgraph_idx].tensors,
651         *scratch_buffer_handles, subgraph_idx));
652     TF_LITE_ENSURE_STATUS(AllocateVariables(
653         subgraph, subgraph_allocations[subgraph_idx].tensors));
654   }
655   model_is_allocating_ = false;
656   return kTfLiteOk;
657 }
658 
AllocatePersistentBuffer(size_t bytes)659 void* MicroAllocator::AllocatePersistentBuffer(size_t bytes) {
660   return memory_allocator_->AllocateFromTail(bytes, kBufferAlignment);
661 }
662 
RequestScratchBufferInArena(size_t bytes,int subgraph_idx,int * buffer_idx)663 TfLiteStatus MicroAllocator::RequestScratchBufferInArena(size_t bytes,
664                                                          int subgraph_idx,
665                                                          int* buffer_idx) {
666   // All scratch buffer requests are stored in the head section of the arena
667   // when a model is in the prepare phase. First align a scratch buffer request
668   // pointer to the start of the head:
669   internal::ScratchBufferRequest* requests = GetScratchBufferRequests();
670 
671   // Count the number of requested scratch buffers for the current node:
672   size_t current_node_request_count = 0;
673   for (size_t i = 0; i < scratch_buffer_request_count_; ++i) {
674     if (requests[i].node_idx == kUnassignedScratchBufferRequestIndex) {
675       ++current_node_request_count;
676     }
677   }
678 
679   // First, ensure that the per-kernel request has not exceeded the limit:
680   if (current_node_request_count >= kMaxScratchBuffersPerOp) {
681     TF_LITE_REPORT_ERROR(
682         error_reporter_,
683         "Scratch buffer request exeeds limit per operator (%d)",
684         kMaxScratchBuffersPerOp);
685     return kTfLiteError;
686   }
687 
688   // Initialize and assign values for the request at the current index:
689   internal::ScratchBufferRequest* current_request =
690       &requests[scratch_buffer_request_count_];
691   *current_request = {};
692   // Assign -1 as a sentinel value that will be updated when the node finishes
693   // allocating:
694   current_request->bytes = bytes;
695   current_request->node_idx = kUnassignedScratchBufferRequestIndex;
696 
697   // Assign the current request index to the out-param:
698   *buffer_idx = scratch_buffer_request_count_;
699 
700   // Bump the request count to prepare for the next request:
701   ++scratch_buffer_request_count_;
702   return kTfLiteOk;
703 }
704 
FinishPrepareNodeAllocations(int node_id)705 TfLiteStatus MicroAllocator::FinishPrepareNodeAllocations(int node_id) {
706   // When a node has finished preparing, all temp allocations performed by the
707   // kernel should be cleaned up:
708   ResetTempAllocations();
709 
710   // Find and update any new scratch buffer requests for the current node:
711   internal::ScratchBufferRequest* requests = GetScratchBufferRequests();
712 
713   for (size_t i = 0; i < scratch_buffer_request_count_; ++i) {
714     // A request with a node_idx of -1 is a sentinel value used to indicate this
715     // was a new request for the current node. The allocator finally knows the
716     // node index at this point. Assign the value and update the list of new
717     // requests so the head section can be adjusted to allow for the next kernel
718     // to allocate at most kMaxScratchBuffersPerOp requests:
719     if (requests[i].node_idx == kUnassignedScratchBufferRequestIndex) {
720       requests[i].node_idx = node_id;
721     }
722   }
723 
724   // Ensure that the head is re-adjusted to allow for another at-most
725   // kMaxScratchBuffersPerOp scratch buffer requests in the next operator:
726   TF_LITE_ENSURE_STATUS(memory_allocator_->SetHeadBufferSize(
727       sizeof(internal::ScratchBufferRequest) *
728           (scratch_buffer_request_count_ + kMaxScratchBuffersPerOp),
729       alignof(internal::ScratchBufferRequest)));
730 
731   return kTfLiteOk;
732 }
733 
used_bytes() const734 size_t MicroAllocator::used_bytes() const {
735   return memory_allocator_->GetUsedBytes();
736 }
737 
AllocateNodeAndRegistrations(const Model * model,SubgraphAllocations * subgraph_allocations)738 TfLiteStatus MicroAllocator::AllocateNodeAndRegistrations(
739     const Model* model, SubgraphAllocations* subgraph_allocations) {
740   TFLITE_DCHECK(subgraph_allocations != nullptr);
741 
742   for (size_t subgraph_idx = 0; subgraph_idx < model->subgraphs()->size();
743        subgraph_idx++) {
744     const SubGraph* subgraph = model->subgraphs()->Get(subgraph_idx);
745     TFLITE_DCHECK(subgraph != nullptr);
746 
747     uint32_t operators_size = NumSubgraphOperators(subgraph);
748 
749     // Initialize NodeAndRegistrations for the subgraph.
750     NodeAndRegistration* output = reinterpret_cast<NodeAndRegistration*>(
751         memory_allocator_->AllocateFromTail(
752             sizeof(NodeAndRegistration) * operators_size,
753             alignof(NodeAndRegistration)));
754     if (output == nullptr) {
755       TF_LITE_REPORT_ERROR(
756           error_reporter_,
757           "Failed to allocate memory for node_and_registrations.");
758       return kTfLiteError;
759     }
760     subgraph_allocations[subgraph_idx].node_and_registrations = output;
761   }
762   return kTfLiteOk;
763 }
AllocatePersistentTfLiteTensor(const Model * model,const SubgraphAllocations * subgraph_allocations,int tensor_index,int subgraph_index)764 TfLiteTensor* MicroAllocator::AllocatePersistentTfLiteTensor(
765     const Model* model, const SubgraphAllocations* subgraph_allocations,
766     int tensor_index, int subgraph_index) {
767   const SubGraph* subgraph = model->subgraphs()->Get(subgraph_index);
768   TFLITE_DCHECK(subgraph != nullptr);
769 
770   // This value is allocated from persistent arena space. It is guaranteed to be
771   // around for the lifetime of the application.
772   TfLiteTensor* tensor = AllocatePersistentTfLiteTensorInternal();
773 
774   // Populate any fields from the flatbuffer, since this TfLiteTensor struct is
775   // allocated in the persistent section of the arena, ensure that additional
776   // allocations also take place in that section of the arena.
777   if (PopulateTfLiteTensorFromFlatbuffer(
778           model, tensor, tensor_index, subgraph_index,
779           /*allocate_temp=*/false) != kTfLiteOk) {
780     TF_LITE_REPORT_ERROR(error_reporter_,
781                          "Failed to populate a persistent TfLiteTensor struct "
782                          "from flatbuffer data!");
783     return nullptr;
784   }
785 
786   if (subgraph_allocations != nullptr) {
787     // Tensor buffers that are allocated at runtime (e.g. non-weight buffers)
788     // and not located in the flatbuffer are stored on the pre-allocated list of
789     // TfLiteEvalTensors structs. These structs are the source of truth, simply
790     // point the corresponding buffer to the new TfLiteTensor data value.
791     tensor->data.data =
792         subgraph_allocations[subgraph_index].tensors[tensor_index].data.data;
793     // TfLiteEvalTensor structs must also be the source of truth for the
794     // TfLiteTensor dims.
795     tensor->dims =
796         subgraph_allocations[subgraph_index].tensors[tensor_index].dims;
797   }
798   return tensor;
799 }
800 
AllocateTempTfLiteTensor(const Model * model,const SubgraphAllocations * subgraph_allocations,int tensor_index,int subgraph_index)801 TfLiteTensor* MicroAllocator::AllocateTempTfLiteTensor(
802     const Model* model, const SubgraphAllocations* subgraph_allocations,
803     int tensor_index, int subgraph_index) {
804   const SubGraph* subgraph = model->subgraphs()->Get(subgraph_index);
805   TFLITE_DCHECK(subgraph != nullptr);
806 
807   // This value is allocated from temporary arena space. It is guaranteed to be
808   // around for at least the scope of the calling function. Since this struct
809   // allocation takes place in temp space, no need to own or cleanup.
810   TfLiteTensor* tensor =
811       reinterpret_cast<TfLiteTensor*>(memory_allocator_->AllocateTemp(
812           sizeof(TfLiteTensor), alignof(TfLiteTensor)));
813 
814   // Populate any fields from the flatbuffer, since this TfLiteTensor struct is
815   // allocated in the temp section of the arena, ensure that additional
816   // allocations also take place in that section of the arena.
817   if (PopulateTfLiteTensorFromFlatbuffer(model, tensor, tensor_index,
818                                          subgraph_index,
819                                          /*allocate_temp=*/true) != kTfLiteOk) {
820     TF_LITE_REPORT_ERROR(
821         error_reporter_,
822         "Failed to populate a temp TfLiteTensor struct from flatbuffer data!");
823     return nullptr;
824   }
825 
826   if (subgraph_allocations != nullptr) {
827     // Tensor buffers that are allocated at runtime (e.g. non-weight buffers)
828     // and not located in the flatbuffer are stored on the pre-allocated list of
829     // TfLiteEvalTensors structs. These structs are the source of truth, simply
830     // point the corresponding buffer to the new TfLiteTensor data value.
831     tensor->data.data =
832         subgraph_allocations[subgraph_index].tensors[tensor_index].data.data;
833     // TfLiteEvalTensor structs must also be the source of truth for the
834     // TfLiteTensor dims.
835     tensor->dims =
836         subgraph_allocations[subgraph_index].tensors[tensor_index].dims;
837   }
838   return tensor;
839 }
840 
ResetTempAllocations()841 void MicroAllocator::ResetTempAllocations() {
842   memory_allocator_->ResetTempAllocations();
843 }
844 
AllocateTfLiteEvalTensors(const Model * model,SubgraphAllocations * subgraph_allocations)845 TfLiteStatus MicroAllocator::AllocateTfLiteEvalTensors(
846     const Model* model, SubgraphAllocations* subgraph_allocations) {
847   TFLITE_DCHECK(subgraph_allocations != nullptr);
848 
849   for (size_t subgraph_idx = 0; subgraph_idx < model->subgraphs()->size();
850        subgraph_idx++) {
851     const SubGraph* subgraph = model->subgraphs()->Get(subgraph_idx);
852     TFLITE_DCHECK(subgraph != nullptr);
853 
854     size_t alloc_count = subgraph->tensors()->size();
855     TfLiteEvalTensor* tensors =
856         reinterpret_cast<TfLiteEvalTensor*>(memory_allocator_->AllocateFromTail(
857             sizeof(TfLiteEvalTensor) * alloc_count, alignof(TfLiteEvalTensor)));
858     if (tensors == nullptr) {
859       TF_LITE_REPORT_ERROR(
860           error_reporter_,
861           "Failed to allocate memory for context->eval_tensors, "
862           "%d bytes required",
863           sizeof(TfLiteEvalTensor) * alloc_count);
864       return kTfLiteError;
865     }
866 
867     for (size_t i = 0; i < alloc_count; ++i) {
868       TfLiteStatus status = internal::InitializeTfLiteEvalTensorFromFlatbuffer(
869           memory_allocator_, *subgraph->tensors()->Get(i), model->buffers(),
870           error_reporter_, &tensors[i]);
871       if (status != kTfLiteOk) {
872         TF_LITE_REPORT_ERROR(error_reporter_, "Failed to initialize tensor %d",
873                              i);
874         return kTfLiteError;
875       }
876     }
877     subgraph_allocations[subgraph_idx].tensors = tensors;
878   }
879   return kTfLiteOk;
880 }
AllocateVariables(const SubGraph * subgraph,TfLiteEvalTensor * eval_tensors)881 TfLiteStatus MicroAllocator::AllocateVariables(const SubGraph* subgraph,
882                                                TfLiteEvalTensor* eval_tensors) {
883   for (size_t i = 0; i < subgraph->tensors()->size(); ++i) {
884     auto* tensor = subgraph->tensors()->Get(i);
885     if (tensor->is_variable()) {
886       size_t buffer_size;
887       TF_LITE_ENSURE_STATUS(
888           TfLiteEvalTensorByteLength(&eval_tensors[i], &buffer_size));
889 
890       eval_tensors[i].data.data =
891           memory_allocator_->AllocateFromTail(buffer_size, kBufferAlignment);
892 
893       if (eval_tensors[i].data.data == nullptr) {
894         TF_LITE_REPORT_ERROR(error_reporter_,
895                              "Failed to allocate variable tensor of size %d",
896                              buffer_size);
897         return kTfLiteError;
898       }
899     }
900   }
901   return kTfLiteOk;
902 }
903 
AllocatePersistentTfLiteTensorInternal()904 TfLiteTensor* MicroAllocator::AllocatePersistentTfLiteTensorInternal() {
905   return reinterpret_cast<TfLiteTensor*>(memory_allocator_->AllocateFromTail(
906       sizeof(TfLiteTensor), alignof(TfLiteTensor)));
907 }
908 
PopulateTfLiteTensorFromFlatbuffer(const Model * model,TfLiteTensor * tensor,int tensor_index,int subgraph_idx,bool allocate_temp)909 TfLiteStatus MicroAllocator::PopulateTfLiteTensorFromFlatbuffer(
910     const Model* model, TfLiteTensor* tensor, int tensor_index,
911     int subgraph_idx, bool allocate_temp) {
912   // TODO(b/162311891): This method serves as a stub to ensure quantized
913   // allocations in the tail can be recorded. Once the interpreter has APIs for
914   // accessing buffers on TfLiteEvalTensor this method can be dropped.
915   return internal::InitializeTfLiteTensorFromFlatbuffer(
916       memory_allocator_, allocate_temp,
917       *model->subgraphs()->Get(subgraph_idx)->tensors()->Get(tensor_index),
918       model->buffers(), error_reporter_, tensor);
919 }
920 
error_reporter() const921 ErrorReporter* MicroAllocator::error_reporter() const {
922   return error_reporter_;
923 }
924 
CommitStaticMemoryPlan(const Model * model,TfLiteEvalTensor * eval_tensors,ScratchBufferHandle * scratch_buffer_handles,int subgraph_idx)925 TfLiteStatus MicroAllocator::CommitStaticMemoryPlan(
926     const Model* model, TfLiteEvalTensor* eval_tensors,
927     ScratchBufferHandle* scratch_buffer_handles, int subgraph_idx) {
928   size_t head_usage = 0;
929   // Create static memory plan
930   // 1. Calculate AllocationInfo to know the lifetime of each tensor/buffer.
931   // 2. Add them into the planner (such as the GreedyMemoryPlanner).
932   // 3. Static memory planning using the planner.
933   // 4. Set tensor/buffer pointers based on the offsets from the previous step.
934   //
935   // Note that AllocationInfo is only needed for creating the plan. It will be
936   // allocated from the temp section and cleaned up at the bottom of this
937   // function.
938 
939   const SubGraph* subgraph = model->subgraphs()->Get(subgraph_idx);
940   size_t allocation_info_count =
941       subgraph->tensors()->size() + scratch_buffer_request_count_;
942   size_t bytes = sizeof(AllocationInfo) * allocation_info_count;
943 
944   // Allocate an array of AllocationInfo structs from the temp section. This
945   // struct will be used by AllocationInfoBuilder to find buffer usage.
946   AllocationInfo* allocation_info = reinterpret_cast<AllocationInfo*>(
947       memory_allocator_->AllocateTemp(bytes, alignof(AllocationInfo)));
948   if (allocation_info == nullptr) {
949     TF_LITE_REPORT_ERROR(
950         error_reporter_,
951         "Failed to allocate memory for allocation_info, %d bytes required",
952         bytes);
953     return kTfLiteError;
954   }
955 
956   // Use the AllocationInfoBuilder class to help determine where buffers are
957   // used in the subgraph.
958   AllocationInfoBuilder builder(allocation_info, subgraph->tensors()->size(),
959                                 scratch_buffer_request_count_, error_reporter_);
960 
961   const int32_t* offline_planner_offsets = nullptr;
962   TF_LITE_ENSURE_STATUS(
963       builder.GetOfflinePlannedOffsets(model, &offline_planner_offsets));
964   TF_LITE_ENSURE_STATUS(
965       builder.AddTensors(subgraph, offline_planner_offsets, eval_tensors));
966 
967   internal::ScratchBufferRequest* scratch_buffer_requests =
968       GetScratchBufferRequests();
969 
970   TF_LITE_ENSURE_STATUS(builder.AddScratchBuffers(scratch_buffer_requests,
971                                                   scratch_buffer_handles));
972 
973   // Remaining arena size that memory planner can use for calculating offsets.
974   size_t remaining_arena_size =
975       memory_allocator_->GetAvailableMemory(kBufferAlignment);
976   uint8_t* planner_arena =
977       memory_allocator_->AllocateTemp(remaining_arena_size, kBufferAlignment);
978   TF_LITE_ENSURE(error_reporter_, planner_arena != nullptr);
979   GreedyMemoryPlanner planner(planner_arena, remaining_arena_size);
980   TF_LITE_ENSURE_STATUS(CreatePlan(error_reporter_, &planner, allocation_info,
981                                    allocation_info_count));
982 
983   // Reset all temp allocations used above:
984   memory_allocator_->ResetTempAllocations();
985 
986   size_t actual_available_arena_size =
987       memory_allocator_->GetAvailableMemory(kBufferAlignment);
988 
989   // Make sure we have enough arena size.
990   if (planner.GetMaximumMemorySize() > actual_available_arena_size) {
991     TF_LITE_REPORT_ERROR(
992         error_reporter_,
993         "Arena size is too small for all buffers. Needed %u but only "
994         "%u was available.",
995         planner.GetMaximumMemorySize(), actual_available_arena_size);
996     return kTfLiteError;
997   }
998   // Commit the plan.
999   TF_LITE_ENSURE_STATUS(CommitPlan(error_reporter_, &planner,
1000                                    memory_allocator_->GetHeadBuffer(),
1001                                    allocation_info, allocation_info_count));
1002 #ifdef TF_LITE_SHOW_MEMORY_USE
1003   planner.PrintMemoryPlan();
1004 #endif
1005   head_usage = planner.GetMaximumMemorySize();
1006 
1007   // The head is used to store memory plans for one model at a time during the
1008   // model preparation stage, and is re-purposed to store scratch buffer handles
1009   // during model invocation. The head must be as large as the greater of the
1010   // largest model memory plan's size and the total space required for all
1011   // scratch buffer handles.
1012   if (max_head_buffer_usage_ < head_usage) {
1013     max_head_buffer_usage_ = head_usage;
1014   }
1015 
1016   // The head is used for storing scratch buffer allocations before finalizing a
1017   // memory plan in this function. Ensure that the head is set to the largest
1018   // memory plan sent through the allocator:
1019   TF_LITE_ENSURE_STATUS(memory_allocator_->SetHeadBufferSize(
1020       max_head_buffer_usage_, kBufferAlignment));
1021   return kTfLiteOk;
1022 }
1023 
AllocateScratchBufferHandles(ScratchBufferHandle ** scratch_buffer_handles,size_t handle_count)1024 TfLiteStatus MicroAllocator::AllocateScratchBufferHandles(
1025     ScratchBufferHandle** scratch_buffer_handles, size_t handle_count) {
1026   TFLITE_DCHECK(scratch_buffer_handles != nullptr);
1027 
1028   if (scratch_buffer_request_count_ == 0) {
1029     // No scratch buffer requests were requested during model allocation.
1030     return kTfLiteOk;
1031   }
1032 
1033   // Allocate a consecutive block of memory store the scratch buffer handles.
1034   // This alignment ensures quick lookup during inference time for the model:
1035   *scratch_buffer_handles = reinterpret_cast<ScratchBufferHandle*>(
1036       memory_allocator_->AllocateFromTail(
1037           sizeof(ScratchBufferHandle) * handle_count,
1038           alignof(ScratchBufferHandle)));
1039 
1040   return kTfLiteOk;
1041 }
1042 
InitScratchBufferData()1043 TfLiteStatus MicroAllocator::InitScratchBufferData() {
1044   // A model is preparing to allocate resources, ensure that scratch buffer
1045   // request counter is cleared:
1046   scratch_buffer_request_count_ = 0;
1047 
1048   // All requests will be stored in the head section. Each kernel is allowed at
1049   // most kMaxScratchBuffersPerOp requests. Adjust the head to reserve at most
1050   // that many requests to begin:
1051   TF_LITE_ENSURE_STATUS(memory_allocator_->SetHeadBufferSize(
1052       sizeof(internal::ScratchBufferRequest) * kMaxScratchBuffersPerOp,
1053       alignof(internal::ScratchBufferRequest)));
1054 
1055   return kTfLiteOk;
1056 }
1057 
GetScratchBufferRequests()1058 internal::ScratchBufferRequest* MicroAllocator::GetScratchBufferRequests() {
1059   return reinterpret_cast<internal::ScratchBufferRequest*>(
1060       AlignPointerUp(memory_allocator_->GetHeadBuffer(),
1061                      alignof(internal::ScratchBufferRequest)));
1062 }
1063 
FlatBufferVectorToTfLiteTypeArray(const flatbuffers::Vector<int32_t> * flatbuffer_array,TfLiteIntArray ** result)1064 TfLiteStatus MicroAllocator::FlatBufferVectorToTfLiteTypeArray(
1065     const flatbuffers::Vector<int32_t>* flatbuffer_array,
1066     TfLiteIntArray** result) {
1067   return internal::FlatBufferVectorToTfLiteTypeArray(
1068       memory_allocator_, error_reporter_, flatbuffer_array, result);
1069 }
1070 
GetBuiltinDataAllocator()1071 BuiltinDataAllocator* MicroAllocator::GetBuiltinDataAllocator() {
1072   return builtin_data_allocator_;
1073 }
1074 
1075 }  // namespace tflite
1076