Several operators (rank, shape) are critical for preserving the ability to resize graphs correctly at runtime. However, introduction of such ops in the graph currently makes it impossible to fully propagate shapes when tensors are allocated. This also prevents delegation of the graph for most delegates, as it introduces dynamic shapes. Introduce a new, persistent tensor type that can be treated as "constant" at the time of TfLiteRegistration::Prepare. This tensor type is allocated immediately when requested, similar to a dynamic tensor, but promises that its contents will be populated after the "producing" node is prepared, and that it won't change across subsequent evals. Update Rank/Shape operators to use this tensor allocation type. A follow-up CL will introduce a new pseudo-constant tensor check that can be used by various kernels to avoid making them dynamic. PiperOrigin-RevId: 311199934 Change-Id: I050704be7d1ff264fc1a852efade53d4021cb034
231 lines
6.3 KiB
C
231 lines
6.3 KiB
C
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
==============================================================================*/
|
|
|
|
#include "tensorflow/lite/c/common.h"
|
|
#ifndef TF_LITE_STATIC_MEMORY
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#endif // TF_LITE_STATIC_MEMORY
|
|
|
|
int TfLiteIntArrayGetSizeInBytes(int size) {
|
|
static TfLiteIntArray dummy;
|
|
return sizeof(dummy) + sizeof(dummy.data[0]) * size;
|
|
}
|
|
|
|
int TfLiteIntArrayEqual(const TfLiteIntArray* a, const TfLiteIntArray* b) {
|
|
if (a == b) return 1;
|
|
if (a == NULL || b == NULL) return 0;
|
|
return TfLiteIntArrayEqualsArray(a, b->size, b->data);
|
|
}
|
|
|
|
int TfLiteIntArrayEqualsArray(const TfLiteIntArray* a, int b_size,
|
|
const int b_data[]) {
|
|
if (a == NULL) return (b_size == 0);
|
|
if (a->size != b_size) return 0;
|
|
int i = 0;
|
|
for (; i < a->size; i++)
|
|
if (a->data[i] != b_data[i]) return 0;
|
|
return 1;
|
|
}
|
|
|
|
#ifndef TF_LITE_STATIC_MEMORY
|
|
|
|
TfLiteIntArray* TfLiteIntArrayCreate(int size) {
|
|
TfLiteIntArray* ret =
|
|
(TfLiteIntArray*)malloc(TfLiteIntArrayGetSizeInBytes(size));
|
|
ret->size = size;
|
|
return ret;
|
|
}
|
|
|
|
TfLiteIntArray* TfLiteIntArrayCopy(const TfLiteIntArray* src) {
|
|
if (!src) return NULL;
|
|
TfLiteIntArray* ret = TfLiteIntArrayCreate(src->size);
|
|
if (ret) {
|
|
memcpy(ret->data, src->data, src->size * sizeof(int));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void TfLiteIntArrayFree(TfLiteIntArray* a) { free(a); }
|
|
|
|
#endif // TF_LITE_STATIC_MEMORY
|
|
|
|
int TfLiteFloatArrayGetSizeInBytes(int size) {
|
|
static TfLiteFloatArray dummy;
|
|
return sizeof(dummy) + sizeof(dummy.data[0]) * size;
|
|
}
|
|
|
|
#ifndef TF_LITE_STATIC_MEMORY
|
|
|
|
TfLiteFloatArray* TfLiteFloatArrayCreate(int size) {
|
|
TfLiteFloatArray* ret =
|
|
(TfLiteFloatArray*)malloc(TfLiteFloatArrayGetSizeInBytes(size));
|
|
ret->size = size;
|
|
return ret;
|
|
}
|
|
|
|
void TfLiteFloatArrayFree(TfLiteFloatArray* a) { free(a); }
|
|
|
|
void TfLiteTensorDataFree(TfLiteTensor* t) {
|
|
if (t->allocation_type == kTfLiteDynamic ||
|
|
t->allocation_type == kTfLitePersistentRo) {
|
|
free(t->data.raw);
|
|
}
|
|
t->data.raw = NULL;
|
|
}
|
|
|
|
void TfLiteQuantizationFree(TfLiteQuantization* quantization) {
|
|
if (quantization->type == kTfLiteAffineQuantization) {
|
|
TfLiteAffineQuantization* q_params =
|
|
(TfLiteAffineQuantization*)(quantization->params);
|
|
if (q_params->scale) {
|
|
TfLiteFloatArrayFree(q_params->scale);
|
|
q_params->scale = NULL;
|
|
}
|
|
if (q_params->zero_point) {
|
|
TfLiteIntArrayFree(q_params->zero_point);
|
|
q_params->zero_point = NULL;
|
|
}
|
|
free(q_params);
|
|
}
|
|
quantization->params = NULL;
|
|
quantization->type = kTfLiteNoQuantization;
|
|
}
|
|
|
|
void TfLiteSparsityFree(TfLiteSparsity* sparsity) {
|
|
if (sparsity == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (sparsity->traversal_order) {
|
|
TfLiteIntArrayFree(sparsity->traversal_order);
|
|
sparsity->traversal_order = NULL;
|
|
}
|
|
|
|
if (sparsity->block_map) {
|
|
TfLiteIntArrayFree(sparsity->block_map);
|
|
sparsity->block_map = NULL;
|
|
}
|
|
|
|
if (sparsity->dim_metadata) {
|
|
int i = 0;
|
|
for (; i < sparsity->dim_metadata_size; i++) {
|
|
TfLiteDimensionMetadata metadata = sparsity->dim_metadata[i];
|
|
if (metadata.format == kTfLiteDimSparseCSR) {
|
|
TfLiteIntArrayFree(metadata.array_segments);
|
|
metadata.array_segments = NULL;
|
|
TfLiteIntArrayFree(metadata.array_indices);
|
|
metadata.array_indices = NULL;
|
|
}
|
|
}
|
|
free(sparsity->dim_metadata);
|
|
sparsity->dim_metadata = NULL;
|
|
}
|
|
|
|
free(sparsity);
|
|
}
|
|
|
|
void TfLiteTensorFree(TfLiteTensor* t) {
|
|
TfLiteTensorDataFree(t);
|
|
if (t->dims) TfLiteIntArrayFree(t->dims);
|
|
t->dims = NULL;
|
|
|
|
if (t->dims_signature) {
|
|
TfLiteIntArrayFree((TfLiteIntArray *) t->dims_signature);
|
|
}
|
|
t->dims_signature = NULL;
|
|
|
|
TfLiteQuantizationFree(&t->quantization);
|
|
TfLiteSparsityFree(t->sparsity);
|
|
t->sparsity = NULL;
|
|
}
|
|
|
|
void TfLiteTensorReset(TfLiteType type, const char* name, TfLiteIntArray* dims,
|
|
TfLiteQuantizationParams quantization, char* buffer,
|
|
size_t size, TfLiteAllocationType allocation_type,
|
|
const void* allocation, bool is_variable,
|
|
TfLiteTensor* tensor) {
|
|
TfLiteTensorFree(tensor);
|
|
tensor->type = type;
|
|
tensor->name = name;
|
|
tensor->dims = dims;
|
|
tensor->params = quantization;
|
|
tensor->data.raw = buffer;
|
|
tensor->bytes = size;
|
|
tensor->allocation_type = allocation_type;
|
|
tensor->allocation = allocation;
|
|
tensor->is_variable = is_variable;
|
|
|
|
tensor->quantization.type = kTfLiteNoQuantization;
|
|
tensor->quantization.params = NULL;
|
|
}
|
|
|
|
void TfLiteTensorRealloc(size_t num_bytes, TfLiteTensor* tensor) {
|
|
if (tensor->allocation_type != kTfLiteDynamic &&
|
|
tensor->allocation_type != kTfLitePersistentRo) {
|
|
return;
|
|
}
|
|
// TODO(b/145340303): Tensor data should be aligned.
|
|
if (!tensor->data.raw) {
|
|
tensor->data.raw = malloc(num_bytes);
|
|
} else if (num_bytes > tensor->bytes) {
|
|
tensor->data.raw = realloc(tensor->data.raw, num_bytes);
|
|
}
|
|
tensor->bytes = num_bytes;
|
|
}
|
|
#endif // TF_LITE_STATIC_MEMORY
|
|
|
|
const char* TfLiteTypeGetName(TfLiteType type) {
|
|
switch (type) {
|
|
case kTfLiteNoType:
|
|
return "NOTYPE";
|
|
case kTfLiteFloat32:
|
|
return "FLOAT32";
|
|
case kTfLiteInt16:
|
|
return "INT16";
|
|
case kTfLiteInt32:
|
|
return "INT32";
|
|
case kTfLiteUInt8:
|
|
return "UINT8";
|
|
case kTfLiteInt8:
|
|
return "INT8";
|
|
case kTfLiteInt64:
|
|
return "INT64";
|
|
case kTfLiteBool:
|
|
return "BOOL";
|
|
case kTfLiteComplex64:
|
|
return "COMPLEX64";
|
|
case kTfLiteString:
|
|
return "STRING";
|
|
case kTfLiteFloat16:
|
|
return "FLOAT16";
|
|
case kTfLiteFloat64:
|
|
return "FLOAT64";
|
|
}
|
|
return "Unknown type";
|
|
}
|
|
|
|
TfLiteDelegate TfLiteDelegateCreate() {
|
|
TfLiteDelegate d = {
|
|
.data_ = NULL,
|
|
.Prepare = NULL,
|
|
.CopyFromBufferHandle = NULL,
|
|
.CopyToBufferHandle = NULL,
|
|
.FreeBufferHandle = NULL,
|
|
.flags = kTfLiteDelegateFlagsNone,
|
|
};
|
|
return d;
|
|
}
|